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 426399
Collapse All | Expand All

(-)a/bundles/org.eclipse.orion.client.javascript/web/javascript/astManager.js (-30 / +46 lines)
Lines 1-6 Link Here
1
/*******************************************************************************
1
/*******************************************************************************
2
 * @license
2
 * @license
3
 * Copyright (c) 2013 IBM Corporation and others.
3
 * Copyright (c) 2013, 2014 IBM Corporation and others.
4
 * All rights reserved. This program and the accompanying materials are made 
4
 * All rights reserved. This program and the accompanying materials are made 
5
 * available under the terms of the Eclipse Public License v1.0 
5
 * available under the terms of the Eclipse Public License v1.0 
6
 * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
6
 * (http://www.eclipse.org/legal/epl-v10.html), and the Eclipse Distribution 
Lines 15-21 Link Here
15
	'orion/Deferred',
15
	'orion/Deferred',
16
	'orion/objects',
16
	'orion/objects',
17
	'orion/serialize'
17
	'orion/serialize'
18
], function(_, Deferred, objects, Serialize) {
18
], function(Esprima, Deferred, Objects, Serialize) {
19
	/**
20
	 * @description Object of error types
21
	 * @since 5.0
22
	 */
23
	var ErrorTypes = {
24
		/**
25
		 * @description Something unexpected has been found while parsing, most commonly a syntax error
26
		 */
27
		Unexpected: 1,
28
		/**
29
		 * @description A Syntax problem that reports the last entered token as the problem
30
		 */
31
		EndOfInput: 2
32
	};
19
33
20
	/**
34
	/**
21
	 * Provides a shared AST.
35
	 * Provides a shared AST.
Lines 25-50 Link Here
25
	function ASTManager() {
39
	function ASTManager() {
26
		this.cache = null;
40
		this.cache = null;
27
	}
41
	}
28
	function emptyAST(text) {
42
	
29
		var charCount = (text && typeof text.length === "number") ? text.length : 0;
43
	Objects.mixin(ASTManager.prototype, /** @lends javascript.ASTManager.prototype */ {
30
		return {
31
			type: "Program", //$NON-NLS-0$
32
			body: [],
33
			comments: [],
34
			tokens: [],
35
			range: [0, charCount]
36
		};
37
	}
38
	objects.mixin(ASTManager.prototype, /** @lends javascript.ASTManager.prototype */ {
39
		/**
40
		 * @description Object of error types
41
		 */
42
		ErrorTypes: {
43
			/**
44
			 * @description Something unexpected has been found while parsing, most commonly a syntax error
45
			 */
46
			Unexpected: "unexp"	
47
		},
48
		/**
44
		/**
49
		 * @param {Object} editorContext
45
		 * @param {Object} editorContext
50
		 * @returns {orion.Promise} A promise resolving to the AST.
46
		 * @returns {orion.Promise} A promise resolving to the AST.
Lines 74-82 Link Here
74
					tokens: true
70
					tokens: true
75
				});
71
				});
76
			} catch (e) {
72
			} catch (e) {
77
				// The "tolerant" esprima sometimes blows up from parse errors in initial statements of code.
73
				// The "tolerant" Esprima sometimes blows up from parse errors in initial statements of code.
78
				// Just return an empty AST with the parse error.
74
				// Just return an empty AST with the parse error.
79
				ast = emptyAST(text);
75
				ast = this._emptyAST(text);
80
				ast.errors = [e];
76
				ast.errors = [e];
81
			}
77
			}
82
			if (ast.errors) {
78
			if (ast.errors) {
Lines 84-89 Link Here
84
				ast.errors = ast.errors.map(Serialize.serializeError);
80
				ast.errors = ast.errors.map(Serialize.serializeError);
85
			}
81
			}
86
			return ast;
82
			return ast;
83
		},
84
		/**
85
		 * @description Returns an empty AST in the event a parse failed with a thrown exception
86
		 * @function
87
		 * @private
88
		 * @param {String} text The text that failed to parse
89
		 * @returns {Object} A new, empty AST object
90
		 */
91
		_emptyAST: function(text) {
92
			var charCount = (text && typeof text.length === "number") ? text.length : 0;
93
			return {
94
				type: "Program", //$NON-NLS-0$
95
				body: [],
96
				comments: [],
97
				tokens: [],
98
				range: [0, charCount]
99
			};
87
		},
100
		},
88
		/**
101
		/**
89
		 * @description Computes the problem type from the error and sets a 'type' property
102
		 * @description Computes the problem type from the error and sets a 'type' property
Lines 96-106 Link Here
96
			if(errors && Array.isArray(errors)) {
109
			if(errors && Array.isArray(errors)) {
97
				errors.forEach(function(error) {
110
				errors.forEach(function(error) {
98
					var msg = error.message;
111
					var msg = error.message;
99
					if(msg) {
112
					//first sanitize it
100
						if(msg.indexOf('token') > -1 ||	msg.indexOf('identifier') > -1 || 
113
					error.message = msg = msg.replace(/^Line \d+: /, '');
101
							msg.indexOf('string') > -1 || msg.indexOf('number') > -1) {
114
					if(/^Unexpected/.test(msg)) {
102
							error.type = 'unexp';
115
						error.type = ErrorTypes.Unexpected;
103
							return;
116
						if(/end of input$/.test(msg)) {
117
							error.type = ErrorTypes.EndOfInput;
104
						}
118
						}
105
					}
119
					}
106
				});
120
				});
Lines 114-118 Link Here
114
			this.cache = null;
128
			this.cache = null;
115
		}
129
		}
116
	});
130
	});
117
	return ASTManager;
131
	return {
132
			ASTManager : ASTManager,
133
			ErrorTypes : ErrorTypes};
118
});
134
});
(-)a/bundles/org.eclipse.orion.client.javascript/web/javascript/eslint/validator.js (-19 / +70 lines)
Lines 12-19 Link Here
12
/*global define*/
12
/*global define*/
13
define([
13
define([
14
	"eslint",
14
	"eslint",
15
	"orion/objects"
15
	"orion/objects",
16
], function(eslint, objects) {
16
	"orion/Deferred",
17
	"javascript/astManager"
18
], function(eslint, Objects, Deferred, ASTManager) {
17
	// Should have a better way of keeping this up-to-date with ./load-rules-async.js
19
	// Should have a better way of keeping this up-to-date with ./load-rules-async.js
18
	var config = {
20
	var config = {
19
		// 0:off, 1:warning, 2:error
21
		// 0:off, 1:warning, 2:error
Lines 27-32 Link Here
27
			"missing-func-decl-doc": [0, 'decl'], //$NON-NLS-0$ //$NON-NLS-1$
29
			"missing-func-decl-doc": [0, 'decl'], //$NON-NLS-0$ //$NON-NLS-1$
28
			"missing-func-expr-doc": [0, 'expr'] //$NON-NLS-0$ //$NON-NLS-1$
30
			"missing-func-expr-doc": [0, 'expr'] //$NON-NLS-0$ //$NON-NLS-1$
29
		},
31
		},
32
		/**
33
		 * @description Sets the given rule to the given enabled value
34
		 * @function
35
		 * @private
36
		 * @param {String} ruleId The id of the rule to change
37
		 * @param {Number} value The vlaue to set the rule to
38
		 */
30
		setOption: function(ruleId, value) {
39
		setOption: function(ruleId, value) {
31
			if (typeof value === "number") {
40
			if (typeof value === "number") {
32
				if(Array.isArray(this.rules[ruleId])) {
41
				if(Array.isArray(this.rules[ruleId])) {
Lines 43-48 Link Here
43
	 * @description Creates a new ESLintValidator
52
	 * @description Creates a new ESLintValidator
44
	 * @constructor
53
	 * @constructor
45
	 * @public
54
	 * @public
55
	 * @param {javascript.ASTManager} astManager The AST manager backing this validator
46
	 * @returns {ESLintValidator} Returns a new validator
56
	 * @returns {ESLintValidator} Returns a new validator
47
	 */
57
	 */
48
	function ESLintValidator(astManager) {
58
	function ESLintValidator(astManager) {
Lines 91-96 Link Here
91
		} else if (typeof e.index === "number") { //$NON-NLS-0$
101
		} else if (typeof e.index === "number") { //$NON-NLS-0$
92
			// Esprima parse error
102
			// Esprima parse error
93
			start = e.index;
103
			start = e.index;
104
			end = e.end ? e.end : start;
94
		}
105
		}
95
		var prob = {
106
		var prob = {
96
			description: e.message,
107
			description: e.message,
Lines 101-130 Link Here
101
		return prob;
112
		return prob;
102
	}
113
	}
103
114
104
	objects.mixin(ESLintValidator.prototype, {
115
	Objects.mixin(ESLintValidator.prototype, {
105
		/**
116
		/**
106
		 * @descritpion Extracts any errors captured by the tolerant esprima parser and returns them
117
		 * @descritpion Extracts any errors captured by the tolerant esprima parser and returns them
107
		 * @function
118
		 * @function
108
		 * @private
119
		 * @private
109
		 * @param {esprima.ASTNode} ast The AST
120
		 * @param {esprima.AST} ast The AST
121
		 * @param {String} buffer the text form the editor
122
		 * @param {Array} asterrors The parse problems in the AST
110
		 * @returns {esprima.Error[]} The array of AST errors (if any)
123
		 * @returns {esprima.Error[]} The array of AST errors (if any)
111
		 */
124
		 */
112
		_extractParseErrors: function(ast) {
125
		_extractParseErrors: function(ast, buffer, offsets) {
113
			var errors = [], errorMap = Object.create(null);
126
			var errors = [], errorMap = Object.create(null);
114
			(ast.errors || []).forEach(function(error) {
127
			var asterrors = ast.errors;
115
				var msg = error.message, match;
128
			if(asterrors) {
116
				// Errors come as 'Line nn: Unexpected foo'. Strip off the first part
129
				var len = asterrors.length;
117
				if ((match = /^Line \d+: /.exec(msg))) {
130
				for(var i = 0; i < len; i++) {
118
					error.message = msg = "Parse error: " + msg.substring(match.index + match[0].length) + ".";
131
					var error = asterrors[i];
132
					var msg = error.message;
133
					if(errorMap[error.index] === msg) {
134
						continue;
135
					}
136
					errorMap[error.index] = msg;
137
					if(error.type) {
138
						switch(error.type) {
139
							case ASTManager.ErrorTypes.Unexpected:
140
								var token = buffer.substring(error.index, offsets[i] + error.column);
141
								error.message = msg = "Syntax error on token '"+token+"', delete this token.";
142
								error.end = offsets[i] + error.column;
143
								break;
144
							case ASTManager.ErrorTypes.EndOfInput:
145
								error.message = "Syntax error, incomplete statement.";
146
								var text = buffer.substring(offsets[i], error.index).replace(/\s\s*$/, '');
147
								//tag the last char, maybe end of word, maybe bad punctuation
148
								error.index = offsets[i] + text.length-1;
149
								error.end = offsets[i] + text.length;
150
								break;
151
						}
152
					}
153
					errors.push(error);
119
				}
154
				}
120
				// Hack to filter out duplicate error produced by our esprima, having same index and message as previous error.
155
			}
121
				if (errorMap[error.index] === msg) {
122
					return;
123
				}
124
				errorMap[error.index] = msg;
125
				errors.push(error);
126
			});
127
			return errors;
156
			return errors;
157
		},
158
		/**
159
		 * @description Computes the array of line start promises for parser errors
160
		 * @function
161
		 * @private
162
		 * @param {orin.editor.EditorContext} editorContext The backing editor context
163
		 * @param {Array} errors The array of parser errors
164
		 * @returns {Array|orion.Promise} Returns the array of line start promises
165
		 */
166
		_getLineStartPromises: function(editorContext, errors) {
167
			var promises = [];
168
			(errors || []).forEach(function(error) {
169
				if(error.lineNumber) {
170
					promises.push(editorContext.getLineStart(error.lineNumber-1));
171
				}
172
			});
173
			return promises;
128
		},
174
		},
129
		/**
175
		/**
130
		 * @descripion Callback to create problems from orion.edit.validator
176
		 * @descripion Callback to create problems from orion.edit.validator
Lines 137-149 Link Here
137
		computeProblems: function(editorContext, context) {
183
		computeProblems: function(editorContext, context) {
138
			var _self = this;
184
			var _self = this;
139
			return this.astManager.getAST(editorContext).then(function(ast) {
185
			return this.astManager.getAST(editorContext).then(function(ast) {
186
				var deferred = new Deferred();
187
				var promises = [deferred.resolve(ast), editorContext.getText()]
188
								.concat(_self._getLineStartPromises(editorContext, ast.errors));
189
				return new Deferred.all(promises);
190
			}).then(function(results) {
140
				var eslintErrors = [], error;
191
				var eslintErrors = [], error;
141
				try {
192
				try {
142
					eslintErrors = eslint.verify(ast, config);
193
					eslintErrors = eslint.verify(results[0], config);
143
				} catch (e) {
194
				} catch (e) {
144
					error = e;
195
					error = e;
145
				}
196
				}
146
				var parseErrors = _self._extractParseErrors(ast);
197
				var parseErrors = _self._extractParseErrors(results[0], results[1], results.splice(2, results.length));
147
				var problems = []
198
				var problems = []
148
					.concat(eslintErrors)
199
					.concat(eslintErrors)
149
					.concat(parseErrors)
200
					.concat(parseErrors)
(-)a/bundles/org.eclipse.orion.client.javascript/web/javascript/plugins/javascriptPlugin.js (-1 / +1 lines)
Lines 60-66 Link Here
60
	/**
60
	/**
61
	 * Create the AST manager
61
	 * Create the AST manager
62
	 */
62
	 */
63
	var astManager = new ASTManager();
63
	var astManager = new ASTManager.ASTManager();
64
64
65
	/**
65
	/**
66
	 * Register AST manager as Model Change listener
66
	 * Register AST manager as Model Change listener
(-)a/bundles/org.eclipse.orion.client.javascript/web/js-tests/javascript/astManagerTests.js (-1 / +1 lines)
Lines 39-45 Link Here
39
				return this.ast;
39
				return this.ast;
40
			}
40
			}
41
		};
41
		};
42
		var astManager = new ASTManager();
42
		var astManager = new ASTManager.ASTManager();
43
		return {
43
		return {
44
			astManager: astManager,
44
			astManager: astManager,
45
			editorContext: mockEditorContext,
45
			editorContext: mockEditorContext,

Return to bug 426399