diff --git a/qt/org.eclipse.cdt.qt.core.acorn/.classpath b/qt/org.eclipse.cdt.qt.core.acorn/.classpath deleted file mode 100644 index b862a296d38..00000000000 --- a/qt/org.eclipse.cdt.qt.core.acorn/.classpath +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/qt/org.eclipse.cdt.qt.core.acorn/.project b/qt/org.eclipse.cdt.qt.core.acorn/.project deleted file mode 100644 index e8454d75162..00000000000 --- a/qt/org.eclipse.cdt.qt.core.acorn/.project +++ /dev/null @@ -1,28 +0,0 @@ - - - org.eclipse.cdt.qt.core.acorn - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.pde.ManifestBuilder - - - - - org.eclipse.pde.SchemaBuilder - - - - - - org.eclipse.pde.PluginNature - org.eclipse.jdt.core.javanature - - diff --git a/qt/org.eclipse.cdt.qt.core.acorn/.settings/org.eclipse.jdt.core.prefs b/qt/org.eclipse.cdt.qt.core.acorn/.settings/org.eclipse.jdt.core.prefs deleted file mode 100644 index 295926d9641..00000000000 --- a/qt/org.eclipse.cdt.qt.core.acorn/.settings/org.eclipse.jdt.core.prefs +++ /dev/null @@ -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 diff --git a/qt/org.eclipse.cdt.qt.core.acorn/META-INF/MANIFEST.MF b/qt/org.eclipse.cdt.qt.core.acorn/META-INF/MANIFEST.MF deleted file mode 100644 index 0cb06697864..00000000000 --- a/qt/org.eclipse.cdt.qt.core.acorn/META-INF/MANIFEST.MF +++ /dev/null @@ -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 diff --git a/qt/org.eclipse.cdt.qt.core.acorn/acorn-qml/inject.js b/qt/org.eclipse.cdt.qt.core.acorn/acorn-qml/inject.js deleted file mode 100644 index 2d9dd903790..00000000000 --- a/qt/org.eclipse.cdt.qt.core.acorn/acorn-qml/inject.js +++ /dev/null @@ -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._ 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' - */ - 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' [as ] - * 'import' [as ] - * - * 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: - * - */ - 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' - */ - 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: - * { ()* } - * - * 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: - * { * } - */ - 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: - * - */ - 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' [( [',' ]* )]? - */ - 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' [] - */ - 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: - * ':' (|) - */ - 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: - * { * } - */ - 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: - * ('.' )* - */ - 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; -}; \ No newline at end of file diff --git a/qt/org.eclipse.cdt.qt.core.acorn/build.properties b/qt/org.eclipse.cdt.qt.core.acorn/build.properties deleted file mode 100644 index 41eb6ade2b4..00000000000 --- a/qt/org.eclipse.cdt.qt.core.acorn/build.properties +++ /dev/null @@ -1,4 +0,0 @@ -source.. = src/ -output.. = bin/ -bin.includes = META-INF/,\ - . diff --git a/qt/org.eclipse.cdt.qt.core.acorn/plugin.properties b/qt/org.eclipse.cdt.qt.core.acorn/plugin.properties deleted file mode 100644 index 7ed20d4606f..00000000000 --- a/qt/org.eclipse.cdt.qt.core.acorn/plugin.properties +++ /dev/null @@ -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 diff --git a/qt/org.eclipse.cdt.qt.core.acorn/pom.xml b/qt/org.eclipse.cdt.qt.core.acorn/pom.xml deleted file mode 100644 index 8bdb21a8da6..00000000000 --- a/qt/org.eclipse.cdt.qt.core.acorn/pom.xml +++ /dev/null @@ -1,51 +0,0 @@ - - - 4.0.0 - - - org.eclipse.cdt - cdt-parent - 8.8.0-SNAPSHOT - ../../pom.xml - - - 2.0.0-SNAPSHOT - org.eclipse.cdt.qt.core.acorn - eclipse-plugin - - - - - maven-antrun-plugin - - - generate-parsers - generate-sources - - run - - - - - - - - - clean-parsers - clean - - run - - - - - - - - - - - - diff --git a/qt/org.eclipse.cdt.qt.core.acorn/src/org/eclipse/cdt/qt/core/acorn/Activator.java b/qt/org.eclipse.cdt.qt.core.acorn/src/org/eclipse/cdt/qt/core/acorn/Activator.java deleted file mode 100644 index e4f60a0bc89..00000000000 --- a/qt/org.eclipse.cdt.qt.core.acorn/src/org/eclipse/cdt/qt/core/acorn/Activator.java +++ /dev/null @@ -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; - } - -} diff --git a/qt/org.eclipse.cdt.qt.core.acorn/acorn-qml/.gitignore b/qt/org.eclipse.cdt.qt.core/acorn-qml/.gitignore similarity index 100% rename from qt/org.eclipse.cdt.qt.core.acorn/acorn-qml/.gitignore rename to qt/org.eclipse.cdt.qt.core/acorn-qml/.gitignore diff --git a/qt/org.eclipse.cdt.qt.core.acorn/acorn-qml/bin/acorn-qml.js b/qt/org.eclipse.cdt.qt.core/acorn-qml/bin/acorn-qml.js similarity index 100% rename from qt/org.eclipse.cdt.qt.core.acorn/acorn-qml/bin/acorn-qml.js rename to qt/org.eclipse.cdt.qt.core/acorn-qml/bin/acorn-qml.js diff --git a/qt/org.eclipse.cdt.qt.core.acorn/acorn-qml/index.js b/qt/org.eclipse.cdt.qt.core/acorn-qml/index.js similarity index 52% rename from qt/org.eclipse.cdt.qt.core.acorn/acorn-qml/index.js rename to qt/org.eclipse.cdt.qt.core/acorn-qml/index.js index 282e1f47250..32c4d1dfd90 100644 --- a/qt/org.eclipse.cdt.qt.core.acorn/acorn-qml/index.js +++ b/qt/org.eclipse.cdt.qt.core/acorn-qml/index.js @@ -10,4 +10,15 @@ *******************************************************************************/ 'use strict'; -module.exports = require('./inject')(require('acorn')); \ No newline at end of file +// 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); +}) \ No newline at end of file diff --git a/qt/org.eclipse.cdt.qt.core/acorn-qml/inject.js b/qt/org.eclipse.cdt.qt.core/acorn-qml/inject.js new file mode 100644 index 00000000000..c176fd9af7b --- /dev/null +++ b/qt/org.eclipse.cdt.qt.core/acorn-qml/inject.js @@ -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._ 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' + */ + 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' [as ] + * 'import' [as ] + * + * 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: + * + */ + 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' + */ + 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: + * { ()* } + * + * 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: + * { * } + */ + 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: + * + */ + 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' [( [',' ]* )]? + */ + 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' [] + */ + 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: + * ':' (|) + */ + 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: + * { * } + */ + 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: + * ('.' )* + */ + 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; + }; +}) \ No newline at end of file diff --git a/qt/org.eclipse.cdt.qt.core.acorn/acorn-qml/package.json b/qt/org.eclipse.cdt.qt.core/acorn-qml/package.json similarity index 100% rename from qt/org.eclipse.cdt.qt.core.acorn/acorn-qml/package.json rename to qt/org.eclipse.cdt.qt.core/acorn-qml/package.json diff --git a/qt/org.eclipse.cdt.qt.core.acorn/acorn-qml/test/driver.js b/qt/org.eclipse.cdt.qt.core/acorn-qml/test/driver.js similarity index 100% rename from qt/org.eclipse.cdt.qt.core.acorn/acorn-qml/test/driver.js rename to qt/org.eclipse.cdt.qt.core/acorn-qml/test/driver.js diff --git a/qt/org.eclipse.cdt.qt.core.acorn/acorn-qml/test/run.js b/qt/org.eclipse.cdt.qt.core/acorn-qml/test/run.js similarity index 100% rename from qt/org.eclipse.cdt.qt.core.acorn/acorn-qml/test/run.js rename to qt/org.eclipse.cdt.qt.core/acorn-qml/test/run.js diff --git a/qt/org.eclipse.cdt.qt.core.acorn/acorn-qml/test/tests-qml.js b/qt/org.eclipse.cdt.qt.core/acorn-qml/test/tests-qml.js similarity index 100% rename from qt/org.eclipse.cdt.qt.core.acorn/acorn-qml/test/tests-qml.js rename to qt/org.eclipse.cdt.qt.core/acorn-qml/test/tests-qml.js diff --git a/qt/org.eclipse.cdt.qt.core/acorn-qml/walk/walk.js b/qt/org.eclipse.cdt.qt.core/acorn-qml/walk/walk.js new file mode 100644 index 00000000000..a753f3e3f07 --- /dev/null +++ b/qt/org.eclipse.cdt.qt.core/acorn-qml/walk/walk.js @@ -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"); + } + } +}) \ No newline at end of file diff --git a/qt/org.eclipse.cdt.qt.core/tern-qml/.gitignore b/qt/org.eclipse.cdt.qt.core/tern-qml/.gitignore new file mode 100644 index 00000000000..4e050c365a2 --- /dev/null +++ b/qt/org.eclipse.cdt.qt.core/tern-qml/.gitignore @@ -0,0 +1,4 @@ +/node_modules +/.settings +.project +.tern-project \ No newline at end of file diff --git a/qt/org.eclipse.cdt.qt.core/tern-qml/.npmignore b/qt/org.eclipse.cdt.qt.core/tern-qml/.npmignore new file mode 100644 index 00000000000..ebc5ac557a4 --- /dev/null +++ b/qt/org.eclipse.cdt.qt.core/tern-qml/.npmignore @@ -0,0 +1,5 @@ +/test +/node_modules +/.settings +.project +.tern-project \ No newline at end of file diff --git a/qt/org.eclipse.cdt.qt.core/tern-qml/demo/qml-demo.html b/qt/org.eclipse.cdt.qt.core/tern-qml/demo/qml-demo.html new file mode 100644 index 00000000000..210a407a201 --- /dev/null +++ b/qt/org.eclipse.cdt.qt.core/tern-qml/demo/qml-demo.html @@ -0,0 +1,97 @@ + + + + + QML Tern Demo + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Demo with QML Tern plugin

+
+ + + diff --git a/qt/org.eclipse.cdt.qt.core/tern-qml/package.json b/qt/org.eclipse.cdt.qt.core/tern-qml/package.json new file mode 100644 index 00000000000..2074871072f --- /dev/null +++ b/qt/org.eclipse.cdt.qt.core/tern-qml/package.json @@ -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" + } +} \ No newline at end of file diff --git a/qt/org.eclipse.cdt.qt.core/tern-qml/qml.js b/qt/org.eclipse.cdt.qt.core/tern-qml/qml.js new file mode 100644 index 00000000000..1e7ac7752f6 --- /dev/null +++ b/qt/org.eclipse.cdt.qt.core/tern-qml/qml.js @@ -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 + } +}) \ No newline at end of file