Skip to content
This repository was archived by the owner on Sep 6, 2021. It is now read-only.

Commit 0d3603e

Browse files
committed
Merge pull request #8155 from adobe/randy/update-tern
JS Code Hints Crash Preventer
2 parents c6c6ccf + 51939a5 commit 0d3603e

File tree

6 files changed

+138
-97
lines changed

6 files changed

+138
-97
lines changed

src/extensions/default/JavaScriptCodeHints/ScopeManager.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1008,7 +1008,8 @@ define(function (require, exports, module) {
10081008
type : MessageIds.TERN_INIT_MSG,
10091009
dir : dir,
10101010
files : files,
1011-
env : ternEnvironment
1011+
env : ternEnvironment,
1012+
timeout : PreferencesManager.get("jscodehints.inferenceTimeout")
10121013
};
10131014

10141015
if (config.debug) {

src/extensions/default/JavaScriptCodeHints/main.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,10 @@ define(function (require, exports, module) {
5757
ignoreChange; // can ignore next "change" event if true;
5858

5959
// Define the defaultExclusions which are files that are known to cause Tern to run out of control.
60-
PreferencesManager.definePreference("jscodehints.defaultExclusions", "array", ["ionic*.min.js"]);
60+
PreferencesManager.definePreference("jscodehints.defaultExclusions", "array", []);
61+
62+
// This preference controls when Tern will time out when trying to understand files
63+
PreferencesManager.definePreference("jscodehints.inferenceTimeout", "number", 5000);
6164

6265
/**
6366
* Sets the configuration, generally for testing/debugging use.

src/extensions/default/JavaScriptCodeHints/tern-worker.js

Lines changed: 126 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@ var config = {};
4141
Tern = tern;
4242
Infer = infer;
4343

44-
var ternServer = null;
44+
var ternServer = null,
45+
inferenceTimeout;
4546

4647
// Save the tern callbacks for when we get the contents of the file
4748
var fileCallBacks = {};
@@ -63,6 +64,28 @@ var config = {};
6364
});
6465
}
6566

67+
/**
68+
* Send a log message back from the worker to the main thread
69+
* @private
70+
* @param {string} msg - the log message
71+
*/
72+
function _log(msg) {
73+
self.postMessage({log: msg });
74+
}
75+
76+
/**
77+
* Report exception
78+
* @private
79+
* @param {Error} e - the error object
80+
*/
81+
function _reportError(e, file) {
82+
if (e instanceof Infer.TimedOut) {
83+
_log("Timeout during Tern processing of " + file);
84+
} else {
85+
_log("Error thrown in tern_worker:" + e.message + "\n" + e.stack);
86+
}
87+
}
88+
6689
/**
6790
* Handle a response from the main thread providing the contents of a file
6891
* @param {string} file - the name of the file
@@ -71,7 +94,11 @@ var config = {};
7194
function handleGetFile(file, text) {
7295
var next = fileCallBacks[file];
7396
if (next) {
74-
next(null, text);
97+
try {
98+
next(null, text);
99+
} catch (e) {
100+
_reportError(e, file);
101+
}
75102
}
76103
delete fileCallBacks[file];
77104
}
@@ -154,23 +181,14 @@ var config = {};
154181
query.expandWordForward = false;
155182
query.lineCharPositions = true;
156183

157-
var request = {query: query, files: [], offset: offset};
184+
var request = {query: query, files: [], offset: offset, timeout: inferenceTimeout};
158185
if (fileInfo.type !== MessageIds.TERN_FILE_INFO_TYPE_EMPTY) {
159186
request.files.push(fileInfo);
160187
}
161188

162189
return request;
163190
}
164191

165-
/**
166-
* Send a log message back from the worker to the main thread
167-
*
168-
* @param {string} msg - the log message
169-
*/
170-
function _log(msg) {
171-
self.postMessage({log: msg });
172-
}
173-
174192
/**
175193
* Get definition location
176194
* @param {{type: string, name: string, offsetLines: number, text: string}} fileInfo
@@ -184,35 +202,40 @@ var config = {};
184202
function getJumptoDef(fileInfo, offset) {
185203
var request = buildRequest(fileInfo, "definition", offset);
186204
// request.query.typeOnly = true; // FIXME: tern doesn't work exactly right yet.
187-
ternServer.request(request, function (error, data) {
188-
if (error) {
189-
_log("Error returned from Tern 'definition' request: " + error);
190-
self.postMessage({type: MessageIds.TERN_JUMPTODEF_MSG, file: fileInfo.name, offset: offset});
191-
return;
192-
}
193-
var isFunc = false,
194-
response = {type: MessageIds.TERN_JUMPTODEF_MSG,
195-
file: fileInfo.name,
196-
resultFile: data.file,
197-
offset: offset,
198-
start: data.start,
199-
end: data.end
200-
};
201-
202-
request = buildRequest(fileInfo, "type", offset);
203-
// See if we can tell if the reference is to a Function type
205+
206+
try {
204207
ternServer.request(request, function (error, data) {
205-
if (!error) {
206-
response.isFunction = data.type.length > 2 && data.type.substring(0, 2) === "fn";
208+
if (error) {
209+
_log("Error returned from Tern 'definition' request: " + error);
210+
self.postMessage({type: MessageIds.TERN_JUMPTODEF_MSG, file: fileInfo.name, offset: offset});
211+
return;
207212
}
208-
209-
// Post a message back to the main thread with the definition
210-
self.postMessage(response);
211-
});
213+
var isFunc = false,
214+
response = {type: MessageIds.TERN_JUMPTODEF_MSG,
215+
file: fileInfo.name,
216+
resultFile: data.file,
217+
offset: offset,
218+
start: data.start,
219+
end: data.end
220+
};
221+
222+
request = buildRequest(fileInfo, "type", offset);
223+
// See if we can tell if the reference is to a Function type
224+
ternServer.request(request, function (error, data) {
225+
if (!error) {
226+
response.isFunction = data.type.length > 2 && data.type.substring(0, 2) === "fn";
227+
}
212228

213-
});
229+
// Post a message back to the main thread with the definition
230+
self.postMessage(response);
231+
});
232+
233+
});
234+
} catch (e) {
235+
_reportError(e, fileInfo.name);
236+
}
214237
}
215-
238+
216239
/**
217240
* Get all the known properties for guessing.
218241
*
@@ -230,25 +253,29 @@ var config = {};
230253
var request = buildRequest(fileInfo, "properties", offset),
231254
i;
232255
//_log("tern properties: request " + request.type + dir + " " + file);
233-
ternServer.request(request, function (error, data) {
234-
var properties = [];
235-
if (error) {
236-
_log("Error returned from Tern 'properties' request: " + error);
237-
} else {
238-
//_log("tern properties: completions = " + data.completions.length);
239-
for (i = 0; i < data.completions.length; ++i) {
240-
var property = data.completions[i];
241-
properties.push({value: property, type: property.type, guess: true});
256+
try {
257+
ternServer.request(request, function (error, data) {
258+
var properties = [];
259+
if (error) {
260+
_log("Error returned from Tern 'properties' request: " + error);
261+
} else {
262+
//_log("tern properties: completions = " + data.completions.length);
263+
for (i = 0; i < data.completions.length; ++i) {
264+
var property = data.completions[i];
265+
properties.push({value: property, type: property.type, guess: true});
266+
}
242267
}
243-
}
244-
245-
// Post a message back to the main thread with the completions
246-
self.postMessage({type: type,
247-
file: fileInfo.name,
248-
offset: offset,
249-
properties: properties
250-
});
251-
});
268+
269+
// Post a message back to the main thread with the completions
270+
self.postMessage({type: type,
271+
file: fileInfo.name,
272+
offset: offset,
273+
properties: properties
274+
});
275+
});
276+
} catch (e) {
277+
_reportError(e, fileInfo.name);
278+
}
252279
}
253280

254281
/**
@@ -270,31 +297,35 @@ var config = {};
270297
i;
271298

272299
//_log("request " + dir + " " + file + " " + offset /*+ " " + text */);
273-
ternServer.request(request, function (error, data) {
274-
var completions = [];
275-
if (error) {
276-
_log("Error returned from Tern 'completions' request: " + error);
277-
} else {
278-
//_log("found " + data.completions.length + " for " + file + "@" + offset);
279-
for (i = 0; i < data.completions.length; ++i) {
280-
var completion = data.completions[i];
281-
completions.push({value: completion.name, type: completion.type, depth: completion.depth,
282-
guess: completion.guess, origin: completion.origin});
300+
try {
301+
ternServer.request(request, function (error, data) {
302+
var completions = [];
303+
if (error) {
304+
_log("Error returned from Tern 'completions' request: " + error);
305+
} else {
306+
//_log("found " + data.completions.length + " for " + file + "@" + offset);
307+
for (i = 0; i < data.completions.length; ++i) {
308+
var completion = data.completions[i];
309+
completions.push({value: completion.name, type: completion.type, depth: completion.depth,
310+
guess: completion.guess, origin: completion.origin});
311+
}
283312
}
284-
}
285-
286-
if (completions.length > 0 || !isProperty) {
287-
// Post a message back to the main thread with the completions
288-
self.postMessage({type: MessageIds.TERN_COMPLETIONS_MSG,
289-
file: fileInfo.name,
290-
offset: offset,
291-
completions: completions
292-
});
293-
} else {
294-
// if there are no completions, then get all the properties
295-
getTernProperties(fileInfo, offset, MessageIds.TERN_COMPLETIONS_MSG);
296-
}
297-
});
313+
314+
if (completions.length > 0 || !isProperty) {
315+
// Post a message back to the main thread with the completions
316+
self.postMessage({type: MessageIds.TERN_COMPLETIONS_MSG,
317+
file: fileInfo.name,
318+
offset: offset,
319+
completions: completions
320+
});
321+
} else {
322+
// if there are no completions, then get all the properties
323+
getTernProperties(fileInfo, offset, MessageIds.TERN_COMPLETIONS_MSG);
324+
}
325+
});
326+
} catch (e) {
327+
_reportError(e, fileInfo.name);
328+
}
298329
}
299330

300331
/**
@@ -512,8 +543,7 @@ var config = {};
512543
}
513544
});
514545
} catch (e) {
515-
error = e.message;
516-
_log("Error thrown in tern_worker:" + error + "\n" + e.stack);
546+
_reportError(e, fileInfo.name);
517547
}
518548

519549
// Post a message back to the main thread with the completions
@@ -561,15 +591,19 @@ var config = {};
561591
* @param {string} path - the path of the file
562592
*/
563593
function handlePrimePump(path) {
564-
var fileInfo = createEmptyUpdate(path);
565-
var request = buildRequest(fileInfo, "completions", {line: 0, ch: 0});
566-
567-
ternServer.request(request, function (error, data) {
568-
// Post a message back to the main thread
569-
self.postMessage({type: MessageIds.TERN_PRIME_PUMP_MSG,
570-
path: path
571-
});
572-
});
594+
var fileInfo = createEmptyUpdate(path),
595+
request = buildRequest(fileInfo, "completions", {line: 0, ch: 0});
596+
597+
try {
598+
ternServer.request(request, function (error, data) {
599+
// Post a message back to the main thread
600+
self.postMessage({type: MessageIds.TERN_PRIME_PUMP_MSG,
601+
path: path
602+
});
603+
});
604+
} catch (e) {
605+
_reportError(e, path);
606+
}
573607
}
574608

575609
/**
@@ -594,6 +628,8 @@ var config = {};
594628

595629
var env = request.env,
596630
files = request.files;
631+
inferenceTimeout = request.timeout;
632+
597633
initTernServer(env, files);
598634
} else if (type === MessageIds.TERN_COMPLETIONS_MSG) {
599635
offset = request.offset;

src/extensions/default/JavaScriptCodeHints/unittests.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -841,10 +841,11 @@ define(function (require, exports, module) {
841841
});
842842

843843
it("should list matching property names", function () {
844-
var start = { line: 12, ch: 10 };
844+
var cursor1 = { line: 12, ch: 0 },
845+
cursor2 = { line: 12, ch: 6 };
845846

846-
testDoc.replaceRange("param", start, start);
847-
testEditor.setCursorPos(start);
847+
testDoc.replaceRange("paramB", cursor1, cursor1);
848+
testEditor.setCursorPos(cursor2);
848849
var hintObj = expectHints(JSCodeHints.jsHintProvider);
849850
runs(function () {
850851
hintsPresentExact(hintObj, ["paramB1", "paramB2"]);

0 commit comments

Comments
 (0)