Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.
View | Details | Raw Unified | Return to bug 488586
Collapse All | Expand All

(-)a/bundles/org.eclipse.orion.client.webtools/web/htmlparser2/tokenizer.js (+12 lines)
Lines 307-312 Link Here
307
			this._baseState = this._state;
307
			this._baseState = this._state;
308
			this._state = BEFORE_ENTITY;
308
			this._state = BEFORE_ENTITY;
309
			this._sectionStart = this._index;
309
			this._sectionStart = this._index;
310
		} else if (c === '<') {
311
			valueStart = this._quoteStart;
312
			this._emitToken("onattribdata");
313
			this._cbs.onattribend(false, valueStart);
314
			this._state = BEFORE_ATTRIBUTE_NAME;
315
			this._index--;
310
		}
316
		}
311
	};
317
	};
312
	
318
	
Lines 321-326 Link Here
321
			this._baseState = this._state;
327
			this._baseState = this._state;
322
			this._state = BEFORE_ENTITY;
328
			this._state = BEFORE_ENTITY;
323
			this._sectionStart = this._index;
329
			this._sectionStart = this._index;
330
		} else if (c === '<') {
331
			valueStart = this._quoteStart;
332
			this._emitToken("onattribdata");
333
			this._cbs.onattribend(false, valueStart);
334
			this._state = BEFORE_ATTRIBUTE_NAME;
335
			this._index--;
324
		}
336
		}
325
	};
337
	};
326
	
338
	
(-)a/bundles/org.eclipse.orion.client.webtools/web/js-tests/webtools/htmlContentAssistTests.js (-1 / +92 lines)
Lines 22-28 Link Here
22
    var astmanager = new ASTManager.HtmlAstManager();
22
    var astmanager = new ASTManager.HtmlAstManager();
23
    var assist = new HTMLAssist.HTMLContentAssistProvider(astmanager);
23
    var assist = new HTMLAssist.HTMLContentAssistProvider(astmanager);
24
    var tagTemplates = assist.getTags("", "");
24
    var tagTemplates = assist.getTags("", "");
25
    var globalTagAttributes = assist.getAttributesForNode({name: "zzz", type: "tag"}, {offset: 0, prefix: ""});
25
    var globalTagAttributes = assist.getAttributesForNode({name: "zzz", type: "tag"}, "", {offset: 0, prefix: ""});
26
    
26
    
27
    /**
27
    /**
28
     * Set up the test and return an object for the test context
28
     * Set up the test and return an object for the test context
Lines 1732-1737 Link Here
1732
    			assert(knownProp, "Could not find expected proposal role");
1732
    			assert(knownProp, "Could not find expected proposal role");
1733
    		});
1733
    		});
1734
    	});
1734
    	});
1735
    	it('Tags that support the attributes of many roles: details (13). Prefix role. <details r>', function() {
1736
    		var _o = setup({buffer: '<html><body><details role></details></body></html>'});
1737
    		return assist.computeContentAssist(_o.editorContext, {offset: 25, prefix: 'role'}).then(function(proposals) {
1738
    			var expectedCount = 2; // "ARIA title" + role
1739
    			assert(proposals.length === expectedCount, "Incorrect number of proposals for details tag attributes. Proposal count: " + proposals.length + " Expected count: " + expectedCount);
1740
    			var knownProp;
1741
    			for (var i=0; i<proposals.length; i++) {
1742
    				if (proposals[i].name === "role"){
1743
    					knownProp = proposals[i];
1744
    				}
1745
    			}
1746
    			assert(knownProp, "Could not find expected proposal role");
1747
    		});
1748
    	});
1749
    	it('Tags that support the attributes of many roles: details (13). Prefix empty. <details r>', function() {
1750
    		var _o = setup({buffer: '<html><body><details role=></details></body></html>'});
1751
    		return assist.computeContentAssist(_o.editorContext, {offset: 26, prefix: ''}).then(function(proposals) {
1752
    			var expectedCount = 1; // "ARIA title" + role
1753
    			assert(proposals.length === expectedCount, "Incorrect number of proposals for details tag attributes. Proposal count: " + proposals.length + " Expected count: " + expectedCount);
1754
    			var knownProp;
1755
    			for (var i=0; i<proposals.length; i++) {
1756
    				if (proposals[i].proposal === "\"\""){
1757
    					knownProp = proposals[i];
1758
    				}
1759
    			}
1760
    			assert(knownProp, "Could not find expected proposal role");
1761
    		});
1762
    	});
1763
    	it('Tags that support the attributes of many roles: details (13). Empty prefix. <details aria->', function() {
1764
    		var _o = setup({buffer: '<html><body><details aria-></details></body></html>'});
1765
    		return assist.computeContentAssist(_o.editorContext, {offset: 26, prefix: ''}).then(function(proposals) {
1766
    			var expectedCount = 17 + 13; // "ARIA title" + 16 aria-* globals + 13 role-specific aria-* attributes
1767
    			assert(proposals.length === expectedCount, "Incorrect number of proposals for details tag attributes. Proposal count: " + proposals.length + " Expected count: " + expectedCount);
1768
    			var knownProp;
1769
    			for (var i=0; i<proposals.length; i++) {
1770
    				if (proposals[i].name === "aria-checked"){
1771
    					knownProp = proposals[i];
1772
    				}
1773
    			}
1774
    			assert(knownProp, "Could not find expected proposal aria-checked");
1775
    			knownProp = null;
1776
    			for (i=0; i<proposals.length; i++) {
1777
    				if (proposals[i].name === "aria-expanded"){
1778
    					knownProp = proposals[i];
1779
    				}
1780
    			}
1781
    			assert(knownProp, "Could not find expected proposal aria-expanded");
1782
    			knownProp = null;
1783
    			for (i=0; i<proposals.length; i++) {
1784
    				if (proposals[i].name === "aria-activedescendant"){
1785
    					knownProp = proposals[i];
1786
    				}
1787
    			}
1788
    			assert(knownProp, "Could not find expected proposal aria-activedescendant");
1789
    			knownProp = null;
1790
    			for (i=0; i<proposals.length; i++) {
1791
    				if (proposals[i].name === "aria-multiselectable"){
1792
    					knownProp = proposals[i];
1793
    				}
1794
    			}
1795
    			assert(knownProp, "Could not find expected proposal aria-multiselectable");
1796
    			knownProp = null;
1797
    			for (i=0; i<proposals.length; i++) {
1798
    				if (proposals[i].name === "aria-pressed"){
1799
    					knownProp = proposals[i];
1800
    				}
1801
    			}
1802
    			assert(knownProp, "Could not find expected proposal aria-pressed");
1803
    			knownProp = null;
1804
    			for (i=0; i<proposals.length; i++) {
1805
    				if (proposals[i].name === "aria-required"){
1806
    					knownProp = proposals[i];
1807
    				}
1808
    			}
1809
    			assert(knownProp, "Could not find expected proposal aria-required");
1810
    			knownProp = null;
1811
    			for (i=0; i<proposals.length; i++) {
1812
    				if (proposals[i].name === "aria-posinset"){
1813
    					knownProp = proposals[i];
1814
    				}
1815
    			}
1816
    			assert(knownProp, "Could not find expected proposal aria-posinset");
1817
    			knownProp = null;
1818
    			for (i=0; i<proposals.length; i++) {
1819
    				if (proposals[i].name === "aria-setsize"){
1820
    					knownProp = proposals[i];
1821
    				}
1822
    			}
1823
    			assert(knownProp, "Could not find expected proposal aria-setsize");
1824
    		});
1825
    	});
1735
    	it('Tags that support the attributes of many roles: details (13). Prefix ar. <details ar>', function() {
1826
    	it('Tags that support the attributes of many roles: details (13). Prefix ar. <details ar>', function() {
1736
    		var _o = setup({buffer: '<html><body><details ar></details></body></html>'});
1827
    		var _o = setup({buffer: '<html><body><details ar></details></body></html>'});
1737
    		return assist.computeContentAssist(_o.editorContext, {offset: 21, prefix: 'ar'}).then(function(proposals) {
1828
    		return assist.computeContentAssist(_o.editorContext, {offset: 21, prefix: 'ar'}).then(function(proposals) {
(-)a/bundles/org.eclipse.orion.client.webtools/web/js-tests/webtools/htmlParserTests.js (-1 / +68 lines)
Lines 490-496 Link Here
490
	    		}
490
	    		}
491
		    ]);
491
		    ]);
492
		});
492
		});
493
		
493
		it("Parse attributes - unclosed value, unclosed tag", function() {
494
			var results = parse('<a src="</a>');
495
			assertResults(results.children, [
496
				{
497
					type: 'tag',
498
					name: 'a',
499
					range: [0,12],
500
					attributes: [
501
						{
502
							name: 'src',
503
							value: null,
504
							range: [3,8]
505
						}
506
					]
507
				}
508
			]);
509
		});
510
		it("Parse attributes - unclosed value, closed tag", function() {
511
			var results = parse('<a src="></a>');
512
			assertResults(results.children, [
513
				{
514
					type: 'tag',
515
					name: 'a',
516
					range: [0,13],
517
					attributes: [
518
						{
519
							name: 'src',
520
							value: ">",
521
							range: [3,9]
522
						}
523
					]
524
				}
525
			]);
526
		});
527
		it("Parse attributes - unclosed value, unclosed tag", function() {
528
			var results = parse("<a src='</a>");
529
			assertResults(results.children, [
530
				{
531
					type: 'tag',
532
					name: 'a',
533
					range: [0,12],
534
					attributes: [
535
						{
536
							name: 'src',
537
							value: null,
538
							range: [3,8]
539
						}
540
					]
541
				}
542
			]);
543
		});
544
		it("Parse attributes - unclosed value, closed tag", function() {
545
			var results = parse("<a src='></a>");
546
			assertResults(results.children, [
547
				{
548
					type: 'tag',
549
					name: 'a',
550
					range: [0,13],
551
					attributes: [
552
						{
553
							name: 'src',
554
							value: ">",
555
							range: [3,9]
556
						}
557
					]
558
				}
559
			]);
560
		});
494
		// Instructions
561
		// Instructions
495
		it("Parse !doctype instruction", function() {
562
		it("Parse !doctype instruction", function() {
496
			var results = parse('<!doctype html><html></html>');
563
			var results = parse('<!doctype html><html></html>');
(-)a/bundles/org.eclipse.orion.client.webtools/web/webtools/htmlContentAssist.js (-11 / +55 lines)
Lines 186-194 Link Here
186
				if (this.isCompletingCommentClose(node, params.offset)){
186
				if (this.isCompletingCommentClose(node, params.offset)){
187
					return this.getComment(node, params.offset, ast.source, false);
187
					return this.getComment(node, params.offset, ast.source, false);
188
				} else if (this.isCompletingAttributeValue(node, ast.source, params)) {
188
				} else if (this.isCompletingAttributeValue(node, ast.source, params)) {
189
					return this.getValuesForAttribute(node, params);
189
					return this.getValuesForAttribute(node, ast.source, params);
190
				} else if (this.isCompletingTagAttribute(node, ast.source, params)) {
190
				} else if (this.isCompletingTagAttribute(node, ast.source, params)) {
191
					return this.getAttributesForNode(node, params);
191
					return this.getAttributesForNode(node, ast.source, params);
192
				} else if (this.isCompletingTag(node, params)){
192
				} else if (this.isCompletingTag(node, params)){
193
					if (this.isCompletingCommentOpen(node)){
193
					if (this.isCompletingCommentOpen(node)){
194
						return this.getComment(node, params.offset, ast.source, true);
194
						return this.getComment(node, params.offset, ast.source, true);
Lines 442-450 Link Here
442
		 * @returns {Array.<Object>} The array of proposals
442
		 * @returns {Array.<Object>} The array of proposals
443
		 * @since 10.0 
443
		 * @since 10.0 
444
		 */
444
		 */
445
		getAttributesForNode: function(node, params) {
445
		getAttributesForNode: function(node, source, params) {
446
			var attrs = Attributes.getAttributesForNode(node);
447
			var proposals = [];
446
			var proposals = [];
447
			var prefix = params.prefix ? params.prefix : "";
448
			// we need to check if we need to rebuild the prefix for completion that contains a '-'
449
			var index = params.offset - prefix.length - 1;
450
			if (index > 0 && index < source.length) {
451
				var precedingChar = source.charAt(index);
452
				if (precedingChar === '-') {
453
					index--;
454
					if (index !== 0) {
455
						// rebuild a prefix based on what characters (letter only) are before the '-'
456
						precedingChar = source.charAt(index);
457
						var currentPrefix = "-" + prefix;
458
						loop: while (index > 0 && /[A-Za-z0-9_-]/.test(precedingChar)) {
459
							index--;
460
							currentPrefix = precedingChar + currentPrefix;
461
							if (index === 0) {
462
								break loop;
463
							}
464
							precedingChar = source.charAt(index);
465
						}
466
						params.prefix = currentPrefix;
467
					}
468
				} else if (precedingChar === '=' && prefix.length === 0 && (index - 1) > 0) {
469
					precedingChar = source.charAt(index - 1);
470
					if (/[A-Za-z0-9_]/.test(precedingChar)) {
471
						proposals.push(this.makeComputedProposal("\"\"",  Messages['addQuotesToAttributes'], " - \"\"", null, prefix)); //$NON-NLS-1$ //$NON-NLS-2$
472
						return proposals;
473
					}
474
				}
475
			}
476
			var attrs = Attributes.getAttributesForNode(node);
448
			if(Array.isArray(attrs.global)) {
477
			if(Array.isArray(attrs.global)) {
449
				proposals = proposals.concat(this.addProposals(node, attrs.global, params));
478
				proposals = proposals.concat(this.addProposals(node, attrs.global, params));
450
			}
479
			}
Lines 460-466 Link Here
460
					});
489
					});
461
					proposals = proposals.concat(arr);
490
					proposals = proposals.concat(arr);
462
				}
491
				}
463
464
			}
492
			}
465
			if(Array.isArray(attrs.keyboardevents)) {
493
			if(Array.isArray(attrs.keyboardevents)) {
466
				arr = this.addProposals(node, attrs.keyboardevents, params);
494
				arr = this.addProposals(node, attrs.keyboardevents, params);
Lines 474-480 Link Here
474
					});
502
					});
475
					proposals = proposals.concat(arr);
503
					proposals = proposals.concat(arr);
476
				}
504
				}
477
478
			}
505
			}
479
			if(Array.isArray(attrs.mouseevents)) {
506
			if(Array.isArray(attrs.mouseevents)) {
480
				arr = this.addProposals(node, attrs.mouseevents, params);
507
				arr = this.addProposals(node, attrs.mouseevents, params);
Lines 488-494 Link Here
488
						});
515
						});
489
					proposals = proposals.concat(arr);
516
					proposals = proposals.concat(arr);
490
				}
517
				}
491
492
			}
518
			}
493
			if(Array.isArray(attrs.windowevents) && attrs.windowevents.length > 0) {
519
			if(Array.isArray(attrs.windowevents) && attrs.windowevents.length > 0) {
494
				arr = this.addProposals(node, attrs.windowevents, params);
520
				arr = this.addProposals(node, attrs.windowevents, params);
Lines 502-508 Link Here
502
						});
528
						});
503
					proposals = proposals.concat(arr);
529
					proposals = proposals.concat(arr);
504
				}
530
				}
505
506
			}
531
			}
507
			if(Array.isArray(attrs.aria)) {
532
			if(Array.isArray(attrs.aria)) {
508
				arr = this.addProposals(node, attrs.aria, params);
533
				arr = this.addProposals(node, attrs.aria, params);
Lines 517-523 Link Here
517
					proposals = proposals.concat(arr);
542
					proposals = proposals.concat(arr);
518
				}
543
				}
519
			}
544
			}
520
			return proposals;	
545
			return proposals;
521
		},
546
		},
522
		
547
		
523
		addProposals: function addProposals(node, attrs, params) {
548
		addProposals: function addProposals(node, attrs, params) {
Lines 652-660 Link Here
652
		 * @returns {Array.<Object>} The array of proposals
677
		 * @returns {Array.<Object>} The array of proposals
653
		 * @since 10.0 
678
		 * @since 10.0 
654
		 */
679
		 */
655
		getValuesForAttribute: function(node, params) {
680
		getValuesForAttribute: function(node, source, params) {
656
			var vals = Attributes.getValuesForAttribute(node);
657
			var proposals = [];
681
			var proposals = [];
682
			var prefix = params.prefix ? params.prefix : "";
683
			// we need to check if we need to rebuild the prefix for completion that contains a '-'
684
			var index = params.offset - prefix.length - 1;
685
			if (index > 0 && index < source.length) {
686
				var precedingChar = source.charAt(index);
687
				if (precedingChar === '=' && prefix.length === 0 && (index - 1) > 0) {
688
					precedingChar = source.charAt(index - 1);
689
					if (/[A-Za-z0-9_]/.test(precedingChar)) {
690
						if (index + 1 >= source.length || 
691
								(source.charAt(index + 1) !== '\"' && source.charAt(index + 1) !== "'")) {
692
							proposals.push(this.makeComputedProposal("\"\"",  Messages['addQuotesToAttributes'], " - \"\"", null, prefix)); //$NON-NLS-1$ //$NON-NLS-2$
693
						}
694
					}
695
				}
696
			}
697
			var vals = Attributes.getValuesForAttribute(node);
658
			if(Array.isArray(vals)) {
698
			if(Array.isArray(vals)) {
659
				proposals = proposals.concat(this.addProposals(node, vals, params));
699
				proposals = proposals.concat(this.addProposals(node, vals, params));
660
			}
700
			}
Lines 703-708 Link Here
703
		isCompletingAttributeValue: function(node, source, params) {
743
		isCompletingAttributeValue: function(node, source, params) {
704
			// TODO We can do better with the new parser, handle no quotes cases too
744
			// TODO We can do better with the new parser, handle no quotes cases too
705
			if(node && node.type === 'attr') {
745
			if(node && node.type === 'attr') {
746
				if (node.valueRange) {
747
					var range = node.valueRange;
748
					return range[0] <= params.offset && range[1] >= params.offset;
749
				}
706
				return this.within('"', '"', source, params.offset, node.range) || //$NON-NLS-1$ //$NON-NLS-2$
750
				return this.within('"', '"', source, params.offset, node.range) || //$NON-NLS-1$ //$NON-NLS-2$
707
						this.within("'", "'", source, params.offset, node.range); //$NON-NLS-1$ //$NON-NLS-2$
751
						this.within("'", "'", source, params.offset, node.range); //$NON-NLS-1$ //$NON-NLS-2$
708
			}
752
			}
(-)a/bundles/org.eclipse.orion.client.webtools/web/webtools/nls/root/messages.js (+1 lines)
Lines 83-88 Link Here
83
	'keyboardeventsHeader': 'Keyboard Events',
83
	'keyboardeventsHeader': 'Keyboard Events',
84
	'mouseeventsHeader': 'Mouse Events',
84
	'mouseeventsHeader': 'Mouse Events',
85
	'windoweventsHeader': 'Window Events',
85
	'windoweventsHeader': 'Window Events',
86
	'addQuotesToAttributes': ' - Add quotes to the current attribute',
86
	
87
	
87
	//CSS content assist
88
	//CSS content assist
88
	'ruleTemplateDescription': 'rule - class selector rule',
89
	'ruleTemplateDescription': 'rule - class selector rule',

Return to bug 488586