aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMiguel Costa <miguel.costa@qt.io>2018-06-05 15:53:21 +0200
committerMiguel Costa <miguel.costa@qt.io>2018-06-19 13:37:57 +0000
commitc11c7e04c5627f90e8bf31bb180d5b4d2371ceba (patch)
treee4b1f983b73fe8028898bc56b020851a6c2e2ffe
parent39ebe357f1c7f5fb692024e19c25f648bed044bd (diff)
Add configurable QML syntax highlighting
Integrated the parser in Qt Declarative with the Visual Studio editor extensibility classes for the purpose of implementing a QML syntax highlighting that is configurable, and has better performance, accuracy and usability than previous approaches. In particular, this fixes an issue reported in VS 2015 and VS 2017, where it is not possible to configure the formatting of the QML syntax highlighting. Task-number: QTVSADDINBUG-501 Change-Id: I8ed61d7a4dc0cd9eeeebc19a36d02f49b21957ac Reviewed-by: Joerg Bornemann <joerg.bornemann@qt.io>
-rw-r--r--src/config/12.0/qtvstools.targets2
-rw-r--r--src/config/14.0/qtvstools.targets2
-rw-r--r--src/config/15.0/qtvstools.targets2
-rw-r--r--src/qtvstools/QML/Classification/QmlClassificationFormat.cs108
-rw-r--r--src/qtvstools/QML/Classification/QmlErrorClassifier.cs158
-rw-r--r--src/qtvstools/QML/Classification/QmlSyntaxClassifier.cs200
-rw-r--r--src/qtvstools/QML/Classification/QmlTag.cs300
-rw-r--r--src/qtvstools/QML/Parser/QmlParserDiagnostics.cs52
-rw-r--r--src/qtvstools/QML/Parser/QmlParserInterop.cs564
-rw-r--r--src/qtvstools/QML/Syntax/QmlAst.cs218
-rw-r--r--src/qtvstools/QML/Syntax/QmlSyntax.cs265
-rw-r--r--src/qtvstools/QtVsTools.csproj13
12 files changed, 1884 insertions, 0 deletions
diff --git a/src/config/12.0/qtvstools.targets b/src/config/12.0/qtvstools.targets
index 21e69d61..1fd4567c 100644
--- a/src/config/12.0/qtvstools.targets
+++ b/src/config/12.0/qtvstools.targets
@@ -10,6 +10,7 @@
<Reference Include="EnvDTE, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<Reference Include="EnvDTE80, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<Reference Include="Microsoft.VisualStudio.CoreUtility, Version=12.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
+ <Reference Include="Microsoft.VisualStudio.Language.StandardClassification, Version=12.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
<Reference Include="Microsoft.VisualStudio.ExtensionsExplorer.UI, Version=12.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
<Reference Include="Microsoft.VisualStudio.OLE.Interop, Version=7.1.40304.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<Reference Include="Microsoft.VisualStudio.Shell.12.0, Version=12.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
@@ -22,6 +23,7 @@
<Reference Include="Microsoft.VisualStudio.TemplateWizardInterface, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
<Reference Include="Microsoft.VisualStudio.Text.Data, Version=12.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
<Reference Include="Microsoft.VisualStudio.Text.Logic, Version=12.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
+ <Reference Include="Microsoft.VisualStudio.Text.UI, Version=12.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
<Reference Include="Microsoft.VisualStudio.Text.UI.Wpf, Version=12.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
<Reference Include="Microsoft.VisualStudio.VCCodeModel, Version=12.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<Reference Include="Microsoft.VisualStudio.VCProjectEngine, Version=12.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
diff --git a/src/config/14.0/qtvstools.targets b/src/config/14.0/qtvstools.targets
index c0644a04..967bf469 100644
--- a/src/config/14.0/qtvstools.targets
+++ b/src/config/14.0/qtvstools.targets
@@ -10,6 +10,7 @@
<Reference Include="EnvDTE, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<Reference Include="EnvDTE80, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<Reference Include="Microsoft.VisualStudio.CoreUtility, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
+ <Reference Include="Microsoft.VisualStudio.Language.StandardClassification, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
<Reference Include="Microsoft.VisualStudio.ExtensionsExplorer.UI, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
<Reference Include="Microsoft.VisualStudio.OLE.Interop, Version=7.1.40304.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<Reference Include="Microsoft.VisualStudio.Shell.14.0, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
@@ -22,6 +23,7 @@
<Reference Include="Microsoft.VisualStudio.TemplateWizardInterface, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
<Reference Include="Microsoft.VisualStudio.Text.Data, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
<Reference Include="Microsoft.VisualStudio.Text.Logic, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
+ <Reference Include="Microsoft.VisualStudio.Text.UI, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
<Reference Include="Microsoft.VisualStudio.Text.UI.Wpf, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
<Reference Include="Microsoft.VisualStudio.Utilities, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
<Reference Include="Microsoft.VisualStudio.VCCodeModel, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
diff --git a/src/config/15.0/qtvstools.targets b/src/config/15.0/qtvstools.targets
index 0207de71..aab4bb87 100644
--- a/src/config/15.0/qtvstools.targets
+++ b/src/config/15.0/qtvstools.targets
@@ -10,6 +10,7 @@
<Reference Include="EnvDTE, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<Reference Include="EnvDTE80, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<Reference Include="Microsoft.VisualStudio.CoreUtility, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
+ <Reference Include="Microsoft.VisualStudio.Language.StandardClassification, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
<Reference Include="Microsoft.VisualStudio.OLE.Interop, Version=7.1.40304.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<Reference Include="Microsoft.VisualStudio.Shell.15.0, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
<Reference Include="Microsoft.VisualStudio.Shell.Immutable.10.0, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
@@ -23,6 +24,7 @@
<Reference Include="Microsoft.VisualStudio.TemplateWizardInterface, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
<Reference Include="Microsoft.VisualStudio.Text.Data, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
<Reference Include="Microsoft.VisualStudio.Text.Logic, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
+ <Reference Include="Microsoft.VisualStudio.Text.UI, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
<Reference Include="Microsoft.VisualStudio.Text.UI.Wpf, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
<Reference Include="Microsoft.VisualStudio.Utilities, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
<Reference Include="Microsoft.VisualStudio.VCCodeModel, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
diff --git a/src/qtvstools/QML/Classification/QmlClassificationFormat.cs b/src/qtvstools/QML/Classification/QmlClassificationFormat.cs
new file mode 100644
index 00000000..3f33e16c
--- /dev/null
+++ b/src/qtvstools/QML/Classification/QmlClassificationFormat.cs
@@ -0,0 +1,108 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt VS Tools.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/// This file contains the definition of syntax highlighting formats.
+/// These definitions can be modified at run-time by the user.
+
+using System.ComponentModel.Composition;
+using System.Windows.Media;
+using Microsoft.VisualStudio.Text.Classification;
+using Microsoft.VisualStudio.Utilities;
+
+namespace QtVsTools.Qml.Classification
+{
+ [Export(typeof(EditorFormatDefinition))]
+ [ClassificationType(ClassificationTypeNames = QmlTag.Keyword)]
+ [Name(QmlTag.Keyword)]
+ [UserVisible(true)]
+ [Order(Before = Priority.Default)]
+ internal sealed class QmlKeywordFormat : ClassificationFormatDefinition
+ {
+ public QmlKeywordFormat()
+ {
+ DisplayName = "QML Keyword";
+ ForegroundColor = Color.FromRgb(86, 156, 214);
+ }
+ }
+
+ [Export(typeof(EditorFormatDefinition))]
+ [ClassificationType(ClassificationTypeNames = QmlTag.Numeric)]
+ [Name(QmlTag.Numeric)]
+ [UserVisible(true)]
+ [Order(Before = Priority.Default)]
+ internal sealed class QmlNumberFormat : ClassificationFormatDefinition
+ {
+ public QmlNumberFormat()
+ {
+ DisplayName = "QML Number";
+ ForegroundColor = Color.FromRgb(181, 206, 168);
+ }
+ }
+
+ [Export(typeof(EditorFormatDefinition))]
+ [ClassificationType(ClassificationTypeNames = QmlTag.String)]
+ [Name(QmlTag.String)]
+ [UserVisible(true)]
+ [Order(Before = Priority.Default)]
+ internal sealed class QmlStringFormat : ClassificationFormatDefinition
+ {
+ public QmlStringFormat()
+ {
+ DisplayName = "QML String";
+ ForegroundColor = Color.FromRgb(214, 157, 133);
+ }
+ }
+
+ [Export(typeof(EditorFormatDefinition))]
+ [ClassificationType(ClassificationTypeNames = QmlTag.TypeName)]
+ [Name(QmlTag.TypeName)]
+ [UserVisible(true)]
+ [Order(Before = Priority.Default, After = QmlTag.Keyword)]
+ internal sealed class QmlTypeNameFormat : ClassificationFormatDefinition
+ {
+ public QmlTypeNameFormat()
+ {
+ DisplayName = "QML Type Name";
+ ForegroundColor = Color.FromRgb(78, 201, 176);
+ }
+ }
+
+ [Export(typeof(EditorFormatDefinition))]
+ [ClassificationType(ClassificationTypeNames = QmlTag.Binding)]
+ [Name(QmlTag.Binding)]
+ [UserVisible(true)]
+ [Order(Before = Priority.Default, After = QmlTag.Keyword)]
+ internal sealed class QmlBindingFormat : ClassificationFormatDefinition
+ {
+ public QmlBindingFormat()
+ {
+ DisplayName = "QML Binding";
+ ForegroundColor = Color.FromRgb(183, 153, 185);
+ }
+ }
+}
diff --git a/src/qtvstools/QML/Classification/QmlErrorClassifier.cs b/src/qtvstools/QML/Classification/QmlErrorClassifier.cs
new file mode 100644
index 00000000..95e57ccb
--- /dev/null
+++ b/src/qtvstools/QML/Classification/QmlErrorClassifier.cs
@@ -0,0 +1,158 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt VS Tools.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/// Highlighting of syntax errors
+
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.Composition;
+using System.Threading.Tasks;
+using System.Windows.Threading;
+using Microsoft.VisualStudio.Text;
+using Microsoft.VisualStudio.Text.Classification;
+using Microsoft.VisualStudio.Text.Editor;
+using Microsoft.VisualStudio.Text.Tagging;
+using Microsoft.VisualStudio.Utilities;
+
+namespace QtVsTools.Qml.Classification
+{
+ [Export(typeof(IViewTaggerProvider))]
+ [ContentType("qml")]
+ [TagType(typeof(ErrorTag))]
+ internal sealed class QmlErrorClassifierProvider : IViewTaggerProvider
+ {
+ [Import]
+ internal IClassificationTypeRegistryService classificationTypeRegistry = null;
+
+ public ITagger<T> CreateTagger<T>(ITextView textView, ITextBuffer buffer) where T : ITag
+ {
+ return new QmlErrorClassifier(buffer, classificationTypeRegistry) as ITagger<T>;
+ }
+ }
+
+ internal sealed class QmlErrorClassifier : ITagger<ErrorTag>
+ {
+ ITextBuffer buffer;
+ Dispatcher dispatcher;
+ DispatcherTimer timer;
+
+ internal QmlErrorClassifier(ITextBuffer buffer,
+ IClassificationTypeRegistryService typeService)
+ {
+ this.buffer = buffer;
+ QmlClassificationType.InitClassificationTypes(typeService);
+ ParseQML(buffer.CurrentSnapshot);
+ buffer.Changed += Buffer_Changed;
+
+ dispatcher = Dispatcher.CurrentDispatcher;
+ timer = new DispatcherTimer(DispatcherPriority.ApplicationIdle, dispatcher)
+ {
+ Interval = TimeSpan.FromMilliseconds(500)
+ };
+ timer.Tick += Timer_Tick;
+ }
+
+ private void Timer_Tick(object sender, EventArgs e)
+ {
+ timer.Stop();
+ var snapshot = buffer.CurrentSnapshot;
+ AsyncParseQML(snapshot);
+ }
+
+ private void Buffer_Changed(object sender, TextContentChangedEventArgs e)
+ {
+ AsyncParseQML(e.After);
+ timer.Stop();
+ timer.Start();
+ }
+
+ public event EventHandler<SnapshotSpanEventArgs> TagsChanged;
+
+ bool flag = false;
+ List<QmlDiagnosticsTag> tags = new List<QmlDiagnosticsTag>();
+ object syncChanged = new object();
+
+ async void AsyncParseQML(ITextSnapshot snapshot)
+ {
+ if (flag)
+ return;
+ flag = true;
+ await Task.Run(() =>
+ {
+ ParseQML(snapshot);
+ flag = false;
+ var currentVersion = buffer.CurrentSnapshot.Version;
+ if (snapshot.Version.VersionNumber == currentVersion.VersionNumber) {
+ timer.Stop();
+ } else {
+ timer.Start();
+ }
+ });
+ }
+
+ void ParseQML(ITextSnapshot snapshot)
+ {
+ lock (syncChanged) {
+ tags.Clear();
+ var text = snapshot.GetText();
+ using (var parser = Parser.Parse(text)) {
+ if (!parser.ParsedCorrectly) {
+ foreach (var diag in parser.DiagnosticMessages) {
+ tags.Add(new QmlDiagnosticsTag(snapshot, diag));
+ }
+ }
+ }
+ }
+ var span = new SnapshotSpan(buffer.CurrentSnapshot, 0, buffer.CurrentSnapshot.Length);
+ var tagsChangedHandler = TagsChanged;
+ if (tagsChangedHandler != null)
+ tagsChangedHandler.Invoke(this, new SnapshotSpanEventArgs(span));
+ }
+
+ public IEnumerable<ITagSpan<ErrorTag>> GetTags(NormalizedSnapshotSpanCollection spans)
+ {
+ List<QmlDiagnosticsTag> tagsCopy;
+ var snapshot = spans[0].Snapshot;
+ lock (syncChanged) {
+ tagsCopy = new List<QmlDiagnosticsTag>(tags);
+ }
+ foreach (var tag in tagsCopy) {
+ var tagSpan = tag.ToTagSpan(snapshot);
+ if (tagSpan.Span.Length == 0)
+ continue;
+
+ if (!spans.IntersectsWith(new NormalizedSnapshotSpanCollection(tagSpan.Span)))
+ continue;
+
+ yield return
+ new TagSpan<ErrorTag>(tagSpan.Tag.Span.GetSpan(snapshot),
+ new ErrorTag("ERROR"));
+ }
+ }
+ }
+}
diff --git a/src/qtvstools/QML/Classification/QmlSyntaxClassifier.cs b/src/qtvstools/QML/Classification/QmlSyntaxClassifier.cs
new file mode 100644
index 00000000..5277a12e
--- /dev/null
+++ b/src/qtvstools/QML/Classification/QmlSyntaxClassifier.cs
@@ -0,0 +1,200 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt VS Tools.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/// This file implements the actual highlighting of the text according to the
+/// classification of the syntax elements recognized by the QML parser.
+
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.Composition;
+using System.Threading.Tasks;
+using System.Windows.Threading;
+using Microsoft.VisualStudio.Text;
+using Microsoft.VisualStudio.Text.Classification;
+using Microsoft.VisualStudio.Text.Editor;
+using Microsoft.VisualStudio.Text.Tagging;
+using Microsoft.VisualStudio.Utilities;
+
+namespace QtVsTools.Qml.Classification
+{
+ [Export(typeof(IViewTaggerProvider))]
+ [ContentType("qml")]
+ [TagType(typeof(ClassificationTag))]
+ internal sealed class QmlSyntaxClassifierProvider : IViewTaggerProvider
+ {
+ [Export]
+ [Name("qml")]
+ [BaseDefinition("code")]
+ internal static ContentTypeDefinition qmlContentType = null;
+
+ [Export]
+ [FileExtension(".qml")]
+ [ContentType("qml")]
+ internal static FileExtensionToContentTypeDefinition qmlFileType = null;
+
+ [Export]
+ [FileExtension(".qmlproject")]
+ [ContentType("qml")]
+ internal static FileExtensionToContentTypeDefinition qmlprojectFileType = null;
+
+ [Import]
+ internal IClassificationTypeRegistryService classificationTypeRegistry = null;
+
+ public ITagger<T> CreateTagger<T>(ITextView textView, ITextBuffer buffer) where T : ITag
+ {
+ return new QmlSyntaxClassifier(buffer, classificationTypeRegistry) as ITagger<T>;
+ }
+ }
+
+ internal sealed class QmlSyntaxClassifier : ITagger<ClassificationTag>
+ {
+ ITextBuffer buffer;
+ Dispatcher dispatcher;
+ DispatcherTimer timer;
+
+ internal QmlSyntaxClassifier(ITextBuffer buffer,
+ IClassificationTypeRegistryService typeService)
+ {
+ this.buffer = buffer;
+ QmlClassificationType.InitClassificationTypes(typeService);
+ ParseQML(buffer.CurrentSnapshot);
+ buffer.Changed += Buffer_Changed;
+
+ dispatcher = Dispatcher.CurrentDispatcher;
+ timer = new DispatcherTimer(DispatcherPriority.ApplicationIdle, dispatcher)
+ {
+ Interval = TimeSpan.FromMilliseconds(500)
+ };
+ timer.Tick += Timer_Tick;
+ }
+
+ private void Timer_Tick(object sender, EventArgs e)
+ {
+ timer.Stop();
+ var snapshot = buffer.CurrentSnapshot;
+ AsyncParseQML(snapshot);
+ }
+
+ private void Buffer_Changed(object sender, TextContentChangedEventArgs e)
+ {
+ AsyncParseQML(e.After);
+ timer.Stop();
+ timer.Start();
+ }
+
+ public event EventHandler<SnapshotSpanEventArgs> TagsChanged;
+
+ bool flag = false;
+ List<QmlTag> tags = new List<QmlTag>();
+ object syncChanged = new object();
+
+ async void AsyncParseQML(ITextSnapshot snapshot)
+ {
+ if (flag)
+ return;
+ flag = true;
+ await Task.Run(() =>
+ {
+ ParseQML(snapshot);
+ flag = false;
+ var currentVersion = buffer.CurrentSnapshot.Version;
+ if (snapshot.Version.VersionNumber == currentVersion.VersionNumber) {
+ timer.Stop();
+ } else {
+ timer.Start();
+ }
+ });
+ }
+
+ void ParseQML(ITextSnapshot snapshot)
+ {
+ bool parsedCorrectly = true;
+ lock (syncChanged) {
+ tags.Clear();
+ var text = snapshot.GetText();
+ using (var parser = Parser.Parse(text)) {
+ parsedCorrectly = parser.ParsedCorrectly;
+ foreach (var token in parser.Tokens) {
+ if (parsedCorrectly
+ || token.Location.Offset < parser.FirstErrorOffset) {
+ tags.AddRange(QmlTag.GetClassification(snapshot, token));
+ }
+ }
+ foreach (var node in parser.AstNodes) {
+ if (parsedCorrectly
+ || node.FirstSourceLocation.Offset < parser.FirstErrorOffset) {
+ tags.AddRange(QmlTag.GetClassification(snapshot, node));
+ }
+ }
+ }
+ }
+ var tagsChangedHandler = TagsChanged;
+ if (parsedCorrectly) {
+ var span = new SnapshotSpan(buffer.CurrentSnapshot,
+ 0, buffer.CurrentSnapshot.Length);
+ if (tagsChangedHandler != null)
+ tagsChangedHandler.Invoke(this, new SnapshotSpanEventArgs(span));
+ } else {
+ List<QmlTag> tagsCopy;
+ lock (syncChanged) {
+ tagsCopy = new List<QmlTag>(tags);
+ }
+ foreach (var tag in tagsCopy) {
+ var tagSpan = tag.ToTagSpan(snapshot);
+ if (tagsChangedHandler != null)
+ tagsChangedHandler.Invoke(this, new SnapshotSpanEventArgs(tagSpan.Span));
+ }
+ }
+ }
+
+ public IEnumerable<ITagSpan<ClassificationTag>> GetTags(
+ NormalizedSnapshotSpanCollection spans)
+ {
+ List<QmlTag> tagsCopy;
+ var snapshot = spans[0].Snapshot;
+ lock (syncChanged) {
+ tagsCopy = new List<QmlTag>(tags);
+ }
+ foreach (var tag in tagsCopy) {
+ var tagSpan = tag.ToTagSpan(snapshot);
+ if (tagSpan.Span.Length == 0)
+ continue;
+
+ if (!spans.IntersectsWith(new NormalizedSnapshotSpanCollection(tagSpan.Span)))
+ continue;
+
+ if (tag.ClassificationType == null)
+ continue;
+
+ yield return
+ new TagSpan<ClassificationTag>(tagSpan.Tag.Span.GetSpan(snapshot),
+ new ClassificationTag(tag.ClassificationType));
+ }
+ }
+ }
+}
diff --git a/src/qtvstools/QML/Classification/QmlTag.cs b/src/qtvstools/QML/Classification/QmlTag.cs
new file mode 100644
index 00000000..1654722d
--- /dev/null
+++ b/src/qtvstools/QML/Classification/QmlTag.cs
@@ -0,0 +1,300 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt VS Tools.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/// This file contains the classification of the syntax elements recognized by the QML parser.
+
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.Composition;
+using Microsoft.VisualStudio.Language.StandardClassification;
+using Microsoft.VisualStudio.Text;
+using Microsoft.VisualStudio.Text.Classification;
+using Microsoft.VisualStudio.Text.Tagging;
+using Microsoft.VisualStudio.Utilities;
+
+namespace QtVsTools.Qml.Classification
+{
+ using Syntax;
+ using VisualStudio.Text.Extensions;
+
+ /// <summary>
+ /// Represents the classification of a QML syntax element
+ /// </summary>
+ public class QmlTag : ITag
+ {
+ public const string Keyword = "keyword.qml";
+ public const string Numeric = "numeric.qml";
+ public const string String = "string.qml";
+ public const string Comment = "comment.qml";
+ public const string TypeName = "typename.qml";
+ public const string Binding = "binding.qml";
+
+ public SyntaxElement SyntaxElement { get; private set; }
+ public SourceLocation SourceLocation { get; private set; }
+ public ITrackingSpan Span { get; private set; }
+ public IClassificationType ClassificationType { get; private set; }
+
+ private QmlTag(ITextSnapshot snapshot, SourceLocation location)
+ {
+ SourceLocation = location;
+ Span = snapshot.CreateTrackingSpan(
+ location.Offset, location.Length, SpanTrackingMode.EdgeExclusive);
+ }
+
+ public QmlTag(
+ ITextSnapshot snapshot,
+ SyntaxElement element,
+ string classificationType,
+ SourceLocation location)
+ : this(snapshot, location)
+ {
+ SyntaxElement = element;
+ ClassificationType = QmlClassificationType.Get(classificationType);
+ }
+
+ public ITagSpan<QmlTag> ToTagSpan(ITextSnapshot snapshot)
+ {
+ return new TagSpan<QmlTag>(Span.GetSpan(snapshot), this);
+ }
+
+ static QmlTag GetClassificationTag(
+ ITextSnapshot snapshot,
+ AstNode parentNode,
+ string classificationType,
+ UiQualifiedId qualifiedId)
+ {
+ var firstName = qualifiedId.IdentifierToken;
+ var lastName = qualifiedId.IdentifierToken;
+ while (qualifiedId.Next != null) {
+ qualifiedId = qualifiedId.Next;
+ lastName = qualifiedId.IdentifierToken;
+ }
+ var fullNameLocation = new SourceLocation
+ {
+ Offset = firstName.Offset,
+ Length = lastName.Offset + lastName.Length - firstName.Offset
+ };
+
+ return new QmlTag(snapshot, parentNode, classificationType, fullNameLocation);
+ }
+
+ public static readonly HashSet<string> QmlBasicTypes = new HashSet<string> {
+ "bool", "double", "enumeration", "int",
+ "list", "real", "string", "url", "var",
+ "date", "point", "rect", "size", "alias"
+ };
+
+ public static IEnumerable<QmlTag> GetClassification(
+ ITextSnapshot snapshot,
+ SyntaxElement element)
+ {
+ var tags = new List<QmlTag>();
+
+ if (element is KeywordToken) {
+ var token = element as KeywordToken;
+ tags.Add(new QmlTag(snapshot, token, Keyword, token.Location));
+
+ } else if (element is NumberToken) {
+ var token = element as NumberToken;
+ tags.Add(new QmlTag(snapshot, token, Numeric, token.Location));
+
+ } else if (element is StringToken) {
+ var token = element as StringToken;
+ tags.Add(new QmlTag(snapshot, token, String, token.Location));
+
+ } else if (element is CommentToken) {
+ var token = element as CommentToken;
+ // QML parser does not report the initial/final tokens of comments
+ var commentStart = snapshot.GetText(token.Location.Offset - 2, 2);
+ var commentLocation = token.Location;
+ if (commentStart == "//") {
+ commentLocation.Offset -= 2;
+ commentLocation.Length += 2;
+ } else {
+ commentLocation.Offset -= 2;
+ commentLocation.Length += 4;
+ }
+ tags.Add(new QmlTag(snapshot, token, Comment, commentLocation));
+
+ } else if (element is UiImport) {
+ var node = element as UiImport;
+ if (node.ImportIdToken.Length > 0)
+ tags.Add(new QmlTag(snapshot, node, TypeName, node.ImportIdToken));
+
+ } else if (element is UiObjectDefinition) {
+ var node = element as UiObjectDefinition;
+ if (node.QualifiedTypeNameId != null) {
+ var name = snapshot.GetText(node.QualifiedTypeNameId.IdentifierToken);
+ // an UiObjectDefinition may be used to group property bindings
+ // think anchors { ... }
+ bool isGroupedBinding = !string.IsNullOrEmpty(name) && char.IsLower(name[0]);
+ if (!isGroupedBinding) {
+ tags.Add(GetClassificationTag(
+ snapshot, node, TypeName, node.QualifiedTypeNameId));
+ } else {
+ tags.Add(GetClassificationTag(
+ snapshot, node, Binding, node.QualifiedTypeNameId));
+ }
+ }
+
+ } else if (element is UiObjectBinding) {
+ var node = element as UiObjectBinding;
+ if (node.QualifiedId != null) {
+ tags.Add(GetClassificationTag(
+ snapshot, node, Binding, node.QualifiedId));
+ }
+ if (node.QualifiedTypeNameId != null) {
+ tags.Add(GetClassificationTag(
+ snapshot, node, TypeName, node.QualifiedTypeNameId));
+ }
+
+ } else if (element is UiScriptBinding) {
+ var node = element as UiScriptBinding;
+ var qualifiedId = node.QualifiedId;
+ while (qualifiedId != null) {
+ tags.Add(GetClassificationTag(snapshot, node, Binding, qualifiedId));
+ qualifiedId = qualifiedId.Next;
+ }
+
+ } else if (element is UiArrayBinding) {
+ var node = element as UiArrayBinding;
+ var qualifiedId = node.QualifiedId;
+ while (qualifiedId != null) {
+ tags.Add(GetClassificationTag(snapshot, node, Binding, qualifiedId));
+ qualifiedId = qualifiedId.Next;
+ }
+
+ } else if (element is UiPublicMember) {
+ var node = element as UiPublicMember;
+ if (node.Type == UiPublicMemberType.Property && node.TypeToken.Length > 0) {
+ var typeName = snapshot.GetText(node.TypeToken);
+ if (QmlBasicTypes.Contains(typeName))
+ tags.Add(new QmlTag(snapshot, node, Keyword, node.TypeToken));
+ else
+ tags.Add(new QmlTag(snapshot, node, TypeName, node.TypeToken));
+ }
+ if (node.IdentifierToken.Length > 0)
+ tags.Add(new QmlTag(snapshot, node, Binding, node.IdentifierToken));
+
+ }
+ return tags;
+ }
+ }
+
+ public class QmlDiagnosticsTag : ITag
+ {
+ public DiagnosticMessage DiagnosticMessage { get; private set; }
+ public ITrackingSpan Span { get; private set; }
+ public QmlDiagnosticsTag(ITextSnapshot snapshot, DiagnosticMessage diagnosticMessage)
+ {
+ DiagnosticMessage = diagnosticMessage;
+ Span = snapshot.CreateTrackingSpan(
+ diagnosticMessage.Location.Offset, diagnosticMessage.Location.Length,
+ SpanTrackingMode.EdgeExclusive);
+ }
+ public ITagSpan<QmlDiagnosticsTag> ToTagSpan(ITextSnapshot snapshot)
+ {
+ return new TagSpan<QmlDiagnosticsTag>(Span.GetSpan(snapshot), this);
+ }
+ }
+
+ internal static class QmlClassificationType
+ {
+ [Export(typeof(ClassificationTypeDefinition))]
+ [Name(QmlTag.Keyword)]
+ internal static ClassificationTypeDefinition qmlKeyword = null;
+
+ [Export(typeof(ClassificationTypeDefinition))]
+ [Name(QmlTag.Numeric)]
+ internal static ClassificationTypeDefinition qmlNumber = null;
+
+ [Export(typeof(ClassificationTypeDefinition))]
+ [Name(QmlTag.String)]
+ internal static ClassificationTypeDefinition qmlString = null;
+
+ [Export(typeof(ClassificationTypeDefinition))]
+ [Name(QmlTag.Comment)]
+ internal static ClassificationTypeDefinition qmlComment = null;
+
+ [Export(typeof(ClassificationTypeDefinition))]
+ [Name(QmlTag.TypeName)]
+ internal static ClassificationTypeDefinition qmlTypeName = null;
+
+ [Export(typeof(ClassificationTypeDefinition))]
+ [Name(QmlTag.Binding)]
+ internal static ClassificationTypeDefinition qmlBinding = null;
+
+ public static IDictionary<string, IClassificationType> ClassificationTypes
+ {
+ get; private set;
+ }
+
+ public static void InitClassificationTypes(IClassificationTypeRegistryService typeService)
+ {
+ if (ClassificationTypes != null)
+ return;
+ ClassificationTypes = new Dictionary<string, IClassificationType>
+ {
+ { QmlTag.Keyword, typeService.GetClassificationType(QmlTag.Keyword) },
+ { QmlTag.Numeric, typeService.GetClassificationType(QmlTag.Numeric) },
+ { QmlTag.String, typeService.GetClassificationType(QmlTag.String) },
+ { QmlTag.TypeName, typeService.GetClassificationType(QmlTag.TypeName) },
+ { QmlTag.Binding, typeService.GetClassificationType(QmlTag.Binding) },
+
+ // QML comments are mapped to the Visual Studio pre-defined comment classification
+ { QmlTag.Comment,
+ typeService.GetClassificationType(PredefinedClassificationTypeNames.Comment) }
+ };
+ }
+ public static IClassificationType Get(string classificationType)
+ {
+ if (ClassificationTypes == null)
+ return null;
+ return ClassificationTypes[classificationType];
+ }
+ }
+
+ namespace VisualStudio.Text.Extensions
+ {
+ public static class TextSnapshotExtensions
+ {
+ public static string GetText(this ITextSnapshot _this, SourceLocation sourceLocation)
+ {
+ if (sourceLocation.Length == 0)
+ return string.Empty;
+ if (_this.Length < sourceLocation.Offset + sourceLocation.Length)
+ return string.Empty;
+ try {
+ return _this.GetText(sourceLocation.Offset, sourceLocation.Length);
+ } catch (Exception) {
+ return string.Empty;
+ }
+ }
+ }
+ }
+}
diff --git a/src/qtvstools/QML/Parser/QmlParserDiagnostics.cs b/src/qtvstools/QML/Parser/QmlParserDiagnostics.cs
new file mode 100644
index 00000000..f9b52d5d
--- /dev/null
+++ b/src/qtvstools/QML/Parser/QmlParserDiagnostics.cs
@@ -0,0 +1,52 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt VS Tools.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+namespace QtVsTools.Qml
+{
+ using Syntax;
+
+ public enum DiagnosticMessageKind { Warning, Error }
+
+ /// <summary>
+ /// Represents a syntax error issued by the QML parser
+ /// </summary>
+ public class DiagnosticMessage
+ {
+ public DiagnosticMessageKind Kind { get; set; }
+ public SourceLocation Location { get; set; }
+ public DiagnosticMessage(DiagnosticMessageKind kind, int offset, int length)
+ {
+ Kind = kind;
+ Location = new SourceLocation
+ {
+ Offset = offset,
+ Length = length
+ };
+ }
+ }
+}
diff --git a/src/qtvstools/QML/Parser/QmlParserInterop.cs b/src/qtvstools/QML/Parser/QmlParserInterop.cs
new file mode 100644
index 00000000..b4959843
--- /dev/null
+++ b/src/qtvstools/QML/Parser/QmlParserInterop.cs
@@ -0,0 +1,564 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt VS Tools.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/// This file contains the integration with the Qt Declarative parser.
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using System.Text;
+
+namespace QtVsTools.Qml
+{
+ using Syntax;
+
+ /// <summary>
+ /// Implements the integration with the Qt declarative parser, including:
+ /// * Managed-unmanaged interop with the vsqml DLL;
+ /// * Unmarshaling of syntax element data types (e.g. AST nodes);
+ /// * Visitor role for AST traversal.
+ /// </summary>
+ class Parser : IDisposable
+ {
+ internal static class NativeMethods
+ {
+ [DllImport("vsqml",
+ CallingConvention = CallingConvention.Cdecl,
+ EntryPoint = "qmlGetTokens")]
+ internal static extern bool GetTokens(
+ IntPtr qmlText,
+ int qmlTextLength,
+ ref IntPtr tokens,
+ ref int tokensLength);
+
+ [DllImport("vsqml",
+ CallingConvention = CallingConvention.Cdecl,
+ EntryPoint = "qmlFreeTokens")]
+ internal static extern bool FreeTokens(IntPtr tokens);
+
+ [DllImport("vsqml",
+ CallingConvention = CallingConvention.Cdecl,
+ EntryPoint = "qmlParse")]
+ internal static extern bool Parse(
+ IntPtr qmlText,
+ int qmlTextLength,
+ ref IntPtr parser,
+ ref bool parsedCorrectly,
+ ref IntPtr diagnosticMessages,
+ ref int diagnosticMessagesLength,
+ ref IntPtr comments,
+ ref int commentsLength);
+
+ [DllImport("vsqml",
+ CallingConvention = CallingConvention.Cdecl,
+ EntryPoint = "qmlFreeParser")]
+ internal static extern bool FreeParser(IntPtr parser);
+
+ [DllImport("vsqml",
+ CallingConvention = CallingConvention.Cdecl,
+ EntryPoint = "qmlFreeDiagnosticMessages")]
+ internal static extern bool FreeDiagnosticMessages(IntPtr diagnosticMessages);
+
+ [DllImport("vsqml",
+ CallingConvention = CallingConvention.Cdecl,
+ EntryPoint = "qmlFreeComments")]
+ internal static extern bool FreeComments(IntPtr comments);
+
+ [DllImport("vsqml",
+ CallingConvention = CallingConvention.Cdecl,
+ EntryPoint = "qmlGetAstVisitor")]
+ internal static extern IntPtr GetAstVisitor();
+
+ [DllImport("vsqml",
+ CallingConvention = CallingConvention.Cdecl,
+ EntryPoint = "qmlFreeAstVisitor")]
+ internal static extern bool FreeAstVisitor(IntPtr astVisitor);
+
+ internal delegate bool Callback(
+ IntPtr astVisitor,
+ int nodeKind,
+ IntPtr node,
+ bool beginVisit,
+ IntPtr nodeData,
+ int nodeDataLength);
+
+ [DllImport("vsqml",
+ CallingConvention = CallingConvention.Cdecl,
+ EntryPoint = "qmlSetAstVisitorCallback")]
+ internal static extern bool SetAstVisitorCallback(
+ IntPtr astVisitor,
+ int nodeKindFilter,
+ Callback visitCallback);
+
+ [DllImport("vsqml",
+ CallingConvention = CallingConvention.Cdecl,
+ EntryPoint = "qmlAcceptAstVisitor")]
+ internal static extern bool AcceptAstVisitor(
+ IntPtr parser,
+ IntPtr node,
+ IntPtr astVisitor);
+ }
+
+ /// <summary>
+ /// List of "interesting" AST node types. To optimize the managed-unmanaged interop,
+ /// during AST traversal, only these node types will be reported.
+ /// </summary>
+ static readonly List<AstNodeKind> CallbackFilters =
+ new List<AstNodeKind> {
+ AstNodeKind.UiImport,
+ AstNodeKind.UiQualifiedId,
+ AstNodeKind.UiObjectDefinition,
+ AstNodeKind.UiObjectBinding,
+ AstNodeKind.UiObjectInitializer,
+ AstNodeKind.UiScriptBinding,
+ AstNodeKind.UiArrayBinding,
+ AstNodeKind.UiPublicMember
+ };
+
+ IntPtr qmlTextPtr = IntPtr.Zero;
+ IntPtr qmlParserPtr = IntPtr.Zero;
+
+ List<Token> tokens;
+ public IEnumerable<Token> Tokens
+ {
+ get { return tokens; }
+ }
+
+ List<DiagnosticMessage> diagnosticMessages;
+ public IEnumerable<DiagnosticMessage> DiagnosticMessages
+ {
+ get { return diagnosticMessages; }
+ }
+
+ public int FirstErrorOffset { get; private set; }
+
+ List<AstNode> visitedNodes;
+ public IEnumerable<AstNode> AstNodes { get { return visitedNodes; } }
+
+ public bool ParsedCorrectly { get; private set; }
+
+ Dictionary<IntPtr, AstNode> nodesBytPtr;
+ Dictionary<IntPtr, List<KeyValuePair<AstNode, PropertyInfo>>> pendingDereferences;
+
+ Parser()
+ {
+ tokens = new List<Token>();
+ diagnosticMessages = new List<DiagnosticMessage>();
+ nodesBytPtr = new Dictionary<IntPtr, AstNode>();
+ pendingDereferences = new Dictionary<IntPtr,
+ List<KeyValuePair<AstNode, PropertyInfo>>>();
+
+ visitedNodes = new List<AstNode>();
+ }
+
+ public static Parser Parse(string qmlText)
+ {
+ var parser = new Parser();
+ parser.Work(qmlText);
+ return parser;
+ }
+
+ void Work(string qmlText)
+ {
+ // The Qt Declarative parser ignores CR's. However, the Visual Studio editor does not.
+ // To ensure that offsets are compatible, CR's are replaced with spaces.
+ string qmlTextNormalized = qmlText.Replace('\r', ' ');
+ var qmlTextData = Encoding.UTF8.GetBytes(qmlTextNormalized);
+
+ qmlTextPtr = Marshal.AllocHGlobal(qmlTextData.Length);
+ Marshal.Copy(qmlTextData, 0, qmlTextPtr, qmlTextData.Length);
+
+ IntPtr tokensPtr = IntPtr.Zero;
+ int tokensLength = 0;
+
+ NativeMethods.GetTokens(qmlTextPtr, qmlTextData.Length,
+ ref tokensPtr, ref tokensLength);
+
+ if (tokensPtr != IntPtr.Zero) {
+ var tokensData = new byte[tokensLength];
+ Marshal.Copy(tokensPtr, tokensData, 0, tokensLength);
+
+ using (var rdr = new BinaryReader(new MemoryStream(tokensData))) {
+ while (rdr.BaseStream.Position + (3 * sizeof(int)) <= tokensLength) {
+ int kind = rdr.ReadInt32();
+ int offset = rdr.ReadInt32();
+ int length = rdr.ReadInt32();
+ tokens.Add(Token.Create((TokenKind)kind, offset, length));
+ }
+ }
+ NativeMethods.FreeTokens(tokensPtr);
+ }
+
+ bool parsedCorrectly = false;
+ IntPtr diagnosticMessagesPtr = IntPtr.Zero;
+ int diagnosticMessagesLength = 0;
+ IntPtr commentsPtr = IntPtr.Zero;
+ int commentsLength = 0;
+
+ NativeMethods.Parse(qmlTextPtr, qmlTextData.Length,
+ ref qmlParserPtr, ref parsedCorrectly,
+ ref diagnosticMessagesPtr, ref diagnosticMessagesLength,
+ ref commentsPtr, ref commentsLength);
+
+ ParsedCorrectly = parsedCorrectly;
+
+ if (diagnosticMessagesPtr != IntPtr.Zero) {
+ var diagnosticMessagesData = new byte[diagnosticMessagesLength];
+ Marshal.Copy(
+ diagnosticMessagesPtr,
+ diagnosticMessagesData, 0, diagnosticMessagesLength);
+
+ FirstErrorOffset = qmlTextNormalized.Length + 1;
+ using (var rdr = new BinaryReader(new MemoryStream(diagnosticMessagesData))) {
+ while (rdr.BaseStream.Position + (3 * sizeof(int)) <= diagnosticMessagesLength)
+ {
+ var kind = (DiagnosticMessageKind)rdr.ReadInt32();
+ int offset = rdr.ReadInt32();
+ int length = rdr.ReadInt32();
+ diagnosticMessages.Add(new DiagnosticMessage(kind, offset, length));
+ if (kind == DiagnosticMessageKind.Error && offset < FirstErrorOffset)
+ FirstErrorOffset = offset;
+ }
+ }
+ NativeMethods.FreeDiagnosticMessages(diagnosticMessagesPtr);
+ }
+
+ if (commentsPtr != IntPtr.Zero) {
+ var commentsData = new byte[commentsLength];
+ Marshal.Copy(commentsPtr, commentsData, 0, commentsLength);
+
+ using (var rdr = new BinaryReader(new MemoryStream(commentsData))) {
+ while (rdr.BaseStream.Position + (2 * sizeof(int)) <= commentsLength) {
+ int offset = rdr.ReadInt32();
+ int length = rdr.ReadInt32();
+ tokens.Add(Token.Create(TokenKind.T_COMMENT, offset, length));
+ }
+ }
+ NativeMethods.FreeComments(commentsPtr);
+ }
+
+ var astVisitor = NativeMethods.GetAstVisitor();
+ var callback = new NativeMethods.Callback(VisitorCallback);
+
+ foreach (var callbackFilter in CallbackFilters)
+ NativeMethods.SetAstVisitorCallback(astVisitor, (int)callbackFilter, callback);
+
+ NativeMethods.AcceptAstVisitor(qmlParserPtr, IntPtr.Zero, astVisitor);
+
+ while (pendingDereferences.Count > 0) {
+ var deref = pendingDereferences.First();
+ NativeMethods.AcceptAstVisitor(qmlParserPtr, deref.Key, astVisitor);
+ pendingDereferences.Remove(deref.Key);
+ }
+
+ GC.KeepAlive(callback);
+ NativeMethods.FreeAstVisitor(astVisitor);
+ }
+
+ SourceLocation UnmarshalLocation(BinaryReader nodeData)
+ {
+ try {
+ return new SourceLocation
+ {
+ Offset = nodeData.ReadInt32(),
+ Length = nodeData.ReadInt32(),
+ };
+ } catch (Exception) {
+ return new SourceLocation();
+ }
+ }
+
+ void UnmarshalPointer(BinaryReader nodeData, AstNode node, PropertyInfo nodeProperty)
+ {
+ if (nodeData == null || node == null || nodeProperty == null)
+ return;
+
+ IntPtr ptrRef;
+ try {
+ ptrRef = new IntPtr(nodeData.ReadInt32());
+ } catch (Exception) {
+ return;
+ }
+
+ if (ptrRef == IntPtr.Zero)
+ return;
+
+ AstNode nodeRef;
+ if (nodesBytPtr.TryGetValue(ptrRef, out nodeRef)) {
+ nodeProperty.SetValue(node, nodeRef);
+ } else {
+ List<KeyValuePair<AstNode, PropertyInfo>> pendingRefList;
+ if (!pendingDereferences.TryGetValue(ptrRef, out pendingRefList)) {
+ pendingDereferences[ptrRef] = pendingRefList =
+ new List<KeyValuePair<AstNode, PropertyInfo>>();
+ }
+ pendingRefList.Add(new KeyValuePair<AstNode, PropertyInfo>(node, nodeProperty));
+ }
+ }
+
+ void UnmarshalNode(BinaryReader nodeData, AstNode node)
+ {
+ node.FirstSourceLocation = UnmarshalLocation(nodeData);
+ node.LastSourceLocation = UnmarshalLocation(nodeData);
+ }
+
+ AstNode UnmarshalNode(BinaryReader nodeData, AstNodeKind nodeKind)
+ {
+ var node = new AstNode(nodeKind);
+ UnmarshalNode(nodeData, node);
+ return node;
+ }
+
+ UiImport UnmarshalUiImport(BinaryReader nodeData)
+ {
+ var node = new UiImport();
+ UnmarshalNode(nodeData, node);
+ node.ImportToken = UnmarshalLocation(nodeData);
+ node.FileNameToken = UnmarshalLocation(nodeData);
+ node.VersionToken = UnmarshalLocation(nodeData);
+ node.AsToken = UnmarshalLocation(nodeData);
+ node.ImportIdToken = UnmarshalLocation(nodeData);
+ node.SemicolonToken = UnmarshalLocation(nodeData);
+ return node;
+ }
+
+ UiQualifiedId UnmarshalUiQualifiedId(BinaryReader nodeData)
+ {
+ var node = new UiQualifiedId();
+ UnmarshalNode(nodeData, node);
+ UnmarshalPointer(nodeData, node, GetPropertyRef(() => node.Next));
+ node.IdentifierToken = UnmarshalLocation(nodeData);
+ return node;
+ }
+
+ UiObjectDefinition UnmarshalUiObjectDefinition(BinaryReader nodeData)
+ {
+ var node = new UiObjectDefinition();
+ UnmarshalNode(nodeData, node);
+ UnmarshalPointer(nodeData, node, GetPropertyRef(() => node.QualifiedTypeNameId));
+ UnmarshalPointer(nodeData, node, GetPropertyRef(() => node.Initializer));
+ return node;
+ }
+
+ UiObjectBinding UnmarshalUiObjectBinding(BinaryReader nodeData)
+ {
+ var node = new UiObjectBinding();
+ UnmarshalNode(nodeData, node);
+ UnmarshalPointer(nodeData, node, GetPropertyRef(() => node.QualifiedId));
+ UnmarshalPointer(nodeData, node, GetPropertyRef(() => node.QualifiedTypeNameId));
+ UnmarshalPointer(nodeData, node, GetPropertyRef(() => node.Initializer));
+ node.ColonToken = UnmarshalLocation(nodeData);
+ return node;
+ }
+
+ UiScriptBinding UnmarshalUiScriptBinding(BinaryReader nodeData)
+ {
+ var node = new UiScriptBinding();
+ UnmarshalNode(nodeData, node);
+ UnmarshalPointer(nodeData, node, GetPropertyRef(() => node.QualifiedId));
+ UnmarshalPointer(nodeData, node, GetPropertyRef(() => node.Statement));
+ node.ColonToken = UnmarshalLocation(nodeData);
+ return node;
+ }
+
+ UiArrayBinding UnmarshalUiArrayBinding(BinaryReader nodeData)
+ {
+ var node = new UiArrayBinding();
+ UnmarshalNode(nodeData, node);
+ UnmarshalPointer(nodeData, node, GetPropertyRef(() => node.QualifiedId));
+ UnmarshalPointer(nodeData, node, GetPropertyRef(() => node.Members));
+ node.ColonToken = UnmarshalLocation(nodeData);
+ node.LBracketToken = UnmarshalLocation(nodeData);
+ node.RBracketToken = UnmarshalLocation(nodeData);
+ return node;
+ }
+
+ UiPublicMember UnmarshalUiPublicMember(BinaryReader nodeData)
+ {
+ var node = new UiPublicMember();
+ UnmarshalNode(nodeData, node);
+ node.Type = (UiPublicMemberType)nodeData.ReadInt32();
+ UnmarshalPointer(nodeData, node, GetPropertyRef(() => node.MemberType));
+ UnmarshalPointer(nodeData, node, GetPropertyRef(() => node.Statement));
+ UnmarshalPointer(nodeData, node, GetPropertyRef(() => node.Binding));
+ node.IsDefaultMember = (nodeData.ReadInt32() != 0);
+ node.IsReadonlyMember = (nodeData.ReadInt32() != 0);
+ UnmarshalPointer(nodeData, node, GetPropertyRef(() => node.Parameters));
+ node.DefaultToken = UnmarshalLocation(nodeData);
+ node.ReadonlyToken = UnmarshalLocation(nodeData);
+ node.PropertyToken = UnmarshalLocation(nodeData);
+ node.TypeModifierToken = UnmarshalLocation(nodeData);
+ node.TypeToken = UnmarshalLocation(nodeData);
+ node.IdentifierToken = UnmarshalLocation(nodeData);
+ node.ColonToken = UnmarshalLocation(nodeData);
+ node.SemicolonToken = UnmarshalLocation(nodeData);
+ return node;
+ }
+
+ /// <summary>
+ /// This delegate method is called during the AST traversal when entering/exiting a node.
+ /// </summary>
+ /// <param name="astVisitor">Unmanaged AST visitor object ref</param>
+ /// <param name="nodeKind">Type of node being visited</param>
+ /// <param name="nodePtr">Node object ref</param>
+ /// <param name="beginVisit">"true" when entering node, "false" when exiting</param>
+ /// <param name="nodeDataPtr">Serialized content of AST node</param>
+ /// <param name="nodeDataLength">Length in bytes of serialized content</param>
+ /// <returns></returns>
+ bool VisitorCallback(
+ IntPtr astVisitor,
+ int nodeKind,
+ IntPtr nodePtr,
+ bool beginVisit,
+ IntPtr nodeDataPtr,
+ int nodeDataLength)
+ {
+ if (!beginVisit)
+ return true;
+
+ AstNode node = null;
+ var nodeData = new byte[nodeDataLength];
+ Marshal.Copy(nodeDataPtr, nodeData, 0, nodeDataLength);
+ using (var rdr = new BinaryReader(new MemoryStream(nodeData))) {
+ switch ((AstNodeKind)nodeKind) {
+ case AstNodeKind.UiImport:
+ node = UnmarshalUiImport(rdr);
+ break;
+ case AstNodeKind.UiQualifiedId:
+ node = UnmarshalUiQualifiedId(rdr);
+ break;
+ case AstNodeKind.UiObjectDefinition:
+ node = UnmarshalUiObjectDefinition(rdr);
+ break;
+ case AstNodeKind.UiObjectBinding:
+ node = UnmarshalUiObjectBinding(rdr);
+ break;
+ case AstNodeKind.UiScriptBinding:
+ node = UnmarshalUiScriptBinding(rdr);
+ break;
+ case AstNodeKind.UiArrayBinding:
+ node = UnmarshalUiArrayBinding(rdr);
+ break;
+ case AstNodeKind.UiPublicMember:
+ node = UnmarshalUiPublicMember(rdr);
+ break;
+ default:
+ node = UnmarshalNode(rdr, (AstNodeKind)nodeKind);
+ break;
+ }
+ }
+ if (node == null)
+ return true;
+
+ visitedNodes.Add(node);
+ nodesBytPtr[nodePtr] = node;
+
+ List<KeyValuePair<AstNode, PropertyInfo>> derefs;
+ if (pendingDereferences.TryGetValue(nodePtr, out derefs)) {
+ foreach (var deref in derefs) {
+ try {
+ deref.Value.SetValue(deref.Key, node);
+ } catch (Exception) { }
+ }
+ pendingDereferences.Remove(nodePtr);
+ }
+
+ return true;
+ }
+
+ void FreeManaged()
+ {
+ }
+
+ void FreeUnmanaged()
+ {
+ if (qmlParserPtr != IntPtr.Zero) {
+ NativeMethods.FreeParser(qmlParserPtr);
+ qmlParserPtr = IntPtr.Zero;
+ }
+ if (qmlTextPtr != IntPtr.Zero) {
+ Marshal.FreeHGlobal(qmlTextPtr);
+ qmlTextPtr = IntPtr.Zero;
+ }
+ }
+
+ #region IDisposable
+ bool disposed = false;
+
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (disposed)
+ return;
+
+ if (disposing)
+ FreeManaged();
+
+ FreeUnmanaged();
+
+ disposed = true;
+ }
+
+ ~Parser()
+ {
+ Dispose(false);
+ }
+ #endregion
+
+ /// <summary>
+ /// Get a reference to a static or instance class member from a member access lambda.
+ /// Adapted from:
+ /// https://stackoverflow.com/questions/2820660/get-name-of-property-as-a-string
+ /// </summary>
+ /// <param name="memberLambda">
+ /// Lambda expression of the form: '() => Class.Member'or '() => object.Member'
+ /// </param>
+ /// <returns>Reference to the class member</returns>
+ public static MemberInfo GetMemberRef<T>(Expression<Func<T>> memberLambda)
+ {
+ var me = memberLambda.Body as MemberExpression;
+ if (me == null)
+ return null;
+ return me.Member;
+ }
+
+ public static PropertyInfo GetPropertyRef<T>(Expression<Func<T>> memberLambda)
+ {
+ return GetMemberRef(memberLambda) as PropertyInfo;
+ }
+ }
+}
diff --git a/src/qtvstools/QML/Syntax/QmlAst.cs b/src/qtvstools/QML/Syntax/QmlAst.cs
new file mode 100644
index 00000000..08c00041
--- /dev/null
+++ b/src/qtvstools/QML/Syntax/QmlAst.cs
@@ -0,0 +1,218 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt VS Tools.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/// The data types in this file represent nodes in the QML abstract sytax tree (AST).
+/// They correspond to classes defined in qqmljsast_p.h
+
+namespace QtVsTools.Qml.Syntax
+{
+ public enum AstNodeKind
+ {
+ #region Copied from qqmljsast_p.h
+ Undefined,
+
+ ArgumentList,
+ ArrayLiteral,
+ ArrayMemberExpression,
+ BinaryExpression,
+ Block,
+ BreakStatement,
+ CallExpression,
+ CaseBlock,
+ CaseClause,
+ CaseClauses,
+ Catch,
+ ConditionalExpression,
+ ContinueStatement,
+ DebuggerStatement,
+ DefaultClause,
+ DeleteExpression,
+ DoWhileStatement,
+ ElementList,
+ Elision,
+ EmptyStatement,
+ Expression,
+ ExpressionStatement,
+ FalseLiteral,
+ FieldMemberExpression,
+ Finally,
+ ForEachStatement,
+ ForStatement,
+ FormalParameterList,
+ FunctionBody,
+ FunctionDeclaration,
+ FunctionExpression,
+ FunctionSourceElement,
+ IdentifierExpression,
+ IdentifierPropertyName,
+ IfStatement,
+ LabelledStatement,
+ LocalForEachStatement,
+ LocalForStatement,
+ NewExpression,
+ NewMemberExpression,
+ NotExpression,
+ NullExpression,
+ NumericLiteral,
+ NumericLiteralPropertyName,
+ ObjectLiteral,
+ PostDecrementExpression,
+ PostIncrementExpression,
+ PreDecrementExpression,
+ PreIncrementExpression,
+ Program,
+ PropertyAssignmentList,
+ PropertyGetterSetter,
+ PropertyName,
+ PropertyNameAndValue,
+ RegExpLiteral,
+ ReturnStatement,
+ SourceElement,
+ SourceElements,
+ StatementList,
+ StatementSourceElement,
+ StringLiteral,
+ StringLiteralPropertyName,
+ SwitchStatement,
+ ThisExpression,
+ ThrowStatement,
+ TildeExpression,
+ TrueLiteral,
+ TryStatement,
+ TypeOfExpression,
+ UnaryMinusExpression,
+ UnaryPlusExpression,
+ VariableDeclaration,
+ VariableDeclarationList,
+ VariableStatement,
+ VoidExpression,
+ WhileStatement,
+ WithStatement,
+ NestedExpression,
+
+ UiArrayBinding,
+ UiImport,
+ UiObjectBinding,
+ UiObjectDefinition,
+ UiObjectInitializer,
+ UiObjectMemberList,
+ UiArrayMemberList,
+ UiPragma,
+ UiProgram,
+ UiParameterList,
+ UiPublicMember,
+ UiQualifiedId,
+ UiQualifiedPragmaId,
+ UiScriptBinding,
+ UiSourceElement,
+ UiHeaderItemList,
+ UiEnumDeclaration,
+ UiEnumMemberList
+ #endregion
+ }
+
+ public class AstNode : SyntaxElement
+ {
+ public AstNodeKind Kind { get; private set; }
+ public AstNode(AstNodeKind kind) { Kind = kind; }
+ public SourceLocation FirstSourceLocation { get; set; }
+ public SourceLocation LastSourceLocation { get; set; }
+ }
+
+ public class UiImport : AstNode
+ {
+ public UiImport() : base(AstNodeKind.UiImport) { }
+ public SourceLocation ImportToken { get; set; }
+ public SourceLocation FileNameToken { get; set; }
+ public SourceLocation VersionToken { get; set; }
+ public SourceLocation AsToken { get; set; }
+ public SourceLocation ImportIdToken { get; set; }
+ public SourceLocation SemicolonToken { get; set; }
+ }
+
+ public class UiQualifiedId : AstNode
+ {
+ public UiQualifiedId() : base(AstNodeKind.UiQualifiedId) { }
+ public SourceLocation IdentifierToken { get; set; }
+ public UiQualifiedId Next { get; set; }
+ }
+
+ public class UiObjectDefinition : AstNode
+ {
+ public UiObjectDefinition() : base(AstNodeKind.UiObjectDefinition) { }
+ public UiQualifiedId QualifiedTypeNameId { get; set; }
+ public AstNode /*UiObjectInitializer*/ Initializer { get; set; }
+ }
+
+ public class UiObjectBinding : AstNode
+ {
+ public UiObjectBinding() : base(AstNodeKind.UiObjectBinding) { }
+ public UiQualifiedId QualifiedId { get; set; }
+ public UiQualifiedId QualifiedTypeNameId { get; set; }
+ public AstNode /*UiObjectInitializer*/ Initializer { get; set; }
+ public SourceLocation ColonToken { get; set; }
+ }
+
+ public class UiScriptBinding : AstNode {
+ public UiScriptBinding() : base(AstNodeKind.UiScriptBinding) { }
+ public UiQualifiedId QualifiedId { get; set; }
+ public AstNode /*Statement*/ Statement { get; set; }
+ public SourceLocation ColonToken { get; set; }
+ }
+
+ public class UiArrayBinding : AstNode {
+ public UiArrayBinding() : base(AstNodeKind.UiArrayBinding) { }
+ public UiQualifiedId QualifiedId { get; set; }
+ public AstNode /*UiArrayMemberList*/ Members { get; set; }
+ public SourceLocation ColonToken { get; set; }
+ public SourceLocation LBracketToken { get; set; }
+ public SourceLocation RBracketToken { get; set; }
+ }
+
+ public enum UiPublicMemberType { Signal, Property };
+
+ public class UiPublicMember : AstNode {
+ public UiPublicMember() : base(AstNodeKind.UiPublicMember) { }
+ public UiPublicMemberType Type { get; set; }
+ public UiQualifiedId MemberType { get; set; }
+ public AstNode /*Statement*/ Statement { get; set; }
+ public AstNode /*UiObjectMember*/ Binding { get; set; }
+ public bool IsDefaultMember { get; set; }
+ public bool IsReadonlyMember { get; set; }
+ public AstNode /*UiParameterList*/ Parameters { get; set; }
+ public SourceLocation DefaultToken { get; set; }
+ public SourceLocation ReadonlyToken { get; set; }
+ public SourceLocation PropertyToken { get; set; }
+ public SourceLocation TypeModifierToken { get; set; }
+ public SourceLocation TypeToken { get; set; }
+ public SourceLocation IdentifierToken { get; set; }
+ public SourceLocation ColonToken { get; set; }
+ public SourceLocation SemicolonToken { get; set; }
+ }
+
+}
diff --git a/src/qtvstools/QML/Syntax/QmlSyntax.cs b/src/qtvstools/QML/Syntax/QmlSyntax.cs
new file mode 100644
index 00000000..90f4b89d
--- /dev/null
+++ b/src/qtvstools/QML/Syntax/QmlSyntax.cs
@@ -0,0 +1,265 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the Qt VS Tools.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/// This file contains definitions of basic QML syntax elements.
+
+namespace QtVsTools.Qml.Syntax
+{
+ /// <summary>
+ /// Reference to a location in the source code parsed by the QML parser.
+ /// Maps one-to-one with the Span concept in the Visual Studio SDK.
+ /// </summary>
+ public struct SourceLocation
+ {
+ public int Offset;
+ public int Length;
+ }
+
+ /// <summary>
+ /// Token constants from the Qt Declarative parser.
+ /// </summary>
+ public enum TokenKind
+ {
+ #region Copied from qqmljsgrammar_p.h
+ EOF_SYMBOL = 0,
+ REDUCE_HERE = 107,
+ SHIFT_THERE = 106,
+ T_AND = 1,
+ T_AND_AND = 2,
+ T_AND_EQ = 3,
+ T_AS = 95,
+ T_AUTOMATIC_SEMICOLON = 62,
+ T_BREAK = 4,
+ T_CASE = 5,
+ T_CATCH = 6,
+ T_COLON = 7,
+ T_COMMA = 8,
+ T_COMMENT = 89,
+ T_COMPATIBILITY_SEMICOLON = 90,
+ T_CONST = 84,
+ T_CONTINUE = 9,
+ T_DEBUGGER = 86,
+ T_DEFAULT = 10,
+ T_DELETE = 11,
+ T_DIVIDE_ = 12,
+ T_DIVIDE_EQ = 13,
+ T_DO = 14,
+ T_DOT = 15,
+ T_ELSE = 16,
+ T_ENUM = 91,
+ T_EQ = 17,
+ T_EQ_EQ = 18,
+ T_EQ_EQ_EQ = 19,
+ T_ERROR = 99,
+ T_FALSE = 83,
+ T_FEED_JS_EXPRESSION = 103,
+ T_FEED_JS_PROGRAM = 105,
+ T_FEED_JS_SOURCE_ELEMENT = 104,
+ T_FEED_JS_STATEMENT = 102,
+ T_FEED_UI_OBJECT_MEMBER = 101,
+ T_FEED_UI_PROGRAM = 100,
+ T_FINALLY = 20,
+ T_FOR = 21,
+ T_FUNCTION = 22,
+ T_GE = 23,
+ T_GET = 97,
+ T_GT = 24,
+ T_GT_GT = 25,
+ T_GT_GT_EQ = 26,
+ T_GT_GT_GT = 27,
+ T_GT_GT_GT_EQ = 28,
+ T_IDENTIFIER = 29,
+ T_IF = 30,
+ T_IMPORT = 93,
+ T_IN = 31,
+ T_INSTANCEOF = 32,
+ T_LBRACE = 33,
+ T_LBRACKET = 34,
+ T_LE = 35,
+ T_LET = 85,
+ T_LPAREN = 36,
+ T_LT = 37,
+ T_LT_LT = 38,
+ T_LT_LT_EQ = 39,
+ T_MINUS = 40,
+ T_MINUS_EQ = 41,
+ T_MINUS_MINUS = 42,
+ T_MULTILINE_STRING_LITERAL = 88,
+ T_NEW = 43,
+ T_NOT = 44,
+ T_NOT_EQ = 45,
+ T_NOT_EQ_EQ = 46,
+ T_NULL = 81,
+ T_NUMERIC_LITERAL = 47,
+ T_ON = 96,
+ T_OR = 48,
+ T_OR_EQ = 49,
+ T_OR_OR = 50,
+ T_PLUS = 51,
+ T_PLUS_EQ = 52,
+ T_PLUS_PLUS = 53,
+ T_PRAGMA = 94,
+ T_PROPERTY = 66,
+ T_PUBLIC = 92,
+ T_QUESTION = 54,
+ T_RBRACE = 55,
+ T_RBRACKET = 56,
+ T_READONLY = 68,
+ T_REMAINDER = 57,
+ T_REMAINDER_EQ = 58,
+ T_RESERVED_WORD = 87,
+ T_RETURN = 59,
+ T_RPAREN = 60,
+ T_SEMICOLON = 61,
+ T_SET = 98,
+ T_SIGNAL = 67,
+ T_STAR = 63,
+ T_STAR_EQ = 64,
+ T_STRING_LITERAL = 65,
+ T_SWITCH = 69,
+ T_THIS = 70,
+ T_THROW = 71,
+ T_TILDE = 72,
+ T_TRUE = 82,
+ T_TRY = 73,
+ T_TYPEOF = 74,
+ T_VAR = 75,
+ T_VOID = 76,
+ T_WHILE = 77,
+ T_WITH = 78,
+ T_XOR = 79,
+ T_XOR_EQ = 80,
+
+ ACCEPT_STATE = 691,
+ RULE_COUNT = 369,
+ STATE_COUNT = 692,
+ TERMINAL_COUNT = 108,
+ NON_TERMINAL_COUNT = 112,
+
+ GOTO_INDEX_OFFSET = 692,
+ GOTO_INFO_OFFSET = 3357,
+ GOTO_CHECK_OFFSET = 3357
+ #endregion
+ }
+
+ public abstract class SyntaxElement { }
+
+ /// <summary>
+ /// Represents a token identified by the QML lexer
+ /// </summary>
+ public class Token : SyntaxElement
+ {
+ public TokenKind Kind { get; private set; }
+ public SourceLocation Location { get; private set; }
+ protected Token() { }
+ public static Token Create(TokenKind kind, int offset, int length)
+ {
+ return Create(kind, new SourceLocation
+ {
+ Offset = offset,
+ Length = length,
+ });
+ }
+
+ public static Token Create(TokenKind kind, SourceLocation location)
+ {
+ Token token;
+ switch (kind) {
+ #region case KEYWORD:
+ case TokenKind.T_AS:
+ case TokenKind.T_BREAK:
+ case TokenKind.T_CASE:
+ case TokenKind.T_CATCH:
+ case TokenKind.T_CONST:
+ case TokenKind.T_CONTINUE:
+ case TokenKind.T_DEFAULT:
+ case TokenKind.T_DELETE:
+ case TokenKind.T_DO:
+ case TokenKind.T_ELSE:
+ case TokenKind.T_ENUM:
+ case TokenKind.T_FALSE:
+ case TokenKind.T_FINALLY:
+ case TokenKind.T_FOR:
+ case TokenKind.T_FUNCTION:
+ case TokenKind.T_IF:
+ case TokenKind.T_IMPORT:
+ case TokenKind.T_IN:
+ case TokenKind.T_INSTANCEOF:
+ case TokenKind.T_LET:
+ case TokenKind.T_NEW:
+ case TokenKind.T_NULL:
+ case TokenKind.T_ON:
+ case TokenKind.T_PRAGMA:
+ case TokenKind.T_PROPERTY:
+ case TokenKind.T_PUBLIC:
+ case TokenKind.T_READONLY:
+ case TokenKind.T_RESERVED_WORD:
+ case TokenKind.T_RETURN:
+ case TokenKind.T_SET:
+ case TokenKind.T_SIGNAL:
+ case TokenKind.T_SWITCH:
+ case TokenKind.T_THIS:
+ case TokenKind.T_THROW:
+ case TokenKind.T_TRUE:
+ case TokenKind.T_TRY:
+ case TokenKind.T_TYPEOF:
+ case TokenKind.T_VAR:
+ case TokenKind.T_VOID:
+ case TokenKind.T_WHILE:
+ case TokenKind.T_WITH:
+ #endregion
+ token = new KeywordToken();
+ break;
+ case TokenKind.T_NUMERIC_LITERAL:
+ token = new NumberToken();
+ break;
+ case TokenKind.T_MULTILINE_STRING_LITERAL:
+ case TokenKind.T_STRING_LITERAL:
+ token = new StringToken();
+ break;
+ case TokenKind.T_COMMENT:
+ token = new CommentToken();
+ break;
+ default:
+ token = new Token();
+ break;
+ }
+ token.Kind = kind;
+ token.Location = location;
+ return token;
+ }
+ }
+
+ public class KeywordToken : Token { }
+
+ public class NumberToken : Token { }
+
+ public class StringToken : Token { }
+
+ public class CommentToken : Token { }
+}
diff --git a/src/qtvstools/QtVsTools.csproj b/src/qtvstools/QtVsTools.csproj
index 74efaa89..fe3df603 100644
--- a/src/qtvstools/QtVsTools.csproj
+++ b/src/qtvstools/QtVsTools.csproj
@@ -90,6 +90,14 @@
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="PropertyToken.cs" />
<Compile Include="QMakeWrapper.cs" />
+ <Compile Include="QML\Classification\QmlClassificationFormat.cs" />
+ <Compile Include="QML\Classification\QmlSyntaxClassifier.cs" />
+ <Compile Include="QML\Classification\QmlErrorClassifier.cs" />
+ <Compile Include="QML\Classification\QmlTag.cs" />
+ <Compile Include="QML\Parser\QmlParserDiagnostics.cs" />
+ <Compile Include="QML\Parser\QmlParserInterop.cs" />
+ <Compile Include="QML\Syntax\QmlAst.cs" />
+ <Compile Include="QML\Syntax\QmlSyntax.cs" />
<Compile Include="Qt4DefaultEditors.cs" />
<Compile Include="Qt5DefaultEditors.cs" />
<Compile Include="QtHelpLinkChooser.xaml.cs">
@@ -190,6 +198,11 @@
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<IncludeInVSIX>true</IncludeInVSIX>
</Content>
+ <Content Include="..\vsqml\bin\vsqml.dll">
+ <Link>vsqml.dll</Link>
+ <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+ <IncludeInVSIX>true</IncludeInVSIX>
+ </Content>
<Content Include="preview.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<IncludeInVSIX>true</IncludeInVSIX>