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

JS Code Hints Crash Preventer #8155

Merged
merged 4 commits into from
Jun 19, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/extensions/default/JavaScriptCodeHints/ScopeManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -1012,7 +1012,8 @@ define(function (require, exports, module) {
type : MessageIds.TERN_INIT_MSG,
dir : dir,
files : files,
env : ternEnvironment
env : ternEnvironment,
timeout : PreferencesManager.get("jscodehints.inferenceTimeout")
};

if (config.debug) {
Expand Down
5 changes: 4 additions & 1 deletion src/extensions/default/JavaScriptCodeHints/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,10 @@ define(function (require, exports, module) {
ignoreChange; // can ignore next "change" event if true;

// Define the defaultExclusions which are files that are known to cause Tern to run out of control.
PreferencesManager.definePreference("jscodehints.defaultExclusions", "array", ["ionic*.min.js"]);
PreferencesManager.definePreference("jscodehints.defaultExclusions", "array", []);

// This preference controls when Tern will time out when trying to understand files
PreferencesManager.definePreference("jscodehints.inferenceTimeout", "number", 5000);

/**
* Sets the configuration, generally for testing/debugging use.
Expand Down
216 changes: 126 additions & 90 deletions src/extensions/default/JavaScriptCodeHints/tern-worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ var config = {};
Tern = tern;
Infer = infer;

var ternServer = null;
var ternServer = null,
inferenceTimeout;

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

/**
* Send a log message back from the worker to the main thread
* @private
* @param {string} msg - the log message
*/
function _log(msg) {
self.postMessage({log: msg });
}

/**
* Report exception
* @private
* @param {Error} e - the error object
*/
function _reportError(e, file) {
if (e instanceof Infer.TimedOut) {
_log("Timeout during Tern processing of " + file);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this sufficient communication to user?

Note that Tern caches results, so file is not processed again until edited. This works well enough that I don't know if we need to keep track of file in preferences. If we do add file to list in preferences, then user may have to learn how to remove it list.

} else {
_log("Error thrown in tern_worker:" + e.message + "\n" + e.stack);
}
}

/**
* Handle a response from the main thread providing the contents of a file
* @param {string} file - the name of the file
Expand All @@ -71,7 +94,11 @@ var config = {};
function handleGetFile(file, text) {
var next = fileCallBacks[file];
if (next) {
next(null, text);
try {
next(null, text);
} catch (e) {
_reportError(e, file);
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the try block that catches the exception, but I added the others to be safe.

}
delete fileCallBacks[file];
}
Expand Down Expand Up @@ -154,23 +181,14 @@ var config = {};
query.expandWordForward = false;
query.lineCharPositions = true;

var request = {query: query, files: [], offset: offset};
var request = {query: query, files: [], offset: offset, timeout: inferenceTimeout};
if (fileInfo.type !== MessageIds.TERN_FILE_INFO_TYPE_EMPTY) {
request.files.push(fileInfo);
}

return request;
}

/**
* Send a log message back from the worker to the main thread
*
* @param {string} msg - the log message
*/
function _log(msg) {
self.postMessage({log: msg });
}

/**
* Get definition location
* @param {{type: string, name: string, offsetLines: number, text: string}} fileInfo
Expand All @@ -184,35 +202,40 @@ var config = {};
function getJumptoDef(fileInfo, offset) {
var request = buildRequest(fileInfo, "definition", offset);
// request.query.typeOnly = true; // FIXME: tern doesn't work exactly right yet.
ternServer.request(request, function (error, data) {
if (error) {
_log("Error returned from Tern 'definition' request: " + error);
self.postMessage({type: MessageIds.TERN_JUMPTODEF_MSG, file: fileInfo.name, offset: offset});
return;
}
var isFunc = false,
response = {type: MessageIds.TERN_JUMPTODEF_MSG,
file: fileInfo.name,
resultFile: data.file,
offset: offset,
start: data.start,
end: data.end
};

request = buildRequest(fileInfo, "type", offset);
// See if we can tell if the reference is to a Function type

try {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most of the changes in this file are due to indenting code to put it in a try block, so use ?w=1 for a much simplified diff.

ternServer.request(request, function (error, data) {
if (!error) {
response.isFunction = data.type.length > 2 && data.type.substring(0, 2) === "fn";
if (error) {
_log("Error returned from Tern 'definition' request: " + error);
self.postMessage({type: MessageIds.TERN_JUMPTODEF_MSG, file: fileInfo.name, offset: offset});
return;
}

// Post a message back to the main thread with the definition
self.postMessage(response);
});
var isFunc = false,
response = {type: MessageIds.TERN_JUMPTODEF_MSG,
file: fileInfo.name,
resultFile: data.file,
offset: offset,
start: data.start,
end: data.end
};

request = buildRequest(fileInfo, "type", offset);
// See if we can tell if the reference is to a Function type
ternServer.request(request, function (error, data) {
if (!error) {
response.isFunction = data.type.length > 2 && data.type.substring(0, 2) === "fn";
}

});
// Post a message back to the main thread with the definition
self.postMessage(response);
});

});
} catch (e) {
_reportError(e, fileInfo.name);
}
}

/**
* Get all the known properties for guessing.
*
Expand All @@ -230,25 +253,29 @@ var config = {};
var request = buildRequest(fileInfo, "properties", offset),
i;
//_log("tern properties: request " + request.type + dir + " " + file);
ternServer.request(request, function (error, data) {
var properties = [];
if (error) {
_log("Error returned from Tern 'properties' request: " + error);
} else {
//_log("tern properties: completions = " + data.completions.length);
for (i = 0; i < data.completions.length; ++i) {
var property = data.completions[i];
properties.push({value: property, type: property.type, guess: true});
try {
ternServer.request(request, function (error, data) {
var properties = [];
if (error) {
_log("Error returned from Tern 'properties' request: " + error);
} else {
//_log("tern properties: completions = " + data.completions.length);
for (i = 0; i < data.completions.length; ++i) {
var property = data.completions[i];
properties.push({value: property, type: property.type, guess: true});
}
}
}

// Post a message back to the main thread with the completions
self.postMessage({type: type,
file: fileInfo.name,
offset: offset,
properties: properties
});
});

// Post a message back to the main thread with the completions
self.postMessage({type: type,
file: fileInfo.name,
offset: offset,
properties: properties
});
});
} catch (e) {
_reportError(e, fileInfo.name);
}
}

/**
Expand All @@ -270,31 +297,35 @@ var config = {};
i;

//_log("request " + dir + " " + file + " " + offset /*+ " " + text */);
ternServer.request(request, function (error, data) {
var completions = [];
if (error) {
_log("Error returned from Tern 'completions' request: " + error);
} else {
//_log("found " + data.completions.length + " for " + file + "@" + offset);
for (i = 0; i < data.completions.length; ++i) {
var completion = data.completions[i];
completions.push({value: completion.name, type: completion.type, depth: completion.depth,
guess: completion.guess, origin: completion.origin});
try {
ternServer.request(request, function (error, data) {
var completions = [];
if (error) {
_log("Error returned from Tern 'completions' request: " + error);
} else {
//_log("found " + data.completions.length + " for " + file + "@" + offset);
for (i = 0; i < data.completions.length; ++i) {
var completion = data.completions[i];
completions.push({value: completion.name, type: completion.type, depth: completion.depth,
guess: completion.guess, origin: completion.origin});
}
}
}

if (completions.length > 0 || !isProperty) {
// Post a message back to the main thread with the completions
self.postMessage({type: MessageIds.TERN_COMPLETIONS_MSG,
file: fileInfo.name,
offset: offset,
completions: completions
});
} else {
// if there are no completions, then get all the properties
getTernProperties(fileInfo, offset, MessageIds.TERN_COMPLETIONS_MSG);
}
});

if (completions.length > 0 || !isProperty) {
// Post a message back to the main thread with the completions
self.postMessage({type: MessageIds.TERN_COMPLETIONS_MSG,
file: fileInfo.name,
offset: offset,
completions: completions
});
} else {
// if there are no completions, then get all the properties
getTernProperties(fileInfo, offset, MessageIds.TERN_COMPLETIONS_MSG);
}
});
} catch (e) {
_reportError(e, fileInfo.name);
}
}

/**
Expand Down Expand Up @@ -512,8 +543,7 @@ var config = {};
}
});
} catch (e) {
error = e.message;
_log("Error thrown in tern_worker:" + error + "\n" + e.stack);
_reportError(e, fileInfo.name);
}

// Post a message back to the main thread with the completions
Expand Down Expand Up @@ -561,15 +591,19 @@ var config = {};
* @param {string} path - the path of the file
*/
function handlePrimePump(path) {
var fileInfo = createEmptyUpdate(path);
var request = buildRequest(fileInfo, "completions", {line: 0, ch: 0});

ternServer.request(request, function (error, data) {
// Post a message back to the main thread
self.postMessage({type: MessageIds.TERN_PRIME_PUMP_MSG,
path: path
});
});
var fileInfo = createEmptyUpdate(path),
request = buildRequest(fileInfo, "completions", {line: 0, ch: 0});

try {
ternServer.request(request, function (error, data) {
// Post a message back to the main thread
self.postMessage({type: MessageIds.TERN_PRIME_PUMP_MSG,
path: path
});
});
} catch (e) {
_reportError(e, path);
}
}

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

var env = request.env,
files = request.files;
inferenceTimeout = request.timeout;

initTernServer(env, files);
} else if (type === MessageIds.TERN_COMPLETIONS_MSG) {
offset = request.offset;
Expand Down
Submodule acorn updated from b1623b to 443501
2 changes: 1 addition & 1 deletion src/extensions/default/JavaScriptCodeHints/thirdparty/tern
Submodule tern updated from c7df88 to ed952d
7 changes: 4 additions & 3 deletions src/extensions/default/JavaScriptCodeHints/unittests.js
Original file line number Diff line number Diff line change
Expand Up @@ -841,10 +841,11 @@ define(function (require, exports, module) {
});

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

testDoc.replaceRange("param", start, start);
testEditor.setCursorPos(start);
testDoc.replaceRange("paramB", cursor1, cursor1);
testEditor.setCursorPos(cursor2);
var hintObj = expectHints(JSCodeHints.jsHintProvider);
runs(function () {
hintsPresentExact(hintObj, ["paramB1", "paramB2"]);
Expand Down