diff --git a/qt/org.eclipse.cdt.qt.core/tern-qml/qml.js b/qt/org.eclipse.cdt.qt.core/tern-qml/qml.js index 89ace08be83..09d03f79503 100644 --- a/qt/org.eclipse.cdt.qt.core/tern-qml/qml.js +++ b/qt/org.eclipse.cdt.qt.core/tern-qml/qml.js @@ -926,6 +926,9 @@ qmlImportHandler.reset(); } + /* + * Called when a completions query is made to the server + */ function completions(file, query) { // We can get relatively simple completions on QML Object Types for free if we // update the Context.paths variable. Tern uses this variable to complete @@ -939,6 +942,35 @@ } } + /* + * Called when a parse query is made to the server. + */ + function parse(srv, query, file) { + var ast = null; + if (query.file) { + // Get the file's AST. It should have been parsed already by the server. + ast = file.ast; + } else if (query.text) { + // Parse the file manually and get the AST. + var text = query.text; + var options = { + allowReturnOutsideFunction: true, + allowImportExportEverywhere: true, + ecmaVersion: srv.options.ecmaVersion + }; + srv.signal("preParse", text, options); + try { + ast = acorn.parse(text, options); + } catch (e) { + ast = acorn.parse_dammit(text, options); + } + srv.signal("postParse", ast, text); + } + return { + ast: ast + }; + } + // Register the QML plugin in Tern tern.registerPlugin("qml", function (server) { // First we want to replace the top-level defs array with our own and save the @@ -955,6 +987,18 @@ // Create the QML Import Handler qmlImportHandler = exports.importHandler = new ImportHandler(server); + // Define the 'parseFile' and 'parseString' query types. The reason we need + // two separate queries for these is that Tern will not allow us to resolve + // a file without 'takesFile' being true. However, if we set 'takesFile' to + // true, Tern will not allow a null file in the query. + tern.defineQueryType("parseFile", { + takesFile: true, + run: parse + }); + tern.defineQueryType("parseString", { + run: parse + }); + // Hook into server signals server.on("preParse", preParse); server.on("beforeLoad", beforeLoad); diff --git a/qt/org.eclipse.cdt.qt.core/tern-qml/test/driver.js b/qt/org.eclipse.cdt.qt.core/tern-qml/test/driver.js index 41be94a0262..6abf37e0961 100644 --- a/qt/org.eclipse.cdt.qt.core/tern-qml/test/driver.js +++ b/qt/org.eclipse.cdt.qt.core/tern-qml/test/driver.js @@ -15,7 +15,7 @@ var ternQML = require("../qml.js"); var tern = require("tern"); var projectDir = path.resolve(__dirname, ".."); -var resolve = function(pth) { +var resolve = function (pth) { return path.resolve(projectDir, pth); }; var testCases = []; @@ -108,8 +108,8 @@ function createServer(defs) { plugins.qml = true; var server = new tern.Server({ ecmaVersion: 5, - plugins : plugins, - defs : [ require("./ecma5-defs.js") ] + plugins: plugins, + defs: [ require("./ecma5-defs.js") ] }); return server; } @@ -117,7 +117,14 @@ function createServer(defs) { var assertCompletion = exports.assertCompletion = function (server, code, expected, pos, callback) { server.addFile("main.qml", code); server.request({ - query : { + files: [ + { + name: "main.qml", + text: code, + type: "full" + } + ], + query: { type: "completions", file: "main.qml", end: pos, @@ -130,7 +137,7 @@ var assertCompletion = exports.assertCompletion = function (server, code, expect expandWordForward: false, guess: false } - }, function(err, resp) { + }, function (err, resp) { if (err) { throw err; } @@ -140,9 +147,15 @@ var assertCompletion = exports.assertCompletion = function (server, code, expect }; var assertDefinition = exports.assertDefinition = function (server, code, expected, pos, callback) { - server.addFile("main.qml", code); server.request({ - query : { + files: [ + { + name: "main.qml", + text: code, + type: "full" + } + ], + query: { type: "definition", file: "main.qml", end: pos, @@ -155,7 +168,7 @@ var assertDefinition = exports.assertDefinition = function (server, code, expect expandWordForward: false, guess: false } - }, function(err, resp) { + }, function (err, resp) { if (err) { throw err; } @@ -169,8 +182,8 @@ function ppJSON(v) { } function addPath(str, pt) { - if (str.charAt(str.length-1) == ")") - return str.slice(0, str.length-1) + "/" + pt + ")"; + if (str.charAt(str.length - 1) == ")") + return str.slice(0, str.length - 1) + "/" + pt + ")"; return str + " (" + pt + ")"; } diff --git a/qt/org.eclipse.cdt.qt.core/tern-qml/test/run.js b/qt/org.eclipse.cdt.qt.core/tern-qml/test/run.js index 02f99944e14..0ab69213c8d 100644 --- a/qt/org.eclipse.cdt.qt.core/tern-qml/test/run.js +++ b/qt/org.eclipse.cdt.qt.core/tern-qml/test/run.js @@ -32,6 +32,7 @@ var stats, tests = []; tests.push(require("./test-scoping.js")); tests.push(require("./test-finddef.js")); tests.push(require("./test-completions.js")); +tests.push(require("./test-parse.js")); function report(state, code, message) { if (state != "ok") {++stats.failed; log(code, message);} diff --git a/qt/org.eclipse.cdt.qt.core/tern-qml/test/test-completions.js b/qt/org.eclipse.cdt.qt.core/tern-qml/test/test-completions.js index d9eb5f91666..56be852048c 100644 --- a/qt/org.eclipse.cdt.qt.core/tern-qml/test/test-completions.js +++ b/qt/org.eclipse.cdt.qt.core/tern-qml/test/test-completions.js @@ -202,13 +202,13 @@ test("{Add File After Import}", function (server, callback, name) { return callback("fail", name, "- failed on initial file " + failed); } server.addFile("MyObject.qml", "QtObject {\n\tproperty var test\n}"); - assertCompletion(server, "M", { + assertCompletion(server, "", { start: { line: 0, ch: 0 }, - end: { line: 0, ch: 1 }, + end: { line: 0, ch: 0 }, isProperty: false, isObjectKey: false, completions: [{ name: "MyObject", type: "MyObject", origin: "MyObject.qml" }] - }, 1, function (mis) { + }, 0, function (mis) { failed = mis; }); if (failed) { diff --git a/qt/org.eclipse.cdt.qt.core/tern-qml/test/test-parse.js b/qt/org.eclipse.cdt.qt.core/tern-qml/test/test-parse.js new file mode 100644 index 00000000000..c4585d8e365 --- /dev/null +++ b/qt/org.eclipse.cdt.qt.core/tern-qml/test/test-parse.js @@ -0,0 +1,80 @@ +/******************************************************************************* + * Copyright (c) 2015 QNX Software Systems and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * QNX Software Systems - Initial API and implementation + *******************************************************************************/ +"use strict"; + +var driver = require("./driver.js"); +var test = driver.test; +var groupStart = driver.groupStart; +var groupEnd = driver.groupEnd; + +var group = exports.group = "Parse Query"; +groupStart(group); + +test("{Parse existing file}", function (server, callback, name) { + server.addFile("main.qml", "import QtQuick 2.0\nModule {\n\tComponent {\n\t}\n}"); + server.request({ + query: { + type: "parseFile", + file: "main.qml" + } + }, function (err, resp) { + if (err) { + throw err; + } + if (!resp.ast && resp.ast.type === "QMLProgram") { + return callback("fail", name, "AST could not be found in response"); + } + return callback("ok", name); + }); +}); + +test("{Parse given file}", function (server, callback, name) { + server.request({ + files: [ + { + name: "main.qml", + text: "import QtQuick 2.0\nModule {\n\tComponent {\n\t}\n}", + type: "full" + } + ], + query: { + type: "parseFile", + file: "main.qml" + } + }, function (err, resp) { + if (err) { + throw err; + } + if (!resp.ast && resp.ast.type === "QMLProgram") { + return callback("fail", name, "AST could not be found in response"); + } + return callback("ok", name); + }); +}); + +test("{Parse text}", function (server, callback, name) { + server.request({ + query: { + type: "parseString", + text: "import QtQuick 2.0\nModule {\n\tComponent {\n\t}\n}" + } + }, function (err, resp) { + if (err) { + throw err; + } + if (!resp.ast && resp.ast.type === "QMLProgram") { + return callback("fail", name, "AST could not be found in response"); + } + return callback("ok", name); + }); +}); + +groupEnd(); \ No newline at end of file