Skip to main content
aboutsummaryrefslogtreecommitdiffstats
path: root/qt
diff options
context:
space:
mode:
authorMatthew Bastien2015-12-03 20:24:36 +0000
committerGerrit Code Review @ Eclipse.org2015-12-04 16:18:00 +0000
commitdd3815bb913ed3c50865d35a4826db26df9af8e0 (patch)
tree62d43ea82451e7e94c547931c6499bcba961f42d /qt
parent3ec33023c301e2042c379d7b820ad5467b96dd34 (diff)
downloadorg.eclipse.cdt-dd3815bb913ed3c50865d35a4826db26df9af8e0.tar.gz
org.eclipse.cdt-dd3815bb913ed3c50865d35a4826db26df9af8e0.tar.xz
org.eclipse.cdt-dd3815bb913ed3c50865d35a4826db26df9af8e0.zip
Bug 481126 - Tern-QML Directory Imports
Added functionality to tern-qml that allows it to recognize directory imports of the form: import "<directory>" [as <Qualifier>]. Content assist and find definitions also work for these new imports. Moreover, fixed the QML Scoping to be easier to handle in the future by defining new Scope Objects specifically for QML. Finally, used JS Hint and Beautify to have a more uniform coding style between all the files of acorn-qml and tern-qml. Also fixed up the HTML Demo to allow editing of multiple files to show the new imports. Change-Id: I2cdd18b1b8765400f6b24145f0677127a221fe10 Signed-off-by: Matthew Bastien <mbastien@blackberry.com>
Diffstat (limited to 'qt')
-rw-r--r--qt/org.eclipse.cdt.qt.core/acorn-qml/index.js14
-rw-r--r--qt/org.eclipse.cdt.qt.core/acorn-qml/inject.js92
-rw-r--r--qt/org.eclipse.cdt.qt.core/acorn-qml/loose/index.js14
-rw-r--r--qt/org.eclipse.cdt.qt.core/acorn-qml/loose/inject.js103
-rw-r--r--qt/org.eclipse.cdt.qt.core/acorn-qml/test/driver.js13
-rw-r--r--qt/org.eclipse.cdt.qt.core/acorn-qml/test/run.js17
-rw-r--r--qt/org.eclipse.cdt.qt.core/acorn-qml/test/tests-qml.js11
-rw-r--r--qt/org.eclipse.cdt.qt.core/acorn-qml/walk/index.js94
-rw-r--r--qt/org.eclipse.cdt.qt.core/tern-qml/demo/defs/ecma5.json.js7
-rw-r--r--qt/org.eclipse.cdt.qt.core/tern-qml/demo/demo.css91
-rw-r--r--qt/org.eclipse.cdt.qt.core/tern-qml/demo/demo.js232
-rw-r--r--qt/org.eclipse.cdt.qt.core/tern-qml/demo/docs.css142
-rw-r--r--qt/org.eclipse.cdt.qt.core/tern-qml/demo/qml-demo.html350
-rw-r--r--qt/org.eclipse.cdt.qt.core/tern-qml/qml.js1200
-rw-r--r--qt/org.eclipse.cdt.qt.core/tern-qml/test/driver.js37
-rw-r--r--qt/org.eclipse.cdt.qt.core/tern-qml/test/ecma5-defs.js4
-rw-r--r--qt/org.eclipse.cdt.qt.core/tern-qml/test/run.js11
-rw-r--r--qt/org.eclipse.cdt.qt.core/tern-qml/test/test-completions.js387
-rw-r--r--qt/org.eclipse.cdt.qt.core/tern-qml/test/test-finddef.js178
-rw-r--r--qt/org.eclipse.cdt.qt.core/tern-qml/test/test-scoping.js409
20 files changed, 2550 insertions, 856 deletions
diff --git a/qt/org.eclipse.cdt.qt.core/acorn-qml/index.js b/qt/org.eclipse.cdt.qt.core/acorn-qml/index.js
index f724fad6cb1..7f4ad9d5586 100644
--- a/qt/org.eclipse.cdt.qt.core/acorn-qml/index.js
+++ b/qt/org.eclipse.cdt.qt.core/acorn-qml/index.js
@@ -8,18 +8,14 @@
* Contributors:
* QNX Software Systems - Initial API and implementation
*******************************************************************************/
-
-// This will only be visible globally if we are in a browser environment
-var acornQML;
-
(function (mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
- return module.exports = mod(require("./inject.js"), require("acorn"));
+ return mod(require("./inject.js"), require("acorn"));
if (typeof define == "function" && define.amd) // AMD
return define(["./inject.js", "acorn/dist/acorn"], mod);
- acornQML = mod(injectQML, acorn); // Plain browser env
-})(function (injectQML, acorn) {
+ mod(acornQMLInjector, acorn); // Plain browser env
+})(function (acornQMLInjector, acorn) {
'use strict';
- return injectQML(acorn);
-}) \ No newline at end of file
+ acornQMLInjector.inject(acorn);
+}); \ No newline at end of file
diff --git a/qt/org.eclipse.cdt.qt.core/acorn-qml/inject.js b/qt/org.eclipse.cdt.qt.core/acorn-qml/inject.js
index 25ed656b34c..16ff8d63165 100644
--- a/qt/org.eclipse.cdt.qt.core/acorn-qml/inject.js
+++ b/qt/org.eclipse.cdt.qt.core/acorn-qml/inject.js
@@ -8,20 +8,16 @@
* Contributors:
* QNX Software Systems - Initial API and implementation
*******************************************************************************/
-
-// This will only be visible globally if we are in a browser environment
-var injectQML;
-
-(function (mod) {
+(function (root, mod) {
if (typeof exports === "object" && typeof module === "object") // CommonJS
- return module.exports = mod();
+ return mod(exports);
if (typeof define === "function" && define.amd) // AMD
- return define([], mod);
- injectQML = mod(); // Plain browser env
-})(function () {
+ return define(["exports"], mod);
+ mod(root.acornQMLInjector || (root.acornQMLInjector = {})); // Plain browser env
+})(this, function (exports) {
'use strict';
- return function (acorn) {
+ exports.inject = function (acorn) {
// Acorn token types
var tt = acorn.tokTypes;
@@ -88,7 +84,7 @@ var injectQML;
* the type import or pragma
*/
pp.qml_parseHeaderStatements = function () {
- var node = this.startNode()
+ var node = this.startNode();
node.statements = [];
var loop = true;
@@ -103,7 +99,7 @@ var injectQML;
}
return this.finishNode(node, "QMLHeaderStatements");
- }
+ };
/*
* Parses a QML Pragma statement of the form:
@@ -115,7 +111,7 @@ var injectQML;
node.id = this.parseIdent(false);
this.semicolon();
return this.finishNode(node, "QMLPragmaStatement");
- }
+ };
/*
* Parses a QML Import statement of the form:
@@ -180,7 +176,7 @@ var injectQML;
node.raw = this.input.slice(this.start, this.end);
node.value = this.value;
var matches;
- if (matches = /(\d+)\.(\d+)/.exec(node.raw)) {
+ if ((matches = /(\d+)\.(\d+)/.exec(node.raw))) {
node.major = parseInt(matches[1]);
node.minor = parseInt(matches[2]);
this.next();
@@ -189,7 +185,7 @@ var injectQML;
}
return this.finishNode(node, "QMLVersionLiteral");
- }
+ };
/*
* Parses a QML Qualifier of the form:
@@ -200,7 +196,7 @@ var injectQML;
this.expectContextual(qtt._as);
node.id = this.qml_parseIdent(false);
return this.finishNode(node, "QMLQualifier");
- }
+ };
/*
* Parses a QML Object Literal of the form:
@@ -217,7 +213,7 @@ var injectQML;
}
node.body = this.qml_parseMemberBlock();
return this.finishNode(node, "QMLObjectLiteral");
- }
+ };
/*
* Parses a QML Member Block of the form:
@@ -231,7 +227,7 @@ var injectQML;
node.members.push(this.qml_parseMember());
}
return this.finishNode(node, "QMLMemberBlock");
- }
+ };
/*
* Parses a QML Member which can be one of the following:
@@ -250,7 +246,7 @@ var injectQML;
return this.qml_parseFunctionMember();
}
return this.qml_parseObjectLiteralOrPropertyBinding();
- }
+ };
/*
* Parses a JavaScript function as a member of a QML Object Literal
@@ -259,7 +255,7 @@ var injectQML;
var node = this.startNode();
this.expect(tt._function);
return this.parseFunction(node, true);
- }
+ };
/*
* Parses a QML Object Literal or Property Binding depending on the tokens found.
@@ -278,7 +274,7 @@ var injectQML;
return this.qml_parsePropertyBinding(node);
}
this.unexpected();
- }
+ };
/*
* Parses a QML Property of the form:
@@ -294,7 +290,7 @@ var injectQML;
this.expect(tt.colon);
node.binding = this.qml_parseBinding();
return this.finishNode(node, "QMLPropertyBinding");
- }
+ };
/*
* Parses a QML Signal Definition of the form:
@@ -325,7 +321,7 @@ var injectQML;
this.qml_parseSignalParams(node);
this.semicolon();
return this.finishNode(node, "QMLSignalDefinition");
- }
+ };
/*
* Parses QML Signal Parameters of the form:
@@ -344,7 +340,7 @@ var injectQML;
this.expect(tt.parenR);
}
}
- }
+ };
/*
* Parses a QML Property Declaration (or Alias) of the form:
@@ -354,10 +350,10 @@ var injectQML;
var node = this.startNode();
// Parse 'default' or 'readonly'
- node["default"] = false;
- node["readonly"] = false;
+ node.default = false;
+ node.readonly = false;
if (this.eat(tt._default)) {
- node["default"] = true;
+ node.default = true;
} else if (this.isContextual(qtt._readonly)) {
// Parse as a qualified id in case this is not a property declaration
var readonly = this.qml_parseQualifiedId(true);
@@ -367,7 +363,7 @@ var injectQML;
node.id = readonly;
return this.qml_parseObjectLiteralOrPropertyBinding(node);
}
- node["readonly"] = true;
+ node.readonly = true;
} else {
// Readonly keyword is a qualified ID. This is not a property declaration.
node.id = readonly;
@@ -377,22 +373,22 @@ var injectQML;
// Parse as a qualified id in case this is not a property declaration
var property = this.qml_parseQualifiedId(true);
- if (property.parts.length === 1 || node["default"] || node["readonly"]) {
+ if (property.parts.length === 1 || node.default || node.readonly) {
if (property.name !== qtt._property) {
this.unexpected();
}
if (this.type === tt.colon || this.type === tt.braceL) {
// This is a property binding or object literal.
- node["default"] = undefined;
- node["readonly"] = undefined;
+ node.default = undefined;
+ node.readonly = undefined;
node.id = property;
return this.qml_parseObjectLiteralOrPropertyBinding(node);
}
} else {
// Property keyword is a qualified ID. This is not a property declaration.
- node["default"] = undefined;
- node["readonly"] = undefined;
+ node.default = undefined;
+ node.readonly = undefined;
node.id = property;
return this.qml_parseObjectLiteralOrPropertyBinding(node);
}
@@ -407,7 +403,7 @@ var injectQML;
}
return this.finishNode(node, "QMLPropertyDeclaration");
- }
+ };
/*
* Parses one of the following possibilities for a QML Property assignment:
@@ -422,7 +418,7 @@ var injectQML;
// test: QMLObject.QualifiedId { }
return this.qml_parseScriptBinding();
- }
+ };
/*
* Parses one of the following Script Bindings:
@@ -440,7 +436,7 @@ var injectQML;
this.semicolon();
}
return this.finishNode(node, "QMLScriptBinding");
- }
+ };
/*
* Parses a QML Statement Block of the form:
@@ -454,7 +450,7 @@ var injectQML;
node.statements.push(this.parseStatement(true, false));
}
return this.finishNode(node, "QMLStatementBlock");
- }
+ };
/*
* Parses a QML Type which can be either a Qualified ID or a primitive type keyword.
@@ -468,7 +464,7 @@ var injectQML;
return this.qml_parseQualifiedId(false);
}
this.unexpected();
- }
+ };
/*
* Parses a Qualified ID of the form:
@@ -494,7 +490,7 @@ var injectQML;
}
return this.finishNode(node, "QMLQualifiedID");
- }
+ };
/*
* Parses an Identifier in a QML Context. That is, this method uses 'isQMLContextual'
@@ -511,7 +507,7 @@ var injectQML;
}
}
return this.parseIdent(liberal);
- }
+ };
/*
* Returns whether or not a given token type and name can be a QML Identifier.
@@ -520,13 +516,13 @@ var injectQML;
pp.qml_isIdent = function (type, name) {
if (type === tt.name) {
var key;
- if (key = keywords[name]) {
- return key.isQMLContextual
+ if ((key = keywords[name])) {
+ return key.isQMLContextual;
}
return true;
}
return false;
- }
+ };
/*
* Returns whether or not the current token is a QML primitive type and consumes
@@ -538,7 +534,7 @@ var injectQML;
return true;
}
return false;
- }
+ };
/*
* Returns whether or not the current token is a QML primitive type.
@@ -550,12 +546,12 @@ var injectQML;
if (type === tt.name) {
var key;
- if (key = keywords[name]) {
+ if ((key = keywords[name])) {
return key.isPrimitive;
}
}
return false;
- }
+ };
acorn.plugins.qml = function (instance) {
@@ -589,8 +585,8 @@ var injectQML;
return this.finishNode(node, "QMLProgram");
};
});
- }
+ };
return acorn;
};
-}) \ No newline at end of file
+}); \ No newline at end of file
diff --git a/qt/org.eclipse.cdt.qt.core/acorn-qml/loose/index.js b/qt/org.eclipse.cdt.qt.core/acorn-qml/loose/index.js
index db2d6f5d249..524c15db178 100644
--- a/qt/org.eclipse.cdt.qt.core/acorn-qml/loose/index.js
+++ b/qt/org.eclipse.cdt.qt.core/acorn-qml/loose/index.js
@@ -8,18 +8,14 @@
* Contributors:
* QNX Software Systems - Initial API and implementation
*******************************************************************************/
-
-// This will only be visible globally if we are in a browser environment
-var acornQMLLoose;
-
(function (mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
- return module.exports = mod(require("./inject.js"), require("acorn"), require("acorn/dist/acorn_loose"));
+ return mod(require("./inject.js"), require("acorn"), require("acorn/dist/acorn_loose"));
if (typeof define == "function" && define.amd) // AMD
return define(["./inject.js", "acorn", "acorn/dist/acorn_loose"], mod);
- acornQMLLoose = mod(injectQMLLoose, acorn, acorn); // Plain browser env
-})(function (injectQMLLoose, acorn, acorn_loose) {
+ mod(acornQMLLooseInjector, acorn, acorn); // Plain browser env
+})(function (acornQMLLooseInjector, acorn, acorn_loose) {
"use strict";
- return injectQMLLoose(acorn);
-}) \ No newline at end of file
+ acornQMLLooseInjector.inject(acorn);
+}); \ No newline at end of file
diff --git a/qt/org.eclipse.cdt.qt.core/acorn-qml/loose/inject.js b/qt/org.eclipse.cdt.qt.core/acorn-qml/loose/inject.js
index 35b6aaf8203..f54e0f943a4 100644
--- a/qt/org.eclipse.cdt.qt.core/acorn-qml/loose/inject.js
+++ b/qt/org.eclipse.cdt.qt.core/acorn-qml/loose/inject.js
@@ -12,16 +12,16 @@
// This will only be visible globally if we are in a browser environment
var injectQMLLoose;
-(function (mod) {
+(function (root, mod) {
if (typeof exports === "object" && typeof module === "object") // CommonJS
- return module.exports = mod();
+ return mod(module.exports);
if (typeof define === "function" && define.amd) // AMD
- return define([], mod);
- injectQMLLoose = mod(); // Plain browser env
-})(function () {
+ return define(["exports"], mod);
+ mod(root.acornQMLLooseInjector || (root.acornQMLLooseInjector = {})); // Plain browser env
+})(this, function (exports) {
"use strict";
- return function (acorn) {
+ exports.inject = function (acorn) {
// Acorn token types
var tt = acorn.tokTypes;
@@ -38,7 +38,7 @@ var injectQMLLoose;
* the type import or pragma
*/
lp.qml_parseHeaderStatements = function () {
- var node = this.startNode()
+ var node = this.startNode();
node.statements = [];
var loop = true;
@@ -53,7 +53,7 @@ var injectQMLLoose;
}
return this.finishNode(node, "QMLHeaderStatements");
- }
+ };
/*
* Parses a QML Pragma statement of the form:
@@ -65,7 +65,7 @@ var injectQMLLoose;
node.id = this.parseIdent(false);
this.semicolon();
return this.finishNode(node, "QMLPragmaStatement");
- }
+ };
/*
* Parses a QML Import statement of the form:
@@ -122,7 +122,7 @@ var injectQMLLoose;
node.value = this.tok.value;
var matches;
if (this.tok.type === tt.num) {
- if (matches = /(\d+)\.(\d+)/.exec(node.raw)) {
+ if ((matches = /(\d+)\.(\d+)/.exec(node.raw))) {
node.major = parseInt(matches[1]);
node.minor = parseInt(matches[2]);
this.next();
@@ -139,7 +139,7 @@ var injectQMLLoose;
}
return this.finishNode(node, "QMLVersionLiteral");
- }
+ };
/*
* Parses a QML Qualifier of the form:
@@ -150,7 +150,7 @@ var injectQMLLoose;
this.expectContextual(qtt._as);
node.id = this.qml_parseIdent(false);
return this.finishNode(node, "QMLQualifier");
- }
+ };
/*
* Parses a QML Object Literal of the form:
@@ -163,7 +163,7 @@ var injectQMLLoose;
node.id = this.qml_parseQualifiedId(false);
node.body = this.qml_parseMemberBlock();
return this.finishNode(node, "QMLObjectLiteral");
- }
+ };
/*
* Parses a QML Member Block of the form:
@@ -184,7 +184,7 @@ var injectQMLLoose;
this.popCx();
this.eat(tt.braceR);
return this.finishNode(node, "QMLMemberBlock");
- }
+ };
/*
* Parses a QML Member which can be one of the following:
@@ -222,7 +222,7 @@ var injectQMLLoose;
}
// ignore the current token if it didn't pass the previous tests
this.next();
- }
+ };
/*
* Parses a JavaScript function as a member of a QML Object Literal
@@ -231,7 +231,7 @@ var injectQMLLoose;
var node = this.startNode();
this.expect(tt._function);
return this.qml_parseFunction(node, true);
- }
+ };
/*
* QML version of 'parseFunction' needed to have proper error tolerant parsing
@@ -250,7 +250,7 @@ var injectQMLLoose;
node.body = this.parseBlock();
} else {
if (this.options.locations) {
- node.body = this.startNodeAt([ this.last.end, this.last.loc.end ])
+ node.body = this.startNodeAt([ this.last.end, this.last.loc.end ]);
} else {
node.body = this.startNodeAt(this.last.end);
}
@@ -258,7 +258,7 @@ var injectQMLLoose;
this.finishNode(node.body, "BlockStatement");
}
return this.finishNode(node, isStatement ? "FunctionDeclaration" : "FunctionExpression");
- }
+ };
/*
* Parses a QML Object Literal or Property Binding depending on the tokens found.
@@ -282,7 +282,7 @@ var injectQMLLoose;
return this.qml_parsePropertyBinding();
}
return null;
- }
+ };
/*
* Parses a QML Property of the form:
@@ -295,7 +295,7 @@ var injectQMLLoose;
this.expect(tt.colon);
node.binding = this.qml_parseBinding(start);
return this.finishNode(node, "QMLPropertyBinding");
- }
+ };
/*
* Parses a QML Signal Definition of the form:
@@ -315,13 +315,13 @@ var injectQMLLoose;
this.qml_parseSignalParams(node);
this.semicolon();
return this.finishNode(node, "QMLSignalDefinition");
- }
+ };
/*
* Checks if the given node is a dummy identifier
*/
function isDummy(node) {
- return node.name === "✖"
+ return node.name === "✖";
}
/*
@@ -329,8 +329,8 @@ var injectQMLLoose;
* [(<Type> <Identifier> [',' <Type> <Identifier>]* )]?
*/
lp.qml_parseSignalParams = function (node) {
- this.pushCx()
- var indent = this.curIndent, line = this.curLineStart
+ this.pushCx();
+ var indent = this.curIndent, line = this.curLineStart;
node.params = [];
if (this.eat(tt.parenL)) {
while (!this.closes(tt.parenR, indent + 1, line)) {
@@ -352,15 +352,15 @@ var injectQMLLoose;
}
this.eat(tt.comma);
}
- this.popCx()
+ this.popCx();
if (!this.eat(tt.parenR)) {
// If there is no closing brace, make the node span to the start
// of the next token (this is useful for Tern)
- this.last.end = this.tok.start
- if (this.options.locations) this.last.loc.end = this.tok.loc.start
+ this.last.end = this.tok.start;
+ if (this.options.locations) this.last.loc.end = this.tok.loc.start;
}
}
- }
+ };
/*
* Parses a QML Property Declaration (or Alias) of the form:
@@ -368,25 +368,26 @@ var injectQMLLoose;
*/
lp.qml_parsePropertyDeclaration = function () {
var node = this.startNode();
+ var objOrBind = null;
// Parse 'default' or 'readonly'
- node["default"] = false;
- node["readonly"] = false;
+ node.default = false;
+ node.readonly = false;
if (this.eat(tt._default)) {
- node["default"] = true;
+ node.default = true;
} else if (this.isContextual(qtt._readonly)) {
- var objOrBind = this.qml_parseObjectLiteralOrPropertyBinding();
+ objOrBind = this.qml_parseObjectLiteralOrPropertyBinding();
if (objOrBind) {
- objOrBind["default"] = undefined;
- objOrBind["readonly"] = undefined;
+ objOrBind.default = undefined;
+ objOrBind.readonly = undefined;
return objOrBind;
}
this.expectContextual(qtt._readonly);
- node["readonly"] = true;
+ node.readonly = true;
}
- if (!node["default"] && !node["readonly"]) {
- var objOrBind = this.qml_parseObjectLiteralOrPropertyBinding();
+ if (!node.default && !node.readonly) {
+ objOrBind = this.qml_parseObjectLiteralOrPropertyBinding();
if (objOrBind) {
return objOrBind;
}
@@ -408,7 +409,7 @@ var injectQMLLoose;
}
return this.finishNode(node, "QMLPropertyDeclaration");
- }
+ };
/*
* Parses one of the following possibilities for a QML Property assignment:
@@ -437,7 +438,7 @@ var injectQMLLoose;
} else {
return this.qml_parseScriptBinding(start);
}
- }
+ };
/*
* Parses one of the following Script Bindings:
@@ -458,7 +459,7 @@ var injectQMLLoose;
}
// If this node consumed valid syntax, reset its start position
- if (!node.script.type === "Identifier" || node.script.name !== "✖") {
+ if (node.script.type !== "Identifier" || node.script.name !== "✖") {
if (node.loc) {
node.loc.start = node.script.loc.start;
}
@@ -470,7 +471,7 @@ var injectQMLLoose;
}
return this.finishNode(node, "QMLScriptBinding");
- }
+ };
/*
* Parses a QML Statement Block of the form:
@@ -489,7 +490,7 @@ var injectQMLLoose;
this.popCx();
this.eat(tt.braceR);
return this.finishNode(node, "QMLStatementBlock");
- }
+ };
/*
* Parses a QML Type which can be either a Qualified ID or a primitive type keyword.
@@ -501,7 +502,7 @@ var injectQMLLoose;
} else {
return this.qml_parseQualifiedId(false);
}
- }
+ };
/*
* Parses a Qualified ID of the form:
@@ -527,7 +528,7 @@ var injectQMLLoose;
}
return this.finishNode(node, "QMLQualifiedID");
- }
+ };
/*
* Parses an Identifier in a QML Context. That is, this method uses 'isQMLContextual'
@@ -544,7 +545,7 @@ var injectQMLLoose;
}
}
return this.parseIdent();
- }
+ };
/*
* Checks the next token to see if it matches the given contextual keyword. If the
@@ -552,14 +553,14 @@ var injectQMLLoose;
* and jumps ahead if it was found there. Returns whether or not the keyword was found.
*/
lp.expectContextual = function(name) {
- if (this.eatContextual(name)) return true
+ if (this.eatContextual(name)) return true;
for (var i = 1; i <= 2; i++) {
if (this.lookAhead(i).type == tt.name && this.lookAhead(i).value === name) {
- for (var j = 0; j < i; j++) this.next()
- return true
+ for (var j = 0; j < i; j++) this.next();
+ return true;
}
}
- }
+ };
// Functions left un-changed from the main parser
lp.qml_isIdent = pp.qml_isIdent;
@@ -592,8 +593,8 @@ var injectQMLLoose;
return this.finishNode(node, "QMLProgram");
};
});
- }
+ };
return acorn;
};
-}) \ No newline at end of file
+}); \ No newline at end of file
diff --git a/qt/org.eclipse.cdt.qt.core/acorn-qml/test/driver.js b/qt/org.eclipse.cdt.qt.core/acorn-qml/test/driver.js
index cfa2c1a3164..b379ea5263d 100644
--- a/qt/org.eclipse.cdt.qt.core/acorn-qml/test/driver.js
+++ b/qt/org.eclipse.cdt.qt.core/acorn-qml/test/driver.js
@@ -8,7 +8,7 @@
* Contributors:
* QNX Software Systems - Initial API and implementation
*******************************************************************************/
-"use-strict";
+"use strict";
var tests = [];
@@ -31,10 +31,10 @@ exports.runTests = function(config, callback) {
try {
var testOpts = test.options || {locations: true};
var expected = {};
- if (expected.onComment = testOpts.onComment) {
- testOpts.onComment = []
+ if ((expected.onComment = testOpts.onComment)) {
+ testOpts.onComment = [];
}
- if (expected.onToken = testOpts.onToken) {
+ if ((expected.onToken = testOpts.onToken)) {
testOpts.onToken = [];
}
testOpts.plugins = { qml: true };
@@ -87,6 +87,7 @@ function addPath(str, pt) {
}
var misMatch = exports.misMatch = function(exp, act) {
+ var mis = null;
if (!exp || !act || (typeof exp != "object") || (typeof act != "object")) {
if (exp !== act) return ppJSON(exp) + " !== " + ppJSON(act);
} else if (exp instanceof RegExp || act instanceof RegExp) {
@@ -96,12 +97,12 @@ var misMatch = exports.misMatch = function(exp, act) {
if (!act.slice) return ppJSON(exp) + " != " + ppJSON(act);
if (act.length != exp.length) return "array length mismatch " + exp.length + " != " + act.length;
for (var i = 0; i < act.length; ++i) {
- var mis = misMatch(exp[i], act[i]);
+ mis = misMatch(exp[i], act[i]);
if (mis) return addPath(mis, i);
}
} else {
for (var prop in exp) {
- var mis = misMatch(exp[prop], act[prop]);
+ mis = misMatch(exp[prop], act[prop]);
if (mis) return addPath(mis, prop);
}
}
diff --git a/qt/org.eclipse.cdt.qt.core/acorn-qml/test/run.js b/qt/org.eclipse.cdt.qt.core/acorn-qml/test/run.js
index fb56196c428..88b1184d665 100644
--- a/qt/org.eclipse.cdt.qt.core/acorn-qml/test/run.js
+++ b/qt/org.eclipse.cdt.qt.core/acorn-qml/test/run.js
@@ -8,11 +8,18 @@
* Contributors:
* QNX Software Systems - Initial API and implementation
*******************************************************************************/
-"use-strict";
+"use strict";
+// Get the driver and test code
var driver = require("./driver.js");
require("./tests-qml.js");
+// Get and inject the QML plugin into Acorn
+var acorn = require("acorn");
+require("acorn/dist/acorn_loose");
+require("..");
+require("../loose");
+
function group(name) {
if (typeof console === "object" && console.group) {
console.group(name);
@@ -32,7 +39,7 @@ function log(title, message) {
var stats, modes = {
Normal: {
config: {
- parse: require("..").parse,
+ parse: acorn.parse,
normal: true,
filter: function (test) {
var opts = test.options || {};
@@ -42,7 +49,7 @@ var stats, modes = {
},
Loose: {
config: {
- parse: require("../loose").parse_dammit,
+ parse: acorn.parse_dammit,
loose: true,
filter: function (test) {
var opts = test.options || {};
@@ -63,9 +70,9 @@ for (var name in modes) {
group(name);
var mode = modes[name];
stats = mode.stats = {testsRun: 0, failed: 0};
- var t0 = +new Date;
+ var t0 = +new Date();
driver.runTests(mode.config, report);
- mode.stats.duration = +new Date - t0;
+ mode.stats.duration = +new Date() - t0;
groupEnd();
}
diff --git a/qt/org.eclipse.cdt.qt.core/acorn-qml/test/tests-qml.js b/qt/org.eclipse.cdt.qt.core/acorn-qml/test/tests-qml.js
index d50df7a4208..ea43827d7a4 100644
--- a/qt/org.eclipse.cdt.qt.core/acorn-qml/test/tests-qml.js
+++ b/qt/org.eclipse.cdt.qt.core/acorn-qml/test/tests-qml.js
@@ -8,11 +8,12 @@
* Contributors:
* QNX Software Systems - Initial API and implementation
*******************************************************************************/
-"use-strict";
+"use strict";
-var test = require("./driver.js").test;
-var testFail = require("./driver.js").testFail;
-var tokTypes = require("../").tokTypes;
+var driver = require("./driver.js");
+var test = driver.test;
+var testFail = driver.testFail;
+var tokTypes = driver.tokTypes;
testFail('', "QML only supports ECMA Script Language Specification 5 or older",
{ locations: true, ecmaVersion: 6, allowReserved: false });
@@ -3241,7 +3242,7 @@ function program(headerStatements, rootObject) {
statements: headerStatements || []
},
rootObject: rootObject || null
- }
+ };
}
/*
diff --git a/qt/org.eclipse.cdt.qt.core/acorn-qml/walk/index.js b/qt/org.eclipse.cdt.qt.core/acorn-qml/walk/index.js
index 4ff5a0769b5..7678b13d403 100644
--- a/qt/org.eclipse.cdt.qt.core/acorn-qml/walk/index.js
+++ b/qt/org.eclipse.cdt.qt.core/acorn-qml/walk/index.js
@@ -10,7 +10,7 @@
*******************************************************************************/
(function (mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
- return module.exports = mod(require("acorn/dist/walk"));
+ return mod(require("acorn/dist/walk"));
if (typeof define == "function" && define.amd) // AMD
return define(["acorn/dist/walk"], mod);
mod(acorn.walk); // Plain browser env
@@ -18,53 +18,59 @@
"use strict";
function skipThrough(node, st, c) {
- c(node, st)
+ c(node, st);
}
function ignore(node, st, c) {}
- var base = walk.base;
- base["QMLProgram"] = function (node, st, c) {
- c(node.headerStatements, st);
- if (node.rootObject) {
- c(node.rootObject, st, "QMLRootObject");
+ function extendWalk(walker, funcs) {
+ for (var prop in funcs) {
+ walker[prop] = funcs[prop];
}
- };
- base["QMLHeaderStatements"] = function (node, st, c) {
- for (var i = 0; i < node.statements.length; i++) {
- c(node.statements[i], st, "QMLHeaderStatement");
- }
- };
- base["QMLHeaderStatement"] = skipThrough;
- base["QMLImportStatement"] = ignore;
- base["QMLPragmaStatement"] = ignore;
- base["QMLRootObject"] = skipThrough;
- base["QMLObjectLiteral"] = function (node, st, c) {
- c(node.body, st);
- };
- base["QMLMemberBlock"] = function (node, st, c) {
- for (var i = 0; i < node.members.length; i++) {
- c(node.members[i], st, "QMLMember");
- }
- };
- base["QMLMember"] = skipThrough;
- base["QMLPropertyDeclaration"] = function (node, st, c) {
- if (node.binding) {
- c(node.binding, st);
- }
- };
- base["QMLSignalDefinition"] = ignore;
- base["QMLPropertyBinding"] = function (node, st, c) {
- c(node.binding, st);
- };
- base["QMLScriptBinding"] = function (node, st, c) {
- c(node.script, st);
}
- base["QMLQualifiedID"] = ignore;
- base["QMLStatementBlock"] = function (node, st, c) {
- for (var i = 0; i < node.statements.length; i++) {
- c(node.statements[i], st, "Statement");
+
+ extendWalk(walk.base, {
+ QMLProgram: function (node, st, c) {
+ c(node.headerStatements, st);
+ if (node.rootObject) {
+ c(node.rootObject, st, "QMLRootObject");
+ }
+ },
+ QMLHeaderStatements: function (node, st, c) {
+ for (var i = 0; i < node.statements.length; i++) {
+ c(node.statements[i], st, "QMLHeaderStatement");
+ }
+ },
+ QMLHeaderStatement: skipThrough,
+ QMLImportStatement: ignore,
+ QMLPragmaStatement: ignore,
+ QMLRootObject: skipThrough,
+ QMLObjectLiteral: function (node, st, c) {
+ c(node.body, st);
+ },
+ QMLMemberBlock: function (node, st, c) {
+ for (var i = 0; i < node.members.length; i++) {
+ c(node.members[i], st, "QMLMember");
+ }
+ },
+ QMLMember: skipThrough,
+ QMLPropertyDeclaration: function (node, st, c) {
+ if (node.binding) {
+ c(node.binding, st);
+ }
+ },
+ QMLSignalDefinition: ignore,
+ QMLPropertyBinding: function (node, st, c) {
+ c(node.binding, st);
+ },
+ QMLScriptBinding: function (node, st, c) {
+ c(node.script, st);
+ },
+ QMLQualifiedID: ignore,
+ QMLStatementBlock: function (node, st, c) {
+ for (var i = 0; i < node.statements.length; i++) {
+ c(node.statements[i], st, "Statement");
+ }
}
- };
- return walk;
-}) \ No newline at end of file
+ });
+}); \ No newline at end of file
diff --git a/qt/org.eclipse.cdt.qt.core/tern-qml/demo/defs/ecma5.json.js b/qt/org.eclipse.cdt.qt.core/tern-qml/demo/defs/ecma5.json.js
index f5377300acd..ed9f62e03de 100644
--- a/qt/org.eclipse.cdt.qt.core/tern-qml/demo/defs/ecma5.json.js
+++ b/qt/org.eclipse.cdt.qt.core/tern-qml/demo/defs/ecma5.json.js
@@ -1,5 +1,4 @@
-(function() {
-var def = {
+var ecma5 = {
"!name": "ecma5",
"!define": {"Error.prototype": "Error.prototype"},
"Infinity": {
@@ -964,6 +963,4 @@ var def = {
"!url": "https://developer.mozilla.org/en-US/docs/JSON",
"!doc": "JSON (JavaScript Object Notation) is a data-interchange format. It closely resembles a subset of JavaScript syntax, although it is not a strict subset. (See JSON in the JavaScript Reference for full details.) It is useful when writing any kind of JavaScript-based application, including websites and browser extensions. For example, you might store user information in JSON format in a cookie, or you might store extension preferences in JSON in a string-valued browser preference."
}
-}
-CodeMirror.tern.addDef(def);
-})(); \ No newline at end of file
+} \ No newline at end of file
diff --git a/qt/org.eclipse.cdt.qt.core/tern-qml/demo/demo.css b/qt/org.eclipse.cdt.qt.core/tern-qml/demo/demo.css
new file mode 100644
index 00000000000..4ee93e66992
--- /dev/null
+++ b/qt/org.eclipse.cdt.qt.core/tern-qml/demo/demo.css
@@ -0,0 +1,91 @@
+.CodeMirror {
+ border: 1px solid silver;
+ height: 400px;
+ line-height: 1.3;
+ font-size: 13pt;
+ font-family: "Ubuntu Mono", monospace;
+ z-index: 2;
+}
+.CodeMirror pre {
+ padding: 0 9px;
+}
+.CodeMirror-linenumber {
+ font-size: 80%;
+ padding-top: 2px;
+}
+
+ul.tabs {
+ list-style: none;
+ margin: 0 0 -1px 0;
+ padding: 0 0 0 37px;
+ font-family: "Ubuntu Mono", monospace;
+ overflow: hidden;
+}
+ul.tabs li {
+ font-size: 11pt;
+ color: #777;
+ cursor: pointer;
+ margin: 0 2px 0 0;
+ padding: 2px 10px 5px;
+ border: 1px solid silver;
+ border-bottom-width: 0px;
+ background: #f3f3f3;
+ display: inline-block;
+ z-index: 1;
+ border-top-left-radius: 6px;
+ border-top-right-radius: 6px;
+ position: relative;
+}
+ul.tabs li:hover {
+ color: #444;
+}
+ul.tabs li.selected {
+ background: white;
+ color: #222;
+ z-index: 3;
+ cursor: default;
+}
+ul.tabs li:before,
+ul.tabs li:after {
+ position: absolute;
+ bottom: 0;
+ width: 6px;
+ height: 6px;
+ content: " ";
+ border: 1px solid silver;
+}
+ul.tabs li:before {
+ left: -7px;
+ border-bottom-right-radius: 6px;
+ border-width: 0 1px 1px 0;
+ box-shadow: 2px 2px 0 #f3f3f3;
+}
+ul.tabs li:after {
+ right: -7px;
+ border-bottom-left-radius: 6px;
+ border-width: 0 0 1px 1px;
+ box-shadow: -2px 2px 0 #f3f3f3;
+}
+ul.tabs li.selected:before {
+ box-shadow: 2px 2px 0 white;
+}
+ul.tabs li.selected:after {
+ box-shadow: -2px 2px 0 white;
+}
+
+#menus {
+ position: absolute;
+ right: 0;
+ top: 0;
+}
+#menus select {
+ font-family: "Ubuntu Mono", monospace;
+ font-size: 11pt;
+ width: 8em;
+}
+
+.CodeMirror div.CodeMirror-cursor {
+ border-left: 2px solid #444;
+}
+
+.CodeMirror-hint { font-size: 12pt; }
diff --git a/qt/org.eclipse.cdt.qt.core/tern-qml/demo/demo.js b/qt/org.eclipse.cdt.qt.core/tern-qml/demo/demo.js
new file mode 100644
index 00000000000..9b04c093709
--- /dev/null
+++ b/qt/org.eclipse.cdt.qt.core/tern-qml/demo/demo.js
@@ -0,0 +1,232 @@
+var project;
+
+function Project(name, place, config, docs) {
+ this.name = name;
+ this.docs = Object.create(null);
+ this.tabs = place.appendChild(document.createElement("ul"));
+ this.tabs.className = "tabs";
+ var self = this;
+ CodeMirror.on(this.tabs, "click", function (e) {
+ var target = e.target || e.srcElement;
+ if (target.nodeName.toLowerCase() != "li") return;
+ var doc = self.findDoc(target.textContent);
+ if (doc) self.selectDoc(doc);
+ });
+
+ var myConf = {
+ switchToDoc: function (name) {
+ self.selectDoc(self.findDoc(name));
+ },
+ useWorker: false
+ };
+ for (var prop in config) myConf[prop] = config[prop];
+ var server = this.server = new CodeMirror.TernServer(myConf);
+
+ var firstDoc;
+ for (name in docs) {
+ var data = this.registerDoc(name, new CodeMirror.Doc(docs[name], "javascript"));
+ if (!firstDoc) firstDoc = data;
+ }
+ this.curDoc = firstDoc;
+ this.setSelectedTab(firstDoc);
+
+ var keyMap = {
+ "Ctrl-I": function (cm) {
+ server.showType(cm);
+ },
+ "Ctrl-O": function (cm) {
+ server.showDocs(cm);
+ },
+ "Ctrl-Space": function (cm) {
+ server.complete(cm);
+ },
+ "Ctrl-J": function (cm) {
+ server.jumpToDef(cm);
+ },
+ "Ctrl-,": function (cm) {
+ server.jumpBack(cm);
+ },
+ "Ctrl-Q": function (cm) {
+ server.rename(cm);
+ }
+ };
+ this.editor = new CodeMirror(place, {
+ mode: 'text/javascript',
+ theme: "eclipse",
+ styleActiveLine: true,
+ lineNumbers: true,
+ lineWrapping: true,
+ autoCloseBrackets: true,
+ matchBrackets: true,
+ indentWithTabs: true,
+ indentUnit: 4,
+ tabSize: 4,
+ extraKeys: keyMap,
+ value: firstDoc.doc
+ });
+ this.editor.on("cursorActivity", function (cm) {
+ server.updateArgHints(cm);
+ });
+}
+
+Project.prototype = {
+ findDoc: function (name) {
+ return this.docs[name];
+ },
+
+ registerDoc: function (name, doc) {
+ this.server.addDoc(name, doc);
+ var data = this.docs[name] = {
+ name: name,
+ doc: doc,
+ tab: this.tabs.appendChild(document.createElement("li"))
+ };
+ data.tab.textContent = name;
+ return data;
+ },
+
+ unregisterDoc: function (doc) {
+ this.server.delDoc(doc.name);
+ delete this.docs[doc.name];
+ this.tabs.removeChild(doc.tab);
+ if (this.curDoc == doc)
+ for (var n in this.docs) return this.selectDoc(this.docs[n]);
+ },
+
+ setSelectedTab: function (doc) {
+ for (var n = this.tabs.firstChild; n; n = n.nextSibling)
+ n.className = n.textContent == doc.name ? "selected" : "";
+ },
+
+ selectDoc: function (doc) {
+ if (doc == this.curDoc) return;
+ this.server.hideDoc(this.curDoc);
+ this.setSelectedTab(doc);
+ this.curDoc = doc;
+ this.editor.swapDoc(doc.doc);
+ }
+};
+
+// Initialization
+
+CodeMirror.on(window, "load", function () {
+ var cmds = document.getElementById("commands");
+ CodeMirror.on(cmds, "change", function () {
+ if (!project || cmds.selectedIndex === 0) return;
+ var found = commands[cmds.value];
+ cmds.selectedIndex = 0;
+ project.editor.focus();
+ if (found) found();
+ });
+
+ var projects = document.getElementById("projects");
+ var projectNames = [];
+ var projectTags = document.querySelectorAll("project");
+ for (var i = 0; i < projectTags.length; i++) {
+ var opt = projects.appendChild(document.createElement("option"));
+ projectNames.push(opt.textContent = projectTags[i].id);
+ }
+ CodeMirror.on(projects, "change", function () {
+ initProject(projects.value, function () {
+ projects.selectedIndex = 0;
+ project.editor.focus();
+ });
+ });
+
+ function updateFromHash() {
+ var name = location.hash.slice(1);
+ if (projectNames.indexOf(name) > -1 &&
+ (!project || project.name != name)) {
+ initProject(name);
+ return true;
+ }
+ }
+ CodeMirror.on(window, "hashchange", updateFromHash);
+
+ updateFromHash() || initProject("simple");
+});
+
+function loadFiles(project, c) {
+ var found = {};
+
+ function inner(node) {
+ while (node && node.nodeName != "PRE") node = node.nextSibling;
+ if (!node) return c(found);
+ if (node.hasAttribute("file")) {
+ load(node.getAttribute("file"), function (data) {
+ found[node.id] = data;
+ inner(node.nextSibling);
+ });
+ } else {
+ found[node.id] = node.textContent;
+ inner(node.nextSibling);
+ }
+ }
+ inner(project.firstChild);
+}
+
+function words(str) {
+ return str ? str.split(" ") : [];
+}
+
+function initProject(name, c) {
+ var node = document.getElementById(name);
+ var plugins = {};
+ words(node.getAttribute("data-plugins")).forEach(function (name) {
+ plugins[name] = true;
+ });
+
+ if (plugins.requirejs) plugins.requirejs = {
+ override: {
+ "jquery": "=$"
+ }
+ };
+
+ loadFiles(node, function (files) {
+ var place = document.getElementById("place");
+ place.textContent = "";
+
+ if (project) project.server.destroy();
+
+ project = new Project(name, place, {
+ defs: [ecma5],
+ plugins: plugins
+ }, files);
+ location.hash = "#" + name;
+ c && c();
+ });
+}
+
+var commands = {
+ complete: function () {
+ project.server.complete(project.editor);
+ },
+ jumptodef: function () {
+ project.server.jumpToDef(project.editor);
+ },
+ finddocs: function () {
+ project.server.showDocs(project.editor);
+ },
+ findtype: function () {
+ project.server.showType(project.editor);
+ },
+ rename: function () {
+ project.server.rename(project.editor);
+ },
+ addfile: function () {
+ var name = prompt("Name of the new buffer", "");
+ if (name == null) return;
+ if (!name) name = "test";
+ var i = 0;
+ while (project.findDoc(name + (i || ""))) ++i;
+ project.selectDoc(project.registerDoc(name + (i || ""), new CodeMirror.Doc("", "javascript")));
+ },
+ delfile: function () {
+ var hasDoc = false;
+ for (var _ in project.docs) {
+ hasDoc = true;
+ break;
+ }
+ if (hasDoc) project.unregisterDoc(project.curDoc);
+ }
+}; \ No newline at end of file
diff --git a/qt/org.eclipse.cdt.qt.core/tern-qml/demo/docs.css b/qt/org.eclipse.cdt.qt.core/tern-qml/demo/docs.css
new file mode 100644
index 00000000000..82b8650fa39
--- /dev/null
+++ b/qt/org.eclipse.cdt.qt.core/tern-qml/demo/docs.css
@@ -0,0 +1,142 @@
+@font-face {
+ font-family: 'Signika';
+ font-style: normal;
+ font-weight: 700;
+ src: local('Signika-Bold'), url(http://themes.googleusercontent.com/static/fonts/signika/v3/7M5kxD4eGxuhgFaIk95pBRsxEYwM7FgeyaSgU71cLG0.woff) format('woff');
+}
+
+@font-face {
+ font-family: 'Ubuntu Mono';
+ font-style: normal;
+ font-weight: 500;
+ src: local('Ubuntu Mono'), local('UbuntuMono-Regular'), url('http://themes.googleusercontent.com/static/fonts/ubuntumono/v3/ViZhet7Ak-LRXZMXzuAfkYbN6UDyHWBl620a-IRfuBk.woff') format('woff');
+}
+
+body {
+ font-family: "Helvetica Neue", "Lucida Grande", "Lucida Sans Unicode", Helvetica, Arial, sans-serif;
+ color: #222;
+ font-size: 11pt;
+ max-width: 700px;
+ margin: 0 0 0 60px;
+ padding: 60px 0 4em;
+}
+
+#top {
+ position: fixed;
+ top: 0;
+ background: white;
+ padding-top: 15px;
+ left: 20px; right: 20px;
+ z-index: 5;
+}
+
+#head {
+ border-radius: 5px;
+ background: #1c5b64;
+ box-shadow: 0px 3px 3px rgba(0, 0, 0, .5);
+ position: relative;
+ font-family: "Signika", sans-serif;
+ font-weight: 700;
+}
+
+#head > a {
+ border-right: 1px solid #276c81;
+ display: inline-block;
+ height: 25px;
+ padding: 5px 20px;
+ color: white;
+ text-decoration: none;
+ line-height: 25px;
+ font-size: 17px;
+}
+
+#head > a.title {
+ font-size: 20px;
+ line-height: 24px;
+ padding-left: 80px;
+ background: url(logo.png);
+ background-position: 40px center;
+ background-size: auto 80%;
+ background-repeat: no-repeat;
+ border-bottom-left-radius: 5px;
+ border-top-left-radius: 5px;
+}
+
+#head > a:hover {
+ background-color: #276c81 !important;
+}
+
+.title span {
+ font-size: 13px;
+}
+
+code, pre { font-family: "Ubuntu Mono", monospace; }
+code { color: #124f55; font-size: 110%; }
+pre {
+ border-left: 3px solid #def;
+ padding: 2px 0 2px 10px;
+}
+
+h1, h2, h3, h4 {
+ font-family: "Signika", sans-serif;
+ font-weight: 700;
+ color: #124f55;
+}
+h1 { font-size: 20pt; }
+h2 { font-size: 17pt; }
+h3 { font-size: 13pt; }
+h4 { font-size: 11pt; }
+
+a, a:visited, a code { color: #df4c11; text-decoration: none; }
+a:hover { text-decoration: underline; }
+
+ul, ol {
+ margin: 0;
+ padding: 0;
+}
+li {
+ margin-left: 1.5em;
+ padding: 0;
+}
+
+li p { margin: 0; }
+dd p:first-child { margin-top: 1px; }
+dd { margin-left: 20px; }
+dt { text-indent: -1em; padding-left: 1em; }
+
+a[id] {
+ position: relative;
+ display: inline-block;
+ vertical-align: top;
+ top: -60px;
+}
+
+.release-note {
+ margin-left: 1em;
+}
+
+/* CodeMirror default theme for highlighting */
+
+.cm-s-default .cm-keyword {color: #708;}
+.cm-s-default .cm-atom {color: #219;}
+.cm-s-default .cm-number {color: #164;}
+.cm-s-default .cm-def {color: #00f;}
+.cm-s-default .cm-variable {color: black;}
+.cm-s-default .cm-variable-2 {color: #05a;}
+.cm-s-default .cm-variable-3 {color: #085;}
+.cm-s-default .cm-property {color: black;}
+.cm-s-default .cm-operator {color: black;}
+.cm-s-default .cm-comment {color: #a50;}
+.cm-s-default .cm-string {color: #a11;}
+.cm-s-default .cm-string-2 {color: #f50;}
+.cm-s-default .cm-meta {color: #555;}
+.cm-s-default .cm-error {color: #f00;}
+.cm-s-default .cm-qualifier {color: #555;}
+.cm-s-default .cm-builtin {color: #30a;}
+.cm-s-default .cm-bracket {color: #997;}
+.cm-s-default .cm-tag {color: #170;}
+.cm-s-default .cm-attribute {color: #00c;}
+.cm-s-default .cm-header {color: blue;}
+.cm-s-default .cm-quote {color: #090;}
+.cm-s-default .cm-hr {color: #999;}
+.cm-s-default .cm-link {color: #00c;}
diff --git a/qt/org.eclipse.cdt.qt.core/tern-qml/demo/qml-demo.html b/qt/org.eclipse.cdt.qt.core/tern-qml/demo/qml-demo.html
index ddb97e23118..75be78a1bef 100644
--- a/qt/org.eclipse.cdt.qt.core/tern-qml/demo/qml-demo.html
+++ b/qt/org.eclipse.cdt.qt.core/tern-qml/demo/qml-demo.html
@@ -1,102 +1,254 @@
<!doctype html>
<html>
- <head>
- <meta charset="utf-8">
- <title>QML Tern Demo</title>
-
- <!-- CodeMirror -->
- <link rel="stylesheet" href="../node_modules/codemirror/lib/codemirror.css">
- <script src="../node_modules/codemirror/lib/codemirror.js"></script>
- <link rel="stylesheet" href="../node_modules/codemirror/theme/eclipse.css">
- <script src="../node_modules/codemirror/addon/hint/show-hint.js"></script>
- <script src="../node_modules/codemirror/addon/edit/closetag.js"></script>
- <script src="../node_modules/codemirror/addon/edit/closebrackets.js"></script>
- <script src="../node_modules/codemirror/addon/edit/matchbrackets.js"></script>
- <script src="../node_modules/codemirror/addon/selection/active-line.js"></script>
- <script src="../node_modules/codemirror/mode/javascript/javascript.js"></script>
-
- <!-- Acorn -->
- <script src="../node_modules/acorn/dist/acorn.js"></script>
- <script src="../node_modules/acorn/dist/acorn_loose.js"></script>
- <script src="../node_modules/acorn/dist/walk.js"></script>
- <script src="../node_modules/acorn-qml/inject.js"></script>
- <script src="../node_modules/acorn-qml/index.js"></script>
- <script src="../node_modules/acorn-qml/loose/inject.js"></script>
- <script src="../node_modules/acorn-qml/loose/index.js"></script>
- <script src="../node_modules/acorn-qml/walk/index.js"></script>
-
- <!-- Tern JS -->
- <script src="../node_modules/tern/lib/signal.js"></script>
- <script src="../node_modules/tern/lib/tern.js"></script>
- <script src="../node_modules/tern/lib/def.js"></script>
- <script src="../node_modules/tern/lib/comment.js"></script>
- <script src="../node_modules/tern/lib/infer.js"></script>
- <script src="../qml.js"></script>
-
- <!-- Official CodeMirror Tern addon -->
- <script src="../node_modules/codemirror/addon/tern/tern.js"></script>
-
- <!-- Extension of CodeMirror Tern addon -->
- <link rel="stylesheet" href="../node_modules/codemirror-javascript/addon/hint/tern/tern-extension.css">
- <script src="../node_modules/codemirror-javascript/addon/hint/tern/tern-extension.js"></script>
- <script src="defs/ecma5.json.js"></script>
-
- <!-- CodeMirror Extension -->
- <link rel="stylesheet" href="../node_modules/codemirror-extension/addon/hint/show-hint-eclipse.css">
- <script src="../node_modules/codemirror-extension/addon/hint/show-context-info.js"></script>
- <link rel="stylesheet" href="../node_modules/codemirror-extension/addon/hint/show-context-info.css">
-
- <link rel="stylesheet" href="../node_modules/codemirror-extension/addon/hint/templates-hint.css">
- <script src="../node_modules/codemirror-extension/addon/hint/templates-hint.js"></script>
-
- <!-- CodeMirror Javascript -->
- <script src="../node_modules/codemirror-javascript/addon/hint/javascript/javascript-templates.js"></script>
-
- <!-- Tern Hover -->
- <link rel="stylesheet" href="../node_modules/codemirror-extension/addon/hover/text-hover.css">
- <script src="../node_modules/codemirror-extension/addon/hover/text-hover.js"></script>
- <script src="../node_modules/codemirror-javascript/addon/hint/tern/tern-hover.js"></script>
- </head>
- <body>
- <h1>Demo with QML Tern plugin </h1>
- <div style="border: solid 1px #AAAAAA;">
- <form><textarea id="code" name="code">import QtQuick 2.3&#10;Window {&#10;&#09;prop: {&#10;&#09;&#09;Qt.quit();&#10;&#09;}&#10;}&#10;</textarea></form>
+
+<head>
+ <meta charset="utf-8">
+ <title>Tern QML Demo</title>
+
+ <!-- CodeMirror Stylesheets -->
+ <link rel=stylesheet href="../node_modules/codemirror/lib/codemirror.css">
+ <link rel="stylesheet" href="../node_modules/codemirror/theme/eclipse.css">
+ <link rel="stylesheet" href="../node_modules/codemirror/addon/dialog/dialog.css">
+
+ <!-- CodeMirror-JavaScript Stylesheets -->
+ <link rel="stylesheet" href="../node_modules/codemirror-javascript/addon/hint/tern/tern-extension.css">
+
+ <!-- CodeMirror-Extension Stylesheets -->
+ <link rel="stylesheet" href="../node_modules/codemirror-extension/addon/hover/text-hover.css">
+ <link rel="stylesheet" href="../node_modules/codemirror-extension/addon/hint/templates-hint.css">
+ <link rel="stylesheet" href="../node_modules/codemirror-extension/addon/hint/show-hint-eclipse.css">
+ <link rel="stylesheet" href="../node_modules/codemirror-extension/addon/hint/show-context-info.css">
+
+ <!-- Demo Specific Stylesheets -->
+ <link rel=stylesheet href="./docs.css">
+ <link rel=stylesheet href="./demo.css">
+</head>
+
+<body>
+ <h1>Demo with QML Tern plugin </h1>
+ <title>QML Tern Demo</title>
+
+ <!-- CodeMirror -->
+ <script src="../node_modules/codemirror/lib/codemirror.js"></script>
+ <script src="../node_modules/codemirror/addon/hint/show-hint.js"></script>
+ <script src="../node_modules/codemirror/addon/edit/closetag.js"></script>
+ <script src="../node_modules/codemirror/addon/edit/closebrackets.js"></script>
+ <script src="../node_modules/codemirror/addon/edit/matchbrackets.js"></script>
+ <script src="../node_modules/codemirror/addon/selection/active-line.js"></script>
+ <script src="../node_modules/codemirror/mode/javascript/javascript.js"></script>
+
+ <!-- Acorn -->
+ <script src="../node_modules/acorn/dist/acorn.js"></script>
+ <script src="../node_modules/acorn/dist/acorn_loose.js"></script>
+ <script src="../node_modules/acorn/dist/walk.js"></script>
+ <script src="../node_modules/acorn-qml/inject.js"></script>
+ <script src="../node_modules/acorn-qml/index.js"></script>
+ <script src="../node_modules/acorn-qml/loose/inject.js"></script>
+ <script src="../node_modules/acorn-qml/loose/index.js"></script>
+ <script src="../node_modules/acorn-qml/walk/index.js"></script>
+
+ <!-- Tern JS -->
+ <script src="../node_modules/tern/lib/signal.js"></script>
+ <script src="../node_modules/tern/lib/tern.js"></script>
+ <script src="../node_modules/tern/lib/def.js"></script>
+ <script src="../node_modules/tern/lib/comment.js"></script>
+ <script src="../node_modules/tern/lib/infer.js"></script>
+ <script src="../qml.js"></script>
+
+ <!-- Official CodeMirror Tern addon -->
+ <script src="../node_modules/codemirror/addon/tern/tern.js"></script>
+
+ <!-- ECMA 5th Edition Definitions File -->
+ <script src="defs/ecma5.json.js"></script>
+
+ <!-- CodeMirror Extension -->
+ <script src="../node_modules/codemirror-extension/addon/hint/show-context-info.js"></script>
+ <script src="../node_modules/codemirror-extension/addon/hint/templates-hint.js"></script>
+
+ <!-- CodeMirror Javascript -->
+ <script src="../node_modules/codemirror-javascript/addon/hint/javascript/javascript-templates.js"></script>
+
+ <!-- Tern Hover -->
+ <script src="../node_modules/codemirror-extension/addon/hover/text-hover.js"></script>
+ <script src="../node_modules/codemirror-javascript/addon/hint/tern/tern-hover.js"></script>
+
+ <!-- Extra script to control project logic -->
+ <script src="./demo.js"></script>
+
+ <div style="position: relative" id="demospace">
+ <div id="menus">
+ <select id="commands">
+ <option>commands...</option>
+ <option value="complete">Autocomplete (Ctrl-Space)</option>
+ <option value="jumptodef">Jump to definition (ctrl-J)</option>
+ <option value="finddocs">Describe (Ctrl-O)</option>
+ <option value="findtype">Find type of (Ctrl-I)</option>
+ <option value="rename">Rename variable (Ctrl-Q)</option>
+ <option value="addfile">Add a new file</option>
+ <option value="delfile">Remove this file</option>
+ </select>
</div>
- <script type="text/javascript">
- function passAndHint(cm) {
- setTimeout(function() {cm.execCommand("autocomplete");}, 100);
- return CodeMirror.Pass;
- }
-
- function myHint(cm) {
- return CodeMirror.showHint(cm, CodeMirror.ternHint, {async: true});
- }
-
- CodeMirror.commands.autocomplete = function(cm) {
- CodeMirror.showHint(cm, myHint);
- }
-
- var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
- mode: 'text/javascript',
- theme : "eclipse",
- styleActiveLine: true,
- lineNumbers: true,
- lineWrapping: true,
- autoCloseBrackets: true,
- matchBrackets: true,
- indentWithTabs: true,
- indentUnit: 4,
- extraKeys: {
- "'.'": passAndHint,
- "Ctrl-Space": "autocomplete",
- "Ctrl-I": function(cm) { CodeMirror.tern.showType(cm); },
- "Ctrl-J": function(cm) { CodeMirror.tern.jumpToDef(cm); },
- "Alt-,": function(cm) { CodeMirror.tern.jumpBack(cm); },
- "Ctrl-Q": function(cm) { CodeMirror.tern.rename(cm); }
- },
- gutters: ["CodeMirror-linenumbers"],
- ternWith: { plugins: { "qml" : true } }
- });
- </script>
- </body>
-</html>
+ <form id="place">
+ <ul class="tabs">
+ <li class="selected">test</li>
+ </ul>
+ <div class="CodeMirror cm-s-default">
+ <div style="overflow: hidden; position: relative; width: 3px; height: 0px; top: 5px; left: 40px;">
+ <textarea autocorrect="off" autocapitalize="off" spellcheck="false" style="position: absolute; padding: 0px; width: 1000px; height: 1em; outline: none;" tabindex="0"></textarea>
+ </div>
+ <div class="CodeMirror-vscrollbar" cm-not-content="true" style="display: block; bottom: 0px;">
+ <div style="min-width: 1px; height: 580px;"></div>
+ </div>
+ <div class="CodeMirror-hscrollbar" cm-not-content="true">
+ <div style="height: 100%; min-height: 1px; width: 0px;"></div>
+ </div>
+ <div class="CodeMirror-scrollbar-filler" cm-not-content="true"></div>
+ <div class="CodeMirror-gutter-filler" cm-not-content="true"></div>
+ <div class="CodeMirror-scroll" tabindex="-1">
+ <div class="CodeMirror-sizer" style="margin-left: 30px; margin-bottom: -21px; border-right-width: 9px; min-height: 580px; min-width: 531.891px; padding-right: 21px; padding-bottom: 0px;">
+ <div style="position: relative; top: 0px;">
+ <div class="CodeMirror-lines">
+ <div style="position: relative; outline: none;">
+ <div class="CodeMirror-measure"><span><span>​</span>x</span>
+ </div>
+ <div class="CodeMirror-measure"></div>
+ <div style="position: relative; z-index: 1;"></div>
+ <div class="CodeMirror-cursors">
+ <div class="CodeMirror-cursor" style="left: 9px; top: 0px; height: 22px;">&nbsp;</div>
+ </div>
+ <div class="CodeMirror-code">
+ <div style="position: relative;">
+ <div class="CodeMirror-gutter-wrapper" style="left: -30px;">
+ <div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 21px;">1</div>
+ </div><pre class=" CodeMirror-line "><span style="padding-right: 0.1px;"><span class="cm-comment">// Use ctrl-space to complete something</span></span></pre></div>
+ <div style="position: relative;">
+ <div class="CodeMirror-gutter-wrapper" style="left: -30px;">
+ <div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 21px;">2</div>
+ </div><pre class=" CodeMirror-line "><span style="padding-right: 0.1px;"><span class="cm-variable">co</span></span></pre></div>
+ <div style="position: relative;">
+ <div class="CodeMirror-gutter-wrapper" style="left: -30px;">
+ <div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 21px;">3</div>
+ </div><pre class=" CodeMirror-line "><span style="padding-right: 0.1px;"><span class="cm-variable">document</span>.<span class="cm-property">body</span>.<span class="cm-property">a</span></span></pre></div>
+ <div style="position: relative;">
+ <div class="CodeMirror-gutter-wrapper" style="left: -30px;">
+ <div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 21px;">4</div>
+ </div><pre class=" CodeMirror-line "><span style="padding-right: 0.1px;"><span cm-text="">​</span></span></pre></div>
+ <div style="position: relative;">
+ <div class="CodeMirror-gutter-wrapper" style="left: -30px;">
+ <div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 21px;">5</div>
+ </div><pre class=" CodeMirror-line "><span style="padding-right: 0.1px;"><span class="cm-comment">// Put the cursor in or after an expression, press ctrl-i to</span></span></pre></div>
+ <div style="position: relative;">
+ <div class="CodeMirror-gutter-wrapper" style="left: -30px;">
+ <div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 21px;">6</div>
+ </div><pre class=" CodeMirror-line "><span style="padding-right: 0.1px;"><span class="cm-comment">// find its type</span></span></pre></div>
+ <div style="position: relative;">
+ <div class="CodeMirror-gutter-wrapper" style="left: -30px;">
+ <div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 21px;">7</div>
+ </div><pre class=" CodeMirror-line "><span style="padding-right: 0.1px;"><span cm-text="">​</span></span></pre></div>
+ <div style="position: relative;">
+ <div class="CodeMirror-gutter-wrapper" style="left: -30px;">
+ <div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 21px;">8</div>
+ </div><pre class=" CodeMirror-line "><span style="padding-right: 0.1px;"><span class="cm-keyword">var</span> <span class="cm-def">foo</span> <span class="cm-operator">=</span> [<span class="cm-string">"array"</span>, <span class="cm-string">"of"</span>, <span class="cm-string">"strings"</span>]</span></pre></div>
+ <div style="position: relative;">
+ <div class="CodeMirror-gutter-wrapper" style="left: -30px;">
+ <div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 21px;">9</div>
+ </div><pre class=" CodeMirror-line "><span style="padding-right: 0.1px;"><span class="cm-keyword">var</span> <span class="cm-def">bar</span> <span class="cm-operator">=</span> <span class="cm-variable">foo</span>.<span class="cm-property">slice</span>(<span class="cm-number">0</span>, <span class="cm-number">2</span>).<span class="cm-property">join</span>(<span class="cm-string">""</span>).<span class="cm-property">split</span>(<span class="cm-string">"a"</span>)[<span class="cm-number">0</span>]</span></pre></div>
+ <div style="position: relative;">
+ <div class="CodeMirror-gutter-wrapper" style="left: -30px;">
+ <div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 21px;">10</div>
+ </div><pre class=" CodeMirror-line "><span style="padding-right: 0.1px;"><span cm-text="">​</span></span></pre></div>
+ <div style="position: relative;">
+ <div class="CodeMirror-gutter-wrapper" style="left: -30px;">
+ <div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 21px;">11</div>
+ </div><pre class=" CodeMirror-line "><span style="padding-right: 0.1px;"><span class="cm-comment">// Works for locally defined types too.</span></span></pre></div>
+ <div style="position: relative;">
+ <div class="CodeMirror-gutter-wrapper" style="left: -30px;">
+ <div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 21px;">12</div>
+ </div><pre class=" CodeMirror-line "><span style="padding-right: 0.1px;"><span cm-text="">​</span></span></pre></div>
+ <div style="position: relative;">
+ <div class="CodeMirror-gutter-wrapper" style="left: -30px;">
+ <div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 21px;">13</div>
+ </div><pre class=" CodeMirror-line "><span style="padding-right: 0.1px;"><span class="cm-keyword">function</span> <span class="cm-def">CTor</span>() { <span class="cm-keyword">this</span>.<span class="cm-property">size</span> <span class="cm-operator">=</span> <span class="cm-number">10</span> }</span></pre></div>
+ <div style="position: relative;">
+ <div class="CodeMirror-gutter-wrapper" style="left: -30px;">
+ <div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 21px;">14</div>
+ </div><pre class=" CodeMirror-line "><span style="padding-right: 0.1px;"><span class="cm-variable">CTor</span>.<span class="cm-property">prototype</span>.<span class="cm-property">hallo</span> <span class="cm-operator">=</span> <span class="cm-string">"hallo"</span></span></pre></div>
+ <div style="position: relative;">
+ <div class="CodeMirror-gutter-wrapper" style="left: -30px;">
+ <div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 21px;">15</div>
+ </div><pre class=" CodeMirror-line "><span style="padding-right: 0.1px;"><span cm-text="">​</span></span></pre></div>
+ <div style="position: relative;">
+ <div class="CodeMirror-gutter-wrapper" style="left: -30px;">
+ <div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 21px;">16</div>
+ </div><pre class=" CodeMirror-line "><span style="padding-right: 0.1px;"><span class="cm-keyword">var</span> <span class="cm-def">baz</span> <span class="cm-operator">=</span> <span class="cm-keyword">new</span> <span class="cm-variable">CTor</span></span></pre></div>
+ <div style="position: relative;">
+ <div class="CodeMirror-gutter-wrapper" style="left: -30px;">
+ <div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 21px;">17</div>
+ </div><pre class=" CodeMirror-line "><span style="padding-right: 0.1px;"><span class="cm-variable">baz</span>.</span></pre></div>
+ <div style="position: relative;">
+ <div class="CodeMirror-gutter-wrapper" style="left: -30px;">
+ <div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 21px;">18</div>
+ </div><pre class=" CodeMirror-line "><span style="padding-right: 0.1px;"><span cm-text="">​</span></span></pre></div>
+ <div style="position: relative;">
+ <div class="CodeMirror-gutter-wrapper" style="left: -30px;">
+ <div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 21px;">19</div>
+ </div><pre class=" CodeMirror-line "><span style="padding-right: 0.1px;"><span class="cm-comment">// You can press ctrl-q when the cursor is on a variable</span></span></pre></div>
+ <div style="position: relative;">
+ <div class="CodeMirror-gutter-wrapper" style="left: -30px;">
+ <div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 21px;">20</div>
+ </div><pre class=" CodeMirror-line "><span style="padding-right: 0.1px;"><span class="cm-comment">// name to rename it. Try it with CTor...</span></span></pre></div>
+ <div style="position: relative;">
+ <div class="CodeMirror-gutter-wrapper" style="left: -30px;">
+ <div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 21px;">21</div>
+ </div><pre class=" CodeMirror-line "><span style="padding-right: 0.1px;"><span cm-text="">​</span></span></pre></div>
+ <div style="position: relative;">
+ <div class="CodeMirror-gutter-wrapper" style="left: -30px;">
+ <div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 21px;">22</div>
+ </div><pre class=" CodeMirror-line "><span style="padding-right: 0.1px;"><span class="cm-comment">// When the cursor is in an argument list, the arguments</span></span></pre></div>
+ <div style="position: relative;">
+ <div class="CodeMirror-gutter-wrapper" style="left: -30px;">
+ <div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 21px;">23</div>
+ </div><pre class=" CodeMirror-line "><span style="padding-right: 0.1px;"><span class="cm-comment">// are shown below the editor.</span></span></pre></div>
+ <div style="position: relative;">
+ <div class="CodeMirror-gutter-wrapper" style="left: -30px;">
+ <div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 21px;">24</div>
+ </div><pre class=" CodeMirror-line "><span style="padding-right: 0.1px;"><span cm-text="">​</span></span></pre></div>
+ <div style="position: relative;">
+ <div class="CodeMirror-gutter-wrapper" style="left: -30px;">
+ <div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 21px;">25</div>
+ </div><pre class=" CodeMirror-line "><span style="padding-right: 0.1px;">[<span class="cm-number">1</span>].<span class="cm-property">reduce</span>( )</span></pre></div>
+ <div style="position: relative;">
+ <div class="CodeMirror-gutter-wrapper" style="left: -30px;">
+ <div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 21px;">26</div>
+ </div><pre class=" CodeMirror-line "><span style="padding-right: 0.1px;"><span cm-text="">​</span></span></pre></div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div style="position: absolute; height: 9px; width: 1px; top: 580px;"></div>
+ <div class="CodeMirror-gutters" style="height: 589px;">
+ <div class="CodeMirror-gutter CodeMirror-linenumbers" style="width: 29px;"></div>
+ </div>
+ </div>
+ </div>
+ </form>
+ </div>
+
+<div style="display: none" id="projects">
+<project id="simple" data-plugins="qml">
+<pre id="main.qml">MyButton {
+
+}
+</pre>
+<pre id="MyButton.qml">Button {
+ property int width;
+ property int height;
+ property var parent;
+}
+</pre>
+</project>
+</div>
+</body>
+
+</html> \ No newline at end of file
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 6f2ca438aca..9e49cac257a 100644
--- a/qt/org.eclipse.cdt.qt.core/tern-qml/qml.js
+++ b/qt/org.eclipse.cdt.qt.core/tern-qml/qml.js
@@ -8,130 +8,507 @@
* Contributors:
* QNX Software Systems - Initial API and implementation
*******************************************************************************/
-(function (mod) {
+(function (root, mod) {
if (typeof exports === "object" && typeof module === "object") // CommonJS
- return mod(require("acorn"), require("acorn/dist/acorn_loose"), require("acorn/dist/walk"), require("acorn-qml"),
- require("acorn-qml/loose"), require("acorn-qml/walk"), require("tern/lib/infer"), require("tern"));
+ return mod(exports, require("acorn"), require("acorn/dist/acorn_loose"), require("acorn/dist/walk"),
+ require("acorn-qml"), require("acorn-qml/loose"), require("acorn-qml/walk"), require("tern"),
+ require("tern/lib/infer"), require("tern/lib/signal"));
if (typeof define === "function" && define.amd) // AMD
- return define([ "acorn", "acorn/dist/acorn_loose", "acorn/dist/walk", "acorn-qml", "acorn-qml/loose", "acorn-qml/walk",
- "tern/lib/infer", "tern"], mod);
- mod(acorn, acorn, acorn.walk, acorn, acorn, acorn.walk, tern, tern, tern.def); // Plain browser env
-})(function (acorn, acornLoose, walk, acornQML, acornQMLLoose, acornQMLWalk, infer, tern, def) {
+ return define(["exports", "acorn/dist/acorn", "acorn/dist/acorn_loose", "acorn/dist/walk", "acorn-qml",
+ "acorn-qml/loose", "acorn-qml/walk", "tern", "tern/lib/infer", "tern/lib/signal"], mod);
+ mod(root.ternQML || (root.ternQML = {}), acorn, acorn, acorn.walk, acorn, acorn, acorn.walk, tern, tern, tern.signal); // Plain browser env
+})(this, function (exports, acorn, acornLoose, walk, acornQML, acornQMLLoose, acornQMLWalk, tern, infer, signal) {
'use strict';
- // Outside of the browser environment we have to grab 'def' from 'infer'
- if (!def) {
- def = infer.def;
+ // Grab 'def' from 'infer' (used for jsDefs)
+ var def = infer.def;
+
+ // 'extend' taken from infer.js
+ function extend(proto, props) {
+ var obj = Object.create(proto);
+ if (props) {
+ for (var prop in props) obj[prop] = props[prop];
+ }
+ return obj;
}
- // QML Scope Builder
- var ScopeBuilder = function (file, jsDefs) {
- this.file = file;
- this.file.qmlIDScope = new infer.Scope();
- this.file.qmlIDScope.name = "<qml-id>";
- this.jsDefs = jsDefs;
- this.scopes = [];
- this.jsScopes = [];
- this.propScopes = [];
- this.sigScopes = [];
+ // QML Import Handler
+ var qmlImportHandler = exports.importHandler = null;
+ var ImportHandler = function (server) {
+ this.server = server;
+ this.imports = null;
};
+ ImportHandler.prototype = {
+ reset: function () {
+ this.imports = null;
+ },
+ resolveDirectory: function (file, path) {
+ var impl = this.server.options.resolveDirectory;
+ if (impl) {
+ return impl(file, path);
+ }
+ // Getting to this point means that we were unable to find an implementation of
+ // the 'resolveDirectory' method. The only time this should happen is during
+ // a test case which we expect to have an import of the style "./ ..." and nothing
+ // else. This method will simply remove the './', add the file's base, and return.
+ if (!path) {
+ // If no path was specified, return the base directory of the file
+ var dir = file.name;
+ dir = dir.substring(0, dir.lastIndexOf("/") + 1);
+ return dir;
+ }
+ if (path.startsWith("./")) {
+ path = file.directory + path.substring(2);
+ }
+ if (path.substr(path.length - 1, 1) !== "/") {
+ path = path + "/";
+ }
+ return path;
+ },
+ updateDirectoryImportList: function () {
+ if (!this.imports) {
+ this.imports = {};
+ }
+ var dir, f;
+ var seenDirs = {};
+ for (var i = 0; i < this.server.files.length; i++) {
+ var file = this.server.files[i];
+ dir = file.directory;
+ f = file.nameExt;
+ if (!dir) {
+ // Resolve the directory name and file name/extension
+ dir = file.directory = this.resolveDirectory(file, null);
+ f = file.nameExt = this.getFileNameAndExtension(file);
+ }
+ seenDirs[dir] = true;
+ // No file scope means the file was recently added/changed and we should
+ // update its import reference
+ if (!file.scope) {
+ // Check for a valid QML Object Identifier
+ if (f.extension === "qml") {
+ var ch = f.name.charAt(0);
+ if (ch.toUpperCase() === ch && f.name.indexOf(".") === -1) {
+ // Create the array for this directory if necessary
+ if (!this.imports[dir]) {
+ this.imports[dir] = {};
+ }
+
+ // Create an Obj to represent this import
+ var obj = new infer.Obj(null, f.name);
+ obj.origin = file.name;
+ this.imports[dir][f.name] = obj;
+ }
+ }
+ }
+ }
+ for (dir in this.imports) {
+ if (!(dir in seenDirs)) {
+ this.imports[dir] = undefined;
+ }
+ }
+ },
+ getFileNameAndExtension: function (file) {
+ var fileName = file.name.substring(file.name.lastIndexOf("/") + 1);
+ var dot = fileName.lastIndexOf(".");
+ return {
+ name: dot >= 0 ? fileName.substring(0, dot) : fileName,
+ extension: fileName.substring(dot + 1)
+ };
+ },
+ resolveObject: function (loc, name) {
+ return loc[name];
+ },
+ defineImport: function (scope, loc, name, obj) {
+ var prop = scope.defProp(name);
+ var objRef = new ObjRef(loc, name, this);
+ prop.objType = objRef;
+ objRef.propagate(prop);
+ },
+ defineImports: function (file, scope) {
+ scope = scope || file.scope;
+
+ // Add any imports from the current directory
+ var imports = this.imports[file.directory];
+ var f = file.nameExt;
+ if (imports) {
+ for (var name in imports) {
+ if (f.name !== name) {
+ this.defineImport(scope, imports, name, imports[name]);
+ }
+ }
+ }
- ScopeBuilder.prototype.getRootScope = function () {
- return this.scopes.length > 0 ? this.scopes[0] : null;
+ // Walk the AST for any imports
+ var ih = this;
+ walk.simple(file.ast, {
+ QMLImportStatement: function (node) {
+ var prop = null;
+ var scope = file.scope;
+ if (node.qualifier) {
+ prop = file.scope.defProp(node.qualifier.id.name, node.qualifier.id);
+ prop.origin = file.name;
+ var obj = new infer.Obj(null, node.qualifier.id.name);
+ obj.propagate(prop);
+ prop.objType = obj;
+ scope = obj;
+ }
+ if (node.directory) {
+ var dir = ih.resolveDirectory(file, node.directory.value);
+ var imports = ih.imports[dir];
+ if (imports) {
+ for (var name in imports) {
+ ih.defineImport(scope, imports, name, imports[name]);
+ }
+ }
+ }
+ }
+ });
+ },
+ createQMLObjectType: function (file, node, isRoot) {
+ // Find the imported object
+ var obj = this.getQMLObjectType(file, node.id);
+ // If this is the root, connect the imported object to the root object
+ if (isRoot) {
+ var tmp = this.getRootQMLObjectType(file, node.id);
+ if (tmp) {
+ // Hook up the Obj Reference
+ tmp.proto = obj;
+ obj = tmp;
+ obj.originNode = node.id;
+
+ // Break any cyclic dependencies
+ while ((tmp = tmp.proto)) {
+ if (tmp.resolve() == obj.resolve()) {
+ tmp.proto = null;
+ }
+ }
+ }
+ }
+ return obj;
+ },
+ getQMLObjectType: function (file, qid) {
+ var prop = findProp(qid, file.scope);
+ if (prop) {
+ return prop.objType;
+ }
+ return new infer.Obj(null, qid.name);
+ },
+ getRootQMLObjectType: function (file, qid) {
+ var f = file.nameExt;
+ var imports = this.imports[file.directory];
+ if (imports && imports[f.name]) {
+ return imports[f.name];
+ }
+ return new infer.Obj(null, qid.name);
+ }
};
- ScopeBuilder.prototype.isRootScope = function (scope) {
- return scope === this.rootScope();
+ // 'isInteger' taken from infer.js
+ function isInteger(str) {
+ var c0 = str.charCodeAt(0);
+ if (c0 >= 48 && c0 <= 57) return !/\D/.test(str);
+ else return false;
+ }
+
+ /*
+ * We have to redefine 'hasProp' to make it work with our scoping. The original 'hasProp'
+ * function checked proto.props instead of using proto.hasProp.
+ */
+ infer.Obj.prototype.hasProp = function (prop, searchProto) {
+ if (isInteger(prop)) prop = this.normalizeIntegerProp(prop);
+ var found = this.props[prop];
+ if (searchProto !== false && this.proto && !found)
+ found = this.proto.hasProp(prop, true);
+ return found;
};
- ScopeBuilder.prototype.newObjScope = function (node) {
- var rootScope = this.getRootScope(), scope;
- if (!rootScope) {
- scope = new infer.Scope(this.file.scope, node);
- } else {
- scope = new infer.Scope(rootScope, node);
- }
- scope.name = "<qml-obj>";
- this.scopes.push(scope);
- return scope;
+ // Creating a resolve function on 'infer.Obj' so we can simplify some of our 'ObjRef' logic
+ infer.Obj.prototype.resolve = function () {
+ return this;
};
- ScopeBuilder.prototype.forEachObjScope = function (callback) {
- for (var i = 0; i < this.scopes.length; i++) {
- var scope = this.scopes[i];
- if (scope.name === "<qml-obj>") {
- callback(scope);
+ /*
+ * QML Object Reference
+ *
+ * An ObjRef behaves exactly the same as an ordinary 'infer.Obj' object, except that it
+ * mirrors its internal state to a referenced object. This object is resolved by the QML
+ * Import Handler each time the ObjRef is accessed (including getting and setting internal
+ * variables). In theory this means we don't have to know at runtime whether or not an
+ * object is an ObjRef or an infer.Obj.
+ */
+ var ObjRef = function (loc, lookup, ih) {
+ // Using underscores for property names so we don't accidentally collide with any
+ // 'infer.Obj' property names (which would cause a stack overflow if we were to
+ // try to access them here).
+ this._loc = loc;
+ this._objLookup = lookup;
+ this._ih = ih;
+ var obj = this.resolve();
+ // Use Object.defineProperty to setup getter and setter methods that delegate
+ // to the resolved object's properties. We only need to do this once since all
+ // 'infer.Obj' objects should have the same set of property names.
+ for (var propertyName in obj) {
+ if (!(obj[propertyName] instanceof Function)) {
+ (function () {
+ var prop = propertyName;
+ Object.defineProperty(this, prop, {
+ enumerable: true,
+ get: function () {
+ return this.resolve()[prop];
+ },
+ set: function (value) {
+ this.resolve()[prop] = value;
+ }
+ });
+ }).call(this);
}
}
};
-
- ScopeBuilder.prototype.getObjScope = function (scope) {
- while (scope && scope.name != "<qml-obj>") {
- scope = scope.prev;
+ ObjRef.prototype = extend(infer.Type.prototype, {
+ resolve: function () {
+ return this._ih.resolveObject(this._loc, this._objLookup);
}
- return scope;
- };
-
- ScopeBuilder.prototype.getIDScope = function () {
- return this.file.qmlIDScope;
- };
+ });
+ (function () {
+ // Wire up all base functions to use the resolved object's implementation
+ for (var _func in infer.Obj.prototype) {
+ if (_func !== "resolve") {
+ (function () {
+ var fn = _func;
+ ObjRef.prototype[fn] = function () {
+ return this.resolve()[fn](arguments[0], arguments[1], arguments[2], arguments[3]);
+ };
+ })();
+ }
+ }
+ })();
- ScopeBuilder.prototype.newPropertyScope = function (objScope, node) {
- var propScope = new infer.Scope(null, node);
- propScope.name = "<qml-prop>";
- propScope.objScope = objScope;
- this.propScopes.push(propScope);
- return propScope;
+ /*
+ * QML Object Scope (inherits methods from infer.Scope)
+ *
+ * A QML Object Scope does not contain its own properties. Instead, its properties
+ * are defined in its given Object Type and resolved from there. Any properties
+ * defined within the Object Type are visible without qualifier to any downstream
+ * scopes.
+ */
+ var QMLObjScope = exports.QMLObjScope = function (prev, originNode, objType) {
+ infer.Scope.call(this, prev, originNode, false);
+ this.objType = objType;
};
+ QMLObjScope.prototype = extend(infer.Scope.prototype, {
+ hasProp: function (prop, searchProto) {
+ // Search for a property in the Object type.
+ // Always search the Object Type's prototype as well
+ var found = this.objType.hasProp(prop, true);
+ if (found) {
+ return found;
+ }
- ScopeBuilder.prototype.forEachPropertyScope = function (callback) {
- for (var i = 0; i < this.propScopes.length; i++) {
- callback(this.propScopes[i], this.propScopes[i].objScope);
+ // Search for a property in the prototype (previous scope)
+ if (this.proto && searchProto !== false) {
+ return this.proto.hasProp(prop, searchProto);
+ }
+ },
+ defProp: function (prop, originNode) {
+ return this.objType.defProp(prop, originNode);
+ },
+ removeProp: function (prop) {
+ return this.objType.removeProp(prop);
+ },
+ gatherProperties: function (f, depth) {
+ // Gather properties from the Object Type and its prototype(s)
+ var obj = this.objType;
+ var callback = function (prop, obj, d) {
+ f(prop, obj, depth);
+ };
+ while (obj) {
+ obj.gatherProperties(callback, depth);
+ obj = obj.proto;
+ }
+ // gather properties from the prototype (previous scope)
+ if (this.proto) {
+ this.proto.gatherProperties(f, depth + 1);
+ }
}
- };
+ });
- ScopeBuilder.prototype.newJSScope = function (scope, node) {
- var jsScope = new infer.Scope(scope, node);
- jsScope.name = "<qml-js>";
- var curOrigin = infer.cx().curOrigin;
- for (var i = 0; i < this.jsDefs.length; ++i)
- def.load(this.jsDefs[i], jsScope);
- infer.cx().curOrigin = curOrigin;
- this.jsScopes.push(jsScope);
- return jsScope;
+ /*
+ * QML Member Scope (inherits methods from infer.Scope)
+ *
+ * A QML Member Scope is a bit of a special case when it comes to QML scoping. Like
+ * the QML Object Scope, it does not contain any properties of its own. The reason
+ * that it is special is it only gathers properties from its immediate predecessor
+ * that aren't functions (i.e. They don't have the 'isFunction' flag set. The
+ * 'isFunction' flag is created by QML signal properties and JavaScript functions
+ * that are QML Members.)
+ */
+ var QMLMemScope = exports.QMLMemScope = function (prev, originNode, fileScope) {
+ infer.Scope.call(this, prev, originNode, false);
+ this.fileScope = fileScope;
};
+ QMLMemScope.prototype = extend(infer.Scope.prototype, {
+ hasProp: function (prop, searchProto) {
+ // Search for a property in the prototype
+ var found = null;
+ if (this.proto) {
+ // Don't continue searching after the previous scope
+ found = this.proto.hasProp(prop, false);
+ if (found && !found.isFunction) {
+ return found;
+ }
+ }
- ScopeBuilder.prototype.forEachJSScope = function (callback) {
- for (var i = 0; i < this.jsScopes.length; i++) {
- callback(this.jsScopes[i]);
+ // Search for a property in the file Scope
+ if (this.fileScope) {
+ return this.fileScope.hasProp(prop, searchProto);
+ }
+ },
+ defProp: function (prop, originNode) {
+ return this.prev.defProp(prop, originNode);
+ },
+ removeProp: function (prop) {
+ return this.prev.removeProp(prop);
+ },
+ gatherProperties: function (f, depth) {
+ // Gather properties from the prototype (previous scope)
+ var found = null;
+ if (this.proto) {
+ this.proto.gatherProperties(function (prop, obj, d) {
+ // Don't continue passed the predecessor by checking depth
+ if (d === depth) {
+ var propObj = obj.hasProp(prop);
+ if (propObj && !propObj.isFunction) {
+ f(prop, obj, d);
+ }
+ }
+ }, depth);
+ }
+ // Gather properties from the file Scope
+ this.fileScope.gatherProperties(f, depth);
}
- };
+ });
- ScopeBuilder.prototype.newSignalScope = function (scope, node) {
- var sigScope = new infer.Scope(scope, node);
- sigScope.name = "<qml-signal>";
- this.sigScopes.push(sigScope);
- return sigScope
+ /*
+ * QML JavaScript Scope (inherits methods from infer.Scope)
+ *
+ * A QML JavaScript Scope also contains references to the file's ID Scope, the global
+ * JavaScript Scope, and a possible function parameter scope. Most likely, this
+ * scope will not contain its own properties. The resolution order for 'getProp' and
+ * 'hasProp' are:
+ * 1. The ID Scope
+ * 2. This Scope's properties
+ * 3. The Function Scope (if it exists)
+ * 4. The JavaScript Scope
+ * 5. The Previous Scope in the chain
+ */
+ var QMLJSScope = exports.QMLJSScope = function (prev, originNode, idScope, jsScope, fnScope) {
+ infer.Scope.call(this, prev, originNode, false);
+ this.idScope = idScope;
+ this.jsScope = jsScope;
+ this.fnScope = fnScope;
};
+ QMLJSScope.prototype = extend(infer.Scope.prototype, {
+ hasProp: function (prop, searchProto) {
+ if (isInteger(prop)) {
+ prop = this.normalizeIntegerProp(prop);
+ }
+ // Search the ID scope
+ var found = null;
+ if (this.idScope) {
+ found = this.idScope.hasProp(prop, searchProto);
+ }
+ // Search the current scope
+ if (!found) {
+ found = this.props[prop];
+ }
+ // Search the Function Scope
+ if (!found && this.fnScope) {
+ found = this.fnScope.hasProp(prop, searchProto);
+ }
+ // Search the JavaScript Scope
+ if (!found && this.jsScope) {
+ found = this.jsScope.hasProp(prop, searchProto);
+ }
+ // Search the prototype (previous scope)
+ if (!found && this.proto && searchProto !== false) {
+ found = this.proto.hasProp(prop, searchProto);
+ }
+ return found;
+ },
+ gatherProperties: function (f, depth) {
+ // Gather from the ID Scope
+ if (this.idScope) {
+ this.idScope.gatherProperties(f, depth);
+ }
+ // Gather from the current scope
+ for (var prop in this.props) {
+ f(prop, this, depth);
+ }
+ // Gather from the Function Scope
+ if (this.fnScope) {
+ this.fnScope.gatherProperties(f, depth);
+ }
+ // Gather from the JS Scope
+ if (this.jsScope) {
+ this.jsScope.gatherProperties(f, depth);
+ }
+ // Gather from the prototype (previous scope)
+ if (this.proto) {
+ this.proto.gatherProperties(f, depth + 1);
+ }
+ }
+ });
- ScopeBuilder.prototype.hasFunctionScope = function (objScope) {
- var found = null;
- this.forEachFunctionScope(function (scope) {
- if (scope.objScope == objScope) {
- found = scope;
+ // QML Scope Builder
+ var ScopeBuilder = function (file, jsDefs) {
+ // File Scope
+ this.scope = file.scope;
+ this.file = file;
+ // ID Scope
+ this.idScope = new infer.Scope();
+ this.idScope.name = "<qml-id>";
+ // JavaScript Scope
+ this.jsScope = new infer.Scope();
+ this.jsScope.name = "<qml-js>";
+ var curOrigin = infer.cx().curOrigin;
+ for (var i = 0; i < jsDefs.length; ++i) {
+ def.load(jsDefs[i], this.jsScope);
+ }
+ infer.cx().curOrigin = curOrigin;
+ };
+ ScopeBuilder.prototype = {
+ newObjScope: function (node) {
+ var obj = qmlImportHandler.createQMLObjectType(this.file, node, !this.rootScope);
+ var scope = new QMLObjScope(this.rootScope || this.scope, node, obj);
+ scope.name = "<qml-obj>";
+ if (!this.rootScope) {
+ this.rootScope = scope;
}
- });
- return found;
+ return scope;
+ },
+ getIDScope: function () {
+ return this.idScope;
+ },
+ newMemberScope: function (objScope, node) {
+ var memScope = new QMLMemScope(objScope, node, this.scope);
+ memScope.name = "<qml-member>";
+ return memScope;
+ },
+ newJSScope: function (scope, node, fnScope) {
+ var jsScope = new QMLJSScope(scope, node, this.idScope, this.jsScope, fnScope);
+ jsScope.name = "<qml-js>";
+ return jsScope;
+ },
};
- // Helper for adding a property to a scope.
+ // Helper for adding a variable to a scope.
function addVar(scope, node) {
return scope.defProp(node.name, node);
}
// Helper for finding a property in a scope.
function findProp(node, scope, pos) {
- if (pos == null || pos < 0) {
+ if (pos === null || pos === undefined || pos < 0) {
pos = Number.MAX_VALUE;
}
if (node.type === "QMLQualifiedID") {
@@ -161,124 +538,11 @@
return null;
}
- // Helper for getting the server from the current context
- function getServer() {
- var parent = infer.cx().parent;
- return parent instanceof tern.Server ? parent : null;
- }
-
- // Helper for getting the current file's scope builder
+ // Helper for getting the current context's scope builder
function getScopeBuilder() {
return infer.cx().qmlScopeBuilder;
}
- // Object which holds two scopes
- function Scopes(object, property) {
- this.object = object;
- this.property = property || object;
- }
-
- function extendTernScopeGatherer(scopeGatherer) {
- // Build up the scopes in the QML document using the scopeGatherer. A second
- // pass will be done after the scopeGatherer executes in order to finalize a
- // few of the extra scopes such as the JavaScript, ID, Property, and Signal
- // Handler scopes.
- scopeGatherer["QMLObjectLiteral"] = function (node, scope, c) {
- var inner = node.scope = getScopeBuilder().newObjScope(node);
- c(node.body, inner);
- };
- scopeGatherer["QMLMemberBlock"] = function (node, scope, c) {
- var s = infer.cx().curOrigin;
- var propertyScope = node.scope = getScopeBuilder().newPropertyScope(scope, node);
- var scopes = new Scopes(scope, propertyScope);
- for (var i = 0; i < node.members.length; i++) {
- var member = node.members[i];
- if (member.type === "FunctionDeclaration") {
- c(member, scope);
-
- // Insert the JavaScript scope after the Function has had a chance to build it's own scope
- var jsScope = getScopeBuilder().newJSScope(scope, member);
- member.scope.prev = member.scope.proto = jsScope;
-
- // Indicate that the property is a function
- var prop = scope.hasProp(member.id.name);
- if (prop) {
- prop.isFunction = true;
- }
- } else if (member.type === "QMLPropertyDeclaration" || member.type === "QMLPropertyBinding") {
- c(member, scopes);
- } else {
- c(member, scope);
- }
- }
- };
- scopeGatherer["QMLPropertyDeclaration"] = function (node, scopes, c) {
- var inner = scopes.object;
- var prop = addVar(inner, node.id);
- if (node.binding) {
- node.binding.scope = inner;
- c(node.binding, inner);
- }
- };
- scopeGatherer["QMLPropertyBinding"] = function (node, scopes, c) {
- // Create a JavaScript scope if init is a JavaScript environment
- var inner = node.binding.scope = scopes.object;
- // Check for the 'id' property being set
- if (node.id.name == "id") {
- if (node.binding.type === "QMLScriptBinding") {
- var binding = node.binding;
- if (!binding.block && binding.script.type === "Identifier") {
- node.prop = addVar(getScopeBuilder().getIDScope(), binding.script);
- }
- }
- } else {
- // If this appears to be a signal handler, pre-emptively create a new scope that
- // will store references to the signal's arguments
- if (node.id.name.startsWith("on")) {
- inner = node.binding.scope = new infer.Scope(inner, node);
- }
- }
-
- // Delegate down to the expression
- c(node.binding, inner);
- };
- scopeGatherer["QMLScriptBinding"] = function (node, scope, c) {
- var inner = node.scope = getScopeBuilder().newJSScope(node.scope || scope, node);
- c(node.script, inner);
- };
- scopeGatherer["QMLStatementBlock"] = function (node, scope, c) {
- var inner = getScopeBuilder().newJSScope(node.scope || scope, node);
- node.scope = inner;
- for (var i = 0; i < node.statements.length; i++) {
- c(node.statements[i], inner, "Statement");
- }
- };
- scopeGatherer["QMLSignalDefinition"] = function (node, scope, c) {
- // Scope Builder
- var sb = getScopeBuilder();
-
- // Define the signal arguments in their own separate scope
- var argNames = [],
- argVals = [];
- var sigScope = sb.newSignalScope(scope, node);
- for (var i = 0; i < node.params.length; i++) {
- var param = node.params[i];
- argNames.push(param.id.name);
- argVals.push(addVar(sigScope, param.id));
- }
-
- // Define the signal function type which can be referenced from JavaScript
- var sig = addVar(scope, node.id);
- sig.isFunction = true;
- sig.sigType = new infer.Fn(node.id.name, new infer.AVal, argVals, argNames, infer.ANull);
- sig.sigType.sigScope = sigScope;
-
- // Define the signal handler property
- var handler = scope.defProp(getSignalHandlerName(node.id.name), node.id);
- handler.sig = sig.sigType;
- }
- }
-
// 'infer' taken from infer.js
function inf(node, scope, out, name) {
var handler = infer.inferExprVisitor[node.type];
@@ -288,19 +552,19 @@
// Infers the property's type from its given primitive value
function infKind(kind, out) {
switch (kind) {
- case "int":
- case "double":
- case "real":
- infer.cx().num.propagate(out);
- break;
- case "string":
- case "color":
- infer.cx().str.propagate(out);
- break;
- case "boolean":
- infer.cx().bool.propagate(out);
- break;
- }
+ case "int":
+ case "double":
+ case "real":
+ infer.cx().num.propagate(out);
+ break;
+ case "string":
+ case "color":
+ infer.cx().str.propagate(out);
+ break;
+ case "boolean":
+ infer.cx().bool.propagate(out);
+ break;
+ }
}
// 'ret' taken from infer.js
@@ -314,199 +578,306 @@
// 'fill' taken from infer.js
function fill(f) {
- return function(node, scope, out, name) {
- if (!out) out = new AVal;
+ return function (node, scope, out, name) {
+ if (!out) out = new infer.AVal();
f(node, scope, out, name);
return out;
};
}
- function extendTernInferExprVisitor(inferExprVisitor) {
- // Extend the inferExprVisitor methods
- inferExprVisitor["QMLStatementBlock"] = ret(function (node, scope, name) {
- return infer.ANull; // Statement blocks have no type
- });
- inferExprVisitor["QMLScriptBinding"] = fill(function (node, scope, out, name) {
- return inf(node.script, node.scope || scope, out, name);
- });
- inferExprVisitor["QMLObjectLiteral"] = ret(function (node, scope, name) {
- return node.scope.objType;
- });
- }
-
+ // Helper method to get the last index of an array
function getLastIndex(arr) {
return arr[arr.length - 1];
}
+ // Helper method to get the signal handler name of a signal function
function getSignalHandlerName(str) {
return "on" + str.charAt(0).toUpperCase() + str.slice(1);
}
- function extendTernInferWrapper(inferWrapper) {
- // Extend the inferWrapper methods
- inferWrapper["QMLObjectLiteral"] = function (node, scope, c) {
- // Define a new Obj which represents this Object Literal
- var obj = node.scope.objType = new infer.Obj(true, node.id.name);
- // node.scope will contain all object properties so we don't have to walk the AST to find them
- node.scope.forAllProps(function (name, prop, curr) {
- if (curr) {
- // Copy the property into the new type so that references to both of them
- // will update the same object.
- obj.props[name] = prop;
- }
- });
- c(node.body, node.scope);
- };
- inferWrapper["QMLMemberBlock"] = function (node, scope, c) {
- var scopes = new Scopes(scope, node.scope);
- for (var i = 0; i < node.members.length; i++) {
- var member = node.members[i];
- if (member.type === "QMLPropertyDeclaration" || member.type === "QMLPropertyBinding") {
- c(member, scopes);
- } else {
- c(member, scope);
+ // Object which holds two scopes. Used to store both the Member Scope and Object
+ // Scope for QML Property Bindings and Declarations.
+ function Scopes(obj, mem) {
+ this.object = obj;
+ this.member = mem;
+ }
+
+ // Helper to add functionality to a set of walk methods
+ function extendWalk(walker, funcs) {
+ for (var prop in funcs) {
+ walker[prop] = funcs[prop];
+ }
+ }
+
+ function extendTernScopeGatherer(scopeGatherer) {
+ // Extend the Tern scopeGatherer to build up our custom QML scoping
+ extendWalk(scopeGatherer, {
+ QMLObjectLiteral: function (node, scope, c) {
+ var inner = node.scope = getScopeBuilder().newObjScope(node);
+ c(node.body, inner);
+ },
+ QMLMemberBlock: function (node, scope, c) {
+ var memScope = node.scope = getScopeBuilder().newMemberScope(scope, node);
+ for (var i = 0; i < node.members.length; i++) {
+ var member = node.members[i];
+ if (member.type === "FunctionDeclaration") {
+ c(member, scope);
+
+ // Insert the JavaScript scope after the Function has had a chance to build it's own scope
+ var jsScope = getScopeBuilder().newJSScope(scope, member, member.scope);
+ jsScope.fnType = member.scope.fnType;
+ member.scope.prev = member.scope.proto = null;
+ member.scope = jsScope;
+
+ // Indicate that the property is a function
+ var prop = scope.hasProp(member.id.name);
+ if (prop) {
+ prop.isFunction = true;
+ }
+ } else if (member.type === "QMLPropertyDeclaration" || member.type === "QMLPropertyBinding") {
+ c(member, new Scopes(scope, memScope));
+ } else {
+ c(member, scope);
+ }
}
- }
- };
- inferWrapper["QMLPropertyDeclaration"] = function (node, scopes, c) {
- var prop = findProp(node.id, scopes.property);
- if (prop) {
- infKind(node.kind, prop);
+ },
+ QMLPropertyDeclaration: function (node, scopes, c) {
+ var prop = addVar(scopes.member, node.id);
if (node.binding) {
c(node.binding, scopes.object);
- inf(node.binding, scopes.object, prop, node.id.name);
}
+ },
+ QMLPropertyBinding: function (node, scopes, c) {
+ // Check for the 'id' property being set
+ if (node.id.name == "id") {
+ if (node.binding.type === "QMLScriptBinding") {
+ var binding = node.binding;
+ if (!binding.block && binding.script.type === "Identifier") {
+ node.prop = addVar(getScopeBuilder().getIDScope(), binding.script);
+ }
+ }
+ }
+ // Delegate down to the expression
+ c(node.binding, scopes.object);
+ },
+ QMLScriptBinding: function (node, scope, c) {
+ var inner = node.scope = getScopeBuilder().newJSScope(scope, node);
+ c(node.script, inner);
+ },
+ QMLStatementBlock: function (node, scope, c) {
+ var inner = getScopeBuilder().newJSScope(scope, node);
+ node.scope = inner;
+ for (var i = 0; i < node.statements.length; i++) {
+ c(node.statements[i], inner, "Statement");
+ }
+ },
+ QMLSignalDefinition: function (node, scope, c) {
+ // Scope Builder
+ var sb = getScopeBuilder();
+
+ // Define the signal arguments in their own separate scope
+ var argNames = [];
+ var argVals = [];
+ var sigScope = new infer.Scope(null, node);
+ for (var i = 0; i < node.params.length; i++) {
+ var param = node.params[i];
+ argNames.push(param.id.name);
+ argVals.push(addVar(sigScope, param.id));
+ }
+
+ // Define the signal function type which can be referenced from JavaScript
+ var sig = addVar(scope, node.id);
+ sig.isFunction = true;
+ sig.sigType = new infer.Fn(node.id.name, new infer.AVal(), argVals, argNames, infer.ANull);
+ sig.sigType.sigScope = sigScope;
+
+ // Define the signal handler property
+ var handler = scope.defProp(getSignalHandlerName(node.id.name), node.id);
+ handler.sig = sig.sigType;
}
- };
- inferWrapper["QMLPropertyBinding"] = function (node, scopes, c) {
- c(node.binding, scopes.object);
- // Check for the 'id' property being set
- if (node.id.name === "id") {
- if (node.binding.type === "QMLScriptBinding") {
- var binding = node.binding;
- if (binding.script.type === "Identifier") {
- scopes.object.objType.propagate(node.prop);
+ });
+ }
+
+ function extendTernInferExprVisitor(inferExprVisitor) {
+ // Extend the inferExprVisitor methods
+ extendWalk(inferExprVisitor, {
+ QMLStatementBlock: ret(function (node, scope, name) {
+ return infer.ANull; // TODO: check return statements
+ }),
+ QMLScriptBinding: fill(function (node, scope, out, name) {
+ return inf(node.script, node.scope, out, name);
+ }),
+ QMLObjectLiteral: ret(function (node, scope, name) {
+ return node.scope.objType;
+ })
+ });
+ }
+
+ function extendTernInferWrapper(inferWrapper) {
+ // Extend the inferWrapper methods
+ extendWalk(inferWrapper, {
+ QMLObjectLiteral: function (node, scope, c) {
+ c(node.body, node.scope);
+ },
+ QMLMemberBlock: function (node, scope, c) {
+ for (var i = 0; i < node.members.length; i++) {
+ var member = node.members[i];
+ if (member.type === "QMLPropertyDeclaration" || member.type === "QMLPropertyBinding") {
+ c(member, new Scopes(scope, node.scope));
+ } else {
+ c(member, scope);
}
}
- } else {
- var prop = findProp(node.id, scopes.property);
+ },
+ QMLPropertyDeclaration: function (node, scopes, c) {
+ var prop = findProp(node.id, scopes.member);
if (prop) {
- if (prop.sig) {
- // This is a signal handler and we should populate its scope with
- // the arguments from its parent function.
- prop.sig.sigScope.forAllProps(function (name, prop, curr) {
- if (curr) {
- node.binding.scope.props[name] = prop;
- }
- });
- } else {
- inf(node.binding, node.binding.scope, prop, getLastIndex(node.id.parts));
+ infKind(node.kind, prop);
+ if (node.binding) {
+ c(node.binding, scopes.object);
+ inf(node.binding, scopes.object, prop, node.id.name);
}
}
+ },
+ QMLPropertyBinding: function (node, scopes, c) {
+ c(node.binding, scopes.object);
+ // Check for the 'id' property being set
+ if (node.id.name === "id") {
+ if (node.binding.type === "QMLScriptBinding") {
+ var binding = node.binding;
+ if (binding.script.type === "Identifier") {
+ scopes.object.objType.propagate(node.prop);
+ }
+ }
+ } else {
+ var prop = findProp(node.id, scopes.member);
+ if (prop) {
+ if (prop.sig) {
+ // This is a signal handler
+ node.binding.scope.fnScope = prop.sig.sigScope;
+ } else {
+ inf(node.binding, scopes.object, prop, getLastIndex(node.id.parts));
+ }
+ }
+ }
+ },
+ QMLScriptBinding: function (node, scope, c) {
+ c(node.script, node.scope);
+ },
+ QMLStatementBlock: function (node, scope, c) {
+ for (var i = 0; i < node.statements.length; i++) {
+ c(node.statements[i], node.scope, "Statement");
+ }
+ },
+ QMLSignalDefinition: function (node, scope, c) {
+ var sig = scope.getProp(node.id.name);
+ for (var i = 0; i < node.params.length; i++) {
+ var param = node.params[i];
+ infKind(param.kind.name, sig.sigType.args[i]);
+ }
+ sig.sigType.retval = infer.ANull;
+ sig.sigType.propagate(sig);
+
+ var handler = scope.getProp(getSignalHandlerName(node.id.name));
+ var obj = new infer.Obj(true, "Signal Handler");
+ obj.propagate(handler);
}
- };
- inferWrapper["QMLStatementBlock"] = function (node, scope, c) {
- for (var i = 0; i < node.statements.length; i++) {
- c(node.statements[i], node.scope, "Statement");
- }
- };
- inferWrapper["QMLSignalDefinition"] = function (node, scope, c) {
- var sig = scope.getProp(node.id.name);
- for (var i = 0; i < node.params.length; i++) {
- var param = node.params[i];
- infKind(param.kind.name, sig.sigType.args[i]);
- }
- sig.sigType.retval = infer.ANull;
- sig.sigType.propagate(sig);
-
- var handler = scope.getProp(getSignalHandlerName(node.id.name));
- var obj = new infer.Obj(true, "Signal Handler");
- obj.propagate(handler);
- }
+ });
}
function extendTernTypeFinder(typeFinder) {
// Extend the type finder to return valid types for QML AST elements
- typeFinder["QMLObjectLiteral"] = function (node, scope) {
- return node.scope.objType;
- };
- typeFinder["QMLMemberBlock"] = function (node, scope) {
- return infer.ANull;
- };
- typeFinder["FunctionDeclaration"] = function (node, scope) {
- return scope.name === "<qml-obj>" ? infer.ANull : undefined;
- };
- typeFinder["QMLScriptBinding"] = function (node, scope) {
- // Trick Tern into thinking this node is a type so that it will use
- // this node's scope when handling improperly written script bindings
- return infer.ANull;
- };
- typeFinder["QMLQualifiedID"] = function (node, scope) {
- return findProp(node, scope) || infer.ANull;
- };
- typeFinder["QML_ID"] = function (node, scope) {
- // Reverse the hack from search visitor before finding the property in
- // the id scope
- node.type = "Identifier";
- return findProp(node, getScopeBuilder().getIDScope());
- };
+ extendWalk(typeFinder, {
+ QMLObjectLiteral: function (node, scope) {
+ return node.scope.objType;
+ },
+ QMLMemberBlock: function (node, scope) {
+ return infer.ANull;
+ },
+ FunctionDeclaration: function (node, scope) {
+ // Quick little hack to get 'findExprAt' to find a Function Declaration which
+ // is a QML Object Member. All other Function Declarations are ignored.
+ return scope.name === "<qml-obj>" ? infer.ANull : undefined;
+ },
+ QMLScriptBinding: function (node, scope) {
+ // Trick Tern into thinking this node is a type so that it will use
+ // this node's scope when handling improperly written script bindings
+ return infer.ANull;
+ },
+ QMLQualifiedID: function (node, scope) {
+ return findProp(node, scope) || infer.ANull;
+ },
+ QML_ID: function (node, scope) {
+ // Reverse the hack from search visitor before finding the property in
+ // the id scope
+ node.type = "Identifier";
+ return findProp(node, getScopeBuilder().getIDScope());
+ }
+ });
}
function extendTernSearchVisitor(searchVisitor) {
// Extend the search visitor to traverse the scope properly
- searchVisitor["QMLObjectLiteral"] = function (node, scope, c) {
- c(node.body, node.scope);
- };
- searchVisitor["QMLMemberBlock"] = function (node, scope, c) {
- var scopes = new Scopes(scope, node.scope);
- for (var i = 0; i < node.members.length; i++) {
- var member = node.members[i];
- if (member.type === "QMLPropertyDeclaration" || member.type === "QMLPropertyBinding") {
- c(member, scopes);
- } else {
- c(member, scope);
+ extendWalk(searchVisitor, {
+ QMLObjectLiteral: function (node, scope, c) {
+ c(node.body, node.scope);
+ },
+ QMLMemberBlock: function (node, scope, c) {
+ for (var i = 0; i < node.members.length; i++) {
+ var member = node.members[i];
+ if (member.type === "QMLPropertyDeclaration" || member.type === "QMLPropertyBinding") {
+ c(member, new Scopes(scope, node.scope));
+ } else {
+ c(member, scope);
+ }
}
- }
- };
- searchVisitor["QMLSignalDefinition"] = function (node, scope, c) {
- c(node.id, scope);
- };
- searchVisitor["QMLPropertyDeclaration"] = function (node, scopes, c) {
- if (node.binding) {
- c(node.binding, node.binding.scope);
- }
- };
- searchVisitor["QMLPropertyBinding"] = function (node, scopes, c) {
- if (node.id.name === "id") {
- if (node.binding.type === "QMLScriptBinding") {
- var binding = node.binding;
- if (binding.script.type === "Identifier") {
- // Hack to bypass Tern's type finding algorithm which uses node.type instead
- // of the overriden type.
- binding.script.type = "QML_ID";
- c(binding.script, binding.scope, "QML_ID");
- binding.script.type = "Identifier";
+ },
+ QMLSignalDefinition: function (node, scope, c) {
+ c(node.id, scope);
+ },
+ QMLPropertyDeclaration: function (node, scopes, c) {
+ c(node.id, scopes.member);
+ if (node.binding) {
+ c(node.binding, scopes.object);
+ }
+ },
+ QMLPropertyBinding: function (node, scopes, c) {
+ if (node.id.name === "id") {
+ if (node.binding.type === "QMLScriptBinding") {
+ var binding = node.binding;
+ if (binding.script.type === "Identifier") {
+ // Hack to bypass Tern's type finding algorithm which uses node.type instead
+ // of the overriden type.
+ binding.script.type = "QML_ID";
+ c(binding.script, binding.scope, "QML_ID");
+ binding.script.type = "Identifier";
+ }
+ }
+ var prop = findProp(node.id, scopes.member);
+ if (!prop) {
+ return;
}
}
- var prop = findProp(node.id, scopes.property);
- if (!prop) {
- return;
+ c(node.id, scopes.member);
+ c(node.binding, scopes.object);
+ },
+ QMLScriptBinding: function (node, scope, c) {
+ c(node.script, node.scope);
+ },
+ QML_ID: function (node, st, c) {
+ // Ignore
+ },
+ QMLStatementBlock: function (node, scope, c) {
+ for (var i = 0; i < node.statements.length; i++) {
+ c(node.statements[i], node.scope, "Statement");
}
}
- c(node.id, scopes.property);
- c(node.binding, node.binding.scope);
- };
- searchVisitor["QML_ID"] = function(node, st, c) {};
- searchVisitor["QMLStatementBlock"] = function (node, scope, c) {
- for (var i = 0; i < node.statements.length; i++) {
- c(node.statements[i], node.scope, "Statement");
- }
- };
+ });
}
/*
- * Prepares acorn to consume QML syntax rather than standard JavaScript
- */
+ * Prepares acorn to consume QML syntax rather than standard JavaScript
+ */
function preParse(text, options) {
// Force ECMA Version to 5
options.ecmaVersion = 5;
@@ -514,45 +885,58 @@
// Register qml plugin with main parser
var plugins = options.plugins;
if (!plugins) plugins = options.plugins = {};
- plugins["qml"] = true;
+ plugins.qml = true;
// Register qml plugin with loose parser
var pluginsLoose = options.pluginsLoose;
if (!pluginsLoose) pluginsLoose = options.pluginsLoose = {};
- pluginsLoose["qml"] = true;
+ pluginsLoose.qml = true;
}
/*
- * Performs a second pass over the generated scopes directly after the scopeGatherer
- * executes and before type inferral begins. This pass is used to finalize any
- * scopes that require information from other scopes created during the scope
- * gatherer's pass. This includes the JavaScript scopes which require information
- * from the QML ID scope, as well as the Property scopes which require information
- * from their respective Object Literal scopes.
- */
- function scopeGatheringSecondPass(ast, scope) {
- var scopeBuilder = getScopeBuilder();
-
- // Aggregate IDs and Signal Handlers for the JavaScript scopes
- scopeBuilder.forEachJSScope(function (scope) {
- // Merge any QML IDs into all JavaScript scopes
- scopeBuilder.getIDScope().forAllProps(function (name, prop, curr) {
- if (curr) {
- // Since QML checks the idScope before all others, we can safely over-write
- // conflicting property names as they will be hidden anyway.
- scope.props[name] = prop;
- }
- });
- });
+ * Initializes the file's top level scope and creates a ScopeBuilder to facilitate
+ * the creation of QML scopes.
+ */
+ function beforeLoad(file) {
+ // We dont care for the Context's top scope
+ file.scope = null;
+
+ // Update the ImportHandler
+ qmlImportHandler.updateDirectoryImportList();
+
+ // Create the file's top scope
+ file.scope = new infer.Scope(infer.cx().topScope);
+ var name = file.name;
+ var end = file.name.lastIndexOf(".qml");
+ file.scope.name = end > 0 ? name.substring(0, end) : name;
+
+ // Get the ImportHandler to define imports for us
+ qmlImportHandler.defineImports(file, file.scope);
+
+ // Create the ScopeBuilder
+ var sb = new ScopeBuilder(file, infer.cx().parent.jsDefs);
+ infer.cx().qmlScopeBuilder = sb;
+ }
- // Aggregate properties for the property scopes
- scopeBuilder.forEachPropertyScope(function (scope, objScope) {
- objScope.forAllProps(function (name, prop, curr) {
- if (curr && !prop.isFunction) {
- scope.props[name] = prop;
- }
- });
- });
+ /*
+ * Helper to reset some of the internal state of the QML plugin when the server
+ * resets
+ */
+ function reset() {
+ qmlImportHandler.reset();
+ }
+
+ 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
+ // non Member Expressions that contain a '.' character. Will be much more
+ // accurate if we just roll our own completions for this in the future, but it
+ // works relatively well for now.
+ var cx = infer.cx();
+ cx.paths = {};
+ for (var prop in file.scope.props) {
+ cx.paths[prop] = file.scope[prop];
+ }
}
// Register the QML plugin in Tern
@@ -566,22 +950,16 @@
if (toFront) this.jsDefs.unshift(defs);
else this.jsDefs.push(defs);
if (this.cx) this.reset();
- }
+ };
+
+ // Create the QML Import Handler
+ qmlImportHandler = exports.importHandler = new ImportHandler(server);
// Hook into server signals
server.on("preParse", preParse);
- server.on("preInfer", scopeGatheringSecondPass);
- server.on("beforeLoad", function(file) {
- // Create the file's top scope
- file.scope = new infer.Scope(infer.cx().topScope);
- var name = file.name;
- var end = file.name.lastIndexOf(".qml");
- file.scope.name = end > 0 ? name.substring(0, end) : name;
-
- // Create the ScopeBuilder
- var sb = new ScopeBuilder(file, server.jsDefs);
- infer.cx().qmlScopeBuilder = sb;
- });
+ server.on("beforeLoad", beforeLoad);
+ server.on("postReset", reset);
+ server.on("completion", completions);
// Extend Tern's inferencing system to include QML syntax
extendTernScopeGatherer(infer.scopeGatherer);
@@ -590,4 +968,4 @@
extendTernTypeFinder(infer.typeFinder);
extendTernSearchVisitor(infer.searchVisitor);
});
-}) \ No newline at end of file
+}); \ No newline at end of file
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 5681747c39d..41be94a0262 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
@@ -24,8 +24,8 @@ var groupName;
function TestCase(group, code, run) {
this.code = code;
this.group = group;
- this.runTest = run || function (server, callback) {
- callback("fail", code, "runTest function was not provided.");
+ this.runTest = run || function (server, callback, code) {
+ callback("fail", this.code, "runTest function was not provided.");
};
}
@@ -37,19 +37,19 @@ exports.isolate = function (code) {
i--;
}
}
-}
+};
exports.groupStart = function (group) {
groupName = group;
-}
+};
exports.groupEnd = function () {
groupName = undefined;
-}
+};
exports.test = function (code, runTest) {
testCases.push(new TestCase(groupName || "Default", code, runTest));
-}
+};
exports.testCompletion = function (code, expected, beforeTest) {
testCases.push(new TestCase(groupName || "Default", code, function (server, callback) {
@@ -70,7 +70,7 @@ exports.testCompletion = function (code, expected, beforeTest) {
}
});
}, beforeTest));
-}
+};
exports.testDefinition = function (code, expected, beforeTest) {
testCases.push(new TestCase(groupName || "Default", code, function (server, callback) {
@@ -91,21 +91,21 @@ exports.testDefinition = function (code, expected, beforeTest) {
}
});
}, beforeTest));
-}
+};
exports.runTests = function (config, callback) {
for (var i = 0; i < testCases.length; ++i) {
var test = testCases[i];
if (test.group === config.group) {
var server = createServer();
- test.runTest(server, callback);
+ test.runTest(server, callback, test.code);
}
}
};
function createServer(defs) {
var plugins = {};
- plugins["qml"] = {};
+ plugins.qml = true;
var server = new tern.Server({
ecmaVersion: 5,
plugins : plugins,
@@ -114,12 +114,12 @@ function createServer(defs) {
return server;
}
-function assertCompletion(server, code, expected, pos, callback) {
- server.addFile("test1.qml", code);
+var assertCompletion = exports.assertCompletion = function (server, code, expected, pos, callback) {
+ server.addFile("main.qml", code);
server.request({
query : {
type: "completions",
- file: "test1.qml",
+ file: "main.qml",
end: pos,
types: true,
docs: false,
@@ -139,12 +139,12 @@ function assertCompletion(server, code, expected, pos, callback) {
});
};
-function assertDefinition(server, code, expected, pos, callback) {
- server.addFile("test1.qml", code);
+var assertDefinition = exports.assertDefinition = function (server, code, expected, pos, callback) {
+ server.addFile("main.qml", code);
server.request({
query : {
type: "definition",
- file: "test1.qml",
+ file: "main.qml",
end: pos,
types: true,
docs: false,
@@ -175,6 +175,7 @@ function addPath(str, pt) {
}
var misMatch = exports.misMatch = function (exp, act) {
+ var mis = null;
if (!exp || !act || (typeof exp != "object") || (typeof act != "object")) {
if (exp !== act) return ppJSON(exp) + " !== " + ppJSON(act);
} else if (exp instanceof RegExp || act instanceof RegExp) {
@@ -184,12 +185,12 @@ var misMatch = exports.misMatch = function (exp, act) {
if (!act.slice) return ppJSON(exp) + " != " + ppJSON(act);
if (act.length != exp.length) return "array length mismatch " + exp.length + " != " + act.length;
for (var i = 0; i < act.length; ++i) {
- var mis = misMatch(exp[i], act[i]);
+ mis = misMatch(exp[i], act[i]);
if (mis) return addPath(mis, i);
}
} else {
for (var prop in exp) {
- var mis = misMatch(exp[prop], act[prop]);
+ mis = misMatch(exp[prop], act[prop]);
if (mis) return addPath(mis, prop);
}
}
diff --git a/qt/org.eclipse.cdt.qt.core/tern-qml/test/ecma5-defs.js b/qt/org.eclipse.cdt.qt.core/tern-qml/test/ecma5-defs.js
index 952c6fbc27b..58fb3b127f4 100644
--- a/qt/org.eclipse.cdt.qt.core/tern-qml/test/ecma5-defs.js
+++ b/qt/org.eclipse.cdt.qt.core/tern-qml/test/ecma5-defs.js
@@ -8,6 +8,8 @@
* Contributors:
* QNX Software Systems - Initial API and implementation
*******************************************************************************/
+"use strict";
+
module.exports = {
"!name": "ecma5",
"!define": {
@@ -975,4 +977,4 @@ module.exports = {
"!url": "https://developer.mozilla.org/en-US/docs/JSON",
"!doc": "JSON (JavaScript Object Notation) is a data-interchange format. It closely resembles a subset of JavaScript syntax, although it is not a strict subset. (See JSON in the JavaScript Reference for full details.) It is useful when writing any kind of JavaScript-based application, including websites and browser extensions. For example, you might store user information in JSON format in a cookie, or you might store extension preferences in JSON in a string-valued browser preference."
}
-} \ No newline at end of file
+}; \ No newline at end of file
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 0af2df252c1..02f99944e14 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
@@ -8,7 +8,7 @@
* Contributors:
* QNX Software Systems - Initial API and implementation
*******************************************************************************/
-"use-strict";
+"use strict";
var driver = require("./driver.js");
@@ -20,7 +20,7 @@ function group(name) {
function groupEnd() {
if (typeof console === "object" && console.groupEnd) {
- console.groupEnd(name);
+ console.groupEnd();
}
}
@@ -29,8 +29,9 @@ function log(title, message) {
}
var stats, tests = [];
-tests.push(require("./test-completions.js"));
+tests.push(require("./test-scoping.js"));
tests.push(require("./test-finddef.js"));
+tests.push(require("./test-completions.js"));
function report(state, code, message) {
if (state != "ok") {++stats.failed; log(code, message);}
@@ -45,9 +46,9 @@ for (var i = 0; i < tests.length; i++) {
stats = test.stats = {testsRun: 0, failed: 0};
var config = test.config || {};
config.group = test.group;
- var t0 = +new Date;
+ var t0 = +new Date();
driver.runTests(config, report);
- test.stats.duration = +new Date - t0;
+ test.stats.duration = +new Date() - t0;
groupEnd();
}
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 50b1502703c..d9eb5f91666 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
@@ -8,58 +8,259 @@
* Contributors:
* QNX Software Systems - Initial API and implementation
*******************************************************************************/
-"use-strict";
+"use strict";
var driver = require("./driver.js");
var test = driver.test;
var testCompletion = driver.testCompletion;
+var assertCompletion = driver.assertCompletion;
var groupStart = driver.groupStart;
var groupEnd = driver.groupEnd;
-var group = exports.group = "Code Completion"
+var group = exports.group = "Code Completion";
groupStart(group);
-// Import Completions
-//testCompletion("import QtQu|", {
-// start: { line: 0, ch: 7 },
-// end: { line: 0, ch: 11 },
-// isProperty: false,
-// isObjectKey: false,
-// completions: [{ name: "QtQuick", type: "", origin: "QML" }]
-//});
-//
-//testCompletion("import QtQuick 2.|", {
-// start: { line: 0, ch: 17 },
-// end: { line: 0, ch: 17 },
-// isProperty: false,
-// isObjectKey: false,
-// completions: [
-// { name: "0", type: "number", origin: "QML" },
-// { name: "1", type: "number", origin: "QML" },
-// { name: "2", type: "number", origin: "QML" },
-// { name: "3", type: "number", origin: "QML" },
-// { name: "4", type: "number", origin: "QML" },
-// { name: "5", type: "number", origin: "QML" }
-// ]
-//});
-//
-//testCompletion('import "other.qml" as Other\n|', {
-// start: { line: 1, ch: 0 },
-// end: { line: 1, ch: 0 },
-// isProperty: false,
-// isObjectKey: false,
-// completions: [{ name: "Other", type: "?", origin: "test1.qml" }]
-//})
-//
-//testCompletion('My|', {
-// start: { line: 0, ch: 2 },
-// end: { line: 0, ch: 2 },
-// isProperty: false,
-// isObjectKey: false,
-// completions: [{ name: "MyObject", type: "Button", origin: "MyObject.qml" }]
-//}, function (server) {
-// server.addFile("MyObject.qml", "Button {\n\tproperty int width\n}");
-//});
+// Local Directory Completions
+testCompletion('My|', {
+ start: { line: 0, ch: 0 },
+ end: { line: 0, ch: 2 },
+ isProperty: false,
+ isObjectKey: false,
+ completions: [{ name: "MyObject", type: "MyObject", origin: "MyObject.qml" }]
+}, function (server) {
+ server.addFile("MyObject.qml", "Button {}");
+});
+
+testCompletion('import "./subdir/"\nSameDirTest {\n\t|\n}', {
+ start: { line: 2, ch: 1 },
+ end: { line: 2, ch: 1 },
+ isProperty: false,
+ isObjectKey: false,
+ completions: [
+ { name: "obj", type: "?", origin: "subdir/Button.qml" },
+ { name: "Button", type: "Button", origin: "subdir/Button.qml" },
+ { name: "SameDirTest", type: "SameDirTest", origin: "subdir/SameDirTest.qml" }
+ ]
+}, function (server) {
+ server.addFile("subdir/SameDirTest.qml", "Button {}");
+ server.addFile("subdir/Button.qml", "QtObject {property var obj}");
+});
+
+testCompletion('MyObject {\n\t|\n}', {
+ start: { line: 1, ch: 1 },
+ end: { line: 1, ch: 1 },
+ isProperty: false,
+ isObjectKey: false,
+ completions: [
+ { name: "width", type: "number", origin: "MyObject.qml" },
+ { name: "MyObject", type: "MyObject", origin: "MyObject.qml" }
+ ]
+}, function (server) {
+ server.addFile("MyObject.qml", "Button {\n\tproperty int width\n}");
+});
+
+testCompletion('MyObject {\n\tid: obj\n\ts: obj.|\n}', {
+ start: { line: 2, ch: 8 },
+ end: { line: 2, ch: 8 },
+ isProperty: true,
+ isObjectKey: false,
+ completions: [{ name: "width", type: "number", origin: "MyObject.qml" }]
+}, function (server) {
+ server.addFile("MyObject.qml", "Button {\n\tproperty int width\n}");
+});
+
+testCompletion('Button {\n\t|\n}', {
+ start: { line: 1, ch: 1 },
+ end: { line: 1, ch: 1 },
+ isProperty: false,
+ isObjectKey: false,
+ completions: [
+ { name: "onClicked", type: "Signal Handler", origin: "Button.qml" },
+ { name: "Button", type: "Button", origin: "Button.qml" }
+ ]
+}, function (server) {
+ server.addFile("Button.qml", "QtObject {\n\signal clicked(int mouseX, int mouseY)\n}");
+});
+
+testCompletion('CButton {\n\t|\n}', {
+ start: { line: 1, ch: 1 },
+ end: { line: 1, ch: 1 },
+ isProperty: false,
+ isObjectKey: false,
+ completions: [
+ { name: "height", type: "number", origin: "Button.qml" },
+ { name: "numClicks", type: "number", origin: "CButton.qml" },
+ { name: "text", type: "string", origin: "Button.qml" },
+ { name: "width", type: "number", origin: "Button.qml" },
+ { name: "Button", type: "Button", origin: "Button.qml" },
+ { name: "CButton", type: "CButton", origin: "CButton.qml" }
+ ]
+}, function (server) {
+ server.addFile("CButton.qml", "Button {\n\tproperty int numClicks\n}");
+ server.addFile("Button.qml", "QtObject {\n\tproperty string text\n\tproperty int width\n\tproperty int height\n}");
+});
+
+testCompletion('CButton {\n\tid:btn\n\ts: btn.|\n}', {
+ start: { line: 2, ch: 8 },
+ end: { line: 2, ch: 8 },
+ isProperty: true,
+ isObjectKey: false,
+ completions: [
+ { name: "height", type: "number", origin: "Button.qml" },
+ { name: "numClicks", type: "number", origin: "CButton.qml" },
+ { name: "text", type: "string", origin: "Button.qml" },
+ { name: "width", type: "number", origin: "Button.qml" }
+ ]
+}, function (server) {
+ server.addFile("CButton.qml", "Button {\n\tproperty int numClicks\n}");
+ server.addFile("Button.qml", "QtObject {\n\tproperty string text\n\tproperty int width\n\tproperty int height\n}");
+});
+
+// Directory Import Completions
+testCompletion('NotVisible {\n\t|\n}', {
+ start: { line: 1, ch: 1 },
+ end: { line: 1, ch: 1 },
+ isProperty: false,
+ isObjectKey: false,
+ completions: []
+}, function (server) {
+ server.addFile("subdir/NotVisible.qml", "QtObject {\n\signal clicked(int mouseX, int mouseY)\n}");
+});
+
+testCompletion('import "./subdir/"\nButton {\n\t|\n}', {
+ start: { line: 2, ch: 1 },
+ end: { line: 2, ch: 1 },
+ isProperty: false,
+ isObjectKey: false,
+ completions: [
+ { name: "onClicked", type: "Signal Handler", origin: "subdir/Button.qml" },
+ { name: "Button", type: "Button", origin: "subdir/Button.qml" }
+ ]
+}, function (server) {
+ server.addFile("subdir/Button.qml", "QtObject {\n\signal clicked(int mouseX, int mouseY)\n}");
+});
+
+testCompletion('import "./subdir/" as Controls\nControls.Button {\n\t|\n}', {
+ start: { line: 2, ch: 1 },
+ end: { line: 2, ch: 1 },
+ isProperty: false,
+ isObjectKey: false,
+ completions: [
+ { name: "onClicked", type: "Signal Handler", origin: "subdir/Button.qml" },
+ { name: "Controls", type: "Controls", origin: "main.qml" }
+ ]
+}, function (server) {
+ server.addFile("subdir/Button.qml", "QtObject {\n\signal clicked(int mouseX, int mouseY)\n}");
+});
+
+testCompletion('import "./subdir/" as Controls\nControls.|', {
+ start: { line: 1, ch: 9 },
+ end: { line: 1, ch: 9 },
+ isProperty: false,
+ isObjectKey: false,
+ completions: [
+ { name: "Button", type: "Button", origin: "subdir/Button.qml" }
+ ]
+}, function (server) {
+ server.addFile("subdir/Button.qml", "QtObject {\n\signal clicked(int mouseX, int mouseY)\n}");
+});
+
+testCompletion('import "./subdir/" as Controls\nControls.|.', {
+ start: { line: 1, ch: 9 },
+ end: { line: 1, ch: 9 },
+ isProperty: false,
+ isObjectKey: false,
+ completions: [
+ { name: "Button", type: "Button", origin: "subdir/Button.qml" }
+ ]
+}, function (server) {
+ server.addFile("subdir/Button.qml", "QtObject {\n\signal clicked(int mouseX, int mouseY)\n}");
+});
+
+testCompletion('import "./subdir/" as Controls\nControls..|', {
+ start: { line: 1, ch: 10 },
+ end: { line: 1, ch: 10 },
+ isProperty: false,
+ isObjectKey: false,
+ completions: []
+}, function (server) {
+ server.addFile("subdir/Button.qml", "QtObject {\n\signal clicked(int mouseX, int mouseY)\n}");
+});
+
+test("{Add File After Import}", function (server, callback, name) {
+ var failed;
+ assertCompletion(server, "", {
+ start: { line: 0, ch: 0 },
+ end: { line: 0, ch: 0 },
+ isProperty: false,
+ isObjectKey: false,
+ completions: []
+ }, 0, function (mis) {
+ failed = mis;
+ });
+ if (failed) {
+ return callback("fail", name, "- failed on initial file " + failed);
+ }
+ server.addFile("MyObject.qml", "QtObject {\n\tproperty var test\n}");
+ assertCompletion(server, "M", {
+ start: { line: 0, ch: 0 },
+ end: { line: 0, ch: 1 },
+ isProperty: false,
+ isObjectKey: false,
+ completions: [{ name: "MyObject", type: "MyObject", origin: "MyObject.qml" }]
+ }, 1, function (mis) {
+ failed = mis;
+ });
+ if (failed) {
+ return callback("fail", name, "- failed after adding file " + failed);
+ }
+ return callback("ok", name);
+});
+
+// Cyclic Dependency Completions
+testCompletion('Cyclic {\n\t|\n}', {
+ start: { line: 1, ch: 1 },
+ end: { line: 1, ch: 1 },
+ isProperty: false,
+ isObjectKey: false,
+ completions: [
+ { name: "test", type: "?", origin: "Cyclic.qml" },
+ { name: "Cyclic", type: "Cyclic", origin: "Cyclic.qml" }
+ ]
+}, function (server) {
+ server.addFile("Cyclic.qml", "Cyclic {\n\property var test\n}");
+});
+
+testCompletion('Cyclic2 {\n\t|\n}', {
+ start: { line: 1, ch: 1 },
+ end: { line: 1, ch: 1 },
+ isProperty: false,
+ isObjectKey: false,
+ completions: [
+ { name: "test1", type: "?", origin: "Cyclic2.qml" },
+ { name: "test2", type: "?", origin: "OtherCyclic.qml" },
+ { name: "Cyclic2", type: "Cyclic2", origin: "Cyclic2.qml" },
+ { name: "OtherCyclic", type: "OtherCyclic", origin: "OtherCyclic.qml" }
+ ]
+}, function (server) {
+ server.addFile("Cyclic2.qml", "OtherCyclic {\n\property var test1\n}");
+ server.addFile("OtherCyclic.qml", "Cyclic2 {\n\property var test2\n}");
+});
+
+testCompletion('OtherCyclic {\n\t|\n}', {
+ start: { line: 1, ch: 1 },
+ end: { line: 1, ch: 1 },
+ isProperty: false,
+ isObjectKey: false,
+ completions: [
+ { name: "test2", type: "?", origin: "OtherCyclic.qml" },
+ { name: "Cyclic", type: "Cyclic", origin: "Cyclic.qml" },
+ { name: "OtherCyclic", type: "OtherCyclic", origin: "OtherCyclic.qml" }
+ ]
+}, function (server) {
+ server.addFile("Cyclic.qml", "OtherCyclic {\n\property var test1\n}");
+ server.addFile("OtherCyclic.qml", "Cyclic {\n\property var test2\n}");
+});
// QML Object Property Completions
testCompletion("Window {\n\tproperty int height\n\the|\n}", {
@@ -67,7 +268,7 @@ testCompletion("Window {\n\tproperty int height\n\the|\n}", {
end: { line: 2, ch: 3 },
isProperty: false,
isObjectKey: false,
- completions: [{ name: "height", type: "number", origin: "test1.qml" }]
+ completions: [{ name: "height", type: "number", origin: "main.qml" }]
});
testCompletion("Window {\n\tproperty int height\n\tproperty int width\n\tproperty string text\n\t|\n}", {
@@ -76,9 +277,9 @@ testCompletion("Window {\n\tproperty int height\n\tproperty int width\n\tpropert
isProperty: false,
isObjectKey: false,
completions: [
- { name: "height", type: "number", origin: "test1.qml" },
- { name: "text", type: "string", origin: "test1.qml" },
- { name: "width", type: "number", origin: "test1.qml" }
+ { name: "height", type: "number", origin: "main.qml" },
+ { name: "text", type: "string", origin: "main.qml" },
+ { name: "width", type: "number", origin: "main.qml" }
]
});
@@ -96,8 +297,8 @@ testCompletion("Window {\n\tproperty var prop\n\tfunction test() {\n\t\t|\n\t}\n
isProperty: false,
isObjectKey: false,
completions: [
- { name: "prop", type: "?", origin: "test1.qml" },
- { name: "test", type: "fn()", origin: "test1.qml" }
+ { name: "prop", type: "?", origin: "main.qml" },
+ { name: "test", type: "fn()", origin: "main.qml" }
]
}, function (server) { server.jsDefs = []; });
@@ -115,7 +316,7 @@ testCompletion("Window {\n\tButton {\n\t\tid: btn\n\t\tproperty int height\n\t}\
end: { line: 5, ch: 11 },
isProperty: true,
isObjectKey: false,
- completions: [{ name: "height", type: "number", origin: "test1.qml" }]
+ completions: [{ name: "height", type: "number", origin: "main.qml" }]
}, function (server) { server.jsDefs = []; });
testCompletion("Window {\n\tproperty var btn\n\tButton {\n\t\tid: btn\n\t\tproperty int height\n\t}\n\ttest: btn.|\n}", {
@@ -123,7 +324,7 @@ testCompletion("Window {\n\tproperty var btn\n\tButton {\n\t\tid: btn\n\t\tprope
end: { line: 6, ch: 11 },
isProperty: true,
isObjectKey: false,
- completions: [{ name: "height", type: "number", origin: "test1.qml" }]
+ completions: [{ name: "height", type: "number", origin: "main.qml" }]
}, function (server) { server.jsDefs = []; });
testCompletion("Window {\n\tButton {\n\t\tproperty var id\n\t\tid: btn\n\t}\n\ts: bt|\n}", {
@@ -131,7 +332,7 @@ testCompletion("Window {\n\tButton {\n\t\tproperty var id\n\t\tid: btn\n\t}\n\ts
end: { line: 5, ch: 6 },
isProperty: false,
isObjectKey: false,
- completions: [{ name: "btn", type: "Button", origin: "test1.qml" }]
+ completions: [{ name: "btn", type: "Button", origin: "main.qml" }]
}, function (server) { server.jsDefs = []; });
testCompletion("Window {\n\tButton {\n\t\tproperty var id\n\t\tid: btn\n\t\ts: i|\n\t}\n}", {
@@ -139,7 +340,7 @@ testCompletion("Window {\n\tButton {\n\t\tproperty var id\n\t\tid: btn\n\t\ts: i
end: { line: 4, ch: 6 },
isProperty: false,
isObjectKey: false,
- completions: [{ name: "id", type: "?", origin: "test1.qml" }]
+ completions: [{ name: "id", type: "?", origin: "main.qml" }]
}, function (server) { server.jsDefs = []; });
testCompletion("Window {\n\tButton {\n\t\tproperty var id: 34\n\t\tid: btn\n\t\ts: i|\n\t}\n}", {
@@ -147,7 +348,7 @@ testCompletion("Window {\n\tButton {\n\t\tproperty var id: 34\n\t\tid: btn\n\t\t
end: { line: 4, ch: 6 },
isProperty: false,
isObjectKey: false,
- completions: [{ name: "id", type: "number", origin: "test1.qml" }]
+ completions: [{ name: "id", type: "number", origin: "main.qml" }]
}, function (server) { server.jsDefs = []; });
testCompletion("Window {\n\tButton {\n\t\tproperty string id\n\t\tid: btn\n\t\ts: i|\n\t}\n}", {
@@ -155,7 +356,7 @@ testCompletion("Window {\n\tButton {\n\t\tproperty string id\n\t\tid: btn\n\t\ts
end: { line: 4, ch: 6 },
isProperty: false,
isObjectKey: false,
- completions: [{ name: "id", type: "string", origin: "test1.qml" }]
+ completions: [{ name: "id", type: "string", origin: "main.qml" }]
}, function (server) { server.jsDefs = []; });
testCompletion("Window {\n\tButton {\n\t\tproperty var id\n\t\ts: i|\n\t}\n}", {
@@ -163,7 +364,7 @@ testCompletion("Window {\n\tButton {\n\t\tproperty var id\n\t\ts: i|\n\t}\n}", {
end: { line: 3, ch: 6 },
isProperty: false,
isObjectKey: false,
- completions: [{ name: "id", type: "?", origin: "test1.qml" }]
+ completions: [{ name: "id", type: "?", origin: "main.qml" }]
}, function (server) { server.jsDefs = []; });
testCompletion("Window {\n\tButton {\n\t\tproperty var id: 34\n\t\ts: i|\n\t}\n}", {
@@ -171,7 +372,7 @@ testCompletion("Window {\n\tButton {\n\t\tproperty var id: 34\n\t\ts: i|\n\t}\n}
end: { line: 3, ch: 6 },
isProperty: false,
isObjectKey: false,
- completions: [{ name: "id", type: "number", origin: "test1.qml" }]
+ completions: [{ name: "id", type: "number", origin: "main.qml" }]
}, function (server) { server.jsDefs = []; });
testCompletion("Window {\n\tButton {\n\t\tproperty string id\n\t\ts: i|\n\t}\n}", {
@@ -179,7 +380,7 @@ testCompletion("Window {\n\tButton {\n\t\tproperty string id\n\t\ts: i|\n\t}\n}"
end: { line: 3, ch: 6 },
isProperty: false,
isObjectKey: false,
- completions: [{ name: "id", type: "string", origin: "test1.qml" }]
+ completions: [{ name: "id", type: "string", origin: "main.qml" }]
}, function (server) { server.jsDefs = []; });
testCompletion("Window {\n\tid: wind\n\tfunction test() {\n\t\t|\n\t}\n}", {
@@ -188,8 +389,8 @@ testCompletion("Window {\n\tid: wind\n\tfunction test() {\n\t\t|\n\t}\n}", {
isProperty: false,
isObjectKey: false,
completions: [
- { name: "test", type: "fn()", origin: "test1.qml" },
- { name: "wind", type: "Window", origin: "test1.qml" }
+ { name: "test", type: "fn()", origin: "main.qml" },
+ { name: "wind", type: "Window", origin: "main.qml" }
]
}, function (server) { server.jsDefs = []; });
@@ -209,7 +410,7 @@ testCompletion("Window {\n\tproperty var test: |\n}", {
{ name: "isNaN", type: "fn(value: number) -> bool", origin: "ecma5" },
{ name: "parseFloat", type: "fn(string: string) -> number", origin: "ecma5" },
{ name: "parseInt", type: "fn(string: string, radix?: number) -> number", origin: "ecma5" },
- { name: "test", type: "?", origin: "test1.qml" },
+ { name: "test", type: "?", origin: "main.qml" },
{ name: "undefined", type: "?", origin: "ecma5" },
{ name: "Array", type: "fn(size: number)", origin: "ecma5" },
{ name: "Boolean", type: "fn(value: ?) -> bool", origin: "ecma5" },
@@ -324,7 +525,7 @@ testCompletion("Window {\n\tfunction test() {\n\t\t|\n\t}\n}", {
{ name: "isNaN", type: "fn(value: number) -> bool", origin: "ecma5" },
{ name: "parseFloat", type: "fn(string: string) -> number", origin: "ecma5" },
{ name: "parseInt", type: "fn(string: string, radix?: number) -> number", origin: "ecma5" },
- { name: "test", type: "fn()", origin: "test1.qml" },
+ { name: "test", type: "fn()", origin: "main.qml" },
{ name: "undefined", type: "?", origin: "ecma5" },
{ name: "Array", type: "fn(size: number)", origin: "ecma5" },
{ name: "Boolean", type: "fn(value: ?) -> bool", origin: "ecma5" },
@@ -355,7 +556,7 @@ testCompletion("Window {\n\tsignal clicked(int mouseX, int mouseY)\n\t|\n}", {
isProperty: false,
isObjectKey: false,
completions: [
- { name: "onClicked", type: "Signal Handler", origin: "test1.qml" }
+ { name: "onClicked", type: "Signal Handler", origin: "main.qml" }
]
});
@@ -373,8 +574,8 @@ testCompletion("Window {\n\tsignal clicked(int mouseX, int mouseY)\n\ts: |\n}",
isProperty: false,
isObjectKey: false,
completions: [
- { name: "clicked", type: "fn(mouseX: number, mouseY: number)", origin: "test1.qml" },
- { name: "onClicked", type: "Signal Handler", origin: "test1.qml" }
+ { name: "clicked", type: "fn(mouseX: number, mouseY: number)", origin: "main.qml" },
+ { name: "onClicked", type: "Signal Handler", origin: "main.qml" }
]
}, function (server) { server.jsDefs = []; });
@@ -384,8 +585,8 @@ testCompletion("Window {\n\tsignal error(string msg, boolean flag)\n\ts: |\n}",
isProperty: false,
isObjectKey: false,
completions: [
- { name: "error", type: "fn(msg: string, flag: bool)", origin: "test1.qml" },
- { name: "onError", type: "Signal Handler", origin: "test1.qml" }
+ { name: "error", type: "fn(msg: string, flag: bool)", origin: "main.qml" },
+ { name: "onError", type: "Signal Handler", origin: "main.qml" }
]
}, function (server) { server.jsDefs = []; });
@@ -395,8 +596,8 @@ testCompletion("Window {\n\tid: wind\n\tsignal error(string msg, boolean flag)\n
isProperty: true,
isObjectKey: false,
completions: [
- { name: "error", type: "fn(msg: string, flag: bool)", origin: "test1.qml" },
- { name: "onError", type: "Signal Handler", origin: "test1.qml" }
+ { name: "error", type: "fn(msg: string, flag: bool)", origin: "main.qml" },
+ { name: "onError", type: "Signal Handler", origin: "main.qml" }
]
}, function (server) { server.jsDefs = []; });
@@ -406,10 +607,10 @@ testCompletion("Window {\n\tsignal error(string msg, boolean flag)\n\tonError: |
isProperty: false,
isObjectKey: false,
completions: [
- { name: "error", type: "fn(msg: string, flag: bool)", origin: "test1.qml" },
- { name: "flag", type: "bool", origin: "test1.qml" },
- { name: "msg", type: "string", origin: "test1.qml" },
- { name: "onError", type: "Signal Handler", origin: "test1.qml" }
+ { name: "error", type: "fn(msg: string, flag: bool)", origin: "main.qml" },
+ { name: "flag", type: "bool", origin: "main.qml" },
+ { name: "msg", type: "string", origin: "main.qml" },
+ { name: "onError", type: "Signal Handler", origin: "main.qml" }
]
}, function (server) { server.jsDefs = []; });
@@ -419,10 +620,10 @@ testCompletion("Window {\n\tsignal error(string msg, boolean flag)\n\tonError: {
isProperty: false,
isObjectKey: false,
completions: [
- { name: "error", type: "fn(msg: string, flag: bool)", origin: "test1.qml" },
- { name: "flag", type: "bool", origin: "test1.qml" },
- { name: "msg", type: "string", origin: "test1.qml" },
- { name: "onError", type: "Signal Handler", origin: "test1.qml" }
+ { name: "error", type: "fn(msg: string, flag: bool)", origin: "main.qml" },
+ { name: "flag", type: "bool", origin: "main.qml" },
+ { name: "msg", type: "string", origin: "main.qml" },
+ { name: "onError", type: "Signal Handler", origin: "main.qml" }
]
}, function (server) { server.jsDefs = []; });
@@ -432,10 +633,10 @@ testCompletion("Window {\n\tproperty int msg\n\tsignal error(string msg, boolean
isProperty: false,
isObjectKey: false,
completions: [
- { name: "error", type: "fn(msg: string, flag: bool)", origin: "test1.qml" },
- { name: "flag", type: "bool", origin: "test1.qml" },
- { name: "msg", type: "string", origin: "test1.qml" },
- { name: "onError", type: "Signal Handler", origin: "test1.qml" }
+ { name: "error", type: "fn(msg: string, flag: bool)", origin: "main.qml" },
+ { name: "flag", type: "bool", origin: "main.qml" },
+ { name: "msg", type: "string", origin: "main.qml" },
+ { name: "onError", type: "Signal Handler", origin: "main.qml" }
]
}, function (server) { server.jsDefs = []; });
@@ -454,10 +655,10 @@ testCompletion("Window {\n\tfunction test(a, b, c) {\n\t\t|\n\t}\n}", {
isProperty: false,
isObjectKey: false,
completions: [
- { name: "a", type: "?", origin: "test1.qml" },
- { name: "b", type: "?", origin: "test1.qml" },
- { name: "c", type: "?", origin: "test1.qml" },
- { name: "test", type: "fn(a: ?, b: ?, c: ?)", origin: "test1.qml" }
+ { name: "a", type: "?", origin: "main.qml" },
+ { name: "b", type: "?", origin: "main.qml" },
+ { name: "c", type: "?", origin: "main.qml" },
+ { name: "test", type: "fn(a: ?, b: ?, c: ?)", origin: "main.qml" }
]
}, function (server) { server.jsDefs = []; });
@@ -467,8 +668,8 @@ testCompletion("Window {\n\tfunction test(a) {\n\t\ta = 3\n\t\t|\n\t}\n}", {
isProperty: false,
isObjectKey: false,
completions: [
- { name: "a", type: "number", origin: "test1.qml" },
- { name: "test", type: "fn(a: number)", origin: "test1.qml" }
+ { name: "a", type: "number", origin: "main.qml" },
+ { name: "test", type: "fn(a: number)", origin: "main.qml" }
]
}, function (server) { server.jsDefs = []; });
@@ -478,8 +679,8 @@ testCompletion('Window {\n\tfunction test(a) {\n\t\ttest("something")\n\t\t|\n\t
isProperty: false,
isObjectKey: false,
completions: [
- { name: "a", type: "string", origin: "test1.qml" },
- { name: "test", type: "fn(a: string)", origin: "test1.qml" }
+ { name: "a", type: "string", origin: "main.qml" },
+ { name: "test", type: "fn(a: string)", origin: "main.qml" }
]
}, function (server) { server.jsDefs = []; });
@@ -489,8 +690,8 @@ testCompletion('Window {\n\tfunction test(a) {\n\t\t|\n\t\treturn 7\n\t}\n}', {
isProperty: false,
isObjectKey: false,
completions: [
- { name: "a", type: "?", origin: "test1.qml" },
- { name: "test", type: "fn(a: ?) -> number", origin: "test1.qml" }
+ { name: "a", type: "?", origin: "main.qml" },
+ { name: "test", type: "fn(a: ?) -> number", origin: "main.qml" }
]
}, function (server) { server.jsDefs = []; });
@@ -502,8 +703,8 @@ testCompletion('Window {\n\tfunction test(a) {\n\t\t|\n\t\treturn 7\n\t}\n}', {
// isProperty: false,
// isObjectKey: false,
// completions: [
-// { name: "a", type: "?", origin: "test1.qml" },
-// { name: "test", type: "fn()", origin: "test1.qml" }
+// { name: "a", type: "?", origin: "main.qml" },
+// { name: "test", type: "fn()", origin: "main.qml" }
// ]
//}, function (server) { server.jsDefs = []; });
diff --git a/qt/org.eclipse.cdt.qt.core/tern-qml/test/test-finddef.js b/qt/org.eclipse.cdt.qt.core/tern-qml/test/test-finddef.js
index 626b7d08afe..c6090d4f1b4 100644
--- a/qt/org.eclipse.cdt.qt.core/tern-qml/test/test-finddef.js
+++ b/qt/org.eclipse.cdt.qt.core/tern-qml/test/test-finddef.js
@@ -8,79 +8,163 @@
* Contributors:
* QNX Software Systems - Initial API and implementation
*******************************************************************************/
-"use-strict";
+"use strict";
var driver = require("./driver.js");
+var test = driver.test;
var testDefinition = driver.testDefinition;
+var assertDefinition = driver.assertDefintion;
var groupStart = driver.groupStart;
var groupEnd = driver.groupEnd;
var group = exports.group = "Find Definition";
groupStart(group);
+// Directory Imports
+// TODO: Getting this test to pass breaks some of the type inferencing which is more important
+//testDefinition('My|Object {}', {
+// origin: "MyObject.qml",
+// start: { line: 0, ch: 0 },
+// end: { line: 0, ch: 6 },
+// file: "MyObject.qml",
+// contextOffset: 0
+//}, function (server) {
+// server.addFile("MyObject.qml", "Button {}");
+//});
+
+testDefinition("MyObject {\n\tte|stProp: ident\n}", {
+ origin: "MyObject.qml",
+ start: { line: 1, ch: 14 },
+ end: { line: 1, ch: 22 },
+ file: "MyObject.qml",
+ contextOffset: 23
+}, function (server) {
+ server.addFile("MyObject.qml", "Button {\n\tproperty var testProp\n}");
+});
+
+testDefinition("MyObject {\n\ts: te|stSig()\n}", {
+ origin: "MyObject.qml",
+ start: { line: 1, ch: 8 },
+ end: { line: 1, ch: 15 },
+ file: "MyObject.qml",
+ contextOffset: 17
+}, function (server) {
+ server.addFile("MyObject.qml", "Button {\n\tsignal testSig()\n}");
+});
+
+testDefinition("MyObject {\n\tonTe|stSig: ident\n}", {
+ origin: "MyObject.qml",
+ start: { line: 1, ch: 8 },
+ end: { line: 1, ch: 15 },
+ file: "MyObject.qml",
+ contextOffset: 17
+}, function (server) {
+ server.addFile("MyObject.qml", "Button {\n\tsignal testSig()\n}");
+});
+
+testDefinition("MyObject {\n\tonTestSig: ar|g0\n}", {
+ origin: "MyObject.qml",
+ start: { line: 1, ch: 20 },
+ end: { line: 1, ch: 24 },
+ file: "MyObject.qml",
+ contextOffset: 29
+}, function (server) {
+ server.addFile("MyObject.qml", "Button {\n\tsignal testSig(int arg0)\n}");
+});
+
+testDefinition("MyObject {\n\ts: te|stFn()\n}", {
+ origin: "MyObject.qml",
+ start: { line: 1, ch: 10 },
+ end: { line: 1, ch: 16 },
+ file: "MyObject.qml",
+ contextOffset: 19
+}, function (server) {
+ server.addFile("MyObject.qml", "Button {\n\tfunction testFn() {}\n}");
+});
+
+testDefinition("MyObject {\n\ts: btn|Id\n}", {
+ origin: undefined,
+ start: undefined,
+ end: undefined,
+ file: undefined,
+ contextOffset: undefined
+}, function (server) {
+ server.addFile("MyObject.qml", "Button {\n\tid: btnId\n}");
+});
+
+testDefinition("MyObject {\n\tot|her: ident\n}", {
+ origin: undefined,
+ start: undefined,
+ end: undefined,
+ file: undefined,
+ contextOffset: undefined
+}, function (server) {
+ server.addFile("MyObject.qml", "Button {\n\tSecondButton {\n\t\tjproperty var other\n\t}\n}");
+});
+
// ID Property
testDefinition("QtObject {\n\tid: obj\n\tproperty var prop: {\n\t\tob|j\n\t}\n}", {
- origin: "test1.qml",
+ origin: "main.qml",
start: { line: 1, ch: 5 },
end: { line: 1, ch: 8 },
- file: "test1.qml",
+ file: "main.qml",
contextOffset: 16
});
testDefinition("Window {\n\tButton {\n\t\tid: btn\n\t\tproperty int height\n\t}\n\ttest: bt|n\n}", {
- origin: "test1.qml",
+ origin: "main.qml",
start: { line: 2, ch: 6 },
end: { line: 2, ch: 9 },
- file: "test1.qml",
+ file: "main.qml",
contextOffset: 25
});
testDefinition("Window {\n\tproperty var btn\n\tButton {\n\t\tid: btn\n\t\tproperty int height\n\t}\n\ttest: bt|n\n}", {
- origin: "test1.qml",
+ origin: "main.qml",
start: { line: 3, ch: 6 },
end: { line: 3, ch: 9 },
- file: "test1.qml",
+ file: "main.qml",
contextOffset: 43
});
testDefinition("Window {\n\tproperty var btn\n\tButton {\n\t\tid: bt|n\n\t\tproperty int height\n\t}\n}", {
- origin: "test1.qml",
+ origin: "main.qml",
start: { line: 3, ch: 6 },
end: { line: 3, ch: 9 },
- file: "test1.qml",
+ file: "main.qml",
contextOffset: 43
});
// Property Declarations
testDefinition("QtObject {\n\tproperty var prop\n\tpr|op: 3\n}", {
- origin: "test1.qml",
+ origin: "main.qml",
start: { line: 1, ch: 14 },
end: { line: 1, ch: 18 },
- file: "test1.qml",
+ file: "main.qml",
contextOffset: 25
});
testDefinition("Window {\n\tproperty var btn\n\tButton {\n\t\tprop: b|tn\n\t}\n}", {
- origin: "test1.qml",
+ origin: "main.qml",
start: { line: 1, ch: 14 },
end: { line: 1, ch: 17 },
- file: "test1.qml",
+ file: "main.qml",
contextOffset: 23
});
testDefinition("Window {\n\tproperty var btn\n\tButton {\n\t\tButton {\n\t\t\tprop: b|tn\n\t\t}\n\t}\n}", {
- origin: "test1.qml",
+ origin: "main.qml",
start: { line: 1, ch: 14 },
end: { line: 1, ch: 17 },
- file: "test1.qml",
+ file: "main.qml",
contextOffset: 23
});
testDefinition("Window {\n\tButton {\n\t\tproperty var btn\n\t\tbt|n: 3\n\t}\n}", {
- origin: "test1.qml",
+ origin: "main.qml",
start: { line: 2, ch: 15 },
end: { line: 2, ch: 18 },
- file: "test1.qml",
+ file: "main.qml",
contextOffset: 34
});
@@ -94,115 +178,115 @@ testDefinition("Window {\n\tButton {\n\t\tproperty var btn\n\t\tButton {\n\t\t\t
// Signals and Signal Handlers
testDefinition("Window {\n\tsignal clic|ked(int mouseX, int mouseY)\n}", {
- origin: "test1.qml",
+ origin: "main.qml",
start: { line: 1, ch: 8 },
end: { line: 1, ch: 15 },
- file: "test1.qml",
+ file: "main.qml",
contextOffset: 17
});
testDefinition("Window {\n\tsignal clicked(int mouseX, int mouseY)\n\tonCli|cked: 3\n}", {
- origin: "test1.qml",
+ origin: "main.qml",
start: { line: 1, ch: 8 },
end: { line: 1, ch: 15 },
- file: "test1.qml",
+ file: "main.qml",
contextOffset: 17
});
testDefinition("Window {\n\tsignal clicked(int mouseX, int mouseY)\n\tonClicked: mou|seX\n}", {
- origin: "test1.qml",
+ origin: "main.qml",
start: { line: 1, ch: 20 },
end: { line: 1, ch: 26 },
- file: "test1.qml",
+ file: "main.qml",
contextOffset: 29
});
testDefinition("Window {\n\tsignal clicked(int mouseX, int mouseY)\n\tonClicked: cli|cked(3,4)\n}", {
- origin: "test1.qml",
+ origin: "main.qml",
start: { line: 1, ch: 8 },
end: { line: 1, ch: 15 },
- file: "test1.qml",
+ file: "main.qml",
contextOffset: 17
});
testDefinition("Window {\n\tsignal clicked(int mouseX, int mouseY)\n\tonClicked: onCli|cked\n}", {
- origin: "test1.qml",
+ origin: "main.qml",
start: { line: 1, ch: 8 },
end: { line: 1, ch: 15 },
- file: "test1.qml",
+ file: "main.qml",
contextOffset: 17
});
testDefinition("Window {\n\tsignal clicked(int mouseX, int mouseY)\n\tid: wind\n\tonClicked: wind.onCli|cked\n}", {
- origin: "test1.qml",
+ origin: "main.qml",
start: { line: 1, ch: 8 },
end: { line: 1, ch: 15 },
- file: "test1.qml",
+ file: "main.qml",
contextOffset: 17
});
testDefinition("Window {\n\tsignal clicked(int mouseX, int mouseY)\n\tid: wind\n\tonClicked: wind.cli|cked(3,4)\n}", {
- origin: "test1.qml",
+ origin: "main.qml",
start: { line: 1, ch: 8 },
end: { line: 1, ch: 15 },
- file: "test1.qml",
+ file: "main.qml",
contextOffset: 17
});
testDefinition("Window {\n\tproperty int msg\n\tsignal error(string msg, boolean flag)\n\tonError: {\n\t\tms|g\n\t}\n}", {
- origin: "test1.qml",
+ origin: "main.qml",
start: { line: 2, ch: 21 },
end: { line: 2, ch: 24 },
- file: "test1.qml",
+ file: "main.qml",
contextOffset: 48
}, function (server) { server.jsDefs = []; });
// Function Declarations
testDefinition("Window {\n\tfunction te|st(a) {}\n}", {
- origin: "test1.qml",
+ origin: "main.qml",
start: { line: 1, ch: 10 },
end: { line: 1, ch: 14 },
- file: "test1.qml",
+ file: "main.qml",
contextOffset: 19
});
testDefinition("Window {\n\tfunction test(a) {\n\t\ta|\n\t}\n}", {
- origin: "test1.qml",
+ origin: "main.qml",
start: { line: 1, ch: 15 },
end: { line: 1, ch: 16 },
- file: "test1.qml",
+ file: "main.qml",
contextOffset: 24
});
testDefinition("Window {\n\tfunction test(a) {\n\t\tte|st(3)\n\t}\n}", {
- origin: "test1.qml",
+ origin: "main.qml",
start: { line: 1, ch: 10 },
end: { line: 1, ch: 14 },
- file: "test1.qml",
+ file: "main.qml",
contextOffset: 19
});
testDefinition("Window {\n\tfunction test(a) {}\n\ts: te|st(3)\n}", {
- origin: "test1.qml",
+ origin: "main.qml",
start: { line: 1, ch: 10 },
end: { line: 1, ch: 14 },
- file: "test1.qml",
+ file: "main.qml",
contextOffset: 19
});
testDefinition("Window {\n\tfunction test(a) {}\n\ts: {\n\t\tte|st(3)\n\t}\n}", {
- origin: "test1.qml",
+ origin: "main.qml",
start: { line: 1, ch: 10 },
end: { line: 1, ch: 14 },
- file: "test1.qml",
+ file: "main.qml",
contextOffset: 19
});
testDefinition("Window {\n\tfunction test(a) {}\n\tid: wind\n\ts: {\n\t\twind.te|st(3)\n\t}\n}", {
- origin: "test1.qml",
+ origin: "main.qml",
start: { line: 1, ch: 10 },
end: { line: 1, ch: 14 },
- file: "test1.qml",
+ file: "main.qml",
contextOffset: 19
});
@@ -215,10 +299,10 @@ testDefinition("Window {\n\tfunction test(a) {}\n\tte|st: 3;\n}", {
});
testDefinition('Window {\n\tfunction test(a) {\n\t\ta|\n\t}\n}\n\tproperty int a', {
- origin: "test1.qml",
+ origin: "main.qml",
start: { line: 1, ch: 15 },
end: { line: 1, ch: 16 },
- file: "test1.qml",
+ file: "main.qml",
contextOffset: 24
});
diff --git a/qt/org.eclipse.cdt.qt.core/tern-qml/test/test-scoping.js b/qt/org.eclipse.cdt.qt.core/tern-qml/test/test-scoping.js
new file mode 100644
index 00000000000..c44f8b4104c
--- /dev/null
+++ b/qt/org.eclipse.cdt.qt.core/tern-qml/test/test-scoping.js
@@ -0,0 +1,409 @@
+/*******************************************************************************
+ * 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 plugin = require("../qml.js");
+var infer = require("tern/lib/infer");
+var driver = require("./driver.js");
+var test = driver.test;
+var groupStart = driver.groupStart;
+var groupEnd = driver.groupEnd;
+
+var group = exports.group = "Custom Scoping";
+groupStart(group);
+
+test("QMLObjScope::defProp()", function (server, callback, name) {
+ infer.withContext(new infer.Context([], server), function () {
+ // Create the scope and objects to test
+ var fileScope = new infer.Scope();
+ var obj = new infer.Obj(null, "MyObject");
+ var scope = new plugin.QMLObjScope(fileScope, null, obj);
+
+ // Define some properties
+ scope.defProp("first", null);
+ scope.defProp("second", null);
+
+ // Make sure there are no properties in the scopes themselves
+ if (fileScope.props && fileScope.props.length > 0) {
+ return callback("fail", name, "- File scope contained properties (none expected)");
+ }
+ if (scope.props && scope.props.length > 0) {
+ return callback("fail", name, "- QMLObjScope contained properties (none expected)");
+ }
+
+ // Check the Obj Type for the props we created
+ if (!obj.props.first) {
+ return callback("fail", name, "- Obj did not contain property 'first'");
+ }
+ if (!obj.props.second) {
+ return callback("fail", name, "- Obj did not contain property 'second'");
+ }
+ return callback("ok", name);
+ });
+});
+
+test("QMLObjScope::hasProp()", function (server, callback, name) {
+ infer.withContext(new infer.Context([], server), function () {
+ // Create the scope and objects to test
+ var fileScope = new infer.Scope();
+ var obj = new infer.Obj(null, "MyObject");
+ var scope = new plugin.QMLObjScope(fileScope, null, obj);
+
+ // Define a property on the Obj Type
+ obj.defProp("first", null);
+
+ // Query the scope for the prop we created and make sure it returns the right one
+ var prop = scope.hasProp("first", true);
+ if (!prop) {
+ return callback("fail", name, "- hasProp('first') returned null");
+ } else if (prop.propertyName !== "first") {
+ return callback("fail", name, "- hasProp('first') returned invalid property");
+ }
+ return callback("ok", name);
+ });
+});
+
+test("QMLObjScope::removeProp()", function (server, callback, name) {
+ infer.withContext(new infer.Context([], server), function () {
+ // Create the scope and objects to test
+ var fileScope = new infer.Scope();
+ var obj = new infer.Obj(null, "MyObject");
+ var scope = new plugin.QMLObjScope(fileScope, null, obj);
+
+ // Define some properties
+ scope.defProp("first", null);
+ scope.defProp("second", null);
+
+ // Remove the properties we defined
+ scope.removeProp("first");
+ scope.removeProp("second");
+
+ // Check the Obj Type for the props we created
+ if (obj.props && obj.props.length > 0) {
+ return callback("fail", name, "- Obj contained properties (none expected)");
+ }
+ return callback("ok", name);
+ });
+});
+
+test("QMLObjScope::gatherProperties()", function (server, callback, name) {
+ infer.withContext(new infer.Context([], server), function () {
+ // Create the scope and objects to test
+ var fileScope = new infer.Scope();
+ var obj = new infer.Obj(null, "MyObject");
+ var scope = new plugin.QMLObjScope(fileScope, null, obj);
+
+ // Define some properties
+ fileScope.defProp("third", null);
+ scope.defProp("first", null);
+ scope.defProp("second", null);
+
+ // Gather the properties and store them in the order they were received
+ var props = [];
+ scope.gatherProperties(function (prop, obj, depth) {
+ props.push(prop);
+ });
+
+ // Check the gathered properties for correctness (order matters)
+ if (props.length !== 3) {
+ return callback("fail", name, "- Invalid number of properties gathered (" + props.length + ")");
+ }
+ if (props[0] !== "first") {
+ return callback("fail", name, "- props[0] was not property 'first'");
+ }
+ if (props[1] !== "second") {
+ return callback("fail", name, "- props[1] was not property 'second'");
+ }
+ if (props[2] !== "third") {
+ return callback("fail", name, "- props[2] was not property 'third'");
+ }
+ return callback("ok", name);
+ });
+});
+
+test("QMLMemScope::defProp()", function (server, callback, name) {
+ infer.withContext(new infer.Context([], server), function () {
+ // Create the scope and objects to test
+ var fileScope = new infer.Scope();
+ var obj = new infer.Obj(null, "MyObject");
+ var objScope = new plugin.QMLObjScope(fileScope, null, obj);
+ var scope = new plugin.QMLMemScope(objScope, null, fileScope);
+
+ // Define some properties
+ scope.defProp("first", null);
+ scope.defProp("second", null);
+
+ // Make sure there are no properties in the scopes themselves
+ if (fileScope.props && fileScope.props.length > 0) {
+ return callback("fail", name, "- File scope contained properties (none expected)");
+ }
+ if (scope.props && scope.props.length > 0) {
+ return callback("fail", name, "- QMLMemScope contained properties (none expected)");
+ }
+
+ // Check the Obj Type for the props we created
+ if (!obj.props.first) {
+ return callback("fail", name, "- Obj did not contain property 'first'");
+ }
+ if (!obj.props.second) {
+ return callback("fail", name, "- Obj did not contain property 'second'");
+ }
+ return callback("ok", name);
+ });
+});
+
+test("QMLMemScope::hasProp()", function (server, callback, name) {
+ infer.withContext(new infer.Context([], server), function () {
+ // Create the scope and objects to test
+ var fileScope = new infer.Scope();
+ var obj = new infer.Obj(null, "MyObject");
+ var objScope = new plugin.QMLObjScope(fileScope, null, obj);
+ var scope = new plugin.QMLMemScope(objScope, null, fileScope);
+
+ // Define a property on the Obj Type and File Scope
+ obj.defProp("first", null);
+ fileScope.defProp("second", null);
+
+ // Query the scope for the prop we created and make sure it returns the right one
+ var prop = scope.hasProp("first", true);
+ if (!prop) {
+ return callback("fail", name, "- hasProp('first') returned null");
+ } else if (prop.propertyName !== "first") {
+ return callback("fail", name, "- hasProp('first') returned invalid property");
+ }
+ prop = scope.hasProp("second", true);
+ if (!prop) {
+ return callback("fail", name, "- hasProp('second') returned null");
+ } else if (prop.propertyName !== "second") {
+ return callback("fail", name, "- hasProp('second') returned invalid property");
+ }
+ return callback("ok", name);
+ });
+});
+
+test("QMLMemScope::hasProp() [Multiple Parent Scopes]", function (server, callback, name) {
+ infer.withContext(new infer.Context([], server), function () {
+ // Create the scope and objects to test
+ var fileScope = new infer.Scope();
+ var rootObj = new infer.Obj(null, "Root");
+ var rootObjScope = new plugin.QMLObjScope(fileScope, null, rootObj);
+ var obj = new infer.Obj(null, "MyObject");
+ var objScope = new plugin.QMLObjScope(rootObjScope, null, obj);
+ var scope = new plugin.QMLMemScope(objScope, null, fileScope);
+
+ // Define a property on the Root Obj Type and Obj Type
+ rootObj.defProp("notVisible", null);
+ obj.defProp("visible", null);
+
+ // Query the scope for the prop we created and make sure it returns the right one
+ var prop = scope.hasProp("notVisible", true);
+ if (prop) {
+ return callback("fail", name, "- found property 'notVisible'");
+ }
+ prop = scope.hasProp("visible", true);
+ if (!prop) {
+ return callback("fail", name, "- hasProp('visible') returned null");
+ } else if (prop.propertyName !== "visible") {
+ return callback("fail", name, "- hasProp('visible') returned invalid property");
+ }
+ return callback("ok", name);
+ });
+});
+
+test("QMLMemScope::removeProp()", function (server, callback, name) {
+ infer.withContext(new infer.Context([], server), function () {
+ // Create the scope and objects to test
+ var fileScope = new infer.Scope();
+ var obj = new infer.Obj(null, "MyObject");
+ var objScope = new plugin.QMLObjScope(fileScope, null, obj);
+ var scope = new plugin.QMLMemScope(objScope, null, fileScope);
+
+ // Define some properties
+ scope.defProp("first", null);
+ scope.defProp("second", null);
+
+ // Remove the properties we defined
+ scope.removeProp("first");
+ scope.removeProp("second");
+
+ // Check the Obj Type for the props we created
+ if (obj.props && obj.props.length > 0) {
+ return callback("fail", name, "- Obj contained properties (none expected)");
+ }
+ return callback("ok", name);
+ });
+});
+
+test("QMLMemScope::gatherProperties()", function (server, callback, name) {
+ infer.withContext(new infer.Context([], server), function () {
+ // Create the scope and objects to test
+ var fileScope = new infer.Scope();
+ var obj = new infer.Obj(null, "MyObject");
+ var objScope = new plugin.QMLObjScope(fileScope, null, obj);
+ var scope = new plugin.QMLMemScope(objScope, null, fileScope);
+
+ // Define some properties
+ fileScope.defProp("third", null);
+ scope.defProp("first", null);
+ scope.defProp("second", null);
+
+ // Gather the properties and store them in the order they were received
+ var props = [];
+ scope.gatherProperties(function (prop, obj, depth) {
+ props.push(prop);
+ });
+
+ // Check the gathered properties for correctness (order matters)
+ if (props.length !== 3) {
+ return callback("fail", name, "- Invalid number of properties gathered (" + props.length + ")");
+ }
+ if (props[0] !== "first") {
+ return callback("fail", name, "- props[0] was not property 'first'");
+ }
+ if (props[1] !== "second") {
+ return callback("fail", name, "- props[1] was not property 'second'");
+ }
+ if (props[2] !== "third") {
+ return callback("fail", name, "- props[2] was not property 'third'");
+ }
+ return callback("ok", name);
+ });
+});
+
+test("QMLMemScope::gatherProperties() [Multiple Parent Scopes]", function (server, callback, name) {
+ infer.withContext(new infer.Context([], server), function () {
+ // Create the scope and objects to test
+ var fileScope = new infer.Scope();
+ var rootObj = new infer.Obj(null, "Root");
+ var rootObjScope = new plugin.QMLObjScope(fileScope, null, rootObj);
+ var obj = new infer.Obj(null, "MyObject");
+ var objScope = new plugin.QMLObjScope(rootObjScope, null, obj);
+ var scope = new plugin.QMLMemScope(objScope, null, fileScope);
+
+ // Define a property on the Root Obj Type and Obj Type
+ rootObj.defProp("notVisible", null);
+ obj.defProp("visible", null);
+
+ // Gather the properties and store them in the order they were received
+ var props = [];
+ scope.gatherProperties(function (prop, obj, depth) {
+ props.push(prop);
+ });
+
+ // Check the gathered properties for correctness (order matters)
+ if (props.length !== 1) {
+ return callback("fail", name, "- Invalid number of properties gathered (" + props.length + ")");
+ }
+ if (props[0] !== "visible") {
+ return callback("fail", name, "- props[0] was not property 'visible'");
+ }
+ return callback("ok", name);
+ });
+});
+
+test("QMLJSScope::hasProp()", function (server, callback, name) {
+ infer.withContext(new infer.Context([], server), function () {
+ // Create the scope and objects to test
+ var fileScope = new infer.Scope();
+ var idScope = new infer.Scope(null, "<qml-id>");
+ var jsScope = new infer.Scope(null, "<qml-js>");
+ var fnScope = new infer.Scope(null, "<qml-fn>");
+ var scope = new plugin.QMLJSScope(fileScope, null, idScope, jsScope, fnScope);
+
+ // Define properties in each scope
+ fileScope.defProp("first", null);
+ idScope.defProp("second", null);
+ jsScope.defProp("third", null);
+ fnScope.defProp("fourth", null);
+ scope.defProp("fifth", null);
+
+ // Query the scope for the prop we created and make sure it returns the right one
+ var prop = scope.hasProp("first", true);
+ if (!prop) {
+ return callback("fail", name, "- hasProp('first') returned null");
+ } else if (prop.propertyName !== "first") {
+ return callback("fail", name, "- hasProp('first') returned invalid property");
+ }
+ prop = scope.hasProp("second", true);
+ if (!prop) {
+ return callback("fail", name, "- hasProp('second') returned null");
+ } else if (prop.propertyName !== "second") {
+ return callback("fail", name, "- hasProp('second') returned invalid property");
+ }
+ prop = scope.hasProp("third", true);
+ if (!prop) {
+ return callback("fail", name, "- hasProp('third') returned null");
+ } else if (prop.propertyName !== "third") {
+ return callback("fail", name, "- hasProp('third') returned invalid property");
+ }
+ prop = scope.hasProp("fourth", true);
+ if (!prop) {
+ return callback("fail", name, "- hasProp('fourth') returned null");
+ } else if (prop.propertyName !== "fourth") {
+ return callback("fail", name, "- hasProp('fourth') returned invalid property");
+ }
+ prop = scope.hasProp("fifth", true);
+ if (!prop) {
+ return callback("fail", name, "- hasProp('fifth') returned null");
+ } else if (prop.propertyName !== "fifth") {
+ return callback("fail", name, "- hasProp('fifth') returned invalid property");
+ }
+ return callback("ok", name);
+ });
+});
+
+test("QMLJSScope::gatherProperties()", function (server, callback, name) {
+ infer.withContext(new infer.Context([], server), function () {
+ // Create the scope and objects to test
+ // Create the scope and objects to test
+ var fileScope = new infer.Scope();
+ var idScope = new infer.Scope(null, "<qml-id>");
+ var jsScope = new infer.Scope(null, "<qml-js>");
+ var fnScope = new infer.Scope(null, "<qml-fn>");
+ var scope = new plugin.QMLJSScope(fileScope, null, idScope, jsScope, fnScope);
+
+ // Define properties in each scope
+ fileScope.defProp("fifth", null);
+ idScope.defProp("first", null);
+ jsScope.defProp("fourth", null);
+ fnScope.defProp("third", null);
+ scope.defProp("second", null);
+
+ // Gather the properties and store them in the order they were received
+ var props = [];
+ scope.gatherProperties(function (prop, obj, depth) {
+ props.push(prop);
+ });
+
+ // Check the gathered properties for correctness (order matters)
+ if (props.length !== 5) {
+ return callback("fail", name, "- Invalid number of properties gathered (" + props.length + ")");
+ }
+ if (props[0] !== "first") {
+ return callback("fail", name, "- props[0] was not property 'first'");
+ }
+ if (props[1] !== "second") {
+ return callback("fail", name, "- props[1] was not property 'second'");
+ }
+ if (props[2] !== "third") {
+ return callback("fail", name, "- props[2] was not property 'third'");
+ }
+ if (props[3] !== "fourth") {
+ return callback("fail", name, "- props[3] was not property 'fourth'");
+ }
+ if (props[4] !== "fifth") {
+ return callback("fail", name, "- props[4] was not property 'fifth'");
+ }
+ return callback("ok", name);
+ });
+});
+
+groupEnd(); \ No newline at end of file

Back to the top