1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-04-23 14:42:11 +02:00

Bug 477962 - Create Basic QML Editor with Syntax Highlighting

Added a QML Editor which handles basic syntax highlighting for keywords,
multi-line and single-line comments, and strings.  The highlight colours
are similar to that of Qt Creator and cannot be modified by users as of
yet.

Change-Id: Ied0ab0432a3a2a43f93733964747c792e29771c4
Signed-off-by: Matthew Bastien <mbastien@blackberry.com>
This commit is contained in:
Matthew Bastien 2015-09-21 11:16:56 -04:00
parent 366dcbb39a
commit 4cc61d0449
8 changed files with 418 additions and 0 deletions

View file

@ -7,6 +7,8 @@
pluginName=C/C++ Qt Support UI
providerName=Eclipse CDT
qmlEditor.name=QML Editor
qtHighlighting.extName=Qt Semantic Highlighting
qtHighlighting.displayName=Qt Keywords

View file

@ -47,4 +47,22 @@
project="true">
</wizard>
</extension>
<extension
point="org.eclipse.ui.editors">
<editor
class="org.eclipse.cdt.internal.qt.ui.editor.QMLEditor"
default="true"
extensions="qml"
id="org.eclipse.cdt.qt.ui.QMLEditor"
name="%qmlEditor.name">
</editor>
</extension>
<extension
point="org.eclipse.core.filebuffers.documentSetup">
<participant
class="org.eclipse.cdt.internal.qt.ui.editor.QMLDocumentSetupParticipant"
contentTypeId="qmlFile"
extensions="qml">
</participant>
</extension>
</plugin>

View file

@ -0,0 +1,33 @@
/*******************************************************************************
* 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.internal.qt.ui.editor;
import org.eclipse.cdt.internal.qt.ui.text.IQMLPartitions;
import org.eclipse.core.filebuffers.IDocumentSetupParticipant;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IDocumentExtension3;
import org.eclipse.jface.text.IDocumentPartitioner;
import org.eclipse.jface.text.rules.FastPartitioner;
public class QMLDocumentSetupParticipant implements IDocumentSetupParticipant, IQMLPartitions {
@Override
public void setup(IDocument document) {
IDocumentPartitioner partitioner = new FastPartitioner(new QMLPartitionScanner(), IQMLPartitions.ALL_QMLPARTITIONS);
if (document instanceof IDocumentExtension3) {
IDocumentExtension3 ext = (IDocumentExtension3) document;
ext.setDocumentPartitioner(IQMLPartitions.QML_PARTITIONING, partitioner);
} else {
document.setDocumentPartitioner(partitioner);
}
partitioner.connect(document);
}
}

View file

@ -0,0 +1,26 @@
/*******************************************************************************
* 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.internal.qt.ui.editor;
import org.eclipse.cdt.internal.qt.ui.text.QMLSourceViewerConfiguration;
import org.eclipse.ui.editors.text.TextEditor;
/**
* Basic editor for QML. Thus far has only syntax highlighting capabilities.
*/
public class QMLEditor extends TextEditor {
public static final String EDITOR_ID = "org.eclipse.cdt.qt.ui.QMLEditor"; //$NON-NLS-1$
@Override
protected void initializeEditor() {
setSourceViewerConfiguration(new QMLSourceViewerConfiguration());
}
}

View file

@ -0,0 +1,119 @@
/*******************************************************************************
* 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.internal.qt.ui.editor;
/**
* Constants for the various keywords used by QML and ECMA Script
*/
public class QMLKeywords {
// QML reserved words
public static final String IMPORT = "import"; //$NON-NLS-1$
public static final String CONST = "const"; //$NON-NLS-1$
public static final String PROPERTY = "property"; //$NON-NLS-1$
public static final String READONLY = "readonly"; //$NON-NLS-1$
public static final String SIGNAL = "signal"; //$NON-NLS-1$
public static final String AS = "as"; //$NON-NLS-1$
public static final String ON = "on"; //$NON-NLS-1$
public static final String TRUE = "true"; //$NON-NLS-1$
public static final String FALSE = "false"; //$NON-NLS-1$
// QML future reserved words
public static final String TRANSIENT = "transient"; //$NON-NLS-1$
public static final String SYNCHRONIZED = "synchronized"; //$NON-NLS-1$
public static final String ABSTRACT = "abstract"; //$NON-NLS-1$
public static final String VOLATILE = "volatile"; //$NON-NLS-1$
public static final String NATIVE = "native"; //$NON-NLS-1$
public static final String GOTO = "goto"; //$NON-NLS-1$
public static final String BYTE = "byte"; //$NON-NLS-1$
public static final String LONG = "long"; //$NON-NLS-1$
public static final String CHAR = "char"; //$NON-NLS-1$
public static final String SHORT = "short"; //$NON-NLS-1$
public static final String FLOAT = "float"; //$NON-NLS-1$
// QML basic types
public static final String BOOLEAN = "boolean"; //$NON-NLS-1$
public static final String DOUBLE = "double"; //$NON-NLS-1$
public static final String INT = "int"; //$NON-NLS-1$
public static final String LIST = "list"; //$NON-NLS-1$
public static final String COLOR = "color"; //$NON-NLS-1$
public static final String REAL = "real"; //$NON-NLS-1$
public static final String STRING = "string"; //$NON-NLS-1$
public static final String URL = "url"; //$NON-NLS-1$
public static final String VAR = "var"; //$NON-NLS-1$
// ECMA Script reserved words
public static final String BREAK = "break"; //$NON-NLS-1$
public static final String DO = "do"; //$NON-NLS-1$
public static final String INSTANCEOF = "instanceof"; //$NON-NLS-1$
public static final String TYPEOF = "typeof"; //$NON-NLS-1$
public static final String CASE = "case"; //$NON-NLS-1$
public static final String ELSE = "else"; //$NON-NLS-1$
public static final String NEW = "new"; //$NON-NLS-1$
public static final String CATCH = "catch"; //$NON-NLS-1$
public static final String FINALLY = "finally"; //$NON-NLS-1$
public static final String RETURN = "return"; //$NON-NLS-1$
public static final String VOID = "void"; //$NON-NLS-1$
public static final String CONTINUE = "continue"; //$NON-NLS-1$
public static final String FOR = "for"; //$NON-NLS-1$
public static final String SWITCH = "switch"; //$NON-NLS-1$
public static final String WHILE = "while"; //$NON-NLS-1$
public static final String DEBUGGER = "debugger"; //$NON-NLS-1$
public static final String FUNCTION = "function"; //$NON-NLS-1$
public static final String THIS = "this"; //$NON-NLS-1$
public static final String WITH = "with"; //$NON-NLS-1$
public static final String DEFAULT = "default"; //$NON-NLS-1$
public static final String IF = "if"; //$NON-NLS-1$
public static final String THROW = "throw"; //$NON-NLS-1$
public static final String DELETE = "delete"; //$NON-NLS-1$
public static final String IN = "in"; //$NON-NLS-1$
public static final String TRY = "try"; //$NON-NLS-1$
// ECMAScript future reserved words
public static final String CLASS = "class"; //$NON-NLS-1$
public static final String ENUM = "enum"; //$NON-NLS-1$
public static final String EXTENDS = "extends"; //$NON-NLS-1$
public static final String SUPER = "super"; //$NON-NLS-1$
public static final String EXPORT = "export"; //$NON-NLS-1$
// ECMA Script strict-mode future reserved words
public static final String IMPLEMENTS = "implements"; //$NON-NLS-1$
public static final String LET = "let"; //$NON-NLS-1$
public static final String PRIVATE = "private"; //$NON-NLS-1$
public static final String PUBLIC = "public"; //$NON-NLS-1$
public static final String YIELD = "yield"; //$NON-NLS-1$
public static final String INTERFACE = "interface"; //$NON-NLS-1$
public static final String PACKAGE = "package"; //$NON-NLS-1$
public static final String PROTECTED = "protected"; //$NON-NLS-1$
public static final String STATIC = "static"; //$NON-NLS-1$
/**
* Gets an array containing all of the QML and ECMA Script keywords.
*
* @param strictMode
* Whether or not ECMA Script strict mode is enabled. If <code>true</code>, this adds several reserved keywords to
* the list.
* @return An array of keywords
*/
public static final String[] getKeywords(boolean strictMode) {
if (!strictMode) {
return new String[] { IMPORT, CONST, PROPERTY, READONLY, SIGNAL, AS, ON, TRUE, FALSE, TRANSIENT, SYNCHRONIZED, ABSTRACT,
VOLATILE, NATIVE, GOTO, BYTE, LONG, CHAR, SHORT, FLOAT, BOOLEAN, DOUBLE, INT, LIST, COLOR, REAL, STRING, URL,
VAR, BREAK, DO, INSTANCEOF, TYPEOF, CASE, ELSE, NEW, CATCH, FINALLY, RETURN, VOID, CONTINUE, FOR, SWITCH, WHILE,
DEBUGGER, FUNCTION, THIS, WITH, DEFAULT, IF, THROW, DELETE, IN, TRY, CLASS, ENUM, EXTENDS, SUPER, EXPORT };
} else {
return new String[] { IMPORT, CONST, PROPERTY, READONLY, SIGNAL, AS, ON, TRUE, FALSE, TRANSIENT, SYNCHRONIZED, ABSTRACT,
VOLATILE, NATIVE, GOTO, BYTE, LONG, CHAR, SHORT, FLOAT, BOOLEAN, DOUBLE, INT, LIST, COLOR, REAL, STRING, URL,
VAR, BREAK, DO, INSTANCEOF, TYPEOF, CASE, ELSE, NEW, CATCH, FINALLY, RETURN, VOID, CONTINUE, FOR, SWITCH, WHILE,
DEBUGGER, FUNCTION, THIS, WITH, DEFAULT, IF, THROW, DELETE, IN, TRY, CLASS, ENUM, EXTENDS, SUPER, EXPORT,
IMPLEMENTS, LET, PRIVATE, PUBLIC, YIELD, INTERFACE, PACKAGE, PROTECTED, STATIC };
}
}
}

View file

@ -0,0 +1,46 @@
/*******************************************************************************
* 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.internal.qt.ui.editor;
import org.eclipse.cdt.internal.qt.ui.text.IQMLPartitions;
import org.eclipse.jface.text.rules.EndOfLineRule;
import org.eclipse.jface.text.rules.IPredicateRule;
import org.eclipse.jface.text.rules.IToken;
import org.eclipse.jface.text.rules.MultiLineRule;
import org.eclipse.jface.text.rules.RuleBasedPartitionScanner;
import org.eclipse.jface.text.rules.SingleLineRule;
import org.eclipse.jface.text.rules.Token;
/**
* Partitions a QML document into four distinct sections:
* <ol>
* <li>multi-line comments</li>
* <li>single-line comments</li>
* <li>strings</li>
* <li>anything else that does not fall under the aforementioned categories</li>
* </ol>
*/
public class QMLPartitionScanner extends RuleBasedPartitionScanner implements IQMLPartitions {
public QMLPartitionScanner() {
super();
IToken multiLineComment= new Token(QML_MULTI_LINE_COMMENT);
IToken singleLineComment= new Token(QML_SINGLE_LINE_COMMENT);
IToken string= new Token(QML_STRING);
setPredicateRules(new IPredicateRule[] {
new MultiLineRule("/*", "*/", multiLineComment, (char) 0, true), //$NON-NLS-1$ //$NON-NLS-2$
new EndOfLineRule("//", singleLineComment), //$NON-NLS-1$
new SingleLineRule("\"", "\"", string, '\\', true) //$NON-NLS-2$ //$NON-NLS-1$
});
}
}

View file

@ -0,0 +1,29 @@
/*******************************************************************************
* 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.internal.qt.ui.text;
import org.eclipse.cdt.internal.qt.ui.editor.QMLPartitionScanner;
/**
* Constants for the various partitions created by {@link QMLPartitionScanner}.
*/
public interface IQMLPartitions {
final String QML_PARTITIONING = "___qml_partitioning"; //$NON-NLS-1$
final String QML_SINGLE_LINE_COMMENT = "__qml_single_comment"; //$NON-NLS-1$
final String QML_MULTI_LINE_COMMENT = "__qml_multiline_comment"; //$NON-NLS-1$
final String QML_STRING = "__qml_string"; //$NON-NLS-1$
final String[] ALL_QMLPARTITIONS = {
IQMLPartitions.QML_SINGLE_LINE_COMMENT,
IQMLPartitions.QML_MULTI_LINE_COMMENT,
IQMLPartitions.QML_STRING
};
}

View file

@ -0,0 +1,145 @@
/*******************************************************************************
* 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.internal.qt.ui.text;
import org.eclipse.cdt.internal.qt.ui.editor.QMLEditor;
import org.eclipse.cdt.internal.qt.ui.editor.QMLKeywords;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.TextAttribute;
import org.eclipse.jface.text.presentation.IPresentationReconciler;
import org.eclipse.jface.text.presentation.PresentationReconciler;
import org.eclipse.jface.text.rules.DefaultDamagerRepairer;
import org.eclipse.jface.text.rules.IRule;
import org.eclipse.jface.text.rules.IToken;
import org.eclipse.jface.text.rules.ITokenScanner;
import org.eclipse.jface.text.rules.IWordDetector;
import org.eclipse.jface.text.rules.RuleBasedScanner;
import org.eclipse.jface.text.rules.Token;
import org.eclipse.jface.text.rules.WordRule;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.editors.text.TextSourceViewerConfiguration;
/**
* Performs syntax highlighting for the {@link QMLEditor}.
*/
public class QMLSourceViewerConfiguration extends TextSourceViewerConfiguration {
private static final int TOKEN_DEFAULT = 0;
private static final int TOKEN_MULTI_LINE_COMMENT = 1;
private static final int TOKEN_SINGLE_LINE_COMMENT = 2;
private static final int TOKEN_KEYWORD = 3;
private static final int TOKEN_STRING = 4;
private static final int TOKEN_TASK_TAG = 5;
// Just using Qt Creator defaults-ish for now
// TODO: Add preference page for syntax highlighting
private static final IToken[] allTokens = new IToken[] {
new Token(null),
new Token(new TextAttribute(new Color(Display.getCurrent(), new RGB(0, 155, 200)))),
new Token(new TextAttribute(new Color(Display.getCurrent(), new RGB(0, 155, 200)))),
new Token(new TextAttribute(new Color(Display.getCurrent(), new RGB(155, 155, 0)), null, SWT.BOLD)),
new Token(new TextAttribute(new Color(Display.getCurrent(), new RGB(0, 155, 0)), null, SWT.ITALIC)),
new Token(new TextAttribute(new Color(Display.getCurrent(), new RGB(0, 100, 155)), null, SWT.BOLD))
};
@Override
public IPresentationReconciler getPresentationReconciler(ISourceViewer viewer) {
PresentationReconciler reconciler = new PresentationReconciler();
reconciler.setDocumentPartitioning(IQMLPartitions.QML_PARTITIONING);
DefaultDamagerRepairer dr;
dr = new DefaultDamagerRepairer(createMultiLineCommentTokenScanner());
reconciler.setDamager(dr, IQMLPartitions.QML_MULTI_LINE_COMMENT);
reconciler.setRepairer(dr, IQMLPartitions.QML_MULTI_LINE_COMMENT);
dr = new DefaultDamagerRepairer(createSingleLineCommentTokenScanner());
reconciler.setDamager(dr, IQMLPartitions.QML_SINGLE_LINE_COMMENT);
reconciler.setRepairer(dr, IQMLPartitions.QML_SINGLE_LINE_COMMENT);
dr = new DefaultDamagerRepairer(createStringTokenScanner());
reconciler.setDamager(dr, IQMLPartitions.QML_STRING);
reconciler.setRepairer(dr, IQMLPartitions.QML_STRING);
dr = new DefaultDamagerRepairer(createDefaultTokenScanner());
reconciler.setDamager(dr, IDocument.DEFAULT_CONTENT_TYPE);
reconciler.setRepairer(dr, IDocument.DEFAULT_CONTENT_TYPE);
return reconciler;
}
private ITokenScanner createMultiLineCommentTokenScanner() {
RuleBasedScanner scanner = new RuleBasedScanner();
scanner.setDefaultReturnToken(allTokens[TOKEN_MULTI_LINE_COMMENT]);
scanner.setRules(new IRule[] { createTaskTagRule(allTokens[TOKEN_MULTI_LINE_COMMENT]) });
return scanner;
}
private ITokenScanner createSingleLineCommentTokenScanner() {
RuleBasedScanner scanner = new RuleBasedScanner();
scanner.setDefaultReturnToken(allTokens[TOKEN_SINGLE_LINE_COMMENT]);
scanner.setRules(new IRule[] { createTaskTagRule(allTokens[TOKEN_SINGLE_LINE_COMMENT]) });
return scanner;
}
private ITokenScanner createStringTokenScanner() {
RuleBasedScanner scanner = new RuleBasedScanner();
scanner.setDefaultReturnToken(allTokens[TOKEN_STRING]);
return scanner;
}
private ITokenScanner createDefaultTokenScanner() {
RuleBasedScanner scanner = new RuleBasedScanner();
WordRule wordRule = new WordRule(new IWordDetector() {
@Override
public boolean isWordStart(char c) {
return Character.isJavaIdentifierStart(c);
}
@Override
public boolean isWordPart(char c) {
return Character.isJavaIdentifierPart(c);
}
}, allTokens[TOKEN_DEFAULT]);
// Works decently well for now. However, some keywords like 'color' can also be used as identifiers. Can only fix this with
// semantic highlighting after the parser is completed.
for (String keyword : QMLKeywords.getKeywords(true)) {
wordRule.addWord(keyword, allTokens[TOKEN_KEYWORD]);
}
scanner.setRules(new IRule[] { wordRule });
return scanner;
}
private IRule createTaskTagRule(IToken defaultToken) {
WordRule wordRule = new WordRule(new IWordDetector() {
@Override
public boolean isWordStart(char c) {
return Character.isJavaIdentifierStart(c);
}
@Override
public boolean isWordPart(char c) {
return Character.isJavaIdentifierPart(c);
}
}, defaultToken);
// TODO: Add preference page for task tags
wordRule.addWord("TODO", allTokens[TOKEN_TASK_TAG]); //$NON-NLS-1$
wordRule.addWord("FIXME", allTokens[TOKEN_TASK_TAG]); //$NON-NLS-1$
wordRule.addWord("XXX", allTokens[TOKEN_TASK_TAG]); //$NON-NLS-1$
return wordRule;
}
}