Skip to content
This repository was archived by the owner on Sep 6, 2021. It is now read-only.
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
49 changes: 36 additions & 13 deletions src/extensions/default/JavaScriptCodeHints/ScopeManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ define(function (require, exports, module) {
NativeFileSystem = brackets.getModule("file/NativeFileSystem").NativeFileSystem,
ProjectManager = brackets.getModule("project/ProjectManager"),
CollectionUtils = brackets.getModule("utils/CollectionUtils"),
ExtensionUtils = brackets.getModule("utils/ExtensionUtils"),
FileUtils = brackets.getModule("file/FileUtils"),
HintUtils = require("HintUtils");

var ternEnvironment = [],
Expand All @@ -50,8 +52,8 @@ define(function (require, exports, module) {
ternPromise = null,
resolvedFiles = {}, // file -> resolved file
_ternWorker = (function () {
var path = module.uri.substring(0, module.uri.lastIndexOf("/") + 1);
return new Worker(path + "tern-worker.js");
var path = ExtensionUtils.getModulePath(module, "tern-worker.js");
return new Worker(path);
}());

var MAX_TEXT_LENGTH = 1000000, // about 1MB
Expand Down Expand Up @@ -83,16 +85,20 @@ define(function (require, exports, module) {
* Read in the json files that have type information for the builtins, dom,etc
*/
function initTernEnv() {
var path = module.uri.substring(0, module.uri.lastIndexOf("/") + 1) + "thirdparty/tern/defs/",
var path = ExtensionUtils.getModulePath(module, "thirdparty/tern/defs/"),
files = builtinFiles,
library;

files.forEach(function (i) {
DocumentManager.getDocumentForPath(path + i).done(function (document) {
library = JSON.parse(document.getText());
builtinLibraryNames.push(library["!name"]);
ternEnvironment.push(library);
}).fail(function (error) {
NativeFileSystem.resolveNativeFileSystemPath(path + i, function (fileEntry) {
FileUtils.readAsText(fileEntry).done(function (text) {
library = JSON.parse(text);
builtinLibraryNames.push(library["!name"]);
ternEnvironment.push(library);
}).fail(function (error) {
console.log("failed to read tern config file " + i);
});
}, function (error) {
console.log("failed to read tern config file " + i);
});
});
Expand Down Expand Up @@ -172,21 +178,22 @@ define(function (require, exports, module) {
* Add a pending request waiting for the tern-worker to complete.
*
* @param {string} file - the name of the file
* @param {number} offset - the offset into the file the request is for
* @param {string} type - the type of request
* @param {jQuery.Deferred} deferredRequest - the $.Deferred object to save
* @return {jQuery.Promise} - the promise for the request
*/
function addPendingRequest(file, offset, type) {
var requests,
key = file + "@" + offset,
$deferredRequest;
if (Object.prototype.hasOwnProperty.call(pendingTernRequests, key)) {
if (CollectionUtils.hasProperty(pendingTernRequests, key)) {
requests = pendingTernRequests[key];
} else {
requests = {};
pendingTernRequests[key] = requests;
}

if (Object.prototype.hasOwnProperty.call(requests, type)) {
if (CollectionUtils.hasProperty(requests, type)) {
$deferredRequest = requests[type];
} else {
requests[type] = $deferredRequest = $.Deferred();
Expand All @@ -196,6 +203,10 @@ define(function (require, exports, module) {

/**
* Get a Promise for the completions from TernJS, for the file & offset passed in.
* @param {string} dir - the directory the file is in
* @param {string} file - the name of the file
* @param {number} offset - the offset in the file the hints should be calculate at
* @param {string} text - the text of the file
* @return {jQuery.Promise} - a promise that will resolve to an array of completions when
* it is done
*/
Expand All @@ -214,7 +225,10 @@ define(function (require, exports, module) {
/**
* Get a Promise for all of the known properties from TernJS, for the directory and file.
* The properties will be used as guesses in tern.
*
* @param {string} dir - the directory the file is in
* @param {string} file - the name of the file
* @param {number} offset - the offset in the file the hints should be calculate at
* @param {string} text - the text of the file
* @return {jQuery.Promise} - a promise that will resolve to an array of properties when
* it is done
*/
Expand All @@ -232,6 +246,13 @@ define(function (require, exports, module) {

/**
* Get a Promise for the function type from TernJS.
* @param {string} dir - the directory the file is in
* @param {string} file - the name of the file
* @param {{line:number, ch:number}} pos - the line, column info for what we want the function type of.
* Unfortunately tern requires line/col for this request instead of offset, but we cache all the request
* promises by file & offset, so we need the pos and offset for this method
* @param {number} offset - the offset in the file the hints should be calculate at
* @param {string} text - the text of the file
* @return {jQuery.Promise} - a promise that will resolve to the function type of the function being called.
*/
function getTernFunctionType(dir, file, pos, offset, text) {
Expand All @@ -256,6 +277,7 @@ define(function (require, exports, module) {
* on some temporary context) should copy them first. See, e.g.,
* Session.getHints().
*
* @param {Session} session - the active hinting session
* @param {Document} document - the document for which scope info is
* desired
* @param {number} offset - the offset into the document at which scope
Expand Down Expand Up @@ -307,8 +329,9 @@ define(function (require, exports, module) {
/**
* Get any pending $.Deferred object waiting on the specified file and request type
* @param {string} file - the file
* @param {number} offset - the offset in the file the request was at
* @param {string} type - the type of request
* @param {jQuery.Deferred} - the $.Deferred for the request
* @return {jQuery.Deferred} - the $.Deferred for the request
*/
function getPendingRequest(file, offset, type) {
var key = file + "@" + offset;
Expand Down
12 changes: 12 additions & 0 deletions src/extensions/default/JavaScriptCodeHints/Session.js
Original file line number Diff line number Diff line change
Expand Up @@ -244,13 +244,25 @@ define(function (require, exports, module) {
token = this.getToken(cursor);

if (token) {
// if this token is part of a function call, then the tokens lexical info
// will be annotated with "call"
if (token.state.lexical.info === "call") {
inFunctionCall = true;
if (this.getQuery().length > 0) {
inFunctionCall = false;
showFunctionType = false;
} else {
showFunctionType = true;
// we need to find the location of the called function so that we can request the functions type.
// the token's lexical info will contain the column where the open "(" for the
// function call occurrs, but for whatever reason it does not have the line, so
// we have to walk back and try to find the correct location. We do this by walking
// up the lines starting with the line the token is on, and seeing if any of the lines
// have "(" at the column indicated by the tokens lexical state.
// We walk back 9 lines, as that should be far enough to find most function calls,
// and it will prevent us from walking back thousands of lines if something went wrong.
// there is nothing magical about 9 lines, and it can be adjusted if it doesn't seem to be
// working well
var col = token.state.lexical.column,
line,
e,
Expand Down
40 changes: 21 additions & 19 deletions src/extensions/default/JavaScriptCodeHints/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,23 @@ define(function (require, exports, module) {
function JSHints() {
}

/**
* determine if the cached hint information should be invalidated and re-calculated
*
* @param {Session} session - the active hinting session
* @return {boolean} - true if the hints should be recalculated
*/
JSHints.prototype.needNewHints = function (session) {
var cursor = session.getCursor(),
type = session.getType();

return !cachedHints ||
cachedLine !== cursor.line ||
type.property !== cachedType.property ||
type.context !== cachedType.context ||
type.showFunctionType !== cachedType.showFunctionType;
};

/**
* Determine whether hints are available for a given editor context
*
Expand All @@ -178,15 +195,7 @@ define(function (require, exports, module) {
type = session.getType(),
query = session.getQuery();

// Invalidate cached information if: 1) no scope exists; 2) the
// cursor has moved a line; 3) the scope is dirty; or 4) if the
// cursor has moved into a different scope. Cached information
// is also reset on editor change.
if (!cachedHints ||
cachedLine !== cursor.line ||
type.property !== cachedType.property ||
type.context !== cachedType.context ||
type.showFunctionType !== cachedType.showFunctionType) {
if (this.needNewHints(session)) {
//console.log("clear hints");
cachedLine = null;
cachedHints = null;
Expand Down Expand Up @@ -215,10 +224,7 @@ define(function (require, exports, module) {

// Compute fresh hints if none exist, or if the session
// type has changed since the last hint computation
if (!cachedHints ||
type.property !== cachedType.property ||
type.context !== cachedType.context ||
type.showFunctionType !== cachedType.showFunctionType || query.length === 0) {
if (this.needNewHints(session)) {
var offset = session.getOffset(),
scopeResponse = ScopeManager.requestHints(session, session.editor.document, offset),
self = this;
Expand Down Expand Up @@ -398,15 +404,11 @@ define(function (require, exports, module) {
if (resolvedPath) {
CommandManager.execute(Commands.FILE_OPEN, {fullPath: resolvedPath})
.done(function () {
session.editor.setCursorPos(jumpResp.start);
session.editor.setSelection(jumpResp.start, jumpResp.end);
session.editor.centerOnCursor();
session.editor.setSelection(jumpResp.start, jumpResp.end, true);
});
}
} else {
session.editor.setCursorPos(jumpResp.start);
session.editor.setSelection(jumpResp.start, jumpResp.end);
session.editor.centerOnCursor();
session.editor.setSelection(jumpResp.start, jumpResp.end, true);
}
}

Expand Down
16 changes: 16 additions & 0 deletions src/extensions/default/JavaScriptCodeHints/tern-worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ importScripts("thirdparty/requirejs/require.js");

/**
* Provide the contents of the requested file to tern
* @param {string} name - the name of the file
* @param {Function} next - the function to call with the text of the file
* once it has been read in.
*/
function getFile(name, next) {
// save the callback
Expand Down Expand Up @@ -73,6 +76,11 @@ importScripts("thirdparty/requirejs/require.js");

/**
* Create a new tern server.
*
* @param {Object} env - an Object with the environment, as read in from
* the json files in thirdparty/tern/defs
* @param {string} dir - the current directory
* @param {Array.<string>} files - a list of filenames tern should be aware of
*/
function initTernServer(env, dir, files) {
var ternOptions = {
Expand All @@ -89,6 +97,14 @@ importScripts("thirdparty/requirejs/require.js");

}

/**
* Build an object that can be used as a request to tern
* @param {string} dir - the current directory
* @param {string} file - the filename the request is in
* @param {string} query - the type of request being made
* @param {number} offset - the offset in the file the request is at
* @param {string} text - the text of the file
*/
function buildRequest(dir, file, query, offset, text) {
query = {type: query};
query.start = offset;
Expand Down
14 changes: 7 additions & 7 deletions src/extensions/default/JavaScriptCodeHints/unittests.js
Original file line number Diff line number Diff line change
Expand Up @@ -507,7 +507,7 @@ define(function (require, exports, module) {

testEditor.setCursorPos(start);
var hintObj = expectHints(JSCodeHints.jsHintProvider);
selectHint(JSCodeHints.jsHintProvider, hintObj, "A2"); // hint 2 is "A2"
selectHint(JSCodeHints.jsHintProvider, hintObj, "A2");
runs(function () {
//expect(testEditor.getCursorPos()).toEqual(end);
expect(testDoc.getRange(start, end)).toEqual("A2");
Expand All @@ -522,7 +522,7 @@ define(function (require, exports, module) {
testEditor.setCursorPos(start);
var hintObj = expectHints(JSCodeHints.jsHintProvider);
hintsPresent(hintObj, ["A1", "A2", "A3"]);
selectHint(JSCodeHints.jsHintProvider, hintObj, "A1"); // hint 1 is "A1"
selectHint(JSCodeHints.jsHintProvider, hintObj, "A1");
runs(function () {
//expect(testEditor.getCursorPos()).toEqual(end);
expect(testDoc.getRange(before, end)).toEqual("A1");
Expand All @@ -537,7 +537,7 @@ define(function (require, exports, module) {
testDoc.replaceRange("A1.", start, start);
testEditor.setCursorPos(middle);
var hintObj = expectHints(JSCodeHints.jsHintProvider);
selectHint(JSCodeHints.jsHintProvider, hintObj, "propA"); // hint 0 is "propA"
selectHint(JSCodeHints.jsHintProvider, hintObj, "propA");

runs(function () {
expect(testEditor.getCursorPos()).toEqual(end);
Expand All @@ -554,7 +554,7 @@ define(function (require, exports, module) {
testDoc.replaceRange("A1.prop", start, start);
testEditor.setCursorPos(middle);
var hintObj = expectHints(JSCodeHints.jsHintProvider);
selectHint(JSCodeHints.jsHintProvider, hintObj, "propA"); // hint 0 is "propA"
selectHint(JSCodeHints.jsHintProvider, hintObj, "propA");

runs(function () {
expect(testEditor.getCursorPos()).toEqual(end);
Expand All @@ -571,7 +571,7 @@ define(function (require, exports, module) {
testDoc.replaceRange("A1.pro", start, start);
testEditor.setCursorPos(middle);
var hintObj = expectHints(JSCodeHints.jsHintProvider);
selectHint(JSCodeHints.jsHintProvider, hintObj, "propA"); // hint 0 is "propA"
selectHint(JSCodeHints.jsHintProvider, hintObj, "propA");
runs(function () {
expect(testEditor.getCursorPos()).toEqual(end);
expect(testDoc.getRange(start, end)).toEqual("A1.propA");
Expand All @@ -587,7 +587,7 @@ define(function (require, exports, module) {
testDoc.replaceRange("A1.propB", start, start);
testEditor.setCursorPos(middle);
var hintObj = expectHints(JSCodeHints.jsHintProvider);
selectHint(JSCodeHints.jsHintProvider, hintObj, "propA"); // hint 0 is "propA"
selectHint(JSCodeHints.jsHintProvider, hintObj, "propA");
runs(function () {
expect(testEditor.getCursorPos()).toEqual(end);
expect(testDoc.getRange(start, end)).toEqual("A1.propA");
Expand All @@ -604,7 +604,7 @@ define(function (require, exports, module) {
testDoc.replaceRange("(A1.prop)", start, start);
testEditor.setCursorPos(middle);
var hintObj = expectHints(JSCodeHints.jsHintProvider);
selectHint(JSCodeHints.jsHintProvider, hintObj, "propA"); // hint 0 is "propA"
selectHint(JSCodeHints.jsHintProvider, hintObj, "propA");

runs(function () {
expect(testEditor.getCursorPos()).toEqual(end);
Expand Down