mirror of
https://github.com/eclipse-cdt/cdt
synced 2025-07-26 02:15:31 +02:00
Bug 481126 - QML Scoping Issues
Fixed the following issues with QML: -The parent scope of a QML Object is the file's root Object scope -Content assist for an Object Property should only display the immediate Object's properties, not it's parent(s) -Content assist for an incomplete property binding should display JavaScript hints as well Added missing license headers and new tests. Had to modify the acorn-qml parser plugin in order to make some of the changes. Added tests for those as well. Change-Id: I289167cbaacd8088f87dfafc689e67c0110d942f Signed-off-by: Matthew Bastien <mbastien@blackberry.com>
This commit is contained in:
parent
069d106ab4
commit
31150e4af1
15 changed files with 1491 additions and 515 deletions
|
@ -8,7 +8,6 @@
|
||||||
* Contributors:
|
* Contributors:
|
||||||
* QNX Software Systems - Initial API and implementation
|
* QNX Software Systems - Initial API and implementation
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
'use strict';
|
|
||||||
|
|
||||||
// This will only be visible globally if we are in a browser environment
|
// This will only be visible globally if we are in a browser environment
|
||||||
var acornQML;
|
var acornQML;
|
||||||
|
@ -20,5 +19,7 @@ var acornQML;
|
||||||
return define(["./inject.js", "acorn/dist/acorn"], mod);
|
return define(["./inject.js", "acorn/dist/acorn"], mod);
|
||||||
acornQML = mod(injectQML, acorn); // Plain browser env
|
acornQML = mod(injectQML, acorn); // Plain browser env
|
||||||
})(function (injectQML, acorn) {
|
})(function (injectQML, acorn) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
return injectQML(acorn);
|
return injectQML(acorn);
|
||||||
})
|
})
|
|
@ -8,7 +8,6 @@
|
||||||
* Contributors:
|
* Contributors:
|
||||||
* QNX Software Systems - Initial API and implementation
|
* QNX Software Systems - Initial API and implementation
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
'use strict';
|
|
||||||
|
|
||||||
// This will only be visible globally if we are in a browser environment
|
// This will only be visible globally if we are in a browser environment
|
||||||
var injectQML;
|
var injectQML;
|
||||||
|
@ -20,6 +19,8 @@ var injectQML;
|
||||||
return define([], mod);
|
return define([], mod);
|
||||||
injectQML = mod(); // Plain browser env
|
injectQML = mod(); // Plain browser env
|
||||||
})(function () {
|
})(function () {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
return function (acorn) {
|
return function (acorn) {
|
||||||
// Acorn token types
|
// Acorn token types
|
||||||
var tt = acorn.tokTypes;
|
var tt = acorn.tokTypes;
|
||||||
|
@ -93,11 +94,9 @@ var injectQML;
|
||||||
var loop = true;
|
var loop = true;
|
||||||
while (loop) {
|
while (loop) {
|
||||||
if (this.isContextual(qtt._import)) {
|
if (this.isContextual(qtt._import)) {
|
||||||
var qmlImport = this.qml_parseImportStatement();
|
node.statements.push(this.qml_parseImportStatement());
|
||||||
node.statements.push(qmlImport);
|
|
||||||
} else if (this.isContextual(qtt._pragma)) {
|
} else if (this.isContextual(qtt._pragma)) {
|
||||||
var qmlPragma = this.qml_parsePragmaStatement();
|
node.statements.push(this.qml_parsePragmaStatement());
|
||||||
node.statements.push(qmlPragma);
|
|
||||||
} else {
|
} else {
|
||||||
loop = false;
|
loop = false;
|
||||||
}
|
}
|
||||||
|
@ -293,7 +292,7 @@ var injectQML;
|
||||||
node.id = this.qml_parseQualifiedId(false);
|
node.id = this.qml_parseQualifiedId(false);
|
||||||
}
|
}
|
||||||
this.expect(tt.colon);
|
this.expect(tt.colon);
|
||||||
node.expr = this.qml_parsePropertyAssignment();
|
node.binding = this.qml_parseBinding();
|
||||||
return this.finishNode(node, "QMLPropertyBinding");
|
return this.finishNode(node, "QMLPropertyBinding");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -401,10 +400,10 @@ var injectQML;
|
||||||
node.kind = this.qml_parseKind();
|
node.kind = this.qml_parseKind();
|
||||||
node.id = this.qml_parseIdent(false);
|
node.id = this.qml_parseIdent(false);
|
||||||
if (!this.eat(tt.colon)) {
|
if (!this.eat(tt.colon)) {
|
||||||
node.init = null;
|
node.binding = null;
|
||||||
this.semicolon();
|
this.semicolon();
|
||||||
} else {
|
} else {
|
||||||
node.init = this.qml_parsePropertyAssignment();
|
node.binding = this.qml_parseBinding();
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.finishNode(node, "QMLPropertyDeclaration");
|
return this.finishNode(node, "QMLPropertyDeclaration");
|
||||||
|
@ -412,24 +411,35 @@ var injectQML;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Parses one of the following possibilities for a QML Property assignment:
|
* Parses one of the following possibilities for a QML Property assignment:
|
||||||
* - JavaScript Expression
|
|
||||||
* - QML JavaScript Statement Block
|
|
||||||
* - QML Object Literal
|
* - QML Object Literal
|
||||||
|
* - QML Script Binding
|
||||||
*/
|
*/
|
||||||
pp.qml_parsePropertyAssignment = function () {
|
pp.qml_parseBinding = function () {
|
||||||
// TODO: solve ambiguity where a QML Object Literal starts with a
|
// TODO: solve ambiguity where a QML Object Literal starts with a
|
||||||
// Qualified Id that looks very similar to a MemberExpression in
|
// Qualified Id that looks very similar to a MemberExpression in
|
||||||
// JavaScript. For now, we just won't parse statements like:
|
// JavaScript. For now, we just won't parse statements like:
|
||||||
// test: QMLObject { }
|
// test: QMLObject { }
|
||||||
// test: QMLObject.QualifiedId { }
|
// test: QMLObject.QualifiedId { }
|
||||||
|
|
||||||
|
return this.qml_parseScriptBinding();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Parses one of the following Script Bindings:
|
||||||
|
* - Single JavaScript Expression
|
||||||
|
* - QML Statement Block (A block of JavaScript statements)
|
||||||
|
*/
|
||||||
|
pp.qml_parseScriptBinding = function () {
|
||||||
|
var node = this.startNode();
|
||||||
|
node.block = false;
|
||||||
if (this.type === tt.braceL) {
|
if (this.type === tt.braceL) {
|
||||||
return this.qml_parseStatementBlock();
|
node.block = true;
|
||||||
|
node.script = this.qml_parseStatementBlock();
|
||||||
} else {
|
} else {
|
||||||
var node = this.parseExpression(false);
|
node.script = this.parseExpression(false);
|
||||||
this.semicolon();
|
this.semicolon();
|
||||||
return node;
|
|
||||||
}
|
}
|
||||||
|
return this.finishNode(node, "QMLScriptBinding");
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -576,7 +586,7 @@ var injectQML;
|
||||||
this.raise(this.pos, "Expected EOF after QML Root Object");
|
this.raise(this.pos, "Expected EOF after QML Root Object");
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.finishNode(node, "Program");
|
return this.finishNode(node, "QMLProgram");
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
* Contributors:
|
* Contributors:
|
||||||
* QNX Software Systems - Initial API and implementation
|
* QNX Software Systems - Initial API and implementation
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
'use strict';
|
|
||||||
|
|
||||||
// This will only be visible globally if we are in a browser environment
|
// This will only be visible globally if we are in a browser environment
|
||||||
var acornQMLLoose;
|
var acornQMLLoose;
|
||||||
|
@ -20,5 +19,7 @@ var acornQMLLoose;
|
||||||
return define(["./inject.js", "acorn", "acorn/dist/acorn_loose"], mod);
|
return define(["./inject.js", "acorn", "acorn/dist/acorn_loose"], mod);
|
||||||
acornQMLLoose = mod(injectQMLLoose, acorn, acorn); // Plain browser env
|
acornQMLLoose = mod(injectQMLLoose, acorn, acorn); // Plain browser env
|
||||||
})(function (injectQMLLoose, acorn, acorn_loose) {
|
})(function (injectQMLLoose, acorn, acorn_loose) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
return injectQMLLoose(acorn);
|
return injectQMLLoose(acorn);
|
||||||
})
|
})
|
|
@ -8,7 +8,6 @@
|
||||||
* Contributors:
|
* Contributors:
|
||||||
* QNX Software Systems - Initial API and implementation
|
* QNX Software Systems - Initial API and implementation
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
'use strict';
|
|
||||||
|
|
||||||
// This will only be visible globally if we are in a browser environment
|
// This will only be visible globally if we are in a browser environment
|
||||||
var injectQMLLoose;
|
var injectQMLLoose;
|
||||||
|
@ -20,6 +19,8 @@ var injectQMLLoose;
|
||||||
return define([], mod);
|
return define([], mod);
|
||||||
injectQMLLoose = mod(); // Plain browser env
|
injectQMLLoose = mod(); // Plain browser env
|
||||||
})(function () {
|
})(function () {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
return function (acorn) {
|
return function (acorn) {
|
||||||
// Acorn token types
|
// Acorn token types
|
||||||
var tt = acorn.tokTypes;
|
var tt = acorn.tokTypes;
|
||||||
|
@ -43,11 +44,9 @@ var injectQMLLoose;
|
||||||
var loop = true;
|
var loop = true;
|
||||||
while (loop) {
|
while (loop) {
|
||||||
if (this.isContextual(qtt._import)) {
|
if (this.isContextual(qtt._import)) {
|
||||||
var qmlImport = this.qml_parseImportStatement();
|
node.statements.push(this.qml_parseImportStatement());
|
||||||
node.statements.push(qmlImport);
|
|
||||||
} else if (this.isContextual(qtt._pragma)) {
|
} else if (this.isContextual(qtt._pragma)) {
|
||||||
var qmlPragma = this.qml_parsePragmaStatement();
|
node.statements.push(this.qml_parsePragmaStatement());
|
||||||
node.statements.push(qmlPragma);
|
|
||||||
} else {
|
} else {
|
||||||
loop = false;
|
loop = false;
|
||||||
}
|
}
|
||||||
|
@ -292,8 +291,9 @@ var injectQMLLoose;
|
||||||
lp.qml_parsePropertyBinding = function () {
|
lp.qml_parsePropertyBinding = function () {
|
||||||
var node = this.startNode();
|
var node = this.startNode();
|
||||||
node.id = this.qml_parseQualifiedId(false);
|
node.id = this.qml_parseQualifiedId(false);
|
||||||
|
var start = this.storeCurrentPos();
|
||||||
this.expect(tt.colon);
|
this.expect(tt.colon);
|
||||||
node.expr = this.qml_parsePropertyAssignment();
|
node.binding = this.qml_parseBinding(start);
|
||||||
return this.finishNode(node, "QMLPropertyBinding");
|
return this.finishNode(node, "QMLPropertyBinding");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -399,10 +399,11 @@ var injectQMLLoose;
|
||||||
node.kind = this.qml_parseKind();
|
node.kind = this.qml_parseKind();
|
||||||
node.id = this.qml_parseIdent(false);
|
node.id = this.qml_parseIdent(false);
|
||||||
|
|
||||||
|
var start = this.storeCurrentPos();
|
||||||
if (this.eat(tt.colon)) {
|
if (this.eat(tt.colon)) {
|
||||||
node.init = this.qml_parsePropertyAssignment();
|
node.binding = this.qml_parseBinding(start);
|
||||||
} else {
|
} else {
|
||||||
node.init = null;
|
node.binding = null;
|
||||||
this.semicolon();
|
this.semicolon();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -411,35 +412,64 @@ var injectQMLLoose;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Parses one of the following possibilities for a QML Property assignment:
|
* Parses one of the following possibilities for a QML Property assignment:
|
||||||
* - JavaScript Expression
|
|
||||||
* - QML JavaScript Statement Block
|
|
||||||
* - QML Object Literal
|
* - QML Object Literal
|
||||||
|
* - QML Script Binding
|
||||||
*/
|
*/
|
||||||
lp.qml_parsePropertyAssignment = function () {
|
lp.qml_parseBinding = function (start) {
|
||||||
if (this.tok.type === tt.braceL) {
|
if (this.tok.type === tt.braceL) {
|
||||||
return this.qml_parseStatementBlock();
|
return this.qml_parseScriptBinding(start);
|
||||||
} else {
|
}
|
||||||
// Perform look ahead to determine whether this is an expression or
|
// Perform look ahead to determine whether this is an expression or
|
||||||
// a QML Object Literal
|
// a QML Object Literal
|
||||||
var i = 1, la = this.tok;
|
var i = 1, la = this.tok;
|
||||||
|
if (this.qml_isIdent(la.type, la.value)) {
|
||||||
|
la = this.lookAhead(i++);
|
||||||
|
}
|
||||||
|
while (la.type === tt.dot) {
|
||||||
|
la = this.lookAhead(i++);
|
||||||
if (this.qml_isIdent(la.type, la.value)) {
|
if (this.qml_isIdent(la.type, la.value)) {
|
||||||
la = this.lookAhead(i++);
|
la = this.lookAhead(i++);
|
||||||
}
|
}
|
||||||
while (la.type === tt.dot) {
|
|
||||||
la = this.lookAhead(i++);
|
|
||||||
if (this.qml_isIdent(la.type, la.value)) {
|
|
||||||
la = this.lookAhead(i++);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (la.type === tt.braceL) {
|
|
||||||
return this.qml_parseObjectLiteral();
|
|
||||||
} else {
|
|
||||||
var node = this.parseExpression(false);
|
|
||||||
this.semicolon();
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (la.type === tt.braceL) {
|
||||||
|
return this.qml_parseObjectLiteral();
|
||||||
|
} else {
|
||||||
|
return this.qml_parseScriptBinding(start);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Parses one of the following Script Bindings:
|
||||||
|
* - Single JavaScript Expression
|
||||||
|
* - QML Statement Block (A block of JavaScript statements)
|
||||||
|
*/
|
||||||
|
lp.qml_parseScriptBinding = function (start) {
|
||||||
|
// Help out Tern a little by starting the Script Binding at the end of
|
||||||
|
// the colon token (only if we consume invalid syntax).
|
||||||
|
var node = this.startNodeAt(start);
|
||||||
|
node.block = false;
|
||||||
|
if (this.tok.type === tt.braceL) {
|
||||||
|
node.block = true;
|
||||||
|
node.script = this.qml_parseStatementBlock();
|
||||||
|
} else {
|
||||||
|
node.script = this.parseExpression(false);
|
||||||
|
this.semicolon();
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this node consumed valid syntax, reset its start position
|
||||||
|
if (!node.script.type === "Identifier" || node.script.name !== "✖") {
|
||||||
|
if (node.loc) {
|
||||||
|
node.loc.start = node.script.loc.start;
|
||||||
|
}
|
||||||
|
if (node.range) {
|
||||||
|
node.range = node.script.range;
|
||||||
|
}
|
||||||
|
node.start = node.script.start;
|
||||||
|
node.end = node.script.end;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.finishNode(node, "QMLScriptBinding");
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -559,7 +589,7 @@ var injectQMLLoose;
|
||||||
node.rootObject = this.qml_parseObjectLiteral();
|
node.rootObject = this.qml_parseObjectLiteral();
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.finishNode(node, "Program");
|
return this.finishNode(node, "QMLProgram");
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
* Contributors:
|
* Contributors:
|
||||||
* QNX Software Systems - Initial API and implementation
|
* QNX Software Systems - Initial API and implementation
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
"use-strict";
|
||||||
|
|
||||||
var tests = [];
|
var tests = [];
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
* Contributors:
|
* Contributors:
|
||||||
* QNX Software Systems - Initial API and implementation
|
* QNX Software Systems - Initial API and implementation
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
"use-strict";
|
||||||
|
|
||||||
var driver = require("./driver.js");
|
var driver = require("./driver.js");
|
||||||
require("./tests-qml.js");
|
require("./tests-qml.js");
|
||||||
|
@ -48,7 +49,7 @@ var stats, modes = {
|
||||||
return opts.loose !== false;
|
return opts.loose !== false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
function report(state, code, message) {
|
function report(state, code, message) {
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -8,15 +8,15 @@
|
||||||
* Contributors:
|
* Contributors:
|
||||||
* QNX Software Systems - Initial API and implementation
|
* QNX Software Systems - Initial API and implementation
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
'use strict';
|
|
||||||
|
|
||||||
(function (mod) {
|
(function (mod) {
|
||||||
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||||
return mod(require("acorn/dist/walk"));
|
return module.exports = mod(require("acorn/dist/walk"));
|
||||||
if (typeof define == "function" && define.amd) // AMD
|
if (typeof define == "function" && define.amd) // AMD
|
||||||
return define(["acorn/dist/walk"], mod);
|
return define(["acorn/dist/walk"], mod);
|
||||||
mod(acorn.walk); // Plain browser env
|
mod(acorn.walk); // Plain browser env
|
||||||
})(function (walk) {
|
})(function (walk) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
function skipThrough(node, st, c) {
|
function skipThrough(node, st, c) {
|
||||||
c(node, st)
|
c(node, st)
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@
|
||||||
function ignore(node, st, c) {}
|
function ignore(node, st, c) {}
|
||||||
|
|
||||||
var base = walk.base;
|
var base = walk.base;
|
||||||
base["Program"] = function (node, st, c) {
|
base["QMLProgram"] = function (node, st, c) {
|
||||||
c(node.headerStatements, st);
|
c(node.headerStatements, st);
|
||||||
if (node.rootObject) {
|
if (node.rootObject) {
|
||||||
c(node.rootObject, st, "QMLRootObject");
|
c(node.rootObject, st, "QMLRootObject");
|
||||||
|
@ -49,18 +49,22 @@
|
||||||
};
|
};
|
||||||
base["QMLMember"] = skipThrough;
|
base["QMLMember"] = skipThrough;
|
||||||
base["QMLPropertyDeclaration"] = function (node, st, c) {
|
base["QMLPropertyDeclaration"] = function (node, st, c) {
|
||||||
if (node.init) {
|
if (node.binding) {
|
||||||
c(node.init, st);
|
c(node.binding, st);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
base["QMLSignalDefinition"] = ignore;
|
base["QMLSignalDefinition"] = ignore;
|
||||||
base["QMLPropertyBinding"] = function (node, st, c) {
|
base["QMLPropertyBinding"] = function (node, st, c) {
|
||||||
c(node.expr, st);
|
c(node.binding, st);
|
||||||
};
|
};
|
||||||
|
base["QMLScriptBinding"] = function (node, st, c) {
|
||||||
|
c(node.script, st);
|
||||||
|
}
|
||||||
base["QMLQualifiedID"] = ignore;
|
base["QMLQualifiedID"] = ignore;
|
||||||
base["QMLStatementBlock"] = function (node, st, c) {
|
base["QMLStatementBlock"] = function (node, st, c) {
|
||||||
for (var i = 0; i < node.statements.length; i++) {
|
for (var i = 0; i < node.statements.length; i++) {
|
||||||
c(node.statements[i], st, "Statement");
|
c(node.statements[i], st, "Statement");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
return walk;
|
||||||
})
|
})
|
|
@ -21,7 +21,9 @@
|
||||||
<script src="../node_modules/acorn/dist/walk.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/inject.js"></script>
|
||||||
<script src="../node_modules/acorn-qml/index.js"></script>
|
<script src="../node_modules/acorn-qml/index.js"></script>
|
||||||
<script src="../node_modules/acorn-qml/walk/walk.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 -->
|
<!-- Tern JS -->
|
||||||
<script src="../node_modules/tern/lib/signal.js"></script>
|
<script src="../node_modules/tern/lib/signal.js"></script>
|
||||||
|
@ -57,7 +59,9 @@
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>Demo with QML Tern plugin </h1>
|
<h1>Demo with QML Tern plugin </h1>
|
||||||
|
<div style="border: solid 1px #AAAAAA;">
|
||||||
<form><textarea id="code" name="code">import QtQuick 2.3 Window { 	prop: { 		Qt.quit(); 	} } </textarea></form>
|
<form><textarea id="code" name="code">import QtQuick 2.3 Window { 	prop: { 		Qt.quit(); 	} } </textarea></form>
|
||||||
|
</div>
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
function passAndHint(cm) {
|
function passAndHint(cm) {
|
||||||
setTimeout(function() {cm.execCommand("autocomplete");}, 100);
|
setTimeout(function() {cm.execCommand("autocomplete");}, 100);
|
||||||
|
|
|
@ -10,11 +10,13 @@
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
(function (mod) {
|
(function (mod) {
|
||||||
if (typeof exports === "object" && typeof module === "object") // CommonJS
|
if (typeof exports === "object" && typeof module === "object") // CommonJS
|
||||||
return mod(require("acorn"), require("acorn/dist/acorn_loose"), require("tern/lib/infer"), require("tern/lib/tern"));
|
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"));
|
||||||
if (typeof define === "function" && define.amd) // AMD
|
if (typeof define === "function" && define.amd) // AMD
|
||||||
return define([ "acorn", "acorn/dist/acorn_loose", "tern/lib/infer", "tern/lib/tern", "tern/lib/def"], mod);
|
return define([ "acorn", "acorn/dist/acorn_loose", "acorn/dist/walk", "acorn-qml", "acorn-qml/loose", "acorn-qml/walk",
|
||||||
mod(acorn, acorn, tern, tern, tern.def); // Plain browser env
|
"tern/lib/infer", "tern"], mod);
|
||||||
})(function (acorn, acornLoose, infer, tern, def) {
|
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) {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
// Outside of the browser environment we have to grab 'def' from 'infer'
|
// Outside of the browser environment we have to grab 'def' from 'infer'
|
||||||
|
@ -22,33 +24,137 @@
|
||||||
def = infer.def;
|
def = infer.def;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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 = [];
|
||||||
|
};
|
||||||
|
|
||||||
|
ScopeBuilder.prototype.getRootScope = function () {
|
||||||
|
return this.scopes.length > 0 ? this.scopes[0] : null;
|
||||||
|
};
|
||||||
|
|
||||||
|
ScopeBuilder.prototype.isRootScope = function (scope) {
|
||||||
|
return scope === this.rootScope();
|
||||||
|
};
|
||||||
|
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ScopeBuilder.prototype.getObjScope = function (scope) {
|
||||||
|
while (scope && scope.name != "<qml-obj>") {
|
||||||
|
scope = scope.prev;
|
||||||
|
}
|
||||||
|
return scope;
|
||||||
|
};
|
||||||
|
|
||||||
|
ScopeBuilder.prototype.getIDScope = function () {
|
||||||
|
return this.file.qmlIDScope;
|
||||||
|
};
|
||||||
|
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
|
||||||
|
ScopeBuilder.prototype.forEachPropertyScope = function (callback) {
|
||||||
|
for (var i = 0; i < this.propScopes.length; i++) {
|
||||||
|
callback(this.propScopes[i], this.propScopes[i].objScope);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
|
||||||
|
ScopeBuilder.prototype.forEachJSScope = function (callback) {
|
||||||
|
for (var i = 0; i < this.jsScopes.length; i++) {
|
||||||
|
callback(this.jsScopes[i]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ScopeBuilder.prototype.newSignalScope = function (scope, node) {
|
||||||
|
var sigScope = new infer.Scope(scope, node);
|
||||||
|
sigScope.name = "<qml-signal>";
|
||||||
|
this.sigScopes.push(sigScope);
|
||||||
|
return sigScope
|
||||||
|
};
|
||||||
|
|
||||||
|
ScopeBuilder.prototype.hasFunctionScope = function (objScope) {
|
||||||
|
var found = null;
|
||||||
|
this.forEachFunctionScope(function (scope) {
|
||||||
|
if (scope.objScope == objScope) {
|
||||||
|
found = scope;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return found;
|
||||||
|
};
|
||||||
|
|
||||||
// Helper for adding a property to a scope.
|
// Helper for adding a property to a scope.
|
||||||
function addVar(scope, node) {
|
function addVar(scope, node) {
|
||||||
if (node.type === "QMLQualifiedID") {
|
return scope.defProp(node.name, node);
|
||||||
var currScope = scope;
|
|
||||||
for (var i = 1; i < node.parts.length; i++) {
|
|
||||||
var name = node.parts[i].name;
|
|
||||||
curr = curr.hasProp(name);
|
|
||||||
}
|
|
||||||
return prop;
|
|
||||||
} else if (node.type === "Identifier") {
|
|
||||||
return scope.defProp(node.name, node);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper for finding a property in a scope.
|
// Helper for finding a property in a scope.
|
||||||
function findProp(node, scope) {
|
function findProp(node, scope, pos) {
|
||||||
|
if (pos == null || pos < 0) {
|
||||||
|
pos = Number.MAX_VALUE;
|
||||||
|
}
|
||||||
if (node.type === "QMLQualifiedID") {
|
if (node.type === "QMLQualifiedID") {
|
||||||
var curr = scope;
|
return (function recurse(i, prop) {
|
||||||
for (var i = 0; i < node.parts.length; i++) {
|
if (i >= node.parts.length || pos < node.parts[i].start) {
|
||||||
var name = node.parts[i].name;
|
return prop;
|
||||||
curr = curr.hasProp(name);
|
|
||||||
if (!curr) {
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
if (!prop) {
|
||||||
return curr;
|
prop = scope.hasProp(node.parts[i].name);
|
||||||
|
if (prop) {
|
||||||
|
return recurse(i + 1, prop);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var obj = prop.getType();
|
||||||
|
if (obj) {
|
||||||
|
var p = obj.hasProp(node.parts[i].name);
|
||||||
|
if (p) {
|
||||||
|
return recurse(i + 1, p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
})(0, null);
|
||||||
} else if (node.type === "Identifier") {
|
} else if (node.type === "Identifier") {
|
||||||
return scope.hasProp(node.name);
|
return scope.hasProp(node.name);
|
||||||
}
|
}
|
||||||
|
@ -61,109 +167,168 @@
|
||||||
return parent instanceof tern.Server ? parent : null;
|
return parent instanceof tern.Server ? parent : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function preParse(text, options) {
|
// Helper for getting the current file's scope builder
|
||||||
// Force ECMA Version to 5
|
function getScopeBuilder() {
|
||||||
options.ecmaVersion = 5;
|
return infer.cx().qmlScopeBuilder;
|
||||||
|
}
|
||||||
|
|
||||||
// Register qml plugin with main parser
|
// Object which holds two scopes
|
||||||
var plugins = options.plugins;
|
function Scopes(object, property) {
|
||||||
if (!plugins) plugins = options.plugins = {};
|
this.object = object;
|
||||||
plugins["qml"] = true;
|
this.property = property || object;
|
||||||
|
|
||||||
// Register qml plugin with loose parser
|
|
||||||
var pluginsLoose = options.pluginsLoose;
|
|
||||||
if (!pluginsLoose) pluginsLoose = options.pluginsLoose = {};
|
|
||||||
pluginsLoose["qml"] = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function extendTernScopeGatherer(scopeGatherer) {
|
function extendTernScopeGatherer(scopeGatherer) {
|
||||||
scopeGatherer["QMLImportStatement"] = function (node, scope, c) {
|
// Build up the scopes in the QML document using the scopeGatherer. A second
|
||||||
if (node.qualifier) {
|
// pass will be done after the scopeGatherer executes in order to finalize a
|
||||||
addVar(scope, node.qualifier.id);
|
// few of the extra scopes such as the JavaScript, ID, Property, and Signal
|
||||||
}
|
// Handler scopes.
|
||||||
};
|
|
||||||
scopeGatherer["QMLObjectLiteral"] = function (node, scope, c) {
|
scopeGatherer["QMLObjectLiteral"] = function (node, scope, c) {
|
||||||
var inner = node.scope = new infer.Scope(scope, node);
|
var inner = node.scope = getScopeBuilder().newObjScope(node);
|
||||||
c(node.body, inner);
|
c(node.body, inner);
|
||||||
};
|
};
|
||||||
scopeGatherer["QMLPropertyDeclaration"] = function (node, scope, c) {
|
scopeGatherer["QMLMemberBlock"] = function (node, scope, c) {
|
||||||
var prop = addVar(scope, node.id);
|
var s = infer.cx().curOrigin;
|
||||||
var inner = scope;
|
var propertyScope = node.scope = getScopeBuilder().newPropertyScope(scope, node);
|
||||||
if (node.init) {
|
var scopes = new Scopes(scope, propertyScope);
|
||||||
// Create a JavaScript scope if init is a JavaScript environment
|
for (var i = 0; i < node.members.length; i++) {
|
||||||
if (!node.init.type.startsWith("QML")) {
|
var member = node.members[i];
|
||||||
inner = node.scope = getServer().createJSScope(scope, node);
|
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);
|
||||||
}
|
}
|
||||||
c(node.init, inner);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
scopeGatherer["QMLPropertyBinding"] = function (node, scope, c) {
|
scopeGatherer["QMLPropertyDeclaration"] = function (node, scopes, c) {
|
||||||
// Check for the 'id' property being set
|
var inner = scopes.object;
|
||||||
var idParts = node.id.parts;
|
var prop = addVar(inner, node.id);
|
||||||
if (idParts.length == 1 && idParts[0].name === "id") {
|
if (node.binding) {
|
||||||
if (node.expr.type === "Identifier") {
|
node.binding.scope = inner;
|
||||||
node.prop = scope.prev.defProp(node.expr.name, node.expr);
|
c(node.binding, inner);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
scopeGatherer["QMLPropertyBinding"] = function (node, scopes, c) {
|
||||||
// Create a JavaScript scope if init is a JavaScript environment
|
// Create a JavaScript scope if init is a JavaScript environment
|
||||||
var inner = scope;
|
var inner = node.binding.scope = scopes.object;
|
||||||
if (!node.expr.type.startsWith("QML")) {
|
// Check for the 'id' property being set
|
||||||
inner = node.scope = getServer().createJSScope(scope, node);
|
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 {
|
} else {
|
||||||
// If this appears to be a signal handler, pre-emptively create a new scope that
|
// If this appears to be a signal handler, pre-emptively create a new scope that
|
||||||
// will store references to the signal's arguments
|
// will store references to the signal's arguments
|
||||||
var last = getLastIndex(idParts).name;
|
if (node.id.name.startsWith("on")) {
|
||||||
if (last.startsWith("on")) {
|
inner = node.binding.scope = new infer.Scope(inner, node);
|
||||||
inner = node.scope = new infer.Scope(scope, node);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delegate down to the expression
|
// Delegate down to the expression
|
||||||
c(node.expr, inner);
|
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) {
|
scopeGatherer["QMLStatementBlock"] = function (node, scope, c) {
|
||||||
var inner = node.scope = getServer().createJSScope(scope, node);
|
var inner = getScopeBuilder().newJSScope(node.scope || scope, node);
|
||||||
|
node.scope = inner;
|
||||||
for (var i = 0; i < node.statements.length; i++) {
|
for (var i = 0; i < node.statements.length; i++) {
|
||||||
c(node.statements[i], inner, "Statement");
|
c(node.statements[i], inner, "Statement");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
scopeGatherer["QMLSignalDefinition"] = function (node, scope, c) {
|
scopeGatherer["QMLSignalDefinition"] = function (node, scope, c) {
|
||||||
|
// Scope Builder
|
||||||
|
var sb = getScopeBuilder();
|
||||||
|
|
||||||
// Define the signal arguments in their own separate scope
|
// Define the signal arguments in their own separate scope
|
||||||
var argNames = [],
|
var argNames = [],
|
||||||
argVals = [];
|
argVals = [];
|
||||||
var fnScope = new infer.Scope(scope, node);
|
var sigScope = sb.newSignalScope(scope, node);
|
||||||
for (var i = 0; i < node.params.length; i++) {
|
for (var i = 0; i < node.params.length; i++) {
|
||||||
var param = node.params[i];
|
var param = node.params[i];
|
||||||
argNames.push(param.id.name);
|
argNames.push(param.id.name);
|
||||||
argVals.push(addVar(fnScope, param.id));
|
argVals.push(addVar(sigScope, param.id));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Define the signal function type which can be referenced from JavaScript
|
// Define the signal function type which can be referenced from JavaScript
|
||||||
var sig = addVar(scope, node.id);
|
var sig = addVar(scope, node.id);
|
||||||
sig.fnType = new infer.Fn(node.id.name, new infer.AVal, argVals, argNames, infer.ANull);
|
sig.isFunction = true;
|
||||||
sig.fnType.fnScope = fnScope;
|
sig.sigType = new infer.Fn(node.id.name, new infer.AVal, argVals, argNames, infer.ANull);
|
||||||
|
sig.sigType.sigScope = sigScope;
|
||||||
|
|
||||||
// Define the signal handler property
|
// Define the signal handler property
|
||||||
var handler = scope.defProp(getSignalHandlerName(node.id.name), node.id);
|
var handler = scope.defProp(getSignalHandlerName(node.id.name), node.id);
|
||||||
handler.sig = sig.fnType;
|
handler.sig = sig.sigType;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function extendTernInferExprVisitor(inferExprVisitor) {
|
// 'infer' taken from infer.js
|
||||||
// ret' taken from infer.js
|
function inf(node, scope, out, name) {
|
||||||
function ret(f) {
|
var handler = infer.inferExprVisitor[node.type];
|
||||||
return function (node, scope, out, name) {
|
return handler ? handler(node, scope, out, name) : infer.ANull;
|
||||||
var r = f(node, scope, name);
|
}
|
||||||
if (out) r.propagate(out);
|
|
||||||
return r;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 'ret' taken from infer.js
|
||||||
|
function ret(f) {
|
||||||
|
return function (node, scope, out, name) {
|
||||||
|
var r = f(node, scope, name);
|
||||||
|
if (out) r.propagate(out);
|
||||||
|
return r;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 'fill' taken from infer.js
|
||||||
|
function fill(f) {
|
||||||
|
return function(node, scope, out, name) {
|
||||||
|
if (!out) out = new AVal;
|
||||||
|
f(node, scope, out, name);
|
||||||
|
return out;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function extendTernInferExprVisitor(inferExprVisitor) {
|
||||||
// Extend the inferExprVisitor methods
|
// Extend the inferExprVisitor methods
|
||||||
inferExprVisitor["QMLStatementBlock"] = ret(function (node, scope, name) {
|
inferExprVisitor["QMLStatementBlock"] = ret(function (node, scope, name) {
|
||||||
return infer.ANull; // Statement blocks have no type
|
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) {
|
inferExprVisitor["QMLObjectLiteral"] = ret(function (node, scope, name) {
|
||||||
return node.scope.objType;
|
return node.scope.objType;
|
||||||
});
|
});
|
||||||
|
@ -178,12 +343,6 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
function extendTernInferWrapper(inferWrapper) {
|
function extendTernInferWrapper(inferWrapper) {
|
||||||
// 'infer' taken from infer.js
|
|
||||||
function inf(node, scope, out, name) {
|
|
||||||
var handler = infer.inferExprVisitor[node.type];
|
|
||||||
return handler ? handler(node, scope, out, name) : infer.ANull;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extend the inferWrapper methods
|
// Extend the inferWrapper methods
|
||||||
inferWrapper["QMLObjectLiteral"] = function (node, scope, c) {
|
inferWrapper["QMLObjectLiteral"] = function (node, scope, c) {
|
||||||
// Define a new Obj which represents this Object Literal
|
// Define a new Obj which represents this Object Literal
|
||||||
|
@ -198,50 +357,50 @@
|
||||||
});
|
});
|
||||||
c(node.body, node.scope);
|
c(node.body, node.scope);
|
||||||
};
|
};
|
||||||
inferWrapper["QMLPropertyDeclaration"] = function (node, scope, c) {
|
inferWrapper["QMLMemberBlock"] = function (node, scope, c) {
|
||||||
var prop = findProp(node.id, scope);
|
var scopes = new Scopes(scope, node.scope);
|
||||||
// Infer the property's type from its assigned type
|
for (var i = 0; i < node.members.length; i++) {
|
||||||
switch (node.kind) {
|
var member = node.members[i];
|
||||||
case "int":
|
if (member.type === "QMLPropertyDeclaration" || member.type === "QMLPropertyBinding") {
|
||||||
case "double":
|
c(member, scopes);
|
||||||
case "real":
|
} else {
|
||||||
infer.cx().num.propagate(prop);
|
c(member, scope);
|
||||||
break;
|
}
|
||||||
case "string":
|
|
||||||
case "color":
|
|
||||||
infer.cx().str.propagate(prop);
|
|
||||||
break;
|
|
||||||
case "boolean":
|
|
||||||
infer.cx().bool.propagate(prop);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// Also infer the type from its init expression
|
|
||||||
if (node.init) {
|
|
||||||
c(node.init, scope);
|
|
||||||
inf(node.init, scope, prop, node.id.name);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
inferWrapper["QMLPropertyBinding"] = function (node, scope, c) {
|
inferWrapper["QMLPropertyDeclaration"] = function (node, scopes, c) {
|
||||||
c(node.expr, node.scope || scope);
|
var prop = findProp(node.id, scopes.property);
|
||||||
var prop = findProp(node.id, scope);
|
|
||||||
if (prop) {
|
if (prop) {
|
||||||
if (prop.sig) {
|
infKind(node.kind, prop);
|
||||||
// This is a signal handler and we should populate its scope with
|
if (node.binding) {
|
||||||
// the arguments from its parent function.
|
c(node.binding, scopes.object);
|
||||||
prop.sig.fnScope.forAllProps(function (name, prop, curr) {
|
inf(node.binding, scopes.object, prop, node.id.name);
|
||||||
if (curr) {
|
}
|
||||||
node.scope.props[name] = prop;
|
}
|
||||||
}
|
};
|
||||||
});
|
inferWrapper["QMLPropertyBinding"] = function (node, scopes, c) {
|
||||||
} else {
|
c(node.binding, scopes.object);
|
||||||
inf(node.expr, scope, prop, getLastIndex(node.id.parts));
|
// 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 {
|
} else {
|
||||||
// Check for the 'id' property being set
|
var prop = findProp(node.id, scopes.property);
|
||||||
var idParts = node.id.parts;
|
if (prop) {
|
||||||
if (idParts.length == 1 && idParts[0].name === "id") {
|
if (prop.sig) {
|
||||||
if (node.expr.type === "Identifier") {
|
// This is a signal handler and we should populate its scope with
|
||||||
scope.objType.propagate(node.prop);
|
// 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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -253,9 +412,12 @@
|
||||||
};
|
};
|
||||||
inferWrapper["QMLSignalDefinition"] = function (node, scope, c) {
|
inferWrapper["QMLSignalDefinition"] = function (node, scope, c) {
|
||||||
var sig = scope.getProp(node.id.name);
|
var sig = scope.getProp(node.id.name);
|
||||||
var retval = new infer.Obj(true, "Signal");
|
for (var i = 0; i < node.params.length; i++) {
|
||||||
sig.fnType.retval = retval;
|
var param = node.params[i];
|
||||||
sig.fnType.propagate(sig);
|
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 handler = scope.getProp(getSignalHandlerName(node.id.name));
|
||||||
var obj = new infer.Obj(true, "Signal Handler");
|
var obj = new infer.Obj(true, "Signal Handler");
|
||||||
|
@ -268,12 +430,26 @@
|
||||||
typeFinder["QMLObjectLiteral"] = function (node, scope) {
|
typeFinder["QMLObjectLiteral"] = function (node, scope) {
|
||||||
return node.scope.objType;
|
return node.scope.objType;
|
||||||
};
|
};
|
||||||
typeFinder["QMLStatementBlock"] = function (node, scope) {
|
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;
|
return infer.ANull;
|
||||||
};
|
};
|
||||||
typeFinder["QMLQualifiedID"] = function (node, scope) {
|
typeFinder["QMLQualifiedID"] = function (node, scope) {
|
||||||
return findProp(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());
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function extendTernSearchVisitor(searchVisitor) {
|
function extendTernSearchVisitor(searchVisitor) {
|
||||||
|
@ -281,11 +457,46 @@
|
||||||
searchVisitor["QMLObjectLiteral"] = function (node, scope, c) {
|
searchVisitor["QMLObjectLiteral"] = function (node, scope, c) {
|
||||||
c(node.body, node.scope);
|
c(node.body, node.scope);
|
||||||
};
|
};
|
||||||
searchVisitor["QMLPropertyBinding"] = function (node, scope, c) {
|
searchVisitor["QMLMemberBlock"] = function (node, scope, c) {
|
||||||
c(node.id, scope);
|
var scopes = new Scopes(scope, node.scope);
|
||||||
// A binding that is referencing a signal holds a scope. Other property bindings do not.
|
for (var i = 0; i < node.members.length; i++) {
|
||||||
c(node.expr, node.scope || scope);
|
var member = node.members[i];
|
||||||
|
if (member.type === "QMLPropertyDeclaration" || member.type === "QMLPropertyBinding") {
|
||||||
|
c(member, scopes);
|
||||||
|
} 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";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var prop = findProp(node.id, scopes.property);
|
||||||
|
if (!prop) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c(node.id, scopes.property);
|
||||||
|
c(node.binding, node.binding.scope);
|
||||||
|
};
|
||||||
|
searchVisitor["QML_ID"] = function(node, st, c) {};
|
||||||
searchVisitor["QMLStatementBlock"] = function (node, scope, c) {
|
searchVisitor["QMLStatementBlock"] = function (node, scope, c) {
|
||||||
for (var i = 0; i < node.statements.length; i++) {
|
for (var i = 0; i < node.statements.length; i++) {
|
||||||
c(node.statements[i], node.scope, "Statement");
|
c(node.statements[i], node.scope, "Statement");
|
||||||
|
@ -293,40 +504,83 @@
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Prepares acorn to consume QML syntax rather than standard JavaScript
|
||||||
|
*/
|
||||||
|
function preParse(text, options) {
|
||||||
|
// Force ECMA Version to 5
|
||||||
|
options.ecmaVersion = 5;
|
||||||
|
|
||||||
|
// Register qml plugin with main parser
|
||||||
|
var plugins = options.plugins;
|
||||||
|
if (!plugins) plugins = options.plugins = {};
|
||||||
|
plugins["qml"] = true;
|
||||||
|
|
||||||
|
// Register qml plugin with loose parser
|
||||||
|
var pluginsLoose = options.pluginsLoose;
|
||||||
|
if (!pluginsLoose) pluginsLoose = options.pluginsLoose = {};
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Register the QML plugin in Tern
|
// Register the QML plugin in Tern
|
||||||
tern.registerPlugin("qml", function (server) {
|
tern.registerPlugin("qml", function (server) {
|
||||||
// First we want to replace the top-level defs array with our own and save the
|
// First we want to replace the top-level defs array with our own and save the
|
||||||
// JavaScript specific defs to a new array 'jsDefs'. In order to make sure no
|
// JavaScript specific defs to a new array 'jsDefs'. In order to make sure no
|
||||||
// other plugins mess with the new defs after us, we override addDefs and add
|
// other plugins mess with the new defs after us, we override addDefs.
|
||||||
// a new method called 'addQMLDefs' to facilitate adding QML specific definitions.
|
|
||||||
server.jsDefs = server.defs;
|
server.jsDefs = server.defs;
|
||||||
server.defs = [];
|
server.defs = [];
|
||||||
server.addQMLDefs = function (defs, toFront) {
|
|
||||||
if (toFront) this.defs.unshift(defs)
|
|
||||||
else this.defs.push(defs)
|
|
||||||
if (this.cx) this.reset()
|
|
||||||
}
|
|
||||||
server.addDefs = function (defs, toFront) {
|
server.addDefs = function (defs, toFront) {
|
||||||
if (toFront) this.jsDefs.unshift(defs)
|
if (toFront) this.jsDefs.unshift(defs);
|
||||||
else this.jsDefs.push(defs)
|
else this.jsDefs.push(defs);
|
||||||
if (this.cx) this.reset()
|
if (this.cx) this.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add a new method to server which creates a js scope based on jsDefs
|
// Hook into server signals
|
||||||
server.createJSScope = function (prev, node, isBlock) {
|
|
||||||
var scope = new infer.Scope(prev, node, isBlock);
|
|
||||||
if (this.jsDefs) for (var i = 0; i < this.jsDefs.length; ++i)
|
|
||||||
def.load(this.jsDefs[i], scope);
|
|
||||||
return scope;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Force Tern to use the QML plugin for Acorn
|
|
||||||
server.on("preParse", preParse);
|
server.on("preParse", preParse);
|
||||||
|
server.on("preInfer", scopeGatheringSecondPass);
|
||||||
// Add a new scope to a file which will hold its root Object and such
|
|
||||||
server.on("beforeLoad", function(file) {
|
server.on("beforeLoad", function(file) {
|
||||||
|
// Create the file's top scope
|
||||||
file.scope = new infer.Scope(infer.cx().topScope);
|
file.scope = new infer.Scope(infer.cx().topScope);
|
||||||
file.scope.name = file.name;
|
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;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Extend Tern's inferencing system to include QML syntax
|
// Extend Tern's inferencing system to include QML syntax
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* 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";
|
"use strict";
|
||||||
|
|
||||||
var fs = require("fs"), path = require("path");
|
var fs = require("fs"), path = require("path");
|
||||||
|
var ternQML = require("../qml.js");
|
||||||
// Acorn and Acorn-QML
|
var tern = require("tern");
|
||||||
var acorn = require("acorn"),
|
|
||||||
acorn_loose = require("acorn/dist/acorn_loose"),
|
|
||||||
walk = require("acorn/dist/walk"),
|
|
||||||
acornQML = require("acorn-qml"),
|
|
||||||
acornQML_loose = require("acorn-qml/loose"),
|
|
||||||
acornQML_walk = require("acorn-qml/walk");
|
|
||||||
|
|
||||||
// Tern and Tern-QML
|
|
||||||
var tern = require("tern"),
|
|
||||||
ternQML = require("../qml.js");
|
|
||||||
|
|
||||||
var projectDir = path.resolve(__dirname, "..");
|
var projectDir = path.resolve(__dirname, "..");
|
||||||
var resolve = function(pth) {
|
var resolve = function(pth) {
|
||||||
|
@ -22,17 +22,28 @@ var testCases = [];
|
||||||
var groupName;
|
var groupName;
|
||||||
|
|
||||||
function TestCase(group, code, run) {
|
function TestCase(group, code, run) {
|
||||||
|
this.code = code;
|
||||||
this.group = group;
|
this.group = group;
|
||||||
this.runTest = run || function (server, callback) {
|
this.runTest = run || function (server, callback) {
|
||||||
callback("fail", code, "runTest function was not provided.");
|
callback("fail", code, "runTest function was not provided.");
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.groupStart = function(group) {
|
exports.isolate = function (code) {
|
||||||
|
for (var i = 0; i < testCases.length; i++) {
|
||||||
|
var test = testCases[i];
|
||||||
|
if (test.group === groupName && test.code !== code) {
|
||||||
|
testCases.splice(i, 1);
|
||||||
|
i--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.groupStart = function (group) {
|
||||||
groupName = group;
|
groupName = group;
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.groupEnd = function() {
|
exports.groupEnd = function () {
|
||||||
groupName = undefined;
|
groupName = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,7 +93,7 @@ exports.testDefinition = function (code, expected, beforeTest) {
|
||||||
}, beforeTest));
|
}, beforeTest));
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.runTests = function(config, callback) {
|
exports.runTests = function (config, callback) {
|
||||||
for (var i = 0; i < testCases.length; ++i) {
|
for (var i = 0; i < testCases.length; ++i) {
|
||||||
var test = testCases[i];
|
var test = testCases[i];
|
||||||
if (test.group === config.group) {
|
if (test.group === config.group) {
|
||||||
|
@ -103,7 +114,7 @@ function createServer(defs) {
|
||||||
return server;
|
return server;
|
||||||
}
|
}
|
||||||
|
|
||||||
function assertCompletion (server, code, expected, pos, callback) {
|
function assertCompletion(server, code, expected, pos, callback) {
|
||||||
server.addFile("test1.qml", code);
|
server.addFile("test1.qml", code);
|
||||||
server.request({
|
server.request({
|
||||||
query : {
|
query : {
|
||||||
|
@ -128,7 +139,7 @@ function assertCompletion (server, code, expected, pos, callback) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
function assertDefinition (server, code, expected, pos, callback) {
|
function assertDefinition(server, code, expected, pos, callback) {
|
||||||
server.addFile("test1.qml", code);
|
server.addFile("test1.qml", code);
|
||||||
server.request({
|
server.request({
|
||||||
query : {
|
query : {
|
||||||
|
@ -163,7 +174,7 @@ function addPath(str, pt) {
|
||||||
return str + " (" + pt + ")";
|
return str + " (" + pt + ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
var misMatch = exports.misMatch = function(exp, act) {
|
var misMatch = exports.misMatch = function (exp, act) {
|
||||||
if (!exp || !act || (typeof exp != "object") || (typeof act != "object")) {
|
if (!exp || !act || (typeof exp != "object") || (typeof act != "object")) {
|
||||||
if (exp !== act) return ppJSON(exp) + " !== " + ppJSON(act);
|
if (exp !== act) return ppJSON(exp) + " !== " + ppJSON(act);
|
||||||
} else if (exp instanceof RegExp || act instanceof RegExp) {
|
} else if (exp instanceof RegExp || act instanceof RegExp) {
|
||||||
|
|
|
@ -1,3 +1,13 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* 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
|
||||||
|
*******************************************************************************/
|
||||||
module.exports = {
|
module.exports = {
|
||||||
"!name": "ecma5",
|
"!name": "ecma5",
|
||||||
"!define": {
|
"!define": {
|
||||||
|
|
|
@ -1,3 +1,15 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2015 QNX Software Systems and others.
|
||||||
|
* All rights reserved. This program and the accompanying materials
|
||||||
|
* are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
* which accompanies this distribution, and is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
*
|
||||||
|
* Contributors:
|
||||||
|
* QNX Software Systems - Initial API and implementation
|
||||||
|
*******************************************************************************/
|
||||||
|
"use-strict";
|
||||||
|
|
||||||
var driver = require("./driver.js");
|
var driver = require("./driver.js");
|
||||||
|
|
||||||
function group(name) {
|
function group(name) {
|
||||||
|
|
|
@ -1,58 +1,67 @@
|
||||||
var testCompletion = require("./driver.js").testCompletion
|
/*******************************************************************************
|
||||||
var groupStart = require("./driver.js").groupStart;
|
* Copyright (c) 2015 QNX Software Systems and others.
|
||||||
var groupEnd = require("./driver.js").groupEnd;
|
* All rights reserved. This program and the accompanying materials
|
||||||
|
* are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
* which accompanies this distribution, and is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
*
|
||||||
|
* Contributors:
|
||||||
|
* QNX Software Systems - Initial API and implementation
|
||||||
|
*******************************************************************************/
|
||||||
|
"use-strict";
|
||||||
|
|
||||||
|
var driver = require("./driver.js");
|
||||||
|
var test = driver.test;
|
||||||
|
var testCompletion = driver.testCompletion;
|
||||||
|
var groupStart = driver.groupStart;
|
||||||
|
var groupEnd = driver.groupEnd;
|
||||||
|
|
||||||
var group = exports.group = "Code Completion"
|
var group = exports.group = "Code Completion"
|
||||||
groupStart(group);
|
groupStart(group);
|
||||||
|
|
||||||
// Import Completions
|
// Import Completions
|
||||||
testCompletion("import QtQu|", {
|
//testCompletion("import QtQu|", {
|
||||||
start: { line: 0, ch: 7 },
|
// start: { line: 0, ch: 7 },
|
||||||
end: { line: 0, ch: 11 },
|
// end: { line: 0, ch: 11 },
|
||||||
isProperty: false,
|
// isProperty: false,
|
||||||
isObjectKey: false,
|
// isObjectKey: false,
|
||||||
completions: [{ name: "QtQuick", type: "", origin: "QML" }]
|
// 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}");
|
||||||
|
//});
|
||||||
|
|
||||||
testCompletion("import QtQuick 2.|", {
|
// QML Object Property Completions
|
||||||
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('import "test2.qml" as Other\nWindow {\n\tOther {\n\t\t|\n\t}\n}', {
|
|
||||||
start: { line: 3, ch: 2 },
|
|
||||||
end: { line: 3, ch: 2 },
|
|
||||||
isProperty: false,
|
|
||||||
isObjectKey: false,
|
|
||||||
completions: [{ name: "width", type: "number", origin: "test2.qml" }]
|
|
||||||
}, function (server) {
|
|
||||||
server.options.getFile = function (name, callback) {
|
|
||||||
if (name == "test2.qml") {
|
|
||||||
return callback(null, "Button {\n\tproperty int width\n}");
|
|
||||||
}
|
|
||||||
return callback(null, null);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Property Binding Completions
|
|
||||||
testCompletion("Window {\n\tproperty int height\n\the|\n}", {
|
testCompletion("Window {\n\tproperty int height\n\the|\n}", {
|
||||||
start: { line: 2, ch: 1 },
|
start: { line: 2, ch: 1 },
|
||||||
end: { line: 2, ch: 3 },
|
end: { line: 2, ch: 3 },
|
||||||
|
@ -73,9 +82,121 @@ testCompletion("Window {\n\tproperty int height\n\tproperty int width\n\tpropert
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
testCompletion("Window {\n\ttest: |\n}", {
|
testCompletion("Window {\n\tproperty int height\n\tObject {\n\t\t|\n\t}\n}", {
|
||||||
start: { line: 1, ch: 7 },
|
start: { line: 3, ch: 2 },
|
||||||
end: { line: 1, ch: 7 },
|
end: { line: 3, ch: 2 },
|
||||||
|
isProperty: false,
|
||||||
|
isObjectKey: false,
|
||||||
|
completions: []
|
||||||
|
});
|
||||||
|
|
||||||
|
testCompletion("Window {\n\tproperty var prop\n\tfunction test() {\n\t\t|\n\t}\n}", {
|
||||||
|
start: { line: 3, ch: 2 },
|
||||||
|
end: { line: 3, ch: 2 },
|
||||||
|
isProperty: false,
|
||||||
|
isObjectKey: false,
|
||||||
|
completions: [
|
||||||
|
{ name: "prop", type: "?", origin: "test1.qml" },
|
||||||
|
{ name: "test", type: "fn()", origin: "test1.qml" }
|
||||||
|
]
|
||||||
|
}, function (server) { server.jsDefs = []; });
|
||||||
|
|
||||||
|
// QML ID Property Completions
|
||||||
|
testCompletion("Window {\n\tid: btn\n\t|\n}", {
|
||||||
|
start: { line: 2, ch: 1 },
|
||||||
|
end: { line: 2, ch: 1 },
|
||||||
|
isProperty: false,
|
||||||
|
isObjectKey: false,
|
||||||
|
completions: []
|
||||||
|
});
|
||||||
|
|
||||||
|
testCompletion("Window {\n\tButton {\n\t\tid: btn\n\t\tproperty int height\n\t}\n\ttest: btn.|\n}", {
|
||||||
|
start: { line: 5, ch: 11 },
|
||||||
|
end: { line: 5, ch: 11 },
|
||||||
|
isProperty: true,
|
||||||
|
isObjectKey: false,
|
||||||
|
completions: [{ name: "height", type: "number", origin: "test1.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}", {
|
||||||
|
start: { line: 6, ch: 11 },
|
||||||
|
end: { line: 6, ch: 11 },
|
||||||
|
isProperty: true,
|
||||||
|
isObjectKey: false,
|
||||||
|
completions: [{ name: "height", type: "number", origin: "test1.qml" }]
|
||||||
|
}, function (server) { server.jsDefs = []; });
|
||||||
|
|
||||||
|
testCompletion("Window {\n\tButton {\n\t\tproperty var id\n\t\tid: btn\n\t}\n\ts: bt|\n}", {
|
||||||
|
start: { line: 5, ch: 4 },
|
||||||
|
end: { line: 5, ch: 6 },
|
||||||
|
isProperty: false,
|
||||||
|
isObjectKey: false,
|
||||||
|
completions: [{ name: "btn", type: "Button", origin: "test1.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}", {
|
||||||
|
start: { line: 4, ch: 5 },
|
||||||
|
end: { line: 4, ch: 6 },
|
||||||
|
isProperty: false,
|
||||||
|
isObjectKey: false,
|
||||||
|
completions: [{ name: "id", type: "?", origin: "test1.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}", {
|
||||||
|
start: { line: 4, ch: 5 },
|
||||||
|
end: { line: 4, ch: 6 },
|
||||||
|
isProperty: false,
|
||||||
|
isObjectKey: false,
|
||||||
|
completions: [{ name: "id", type: "number", origin: "test1.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}", {
|
||||||
|
start: { line: 4, ch: 5 },
|
||||||
|
end: { line: 4, ch: 6 },
|
||||||
|
isProperty: false,
|
||||||
|
isObjectKey: false,
|
||||||
|
completions: [{ name: "id", type: "string", origin: "test1.qml" }]
|
||||||
|
}, function (server) { server.jsDefs = []; });
|
||||||
|
|
||||||
|
testCompletion("Window {\n\tButton {\n\t\tproperty var id\n\t\ts: i|\n\t}\n}", {
|
||||||
|
start: { line: 3, ch: 5 },
|
||||||
|
end: { line: 3, ch: 6 },
|
||||||
|
isProperty: false,
|
||||||
|
isObjectKey: false,
|
||||||
|
completions: [{ name: "id", type: "?", origin: "test1.qml" }]
|
||||||
|
}, function (server) { server.jsDefs = []; });
|
||||||
|
|
||||||
|
testCompletion("Window {\n\tButton {\n\t\tproperty var id: 34\n\t\ts: i|\n\t}\n}", {
|
||||||
|
start: { line: 3, ch: 5 },
|
||||||
|
end: { line: 3, ch: 6 },
|
||||||
|
isProperty: false,
|
||||||
|
isObjectKey: false,
|
||||||
|
completions: [{ name: "id", type: "number", origin: "test1.qml" }]
|
||||||
|
}, function (server) { server.jsDefs = []; });
|
||||||
|
|
||||||
|
testCompletion("Window {\n\tButton {\n\t\tproperty string id\n\t\ts: i|\n\t}\n}", {
|
||||||
|
start: { line: 3, ch: 5 },
|
||||||
|
end: { line: 3, ch: 6 },
|
||||||
|
isProperty: false,
|
||||||
|
isObjectKey: false,
|
||||||
|
completions: [{ name: "id", type: "string", origin: "test1.qml" }]
|
||||||
|
}, function (server) { server.jsDefs = []; });
|
||||||
|
|
||||||
|
testCompletion("Window {\n\tid: wind\n\tfunction test() {\n\t\t|\n\t}\n}", {
|
||||||
|
start: { line: 3, ch: 2 },
|
||||||
|
end: { line: 3, ch: 2 },
|
||||||
|
isProperty: false,
|
||||||
|
isObjectKey: false,
|
||||||
|
completions: [
|
||||||
|
{ name: "test", type: "fn()", origin: "test1.qml" },
|
||||||
|
{ name: "wind", type: "Window", origin: "test1.qml" }
|
||||||
|
]
|
||||||
|
}, function (server) { server.jsDefs = []; });
|
||||||
|
|
||||||
|
// JavaScript Completions
|
||||||
|
testCompletion("Window {\n\tproperty var test: |\n}", {
|
||||||
|
start: { line: 1, ch: 20 },
|
||||||
|
end: { line: 1, ch: 20 },
|
||||||
isProperty: false,
|
isProperty: false,
|
||||||
isObjectKey: false,
|
isObjectKey: false,
|
||||||
completions: [
|
completions: [
|
||||||
|
@ -88,6 +209,7 @@ testCompletion("Window {\n\ttest: |\n}", {
|
||||||
{ name: "isNaN", type: "fn(value: number) -> bool", origin: "ecma5" },
|
{ name: "isNaN", type: "fn(value: number) -> bool", origin: "ecma5" },
|
||||||
{ name: "parseFloat", type: "fn(string: string) -> number", origin: "ecma5" },
|
{ name: "parseFloat", type: "fn(string: string) -> number", origin: "ecma5" },
|
||||||
{ name: "parseInt", type: "fn(string: string, radix?: number) -> number", origin: "ecma5" },
|
{ name: "parseInt", type: "fn(string: string, radix?: number) -> number", origin: "ecma5" },
|
||||||
|
{ name: "test", type: "?", origin: "test1.qml" },
|
||||||
{ name: "undefined", type: "?", origin: "ecma5" },
|
{ name: "undefined", type: "?", origin: "ecma5" },
|
||||||
{ name: "Array", type: "fn(size: number)", origin: "ecma5" },
|
{ name: "Array", type: "fn(size: number)", origin: "ecma5" },
|
||||||
{ name: "Boolean", type: "fn(value: ?) -> bool", origin: "ecma5" },
|
{ name: "Boolean", type: "fn(value: ?) -> bool", origin: "ecma5" },
|
||||||
|
@ -111,9 +233,9 @@ testCompletion("Window {\n\ttest: |\n}", {
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
testCompletion("Window {\n\ttest:| \n}", {
|
testCompletion("Window {\n\ttest: |\n}", {
|
||||||
start: { line: 1, ch: 6 },
|
start: { line: 1, ch: 7 },
|
||||||
end: { line: 1, ch: 6 },
|
end: { line: 1, ch: 7 },
|
||||||
isProperty: false,
|
isProperty: false,
|
||||||
isObjectKey: false,
|
isObjectKey: false,
|
||||||
completions: [
|
completions: [
|
||||||
|
@ -187,16 +309,202 @@ testCompletion("Window {\n\ttest: {\n\t\t|\n\t}\n}", {
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
// Signal Completions
|
testCompletion("Window {\n\tfunction test() {\n\t\t|\n\t}\n}", {
|
||||||
|
start: { line: 2, ch: 2 },
|
||||||
|
end: { line: 2, ch: 2 },
|
||||||
|
isProperty: false,
|
||||||
|
isObjectKey: false,
|
||||||
|
completions: [
|
||||||
|
{ name: "decodeURI", type: "fn(uri: string) -> string", origin: "ecma5" },
|
||||||
|
{ name: "decodeURIComponent", type: "fn(uri: string) -> string", origin: "ecma5" },
|
||||||
|
{ name: "encodeURI", type: "fn(uri: string) -> string", origin: "ecma5" },
|
||||||
|
{ name: "encodeURIComponent", type: "fn(uri: string) -> string", origin: "ecma5" },
|
||||||
|
{ name: "eval", type: "fn(code: string)", origin: "ecma5" },
|
||||||
|
{ name: "isFinite", type: "fn(value: number) -> bool", origin: "ecma5" },
|
||||||
|
{ 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: "undefined", type: "?", origin: "ecma5" },
|
||||||
|
{ name: "Array", type: "fn(size: number)", origin: "ecma5" },
|
||||||
|
{ name: "Boolean", type: "fn(value: ?) -> bool", origin: "ecma5" },
|
||||||
|
{ name: "Date", type: "fn(ms: number)", origin: "ecma5" },
|
||||||
|
{ name: "Error", type: "fn(message: string)", origin: "ecma5" },
|
||||||
|
{ name: "EvalError", type: "fn(message: string)", origin: "ecma5" },
|
||||||
|
{ name: "Function", type: "fn(body: string) -> fn()", origin: "ecma5" },
|
||||||
|
{ name: "Infinity", type: "number", origin: "ecma5" },
|
||||||
|
{ name: "JSON", type: "JSON", origin: "ecma5" },
|
||||||
|
{ name: "Math", type: "Math", origin: "ecma5" },
|
||||||
|
{ name: "NaN", type: "number", origin: "ecma5" },
|
||||||
|
{ name: "Number", type: "fn(value: ?) -> number", origin: "ecma5" },
|
||||||
|
{ name: "Object", type: "fn()", origin: "ecma5" },
|
||||||
|
{ name: "RangeError", type: "fn(message: string)", origin: "ecma5" },
|
||||||
|
{ name: "ReferenceError", type: "fn(message: string)", origin: "ecma5" },
|
||||||
|
{ name: "RegExp", type: "fn(source: string, flags?: string)", origin: "ecma5" },
|
||||||
|
{ name: "String", type: "fn(value: ?) -> string", origin: "ecma5" },
|
||||||
|
{ name: "SyntaxError", type: "fn(message: string)", origin: "ecma5" },
|
||||||
|
{ name: "TypeError", type: "fn(message: string)", origin: "ecma5" },
|
||||||
|
{ name: "URIError", type: "fn(message: string)", origin: "ecma5" }
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
// Signal and Signal Handler Completions
|
||||||
testCompletion("Window {\n\tsignal clicked(int mouseX, int mouseY)\n\t|\n}", {
|
testCompletion("Window {\n\tsignal clicked(int mouseX, int mouseY)\n\t|\n}", {
|
||||||
start: { line: 2, ch: 1 },
|
start: { line: 2, ch: 1 },
|
||||||
end: { line: 2, ch: 1 },
|
end: { line: 2, ch: 1 },
|
||||||
isProperty: false,
|
isProperty: false,
|
||||||
isObjectKey: false,
|
isObjectKey: false,
|
||||||
completions: [
|
completions: [
|
||||||
/* TODO: Should not include this line */ { name: "clicked", type: "fn(mouseX: ?, mouseY: ?) -> Signal", origin: "test1.qml" },
|
|
||||||
{ name: "onClicked", type: "Signal Handler", origin: "test1.qml" }
|
{ name: "onClicked", type: "Signal Handler", origin: "test1.qml" }
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testCompletion("Window {\n\tsignal clicked(int mouseX, int mouseY)\n\tButton {\n\t\t|\n\t}\n}", {
|
||||||
|
start: { line: 3, ch: 2 },
|
||||||
|
end: { line: 3, ch: 2 },
|
||||||
|
isProperty: false,
|
||||||
|
isObjectKey: false,
|
||||||
|
completions: []
|
||||||
|
});
|
||||||
|
|
||||||
|
testCompletion("Window {\n\tsignal clicked(int mouseX, int mouseY)\n\ts: |\n}", {
|
||||||
|
start: { line: 2, ch: 4 },
|
||||||
|
end: { line: 2, ch: 4 },
|
||||||
|
isProperty: false,
|
||||||
|
isObjectKey: false,
|
||||||
|
completions: [
|
||||||
|
{ name: "clicked", type: "fn(mouseX: number, mouseY: number)", origin: "test1.qml" },
|
||||||
|
{ name: "onClicked", type: "Signal Handler", origin: "test1.qml" }
|
||||||
|
]
|
||||||
|
}, function (server) { server.jsDefs = []; });
|
||||||
|
|
||||||
|
testCompletion("Window {\n\tsignal error(string msg, boolean flag)\n\ts: |\n}", {
|
||||||
|
start: { line: 2, ch: 4 },
|
||||||
|
end: { line: 2, ch: 4 },
|
||||||
|
isProperty: false,
|
||||||
|
isObjectKey: false,
|
||||||
|
completions: [
|
||||||
|
{ name: "error", type: "fn(msg: string, flag: bool)", origin: "test1.qml" },
|
||||||
|
{ name: "onError", type: "Signal Handler", origin: "test1.qml" }
|
||||||
|
]
|
||||||
|
}, function (server) { server.jsDefs = []; });
|
||||||
|
|
||||||
|
testCompletion("Window {\n\tid: wind\n\tsignal error(string msg, boolean flag)\n\ts: wind.|\n}", {
|
||||||
|
start: { line: 3, ch: 9 },
|
||||||
|
end: { line: 3, ch: 9 },
|
||||||
|
isProperty: true,
|
||||||
|
isObjectKey: false,
|
||||||
|
completions: [
|
||||||
|
{ name: "error", type: "fn(msg: string, flag: bool)", origin: "test1.qml" },
|
||||||
|
{ name: "onError", type: "Signal Handler", origin: "test1.qml" }
|
||||||
|
]
|
||||||
|
}, function (server) { server.jsDefs = []; });
|
||||||
|
|
||||||
|
testCompletion("Window {\n\tsignal error(string msg, boolean flag)\n\tonError: |\n}", {
|
||||||
|
start: { line: 2, ch: 10 },
|
||||||
|
end: { line: 2, ch: 10 },
|
||||||
|
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" }
|
||||||
|
]
|
||||||
|
}, function (server) { server.jsDefs = []; });
|
||||||
|
|
||||||
|
testCompletion("Window {\n\tsignal error(string msg, boolean flag)\n\tonError: {\n\t\t|\n\t}\n}", {
|
||||||
|
start: { line: 3, ch: 2 },
|
||||||
|
end: { line: 3, ch: 2 },
|
||||||
|
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" }
|
||||||
|
]
|
||||||
|
}, function (server) { server.jsDefs = []; });
|
||||||
|
|
||||||
|
testCompletion("Window {\n\tproperty int msg\n\tsignal error(string msg, boolean flag)\n\tonError: {\n\t\t|\n\t}\n}", {
|
||||||
|
start: { line: 4, ch: 2 },
|
||||||
|
end: { line: 4, ch: 2 },
|
||||||
|
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" }
|
||||||
|
]
|
||||||
|
}, function (server) { server.jsDefs = []; });
|
||||||
|
|
||||||
|
// Function Declarations
|
||||||
|
testCompletion("Window {\n\tfunction test() {}\n\t|\n}", {
|
||||||
|
start: { line: 2, ch: 1 },
|
||||||
|
end: { line: 2, ch: 1 },
|
||||||
|
isProperty: false,
|
||||||
|
isObjectKey: false,
|
||||||
|
completions: []
|
||||||
|
});
|
||||||
|
|
||||||
|
testCompletion("Window {\n\tfunction test(a, b, c) {\n\t\t|\n\t}\n}", {
|
||||||
|
start: { line: 2, ch: 2 },
|
||||||
|
end: { line: 2, ch: 2 },
|
||||||
|
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" }
|
||||||
|
]
|
||||||
|
}, function (server) { server.jsDefs = []; });
|
||||||
|
|
||||||
|
testCompletion("Window {\n\tfunction test(a) {\n\t\ta = 3\n\t\t|\n\t}\n}", {
|
||||||
|
start: { line: 3, ch: 2 },
|
||||||
|
end: { line: 3, ch: 2 },
|
||||||
|
isProperty: false,
|
||||||
|
isObjectKey: false,
|
||||||
|
completions: [
|
||||||
|
{ name: "a", type: "number", origin: "test1.qml" },
|
||||||
|
{ name: "test", type: "fn(a: number)", origin: "test1.qml" }
|
||||||
|
]
|
||||||
|
}, function (server) { server.jsDefs = []; });
|
||||||
|
|
||||||
|
testCompletion('Window {\n\tfunction test(a) {\n\t\ttest("something")\n\t\t|\n\t}\n}', {
|
||||||
|
start: { line: 3, ch: 2 },
|
||||||
|
end: { line: 3, ch: 2 },
|
||||||
|
isProperty: false,
|
||||||
|
isObjectKey: false,
|
||||||
|
completions: [
|
||||||
|
{ name: "a", type: "string", origin: "test1.qml" },
|
||||||
|
{ name: "test", type: "fn(a: string)", origin: "test1.qml" }
|
||||||
|
]
|
||||||
|
}, function (server) { server.jsDefs = []; });
|
||||||
|
|
||||||
|
testCompletion('Window {\n\tfunction test(a) {\n\t\t|\n\t\treturn 7\n\t}\n}', {
|
||||||
|
start: { line: 2, ch: 2 },
|
||||||
|
end: { line: 2, ch: 2 },
|
||||||
|
isProperty: false,
|
||||||
|
isObjectKey: false,
|
||||||
|
completions: [
|
||||||
|
{ name: "a", type: "?", origin: "test1.qml" },
|
||||||
|
{ name: "test", type: "fn(a: ?) -> number", origin: "test1.qml" }
|
||||||
|
]
|
||||||
|
}, function (server) { server.jsDefs = []; });
|
||||||
|
|
||||||
|
// TODO: Uncomment once this is fixed. The find defs version of this test does find the right definition
|
||||||
|
// so it seems the problem is related to Tern and not the QML plugin.
|
||||||
|
//testCompletion('Window {\n\tproperty int a\n\tfunction test(a) {\n\t\t|\n\t}\n}', {
|
||||||
|
// start: { line: 3, ch: 2 },
|
||||||
|
// end: { line: 3, ch: 2 },
|
||||||
|
// isProperty: false,
|
||||||
|
// isObjectKey: false,
|
||||||
|
// completions: [
|
||||||
|
// { name: "a", type: "?", origin: "test1.qml" },
|
||||||
|
// { name: "test", type: "fn()", origin: "test1.qml" }
|
||||||
|
// ]
|
||||||
|
//}, function (server) { server.jsDefs = []; });
|
||||||
|
|
||||||
groupEnd();
|
groupEnd();
|
|
@ -1,10 +1,24 @@
|
||||||
var testDefinition = require("./driver.js").testDefinition;
|
/*******************************************************************************
|
||||||
var groupStart = require("./driver.js").groupStart;
|
* Copyright (c) 2015 QNX Software Systems and others.
|
||||||
var groupEnd = require("./driver.js").groupEnd;
|
* All rights reserved. This program and the accompanying materials
|
||||||
|
* are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
* which accompanies this distribution, and is available at
|
||||||
|
* http://www.eclipse.org/legal/epl-v10.html
|
||||||
|
*
|
||||||
|
* Contributors:
|
||||||
|
* QNX Software Systems - Initial API and implementation
|
||||||
|
*******************************************************************************/
|
||||||
|
"use-strict";
|
||||||
|
|
||||||
|
var driver = require("./driver.js");
|
||||||
|
var testDefinition = driver.testDefinition;
|
||||||
|
var groupStart = driver.groupStart;
|
||||||
|
var groupEnd = driver.groupEnd;
|
||||||
|
|
||||||
var group = exports.group = "Find Definition";
|
var group = exports.group = "Find Definition";
|
||||||
groupStart(group);
|
groupStart(group);
|
||||||
|
|
||||||
|
// ID Property
|
||||||
testDefinition("QtObject {\n\tid: obj\n\tproperty var prop: {\n\t\tob|j\n\t}\n}", {
|
testDefinition("QtObject {\n\tid: obj\n\tproperty var prop: {\n\t\tob|j\n\t}\n}", {
|
||||||
origin: "test1.qml",
|
origin: "test1.qml",
|
||||||
start: { line: 1, ch: 5 },
|
start: { line: 1, ch: 5 },
|
||||||
|
@ -13,6 +27,31 @@ testDefinition("QtObject {\n\tid: obj\n\tproperty var prop: {\n\t\tob|j\n\t}\n}"
|
||||||
contextOffset: 16
|
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",
|
||||||
|
start: { line: 2, ch: 6 },
|
||||||
|
end: { line: 2, ch: 9 },
|
||||||
|
file: "test1.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",
|
||||||
|
start: { line: 3, ch: 6 },
|
||||||
|
end: { line: 3, ch: 9 },
|
||||||
|
file: "test1.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",
|
||||||
|
start: { line: 3, ch: 6 },
|
||||||
|
end: { line: 3, ch: 9 },
|
||||||
|
file: "test1.qml",
|
||||||
|
contextOffset: 43
|
||||||
|
});
|
||||||
|
|
||||||
|
// Property Declarations
|
||||||
testDefinition("QtObject {\n\tproperty var prop\n\tpr|op: 3\n}", {
|
testDefinition("QtObject {\n\tproperty var prop\n\tpr|op: 3\n}", {
|
||||||
origin: "test1.qml",
|
origin: "test1.qml",
|
||||||
start: { line: 1, ch: 14 },
|
start: { line: 1, ch: 14 },
|
||||||
|
@ -21,4 +60,166 @@ testDefinition("QtObject {\n\tproperty var prop\n\tpr|op: 3\n}", {
|
||||||
contextOffset: 25
|
contextOffset: 25
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testDefinition("Window {\n\tproperty var btn\n\tButton {\n\t\tprop: b|tn\n\t}\n}", {
|
||||||
|
origin: "test1.qml",
|
||||||
|
start: { line: 1, ch: 14 },
|
||||||
|
end: { line: 1, ch: 17 },
|
||||||
|
file: "test1.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",
|
||||||
|
start: { line: 1, ch: 14 },
|
||||||
|
end: { line: 1, ch: 17 },
|
||||||
|
file: "test1.qml",
|
||||||
|
contextOffset: 23
|
||||||
|
});
|
||||||
|
|
||||||
|
testDefinition("Window {\n\tButton {\n\t\tproperty var btn\n\t\tbt|n: 3\n\t}\n}", {
|
||||||
|
origin: "test1.qml",
|
||||||
|
start: { line: 2, ch: 15 },
|
||||||
|
end: { line: 2, ch: 18 },
|
||||||
|
file: "test1.qml",
|
||||||
|
contextOffset: 34
|
||||||
|
});
|
||||||
|
|
||||||
|
testDefinition("Window {\n\tButton {\n\t\tproperty var btn\n\t\tButton {\n\t\t\tprop: b|tn\n\t\t}\n\t}\n}", {
|
||||||
|
origin: undefined,
|
||||||
|
start: undefined,
|
||||||
|
end: undefined,
|
||||||
|
file: undefined,
|
||||||
|
contextOffset: undefined
|
||||||
|
});
|
||||||
|
|
||||||
|
// Signals and Signal Handlers
|
||||||
|
testDefinition("Window {\n\tsignal clic|ked(int mouseX, int mouseY)\n}", {
|
||||||
|
origin: "test1.qml",
|
||||||
|
start: { line: 1, ch: 8 },
|
||||||
|
end: { line: 1, ch: 15 },
|
||||||
|
file: "test1.qml",
|
||||||
|
contextOffset: 17
|
||||||
|
});
|
||||||
|
|
||||||
|
testDefinition("Window {\n\tsignal clicked(int mouseX, int mouseY)\n\tonCli|cked: 3\n}", {
|
||||||
|
origin: "test1.qml",
|
||||||
|
start: { line: 1, ch: 8 },
|
||||||
|
end: { line: 1, ch: 15 },
|
||||||
|
file: "test1.qml",
|
||||||
|
contextOffset: 17
|
||||||
|
});
|
||||||
|
|
||||||
|
testDefinition("Window {\n\tsignal clicked(int mouseX, int mouseY)\n\tonClicked: mou|seX\n}", {
|
||||||
|
origin: "test1.qml",
|
||||||
|
start: { line: 1, ch: 20 },
|
||||||
|
end: { line: 1, ch: 26 },
|
||||||
|
file: "test1.qml",
|
||||||
|
contextOffset: 29
|
||||||
|
});
|
||||||
|
|
||||||
|
testDefinition("Window {\n\tsignal clicked(int mouseX, int mouseY)\n\tonClicked: cli|cked(3,4)\n}", {
|
||||||
|
origin: "test1.qml",
|
||||||
|
start: { line: 1, ch: 8 },
|
||||||
|
end: { line: 1, ch: 15 },
|
||||||
|
file: "test1.qml",
|
||||||
|
contextOffset: 17
|
||||||
|
});
|
||||||
|
|
||||||
|
testDefinition("Window {\n\tsignal clicked(int mouseX, int mouseY)\n\tonClicked: onCli|cked\n}", {
|
||||||
|
origin: "test1.qml",
|
||||||
|
start: { line: 1, ch: 8 },
|
||||||
|
end: { line: 1, ch: 15 },
|
||||||
|
file: "test1.qml",
|
||||||
|
contextOffset: 17
|
||||||
|
});
|
||||||
|
|
||||||
|
testDefinition("Window {\n\tsignal clicked(int mouseX, int mouseY)\n\tid: wind\n\tonClicked: wind.onCli|cked\n}", {
|
||||||
|
origin: "test1.qml",
|
||||||
|
start: { line: 1, ch: 8 },
|
||||||
|
end: { line: 1, ch: 15 },
|
||||||
|
file: "test1.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",
|
||||||
|
start: { line: 1, ch: 8 },
|
||||||
|
end: { line: 1, ch: 15 },
|
||||||
|
file: "test1.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",
|
||||||
|
start: { line: 2, ch: 21 },
|
||||||
|
end: { line: 2, ch: 24 },
|
||||||
|
file: "test1.qml",
|
||||||
|
contextOffset: 48
|
||||||
|
}, function (server) { server.jsDefs = []; });
|
||||||
|
|
||||||
|
// Function Declarations
|
||||||
|
testDefinition("Window {\n\tfunction te|st(a) {}\n}", {
|
||||||
|
origin: "test1.qml",
|
||||||
|
start: { line: 1, ch: 10 },
|
||||||
|
end: { line: 1, ch: 14 },
|
||||||
|
file: "test1.qml",
|
||||||
|
contextOffset: 19
|
||||||
|
});
|
||||||
|
|
||||||
|
testDefinition("Window {\n\tfunction test(a) {\n\t\ta|\n\t}\n}", {
|
||||||
|
origin: "test1.qml",
|
||||||
|
start: { line: 1, ch: 15 },
|
||||||
|
end: { line: 1, ch: 16 },
|
||||||
|
file: "test1.qml",
|
||||||
|
contextOffset: 24
|
||||||
|
});
|
||||||
|
|
||||||
|
testDefinition("Window {\n\tfunction test(a) {\n\t\tte|st(3)\n\t}\n}", {
|
||||||
|
origin: "test1.qml",
|
||||||
|
start: { line: 1, ch: 10 },
|
||||||
|
end: { line: 1, ch: 14 },
|
||||||
|
file: "test1.qml",
|
||||||
|
contextOffset: 19
|
||||||
|
});
|
||||||
|
|
||||||
|
testDefinition("Window {\n\tfunction test(a) {}\n\ts: te|st(3)\n}", {
|
||||||
|
origin: "test1.qml",
|
||||||
|
start: { line: 1, ch: 10 },
|
||||||
|
end: { line: 1, ch: 14 },
|
||||||
|
file: "test1.qml",
|
||||||
|
contextOffset: 19
|
||||||
|
});
|
||||||
|
|
||||||
|
testDefinition("Window {\n\tfunction test(a) {}\n\ts: {\n\t\tte|st(3)\n\t}\n}", {
|
||||||
|
origin: "test1.qml",
|
||||||
|
start: { line: 1, ch: 10 },
|
||||||
|
end: { line: 1, ch: 14 },
|
||||||
|
file: "test1.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",
|
||||||
|
start: { line: 1, ch: 10 },
|
||||||
|
end: { line: 1, ch: 14 },
|
||||||
|
file: "test1.qml",
|
||||||
|
contextOffset: 19
|
||||||
|
});
|
||||||
|
|
||||||
|
testDefinition("Window {\n\tfunction test(a) {}\n\tte|st: 3;\n}", {
|
||||||
|
origin: undefined,
|
||||||
|
start: undefined,
|
||||||
|
end: undefined,
|
||||||
|
file: undefined,
|
||||||
|
contextOffset: undefined
|
||||||
|
});
|
||||||
|
|
||||||
|
testDefinition('Window {\n\tfunction test(a) {\n\t\ta|\n\t}\n}\n\tproperty int a', {
|
||||||
|
origin: "test1.qml",
|
||||||
|
start: { line: 1, ch: 15 },
|
||||||
|
end: { line: 1, ch: 16 },
|
||||||
|
file: "test1.qml",
|
||||||
|
contextOffset: 24
|
||||||
|
});
|
||||||
|
|
||||||
groupEnd();
|
groupEnd();
|
Loading…
Add table
Reference in a new issue