diff --git a/src/extensions/default/JavaScriptCodeHints/ScopeManager.js b/src/extensions/default/JavaScriptCodeHints/ScopeManager.js index f99fb5b2a0f..cad70a61da0 100644 --- a/src/extensions/default/JavaScriptCodeHints/ScopeManager.js +++ b/src/extensions/default/JavaScriptCodeHints/ScopeManager.js @@ -152,7 +152,7 @@ define(function (require, exports, module) { dir = split.dir, file = split.file; - var ternPromise = getJumptoDef(dir, file, offset, document.getText()); + var ternPromise = getJumptoDef(dir, file, offset, session.getJavascriptText()); return {promise: ternPromise}; } @@ -286,7 +286,7 @@ define(function (require, exports, module) { * @return {jQuery.Promise} - The promise will not complete until the tern * hints have completed. */ - function requestHints(session, document, offset) { + function requestHints(session, document) { var path = document.file.fullPath, split = HintUtils.splitPath(path), dir = split.dir, @@ -295,12 +295,14 @@ define(function (require, exports, module) { var $deferredHints = $.Deferred(), hintPromise, fnTypePromise, - propsPromise; + propsPromise, + text = session.getJavascriptText(), + offset = session.getOffset(); - hintPromise = getTernHints(dir, file, offset, document.getText()); + hintPromise = getTernHints(dir, file, offset, text); var sessionType = session.getType(); if (sessionType.property) { - propsPromise = getTernProperties(dir, file, offset, document.getText()); + propsPromise = getTernProperties(dir, file, offset, text); } else { var $propsDeferred = $.Deferred(); propsPromise = $propsDeferred.promise(); @@ -309,7 +311,7 @@ define(function (require, exports, module) { if (sessionType.showFunctionType) { // Show function sig - fnTypePromise = getTernFunctionType(dir, file, sessionType.functionCallPos, offset, document.getText()); + fnTypePromise = getTernFunctionType(dir, file, sessionType.functionCallPos, offset, text); } else { var $fnTypeDeferred = $.Deferred(); fnTypePromise = $fnTypeDeferred.promise(); diff --git a/src/extensions/default/JavaScriptCodeHints/Session.js b/src/extensions/default/JavaScriptCodeHints/Session.js index 163094e9db1..c4f43e0619e 100644 --- a/src/extensions/default/JavaScriptCodeHints/Session.js +++ b/src/extensions/default/JavaScriptCodeHints/Session.js @@ -21,13 +21,16 @@ * */ -/*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, maxerr: 50 */ +/*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, maxerr: 50, regexp: true */ /*global define, brackets, $ */ define(function (require, exports, module) { "use strict"; var StringMatch = brackets.getModule("utils/StringMatch"), + LanguageManager = brackets.getModule("language/LanguageManager"), + HTMLUtils = brackets.getModule("language/HTMLUtils"), + TokenUtils = brackets.getModule("utils/TokenUtils"), HintUtils = require("HintUtils"), ScopeManager = require("ScopeManager"); @@ -273,18 +276,30 @@ define(function (require, exports, module) { * function being called */ Session.prototype.getType = function () { + function getLexicalState(token) { + if (token.state.lexical) { + // in a javascript file this is just in the state field + return token.state.lexical; + } else if (token.state.localState && token.state.localState.lexical) { + // inline javascript in an html file will have this in + // the localState field + return token.state.localState.lexical; + } + } var propertyLookup = false, inFunctionCall = false, showFunctionType = false, context = null, cursor = this.getCursor(), functionCallPos, - token = this.getToken(cursor); + token = this.getToken(cursor), + lexical; 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") { + lexical = getLexicalState(token); + if (lexical.info === "call") { inFunctionCall = true; if (this.getQuery().length > 0) { inFunctionCall = false; @@ -301,7 +316,7 @@ define(function (require, exports, module) { // 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, + var col = lexical.column, line, e, found; @@ -471,6 +486,51 @@ define(function (require, exports, module) { } return hints; }; + + /** + * Get the javascript text of the file open in the editor for this Session. + * For a javascript file, this is just the text of the file. For an HTML file, + * this will be only the text in the + + some stuff + + + +
+
+ + + some other stuff +
+
+ + + + + \ No newline at end of file diff --git a/src/extensions/default/JavaScriptCodeHints/unittests.js b/src/extensions/default/JavaScriptCodeHints/unittests.js index 9050ef3ea92..6f8f1a5fc6f 100644 --- a/src/extensions/default/JavaScriptCodeHints/unittests.js +++ b/src/extensions/default/JavaScriptCodeHints/unittests.js @@ -37,6 +37,7 @@ define(function (require, exports, module) { var extensionPath = FileUtils.getNativeModuleDirectoryPath(module), testPath = extensionPath + "/test/file1.js", + testHtmlPath = extensionPath + "/test/index.html", testDoc = null, testEditor; @@ -76,7 +77,7 @@ define(function (require, exports, module) { if (key === undefined) { key = null; } - + expect(provider.hasHints(testEditor, key)).toBe(true); return provider.getHints(null); } @@ -324,7 +325,6 @@ define(function (require, exports, module) { DocumentManager.getDocumentForPath(testPath).done(function (doc) { testDoc = doc; }); - waitsFor(function () { return testDoc !== null; }, "Unable to open test document", 10000); @@ -779,6 +779,66 @@ define(function (require, exports, module) { }); }); }); + + describe("JavaScript Code Hinting in a HTML file", function () { + + beforeEach(function () { + + DocumentManager.getDocumentForPath(testHtmlPath).done(function (doc) { + testDoc = doc; + }); + + waitsFor(function () { + return testDoc !== null; + }, "Unable to open test document", 10000); + + // create Editor instance (containing a CodeMirror instance) + runs(function () { + testEditor = createMockEditor(testDoc); + JSCodeHints.initializeSession(testEditor, false); + }); + }); + + afterEach(function () { + // The following call ensures that the document is reloaded + // from disk before each test + DocumentManager.closeAll(); + + SpecRunnerUtils.destroyMockEditor(testDoc); + testEditor = null; + testDoc = null; + }); + it("basic codehints in html file", function () { + var start = { line: 30, ch: 9 }, + end = { line: 30, ch: 11 }; + + testDoc.replaceRange("x.", start, start); + testEditor.setCursorPos(end); + var hintObj = expectHints(JSCodeHints.jsHintProvider); + runs(function () { + hintsPresentOrdered(hintObj, ["charAt", "charCodeAt", "concat", "indexOf"]); + }); + }); + + it("function type hint in html file", function () { + var start = { line: 29, ch: 12 }; + + testEditor.setCursorPos(start); + var hintObj = expectHints(JSCodeHints.jsHintProvider); + runs(function () { + hintsPresentExact(hintObj, ["foo(a: number) -> string"]); + }); + }); + + it("jump-to-def in html file", function () { + var start = { line: 29, ch: 10 }; + + testEditor.setCursorPos(start); + runs(function () { + editorJumped({line: 18, ch: 20}); + }); + }); + }); }); }); \ No newline at end of file diff --git a/src/language/HTMLUtils.js b/src/language/HTMLUtils.js index 6caa7274e25..3ae729593e6 100644 --- a/src/language/HTMLUtils.js +++ b/src/language/HTMLUtils.js @@ -465,46 +465,62 @@ define(function (require, exports, module) { } + /** - * Returns an Array of info about all