aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMiguel Costa <miguel.costa@qt.io>2023-09-27 16:32:24 +0200
committerMiguel Costa <miguel.costa@qt.io>2024-01-16 10:57:56 +0000
commit8e192cbe7a90ec57a0ed482ef277915264b124de (patch)
tree33ca552c9dfea708a9cae10e10e1796c77682fe8
parent975ff836028af16080583323f8a12ce9e3df3bf2 (diff)
Add QML LSP client
Change-Id: Ia833267f8790bcec47cf16c9aa8a53b57c70d954 Reviewed-by: Karsten Heimrich <karsten.heimrich@qt.io>
-rw-r--r--QtVsTools.Core/Options/QtOptionsPage.cs72
-rw-r--r--QtVsTools.Core/VersionInformation.cs2
-rw-r--r--QtVsTools.Package/QML/Classification/QmlAsyncClassifier.cs7
-rw-r--r--QtVsTools.Package/QML/Classification/QmlErrorClassifier.cs13
-rw-r--r--QtVsTools.Package/QML/Classification/QmlExpressionEvalClassifier.cs2
-rw-r--r--QtVsTools.Package/QML/Classification/QmlSyntaxClassifier.cs28
-rw-r--r--QtVsTools.Package/QML/Language/QmlLspClient.cs308
-rw-r--r--QtVsTools.Package/QML/QmlContentType.cs31
-rw-r--r--QtVsTools.Package/QtVsTools.Package.csproj3
-rw-r--r--references.props3
10 files changed, 439 insertions, 30 deletions
diff --git a/QtVsTools.Core/Options/QtOptionsPage.cs b/QtVsTools.Core/Options/QtOptionsPage.cs
index 5ed8aaf8..f9dfd186 100644
--- a/QtVsTools.Core/Options/QtOptionsPage.cs
+++ b/QtVsTools.Core/Options/QtOptionsPage.cs
@@ -8,6 +8,7 @@ using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Linq.Expressions;
+using System.IO;
using System.Reflection;
using Microsoft.Build.Framework;
using Microsoft.VisualStudio.Shell;
@@ -21,6 +22,7 @@ namespace QtVsTools.Core.Options
using VisualStudio;
using static Common.Utils;
using static QtVsTools.Common.EnumExt;
+ using static Instances;
public static class Options
{
@@ -87,6 +89,14 @@ namespace QtVsTools.Core.Options
[String("LinkNatvis")] Link
}
+ public enum QmlLsp
+ {
+ [String("QmlLsp_Enable")] Enable,
+ [String("QmlLsp_QtVersion")] QtVersion,
+ [String("QmlLsp_Log")] Log,
+ [String("QmlLsp_LogSize")] LogSize
+ }
+
public enum Timeout : uint { Disabled = 0 }
private class TimeoutConverter : EnumConverter
@@ -151,6 +161,30 @@ namespace QtVsTools.Core.Options
}
}
+ private class QtVersionConverter : StringConverter
+ {
+ public override bool GetStandardValuesSupported(ITypeDescriptorContext _) => true;
+ public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext _)
+ {
+ return new(VersionManager.GetVersions()
+ .Where(IsCompatible)
+ .Prepend("$(DefaultQtVersion)")
+ .Cast<object>()
+ .ToArray());
+ }
+ protected virtual bool IsCompatible(string qtVersion) => true;
+ }
+
+ private class QmlLspProviderConverter : QtVersionConverter
+ {
+ public override bool GetStandardValuesExclusive(ITypeDescriptorContext _) => true;
+ protected override bool IsCompatible(string qtVersion)
+ {
+ return VersionManager.GetVersionInfo(qtVersion) is { } qt
+ && File.Exists(Path.Combine(qt.LibExecs, "qmlls.exe"));
+ }
+ }
+
[Category("Qt/MSBuild")]
[DisplayName("Path to Qt/MSBuild files")]
[Description("Corresponds to the QTMSBUILD environment variable")]
@@ -261,6 +295,29 @@ namespace QtVsTools.Core.Options
[TypeConverter(typeof(EnableDisableConverter))]
public bool LinkNatvis { get; set; }
+ [Category("QML Language Server")]
+ [DisplayName("Enable")]
+ [Description("Connect to a QML language server for enhanced code editing experience.")]
+ [TypeConverter(typeof(EnableDisableConverter))]
+ public bool QmlLspEnable { get; set; }
+
+ [Category("QML Language Server")]
+ [DisplayName("Qt Version")]
+ [Description("Look for a QML language server in the specified Qt installation.")]
+ [TypeConverter(typeof(QmlLspProviderConverter))]
+ public string QmlLspVersion { get; set; }
+
+ [Category("QML Language Server")]
+ [DisplayName("Log")]
+ [Description("Write exchanged LSP messages to log file in %TEMP%.")]
+ [TypeConverter(typeof(EnableDisableConverter))]
+ public bool QmlLspLog { get; set; }
+
+ [Category("QML Language Server")]
+ [DisplayName("Log Size")]
+ [Description("Maximum size (in KB) of QML LSP log file.")]
+ public int QmlLspLogSize { get; set; }
+
public override void ResetSettings()
{
ThreadHelper.ThrowIfNotOnUIThread();
@@ -281,6 +338,11 @@ namespace QtVsTools.Core.Options
NotifyCMakeConversion = true;
LinkNatvis = true;
+ QmlLspEnable = false;
+ QmlLspVersion = "$(DefaultQtVersion)";
+ QmlLspLog = false;
+ QmlLspLogSize = 2500;
+
////////
// Get Qt Help keyboard shortcut
//
@@ -321,6 +383,10 @@ namespace QtVsTools.Core.Options
Load(() => NotifyCMakeConversion, key, Notifications.CMakeConversion);
Load(() => UpdateProjectFormat, key, Notifications.UpdateProjectFormat);
Load(() => LinkNatvis, key, Natvis.Link);
+ Load(() => QmlLspEnable, key, QmlLsp.Enable);
+ Load(() => QmlLspVersion, key, QmlLsp.QtVersion);
+ Load(() => QmlLspLog, key, QmlLsp.Log);
+ Load(() => QmlLspLogSize, key, QmlLsp.LogSize);
} catch (Exception exception) {
exception.Log();
}
@@ -336,6 +402,8 @@ namespace QtVsTools.Core.Options
Environment.SetEnvironmentVariable(
"QTMSBUILD", QtMsBuildPath, EnvironmentVariableTarget.Process);
}
+ if (QmlLspLogSize < 100)
+ QmlLspLogSize = 100;
using var key = Registry.CurrentUser.CreateSubKey(Resources.RegistryPackagePath);
if (key == null)
@@ -356,6 +424,10 @@ namespace QtVsTools.Core.Options
Save(NotifyCMakeConversion, key, Notifications.CMakeConversion);
Save(UpdateProjectFormat, key, Notifications.UpdateProjectFormat);
Save(LinkNatvis, key, Natvis.Link);
+ Save(QmlLspEnable, key, QmlLsp.Enable);
+ Save(QmlLspVersion, key, QmlLsp.QtVersion);
+ Save(QmlLspLog, key, QmlLsp.Log);
+ Save(QmlLspLogSize, key, QmlLsp.LogSize);
} catch (Exception exception) {
exception.Log();
}
diff --git a/QtVsTools.Core/VersionInformation.cs b/QtVsTools.Core/VersionInformation.cs
index 7ef0bfcc..1a017cd2 100644
--- a/QtVsTools.Core/VersionInformation.cs
+++ b/QtVsTools.Core/VersionInformation.cs
@@ -262,5 +262,7 @@ namespace QtVsTools.Core
}
public string InstallPrefix => qmakeQuery["QT_INSTALL_PREFIX"];
+
+ public string LibExecs => qmakeQuery["QT_INSTALL_LIBEXECS"];
}
}
diff --git a/QtVsTools.Package/QML/Classification/QmlAsyncClassifier.cs b/QtVsTools.Package/QML/Classification/QmlAsyncClassifier.cs
index bfbcb84f..b423a66d 100644
--- a/QtVsTools.Package/QML/Classification/QmlAsyncClassifier.cs
+++ b/QtVsTools.Package/QML/Classification/QmlAsyncClassifier.cs
@@ -218,9 +218,16 @@ namespace QtVsTools.Qml.Classification
string classificationType,
ITextView textView,
ITextBuffer buffer)
+ : this(classificationType, buffer)
{
TextView = textView;
textView.Closed += TextView_Closed;
+ }
+
+ protected QmlAsyncClassifier(
+ string classificationType,
+ ITextBuffer buffer)
+ {
Buffer = buffer;
buffer.Changed += Buffer_Changed;
diff --git a/QtVsTools.Package/QML/Classification/QmlErrorClassifier.cs b/QtVsTools.Package/QML/Classification/QmlErrorClassifier.cs
index d8bff21e..6ae80967 100644
--- a/QtVsTools.Package/QML/Classification/QmlErrorClassifier.cs
+++ b/QtVsTools.Package/QML/Classification/QmlErrorClassifier.cs
@@ -14,27 +14,26 @@ using Microsoft.VisualStudio.Utilities;
namespace QtVsTools.Qml.Classification
{
- [Export(typeof(IViewTaggerProvider))]
- [ContentType("qml")]
+ [Export(typeof(ITaggerProvider))]
+ [ContentType(QmlContentType.Name)]
[TagType(typeof(ErrorTag))]
- internal sealed class QmlErrorClassifierProvider : IViewTaggerProvider
+ internal sealed class QmlErrorClassifierProvider : ITaggerProvider
{
[Import]
internal IClassificationTypeRegistryService classificationTypeRegistry = null;
- public ITagger<T> CreateTagger<T>(ITextView textView, ITextBuffer buffer) where T : ITag
+ public ITagger<T> CreateTagger<T>(ITextBuffer buffer) where T : ITag
{
QmlClassificationType.InitClassificationTypes(classificationTypeRegistry);
- return new QmlErrorClassifier(textView, buffer) as ITagger<T>;
+ return new QmlErrorClassifier(buffer) as ITagger<T>;
}
}
internal sealed class QmlErrorClassifier : QmlAsyncClassifier<ErrorTag>
{
internal QmlErrorClassifier(
- ITextView textView,
ITextBuffer buffer)
- : base("Error", textView, buffer)
+ : base("Error", buffer)
{
}
diff --git a/QtVsTools.Package/QML/Classification/QmlExpressionEvalClassifier.cs b/QtVsTools.Package/QML/Classification/QmlExpressionEvalClassifier.cs
index 04811df7..d474d7d4 100644
--- a/QtVsTools.Package/QML/Classification/QmlExpressionEvalClassifier.cs
+++ b/QtVsTools.Package/QML/Classification/QmlExpressionEvalClassifier.cs
@@ -25,7 +25,7 @@ namespace QtVsTools.Qml.Classification
using Syntax;
[Export(typeof(IViewTaggerProvider))]
- [ContentType("qml")]
+ [ContentType(QmlContentType.Name)]
[TagType(typeof(ClassificationTag))]
internal sealed class QmlExpressionEvalProvider : IViewTaggerProvider
{
diff --git a/QtVsTools.Package/QML/Classification/QmlSyntaxClassifier.cs b/QtVsTools.Package/QML/Classification/QmlSyntaxClassifier.cs
index 76290c34..c991eea6 100644
--- a/QtVsTools.Package/QML/Classification/QmlSyntaxClassifier.cs
+++ b/QtVsTools.Package/QML/Classification/QmlSyntaxClassifier.cs
@@ -15,42 +15,26 @@ using Microsoft.VisualStudio.Utilities;
namespace QtVsTools.Qml.Classification
{
- [Export(typeof(IViewTaggerProvider))]
- [ContentType("qml")]
+ [Export(typeof(ITaggerProvider))]
+ [ContentType(QmlContentType.Name)]
[TagType(typeof(ClassificationTag))]
- internal sealed class QmlSyntaxClassifierProvider : IViewTaggerProvider
+ internal sealed class QmlSyntaxClassifierProvider : ITaggerProvider
{
- [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
+ public ITagger<T> CreateTagger<T>(ITextBuffer buffer) where T : ITag
{
QmlClassificationType.InitClassificationTypes(classificationTypeRegistry);
- return new QmlSyntaxClassifier(textView, buffer) as ITagger<T>;
+ return new QmlSyntaxClassifier(buffer) as ITagger<T>;
}
}
internal sealed class QmlSyntaxClassifier : QmlAsyncClassifier<ClassificationTag>
{
internal QmlSyntaxClassifier(
- ITextView textView,
ITextBuffer buffer)
- : base("Syntax", textView, buffer)
+ : base("Syntax", buffer)
{
}
diff --git a/QtVsTools.Package/QML/Language/QmlLspClient.cs b/QtVsTools.Package/QML/Language/QmlLspClient.cs
new file mode 100644
index 00000000..3647f2cb
--- /dev/null
+++ b/QtVsTools.Package/QML/Language/QmlLspClient.cs
@@ -0,0 +1,308 @@
+/***************************************************************************************************
+ Copyright (C) 2023 The Qt Company Ltd.
+ SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+***************************************************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.Composition;
+using System.Diagnostics;
+using System.IO;
+using System.IO.Pipes;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.VisualStudio.LanguageServer.Client;
+using Microsoft.VisualStudio.Shell;
+using Microsoft.VisualStudio.Threading;
+using Microsoft.VisualStudio.Utilities;
+using Microsoft.VisualStudio.Text;
+using Microsoft.VisualStudio.Text.BraceCompletion;
+using Microsoft.VisualStudio.Text.Editor;
+
+namespace QtVsTools.Qml.Language
+{
+ using Core;
+ using static Core.Common.Utils;
+ using static Instances;
+ using static Core.Instances;
+
+ [Export(typeof(ILanguageClient))]
+ [Export(typeof(IBraceCompletionSessionProvider))]
+ [BracePair('{', '}')]
+ [ContentType(QmlContentType.Name)]
+ public class QmlLspClient :
+ ILanguageClient,
+ IBraceCompletionSessionProvider
+ {
+ public bool TryCreateSession(ITextView textView, SnapshotPoint openingPoint,
+ char openingBrace, char closingBrace, out IBraceCompletionSession session)
+ {
+ session = null;
+ return true;
+ }
+
+ public event AsyncEventHandler<EventArgs> StartAsync;
+ public event AsyncEventHandler<EventArgs> StopAsync;
+
+ public QmlLspClient()
+ {
+ }
+
+ public string Name => "QML Language Client";
+
+ private string PathToQt { get; set; }
+ private string PathToServer { get; set; }
+
+ private static string LogFilePath { get; } = @$"{Path.GetTempPath()}\qmllsp.log.txt";
+ private LogFile Log { get; set; }
+
+ private NamedPipeClientStream StdIn { get; set; }
+ private NamedPipeClientStream StdOut { get; set; }
+ private NamedPipeClientStream StdErr { get; set; }
+
+ private Task StdInListner { get; set; }
+ private Task StdOutListner { get; set; }
+ private Task StdErrListner { get; set; }
+ private Task ServerAwaiter { get; set; }
+
+ private Process Server { get; set; }
+
+ public async Task<Connection> ActivateAsync(CancellationToken token)
+ {
+ if (!Package.Options.QmlLspEnable)
+ return null;
+
+ var logMaxSize = 1000 * (Package.Options.QmlLspLogSize switch
+ {
+ >= 10 and <= 10000 => Package.Options.QmlLspLogSize,
+ _ => 2500
+ });
+ var logTruncSize = 2 * logMaxSize / 3;
+
+ if (PathToServer is not { Length: > 0 })
+ return null;
+
+ if (PathToQt is not { Length: > 0 })
+ return null;
+
+ if (Server is { HasExited: false }) {
+ try {
+ Server.Kill();
+ Server.WaitForExit();
+ } catch (Exception ex) {
+ ex.Log();
+ }
+ Server = null;
+ }
+
+ var server = new Process
+ {
+ StartInfo = new ProcessStartInfo
+ {
+ FileName = PathToServer,
+ Arguments = $"-b \"{PathToQt}\"",
+ RedirectStandardInput = true,
+ RedirectStandardOutput = true,
+ RedirectStandardError = true,
+ UseShellExecute = false,
+ CreateNoWindow = true
+ }
+ };
+ try {
+ if (!server.Start())
+ return null;
+ if (token.IsCancellationRequested) {
+ await StopAsync.InvokeAsync(this, EventArgs.Empty);
+ await server.WaitForExitAsync();
+ return null;
+ }
+ } catch (Exception e) {
+ e.Log();
+ return null;
+ }
+
+ Server = server;
+ if (Package.Options.QmlLspLog)
+ return await LogConnectionAsync(server, logMaxSize, logTruncSize);
+
+ return new(server.StandardOutput.BaseStream, server.StandardInput.BaseStream);
+ }
+
+ public async Task OnLoadedAsync()
+ {
+ if (VersionManager is null)
+ return;
+
+ if (!Package.Options.QmlLspEnable)
+ return;
+ var qtVersionName = Package.Options.QmlLspVersion switch
+ {
+ { Length: > 0} x when !x.Equals("$(DefaultQtVersion)", IgnoreCase) => x,
+ _ => VersionManager.GetDefaultVersion()
+ };
+
+ if (VersionManager.GetVersionInfo(qtVersionName) is not { } qtVersion)
+ return;
+
+ var qtPath = qtVersion.InstallPrefix.Replace('/', '\\').TrimEnd('\\');
+ if (!Directory.Exists(qtPath))
+ return;
+
+ var qmlLsPath = $"{qtVersion.LibExecs.Replace('/', '\\').TrimEnd('\\')}\\qmlls.exe";
+ if (!File.Exists(qmlLsPath))
+ return;
+
+ PathToQt = qtPath;
+ PathToServer = qmlLsPath;
+
+ await StartAsync.InvokeAsync(this, EventArgs.Empty);
+ }
+
+ public async Task OnServerInitializedAsync()
+ {
+ await Task.Yield();
+ }
+
+ public async Task<InitializationFailureContext> OnServerInitializeFailedAsync(
+ ILanguageClientInitializationInfo initializationState)
+ {
+ await Task.Yield();
+ return new InitializationFailureContext
+ {
+ FailureMessage = initializationState.StatusMessage
+ };
+ }
+
+ public IEnumerable<string> ConfigurationSections => null;
+
+ public object InitializationOptions => null;
+
+ public IEnumerable<string> FilesToWatch => null;
+
+ public bool ShowNotificationOnInitializeFailed => false;
+
+ private async Task<Connection> LogConnectionAsync(Process server, int size, int truncSize)
+ {
+ Log = new(LogFilePath, size, truncSize, "===");
+ StdInListner = Task.Run(async () =>
+ {
+ var data = new byte[4096];
+ using var stdIn = new NamedPipeServerStream("qmllsp_stdin", PipeDirection.In);
+ await stdIn.WaitForConnectionAsync();
+ while (!server.HasExited) {
+ try {
+ var taskRead = stdIn.ReadAsync(data, 0, data.Length);
+ var taskWaitForExit = server.WaitForExitAsync();
+ await Task.WhenAny(taskRead, taskWaitForExit);
+ if (!server.HasExited && taskRead.IsCompleted) {
+ var size = await taskRead;
+ Log.Write(@$"
+CLI <<< SRV [{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}]
+{Encoding.UTF8.GetString(data, 0, size)}
+===".Trim(' ', '\r', '\n') + "\r\n");
+ await server.StandardInput.BaseStream.WriteAsync(data, 0, size);
+ await server.StandardInput.BaseStream.FlushAsync();
+ }
+ } catch (Exception ex) {
+ ex.Log();
+ return;
+ }
+ }
+ });
+
+ StdOutListner = Task.Run(async () =>
+ {
+ var data = new byte[4096];
+ using var stdOut = new NamedPipeServerStream("qmllsp_stdout", PipeDirection.Out);
+ await stdOut.WaitForConnectionAsync();
+ while (!server.HasExited) {
+ try {
+ var taskRead = server.StandardOutput.BaseStream.ReadAsync(data, 0, data.Length);
+ var taskWaitForExit = server.WaitForExitAsync();
+ var completedTask = await Task.WhenAny(taskRead, taskWaitForExit);
+ if (!server.HasExited && taskRead.IsCompleted) {
+ var size = await taskRead;
+ Log.Write(@$"
+CLI >>> SRV [{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}]
+{Encoding.UTF8.GetString(data, 0, size)}
+===".Trim(' ', '\r', '\n') + "\r\n");
+ await stdOut.WriteAsync(data, 0, size);
+ await stdOut.FlushAsync();
+ }
+ } catch (Exception ex) {
+ ex.Log();
+ return;
+ }
+ }
+ });
+
+ StdErrListner = Task.Run(async () =>
+ {
+ var data = new byte[4096];
+ using var stdErr = new NamedPipeServerStream("qmllsp_stderr", PipeDirection.Out);
+ await stdErr.WaitForConnectionAsync();
+ while (!server.HasExited) {
+ try {
+ var taskRead = server.StandardError.BaseStream.ReadAsync(data, 0, data.Length);
+ var taskWaitForExit = server.WaitForExitAsync();
+ var completedTask = await Task.WhenAny(taskRead, taskWaitForExit);
+ if (!server.HasExited && taskRead.IsCompleted) {
+ var size = await taskRead;
+ Log.Write(@$"
+CLI !!! [{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}]
+{Encoding.UTF8.GetString(data, 0, size)}
+===".Trim(' ', '\r', '\n') + "\r\n");
+ await stdErr.WriteAsync(data, 0, size);
+ await stdErr.FlushAsync();
+ }
+ } catch (Exception ex) {
+ ex.Log();
+ return;
+ }
+ }
+ });
+
+ StdIn = new NamedPipeClientStream(".", "qmllsp_stdin", PipeDirection.Out);
+ while (!StdIn.IsConnected) {
+ try {
+ await StdIn.ConnectAsync();
+ } catch (Exception ex) {
+ ex.Log();
+ await Task.Delay(100);
+ }
+ }
+
+ StdOut = new NamedPipeClientStream(".", "qmllsp_stdout", PipeDirection.In);
+ while (!StdOut.IsConnected) {
+ try {
+ await StdOut.ConnectAsync();
+ } catch (Exception ex) {
+ ex.Log();
+ await Task.Delay(100);
+ }
+ }
+
+ StdErr = new NamedPipeClientStream(".", "qmllsp_stderr", PipeDirection.In);
+ while (!StdErr.IsConnected) {
+ try {
+ await StdErr.ConnectAsync();
+ } catch (Exception ex) {
+ ex.Log();
+ await Task.Delay(100);
+ }
+ }
+
+ ServerAwaiter = Task.Run(async () =>
+ {
+ server.WaitForExit();
+ await server.WaitForExitAsync();
+ StdIn.Dispose();
+ StdOut.Dispose();
+ StdErr.Dispose();
+ });
+
+ return new(StdOut, StdIn);
+ }
+ }
+}
diff --git a/QtVsTools.Package/QML/QmlContentType.cs b/QtVsTools.Package/QML/QmlContentType.cs
new file mode 100644
index 00000000..9ce8dd0f
--- /dev/null
+++ b/QtVsTools.Package/QML/QmlContentType.cs
@@ -0,0 +1,31 @@
+/***************************************************************************************************
+ Copyright (C) 2023 The Qt Company Ltd.
+ SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+***************************************************************************************************/
+
+using System.ComponentModel.Composition;
+using Microsoft.VisualStudio.LanguageServer.Client;
+using Microsoft.VisualStudio.Utilities;
+
+namespace QtVsTools.Qml
+{
+ internal sealed class QmlContentType
+ {
+ public const string Name = "qml";
+
+ [Export]
+ [Name(Name)]
+ [BaseDefinition(CodeRemoteContentDefinition.CodeRemoteContentTypeName)]
+ internal static ContentTypeDefinition qmlContentType = null;
+
+ [Export]
+ [FileExtension(".qml")]
+ [ContentType(Name)]
+ internal static FileExtensionToContentTypeDefinition qmlFileType = null;
+
+ [Export]
+ [FileExtension(".qmlproject")]
+ [ContentType(Name)]
+ internal static FileExtensionToContentTypeDefinition qmlprojectFileType = null;
+ }
+}
diff --git a/QtVsTools.Package/QtVsTools.Package.csproj b/QtVsTools.Package/QtVsTools.Package.csproj
index 7d1db0cc..f1df514b 100644
--- a/QtVsTools.Package/QtVsTools.Package.csproj
+++ b/QtVsTools.Package/QtVsTools.Package.csproj
@@ -94,6 +94,7 @@
<PackageReference Include="$(Name_Microsoft_VisualStudio_Composition)" Version="$(Version_Microsoft_VisualStudio_Composition)" />
<PackageReference Include="$(Name_Microsoft_VisualStudio_Workspace)" Version="$(Version_Microsoft_VisualStudio_Workspace)" />
<PackageReference Include="$(Name_Microsoft_VisualStudio_Workspace_VSIntegration)" Version="$(Version_Microsoft_VisualStudio_Workspace_VSIntegration)" />
+ <PackageReference Include="$(Name_Microsoft_VisualStudio_LanguageServer_Client)" Version="$(Version_Microsoft_VisualStudio_LanguageServer_Client)" />
<PackageReference Include="Community.VisualStudio.VSCT">
<Version>16.0.29.6</Version>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
@@ -320,6 +321,8 @@
<DesignTime>True</DesignTime>
<DependentUpon>AssemblyInfo.cs</DependentUpon>
</Compile>
+ <Compile Include="QML\QmlContentType.cs" />
+ <Compile Include="QML\Language\QmlLspClient.cs" />
<Compile Include="QtVsToolsPackage.cs" />
<T4Template Include="QtVersionId.vsct_TT">
<Generator>TextTemplatingFileGenerator</Generator>
diff --git a/references.props b/references.props
index 86f4ed55..494d1b6d 100644
--- a/references.props
+++ b/references.props
@@ -19,6 +19,7 @@
<Name_Microsoft_VisualStudio_Composition>Microsoft.VisualStudio.Composition</Name_Microsoft_VisualStudio_Composition>
<Name_Microsoft_VisualStudio_Workspace>Microsoft.VisualStudio.Workspace</Name_Microsoft_VisualStudio_Workspace>
<Name_Microsoft_VisualStudio_Workspace_VSIntegration>Microsoft.VisualStudio.Workspace.VSIntegration</Name_Microsoft_VisualStudio_Workspace_VSIntegration>
+ <Name_Microsoft_VisualStudio_LanguageServer_Client>Microsoft.VisualStudio.LanguageServer.Client</Name_Microsoft_VisualStudio_LanguageServer_Client>
<Name_MSTest_TestAdapter>MSTest.TestAdapter</Name_MSTest_TestAdapter>
<Version_MSTest_TestAdapter>2.2.8</Version_MSTest_TestAdapter>
@@ -52,6 +53,7 @@
<Version_Microsoft_VisualStudio_Workspace>16.3.43</Version_Microsoft_VisualStudio_Workspace>
<Version_Microsoft_VisualStudio_Workspace_VSIntegration>16.3.43</Version_Microsoft_VisualStudio_Workspace_VSIntegration>
<Version_Newtonsoft_Json>13.0.1</Version_Newtonsoft_Json>
+ <Version_Microsoft_VisualStudio_LanguageServer_Client>17.4.1068</Version_Microsoft_VisualStudio_LanguageServer_Client>
</PropertyGroup>
<!-- // Visual Studio 2019 -->
@@ -71,6 +73,7 @@
<Version_Microsoft_VisualStudio_Workspace>16.3.43</Version_Microsoft_VisualStudio_Workspace>
<Version_Microsoft_VisualStudio_Workspace_VSIntegration>16.3.43</Version_Microsoft_VisualStudio_Workspace_VSIntegration>
<Version_Newtonsoft_Json>12.0.3</Version_Newtonsoft_Json>
+ <Version_Microsoft_VisualStudio_LanguageServer_Client>16.10.1220</Version_Microsoft_VisualStudio_LanguageServer_Client>
</PropertyGroup>
</Project>