mirror of
https://github.com/eclipse-cdt/cdt
synced 2025-08-07 08:15:48 +02:00
Bug 481126 - QML Static Analysis based on Tern.js
Moved the Tern.js work and acorn-qml into the Qt Core plugin rather than in a separate plugin. Added walk.js in order to facilitate walking the QML AST by acorn-qml. Changed a few things in index.js and inject.js for acorn-qml in order to get it working in a browser environment. Added a tern-qml webpage demo which doesn't do much for now. Change-Id: I3c8a3d57c98a4936d0e038774b410bb2a68afb6c Signed-off-by: Matthew Bastien <mbastien@blackberry.com>
This commit is contained in:
parent
ae53f82634
commit
f9143ebfdc
23 changed files with 808 additions and 674 deletions
|
@ -1,7 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
|
||||
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
|
||||
<classpathentry kind="src" path="src"/>
|
||||
<classpathentry kind="output" path="bin"/>
|
||||
</classpath>
|
|
@ -1,28 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>org.eclipse.cdt.qt.core.acorn</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.pde.ManifestBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.pde.SchemaBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.pde.PluginNature</nature>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
|
@ -1,7 +0,0 @@
|
|||
eclipse.preferences.version=1
|
||||
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
|
||||
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
|
||||
org.eclipse.jdt.core.compiler.compliance=1.8
|
||||
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
|
||||
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
|
||||
org.eclipse.jdt.core.compiler.source=1.8
|
|
@ -1,10 +0,0 @@
|
|||
Manifest-Version: 1.0
|
||||
Bundle-ManifestVersion: 2
|
||||
Bundle-Name: %pluginName
|
||||
Bundle-SymbolicName: org.eclipse.cdt.qt.core.acorn
|
||||
Bundle-Version: 2.0.0.qualifier
|
||||
Bundle-Activator: org.eclipse.cdt.qt.core.acorn.Activator
|
||||
Bundle-Vendor: %providerName
|
||||
Require-Bundle: org.eclipse.core.runtime
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
|
||||
Bundle-ActivationPolicy: lazy
|
|
@ -1,518 +0,0 @@
|
|||
/*******************************************************************************
|
||||
* 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';
|
||||
|
||||
module.exports = function(acorn) {
|
||||
// Acorn token types
|
||||
var tt = acorn.tokTypes;
|
||||
|
||||
// QML token types
|
||||
var qtt = {};
|
||||
var keywords = {};
|
||||
|
||||
/*
|
||||
* Shorthand for defining keywords in the 'keywords' variable with the following
|
||||
* format:
|
||||
* keywords[name].isPrimitive : if this is a primitive type
|
||||
* keywords[name].isQMLContextual : if this is a contextual keyword for QML
|
||||
*
|
||||
* Also stores the token's name in qtt._<keyword> for easy referencing later. None
|
||||
* of these keywords will be tokenized and, as such, are allowed to be used in
|
||||
* JavaScript expressions by acorn. The 'isQMLContextual' boolean in keywords refers
|
||||
* to those contextual keywords that are also contextual in QML's parser rules such
|
||||
* as 'color', 'list', 'alias', etc.
|
||||
*/
|
||||
function kw(name, options) {
|
||||
if (options === undefined)
|
||||
options = {};
|
||||
qtt["_" + name] = name;
|
||||
keywords[name] = {};
|
||||
keywords[name].isPrimitive = options.isPrimitive ? true : false;
|
||||
keywords[name].isQMLContextual = options.isQMLContextual ? true : false;
|
||||
}
|
||||
|
||||
// QML keywords
|
||||
kw("import");
|
||||
kw("pragma");
|
||||
kw("property", { isQMLContextual : true });
|
||||
kw("readonly", { isQMLContextual : true });
|
||||
kw("signal", { isQMLContextual : true });
|
||||
kw("as");
|
||||
kw("boolean", { isPrimitive: true });
|
||||
kw("double", { isPrimitive: true });
|
||||
kw("int", { isPrimitive: true });
|
||||
kw("alias", { isQMLContextual: true });
|
||||
kw("list", { isPrimitive: true, isQMLContextual: true });
|
||||
kw("color", { isPrimitive: true, isQMLContextual: true });
|
||||
kw("real", { isPrimitive: true, isQMLContextual: true });
|
||||
kw("string", { isPrimitive: true, isQMLContextual: true });
|
||||
kw("url", { isPrimitive: true, isQMLContextual: true });
|
||||
|
||||
// Future reserved words
|
||||
kw("transient");
|
||||
kw("synchronized");
|
||||
kw("abstract");
|
||||
kw("volatile");
|
||||
kw("native");
|
||||
kw("goto");
|
||||
kw("byte");
|
||||
kw("long");
|
||||
kw("char");
|
||||
kw("short");
|
||||
kw("float");
|
||||
|
||||
// QML parser methods
|
||||
var pp = acorn.Parser.prototype;
|
||||
|
||||
/*
|
||||
* Parses a set of QML Header Statements which can either be of
|
||||
* the type import or pragma
|
||||
*/
|
||||
pp.qml_parseHeaderStatements = function() {
|
||||
var node = this.startNode()
|
||||
node.statements = [];
|
||||
|
||||
var loop = true;
|
||||
while (loop) {
|
||||
if (this.type === tt._import || this.isContextual(qtt._import)) {
|
||||
var qmlImport = this.qml_parseImportStatement();
|
||||
node.statements.push(qmlImport);
|
||||
} else if (this.isContextual(qtt._pragma)) {
|
||||
var qmlPragma = this.qml_parsePragmaStatement();
|
||||
node.statements.push(qmlPragma);
|
||||
} else {
|
||||
loop = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (node.statements.length > 0) {
|
||||
return this.finishNode(node, "QMLHeaderStatements");
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parses a QML Pragma statement of the form:
|
||||
* 'pragma' <Identifier>
|
||||
*/
|
||||
pp.qml_parsePragmaStatement = function() {
|
||||
var node = this.startNode();
|
||||
this.next();
|
||||
node.identifier = this.parseIdent(false);
|
||||
this.semicolon();
|
||||
return this.finishNode(node, "QMLPragmaStatement");
|
||||
}
|
||||
|
||||
/*
|
||||
* Parses a QML Import statement of the form:
|
||||
* 'import' <ModuleIdentifier> <Version.Number> [as <Qualifier>]
|
||||
* 'import' <DirectoryPath> [as <Qualifier>]
|
||||
*
|
||||
* as specified by http://doc.qt.io/qt-5/qtqml-syntax-imports.html
|
||||
*/
|
||||
pp.qml_parseImportStatement = function() {
|
||||
var node = this.startNode();
|
||||
this.next();
|
||||
|
||||
// The type of import varies solely on the next token
|
||||
switch(this.type) {
|
||||
case tt.name:
|
||||
node.module = this.qml_parseModule();
|
||||
break;
|
||||
case tt.string:
|
||||
node.directoryPath = this.parseLiteral(this.value);
|
||||
break;
|
||||
default:
|
||||
this.unexpected();
|
||||
break;
|
||||
}
|
||||
|
||||
// Parse the qualifier, if any
|
||||
if (this.isContextual(qtt._as)) {
|
||||
node.qualifier = this.qml_parseQualifier();
|
||||
}
|
||||
|
||||
this.semicolon();
|
||||
return this.finishNode(node, "QMLImportStatement");
|
||||
};
|
||||
|
||||
/*
|
||||
* Parses a QML Module of the form:
|
||||
* <QualifiedId> <VersionLiteral>
|
||||
*/
|
||||
pp.qml_parseModule = function() {
|
||||
var node = this.startNode();
|
||||
|
||||
node.qualifiedId = this.qml_parseQualifiedId();
|
||||
if (this.type === tt.num) {
|
||||
node.version = this.qml_parseVersionLiteral();
|
||||
} else {
|
||||
this.unexpected();
|
||||
}
|
||||
|
||||
return this.finishNode(node, "QMLModule");
|
||||
};
|
||||
|
||||
/*
|
||||
* Parses a QML Version Literal which consists of a major and minor
|
||||
* version separated by a '.'
|
||||
*/
|
||||
pp.qml_parseVersionLiteral = function() {
|
||||
var node = this.startNode();
|
||||
|
||||
node.raw = this.input.slice(this.start, this.end);
|
||||
node.value = this.value;
|
||||
var matches;
|
||||
if (matches = /(\d+)\.(\d+)/.exec(node.raw)) {
|
||||
node.major = parseInt(matches[1]);
|
||||
node.minor = parseInt(matches[2]);
|
||||
} else {
|
||||
this.raise(this.start, "QML module must specify major and minor version");
|
||||
}
|
||||
|
||||
this.next();
|
||||
return this.finishNode(node, "QMLVersionLiteral");
|
||||
}
|
||||
|
||||
/*
|
||||
* Parses a QML Qualifier of the form:
|
||||
* 'as' <Identifier>
|
||||
*/
|
||||
pp.qml_parseQualifier = function() {
|
||||
var node = this.startNode();
|
||||
this.next();
|
||||
node.identifier = this.qml_parseIdent(false);
|
||||
return this.finishNode(node, "QMLQualifier");
|
||||
}
|
||||
|
||||
/*
|
||||
* Parses a QML Object Literal of the form:
|
||||
* <QualifiedId> { (<QMLMember>)* }
|
||||
*
|
||||
* http://doc.qt.io/qt-5/qtqml-syntax-basics.html#object-declarations
|
||||
*/
|
||||
pp.qml_parseObjectLiteral = function(node) {
|
||||
if (!node) {
|
||||
node = this.startNode();
|
||||
}
|
||||
if (!node.qualifiedId) {
|
||||
node.qualifiedId = this.qml_parseQualifiedId();
|
||||
}
|
||||
node.block = this.qml_parseMemberBlock();
|
||||
return this.finishNode(node, "QMLObjectLiteral");
|
||||
}
|
||||
|
||||
/*
|
||||
* Parses a QML Member Block of the form:
|
||||
* { <QMLMember>* }
|
||||
*/
|
||||
pp.qml_parseMemberBlock = function() {
|
||||
var node = this.startNode();
|
||||
this.expect(tt.braceL);
|
||||
node.members = [];
|
||||
while (!this.eat(tt.braceR)) {
|
||||
node.members.push(this.qml_parseMember());
|
||||
}
|
||||
return this.finishNode(node, "QMLMemberBlock");
|
||||
}
|
||||
|
||||
/*
|
||||
* Parses a QML Member which can be one of the following:
|
||||
* - a QML Property Binding
|
||||
* - a Property Declaration (or Alias)
|
||||
* - a QML Object Literal
|
||||
* - a JavaScript Function Declaration
|
||||
* - a Signal Definition
|
||||
*/
|
||||
pp.qml_parseMember = function() {
|
||||
var node = this.startNode();
|
||||
|
||||
if (this.type === tt._default
|
||||
|| this.isContextual(qtt._default)
|
||||
|| this.isContextual(qtt._readonly)
|
||||
|| this.isContextual(qtt._property)) {
|
||||
return this.qml_parsePropertyDeclaration(node);
|
||||
} else if (this.isContextual(qtt._signal)) {
|
||||
return this.qml_parseSignalDefinition(node);
|
||||
} else if (this.type === tt._function) {
|
||||
return this.parseFunctionStatement(node);
|
||||
}
|
||||
|
||||
node.qualifiedId = this.qml_parseQualifiedId();
|
||||
switch(this.type) {
|
||||
case tt.braceL:
|
||||
return this.qml_parseObjectLiteral(node);
|
||||
case tt.colon:
|
||||
return this.qml_parseProperty(node);
|
||||
}
|
||||
|
||||
this.unexpected();
|
||||
}
|
||||
|
||||
/*
|
||||
* Parses a QML Property of the form:
|
||||
* <QMLQualifiedID> <QMLBinding>
|
||||
*/
|
||||
pp.qml_parseProperty = function(node) {
|
||||
if (!node) {
|
||||
node = this.startNode();
|
||||
}
|
||||
if (!node.qualifiedId) {
|
||||
node.qualifiedId = this.qml_parseQualifiedId();
|
||||
}
|
||||
node.binding = this.qml_parseBinding();
|
||||
return this.finishNode(node, "QMLProperty");
|
||||
}
|
||||
|
||||
/*
|
||||
* Parses a QML Signal Definition of the form:
|
||||
* 'signal' <Identifier> [(<Type> <Identifier> [',' <Type> <Identifier>]* )]?
|
||||
*/
|
||||
pp.qml_parseSignalDefinition = function(node) {
|
||||
if (!node) {
|
||||
node = this.startNode();
|
||||
}
|
||||
this.next();
|
||||
node.identifier = this.qml_parseIdent(false);
|
||||
node.parameters = [];
|
||||
if (this.type === tt.parenL) {
|
||||
this.next();
|
||||
if (this.type !== tt.parenR) {
|
||||
do {
|
||||
var paramNode = this.startNode();
|
||||
paramNode.type = this.qml_parseIdent(false);
|
||||
paramNode.identifier = this.qml_parseIdent(false);
|
||||
node.parameters.push(paramNode);
|
||||
} while(this.eat(tt.comma));
|
||||
}
|
||||
if (!this.eat(tt.parenR)) {
|
||||
this.unexpected();
|
||||
}
|
||||
}
|
||||
this.semicolon();
|
||||
return this.finishNode(node, "QMLSignalDefinition");
|
||||
}
|
||||
|
||||
/*
|
||||
* Parses a QML Property Declaration (or Alias) of the form:
|
||||
* ['default'|'readonly'] 'property' <QMLType> <Identifier> [<QMLBinding>]
|
||||
*/
|
||||
pp.qml_parsePropertyDeclaration = function(node) {
|
||||
node["default"] = false;
|
||||
node["readonly"] = false;
|
||||
|
||||
if (this.type === tt._default || this.isContextual(qtt._default)) {
|
||||
node["default"] = true;
|
||||
this.next();
|
||||
} else if (this.eatContextual(qtt._readonly)) {
|
||||
node["readonly"] = true;
|
||||
}
|
||||
this.expectContextual(qtt._property);
|
||||
node.typeInfo = this.qml_parseType();
|
||||
node.identifier = this.qml_parseIdent(false);
|
||||
if (this.type !== tt.colon) {
|
||||
this.semicolon();
|
||||
} else {
|
||||
node.binding = this.qml_parseBinding();
|
||||
}
|
||||
|
||||
if (node.typeInfo.type === qtt._alias) {
|
||||
node.typeInfo = undefined;
|
||||
return this.finishNode(node, "QMLPropertyAlias");
|
||||
}
|
||||
return this.finishNode(node, "QMLPropertyDeclaration");
|
||||
}
|
||||
|
||||
/*
|
||||
* Parses a QML Binding of the form:
|
||||
* ':' (<Expression>|<QMLStatementBlock>)
|
||||
*/
|
||||
pp.qml_parseBinding = function() {
|
||||
var node = this.startNode();
|
||||
this.expect(tt.colon);
|
||||
|
||||
// TODO: solve ambiguity where a QML Object Literal starts with a
|
||||
// Qualified Id that looks very similar to a MemberExpression in
|
||||
// JavaScript. For now, we just won't parse statements like:
|
||||
// test: QMLObject { }
|
||||
// test: QMLObject.QualifiedId { }
|
||||
|
||||
if (this.type === tt.braceL) {
|
||||
node.block = this.qml_parseStatementBlock();
|
||||
return this.finishNode(node, "QMLBinding");
|
||||
}
|
||||
node.expr = this.parseExpression(false);
|
||||
this.semicolon();
|
||||
return this.finishNode(node, "QMLBinding");
|
||||
}
|
||||
|
||||
/*
|
||||
* Parses a QML Statement Block of the form:
|
||||
* { <JavaScript Statement>* }
|
||||
*/
|
||||
pp.qml_parseStatementBlock = function() {
|
||||
var node = this.startNode();
|
||||
this.expect(tt.braceL);
|
||||
node.statements = [];
|
||||
while(!this.eat(tt.braceR)) {
|
||||
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.
|
||||
* Returns a node of type qtt._alias if the type keyword parsed was "alias".
|
||||
*/
|
||||
pp.qml_parseType = function() {
|
||||
var node = this.startNode();
|
||||
|
||||
if (this.type === tt.name || this.type === tt._var) {
|
||||
var value = this.value;
|
||||
if (this.qml_eatPrimitiveType(value)) {
|
||||
node.isPrimitive = true;
|
||||
node.primitive = value;
|
||||
} else if (this.eatContextual(qtt._alias)) {
|
||||
return this.finishNode(node, qtt._alias);
|
||||
} else {
|
||||
node.isPrimitive = false;
|
||||
node.qualifiedId = this.qml_parseQualifiedId();
|
||||
}
|
||||
} else {
|
||||
this.unexpected();
|
||||
}
|
||||
|
||||
return this.finishNode(node, "QMLType");
|
||||
}
|
||||
|
||||
/*
|
||||
* Parses a Qualified ID of the form:
|
||||
* <Identifier> ('.' <Identifier>)*
|
||||
*/
|
||||
pp.qml_parseQualifiedId = function() {
|
||||
var node = this.startNode();
|
||||
|
||||
node.parts = [];
|
||||
if (!this.qml_isIdent(this.type, this.value)) {
|
||||
this.unexpected();
|
||||
}
|
||||
var id = this.value;
|
||||
this.next();
|
||||
node.parts.push(id);
|
||||
while(this.type === tt.dot) {
|
||||
id += '.';
|
||||
this.next();
|
||||
if (!this.qml_isIdent(this.type, this.value)) {
|
||||
this.unexpected();
|
||||
}
|
||||
id += this.value;
|
||||
node.parts.push(this.value);
|
||||
this.next();
|
||||
}
|
||||
node.raw = id;
|
||||
|
||||
return this.finishNode(node, "QMLQualifiedID");
|
||||
}
|
||||
|
||||
/*
|
||||
* Parses an Identifier in a QML Context. That is, this method uses 'isQMLContextual'
|
||||
* to throw an error if a non-contextual QML keyword is found.
|
||||
*/
|
||||
pp.qml_parseIdent = function(liberal) {
|
||||
// Check for non-contextual QML keywords
|
||||
if (this.type === tt.name) {
|
||||
for (var key in keywords) {
|
||||
if (!keywords[key].isQMLContextual && this.isContextual(key)) {
|
||||
this.unexpected();
|
||||
}
|
||||
}
|
||||
}
|
||||
return this.parseIdent(liberal);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns whether or not a given token type and name can be a QML Identifier.
|
||||
* Uses the 'isQMLContextual' boolean of 'keywords' to determine this.
|
||||
*/
|
||||
pp.qml_isIdent = function(type, name) {
|
||||
if (type === tt.name) {
|
||||
var key;
|
||||
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
|
||||
* it as a side effect if it is.
|
||||
*/
|
||||
pp.qml_eatPrimitiveType = function(name) {
|
||||
if (this.qml_isPrimitiveType(name)) {
|
||||
this.next();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns whether or not the current token is a QML primitive type.
|
||||
*/
|
||||
pp.qml_isPrimitiveType = function(name) {
|
||||
if (name === "var") {
|
||||
return true;
|
||||
}
|
||||
|
||||
var key;
|
||||
if (key = keywords[name]) {
|
||||
return key.isPrimitive;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
acorn.plugins.qml = function(instance) {
|
||||
|
||||
// Extend acorn's 'parseTopLevel' method
|
||||
instance.extend("parseTopLevel", function(nextMethod) {
|
||||
return function(node) {
|
||||
// Most of QML's constructs sit at the top-level of the parse tree,
|
||||
// replacing JavaScripts top-level. Here we are parsing such things
|
||||
// as the root object literal and header statements of QML. Eventually,
|
||||
// these rules will delegate down to JavaScript expressions.
|
||||
if (!node.body) {
|
||||
node.body = [];
|
||||
}
|
||||
|
||||
var headerStmts = this.qml_parseHeaderStatements();
|
||||
if (headerStmts !== undefined) {
|
||||
node.body.push(headerStmts);
|
||||
}
|
||||
|
||||
if (this.type !== tt.eof) {
|
||||
var objRoot = this.qml_parseObjectLiteral();
|
||||
if (objRoot !== undefined) {
|
||||
node.body.push(objRoot);
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.eat(tt.eof)) {
|
||||
this.raise(this.pos, "Expected EOF after QML Root Object");
|
||||
}
|
||||
|
||||
return this.finishNode(node, "Program");
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
return acorn;
|
||||
};
|
|
@ -1,4 +0,0 @@
|
|||
source.. = src/
|
||||
output.. = bin/
|
||||
bin.includes = META-INF/,\
|
||||
.
|
|
@ -1,8 +0,0 @@
|
|||
# Copyright (c) 2013 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
|
||||
|
||||
pluginName=C/C++ Qt Acorn QML Parser
|
||||
providerName=Eclipse CDT
|
|
@ -1,51 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
|
||||
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>org.eclipse.cdt</groupId>
|
||||
<artifactId>cdt-parent</artifactId>
|
||||
<version>8.8.0-SNAPSHOT</version>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<version>2.0.0-SNAPSHOT</version>
|
||||
<artifactId>org.eclipse.cdt.qt.core.acorn</artifactId>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-antrun-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>generate-parsers</id>
|
||||
<phase>generate-sources</phase>
|
||||
<goals>
|
||||
<goal>run</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<target>
|
||||
<ant antfile="${basedir}/build.xml" target="build"/>
|
||||
</target>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>clean-parsers</id>
|
||||
<phase>clean</phase>
|
||||
<goals>
|
||||
<goal>run</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<target>
|
||||
<ant antfile="${basedir}/build.xml" target="clean"/>
|
||||
</target>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
|
@ -1,40 +0,0 @@
|
|||
/*******************************************************************************
|
||||
* 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
|
||||
*******************************************************************************/
|
||||
package org.eclipse.cdt.qt.core.acorn;
|
||||
|
||||
import org.osgi.framework.BundleActivator;
|
||||
import org.osgi.framework.BundleContext;
|
||||
|
||||
public class Activator implements BundleActivator {
|
||||
|
||||
private static BundleContext context;
|
||||
|
||||
static BundleContext getContext() {
|
||||
return context;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext)
|
||||
*/
|
||||
public void start(BundleContext bundleContext) throws Exception {
|
||||
Activator.context = bundleContext;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
|
||||
*/
|
||||
public void stop(BundleContext bundleContext) throws Exception {
|
||||
Activator.context = null;
|
||||
}
|
||||
|
||||
}
|
|
@ -10,4 +10,15 @@
|
|||
*******************************************************************************/
|
||||
'use strict';
|
||||
|
||||
module.exports = require('./inject')(require('acorn'));
|
||||
// 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"));
|
||||
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) {
|
||||
return injectQML(acorn);
|
||||
})
|
529
qt/org.eclipse.cdt.qt.core/acorn-qml/inject.js
Normal file
529
qt/org.eclipse.cdt.qt.core/acorn-qml/inject.js
Normal file
|
@ -0,0 +1,529 @@
|
|||
/*******************************************************************************
|
||||
* 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';
|
||||
|
||||
// This will only be visible globally if we are in a browser environment
|
||||
var injectQML;
|
||||
|
||||
(function(mod) {
|
||||
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||
return module.exports = mod();
|
||||
if (typeof define == "function" && define.amd) // AMD
|
||||
return define([], mod);
|
||||
injectQML = mod(); // Plain browser env
|
||||
})(function() {
|
||||
return function(acorn) {
|
||||
// Acorn token types
|
||||
var tt = acorn.tokTypes;
|
||||
|
||||
// QML token types
|
||||
var qtt = {};
|
||||
var keywords = {};
|
||||
|
||||
/*
|
||||
* Shorthand for defining keywords in the 'keywords' variable with the following
|
||||
* format:
|
||||
* keywords[name].isPrimitive : if this is a primitive type
|
||||
* keywords[name].isQMLContextual : if this is a contextual keyword for QML
|
||||
*
|
||||
* Also stores the token's name in qtt._<keyword> for easy referencing later. None
|
||||
* of these keywords will be tokenized and, as such, are allowed to be used in
|
||||
* JavaScript expressions by acorn. The 'isQMLContextual' boolean in keywords refers
|
||||
* to those contextual keywords that are also contextual in QML's parser rules such
|
||||
* as 'color', 'list', 'alias', etc.
|
||||
*/
|
||||
function kw(name, options) {
|
||||
if (options === undefined)
|
||||
options = {};
|
||||
qtt["_" + name] = name;
|
||||
keywords[name] = {};
|
||||
keywords[name].isPrimitive = options.isPrimitive ? true : false;
|
||||
keywords[name].isQMLContextual = options.isQMLContextual ? true : false;
|
||||
}
|
||||
|
||||
// QML keywords
|
||||
kw("import");
|
||||
kw("pragma");
|
||||
kw("property", { isQMLContextual : true });
|
||||
kw("readonly", { isQMLContextual : true });
|
||||
kw("signal", { isQMLContextual : true });
|
||||
kw("as");
|
||||
kw("boolean", { isPrimitive: true });
|
||||
kw("double", { isPrimitive: true });
|
||||
kw("int", { isPrimitive: true });
|
||||
kw("alias", { isQMLContextual: true });
|
||||
kw("list", { isPrimitive: true, isQMLContextual: true });
|
||||
kw("color", { isPrimitive: true, isQMLContextual: true });
|
||||
kw("real", { isPrimitive: true, isQMLContextual: true });
|
||||
kw("string", { isPrimitive: true, isQMLContextual: true });
|
||||
kw("url", { isPrimitive: true, isQMLContextual: true });
|
||||
|
||||
// Future reserved words
|
||||
kw("transient");
|
||||
kw("synchronized");
|
||||
kw("abstract");
|
||||
kw("volatile");
|
||||
kw("native");
|
||||
kw("goto");
|
||||
kw("byte");
|
||||
kw("long");
|
||||
kw("char");
|
||||
kw("short");
|
||||
kw("float");
|
||||
|
||||
// QML parser methods
|
||||
var pp = acorn.Parser.prototype;
|
||||
|
||||
/*
|
||||
* Parses a set of QML Header Statements which can either be of
|
||||
* the type import or pragma
|
||||
*/
|
||||
pp.qml_parseHeaderStatements = function() {
|
||||
var node = this.startNode()
|
||||
node.statements = [];
|
||||
|
||||
var loop = true;
|
||||
while (loop) {
|
||||
if (this.type === tt._import || this.isContextual(qtt._import)) {
|
||||
var qmlImport = this.qml_parseImportStatement();
|
||||
node.statements.push(qmlImport);
|
||||
} else if (this.isContextual(qtt._pragma)) {
|
||||
var qmlPragma = this.qml_parsePragmaStatement();
|
||||
node.statements.push(qmlPragma);
|
||||
} else {
|
||||
loop = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (node.statements.length > 0) {
|
||||
return this.finishNode(node, "QMLHeaderStatements");
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parses a QML Pragma statement of the form:
|
||||
* 'pragma' <Identifier>
|
||||
*/
|
||||
pp.qml_parsePragmaStatement = function() {
|
||||
var node = this.startNode();
|
||||
this.next();
|
||||
node.identifier = this.parseIdent(false);
|
||||
this.semicolon();
|
||||
return this.finishNode(node, "QMLPragmaStatement");
|
||||
}
|
||||
|
||||
/*
|
||||
* Parses a QML Import statement of the form:
|
||||
* 'import' <ModuleIdentifier> <Version.Number> [as <Qualifier>]
|
||||
* 'import' <DirectoryPath> [as <Qualifier>]
|
||||
*
|
||||
* as specified by http://doc.qt.io/qt-5/qtqml-syntax-imports.html
|
||||
*/
|
||||
pp.qml_parseImportStatement = function() {
|
||||
var node = this.startNode();
|
||||
this.next();
|
||||
|
||||
// The type of import varies solely on the next token
|
||||
switch(this.type) {
|
||||
case tt.name:
|
||||
node.module = this.qml_parseModule();
|
||||
break;
|
||||
case tt.string:
|
||||
node.directoryPath = this.parseLiteral(this.value);
|
||||
break;
|
||||
default:
|
||||
this.unexpected();
|
||||
break;
|
||||
}
|
||||
|
||||
// Parse the qualifier, if any
|
||||
if (this.isContextual(qtt._as)) {
|
||||
node.qualifier = this.qml_parseQualifier();
|
||||
}
|
||||
|
||||
this.semicolon();
|
||||
return this.finishNode(node, "QMLImportStatement");
|
||||
};
|
||||
|
||||
/*
|
||||
* Parses a QML Module of the form:
|
||||
* <QualifiedId> <VersionLiteral>
|
||||
*/
|
||||
pp.qml_parseModule = function() {
|
||||
var node = this.startNode();
|
||||
|
||||
node.qualifiedId = this.qml_parseQualifiedId();
|
||||
if (this.type === tt.num) {
|
||||
node.version = this.qml_parseVersionLiteral();
|
||||
} else {
|
||||
this.unexpected();
|
||||
}
|
||||
|
||||
return this.finishNode(node, "QMLModule");
|
||||
};
|
||||
|
||||
/*
|
||||
* Parses a QML Version Literal which consists of a major and minor
|
||||
* version separated by a '.'
|
||||
*/
|
||||
pp.qml_parseVersionLiteral = function() {
|
||||
var node = this.startNode();
|
||||
|
||||
node.raw = this.input.slice(this.start, this.end);
|
||||
node.value = this.value;
|
||||
var matches;
|
||||
if (matches = /(\d+)\.(\d+)/.exec(node.raw)) {
|
||||
node.major = parseInt(matches[1]);
|
||||
node.minor = parseInt(matches[2]);
|
||||
} else {
|
||||
this.raise(this.start, "QML module must specify major and minor version");
|
||||
}
|
||||
|
||||
this.next();
|
||||
return this.finishNode(node, "QMLVersionLiteral");
|
||||
}
|
||||
|
||||
/*
|
||||
* Parses a QML Qualifier of the form:
|
||||
* 'as' <Identifier>
|
||||
*/
|
||||
pp.qml_parseQualifier = function() {
|
||||
var node = this.startNode();
|
||||
this.next();
|
||||
node.identifier = this.qml_parseIdent(false);
|
||||
return this.finishNode(node, "QMLQualifier");
|
||||
}
|
||||
|
||||
/*
|
||||
* Parses a QML Object Literal of the form:
|
||||
* <QualifiedId> { (<QMLMember>)* }
|
||||
*
|
||||
* http://doc.qt.io/qt-5/qtqml-syntax-basics.html#object-declarations
|
||||
*/
|
||||
pp.qml_parseObjectLiteral = function(node) {
|
||||
if (!node) {
|
||||
node = this.startNode();
|
||||
}
|
||||
if (!node.qualifiedId) {
|
||||
node.qualifiedId = this.qml_parseQualifiedId();
|
||||
}
|
||||
node.block = this.qml_parseMemberBlock();
|
||||
return this.finishNode(node, "QMLObjectLiteral");
|
||||
}
|
||||
|
||||
/*
|
||||
* Parses a QML Member Block of the form:
|
||||
* { <QMLMember>* }
|
||||
*/
|
||||
pp.qml_parseMemberBlock = function() {
|
||||
var node = this.startNode();
|
||||
this.expect(tt.braceL);
|
||||
node.members = [];
|
||||
while (!this.eat(tt.braceR)) {
|
||||
node.members.push(this.qml_parseMember());
|
||||
}
|
||||
return this.finishNode(node, "QMLMemberBlock");
|
||||
}
|
||||
|
||||
/*
|
||||
* Parses a QML Member which can be one of the following:
|
||||
* - a QML Property Binding
|
||||
* - a Property Declaration (or Alias)
|
||||
* - a QML Object Literal
|
||||
* - a JavaScript Function Declaration
|
||||
* - a Signal Definition
|
||||
*/
|
||||
pp.qml_parseMember = function() {
|
||||
var node = this.startNode();
|
||||
|
||||
if (this.type === tt._default
|
||||
|| this.isContextual(qtt._default)
|
||||
|| this.isContextual(qtt._readonly)
|
||||
|| this.isContextual(qtt._property)) {
|
||||
return this.qml_parsePropertyDeclaration(node);
|
||||
} else if (this.isContextual(qtt._signal)) {
|
||||
return this.qml_parseSignalDefinition(node);
|
||||
} else if (this.type === tt._function) {
|
||||
return this.parseFunctionStatement(node);
|
||||
}
|
||||
|
||||
node.qualifiedId = this.qml_parseQualifiedId();
|
||||
switch(this.type) {
|
||||
case tt.braceL:
|
||||
return this.qml_parseObjectLiteral(node);
|
||||
case tt.colon:
|
||||
return this.qml_parseProperty(node);
|
||||
}
|
||||
|
||||
this.unexpected();
|
||||
}
|
||||
|
||||
/*
|
||||
* Parses a QML Property of the form:
|
||||
* <QMLQualifiedID> <QMLBinding>
|
||||
*/
|
||||
pp.qml_parseProperty = function(node) {
|
||||
if (!node) {
|
||||
node = this.startNode();
|
||||
}
|
||||
if (!node.qualifiedId) {
|
||||
node.qualifiedId = this.qml_parseQualifiedId();
|
||||
}
|
||||
node.binding = this.qml_parseBinding();
|
||||
return this.finishNode(node, "QMLProperty");
|
||||
}
|
||||
|
||||
/*
|
||||
* Parses a QML Signal Definition of the form:
|
||||
* 'signal' <Identifier> [(<Type> <Identifier> [',' <Type> <Identifier>]* )]?
|
||||
*/
|
||||
pp.qml_parseSignalDefinition = function(node) {
|
||||
if (!node) {
|
||||
node = this.startNode();
|
||||
}
|
||||
this.next();
|
||||
node.identifier = this.qml_parseIdent(false);
|
||||
node.parameters = [];
|
||||
if (this.type === tt.parenL) {
|
||||
this.next();
|
||||
if (this.type !== tt.parenR) {
|
||||
do {
|
||||
var paramNode = this.startNode();
|
||||
paramNode.type = this.qml_parseIdent(false);
|
||||
paramNode.identifier = this.qml_parseIdent(false);
|
||||
node.parameters.push(paramNode);
|
||||
} while(this.eat(tt.comma));
|
||||
}
|
||||
if (!this.eat(tt.parenR)) {
|
||||
this.unexpected();
|
||||
}
|
||||
}
|
||||
this.semicolon();
|
||||
return this.finishNode(node, "QMLSignalDefinition");
|
||||
}
|
||||
|
||||
/*
|
||||
* Parses a QML Property Declaration (or Alias) of the form:
|
||||
* ['default'|'readonly'] 'property' <QMLType> <Identifier> [<QMLBinding>]
|
||||
*/
|
||||
pp.qml_parsePropertyDeclaration = function(node) {
|
||||
node["default"] = false;
|
||||
node["readonly"] = false;
|
||||
|
||||
if (this.type === tt._default || this.isContextual(qtt._default)) {
|
||||
node["default"] = true;
|
||||
this.next();
|
||||
} else if (this.eatContextual(qtt._readonly)) {
|
||||
node["readonly"] = true;
|
||||
}
|
||||
this.expectContextual(qtt._property);
|
||||
node.typeInfo = this.qml_parseType();
|
||||
node.identifier = this.qml_parseIdent(false);
|
||||
if (this.type !== tt.colon) {
|
||||
this.semicolon();
|
||||
} else {
|
||||
node.binding = this.qml_parseBinding();
|
||||
}
|
||||
|
||||
if (node.typeInfo.type === qtt._alias) {
|
||||
node.typeInfo = undefined;
|
||||
return this.finishNode(node, "QMLPropertyAlias");
|
||||
}
|
||||
return this.finishNode(node, "QMLPropertyDeclaration");
|
||||
}
|
||||
|
||||
/*
|
||||
* Parses a QML Binding of the form:
|
||||
* ':' (<Expression>|<QMLStatementBlock>)
|
||||
*/
|
||||
pp.qml_parseBinding = function() {
|
||||
var node = this.startNode();
|
||||
this.expect(tt.colon);
|
||||
|
||||
// TODO: solve ambiguity where a QML Object Literal starts with a
|
||||
// Qualified Id that looks very similar to a MemberExpression in
|
||||
// JavaScript. For now, we just won't parse statements like:
|
||||
// test: QMLObject { }
|
||||
// test: QMLObject.QualifiedId { }
|
||||
|
||||
if (this.type === tt.braceL) {
|
||||
node.block = this.qml_parseStatementBlock();
|
||||
return this.finishNode(node, "QMLBinding");
|
||||
}
|
||||
node.expr = this.parseExpression(false);
|
||||
this.semicolon();
|
||||
return this.finishNode(node, "QMLBinding");
|
||||
}
|
||||
|
||||
/*
|
||||
* Parses a QML Statement Block of the form:
|
||||
* { <JavaScript Statement>* }
|
||||
*/
|
||||
pp.qml_parseStatementBlock = function() {
|
||||
var node = this.startNode();
|
||||
this.expect(tt.braceL);
|
||||
node.statements = [];
|
||||
while(!this.eat(tt.braceR)) {
|
||||
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.
|
||||
* Returns a node of type qtt._alias if the type keyword parsed was "alias".
|
||||
*/
|
||||
pp.qml_parseType = function() {
|
||||
var node = this.startNode();
|
||||
|
||||
if (this.type === tt.name || this.type === tt._var) {
|
||||
var value = this.value;
|
||||
if (this.qml_eatPrimitiveType(value)) {
|
||||
node.isPrimitive = true;
|
||||
node.primitive = value;
|
||||
} else if (this.eatContextual(qtt._alias)) {
|
||||
return this.finishNode(node, qtt._alias);
|
||||
} else {
|
||||
node.isPrimitive = false;
|
||||
node.qualifiedId = this.qml_parseQualifiedId();
|
||||
}
|
||||
} else {
|
||||
this.unexpected();
|
||||
}
|
||||
|
||||
return this.finishNode(node, "QMLType");
|
||||
}
|
||||
|
||||
/*
|
||||
* Parses a Qualified ID of the form:
|
||||
* <Identifier> ('.' <Identifier>)*
|
||||
*/
|
||||
pp.qml_parseQualifiedId = function() {
|
||||
var node = this.startNode();
|
||||
|
||||
node.parts = [];
|
||||
if (!this.qml_isIdent(this.type, this.value)) {
|
||||
this.unexpected();
|
||||
}
|
||||
var id = this.value;
|
||||
this.next();
|
||||
node.parts.push(id);
|
||||
while(this.type === tt.dot) {
|
||||
id += '.';
|
||||
this.next();
|
||||
if (!this.qml_isIdent(this.type, this.value)) {
|
||||
this.unexpected();
|
||||
}
|
||||
id += this.value;
|
||||
node.parts.push(this.value);
|
||||
this.next();
|
||||
}
|
||||
node.raw = id;
|
||||
|
||||
return this.finishNode(node, "QMLQualifiedID");
|
||||
}
|
||||
|
||||
/*
|
||||
* Parses an Identifier in a QML Context. That is, this method uses 'isQMLContextual'
|
||||
* to throw an error if a non-contextual QML keyword is found.
|
||||
*/
|
||||
pp.qml_parseIdent = function(liberal) {
|
||||
// Check for non-contextual QML keywords
|
||||
if (this.type === tt.name) {
|
||||
for (var key in keywords) {
|
||||
if (!keywords[key].isQMLContextual && this.isContextual(key)) {
|
||||
this.unexpected();
|
||||
}
|
||||
}
|
||||
}
|
||||
return this.parseIdent(liberal);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns whether or not a given token type and name can be a QML Identifier.
|
||||
* Uses the 'isQMLContextual' boolean of 'keywords' to determine this.
|
||||
*/
|
||||
pp.qml_isIdent = function(type, name) {
|
||||
if (type === tt.name) {
|
||||
var key;
|
||||
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
|
||||
* it as a side effect if it is.
|
||||
*/
|
||||
pp.qml_eatPrimitiveType = function(name) {
|
||||
if (this.qml_isPrimitiveType(name)) {
|
||||
this.next();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns whether or not the current token is a QML primitive type.
|
||||
*/
|
||||
pp.qml_isPrimitiveType = function(name) {
|
||||
if (name === "var") {
|
||||
return true;
|
||||
}
|
||||
|
||||
var key;
|
||||
if (key = keywords[name]) {
|
||||
return key.isPrimitive;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
acorn.plugins.qml = function(instance) {
|
||||
|
||||
// Extend acorn's 'parseTopLevel' method
|
||||
instance.extend("parseTopLevel", function(nextMethod) {
|
||||
return function(node) {
|
||||
// Most of QML's constructs sit at the top-level of the parse tree,
|
||||
// replacing JavaScripts top-level. Here we are parsing such things
|
||||
// as the root object literal and header statements of QML. Eventually,
|
||||
// these rules will delegate down to JavaScript expressions.
|
||||
if (!node.body) {
|
||||
node.body = [];
|
||||
}
|
||||
|
||||
var headerStmts = this.qml_parseHeaderStatements();
|
||||
if (headerStmts !== undefined) {
|
||||
node.body.push(headerStmts);
|
||||
}
|
||||
|
||||
if (this.type !== tt.eof) {
|
||||
var objRoot = this.qml_parseObjectLiteral();
|
||||
if (objRoot !== undefined) {
|
||||
node.body.push(objRoot);
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.eat(tt.eof)) {
|
||||
this.raise(this.pos, "Expected EOF after QML Root Object");
|
||||
}
|
||||
|
||||
return this.finishNode(node, "Program");
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
return acorn;
|
||||
};
|
||||
})
|
76
qt/org.eclipse.cdt.qt.core/acorn-qml/walk/walk.js
Normal file
76
qt/org.eclipse.cdt.qt.core/acorn-qml/walk/walk.js
Normal file
|
@ -0,0 +1,76 @@
|
|||
/*******************************************************************************
|
||||
* 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';
|
||||
|
||||
(function(mod) {
|
||||
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||
return mod(require("acorn/walk"));
|
||||
if (typeof define == "function" && define.amd) // AMD
|
||||
return define([ "acorn/dist/walk" ], mod);
|
||||
mod(acorn.walk); // Plain browser env
|
||||
})(function(walk) {
|
||||
function skipThrough(node, st, c) { c(node, st) }
|
||||
function ignore(node, st, c) {}
|
||||
|
||||
var base = walk.base;
|
||||
base["Program"] = function(node, st, c) {
|
||||
for (var i = 0; i < node.body.length; i++) {
|
||||
var nodeBody = node.body[i];
|
||||
if (node.body[i].type === "QMLObjectLiteral") {
|
||||
c(node.body[i], st, "QMLRootObject");
|
||||
} else {
|
||||
c(node.body[i], st);
|
||||
}
|
||||
}
|
||||
}
|
||||
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"] = function(node, st, c) {
|
||||
c(node.module, st);
|
||||
}
|
||||
base["QMLModule"] = ignore;
|
||||
base["QMLPragmaStatement"] = ignore;
|
||||
base["QMLRootObject"] = skipThrough;
|
||||
base["QMLObjectLiteral"] = function(node, st, c) {
|
||||
c(node.block, 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) {
|
||||
c(node.identifier, st, "Pattern");
|
||||
c(node.binding, st);
|
||||
}
|
||||
base["QMLSignalDefinition"] = ignore;
|
||||
base["QMLProperty"] = function(node, st, c) {
|
||||
// c(node.qualifiedId, st)
|
||||
c(node.binding, st);
|
||||
}
|
||||
base["QMLBinding"] = function(node, st, c) {
|
||||
if (node.block) {
|
||||
c(node.block, st);
|
||||
} else {
|
||||
c(node.expr, st, "Expression");
|
||||
}
|
||||
}
|
||||
base["QMLStatementBlock"] = function(node, st, c) {
|
||||
for (var i = 0; i < node.statements.length; i++) {
|
||||
c(node.statements[i], st, "Statement");
|
||||
}
|
||||
}
|
||||
})
|
4
qt/org.eclipse.cdt.qt.core/tern-qml/.gitignore
vendored
Normal file
4
qt/org.eclipse.cdt.qt.core/tern-qml/.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
/node_modules
|
||||
/.settings
|
||||
.project
|
||||
.tern-project
|
5
qt/org.eclipse.cdt.qt.core/tern-qml/.npmignore
Normal file
5
qt/org.eclipse.cdt.qt.core/tern-qml/.npmignore
Normal file
|
@ -0,0 +1,5 @@
|
|||
/test
|
||||
/node_modules
|
||||
/.settings
|
||||
.project
|
||||
.tern-project
|
97
qt/org.eclipse.cdt.qt.core/tern-qml/demo/qml-demo.html
Normal file
97
qt/org.eclipse.cdt.qt.core/tern-qml/demo/qml-demo.html
Normal file
|
@ -0,0 +1,97 @@
|
|||
<!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/walk/walk.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>
|
||||
<script src="defs/browser.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>
|
||||
<form><textarea id="code" name="code">import QtQuick 2.3 Window { 	prop: { 		Qt.quit(); 		 	} } </textarea></form>
|
||||
<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,
|
||||
extraKeys: {
|
||||
"'.'": passAndHint,
|
||||
"Ctrl-Space": "autocomplete",
|
||||
"Ctrl-I": function(cm) { CodeMirror.tern.showType(cm); },
|
||||
"Ctrl-B": 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>
|
16
qt/org.eclipse.cdt.qt.core/tern-qml/package.json
Normal file
16
qt/org.eclipse.cdt.qt.core/tern-qml/package.json
Normal file
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"name": "tern-qt",
|
||||
"description": "Tern Qt Plugin",
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"tern": "^0.16.0",
|
||||
"acorn": "^2.4.0",
|
||||
"acorn-qml": "../acorn-qml"
|
||||
},
|
||||
"devDependencies": {
|
||||
"test": ">=0.0.5",
|
||||
"codemirror": "^5.6.0",
|
||||
"codemirror-extension": "^0.1.0",
|
||||
"codemirror-javascript": "^0.1.0"
|
||||
}
|
||||
}
|
69
qt/org.eclipse.cdt.qt.core/tern-qml/qml.js
Normal file
69
qt/org.eclipse.cdt.qt.core/tern-qml/qml.js
Normal file
|
@ -0,0 +1,69 @@
|
|||
/*******************************************************************************
|
||||
* 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";
|
||||
|
||||
(function(mod) {
|
||||
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||
return mod(require("tern/lib/infer"), require("tern/lib/tern"));
|
||||
if (typeof define == "function" && define.amd) // AMD
|
||||
return define([ "tern/lib/infer", "tern/lib/tern" ], mod);
|
||||
mod(tern, tern); // Plain browser env
|
||||
})(function(infer, tern) {
|
||||
// Define a few shorthand variables/functions
|
||||
var Scope = infer.Scope;
|
||||
function skipThrough(node, st, c) { c(node, st) }
|
||||
function ignore(node, st, c) {}
|
||||
|
||||
// Register the QML plugin in Tern
|
||||
tern.registerPlugin("qml", function(server) {
|
||||
extendTernScopeGatherer(infer.scopeGatherer);
|
||||
extendTernInferWrapper(infer.inferWrapper);
|
||||
extendTernTypeFinder(infer.typeFinder);
|
||||
extendTernSearchVisitor(infer.searchVisitor);
|
||||
server.on("preParse", preParse);
|
||||
});
|
||||
|
||||
function preParse(text, options) {
|
||||
var plugins = options.plugins;
|
||||
if (!plugins) plugins = options.plugins = {};
|
||||
plugins["qml"] = true;
|
||||
}
|
||||
|
||||
function extendTernScopeGatherer(scopeGatherer) {
|
||||
scopeGatherer["QMLModule"] = function(node, scope, c) {
|
||||
scope.defProp(node.qualifiedId.raw, node.qualifiedId);
|
||||
}
|
||||
scopeGatherer["QMLMemberBlock"] = function(node, scope, c) {
|
||||
var inner = node.scope = new Scope(scope, node);
|
||||
for (var i = 0; i < node.members.length; i++) {
|
||||
c(node.members[i], inner, "QMLMember");
|
||||
}
|
||||
}
|
||||
scopeGatherer["QMLStatementBlock"] = function(node, scope, c) {
|
||||
var inner = node.scope = new Scope(scope, node);
|
||||
for (var i = 0; i < node.statements.length; i++) {
|
||||
c(node.statements[i], inner, "Statement");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function extendTernInferWrapper(inferWrapper) {
|
||||
// TODO: Implement the AST walk methods for inferWrapper
|
||||
}
|
||||
|
||||
function extendTernTypeFinder(typeFinder) {
|
||||
// TODO: Implement the AST walk methods for typeFinder
|
||||
}
|
||||
|
||||
function extendTernSearchVisitor(searchVisitor) {
|
||||
// TODO: Implement the AST walk methods for searchVisitor
|
||||
}
|
||||
})
|
Loading…
Add table
Reference in a new issue