From 1a2bd020917938be8a4aff90c8a7cbd3ff86a076 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Mon, 18 Oct 2021 19:14:35 -0700 Subject: [PATCH 01/37] changes to add the formatter to the Sdk --- README.md | 1 + build/manifest.ps1 | 1 + build/pack.ps1 | 1 + .../DefaultItems/DefaultItems.props.v.template | 2 ++ src/QuantumSdk/QuantumSdk.nuspec | 1 + src/QuantumSdk/Sdk/Sdk.targets | 13 +++++++++++++ 6 files changed, 19 insertions(+) diff --git a/README.md b/README.md index ba43e8d56e..50e63c0e30 100644 --- a/README.md +++ b/README.md @@ -73,6 +73,7 @@ If you edit the [Microsoft.Quantum.Sdk](./src/QuantumSdk) as part of your change ``` dotnet publish src/QuantumSdk/Tools/Tools.sln -c Debug dotnet publish src/QsCompiler/CommandLineTool/CommandLineTool.csproj -c Debug +dotnet publish src/QsFmt/App/App.fsproj -c Debug nuget.exe pack src/QuantumSdk/QuantumSdk.nuspec -Version 1.0.0 -Properties Configuration=Debug ``` Move the created .nupkg file into your [local NuGet folder](https://docs.microsoft.com/en-us/nuget/hosting-packages/local-feeds). You can now use the package to build any Q# project by opening the project file in a text editor, and editing the Sdk version number in the first line to be diff --git a/build/manifest.ps1 b/build/manifest.ps1 index 2e47fe5a27..58dbca40db 100644 --- a/build/manifest.ps1 +++ b/build/manifest.ps1 @@ -56,6 +56,7 @@ $artifacts = @{ ".\src\QsCompiler\LlvmBindings\bin\$Env:BUILD_CONFIGURATION\netstandard2.1\Microsoft.Quantum.LlvmBindings.dll", ".\src\QsCompiler\Transformations\bin\$Env:BUILD_CONFIGURATION\netstandard2.1\Microsoft.Quantum.QsTransformations.dll", ".\src\QsCompiler\CommandLineTool\bin\$Env:BUILD_CONFIGURATION\netcoreapp3.1\qsc.dll", + ".\src\QsFmt\App\bin\$Env:BUILD_CONFIGURATION\netcoreapp3.1\qsfmt.dll", ".\src\QuantumSdk\Tools\BuildConfiguration\bin\$Env:BUILD_CONFIGURATION\netcoreapp3.1\Microsoft.Quantum.Sdk.BuildConfiguration.dll", ".\src\QuantumSdk\Tools\DefaultEntryPoint\bin\$Env:BUILD_CONFIGURATION\netcoreapp3.1\Microsoft.Quantum.Sdk.DefaultEntryPoint.Generation.dll" ) | ForEach-Object { Join-Path $PSScriptRoot (Join-Path ".." $_) }; diff --git a/build/pack.ps1 b/build/pack.ps1 index 7d86455a05..76557d57b2 100644 --- a/build/pack.ps1 +++ b/build/pack.ps1 @@ -89,6 +89,7 @@ Publish-One '../src/QsCompiler/CommandLineTool/CommandLineTool.csproj' Publish-One '../src/QsCompiler/LlvmBindings/LlvmBindings.csproj' Publish-One '../src/QuantumSdk/Tools/BuildConfiguration/BuildConfiguration.csproj' Publish-One '../src/QuantumSdk/Tools/DefaultEntryPoint/DefaultEntryPoint.csproj' +Publish-One '../src/QsFmt/App/App.fsproj' Pack-One '../src/QsCompiler/Compiler/Compiler.csproj' '-IncludeReferencedProjects' Pack-One '../src/QsCompiler/QirGeneration/QirGeneration.csproj' diff --git a/src/QuantumSdk/DefaultItems/DefaultItems.props.v.template b/src/QuantumSdk/DefaultItems/DefaultItems.props.v.template index 5df553c6e5..5a5c8b4209 100644 --- a/src/QuantumSdk/DefaultItems/DefaultItems.props.v.template +++ b/src/QuantumSdk/DefaultItems/DefaultItems.props.v.template @@ -54,6 +54,8 @@ $(DefaultQscBuildConfigExe) dotnet "$(MSBuildThisFileDirectory)../tools/qsc/qsc.dll" $(DefaultQscExe) + dotnet "$(MSBuildThisFileDirectory)../tools/qsfmt/qsfmt.dll" + $(DefaultQsFmtExe) false diff --git a/src/QuantumSdk/QuantumSdk.nuspec b/src/QuantumSdk/QuantumSdk.nuspec index f1bc0a2943..07b1bd9bba 100644 --- a/src/QuantumSdk/QuantumSdk.nuspec +++ b/src/QuantumSdk/QuantumSdk.nuspec @@ -25,6 +25,7 @@ + diff --git a/src/QuantumSdk/Sdk/Sdk.targets b/src/QuantumSdk/Sdk/Sdk.targets index c7ef6d7fae..54e6b5218f 100644 --- a/src/QuantumSdk/Sdk/Sdk.targets +++ b/src/QuantumSdk/Sdk/Sdk.targets @@ -134,6 +134,19 @@ + + + + $(QsFmtExe) update --qsharp-version $(QuantumSdkVersion) --inputs @(QSharpCompile) + + + + + + + + + Date: Mon, 18 Oct 2021 19:29:16 -0700 Subject: [PATCH 02/37] exclude pdbs from sdk --- src/QuantumSdk/QuantumSdk.nuspec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/QuantumSdk/QuantumSdk.nuspec b/src/QuantumSdk/QuantumSdk.nuspec index 07b1bd9bba..c328de2b4b 100644 --- a/src/QuantumSdk/QuantumSdk.nuspec +++ b/src/QuantumSdk/QuantumSdk.nuspec @@ -24,8 +24,8 @@ - - + + From 01351c50da06ed7a16fb7ea61a1f758b4c589d97 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Mon, 18 Oct 2021 19:54:37 -0700 Subject: [PATCH 03/37] just moving the target --- src/QuantumSdk/Sdk/Sdk.targets | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/src/QuantumSdk/Sdk/Sdk.targets b/src/QuantumSdk/Sdk/Sdk.targets index 54e6b5218f..fe27bb2e5c 100644 --- a/src/QuantumSdk/Sdk/Sdk.targets +++ b/src/QuantumSdk/Sdk/Sdk.targets @@ -134,19 +134,6 @@ - - - - $(QsFmtExe) update --qsharp-version $(QuantumSdkVersion) --inputs @(QSharpCompile) - - - - - - - - - + + + + $(QsFmtExe) update --qsharp-version $(QuantumSdkVersion) --inputs @(QSharpCompile) + + + + + + + + - $(QsFmtExe) update --qsharp-version $(QuantumSdkVersion) --inputs @(QSharpCompile) + $(QsFmtExe) update --qsharp-version $(QuantumSdkVersion) --inputs "@(QSharpCompile->'%(FullPath)', '" "')" From 3188d82c47bdb57a16f718e3f66887508f651099 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Wed, 20 Oct 2021 22:22:20 -0700 Subject: [PATCH 06/37] adding formatting with a todo for the implementation --- .../CompilationUnitManager.cs | 27 ++++++++++++++++++ .../CompilationManager/FileContentManager.cs | 18 +++++++++++- .../CompilationManager/ProjectManager.cs | 14 ++++++++-- src/QsCompiler/LanguageServer/EditorState.cs | 9 ++++++ .../LanguageServer/LanguageServer.cs | 28 +++++++++++++++++++ src/QsCompiler/Tests.LanguageServer/Tests.cs | 2 +- .../DefaultItems/DefaultItems.targets | 1 + src/QuantumSdk/Sdk/Sdk.targets | 7 +++-- 8 files changed, 99 insertions(+), 7 deletions(-) diff --git a/src/QsCompiler/CompilationManager/CompilationUnitManager.cs b/src/QsCompiler/CompilationManager/CompilationUnitManager.cs index e28b5c22ae..6d6d20710f 100644 --- a/src/QsCompiler/CompilationManager/CompilationUnitManager.cs +++ b/src/QsCompiler/CompilationManager/CompilationUnitManager.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; +using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -781,6 +782,32 @@ private void TypeCheckFile(string fileId, CancellationToken cancellationToken) return success ? edit : null; } + /// + /// Returns the edits to format the file according to the specified settings. + /// + /// + /// Returns null if some parameters are unspecified (null), + /// or if the specified file is not listed as source file + /// + public TextEdit[]? Formatting(TextDocumentIdentifier? textDocument) + { + var succeeded = this.Processing.QueueForExecution( + () => this.FileQuery( + textDocument, + (file, compilation) => + { + var tempFile = Path.ChangeExtension(Path.GetTempFileName(), ".qs"); + var currentContent = file.GetFileContent(); + File.WriteAllText(tempFile, currentContent); + + // TODO ... format the tempFile, and create an edit based on that + return null; + }, + suppressExceptionLogging: false), // since the operation is blocking, no exceptions should occur + out var edits); + return succeeded ? edits : null; + } + // routines related to providing information for non-blocking editor commands // -> these commands need to be responsive and therefore won't wait for any processing to finish // -> if the query cannot be processed immediately, they simply return null diff --git a/src/QsCompiler/CompilationManager/FileContentManager.cs b/src/QsCompiler/CompilationManager/FileContentManager.cs index fe925300dc..2cdd403d33 100644 --- a/src/QsCompiler/CompilationManager/FileContentManager.cs +++ b/src/QsCompiler/CompilationManager/FileContentManager.cs @@ -1147,13 +1147,29 @@ internal void ReplaceFileContent(string text) } } + /// + /// Forces the processing of all currently queued changes, and return the current file content. + /// + internal string GetFileContent() + { + this.SyncRoot.EnterUpgradeableReadLock(); + try + { + this.Flush(); + return string.Concat(this.content.Get().Select(line => line.Text)); + } + finally + { + this.SyncRoot.ExitUpgradeableReadLock(); + } + } + /// /// Forces the processing of all currently queued changes. /// /// /// Does *NOT* call with the update. /// - /// internal void Flush() { this.timer.Stop(); diff --git a/src/QsCompiler/CompilationManager/ProjectManager.cs b/src/QsCompiler/CompilationManager/ProjectManager.cs index f2b608c462..2b5b65ca70 100644 --- a/src/QsCompiler/CompilationManager/ProjectManager.cs +++ b/src/QsCompiler/CompilationManager/ProjectManager.cs @@ -1068,6 +1068,16 @@ IEnumerable> CastT // -> these commands need to be responsive and therefore won't wait for any processing to finish // -> if the query cannot be processed immediately, they simply return null + /// + /// Returns the edits to format the file according to the specified settings. + /// + /// + /// Returns null if some parameters are unspecified (null), + /// or if the specified file is not listed as source file + /// + public TextEdit[]? Formatting(DocumentFormattingParams? param) => + this.Manager(param?.TextDocument?.Uri)?.Formatting(param?.TextDocument); // Formatting flushes unprocessed text changes + /// /// Returns the source file and position where the item at the given position is declared at, /// if such a declaration exists, and returns null otherwise. @@ -1084,8 +1094,8 @@ IEnumerable> CastT /// Returns the signature help information for a call expression if there is such an expression at the specified position. /// /// - /// Returns null if the given file is listed as to be ignored, - /// or if some parameters are unspecified (null), + /// Returns null if some parameters are unspecified (null), + /// or if the specified file is not listed as source file, /// or if the specified position is not a valid position within the currently processed file content, /// or if no call expression exists at the specified position at this time, /// or if no signature help information can be provided for the call expression at the specified position. diff --git a/src/QsCompiler/LanguageServer/EditorState.cs b/src/QsCompiler/LanguageServer/EditorState.cs index 450a5a880d..c36d792b7d 100644 --- a/src/QsCompiler/LanguageServer/EditorState.cs +++ b/src/QsCompiler/LanguageServer/EditorState.cs @@ -411,6 +411,15 @@ internal Task CloseFileAsync(TextDocumentIdentifier textDocument, Action ValidFileUri(param?.TextDocument?.Uri) && !this.IgnoreFile(param?.TextDocument?.Uri) ? this.projects.Rename(param, versionedChanges) : null; + /// + /// Returns the edits to format the file according to the specified settings. + /// Returns null if the specified uri is not a valid file uri, + /// or the given file is listed as to be ignored, + /// or if some parameters are unspecified (null). + /// + public TextEdit[]? Formatting(DocumentFormattingParams param) => + ValidFileUri(param?.TextDocument?.Uri) && !this.IgnoreFile(param?.TextDocument?.Uri) ? this.projects.Formatting(param) : null; + /// /// Returns the source file and position where the item at the given position is declared at, /// if such a declaration exists, and returns the given position and file otherwise. diff --git a/src/QsCompiler/LanguageServer/LanguageServer.cs b/src/QsCompiler/LanguageServer/LanguageServer.cs index fd4d61edd7..485f7d3068 100644 --- a/src/QsCompiler/LanguageServer/LanguageServer.cs +++ b/src/QsCompiler/LanguageServer/LanguageServer.cs @@ -292,6 +292,7 @@ public object Initialize(JToken arg) capabilities.WorkspaceSymbolProvider = false; capabilities.RenameProvider = true; capabilities.HoverProvider = true; + capabilities.DocumentFormattingProvider = true; capabilities.DocumentHighlightProvider = true; capabilities.SignatureHelpProvider.TriggerCharacters = new[] { "(", "," }; capabilities.ExecuteCommandProvider.Commands = new[] { CommandIds.ApplyEdit }; // do not declare internal capabilities @@ -421,6 +422,33 @@ public Task OnTextDocumentChangedAsync(JToken arg) } } + [JsonRpcMethod(Methods.TextDocumentFormattingName)] + public object OnFormatting(JToken arg) + { + if (this.waitForInit != null) + { + return ProtocolError.AwaitingInitialization; + } + + var param = Utils.TryJTokenAs(arg); + if (param == null) + { + throw new JsonSerializationException($"Expected parameters for {Methods.TextDocumentFormattingName}, but got null."); + } + + try + { + return + QsCompilerError.RaiseOnFailure( + () => this.editorState.Formatting(param) ?? Array.Empty(), + "Formatting threw an exception"); + } + catch + { + return Array.Empty(); + } + } + [JsonRpcMethod(Methods.TextDocumentDefinitionName)] public object OnTextDocumentDefinition(JToken arg) { diff --git a/src/QsCompiler/Tests.LanguageServer/Tests.cs b/src/QsCompiler/Tests.LanguageServer/Tests.cs index 5618317220..8b5d2f8d29 100644 --- a/src/QsCompiler/Tests.LanguageServer/Tests.cs +++ b/src/QsCompiler/Tests.LanguageServer/Tests.cs @@ -138,7 +138,7 @@ public async Task ServerCapabilitiesAsync() initReply.Capabilities.WorkspaceSymbolProvider.AssertCapability(shouldHave: false); initReply.Capabilities.RenameProvider.AssertCapability(); initReply.Capabilities.HoverProvider.AssertCapability(); - initReply.Capabilities.DocumentFormattingProvider.AssertCapability(shouldHave: false); + initReply.Capabilities.DocumentFormattingProvider.AssertCapability(shouldHave: true); initReply.Capabilities.DocumentRangeFormattingProvider.AssertCapability(shouldHave: false); initReply.Capabilities.CodeActionProvider.AssertCapability(); } diff --git a/src/QuantumSdk/DefaultItems/DefaultItems.targets b/src/QuantumSdk/DefaultItems/DefaultItems.targets index 1b869cd157..3dc2d2e203 100644 --- a/src/QuantumSdk/DefaultItems/DefaultItems.targets +++ b/src/QuantumSdk/DefaultItems/DefaultItems.targets @@ -60,6 +60,7 @@ + "$(MSBuildThisFileDirectory)../ dotnet "$(MSBuildThisFileDirectory)../tools/utils/Microsoft.Quantum.Sdk.DefaultEntryPoint.Generation.dll" true diff --git a/src/QuantumSdk/Sdk/Sdk.targets b/src/QuantumSdk/Sdk/Sdk.targets index 9cc7bdab2c..f8ca72c19f 100644 --- a/src/QuantumSdk/Sdk/Sdk.targets +++ b/src/QuantumSdk/Sdk/Sdk.targets @@ -179,12 +179,13 @@ - $(QsFmtExe) update --qsharp-version $(QuantumSdkVersion) --inputs "@(QSharpCompile->'%(FullPath)', '" "')" + $(QsFmtExe) update --qsharp-version $(QuantumSdkVersion) --inputs + $(QSharpSyntaxUpdate) "@(QSharpCompile->'%(FullPath)', '" "')" - + - + From 35e466ae79d82a80fc918808e4b239a516dde5ce Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Thu, 21 Oct 2021 00:13:46 -0700 Subject: [PATCH 07/37] formatting is enabled and works --- .../CompilationUnitManager.cs | 43 ++++++++++++++----- .../Process.cs | 0 .../CompilationManager/ProjectManager.cs | 19 +++++--- src/QsCompiler/LanguageServer/EditorState.cs | 21 +++++---- .../DefaultItems/DefaultItems.targets | 3 +- 5 files changed, 59 insertions(+), 27 deletions(-) rename src/QsCompiler/{Compiler => CompilationManager}/Process.cs (100%) diff --git a/src/QsCompiler/CompilationManager/CompilationUnitManager.cs b/src/QsCompiler/CompilationManager/CompilationUnitManager.cs index 6d6d20710f..dabc72b1b7 100644 --- a/src/QsCompiler/CompilationManager/CompilationUnitManager.cs +++ b/src/QsCompiler/CompilationManager/CompilationUnitManager.cs @@ -28,6 +28,8 @@ namespace Microsoft.Quantum.QsCompiler.CompilationBuilder /// public class CompilationUnitManager : IDisposable { + private readonly string sdkPath; + internal bool EnableVerification { get; private set; } /// @@ -79,8 +81,10 @@ public CompilationUnitManager( bool syntaxCheckOnly = false, RuntimeCapability? capability = null, bool isExecutable = false, - string processorArchitecture = "Unspecified") + string processorArchitecture = "Unspecified", + string? sdkPath = null) { + this.sdkPath = sdkPath ?? string.Empty; this.EnableVerification = !syntaxCheckOnly; this.compilationUnit = new CompilationUnit(capability ?? RuntimeCapability.FullComputation, isExecutable, processorArchitecture); this.fileContentManagers = new ConcurrentDictionary(); @@ -791,18 +795,35 @@ private void TypeCheckFile(string fileId, CancellationToken cancellationToken) /// public TextEdit[]? Formatting(TextDocumentIdentifier? textDocument) { + TextEdit[]? FormatFile(FileContentManager file) + { + var tempFile = Path.ChangeExtension(Path.GetTempFileName(), ".qs"); + var currentContent = file.GetFileContent(); + File.WriteAllText(tempFile, currentContent); + + var range = DataTypes.Range.Create(DataTypes.Position.Zero, file.End()); + var qsfmtPath = Path.Combine(this.sdkPath, "tools", "qsfmt", "qsfmt.dll"); + var compilerVersion = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version; // FIXME: OR TAKE Q# VERSION? + var commandArgs = $"{qsfmtPath} update --qsharp-version {compilerVersion} --inputs {tempFile}"; + + if (ProcessRunner.Run("dotnet", commandArgs, out var _, out var _, out var exitCode, out var ex, timeout: 3000) + && exitCode == 0 && ex == null) + { + return new[] { new TextEdit { Range = range.ToLsp(), NewText = File.ReadAllText(tempFile) } }; + } + else if (Directory.Exists(qsfmtPath)) + { + ex ??= new Exception($"Formatting exited with code {exitCode}."); + this.LogException(ex); + } + + return null; + } + var succeeded = this.Processing.QueueForExecution( - () => this.FileQuery( + () => this.FileQuery( textDocument, - (file, compilation) => - { - var tempFile = Path.ChangeExtension(Path.GetTempFileName(), ".qs"); - var currentContent = file.GetFileContent(); - File.WriteAllText(tempFile, currentContent); - - // TODO ... format the tempFile, and create an edit based on that - return null; - }, + (file, _) => FormatFile(file), suppressExceptionLogging: false), // since the operation is blocking, no exceptions should occur out var edits); return succeeded ? edits : null; diff --git a/src/QsCompiler/Compiler/Process.cs b/src/QsCompiler/CompilationManager/Process.cs similarity index 100% rename from src/QsCompiler/Compiler/Process.cs rename to src/QsCompiler/CompilationManager/Process.cs diff --git a/src/QsCompiler/CompilationManager/ProjectManager.cs b/src/QsCompiler/CompilationManager/ProjectManager.cs index 2b5b65ca70..f227b38164 100644 --- a/src/QsCompiler/CompilationManager/ProjectManager.cs +++ b/src/QsCompiler/CompilationManager/ProjectManager.cs @@ -20,6 +20,8 @@ internal class ProjectProperties { public string Version { get; } + public string SdkPath { get; } + public string OutputPath { get; } public RuntimeCapability RuntimeCapability { get; } @@ -32,13 +34,15 @@ internal class ProjectProperties public ProjectProperties( string version, + string sdkPath, string outputPath, RuntimeCapability runtimeCapability, bool isExecutable, string processorArchitecture, bool loadTestNames) { - this.Version = version ?? ""; + this.Version = version; + this.SdkPath = sdkPath; this.OutputPath = outputPath; this.RuntimeCapability = runtimeCapability; this.IsExecutable = isExecutable; @@ -65,6 +69,7 @@ internal static ProjectInformation Empty( RuntimeCapability capability) => new ProjectInformation( version, + string.Empty, // no sdk path specified outputPath, capability, false, @@ -76,6 +81,7 @@ internal static ProjectInformation Empty( public ProjectInformation( string version, + string sdkPath, string outputPath, RuntimeCapability runtimeCapability, bool isExecutable, @@ -86,7 +92,7 @@ public ProjectInformation( IEnumerable references) { this.Properties = new ProjectProperties( - version, outputPath, runtimeCapability, isExecutable, processorArchitecture, loadTestNames); + version, sdkPath, outputPath, runtimeCapability, isExecutable, processorArchitecture, loadTestNames); this.SourceFiles = sourceFiles.ToImmutableArray(); this.ProjectReferences = projectReferences.ToImmutableArray(); this.References = references.ToImmutableArray(); @@ -206,7 +212,8 @@ internal Project( syntaxCheckOnly: ignore, this.Properties.RuntimeCapability, this.Properties.IsExecutable, - this.Properties.ProcessorArchitecture); + this.Properties.ProcessorArchitecture, + sdkPath: this.Properties.SdkPath); this.log = log ?? ((msg, severity) => Console.WriteLine($"{severity}: {msg}")); this.loadedSourceFiles = ImmutableHashSet.Empty; @@ -730,7 +737,7 @@ public ProjectManager(Action? exceptionLogger, Action(); - this.defaultManager = new CompilationUnitManager(exceptionLogger, publishDiagnostics, syntaxCheckOnly: true); + this.defaultManager = new CompilationUnitManager(exceptionLogger, publishDiagnostics, syntaxCheckOnly: true, sdkPath: null); this.publishDiagnostics = publishDiagnostics; this.logException = exceptionLogger; this.log = log; @@ -1094,8 +1101,8 @@ IEnumerable> CastT /// Returns the signature help information for a call expression if there is such an expression at the specified position. /// /// - /// Returns null if some parameters are unspecified (null), - /// or if the specified file is not listed as source file, + /// Returns null if the given file is listed as to be ignored, + /// or if some parameters are unspecified (null), /// or if the specified position is not a valid position within the currently processed file content, /// or if no call expression exists at the specified position at this time, /// or if no signature help information can be provided for the call expression at the specified position. diff --git a/src/QsCompiler/LanguageServer/EditorState.cs b/src/QsCompiler/LanguageServer/EditorState.cs index c36d792b7d..c4cd041edf 100644 --- a/src/QsCompiler/LanguageServer/EditorState.cs +++ b/src/QsCompiler/LanguageServer/EditorState.cs @@ -133,6 +133,7 @@ internal bool QsProjectLoader(Uri projectFile, [NotNullWhen(true)] out ProjectIn var projectReferences = GetItemsByType(projectInstance, "ProjectReference"); var references = GetItemsByType(projectInstance, "Reference"); + var sdkPath = projectInstance.GetPropertyValue("QuantumSdkPath"); var version = projectInstance.GetPropertyValue("QSharpLangVersion"); var isExecutable = "QSharpExe".Equals(projectInstance.GetPropertyValue("ResolvedQSharpOutputType"), StringComparison.OrdinalIgnoreCase); var loadTestNames = "true".Equals(projectInstance.GetPropertyValue("ExposeReferencesViaTestNames"), StringComparison.OrdinalIgnoreCase); @@ -143,16 +144,18 @@ internal bool QsProjectLoader(Uri projectFile, [NotNullWhen(true)] out ProjectIn telemetryMeas["csharpfiles"] = csharpFiles.Count(); telemetryProps["defaultSimulator"] = defaultSimulator; this.sendTelemetry("project-load", telemetryProps, telemetryMeas); // does not send anything unless the corresponding flag is defined upon compilation + info = new ProjectInformation( - version, - outputPath, - runtimeCapability, - isExecutable, - string.IsNullOrWhiteSpace(processorArchitecture) ? "Unspecified" : processorArchitecture, - loadTestNames, - sourceFiles, - projectReferences, - references); + version: version, + sdkPath: sdkPath, + outputPath: outputPath, + runtimeCapability: runtimeCapability, + isExecutable: isExecutable, + processorArchitecture: string.IsNullOrWhiteSpace(processorArchitecture) ? "Unspecified" : processorArchitecture, + loadTestNames: loadTestNames, + sourceFiles: sourceFiles, + projectReferences: projectReferences, + references: references); return true; } diff --git a/src/QuantumSdk/DefaultItems/DefaultItems.targets b/src/QuantumSdk/DefaultItems/DefaultItems.targets index 3dc2d2e203..ba34b7f008 100644 --- a/src/QuantumSdk/DefaultItems/DefaultItems.targets +++ b/src/QuantumSdk/DefaultItems/DefaultItems.targets @@ -60,7 +60,8 @@ - "$(MSBuildThisFileDirectory)../ + $(MSBuildThisFileDirectory)../ + $([MSBuild]::Unescape('$(QuantumSdkPath)').Replace('\','/')) dotnet "$(MSBuildThisFileDirectory)../tools/utils/Microsoft.Quantum.Sdk.DefaultEntryPoint.Generation.dll" true From d5fbb2e1d80e2542bc57d2a3f6831b86057af46a Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Thu, 21 Oct 2021 00:54:40 -0700 Subject: [PATCH 08/37] ... and the corresponding code action. --- .../CompilationUnitManager.cs | 6 ++++-- .../EditorSupport/CodeActions.cs | 2 +- .../EditorSupport/EditorCommands.cs | 5 ++--- .../CompilationManager/ProjectManager.cs | 20 ++++++++++++++++++- 4 files changed, 26 insertions(+), 7 deletions(-) diff --git a/src/QsCompiler/CompilationManager/CompilationUnitManager.cs b/src/QsCompiler/CompilationManager/CompilationUnitManager.cs index dabc72b1b7..cc37216bca 100644 --- a/src/QsCompiler/CompilationManager/CompilationUnitManager.cs +++ b/src/QsCompiler/CompilationManager/CompilationUnitManager.cs @@ -801,7 +801,6 @@ private void TypeCheckFile(string fileId, CancellationToken cancellationToken) var currentContent = file.GetFileContent(); File.WriteAllText(tempFile, currentContent); - var range = DataTypes.Range.Create(DataTypes.Position.Zero, file.End()); var qsfmtPath = Path.Combine(this.sdkPath, "tools", "qsfmt", "qsfmt.dll"); var compilerVersion = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version; // FIXME: OR TAKE Q# VERSION? var commandArgs = $"{qsfmtPath} update --qsharp-version {compilerVersion} --inputs {tempFile}"; @@ -809,7 +808,10 @@ private void TypeCheckFile(string fileId, CancellationToken cancellationToken) if (ProcessRunner.Run("dotnet", commandArgs, out var _, out var _, out var exitCode, out var ex, timeout: 3000) && exitCode == 0 && ex == null) { - return new[] { new TextEdit { Range = range.ToLsp(), NewText = File.ReadAllText(tempFile) } }; + var range = DataTypes.Range.Create(DataTypes.Position.Zero, file.End()); + var edit = new TextEdit { Range = range.ToLsp(), NewText = File.ReadAllText(tempFile) }; + File.Delete(tempFile); + return new[] { edit }; } else if (Directory.Exists(qsfmtPath)) { diff --git a/src/QsCompiler/CompilationManager/EditorSupport/CodeActions.cs b/src/QsCompiler/CompilationManager/EditorSupport/CodeActions.cs index 06103e21a7..bd8ff29324 100644 --- a/src/QsCompiler/CompilationManager/EditorSupport/CodeActions.cs +++ b/src/QsCompiler/CompilationManager/EditorSupport/CodeActions.cs @@ -28,7 +28,7 @@ internal static class SuggestedEdits /// /// Returns for as a . /// - private static WorkspaceEdit GetWorkspaceEdit(this FileContentManager file, params TextEdit[] edits) + internal static WorkspaceEdit GetWorkspaceEdit(this FileContentManager file, params TextEdit[] edits) { var versionedFileId = new VersionedTextDocumentIdentifier { Uri = file.Uri, Version = file.Version ?? 1 }; return new WorkspaceEdit diff --git a/src/QsCompiler/CompilationManager/EditorSupport/EditorCommands.cs b/src/QsCompiler/CompilationManager/EditorSupport/EditorCommands.cs index 4ccbf2efae..6cb1ed114e 100644 --- a/src/QsCompiler/CompilationManager/EditorSupport/EditorCommands.cs +++ b/src/QsCompiler/CompilationManager/EditorSupport/EditorCommands.cs @@ -150,7 +150,7 @@ internal static class EditorCommands /// /// Returns null if any of the given arguments is null or if suitable edits cannot be determined. /// - public static ILookup? CodeActions(this FileContentManager file, CompilationUnit compilation, Range? range, CodeActionContext? context) + public static IEnumerable<(string, WorkspaceEdit)>? CodeActions(this FileContentManager file, CompilationUnit compilation, Range? range, CodeActionContext? context) { if (range?.Start is null || range.End is null || !file.ContainsRange(range)) { @@ -165,8 +165,7 @@ internal static class EditorCommands .Concat(file.IndexRangeSuggestions(compilation, range)) .Concat(file.UnreachableCodeSuggestions(diagnostics)) .Concat(file.DocCommentSuggestions(range)) - .Concat(file.BindingSuggestions(compilation, diagnostics)) - .ToLookup(s => s.Item1, s => s.Item2); + .Concat(file.BindingSuggestions(compilation, diagnostics)); } /// diff --git a/src/QsCompiler/CompilationManager/ProjectManager.cs b/src/QsCompiler/CompilationManager/ProjectManager.cs index f227b38164..b527c75345 100644 --- a/src/QsCompiler/CompilationManager/ProjectManager.cs +++ b/src/QsCompiler/CompilationManager/ProjectManager.cs @@ -1188,7 +1188,25 @@ IEnumerable> CastT /// public ILookup? CodeActions(CodeActionParams? param) => this.Manager(param?.TextDocument?.Uri)?.FileQuery( - param?.TextDocument, (file, c) => file.CodeActions(c, param?.Range?.ToQSharp(), param?.Context), suppressExceptionLogging: true); + param?.TextDocument, + (file, c) => + { + var codeActionSuggestions = file.CodeActions(c, param?.Range?.ToQSharp(), param?.Context); + var diagnostics = param?.Context?.Diagnostics; + // TODO: add more that can be formatted + if (diagnostics != null && diagnostics.Any(DiagnosticTools.WarningType(WarningCode.DeprecatedTupleBrackets))) + { + var formattingEdits = this.Manager(param?.TextDocument?.Uri)?.Formatting(param?.TextDocument); + if (formattingEdits != null) + { + codeActionSuggestions = codeActionSuggestions.Append( + ("Format file to update syntax.", file.GetWorkspaceEdit(formattingEdits))); + } + } + + return codeActionSuggestions.ToLookup(s => s.Item1, s => s.Item2); + }, + suppressExceptionLogging: true); /// /// Returns a list of suggested completion items for the given location. From a31adc98f09d72c9ad1c5938960a83310bf812cc Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Thu, 21 Oct 2021 17:28:27 -0700 Subject: [PATCH 09/37] additional formatting --- .../CompilationUnitManager.cs | 20 +++++++++++++------ .../CompilationManager/ProjectManager.cs | 4 ++-- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/QsCompiler/CompilationManager/CompilationUnitManager.cs b/src/QsCompiler/CompilationManager/CompilationUnitManager.cs index cc37216bca..67dc173b32 100644 --- a/src/QsCompiler/CompilationManager/CompilationUnitManager.cs +++ b/src/QsCompiler/CompilationManager/CompilationUnitManager.cs @@ -793,7 +793,7 @@ private void TypeCheckFile(string fileId, CancellationToken cancellationToken) /// Returns null if some parameters are unspecified (null), /// or if the specified file is not listed as source file /// - public TextEdit[]? Formatting(TextDocumentIdentifier? textDocument) + public TextEdit[]? Formatting(TextDocumentIdentifier? textDocument, int timeout = 3000) { TextEdit[]? FormatFile(FileContentManager file) { @@ -803,10 +803,17 @@ private void TypeCheckFile(string fileId, CancellationToken cancellationToken) var qsfmtPath = Path.Combine(this.sdkPath, "tools", "qsfmt", "qsfmt.dll"); var compilerVersion = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version; // FIXME: OR TAKE Q# VERSION? - var commandArgs = $"{qsfmtPath} update --qsharp-version {compilerVersion} --inputs {tempFile}"; + var updateArgs = $"{qsfmtPath} update --qsharp-version {compilerVersion} --inputs {tempFile}"; + var formatArgs = $"{qsfmtPath} format --inputs {tempFile}"; - if (ProcessRunner.Run("dotnet", commandArgs, out var _, out var _, out var exitCode, out var ex, timeout: 3000) - && exitCode == 0 && ex == null) + var updatingSucceeded = + ProcessRunner.Run("dotnet", updateArgs, out var _, out var _, out var exitCode, out var ex1, timeout: timeout) + && exitCode == 0 && ex1 == null; + var formattingSucceeded = + ProcessRunner.Run("dotnet", formatArgs, out var _, out var _, out exitCode, out var ex2, timeout: timeout) + && exitCode == 0 && ex2 == null; + + if (updatingSucceeded && formattingSucceeded) { var range = DataTypes.Range.Create(DataTypes.Position.Zero, file.End()); var edit = new TextEdit { Range = range.ToLsp(), NewText = File.ReadAllText(tempFile) }; @@ -815,8 +822,9 @@ private void TypeCheckFile(string fileId, CancellationToken cancellationToken) } else if (Directory.Exists(qsfmtPath)) { - ex ??= new Exception($"Formatting exited with code {exitCode}."); - this.LogException(ex); + // FIXME: CHECK IF A TIME OUT RESULTS IN THIS EXCEPTION BEING SHOWN + ex1 ??= ex2 ?? new Exception($"Formatting exited with code {exitCode}."); + this.LogException(ex1); } return null; diff --git a/src/QsCompiler/CompilationManager/ProjectManager.cs b/src/QsCompiler/CompilationManager/ProjectManager.cs index b527c75345..3c63f8da3d 100644 --- a/src/QsCompiler/CompilationManager/ProjectManager.cs +++ b/src/QsCompiler/CompilationManager/ProjectManager.cs @@ -1083,7 +1083,7 @@ IEnumerable> CastT /// or if the specified file is not listed as source file /// public TextEdit[]? Formatting(DocumentFormattingParams? param) => - this.Manager(param?.TextDocument?.Uri)?.Formatting(param?.TextDocument); // Formatting flushes unprocessed text changes + this.Manager(param?.TextDocument?.Uri)?.Formatting(param?.TextDocument, timeout: 5000); // Formatting flushes unprocessed text changes /// /// Returns the source file and position where the item at the given position is declared at, @@ -1196,7 +1196,7 @@ IEnumerable> CastT // TODO: add more that can be formatted if (diagnostics != null && diagnostics.Any(DiagnosticTools.WarningType(WarningCode.DeprecatedTupleBrackets))) { - var formattingEdits = this.Manager(param?.TextDocument?.Uri)?.Formatting(param?.TextDocument); + var formattingEdits = this.Manager(param?.TextDocument?.Uri)?.Formatting(param?.TextDocument, timeout: 1000); if (formattingEdits != null) { codeActionSuggestions = codeActionSuggestions.Append( From 1598f2f8adf6015a252f542dbb70b556a8b808a1 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Thu, 21 Oct 2021 21:57:12 -0700 Subject: [PATCH 10/37] added a test --- .../CompilationUnitManager.cs | 24 ++++--- .../CompilationManager/ProjectManager.cs | 18 +++-- .../TestProjects/test12/Operation12a.qs | 2 +- .../TestProjects/test12/Operation12b.qs | 2 +- .../TestProjects/test12/format/Formatted.qs | 11 ++++ .../TestProjects/test12/format/Unformatted.qs | 11 ++++ .../TestProjects/test12/test12.csproj | 8 +-- .../ProjectLoaderTests.cs | 65 ++++++++++--------- src/QsCompiler/Tests.LanguageServer/Tests.cs | 43 ++++++++++++ 9 files changed, 133 insertions(+), 51 deletions(-) create mode 100644 src/QsCompiler/TestProjects/test12/format/Formatted.qs create mode 100644 src/QsCompiler/TestProjects/test12/format/Unformatted.qs diff --git a/src/QsCompiler/CompilationManager/CompilationUnitManager.cs b/src/QsCompiler/CompilationManager/CompilationUnitManager.cs index 67dc173b32..327adb0723 100644 --- a/src/QsCompiler/CompilationManager/CompilationUnitManager.cs +++ b/src/QsCompiler/CompilationManager/CompilationUnitManager.cs @@ -793,8 +793,13 @@ private void TypeCheckFile(string fileId, CancellationToken cancellationToken) /// Returns null if some parameters are unspecified (null), /// or if the specified file is not listed as source file /// - public TextEdit[]? Formatting(TextDocumentIdentifier? textDocument, int timeout = 3000) + public TextEdit[]? Formatting(TextDocumentIdentifier? textDocument, bool format = true, bool update = true, int timeout = 3000) { + if (!update && !format) + { + return null; + } + TextEdit[]? FormatFile(FileContentManager file) { var tempFile = Path.ChangeExtension(Path.GetTempFileName(), ".qs"); @@ -806,25 +811,26 @@ private void TypeCheckFile(string fileId, CancellationToken cancellationToken) var updateArgs = $"{qsfmtPath} update --qsharp-version {compilerVersion} --inputs {tempFile}"; var formatArgs = $"{qsfmtPath} format --inputs {tempFile}"; - var updatingSucceeded = - ProcessRunner.Run("dotnet", updateArgs, out var _, out var _, out var exitCode, out var ex1, timeout: timeout) + // TODO: We should have a verb format-and-update + (Exception? ex1, Exception? ex2) = (null, null); + var updatingSucceeded = update + && ProcessRunner.Run("dotnet", updateArgs, out var _, out var _, out var exitCode, out ex1, timeout: timeout) && exitCode == 0 && ex1 == null; - var formattingSucceeded = - ProcessRunner.Run("dotnet", formatArgs, out var _, out var _, out exitCode, out var ex2, timeout: timeout) + var formattingSucceeded = format + && ProcessRunner.Run("dotnet", formatArgs, out var _, out var _, out exitCode, out ex2, timeout: timeout) && exitCode == 0 && ex2 == null; - if (updatingSucceeded && formattingSucceeded) + if ((!update || updatingSucceeded) && (!format || formattingSucceeded)) { var range = DataTypes.Range.Create(DataTypes.Position.Zero, file.End()); var edit = new TextEdit { Range = range.ToLsp(), NewText = File.ReadAllText(tempFile) }; File.Delete(tempFile); return new[] { edit }; } - else if (Directory.Exists(qsfmtPath)) + else if (Directory.Exists(qsfmtPath) && (ex1 != null || ex2 != null)) { // FIXME: CHECK IF A TIME OUT RESULTS IN THIS EXCEPTION BEING SHOWN - ex1 ??= ex2 ?? new Exception($"Formatting exited with code {exitCode}."); - this.LogException(ex1); + this.LogException(ex1 ?? ex2 ?? new Exception($"Failed to format and/or update file.")); } return null; diff --git a/src/QsCompiler/CompilationManager/ProjectManager.cs b/src/QsCompiler/CompilationManager/ProjectManager.cs index 3c63f8da3d..7c156fcb6f 100644 --- a/src/QsCompiler/CompilationManager/ProjectManager.cs +++ b/src/QsCompiler/CompilationManager/ProjectManager.cs @@ -821,7 +821,8 @@ private Action, Task> MigrateToDefaultManager(Func projectFiles, ProjectInformation.Loader projectLoader, - Func? openInEditor = null) + Func? openInEditor = null, + bool enableLazyLoading = true) { openInEditor ??= _ => null; @@ -847,7 +848,7 @@ public Task LoadProjectsAsync( continue; } - if (project.ContainsAnySourceFiles(uri => openInEditor(uri) != null)) + if (!enableLazyLoading || project.ContainsAnySourceFiles(uri => openInEditor(uri) != null)) { project.LoadProjectAsync(outputPaths, this.MigrateToProject(openInEditor), null); } @@ -1083,7 +1084,7 @@ IEnumerable> CastT /// or if the specified file is not listed as source file /// public TextEdit[]? Formatting(DocumentFormattingParams? param) => - this.Manager(param?.TextDocument?.Uri)?.Formatting(param?.TextDocument, timeout: 5000); // Formatting flushes unprocessed text changes + this.Manager(param?.TextDocument?.Uri)?.Formatting(param?.TextDocument, format: true, update: true, timeout: 5000); // Formatting flushes unprocessed text changes /// /// Returns the source file and position where the item at the given position is declared at, @@ -1193,14 +1194,17 @@ IEnumerable> CastT { var codeActionSuggestions = file.CodeActions(c, param?.Range?.ToQSharp(), param?.Context); var diagnostics = param?.Context?.Diagnostics; - // TODO: add more that can be formatted - if (diagnostics != null && diagnostics.Any(DiagnosticTools.WarningType(WarningCode.DeprecatedTupleBrackets))) + if (diagnostics != null && diagnostics.Any(DiagnosticTools.WarningType( + WarningCode.DeprecatedTupleBrackets, + WarningCode.DeprecatedUnitType, + WarningCode.DeprecatedQubitBindingKeyword))) // TODO: IT WOULD BE NICE TO JUST CHECK FOR DEPRECATED ANYTHING (NYI IN FORMATTER) { - var formattingEdits = this.Manager(param?.TextDocument?.Uri)?.Formatting(param?.TextDocument, timeout: 1000); + var formattingEdits = this.Manager( + param?.TextDocument?.Uri)?.Formatting(param?.TextDocument, update: true, format: false, timeout: 2000); if (formattingEdits != null) { codeActionSuggestions = codeActionSuggestions.Append( - ("Format file to update syntax.", file.GetWorkspaceEdit(formattingEdits))); + ("Update deprecated syntax in file.", file.GetWorkspaceEdit(formattingEdits))); } } diff --git a/src/QsCompiler/TestProjects/test12/Operation12a.qs b/src/QsCompiler/TestProjects/test12/Operation12a.qs index 0ac71dec42..1999004ced 100644 --- a/src/QsCompiler/TestProjects/test12/Operation12a.qs +++ b/src/QsCompiler/TestProjects/test12/Operation12a.qs @@ -5,6 +5,6 @@ namespace test3 { open Microsoft.Quantum.Canon; open Microsoft.Quantum.Intrinsic; - operation Operation3a () : Unit { + operation Operation12a () : () { } } diff --git a/src/QsCompiler/TestProjects/test12/Operation12b.qs b/src/QsCompiler/TestProjects/test12/Operation12b.qs index 2d434f82e3..c87cc79e38 100644 --- a/src/QsCompiler/TestProjects/test12/Operation12b.qs +++ b/src/QsCompiler/TestProjects/test12/Operation12b.qs @@ -5,6 +5,6 @@ namespace test3 { open Microsoft.Quantum.Canon; open Microsoft.Quantum.Intrinsic; - operation Operation3b () : Unit { + operation Operation12b () : Unit { } } diff --git a/src/QsCompiler/TestProjects/test12/format/Formatted.qs b/src/QsCompiler/TestProjects/test12/format/Formatted.qs new file mode 100644 index 0000000000..f715c04ff9 --- /dev/null +++ b/src/QsCompiler/TestProjects/test12/format/Formatted.qs @@ -0,0 +1,11 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +namespace Microsoft.Quantum.Tests { + + operation Test() : Unit { + let arr = [0, size = 10]; + for i in 1 .. 10 { + } + } +} diff --git a/src/QsCompiler/TestProjects/test12/format/Unformatted.qs b/src/QsCompiler/TestProjects/test12/format/Unformatted.qs new file mode 100644 index 0000000000..f8cdd0a64c --- /dev/null +++ b/src/QsCompiler/TestProjects/test12/format/Unformatted.qs @@ -0,0 +1,11 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +namespace Microsoft.Quantum.Tests { + + operation Test() : () { + let arr = new Int[10]; + for (i in 1 .. 10) { + } + } +} diff --git a/src/QsCompiler/TestProjects/test12/test12.csproj b/src/QsCompiler/TestProjects/test12/test12.csproj index ab491aa32a..bc2e617b54 100644 --- a/src/QsCompiler/TestProjects/test12/test12.csproj +++ b/src/QsCompiler/TestProjects/test12/test12.csproj @@ -1,11 +1,11 @@ - + + netstandard2.1 - x64 - - + + diff --git a/src/QsCompiler/Tests.LanguageServer/ProjectLoaderTests.cs b/src/QsCompiler/Tests.LanguageServer/ProjectLoaderTests.cs index 35385e95a6..4422a64ef9 100644 --- a/src/QsCompiler/Tests.LanguageServer/ProjectLoaderTests.cs +++ b/src/QsCompiler/Tests.LanguageServer/ProjectLoaderTests.cs @@ -39,11 +39,13 @@ private static string ProjectFileName(string project) => private static string SourceFileName(string project, string fileName) => Path.Combine("TestProjects", project, fileName); - private (string, ProjectInformation?) Context(string project) + internal static Uri ProjectUri(string project) => + new Uri(Path.GetFullPath(ProjectFileName(project))); + + internal static (Uri, ProjectInformation?) Context(string project) { - var relativePath = ProjectFileName(project); - var uri = new Uri(Path.GetFullPath(relativePath)); - return (uri.LocalPath, CompilationContext.Load(uri)); + var uri = ProjectUri(project); + return (uri, CompilationContext.Load(uri)); } [TestMethod] @@ -74,7 +76,7 @@ public void SupportedTargetFrameworks() [TestMethod] public void FindProjectTargetFramework() { - void CompareFramework(string project, string? expected) + static void CompareFramework(string project, string? expected) { var projectFileName = ProjectFileName(project); var props = new ProjectLoader().DesignTimeBuildProperties(projectFileName, out var _, (x, y) => (y.Contains('.') ? 1 : 0) - (x.Contains('.') ? 1 : 0)); @@ -122,7 +124,7 @@ public void LoadNonQSharpProjects() foreach (var project in invalidProjects) { - var (_, context) = this.Context(project); + var (_, context) = Context(project); Assert.IsNull(context); } } @@ -130,8 +132,8 @@ public void LoadNonQSharpProjects() [TestMethod] public void LoadOutdatedQSharpProject() { - var (projectFile, context) = this.Context("test9"); - var projDir = Path.GetDirectoryName(projectFile) ?? ""; + var (projectFile, context) = Context("test9"); + var projDir = Path.GetDirectoryName(projectFile.LocalPath) ?? ""; Assert.IsNotNull(context); Assert.AreEqual("test9.dll", Path.GetFileName(context!.Properties.OutputPath)); Assert.IsTrue((Path.GetDirectoryName(context.Properties.OutputPath) ?? "").StartsWith(projDir)); @@ -150,8 +152,8 @@ public void LoadOutdatedQSharpProject() [TestMethod] public void LoadQSharpCoreLibraries() { - var (projectFile, context) = this.Context("test3"); - var projDir = Path.GetDirectoryName(projectFile) ?? ""; + var (projectFile, context) = Context("test3"); + var projDir = Path.GetDirectoryName(projectFile.LocalPath) ?? ""; Assert.IsNotNull(context); Assert.AreEqual("test3.dll", Path.GetFileName(context!.Properties.OutputPath)); Assert.IsTrue((Path.GetDirectoryName(context.Properties.OutputPath) ?? "").StartsWith(projDir)); @@ -169,21 +171,23 @@ public void LoadQSharpCoreLibraries() Assert.IsFalse(context.UsesXunitHelper()); CollectionAssert.AreEquivalent(qsFiles, context.SourceFiles.ToArray()); - (projectFile, context) = this.Context("test12"); - projDir = Path.GetDirectoryName(projectFile) ?? ""; + (projectFile, context) = Context("test12"); + projDir = Path.GetDirectoryName(projectFile.LocalPath) ?? ""; Assert.IsNotNull(context); Assert.AreEqual("test12.dll", Path.GetFileName(context!.Properties.OutputPath)); Assert.IsTrue((Path.GetDirectoryName(context.Properties.OutputPath) ?? "").StartsWith(projDir)); qsFiles = new string[] { + Path.Combine(projDir, "format", "Unformatted.qs"), Path.Combine(projDir, "Operation12a.qs"), Path.Combine(projDir, "Operation12b.qs"), Path.Combine(projDir, "sub1", "Operation12b.qs"), Path.Combine(projDir, "sub1", "sub2", "Operation12a.qs"), }; - Assert.IsTrue(context.UsesIntrinsics()); + Assert.IsTrue(context.UsesQSharpCore()); + Assert.IsFalse(context.UsesIntrinsics()); Assert.IsTrue(context.UsesCanon()); Assert.IsFalse(context.UsesXunitHelper()); CollectionAssert.AreEquivalent(qsFiles, context.SourceFiles.ToArray()); @@ -192,8 +196,8 @@ public void LoadQSharpCoreLibraries() [TestMethod] public void LoadQSharpFrameworkLibrary() { - var (projectFile, context) = this.Context("test7"); - var projDir = Path.GetDirectoryName(projectFile) ?? ""; + var (projectFile, context) = Context("test7"); + var projDir = Path.GetDirectoryName(projectFile.LocalPath) ?? ""; Assert.IsNotNull(context); Assert.AreEqual("test7.dll", Path.GetFileName(context!.Properties.OutputPath)); Assert.IsTrue((Path.GetDirectoryName(context.Properties.OutputPath) ?? "").StartsWith(projDir)); @@ -212,8 +216,8 @@ public void LoadQSharpFrameworkLibrary() [TestMethod] public void LoadQSharpConsoleApps() { - var (projectFile, context) = this.Context("test4"); - var projDir = Path.GetDirectoryName(projectFile) ?? ""; + var (projectFile, context) = Context("test4"); + var projDir = Path.GetDirectoryName(projectFile.LocalPath) ?? ""; Assert.IsNotNull(context); Assert.AreEqual("test4.dll", Path.GetFileName(context!.Properties.OutputPath)); Assert.IsTrue((Path.GetDirectoryName(context.Properties.OutputPath) ?? "").StartsWith(projDir)); @@ -229,8 +233,8 @@ public void LoadQSharpConsoleApps() Assert.IsTrue(context.UsesProject("test3.csproj")); CollectionAssert.AreEquivalent(qsFiles, context.SourceFiles.ToArray()); - (projectFile, context) = this.Context("test10"); - projDir = Path.GetDirectoryName(projectFile) ?? ""; + (projectFile, context) = Context("test10"); + projDir = Path.GetDirectoryName(projectFile.LocalPath) ?? ""; Assert.IsNotNull(context); Assert.AreEqual("test10.dll", Path.GetFileName(context!.Properties.OutputPath)); Assert.IsTrue((Path.GetDirectoryName(context.Properties.OutputPath) ?? "").StartsWith(projDir)); @@ -244,8 +248,8 @@ public void LoadQSharpConsoleApps() Assert.IsTrue(context.UsesCanon()); CollectionAssert.AreEquivalent(qsFiles, context.SourceFiles.ToArray()); - (projectFile, context) = this.Context("test11"); - projDir = Path.GetDirectoryName(projectFile) ?? ""; + (projectFile, context) = Context("test11"); + projDir = Path.GetDirectoryName(projectFile.LocalPath) ?? ""; Assert.IsNotNull(context); Assert.AreEqual("test11.dll", Path.GetFileName(context!.Properties.OutputPath)); Assert.IsTrue((Path.GetDirectoryName(context.Properties.OutputPath) ?? "").StartsWith(projDir)); @@ -263,8 +267,8 @@ public void LoadQSharpConsoleApps() [TestMethod] public void LoadQSharpUnitTest() { - var (projectFile, context) = this.Context("test5"); - var projDir = Path.GetDirectoryName(projectFile) ?? ""; + var (projectFile, context) = Context("test5"); + var projDir = Path.GetDirectoryName(projectFile.LocalPath) ?? ""; Assert.IsNotNull(context); Assert.AreEqual("test5.dll", Path.GetFileName(context!.Properties.OutputPath)); Assert.IsTrue((Path.GetDirectoryName(context.Properties.OutputPath) ?? "").StartsWith(projDir)); @@ -287,8 +291,8 @@ public void LoadQSharpUnitTest() [TestMethod] public void LoadQSharpMultiFrameworkLibrary() { - var (projectFile, context) = this.Context("test6"); - var projDir = Path.GetDirectoryName(projectFile) ?? ""; + var (projectFile, context) = Context("test6"); + var projDir = Path.GetDirectoryName(projectFile.LocalPath) ?? ""; Assert.IsNotNull(context); Assert.AreEqual("test6.dll", Path.GetFileName(context!.Properties.OutputPath)); Assert.IsTrue((Path.GetDirectoryName(context.Properties.OutputPath) ?? "").StartsWith(projDir)); @@ -327,13 +331,14 @@ internal static class CompilationContext private static void LogOutput(string msg, MessageType level) => Console.WriteLine($"[{level}]: {msg}"); + internal static EditorState Editor => + new EditorState(new ProjectLoader(LogOutput), null, null, null, null, null); + internal static ProjectInformation? Load(Uri projectFile) => - new EditorState(new ProjectLoader(LogOutput), null, null, null, null, null) - .QsProjectLoader(projectFile, out var loaded) ? loaded : null; + Editor.QsProjectLoader(projectFile, out var loaded) ? loaded : null; internal static Uri CreateTemporaryProject(Uri sourceFile, string sdkVersion) => - new EditorState(new ProjectLoader(LogOutput), null, null, null, null, null) - .QsTemporaryProjectLoader(sourceFile, sdkVersion); + Editor.QsTemporaryProjectLoader(sourceFile, sdkVersion); internal static bool UsesDll(this ProjectInformation info, string dll) => info.References.Any(r => r.EndsWith(dll)); @@ -342,6 +347,8 @@ internal static Uri CreateTemporaryProject(Uri sourceFile, string sdkVersion) => // NB: We check whether the project uses either the 0.3–0.5 name (Primitives) or the 0.6– name (Intrinsic). internal static bool UsesIntrinsics(this ProjectInformation info) => info.UsesDll("Microsoft.Quantum.Intrinsic.dll") || info.UsesDll("Microsoft.Quantum.Primitives.dll"); + internal static bool UsesQSharpCore(this ProjectInformation info) => info.UsesDll("Microsoft.Quantum.QSharp.Core.dll"); + internal static bool UsesCanon(this ProjectInformation info) => info.UsesDll("Microsoft.Quantum.Canon.dll") || info.UsesDll("Microsoft.Quantum.Standard.dll"); diff --git a/src/QsCompiler/Tests.LanguageServer/Tests.cs b/src/QsCompiler/Tests.LanguageServer/Tests.cs index 8b5d2f8d29..39d3c468aa 100644 --- a/src/QsCompiler/Tests.LanguageServer/Tests.cs +++ b/src/QsCompiler/Tests.LanguageServer/Tests.cs @@ -2,9 +2,12 @@ // Licensed under the MIT License. using System; +using System.Collections.Generic; using System.IO; using System.Linq; +using System.Threading; using System.Threading.Tasks; +using Microsoft.Quantum.QsCompiler.CompilationBuilder; using Microsoft.VisualStudio.LanguageServer.Protocol; using Microsoft.VisualStudio.TestTools.UnitTesting; using Newtonsoft.Json.Linq; @@ -313,5 +316,45 @@ public async Task CodeContentTrackingAsync() Assert.AreEqual(expected, got); } } + + [TestMethod] + public async Task UpdateAndFormatAsync() + { + var eventSignal = new ManualResetEvent(false); + void CheckForLoadingCompleted(string msg, MessageType _) + { + if (msg.StartsWith("Done loading project")) + { + eventSignal.Set(); + } + } + + var projectFile = ProjectLoaderTests.ProjectUri("test12"); + var projDir = Path.GetDirectoryName(projectFile.LocalPath) ?? ""; + var projectManager = new ProjectManager(ex => Assert.IsNull(ex), CheckForLoadingCompleted); + await projectManager.LoadProjectsAsync( + new[] { projectFile }, + CompilationContext.Editor.QsProjectLoader, + enableLazyLoading: false); + + // Note that the formatting command will return null until the project has finished loading, + // and similarly when a project is reloaded because it has been modified. + // All in all, that seem like reasonable behavior. + eventSignal.WaitOne(); + eventSignal.Reset(); + + var fileToFormat = new Uri(Path.Combine(projDir, "format", "Unformatted.qs")); + var expectedContent = File.ReadAllText(Path.Combine(projDir, "format", "Formatted.qs")); + var param = new DocumentFormattingParams + { + TextDocument = new TextDocumentIdentifier { Uri = fileToFormat }, + Options = new FormattingOptions { TabSize = 2, InsertSpaces = false, OtherOptions = new Dictionary() }, + }; + + var edits = projectManager.Formatting(param); + Assert.IsNotNull(edits); + Assert.AreEqual(1, edits!.Length); + Assert.AreEqual(expectedContent, edits[0].NewText); + } } } From c4f4b00b3b07909f5314f07635d253b510ac403f Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Thu, 21 Oct 2021 23:00:18 -0700 Subject: [PATCH 11/37] some clean up --- .../CompilationUnitManager.cs | 4 +- src/QsCompiler/CompilationManager/Process.cs | 82 +++++++++---------- .../CompilationManager/ProjectManager.cs | 31 +++++-- src/QsCompiler/LanguageServer/EditorState.cs | 2 +- .../LanguageServer/LanguageServer.cs | 13 +-- 5 files changed, 68 insertions(+), 64 deletions(-) diff --git a/src/QsCompiler/CompilationManager/CompilationUnitManager.cs b/src/QsCompiler/CompilationManager/CompilationUnitManager.cs index 327adb0723..42d34f4089 100644 --- a/src/QsCompiler/CompilationManager/CompilationUnitManager.cs +++ b/src/QsCompiler/CompilationManager/CompilationUnitManager.cs @@ -807,8 +807,7 @@ private void TypeCheckFile(string fileId, CancellationToken cancellationToken) File.WriteAllText(tempFile, currentContent); var qsfmtPath = Path.Combine(this.sdkPath, "tools", "qsfmt", "qsfmt.dll"); - var compilerVersion = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version; // FIXME: OR TAKE Q# VERSION? - var updateArgs = $"{qsfmtPath} update --qsharp-version {compilerVersion} --inputs {tempFile}"; + var updateArgs = $"{qsfmtPath} update --inputs {tempFile}"; var formatArgs = $"{qsfmtPath} format --inputs {tempFile}"; // TODO: We should have a verb format-and-update @@ -829,7 +828,6 @@ private void TypeCheckFile(string fileId, CancellationToken cancellationToken) } else if (Directory.Exists(qsfmtPath) && (ex1 != null || ex2 != null)) { - // FIXME: CHECK IF A TIME OUT RESULTS IN THIS EXCEPTION BEING SHOWN this.LogException(ex1 ?? ex2 ?? new Exception($"Failed to format and/or update file.")); } diff --git a/src/QsCompiler/CompilationManager/Process.cs b/src/QsCompiler/CompilationManager/Process.cs index e79c679c2c..7172cecef4 100644 --- a/src/QsCompiler/CompilationManager/Process.cs +++ b/src/QsCompiler/CompilationManager/Process.cs @@ -18,59 +18,57 @@ public static class ProcessRunner /// public static bool Run(Process process, StringBuilder output, StringBuilder error, out Exception? ex, int timeout) { - using (var outputWaitHandle = new AutoResetEvent(false)) - using (var errorWaitHandle = new AutoResetEvent(false)) + using var outputWaitHandle = new AutoResetEvent(false); + using var errorWaitHandle = new AutoResetEvent(false); + void AddOutput(object sender, DataReceivedEventArgs e) { - void AddOutput(object sender, DataReceivedEventArgs e) + if (e.Data == null) { - if (e.Data == null) - { - outputWaitHandle.Set(); - } - else - { - output.AppendLine(e.Data); - } + outputWaitHandle.Set(); } - - void AddError(object sender, DataReceivedEventArgs e) + else { - if (e.Data == null) - { - errorWaitHandle.Set(); - } - else - { - error.AppendLine(e.Data); - } + output.AppendLine(e.Data); } + } - process.OutputDataReceived += AddOutput; - process.ErrorDataReceived += AddError; - - try - { - ex = null; - process.Start(); - process.BeginOutputReadLine(); - process.BeginErrorReadLine(); - return process.WaitForExit(timeout) - && outputWaitHandle.WaitOne() - && errorWaitHandle.WaitOne(); - } - catch (Exception e) + void AddError(object sender, DataReceivedEventArgs e) + { + if (e.Data == null) { - ex = e; + errorWaitHandle.Set(); } - finally + else { - // unsubscribe such that the AutoResetEvents are not accessed after disposing - process.OutputDataReceived -= AddOutput; - process.ErrorDataReceived -= AddError; + error.AppendLine(e.Data); } + } + + process.OutputDataReceived += AddOutput; + process.ErrorDataReceived += AddError; - return ex == null; + try + { + ex = null; + process.Start(); + process.BeginOutputReadLine(); + process.BeginErrorReadLine(); + return process.WaitForExit(timeout) + && outputWaitHandle.WaitOne() + && errorWaitHandle.WaitOne(); + } + catch (Exception e) + { + ex = e; } + finally + { + // unsubscribe such that the AutoResetEvents are not accessed after disposing + process.OutputDataReceived -= AddOutput; + process.ErrorDataReceived -= AddError; + } + + return ex == null; } /// @@ -114,7 +112,7 @@ public static bool Run( try { var exited = Run(process, outstream, errstream, out ex, timeout); - exitCode = process.ExitCode; + exitCode = process.HasExited ? process.ExitCode : 1; return exited; } finally diff --git a/src/QsCompiler/CompilationManager/ProjectManager.cs b/src/QsCompiler/CompilationManager/ProjectManager.cs index 7c156fcb6f..123138677c 100644 --- a/src/QsCompiler/CompilationManager/ProjectManager.cs +++ b/src/QsCompiler/CompilationManager/ProjectManager.cs @@ -1049,7 +1049,7 @@ public Task ManagerTaskAsync(Uri file, Action exec // TextDocumentEdit | CreateFile | RenameFile | DeleteFile. // Note that the SumType struct is defined in the LSP client, // and works by defining explicit cast operators for each case. - IEnumerable> CastToSumType(SumType[]>? editCollection) => + static IEnumerable> CastToSumType(SumType[]>? editCollection) => editCollection switch { { } edits => edits.Match( @@ -1083,8 +1083,18 @@ IEnumerable> CastT /// Returns null if some parameters are unspecified (null), /// or if the specified file is not listed as source file /// - public TextEdit[]? Formatting(DocumentFormattingParams? param) => - this.Manager(param?.TextDocument?.Uri)?.Formatting(param?.TextDocument, format: true, update: true, timeout: 5000); // Formatting flushes unprocessed text changes + public TextEdit[]? Formatting(DocumentFormattingParams? param) + { + var manager = this.Manager(param?.TextDocument?.Uri); + var edits = manager?.Formatting(param?.TextDocument, format: true, update: true, timeout: 10000); // Formatting flushes unprocessed text changes + + if (manager != null && edits == null) + { + this.log?.Invoke("Failed to format document. Formatter may be unavailable.", MessageType.Info); + } + + return edits; + } /// /// Returns the source file and position where the item at the given position is declared at, @@ -1187,7 +1197,7 @@ IEnumerable> CastT /// Fails silently without logging anything if an exception occurs upon evaluating the query /// (occasional failures are to be expected as the evaluation is a readonly query running in parallel to the ongoing processing). /// - public ILookup? CodeActions(CodeActionParams? param) => + public IEnumerable? CodeActions(CodeActionParams? param) => this.Manager(param?.TextDocument?.Uri)?.FileQuery( param?.TextDocument, (file, c) => @@ -1197,7 +1207,7 @@ IEnumerable> CastT if (diagnostics != null && diagnostics.Any(DiagnosticTools.WarningType( WarningCode.DeprecatedTupleBrackets, WarningCode.DeprecatedUnitType, - WarningCode.DeprecatedQubitBindingKeyword))) // TODO: IT WOULD BE NICE TO JUST CHECK FOR DEPRECATED ANYTHING (NYI IN FORMATTER) + WarningCode.DeprecatedQubitBindingKeyword))) { var formattingEdits = this.Manager( param?.TextDocument?.Uri)?.Formatting(param?.TextDocument, update: true, format: false, timeout: 2000); @@ -1208,7 +1218,16 @@ IEnumerable> CastT } } - return codeActionSuggestions.ToLookup(s => s.Item1, s => s.Item2); + return codeActionSuggestions + .ToLookup(s => s.Item1, s => s.Item2) + .SelectMany(vs => vs.Select(v => CreateAction(vs.Key, v))); + + static CodeAction CreateAction(string title, WorkspaceEdit edit) => + new CodeAction + { + Title = title, + Edit = edit, + }; }, suppressExceptionLogging: true); diff --git a/src/QsCompiler/LanguageServer/EditorState.cs b/src/QsCompiler/LanguageServer/EditorState.cs index c4cd041edf..f210301654 100644 --- a/src/QsCompiler/LanguageServer/EditorState.cs +++ b/src/QsCompiler/LanguageServer/EditorState.cs @@ -484,7 +484,7 @@ internal Task CloseFileAsync(TextDocumentIdentifier textDocument, Action - public ILookup? CodeActions(CodeActionParams param) => + public IEnumerable? CodeActions(CodeActionParams param) => ValidFileUri(param?.TextDocument?.Uri) && !this.IgnoreFile(param?.TextDocument?.Uri) ? this.projects.CodeActions(param) : null; /// diff --git a/src/QsCompiler/LanguageServer/LanguageServer.cs b/src/QsCompiler/LanguageServer/LanguageServer.cs index 485f7d3068..2ddf35f5db 100644 --- a/src/QsCompiler/LanguageServer/LanguageServer.cs +++ b/src/QsCompiler/LanguageServer/LanguageServer.cs @@ -688,15 +688,6 @@ public object OnTextDocumentSymbol(JToken arg) // list all symbols found in a gi [JsonRpcMethod(Methods.TextDocumentCodeActionName)] public object OnCodeAction(JToken arg) { - CodeAction CreateAction(string title, WorkspaceEdit edit) - { - return new CodeAction - { - Title = title, - Edit = edit, - }; - } - if (this.waitForInit != null) { return ProtocolError.AwaitingInitialization; @@ -713,9 +704,7 @@ CodeAction CreateAction(string title, WorkspaceEdit edit) { return QsCompilerError.RaiseOnFailure( - () => this.editorState.CodeActions(param) - ?.SelectMany(vs => vs.Select(v => CreateAction(vs.Key, v))) - ?? Enumerable.Empty(), + () => this.editorState.CodeActions(param) ?? Enumerable.Empty(), "CodeAction threw an exception") .ToArray(); } From ee47cdfc79e9cc75255cffd9f30693e3494153d4 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Thu, 21 Oct 2021 23:13:57 -0700 Subject: [PATCH 12/37] adjusting the command --- src/QsCompiler/CompilationManager/CompilationUnitManager.cs | 4 ++-- src/QuantumSdk/Sdk/Sdk.targets | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/QsCompiler/CompilationManager/CompilationUnitManager.cs b/src/QsCompiler/CompilationManager/CompilationUnitManager.cs index 42d34f4089..4d612b5673 100644 --- a/src/QsCompiler/CompilationManager/CompilationUnitManager.cs +++ b/src/QsCompiler/CompilationManager/CompilationUnitManager.cs @@ -807,8 +807,8 @@ private void TypeCheckFile(string fileId, CancellationToken cancellationToken) File.WriteAllText(tempFile, currentContent); var qsfmtPath = Path.Combine(this.sdkPath, "tools", "qsfmt", "qsfmt.dll"); - var updateArgs = $"{qsfmtPath} update --inputs {tempFile}"; - var formatArgs = $"{qsfmtPath} format --inputs {tempFile}"; + var updateArgs = $"{qsfmtPath} update --input {tempFile}"; + var formatArgs = $"{qsfmtPath} format --input {tempFile}"; // TODO: We should have a verb format-and-update (Exception? ex1, Exception? ex2) = (null, null); diff --git a/src/QuantumSdk/Sdk/Sdk.targets b/src/QuantumSdk/Sdk/Sdk.targets index f8ca72c19f..71ddba13df 100644 --- a/src/QuantumSdk/Sdk/Sdk.targets +++ b/src/QuantumSdk/Sdk/Sdk.targets @@ -179,7 +179,7 @@ - $(QsFmtExe) update --qsharp-version $(QuantumSdkVersion) --inputs + $(QsFmtExe) update --qsharp-version $(QuantumSdkVersion) --input $(QSharpSyntaxUpdate) "@(QSharpCompile->'%(FullPath)', '" "')" From 869e3f3e0ee5fbfc33de529c5a8c7670cfc2bd3f Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Thu, 21 Oct 2021 23:17:03 -0700 Subject: [PATCH 13/37] fixing style --- src/QsFmt/App/CommandLineArguments.fs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/QsFmt/App/CommandLineArguments.fs b/src/QsFmt/App/CommandLineArguments.fs index b536093bc3..296bfffe70 100644 --- a/src/QsFmt/App/CommandLineArguments.fs +++ b/src/QsFmt/App/CommandLineArguments.fs @@ -5,7 +5,7 @@ module Microsoft.Quantum.QsFmt.App.Arguments open System open System.IO -open System.Text.RegularExpressions; +open System.Text.RegularExpressions open CommandLine open CommandLine.Text open Microsoft.Quantum.QsFmt.App.DesignTimeBuild @@ -164,6 +164,7 @@ module Arguments = match version with | Some v -> let m = Regex.Match(v, "^[0-9\\.]+") + if m.Success then match Version.TryParse m.Value with | true, ver -> true, Some ver @@ -175,7 +176,9 @@ module Arguments = if isVersionOkay then match qsharp_version with | Some v when v < Version("0.16.2104.138035") -> - eprintfn "Error: Qdk Version is out of date. Only Qdk version 0.16.2104.138035 or later is supported." + eprintfn + "Error: Qdk Version is out of date. Only Qdk version 0.16.2104.138035 or later is supported." + 6 |> Result.Error | _ -> { @@ -191,6 +194,7 @@ module Arguments = match version with | Some v -> sprintf ": %s" v | None -> "." + eprintfn "Error: Bad Qdk version number%s" s 2 |> Result.Error else From 98bc206dcb14e385f7866522a48246297e79fcd3 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Thu, 21 Oct 2021 23:34:21 -0700 Subject: [PATCH 14/37] need to disable test until I have a set of packages --- src/QsCompiler/TestProjects/test12/test12.csproj | 2 +- src/QsCompiler/Tests.LanguageServer/Tests.cs | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/QsCompiler/TestProjects/test12/test12.csproj b/src/QsCompiler/TestProjects/test12/test12.csproj index bc2e617b54..f55cdd9b14 100644 --- a/src/QsCompiler/TestProjects/test12/test12.csproj +++ b/src/QsCompiler/TestProjects/test12/test12.csproj @@ -1,4 +1,4 @@ - + netstandard2.1 diff --git a/src/QsCompiler/Tests.LanguageServer/Tests.cs b/src/QsCompiler/Tests.LanguageServer/Tests.cs index 39d3c468aa..961d4b7958 100644 --- a/src/QsCompiler/Tests.LanguageServer/Tests.cs +++ b/src/QsCompiler/Tests.LanguageServer/Tests.cs @@ -317,7 +317,8 @@ public async Task CodeContentTrackingAsync() } } - [TestMethod] + // TODO: WILL REENABLE AS SOON AS I HAVE A SUITABLE PACKAGE THAT I CAN USE FOR THE PROJECT + // [TestMethod] public async Task UpdateAndFormatAsync() { var eventSignal = new ManualResetEvent(false); From 2dd8675de3add3e9cfb9217ca63ad7ec839fb4ec Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Fri, 22 Oct 2021 19:35:06 -0700 Subject: [PATCH 15/37] adding an update-and-format verb --- .../CompilationUnitManager.cs | 32 ++--- src/QsCompiler/Tests.LanguageServer/Tests.cs | 2 +- src/QsFmt/App.Tests/Tests.fs | 128 ++++++++++++++---- src/QsFmt/App/App.fsproj | 9 ++ src/QsFmt/App/CommandLineArguments.fs | 59 +++++++- src/QsFmt/App/CommandLineArguments.fsi | 25 +++- src/QsFmt/App/Program.fs | 20 ++- src/QsFmt/Formatter/Formatter.fs | 64 +++++---- src/QsFmt/Formatter/Formatter.fsi | 4 + src/QsFmt/Formatter/SyntaxTree/Namespace.fs | 10 +- 10 files changed, 280 insertions(+), 73 deletions(-) diff --git a/src/QsCompiler/CompilationManager/CompilationUnitManager.cs b/src/QsCompiler/CompilationManager/CompilationUnitManager.cs index 4d612b5673..21a2f413eb 100644 --- a/src/QsCompiler/CompilationManager/CompilationUnitManager.cs +++ b/src/QsCompiler/CompilationManager/CompilationUnitManager.cs @@ -795,7 +795,13 @@ private void TypeCheckFile(string fileId, CancellationToken cancellationToken) /// public TextEdit[]? Formatting(TextDocumentIdentifier? textDocument, bool format = true, bool update = true, int timeout = 3000) { - if (!update && !format) + var verb = + update && format ? "update-and-format" : + update ? "update" : + format ? "format" : + null; + + if (verb == null) { return null; } @@ -807,28 +813,22 @@ private void TypeCheckFile(string fileId, CancellationToken cancellationToken) File.WriteAllText(tempFile, currentContent); var qsfmtPath = Path.Combine(this.sdkPath, "tools", "qsfmt", "qsfmt.dll"); - var updateArgs = $"{qsfmtPath} update --input {tempFile}"; - var formatArgs = $"{qsfmtPath} format --input {tempFile}"; - - // TODO: We should have a verb format-and-update - (Exception? ex1, Exception? ex2) = (null, null); - var updatingSucceeded = update - && ProcessRunner.Run("dotnet", updateArgs, out var _, out var _, out var exitCode, out ex1, timeout: timeout) - && exitCode == 0 && ex1 == null; - var formattingSucceeded = format - && ProcessRunner.Run("dotnet", formatArgs, out var _, out var _, out exitCode, out ex2, timeout: timeout) - && exitCode == 0 && ex2 == null; - - if ((!update || updatingSucceeded) && (!format || formattingSucceeded)) + var commandArgs = $"{qsfmtPath} {verb} --input {tempFile}"; + + var succeeded = + ProcessRunner.Run("dotnet", commandArgs, out var _, out var _, out var exitCode, out var ex, timeout: timeout) + && exitCode == 0 && ex == null; + + if (succeeded) { var range = DataTypes.Range.Create(DataTypes.Position.Zero, file.End()); var edit = new TextEdit { Range = range.ToLsp(), NewText = File.ReadAllText(tempFile) }; File.Delete(tempFile); return new[] { edit }; } - else if (Directory.Exists(qsfmtPath) && (ex1 != null || ex2 != null)) + else if (Directory.Exists(qsfmtPath) && ex != null) { - this.LogException(ex1 ?? ex2 ?? new Exception($"Failed to format and/or update file.")); + this.LogException(ex); } return null; diff --git a/src/QsCompiler/Tests.LanguageServer/Tests.cs b/src/QsCompiler/Tests.LanguageServer/Tests.cs index 961d4b7958..40eef842a5 100644 --- a/src/QsCompiler/Tests.LanguageServer/Tests.cs +++ b/src/QsCompiler/Tests.LanguageServer/Tests.cs @@ -322,7 +322,7 @@ public async Task CodeContentTrackingAsync() public async Task UpdateAndFormatAsync() { var eventSignal = new ManualResetEvent(false); - void CheckForLoadingCompleted(string msg, MessageType _) + void CheckForLoadingCompleted(string msg, MessageType messageType) { if (msg.StartsWith("Done loading project")) { diff --git a/src/QsFmt/App.Tests/Tests.fs b/src/QsFmt/App.Tests/Tests.fs index 142c170ac2..c5eb8bcb87 100644 --- a/src/QsFmt/App.Tests/Tests.fs +++ b/src/QsFmt/App.Tests/Tests.fs @@ -6,6 +6,7 @@ module Microsoft.Quantum.QsFmt.App.Tests open System open System.IO +open Microsoft.Quantum.QsFmt.App.Arguments open Microsoft.Quantum.QsFmt.App.Program open Xunit @@ -28,6 +29,7 @@ type private TestFile = Original: string Formatted: string Updated: string + UpdatedAndFormatted: String } /// @@ -64,6 +66,17 @@ let private makeTestFile (path: string) = name |> sprintf "namespace %s { function Bar() : Int { for i in 0..1 {} return 0; } } +" + |> standardizeNewLines + UpdatedAndFormatted = + name + |> sprintf + "namespace %s { + function Bar() : Int { + for i in 0..1 {} + return 0; + } +} " |> standardizeNewLines } @@ -93,6 +106,15 @@ let private StandardInputTest = |> standardizeNewLines Updated = "namespace StandardIn { function Bar() : Int { for i in 0..1 {} return 0; } } +" + |> standardizeNewLines + UpdatedAndFormatted = + "namespace StandardIn { + function Bar() : Int { + for i in 0..1 {} + return 0; + } +} " |> standardizeNewLines } @@ -125,21 +147,33 @@ let private run args input = Console.SetOut previousOutput Console.SetError previousError -let private runWithFiles isUpdate files standardInput expectedOutput args = +let private runWithFiles (commandKind : CommandKind) files standardInput expectedOutput args = try Assert.Equal(expectedOutput, run args standardInput) for file in files do let after = File.ReadAllText file.Path |> standardizeNewLines - let expected = (if isUpdate then file.Updated else file.Formatted) + let expected = + match commandKind with + | Format -> file.Formatted + | Update -> file.Updated + | UpdateAndFormat -> file.UpdatedAndFormatted Assert.Equal(expected, after) finally for file in files do File.WriteAllText(file.Path, file.Original) [] -let ``Updates file`` () = - runWithFiles true [ Example1 ] "" CleanResult [| "update"; "-i"; Example1.Path |] +let ``Format file`` () = + runWithFiles Format [ Example1 ] "" CleanResult [| "format"; "-i"; Example1.Path |] + +[] +let ``Update file`` () = + runWithFiles Update [ Example1 ] "" CleanResult [| "update"; "-i"; Example1.Path |] + +[] +let ``Update and format file`` () = + runWithFiles UpdateAndFormat [ Example1 ] "" CleanResult [| "update-and-format"; "-i"; Example1.Path |] [] let ``Updates standard input`` () = @@ -177,19 +211,25 @@ let ``Shows file not found error`` () = let ``Input multiple files`` () = let files = [ Example1; Example2 ] - runWithFiles true files "" CleanResult [| "update"; "-i"; Example1.Path; Example2.Path |] + runWithFiles Format files "" CleanResult [| "format"; "-i"; Example1.Path; Example2.Path |] + runWithFiles Update files "" CleanResult [| "update"; "-i"; Example1.Path; Example2.Path |] + runWithFiles UpdateAndFormat files "" CleanResult [| "update-and-format"; "-i"; Example1.Path; Example2.Path |] [] let ``Input directories`` () = let files = [ SubExample1; SubExample2; SubExample3 ] - runWithFiles true files "" CleanResult [| "update"; "-i"; "Examples\\SubExamples1"; "Examples\\SubExamples2" |] + runWithFiles Format files "" CleanResult [| "format"; "-i"; "Examples\\SubExamples1"; "Examples\\SubExamples2" |] + runWithFiles Update files "" CleanResult [| "update"; "-i"; "Examples\\SubExamples1"; "Examples\\SubExamples2" |] + runWithFiles UpdateAndFormat files "" CleanResult [| "update-and-format"; "-i"; "Examples\\SubExamples1"; "Examples\\SubExamples2" |] [] let ``Input directories with files`` () = let files = [ Example1; SubExample1; SubExample2 ] - runWithFiles true files "" CleanResult [| "update"; "-i"; Example1.Path; "Examples\\SubExamples1" |] + runWithFiles Format files "" CleanResult [| "format"; "-i"; Example1.Path; "Examples\\SubExamples1" |] + runWithFiles Update files "" CleanResult [| "update"; "-i"; Example1.Path; "Examples\\SubExamples1" |] + runWithFiles UpdateAndFormat files "" CleanResult [| "update-and-format"; "-i"; Example1.Path; "Examples\\SubExamples1" |] [] let ``Input directories with recursive flag`` () = @@ -203,9 +243,9 @@ let ``Input directories with recursive flag`` () = NestedExample2 ] - let args = + let args verb = [| - "update" + verb "-r" "-i" Example1.Path @@ -213,7 +253,9 @@ let ``Input directories with recursive flag`` () = "Examples\\SubExamples2" |] - runWithFiles true files "" CleanResult args + args "format" |> runWithFiles Format files "" CleanResult + args "update" |> runWithFiles Update files "" CleanResult + args "update-and-format" |> runWithFiles UpdateAndFormat files "" CleanResult [] let ``Process correct files while erroring on incorrect`` () = @@ -267,16 +309,18 @@ This input has already been processed: Examples\Example1.qs " |> standardizeNewLines } - - [| - "update" - "-i" - Example1.Path - "Examples\\SubExamples1" - SubExample1.Path - Example1.Path - |] - |> runWithFiles true files "" outputResult + let args verb = + [| + verb + "-i" + Example1.Path + "Examples\\SubExamples1" + SubExample1.Path + Example1.Path + |] + args "format" |> runWithFiles Format files "" outputResult + args "update" |> runWithFiles Update files "" outputResult + args "update-and-format" |> runWithFiles UpdateAndFormat files "" outputResult [] let ``Project file as input`` () = @@ -302,6 +346,18 @@ let ``Project file as input`` () = "namespace TestTarget { function Bar%i() : Int { for i in 0..1 {} return 0; } } " |> standardizeNewLines + UpdatedAndFormatted = + index + |> sprintf + "namespace TestTarget { + function Bar%i() : Int { + for i in 0..1 {} + return 0; + } +} +" + |> standardizeNewLines + } let TestTargetProgram = @@ -321,6 +377,15 @@ let ``Project file as input`` () = |> standardizeNewLines Updated = "namespace TestTarget { @EntryPoint() operation Bar() : Unit { for i in 0..1 {} } } +" + |> standardizeNewLines + UpdatedAndFormatted = + "namespace TestTarget { + @EntryPoint() + operation Bar() : Unit { + for i in 0..1 {} + } +} " |> standardizeNewLines } @@ -332,14 +397,25 @@ let ``Project file as input`` () = let files = [ TestTargetProgram; TestTargetIncluded ] - [| "update"; "-p"; "Examples\\TestTarget\\TestTarget.csproj" |] - |> runWithFiles true files "" CleanResult + let testCommand commandKind = + let verb = + match commandKind with + | Format -> "format" + | Update -> "update" + | UpdateAndFormat -> "update-and-format" + + [| verb; "-p"; "Examples\\TestTarget\\TestTarget.csproj" |] + |> runWithFiles commandKind files "" CleanResult + + let excluded1 = File.ReadAllText TestTargetExcluded1.Path + Assert.Equal(excluded1, TestTargetExcluded1.Original) - let excluded1 = File.ReadAllText TestTargetExcluded1.Path - Assert.Equal(excluded1, TestTargetExcluded1.Original) + let excluded2 = File.ReadAllText TestTargetExcluded2.Path + Assert.Equal(excluded2, TestTargetExcluded2.Original) - let excluded2 = File.ReadAllText TestTargetExcluded2.Path - Assert.Equal(excluded2, TestTargetExcluded2.Original) + testCommand Format + testCommand Update + testCommand UpdateAndFormat [] let ``Outdated version project file as input`` () = diff --git a/src/QsFmt/App/App.fsproj b/src/QsFmt/App/App.fsproj index d3a15f89bf..4f5ab7054b 100644 --- a/src/QsFmt/App/App.fsproj +++ b/src/QsFmt/App/App.fsproj @@ -4,6 +4,15 @@ netcoreapp3.1 qsfmt Microsoft.Quantum.QsFmt.App + + MSB3277 diff --git a/src/QsFmt/App/CommandLineArguments.fs b/src/QsFmt/App/CommandLineArguments.fs index 296bfffe70..dcf42e015c 100644 --- a/src/QsFmt/App/CommandLineArguments.fs +++ b/src/QsFmt/App/CommandLineArguments.fs @@ -13,7 +13,6 @@ open Microsoft.Quantum.QsFmt.App.DesignTimeBuild [ internal CompilationUnit( - RuntimeCapability capability, - bool isExecutable, - string processorArchitecture, + ProjectProperties projectProperties, References? externals = null, IEnumerable? dependentLocks = null) { @@ -350,16 +344,19 @@ internal CompilationUnit( this.syncRoot = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion); this.dependentLocks = new HashSet(dependentLocks); - this.RuntimeCapability = capability; - this.IsExecutable = isExecutable; - this.ProcessorArchitecture = processorArchitecture; - this.compiledCallables = new Dictionary(); this.compiledTypes = new Dictionary(); this.Externals = externals; + this.BuildProperties = projectProperties; this.GlobalSymbols = this.CreateGlobalSymbols(); } + /// + internal CompilationUnit(CompilationUnit compilationUnit, IEnumerable? dependentLocks = null) + : this(compilationUnit.BuildProperties, compilationUnit.Externals, dependentLocks ?? compilationUnit.dependentLocks) + { + } + /// /// Creates a new instance of the namespace manager for global symbols. /// @@ -369,8 +366,8 @@ internal CompilationUnit( this.Externals.Declarations.Values.SelectMany(h => h.Specializations.Select(t => Tuple.Create(t.Item1, t.Item2))), this.Externals.Declarations.Values.SelectMany(h => h.Types), - this.RuntimeCapability, - this.IsExecutable); + this.BuildProperties.RuntimeCapability, + this.BuildProperties.IsExecutable); /// /// Replaces to match . diff --git a/src/QsCompiler/CompilationManager/CompilationUnitManager.cs b/src/QsCompiler/CompilationManager/CompilationUnitManager.cs index 21a2f413eb..7a0223ee34 100644 --- a/src/QsCompiler/CompilationManager/CompilationUnitManager.cs +++ b/src/QsCompiler/CompilationManager/CompilationUnitManager.cs @@ -28,8 +28,6 @@ namespace Microsoft.Quantum.QsCompiler.CompilationBuilder /// public class CompilationUnitManager : IDisposable { - private readonly string sdkPath; - internal bool EnableVerification { get; private set; } /// @@ -69,24 +67,15 @@ public class CompilationUnitManager : IDisposable /// protected ProcessingQueue Processing { get; } - /// - /// Initializes a instance for a project with the given properties. - /// - /// - /// If provided, called whenever diagnostics within a file have changed and are ready for publishing. - /// + /// public CompilationUnitManager( + ProjectProperties buildProperties, Action? exceptionLogger = null, Action? publishDiagnostics = null, - bool syntaxCheckOnly = false, - RuntimeCapability? capability = null, - bool isExecutable = false, - string processorArchitecture = "Unspecified", - string? sdkPath = null) + bool syntaxCheckOnly = false) { - this.sdkPath = sdkPath ?? string.Empty; this.EnableVerification = !syntaxCheckOnly; - this.compilationUnit = new CompilationUnit(capability ?? RuntimeCapability.FullComputation, isExecutable, processorArchitecture); + this.compilationUnit = new CompilationUnit(buildProperties); this.fileContentManagers = new ConcurrentDictionary(); this.changedFiles = new ManagedHashSet(new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion)); this.PublishDiagnostics = publishDiagnostics ?? (_ => { }); @@ -95,6 +84,39 @@ public CompilationUnitManager( this.waitForTypeCheck = new CancellationTokenSource(); } + [Obsolete("Use CompilationUnitManager(ProjectProperties, Action{Exception}?, Action{PublishDiagnosticParams}?, bool) instead.")] + public CompilationUnitManager( + Action? exceptionLogger = null, + Action? publishDiagnostics = null, + bool syntaxCheckOnly = false, + RuntimeCapability? capability = null, + bool isExecutable = false, + string processorArchitecture = "Unspecified") + : this( + new ProjectProperties(ImmutableDictionary.CreateRange(new[] + { + // TODO + new KeyValuePair("", ""), + })), + exceptionLogger, + publishDiagnostics) + { + } + + /// + /// Initializes a instance for a project with the given properties. + /// + /// + /// If provided, called whenever diagnostics within a file have changed and are ready for publishing. + /// + public CompilationUnitManager( + Action? exceptionLogger = null, + Action? publishDiagnostics = null, + bool syntaxCheckOnly = false) + : this(ProjectProperties.Empty, exceptionLogger, publishDiagnostics, syntaxCheckOnly) + { + } + /// /// Cancels any asynchronously running ongoing global type checking. After all currently queued tasks /// have finished, locks all processing, flushes the unprocessed changes in each source file, @@ -586,12 +608,7 @@ private Task SpawnGlobalTypeCheckingAsync(bool runSynchronously = false) // work with a separate compilation unit instance such that processing of all further edits can go on in parallel var sourceFiles = this.fileContentManagers.Values.OrderBy(m => m.FileName); this.changedFiles.RemoveAll(f => sourceFiles.Any(m => m.FileName == f)); - var compilation = new CompilationUnit( - this.compilationUnit.RuntimeCapability, - this.compilationUnit.IsExecutable, - this.compilationUnit.ProcessorArchitecture, - this.compilationUnit.Externals, - sourceFiles.Select(file => file.SyncRoot)); + var compilation = new CompilationUnit(this.compilationUnit, sourceFiles.Select(file => file.SyncRoot)); var content = compilation.UpdateGlobalSymbolsFor(sourceFiles); foreach (var file in sourceFiles) { @@ -801,7 +818,8 @@ private void TypeCheckFile(string fileId, CancellationToken cancellationToken) format ? "format" : null; - if (verb == null) + var sdkPath = this.compilationUnit.BuildProperties.SdkPath; + if (string.IsNullOrWhiteSpace(sdkPath) || verb == null) { return null; } @@ -812,7 +830,7 @@ private void TypeCheckFile(string fileId, CancellationToken cancellationToken) var currentContent = file.GetFileContent(); File.WriteAllText(tempFile, currentContent); - var qsfmtPath = Path.Combine(this.sdkPath, "tools", "qsfmt", "qsfmt.dll"); + var qsfmtPath = Path.Combine(sdkPath, "tools", "qsfmt", "qsfmt.dll"); var commandArgs = $"{qsfmtPath} {verb} --input {tempFile}"; var succeeded = diff --git a/src/QsCompiler/CompilationManager/ProjectManager.cs b/src/QsCompiler/CompilationManager/ProjectManager.cs index 123138677c..ad86dbd675 100644 --- a/src/QsCompiler/CompilationManager/ProjectManager.cs +++ b/src/QsCompiler/CompilationManager/ProjectManager.cs @@ -11,44 +11,90 @@ using System.Reflection; using System.Threading.Tasks; using Microsoft.Quantum.QsCompiler.Diagnostics; +using Microsoft.Quantum.QsCompiler.ReservedKeywords; using Microsoft.Quantum.QsCompiler.Transformations; using Microsoft.VisualStudio.LanguageServer.Protocol; namespace Microsoft.Quantum.QsCompiler.CompilationBuilder { - internal class ProjectProperties + /// + /// Represents project properties defined in the project file. + /// + public class ProjectProperties { - public string Version { get; } + private static readonly Version DefaultAssemblyVersion = + Assembly.GetExecutingAssembly().GetName().Version; - public string SdkPath { get; } + /// + /// Returns the value specified by , + /// or the language version that corresponding to this compiler version if no valid value is specified. + /// + public Version LanguageVersion => + this.BuildProperties.TryGetValue(MSBuildProperties.QsharpLangVersion, out var versionProp) + && Version.TryParse(versionProp, out Version version) + ? version + : new Version(DefaultAssemblyVersion.Major, DefaultAssemblyVersion.Minor); - public string OutputPath { get; } + /// + /// Returns the value specified by , + /// or an empty string if no value is specified. + /// + public string? SdkPath => + this.BuildProperties.TryGetValue(MSBuildProperties.QuantumSdkPath, out var path) + ? path + : string.Empty; - public RuntimeCapability RuntimeCapability { get; } + /// + /// Returns the value specified by , + /// or an empty string if no value is specified. + /// + public string? DllOutputPath => + this.BuildProperties.TryGetValue(MSBuildProperties.TargetPath, out var path) + ? path + : string.Empty; - public bool IsExecutable { get; } + /// + /// Returns the value specified by , + /// or if no valid value is specified. + /// + public RuntimeCapability RuntimeCapability => + this.BuildProperties.TryGetValue(MSBuildProperties.ResolvedRuntimeCapabilities, out var capability) + ? RuntimeCapability.TryParse(capability).ValueOr(RuntimeCapability.FullComputation) + : RuntimeCapability.FullComputation; - public string ProcessorArchitecture { get; } + /// + /// Returns the value specified by , + /// or an user friendly string indicating and unspecified processor architecture if no value is specified. + /// + public string ProcessorArchitecture => + this.BuildProperties.TryGetValue(MSBuildProperties.ResolvedProcessorArchitecture, out var architecture) + && !string.IsNullOrEmpty(architecture) + ? architecture + : "Unspecified"; - public bool ExposeReferencesViaTestNames { get; } + /// + /// Returns true if the indicates that + /// the project is an executable project opposed to a library. + /// + public bool IsExecutable => + this.BuildProperties.TryGetValue(MSBuildProperties.ResolvedQsharpOutputType, out var outputType) + && AssemblyConstants.QsharpExe.Equals(outputType, StringComparison.OrdinalIgnoreCase); - public ProjectProperties( - string version, - string sdkPath, - string outputPath, - RuntimeCapability runtimeCapability, - bool isExecutable, - string processorArchitecture, - bool loadTestNames) - { - this.Version = version; - this.SdkPath = sdkPath; - this.OutputPath = outputPath; - this.RuntimeCapability = runtimeCapability; - this.IsExecutable = isExecutable; - this.ProcessorArchitecture = processorArchitecture; - this.ExposeReferencesViaTestNames = loadTestNames; - } + /// + /// Returns true if the indicates that + /// declarations should be loaded via a test name specified by an attribute. + /// + internal bool ExposeReferencesViaTestNames => + this.BuildProperties.TryGetValue(MSBuildProperties.ExposeReferencesViaTestNames, out var exposeViaTestNames) + && "true".Equals(exposeViaTestNames, StringComparison.OrdinalIgnoreCase); + + private ImmutableDictionary BuildProperties { get; } + + public static ProjectProperties Empty => + new ProjectProperties(ImmutableDictionary.Empty); + + public ProjectProperties(IDictionary buildProperties) => + this.BuildProperties = buildProperties.ToImmutableDictionary(); } public class ProjectInformation @@ -63,25 +109,31 @@ public class ProjectInformation public ImmutableArray References { get; } - internal static ProjectInformation Empty( - string version, - string outputPath, - RuntimeCapability capability) => + internal static ProjectInformation Empty(string outputPath) => new ProjectInformation( - version, - string.Empty, // no sdk path specified - outputPath, - capability, - false, - "Unspecified", - false, Enumerable.Empty(), Enumerable.Empty(), - Enumerable.Empty()); + Enumerable.Empty(), + ImmutableDictionary.CreateRange(new[] + { + new KeyValuePair(MSBuildProperties.TargetPath, outputPath), + })); + public ProjectInformation( + IEnumerable sourceFiles, + IEnumerable projectReferences, + IEnumerable references, + IDictionary buildProperties) + { + this.Properties = new ProjectProperties(buildProperties); + this.SourceFiles = sourceFiles.ToImmutableArray(); + this.ProjectReferences = projectReferences.ToImmutableArray(); + this.References = references.ToImmutableArray(); + } + + [Obsolete] public ProjectInformation( string version, - string sdkPath, string outputPath, RuntimeCapability runtimeCapability, bool isExecutable, @@ -91,8 +143,15 @@ public ProjectInformation( IEnumerable projectReferences, IEnumerable references) { - this.Properties = new ProjectProperties( - version, sdkPath, outputPath, runtimeCapability, isExecutable, processorArchitecture, loadTestNames); + var buildProperties = ImmutableDictionary.CreateBuilder(); + buildProperties.Add(MSBuildProperties.QsharpLangVersion, version); + buildProperties.Add(MSBuildProperties.TargetPath, outputPath); + buildProperties.Add(MSBuildProperties.ResolvedRuntimeCapabilities, runtimeCapability.Name); + buildProperties.Add(MSBuildProperties.ResolvedQsharpOutputType, isExecutable ? AssemblyConstants.QsharpExe : AssemblyConstants.QsharpLibrary); + buildProperties.Add(MSBuildProperties.ResolvedProcessorArchitecture, processorArchitecture); + buildProperties.Add(MSBuildProperties.ExposeReferencesViaTestNames, loadTestNames ? "true" : "false"); + + this.Properties = new ProjectProperties(buildProperties); this.SourceFiles = sourceFiles.ToImmutableArray(); this.ProjectReferences = projectReferences.ToImmutableArray(); this.References = references.ToImmutableArray(); @@ -195,25 +254,17 @@ internal Project( this.Properties = projectInfo.Properties; this.SetProjectInformation(projectInfo); - var version = Version.TryParse(projectInfo.Properties.Version, out Version v) ? v : null; - if (projectInfo.Properties.Version.Equals("Latest", StringComparison.InvariantCultureIgnoreCase)) - { - version = new Version(0, 3); - } - - var ignore = version == null || version < new Version(0, 3) ? true : false; + var version = projectInfo.Properties.LanguageVersion; + var ignore = version == null || version < new Version(0, 3); // We track the file contents for unsupported projects in case the files are migrated to newer projects while editing, // but we don't do any semantic verification, and we don't publish diagnostics for them. this.processing = new ProcessingQueue(onException); this.Manager = new CompilationUnitManager( + this.Properties, onException, ignore ? null : publishDiagnostics, - syntaxCheckOnly: ignore, - this.Properties.RuntimeCapability, - this.Properties.IsExecutable, - this.Properties.ProcessorArchitecture, - sdkPath: this.Properties.SdkPath); + syntaxCheckOnly: ignore); this.log = log ?? ((msg, severity) => Console.WriteLine($"{severity}: {msg}")); this.loadedSourceFiles = ImmutableHashSet.Empty; @@ -233,7 +284,7 @@ private void SetProjectInformation(ProjectInformation projectInfo) this.Properties = projectInfo.Properties; this.isLoaded = false; - var outputPath = projectInfo.Properties.OutputPath; + var outputPath = projectInfo.Properties.DllOutputPath; try { outputPath = Path.GetFullPath(outputPath); @@ -737,7 +788,7 @@ public ProjectManager(Action? exceptionLogger, Action(); - this.defaultManager = new CompilationUnitManager(exceptionLogger, publishDiagnostics, syntaxCheckOnly: true, sdkPath: null); + this.defaultManager = new CompilationUnitManager(exceptionLogger, publishDiagnostics, syntaxCheckOnly: true); this.publishDiagnostics = publishDiagnostics; this.logException = exceptionLogger; this.log = log; @@ -881,9 +932,7 @@ public Task ProjectChangedOnDiskAsync( null, this.MigrateToDefaultManager(openInEditor), ProjectInformation.Empty( - "Latest", - existing.OutputPath?.LocalPath ?? throw new Exception("Missing output path."), - RuntimeCapability.FullComputation)) + existing.OutputPath?.LocalPath ?? throw new Exception("Missing output path."))) ?.Wait(); // does need to block, or the call to the DefaultManager in ManagerTaskAsync needs to be adapted if (existing != null) { diff --git a/src/QsCompiler/CompilationManager/TypeChecking.cs b/src/QsCompiler/CompilationManager/TypeChecking.cs index 341c432c79..ff7705618b 100644 --- a/src/QsCompiler/CompilationManager/TypeChecking.cs +++ b/src/QsCompiler/CompilationManager/TypeChecking.cs @@ -1666,8 +1666,8 @@ QsSpecialization BuildSpecialization( var requiredFunctorSupport = RequiredFunctorSupport(kind, GetDirective).ToImmutableHashSet(); var context = ScopeContext.Create( compilation.GlobalSymbols, - compilation.RuntimeCapability, - compilation.ProcessorArchitecture, + compilation.BuildProperties.RuntimeCapability, + compilation.BuildProperties.ProcessorArchitecture, spec); implementation = BuildUserDefinedImplementation( root, spec.Source.AssemblyOrCodeFile, arg, requiredFunctorSupport, context, diagnostics); diff --git a/src/QsCompiler/Compiler/CompilationLoader.cs b/src/QsCompiler/Compiler/CompilationLoader.cs index ddf004104e..5debc43d65 100644 --- a/src/QsCompiler/Compiler/CompilationLoader.cs +++ b/src/QsCompiler/Compiler/CompilationLoader.cs @@ -529,14 +529,16 @@ public CompilationLoader(SourceLoader loadSources, ReferenceLoader loadReference PerformanceTracking.TaskStart(PerformanceTracking.Task.Build); this.compilationStatus.Validation = Status.Succeeded; var files = CompilationUnitManager.InitializeFileManagers(sourceFiles, null, this.OnCompilerException); // do *not* live track (i.e. use publishing) here! + var processorArchitecture = this.config.AssemblyConstants?.GetValueOrDefault(AssemblyConstants.ProcessorArchitecture); + var buildProperties = ImmutableDictionary.CreateBuilder(); + buildProperties.Add(MSBuildProperties.ResolvedRuntimeCapabilities, this.config.RuntimeCapability?.Name); + buildProperties.Add(MSBuildProperties.ResolvedQsharpOutputType, this.config.IsExecutable ? AssemblyConstants.QsharpExe : AssemblyConstants.QsharpLibrary); + buildProperties.Add(MSBuildProperties.ResolvedProcessorArchitecture, processorArchitecture); + var compilationManager = new CompilationUnitManager( - this.OnCompilerException, - capability: this.config.RuntimeCapability, - isExecutable: this.config.IsExecutable, - processorArchitecture: string.IsNullOrWhiteSpace(processorArchitecture) - ? "Unspecified" - : processorArchitecture); + new ProjectProperties(buildProperties), + this.OnCompilerException); compilationManager.UpdateReferencesAsync(references); compilationManager.AddOrUpdateSourceFilesAsync(files); this.VerifiedCompilation = compilationManager.Build(); diff --git a/src/QsCompiler/DataStructures/ReservedKeywords.fs b/src/QsCompiler/DataStructures/ReservedKeywords.fs index 53b7c7f2f4..8b88a8e485 100644 --- a/src/QsCompiler/DataStructures/ReservedKeywords.fs +++ b/src/QsCompiler/DataStructures/ReservedKeywords.fs @@ -238,9 +238,19 @@ module GeneratedAttributes = let Namespace = "Microsoft.Quantum.QsCompiler.Metadata.Attributes" let LoadedViaTestNameInsteadOf = "__LoadedViaTestNameInsteadOf__" -/// contains project specific settings specified during Q# compilation +/// contains project properties defined in the project file and used by MSBuild +module MSBuildProperties = + let QuantumSdkPath = "QuantumSdkPath" + let QsharpLangVersion = "QSharpLangVersion" + let TargetPath = "TargetPath" + let ResolvedProcessorArchitecture = "ResolvedProcessorArchitecture" + let ResolvedRuntimeCapabilities = "ResolvedRuntimeCapabilities" + let ResolvedQsharpOutputType = "ResolvedQSharpOutputType" + let ExposeReferencesViaTestNames = "ExposeReferencesViaTestNames" + +/// contains project specific settings specified that can be accessed by rewrite steps during Q# compilation module AssemblyConstants = - let OutputPath = "OutputPath" + let OutputPath = "OutputPath" // defined by the CompilationLoader let AssemblyName = "AssemblyName" let QsharpOutputType = "QSharpOutputType" let QsharpExe = "QSharpExe" diff --git a/src/QsCompiler/DataStructures/RuntimeCapability.fs b/src/QsCompiler/DataStructures/RuntimeCapability.fs index 9661c411fc..9535b51abe 100644 --- a/src/QsCompiler/DataStructures/RuntimeCapability.fs +++ b/src/QsCompiler/DataStructures/RuntimeCapability.fs @@ -59,6 +59,12 @@ type RuntimeCapability = | "Unknown" -> Value FullComputation | _ -> Null + member this.Name = + match this with + | BasicQuantumFunctionality -> "BasicQuantumFunctionality" + | BasicMeasurementFeedback -> "BasicMeasurementFeedback" + | FullComputation -> "FullComputation" + // TODO: RELEASE 2021-04: Remove RuntimeCapabilitiesExtensions. [] [] diff --git a/src/QsCompiler/LanguageServer/EditorState.cs b/src/QsCompiler/LanguageServer/EditorState.cs index f210301654..8c49c54015 100644 --- a/src/QsCompiler/LanguageServer/EditorState.cs +++ b/src/QsCompiler/LanguageServer/EditorState.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; @@ -11,6 +12,7 @@ using Microsoft.Build.Execution; using Microsoft.Quantum.QsCompiler; using Microsoft.Quantum.QsCompiler.CompilationBuilder; +using Microsoft.Quantum.QsCompiler.ReservedKeywords; using Microsoft.VisualStudio.LanguageServer.Protocol; namespace Microsoft.Quantum.QsLanguageServer @@ -120,42 +122,43 @@ internal bool QsProjectLoader(Uri projectFile, [NotNullWhen(true)] out ProjectIn return false; } - var outputDir = projectInstance.GetPropertyValue("OutputPath"); - var targetFile = projectInstance.GetPropertyValue("TargetFileName"); - var outputPath = Path.Combine(projectInstance.Directory, outputDir, targetFile); - - var processorArchitecture = projectInstance.GetPropertyValue("ResolvedProcessorArchitecture"); - var resRuntimeCapability = projectInstance.GetPropertyValue("ResolvedRuntimeCapabilities"); - var runtimeCapability = RuntimeCapability.TryParse(resRuntimeCapability).ValueOr(RuntimeCapability.FullComputation); - + // project item groups var sourceFiles = GetItemsByType(projectInstance, "QSharpCompile"); - var csharpFiles = GetItemsByType(projectInstance, "Compile").Where(file => !file.EndsWith(".g.cs")); var projectReferences = GetItemsByType(projectInstance, "ProjectReference"); var references = GetItemsByType(projectInstance, "Reference"); - var sdkPath = projectInstance.GetPropertyValue("QuantumSdkPath"); - var version = projectInstance.GetPropertyValue("QSharpLangVersion"); - var isExecutable = "QSharpExe".Equals(projectInstance.GetPropertyValue("ResolvedQSharpOutputType"), StringComparison.OrdinalIgnoreCase); - var loadTestNames = "true".Equals(projectInstance.GetPropertyValue("ExposeReferencesViaTestNames"), StringComparison.OrdinalIgnoreCase); + // telemetry data var defaultSimulator = projectInstance.GetPropertyValue("DefaultSimulator")?.Trim(); - + var csharpFiles = GetItemsByType(projectInstance, "Compile").Where(file => !file.EndsWith(".g.cs")); var telemetryMeas = new Dictionary(); telemetryMeas["sources"] = sourceFiles.Count(); telemetryMeas["csharpfiles"] = csharpFiles.Count(); telemetryProps["defaultSimulator"] = defaultSimulator; this.sendTelemetry("project-load", telemetryProps, telemetryMeas); // does not send anything unless the corresponding flag is defined upon compilation + // project properties + var outputPath = projectInstance.GetPropertyValue(MSBuildProperties.TargetPath); + var processorArchitecture = projectInstance.GetPropertyValue(MSBuildProperties.ResolvedProcessorArchitecture); + var runtimeCapability = projectInstance.GetPropertyValue(MSBuildProperties.ResolvedRuntimeCapabilities); + var sdkPath = projectInstance.GetPropertyValue(MSBuildProperties.QuantumSdkPath); + var version = projectInstance.GetPropertyValue(MSBuildProperties.QsharpLangVersion); + var outputType = projectInstance.GetPropertyValue(MSBuildProperties.ResolvedQsharpOutputType); + var exposeRefsViaTestNames = projectInstance.GetPropertyValue(MSBuildProperties.ExposeReferencesViaTestNames); + + var buildProperties = ImmutableDictionary.CreateBuilder(); + buildProperties.Add(MSBuildProperties.QuantumSdkPath, sdkPath); + buildProperties.Add(MSBuildProperties.QsharpLangVersion, version); + buildProperties.Add(MSBuildProperties.TargetPath, outputPath); + buildProperties.Add(MSBuildProperties.ResolvedProcessorArchitecture, processorArchitecture); + buildProperties.Add(MSBuildProperties.ResolvedRuntimeCapabilities, runtimeCapability); + buildProperties.Add(MSBuildProperties.ResolvedQsharpOutputType, outputType); + buildProperties.Add(MSBuildProperties.ExposeReferencesViaTestNames, exposeRefsViaTestNames); + info = new ProjectInformation( - version: version, - sdkPath: sdkPath, - outputPath: outputPath, - runtimeCapability: runtimeCapability, - isExecutable: isExecutable, - processorArchitecture: string.IsNullOrWhiteSpace(processorArchitecture) ? "Unspecified" : processorArchitecture, - loadTestNames: loadTestNames, sourceFiles: sourceFiles, projectReferences: projectReferences, - references: references); + references: references, + buildProperties); return true; } diff --git a/src/QsCompiler/TestProjects/TestProjects.sln b/src/QsCompiler/TestProjects/TestProjects.sln index a2e0503b96..64c34cffbd 100644 --- a/src/QsCompiler/TestProjects/TestProjects.sln +++ b/src/QsCompiler/TestProjects/TestProjects.sln @@ -29,6 +29,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "test12", "test12\test12.csp EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "test13", "test13\test13.csproj", "{3EDCBFE8-4EAD-4B2A-A899-10511A5721D3}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "test15", "test15\test15.csproj", "{956FE0B5-CDBA-4865-BBCC-3AF72FC7C791}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -87,6 +89,10 @@ Global {3EDCBFE8-4EAD-4B2A-A899-10511A5721D3}.Debug|Any CPU.Build.0 = Debug|Any CPU {3EDCBFE8-4EAD-4B2A-A899-10511A5721D3}.Release|Any CPU.ActiveCfg = Release|Any CPU {3EDCBFE8-4EAD-4B2A-A899-10511A5721D3}.Release|Any CPU.Build.0 = Release|Any CPU + {956FE0B5-CDBA-4865-BBCC-3AF72FC7C791}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {956FE0B5-CDBA-4865-BBCC-3AF72FC7C791}.Debug|Any CPU.Build.0 = Debug|Any CPU + {956FE0B5-CDBA-4865-BBCC-3AF72FC7C791}.Release|Any CPU.ActiveCfg = Release|Any CPU + {956FE0B5-CDBA-4865-BBCC-3AF72FC7C791}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/QsCompiler/TestProjects/test15/README.md b/src/QsCompiler/TestProjects/test15/README.md new file mode 100644 index 0000000000..39362c17f4 --- /dev/null +++ b/src/QsCompiler/TestProjects/test15/README.md @@ -0,0 +1 @@ +This is a valid Q# app using the formatter source code. diff --git a/src/QsCompiler/TestProjects/test15/format/Formatted.qs b/src/QsCompiler/TestProjects/test15/format/Formatted.qs new file mode 100644 index 0000000000..006cfc4799 --- /dev/null +++ b/src/QsCompiler/TestProjects/test15/format/Formatted.qs @@ -0,0 +1,12 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +namespace Microsoft.Quantum.Tests { + + @EntryPoint() + operation Test() : Unit { + let arr = [0, size = 10]; + for i in 1 .. 10 { + } + } +} diff --git a/src/QsCompiler/TestProjects/test15/format/Unformatted.qs b/src/QsCompiler/TestProjects/test15/format/Unformatted.qs new file mode 100644 index 0000000000..7c8de6c5da --- /dev/null +++ b/src/QsCompiler/TestProjects/test15/format/Unformatted.qs @@ -0,0 +1,12 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +namespace Microsoft.Quantum.Tests { + + @EntryPoint() + operation Test() : () { + let arr = new Int[10]; + for (i in 1 .. 10) { + } + } +} diff --git a/src/QsCompiler/TestProjects/test15/test15.csproj b/src/QsCompiler/TestProjects/test15/test15.csproj new file mode 100644 index 0000000000..79d9020527 --- /dev/null +++ b/src/QsCompiler/TestProjects/test15/test15.csproj @@ -0,0 +1,16 @@ + + + + Exe + netcoreapp3.1 + + + + dotnet $(MSBuildThisFileDirectory)../../../QsFmt/App/bin/$(Configuration)/netcoreapp3.1/qsfmt.dll + + + + + + + diff --git a/src/QsCompiler/Tests.LanguageServer/ProjectLoaderTests.cs b/src/QsCompiler/Tests.LanguageServer/ProjectLoaderTests.cs index 4422a64ef9..0dbfb1167f 100644 --- a/src/QsCompiler/Tests.LanguageServer/ProjectLoaderTests.cs +++ b/src/QsCompiler/Tests.LanguageServer/ProjectLoaderTests.cs @@ -135,8 +135,8 @@ public void LoadOutdatedQSharpProject() var (projectFile, context) = Context("test9"); var projDir = Path.GetDirectoryName(projectFile.LocalPath) ?? ""; Assert.IsNotNull(context); - Assert.AreEqual("test9.dll", Path.GetFileName(context!.Properties.OutputPath)); - Assert.IsTrue((Path.GetDirectoryName(context.Properties.OutputPath) ?? "").StartsWith(projDir)); + Assert.AreEqual("test9.dll", Path.GetFileName(context!.Properties.DllOutputPath)); + Assert.IsTrue((Path.GetDirectoryName(context.Properties.DllOutputPath) ?? "").StartsWith(projDir)); var qsFiles = new string[] { @@ -155,8 +155,8 @@ public void LoadQSharpCoreLibraries() var (projectFile, context) = Context("test3"); var projDir = Path.GetDirectoryName(projectFile.LocalPath) ?? ""; Assert.IsNotNull(context); - Assert.AreEqual("test3.dll", Path.GetFileName(context!.Properties.OutputPath)); - Assert.IsTrue((Path.GetDirectoryName(context.Properties.OutputPath) ?? "").StartsWith(projDir)); + Assert.AreEqual("test3.dll", Path.GetFileName(context!.Properties.DllOutputPath)); + Assert.IsTrue((Path.GetDirectoryName(context.Properties.DllOutputPath) ?? "").StartsWith(projDir)); var qsFiles = new string[] { @@ -174,8 +174,8 @@ public void LoadQSharpCoreLibraries() (projectFile, context) = Context("test12"); projDir = Path.GetDirectoryName(projectFile.LocalPath) ?? ""; Assert.IsNotNull(context); - Assert.AreEqual("test12.dll", Path.GetFileName(context!.Properties.OutputPath)); - Assert.IsTrue((Path.GetDirectoryName(context.Properties.OutputPath) ?? "").StartsWith(projDir)); + Assert.AreEqual("test12.dll", Path.GetFileName(context!.Properties.DllOutputPath)); + Assert.IsTrue((Path.GetDirectoryName(context.Properties.DllOutputPath) ?? "").StartsWith(projDir)); qsFiles = new string[] { @@ -199,8 +199,8 @@ public void LoadQSharpFrameworkLibrary() var (projectFile, context) = Context("test7"); var projDir = Path.GetDirectoryName(projectFile.LocalPath) ?? ""; Assert.IsNotNull(context); - Assert.AreEqual("test7.dll", Path.GetFileName(context!.Properties.OutputPath)); - Assert.IsTrue((Path.GetDirectoryName(context.Properties.OutputPath) ?? "").StartsWith(projDir)); + Assert.AreEqual("test7.dll", Path.GetFileName(context!.Properties.DllOutputPath)); + Assert.IsTrue((Path.GetDirectoryName(context.Properties.DllOutputPath) ?? "").StartsWith(projDir)); var qsFiles = new string[] { @@ -219,8 +219,8 @@ public void LoadQSharpConsoleApps() var (projectFile, context) = Context("test4"); var projDir = Path.GetDirectoryName(projectFile.LocalPath) ?? ""; Assert.IsNotNull(context); - Assert.AreEqual("test4.dll", Path.GetFileName(context!.Properties.OutputPath)); - Assert.IsTrue((Path.GetDirectoryName(context.Properties.OutputPath) ?? "").StartsWith(projDir)); + Assert.AreEqual("test4.dll", Path.GetFileName(context!.Properties.DllOutputPath)); + Assert.IsTrue((Path.GetDirectoryName(context.Properties.DllOutputPath) ?? "").StartsWith(projDir)); var qsFiles = new string[] { @@ -236,8 +236,8 @@ public void LoadQSharpConsoleApps() (projectFile, context) = Context("test10"); projDir = Path.GetDirectoryName(projectFile.LocalPath) ?? ""; Assert.IsNotNull(context); - Assert.AreEqual("test10.dll", Path.GetFileName(context!.Properties.OutputPath)); - Assert.IsTrue((Path.GetDirectoryName(context.Properties.OutputPath) ?? "").StartsWith(projDir)); + Assert.AreEqual("test10.dll", Path.GetFileName(context!.Properties.DllOutputPath)); + Assert.IsTrue((Path.GetDirectoryName(context.Properties.DllOutputPath) ?? "").StartsWith(projDir)); qsFiles = new string[] { @@ -251,8 +251,8 @@ public void LoadQSharpConsoleApps() (projectFile, context) = Context("test11"); projDir = Path.GetDirectoryName(projectFile.LocalPath) ?? ""; Assert.IsNotNull(context); - Assert.AreEqual("test11.dll", Path.GetFileName(context!.Properties.OutputPath)); - Assert.IsTrue((Path.GetDirectoryName(context.Properties.OutputPath) ?? "").StartsWith(projDir)); + Assert.AreEqual("test11.dll", Path.GetFileName(context!.Properties.DllOutputPath)); + Assert.IsTrue((Path.GetDirectoryName(context.Properties.DllOutputPath) ?? "").StartsWith(projDir)); qsFiles = new string[] { @@ -270,8 +270,8 @@ public void LoadQSharpUnitTest() var (projectFile, context) = Context("test5"); var projDir = Path.GetDirectoryName(projectFile.LocalPath) ?? ""; Assert.IsNotNull(context); - Assert.AreEqual("test5.dll", Path.GetFileName(context!.Properties.OutputPath)); - Assert.IsTrue((Path.GetDirectoryName(context.Properties.OutputPath) ?? "").StartsWith(projDir)); + Assert.AreEqual("test5.dll", Path.GetFileName(context!.Properties.DllOutputPath)); + Assert.IsTrue((Path.GetDirectoryName(context.Properties.DllOutputPath) ?? "").StartsWith(projDir)); var qsFiles = new string[] { @@ -294,8 +294,8 @@ public void LoadQSharpMultiFrameworkLibrary() var (projectFile, context) = Context("test6"); var projDir = Path.GetDirectoryName(projectFile.LocalPath) ?? ""; Assert.IsNotNull(context); - Assert.AreEqual("test6.dll", Path.GetFileName(context!.Properties.OutputPath)); - Assert.IsTrue((Path.GetDirectoryName(context.Properties.OutputPath) ?? "").StartsWith(projDir)); + Assert.AreEqual("test6.dll", Path.GetFileName(context!.Properties.DllOutputPath)); + Assert.IsTrue((Path.GetDirectoryName(context.Properties.DllOutputPath) ?? "").StartsWith(projDir)); var qsFiles = new string[] { diff --git a/src/QsCompiler/Tests.LanguageServer/Tests.LanguageServer.csproj b/src/QsCompiler/Tests.LanguageServer/Tests.LanguageServer.csproj index 9b233a9420..43f3e7a3e5 100644 --- a/src/QsCompiler/Tests.LanguageServer/Tests.LanguageServer.csproj +++ b/src/QsCompiler/Tests.LanguageServer/Tests.LanguageServer.csproj @@ -24,6 +24,9 @@ true + + false + diff --git a/src/QsCompiler/Tests.LanguageServer/Tests.cs b/src/QsCompiler/Tests.LanguageServer/Tests.cs index 4c215e3e5c..193967119c 100644 --- a/src/QsCompiler/Tests.LanguageServer/Tests.cs +++ b/src/QsCompiler/Tests.LanguageServer/Tests.cs @@ -320,7 +320,7 @@ public async Task CodeContentTrackingAsync() [TestMethod] public async Task UpdateAndFormatAsync() { - var eventSignal = new ManualResetEvent(false); + ManualResetEvent eventSignal = new ManualResetEvent(false); void CheckForLoadingCompleted(string msg, MessageType messageType) { if (msg.StartsWith("Done loading project")) @@ -329,32 +329,38 @@ void CheckForLoadingCompleted(string msg, MessageType messageType) } } - var projectFile = ProjectLoaderTests.ProjectUri("test12"); - var projDir = Path.GetDirectoryName(projectFile.LocalPath) ?? ""; - var projectManager = new ProjectManager(ex => Assert.IsNull(ex), CheckForLoadingCompleted); - await projectManager.LoadProjectsAsync( - new[] { projectFile }, - CompilationContext.Editor.QsProjectLoader, - enableLazyLoading: false); - - // Note that the formatting command will return null until the project has finished loading, - // and similarly when a project is reloaded because it has been modified. - // All in all, that seem like reasonable behavior. - eventSignal.WaitOne(); - eventSignal.Reset(); - - var fileToFormat = new Uri(Path.Combine(projDir, "format", "Unformatted.qs")); - var expectedContent = File.ReadAllText(Path.Combine(projDir, "format", "Formatted.qs")); - var param = new DocumentFormattingParams + async Task RunFormattingTestAsync(string projectName) { - TextDocument = new TextDocumentIdentifier { Uri = fileToFormat }, - Options = new FormattingOptions { TabSize = 2, InsertSpaces = false, OtherOptions = new Dictionary() }, - }; - - var edits = projectManager.Formatting(param); - Assert.IsNotNull(edits); - Assert.AreEqual(1, edits!.Length); - Assert.AreEqual(expectedContent, edits[0].NewText); + var projectFile = ProjectLoaderTests.ProjectUri(projectName); + var projDir = Path.GetDirectoryName(projectFile.LocalPath) ?? ""; + var projectManager = new ProjectManager(ex => Assert.IsNull(ex), CheckForLoadingCompleted); + await projectManager.LoadProjectsAsync( + new[] { projectFile }, + CompilationContext.Editor.QsProjectLoader, + enableLazyLoading: false); + + // Note that the formatting command will return null until the project has finished loading, + // and similarly when a project is reloaded because it has been modified. + // All in all, that seem like reasonable behavior. + eventSignal.WaitOne(); + eventSignal.Reset(); + + var fileToFormat = new Uri(Path.Combine(projDir, "format", "Unformatted.qs")); + var expectedContent = File.ReadAllText(Path.Combine(projDir, "format", "Formatted.qs")); + var param = new DocumentFormattingParams + { + TextDocument = new TextDocumentIdentifier { Uri = fileToFormat }, + Options = new FormattingOptions { TabSize = 2, InsertSpaces = false, OtherOptions = new Dictionary() }, + }; + + var edits = projectManager.Formatting(param); + Assert.IsNotNull(edits); + Assert.AreEqual(1, edits!.Length); + Assert.AreEqual(expectedContent, edits[0].NewText); + } + + await RunFormattingTestAsync("test12"); + await RunFormattingTestAsync("test15"); } } } From 411d5c32c5f9d95639927fec5407bf424eee58b8 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Mon, 25 Oct 2021 22:22:32 -0700 Subject: [PATCH 19/37] adding a test that uses the source code version of the formatter --- .../CompilationUnitManager.cs | 41 +++++++++---------- .../CompilationManager/ProjectManager.cs | 17 ++++++-- .../DataStructures/ReservedKeywords.fs | 2 + src/QsCompiler/LanguageServer/EditorState.cs | 24 +++++------ .../TestProjects/test15/test15.csproj | 2 +- .../SimulationCodeTests.fs | 2 +- .../Tests.Compiler/CallGraphTests.fs | 13 +++--- .../Tests.Compiler/ClassicalControlTests.fs | 2 +- .../Tests.Compiler/CompilationLoaderTests.fs | 2 +- src/QsCompiler/Tests.Compiler/LinkingTests.fs | 19 +++++---- .../Tests.Compiler/OptimizationTests.fs | 2 +- .../TestUtils/SetupVerificationTests.fs | 5 ++- .../Tests.Compiler/TransformationTests.fs | 2 +- .../Tests.Compiler/TypeParameterTests.fs | 2 +- .../Tests.LanguageServer.csproj | 4 +- 15 files changed, 73 insertions(+), 66 deletions(-) diff --git a/src/QsCompiler/CompilationManager/CompilationUnitManager.cs b/src/QsCompiler/CompilationManager/CompilationUnitManager.cs index 7a0223ee34..5e2086d4b0 100644 --- a/src/QsCompiler/CompilationManager/CompilationUnitManager.cs +++ b/src/QsCompiler/CompilationManager/CompilationUnitManager.cs @@ -11,6 +11,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.Quantum.QsCompiler.CompilationBuilder.DataStructures; +using Microsoft.Quantum.QsCompiler.ReservedKeywords; using Microsoft.Quantum.QsCompiler.SyntaxProcessing; using Microsoft.Quantum.QsCompiler.SyntaxTokens; using Microsoft.Quantum.QsCompiler.SyntaxTree; @@ -67,7 +68,12 @@ public class CompilationUnitManager : IDisposable /// protected ProcessingQueue Processing { get; } - /// + /// + /// Initializes a instance for a project with the given properties. + /// + /// + /// If provided, called whenever diagnostics within a file have changed and are ready for publishing. + /// public CompilationUnitManager( ProjectProperties buildProperties, Action? exceptionLogger = null, @@ -91,32 +97,19 @@ public CompilationUnitManager( bool syntaxCheckOnly = false, RuntimeCapability? capability = null, bool isExecutable = false, - string processorArchitecture = "Unspecified") + string? processorArchitecture = null) : this( new ProjectProperties(ImmutableDictionary.CreateRange(new[] { - // TODO - new KeyValuePair("", ""), + new KeyValuePair(MSBuildProperties.ResolvedRuntimeCapabilities, capability?.Name), + new KeyValuePair(MSBuildProperties.ResolvedProcessorArchitecture, processorArchitecture), + new KeyValuePair(MSBuildProperties.ResolvedQsharpOutputType, isExecutable ? AssemblyConstants.QsharpExe : AssemblyConstants.QsharpLibrary), })), exceptionLogger, publishDiagnostics) { } - /// - /// Initializes a instance for a project with the given properties. - /// - /// - /// If provided, called whenever diagnostics within a file have changed and are ready for publishing. - /// - public CompilationUnitManager( - Action? exceptionLogger = null, - Action? publishDiagnostics = null, - bool syntaxCheckOnly = false) - : this(ProjectProperties.Empty, exceptionLogger, publishDiagnostics, syntaxCheckOnly) - { - } - /// /// Cancels any asynchronously running ongoing global type checking. After all currently queued tasks /// have finished, locks all processing, flushes the unprocessed changes in each source file, @@ -818,8 +811,9 @@ private void TypeCheckFile(string fileId, CancellationToken cancellationToken) format ? "format" : null; + var qsFmtExe = this.compilationUnit.BuildProperties.QsFmtExe; var sdkPath = this.compilationUnit.BuildProperties.SdkPath; - if (string.IsNullOrWhiteSpace(sdkPath) || verb == null) + if (verb == null || (string.IsNullOrWhiteSpace(qsFmtExe) && string.IsNullOrWhiteSpace(sdkPath))) { return null; } @@ -830,8 +824,11 @@ private void TypeCheckFile(string fileId, CancellationToken cancellationToken) var currentContent = file.GetFileContent(); File.WriteAllText(tempFile, currentContent); - var qsfmtPath = Path.Combine(sdkPath, "tools", "qsfmt", "qsfmt.dll"); - var commandArgs = $"{qsfmtPath} {verb} --input {tempFile}"; + var fmtCommand = qsFmtExe?.Split(); + var (command, dllPath) = fmtCommand != null && fmtCommand.Length > 0 + ? (fmtCommand[0], string.Join(" ", fmtCommand[1..])) + : ("dotnet", Path.Combine(sdkPath, "tools", "qsfmt", "qsfmt.dll")); + var commandArgs = $"{dllPath} {verb} --input {tempFile}"; var succeeded = ProcessRunner.Run("dotnet", commandArgs, out var _, out var _, out var exitCode, out var ex, timeout: timeout) @@ -844,7 +841,7 @@ private void TypeCheckFile(string fileId, CancellationToken cancellationToken) File.Delete(tempFile); return new[] { edit }; } - else if (Directory.Exists(qsfmtPath) && ex != null) + else if (ex != null && (File.Exists(qsFmtExe) || File.Exists(dllPath))) { this.LogException(ex); } diff --git a/src/QsCompiler/CompilationManager/ProjectManager.cs b/src/QsCompiler/CompilationManager/ProjectManager.cs index ad86dbd675..cbbf142160 100644 --- a/src/QsCompiler/CompilationManager/ProjectManager.cs +++ b/src/QsCompiler/CompilationManager/ProjectManager.cs @@ -39,18 +39,18 @@ public class ProjectProperties /// Returns the value specified by , /// or an empty string if no value is specified. /// - public string? SdkPath => + public string SdkPath => this.BuildProperties.TryGetValue(MSBuildProperties.QuantumSdkPath, out var path) - ? path + ? path ?? string.Empty : string.Empty; /// /// Returns the value specified by , /// or an empty string if no value is specified. /// - public string? DllOutputPath => + public string DllOutputPath => this.BuildProperties.TryGetValue(MSBuildProperties.TargetPath, out var path) - ? path + ? path ?? string.Empty : string.Empty; /// @@ -88,6 +88,15 @@ public class ProjectProperties this.BuildProperties.TryGetValue(MSBuildProperties.ExposeReferencesViaTestNames, out var exposeViaTestNames) && "true".Equals(exposeViaTestNames, StringComparison.OrdinalIgnoreCase); + /// + /// Returns the value specified by , + /// or an empty string if no value is specified. + /// + internal string QsFmtExe => + this.BuildProperties.TryGetValue(MSBuildProperties.QsFmtExe, out var path) + ? path ?? string.Empty + : string.Empty; + private ImmutableDictionary BuildProperties { get; } public static ProjectProperties Empty => diff --git a/src/QsCompiler/DataStructures/ReservedKeywords.fs b/src/QsCompiler/DataStructures/ReservedKeywords.fs index 8b88a8e485..7e5540deed 100644 --- a/src/QsCompiler/DataStructures/ReservedKeywords.fs +++ b/src/QsCompiler/DataStructures/ReservedKeywords.fs @@ -247,6 +247,8 @@ module MSBuildProperties = let ResolvedRuntimeCapabilities = "ResolvedRuntimeCapabilities" let ResolvedQsharpOutputType = "ResolvedQSharpOutputType" let ExposeReferencesViaTestNames = "ExposeReferencesViaTestNames" + let QsFmtExe = "QsFmtExe" + let QscExe = "QscExe" /// contains project specific settings specified that can be accessed by rewrite steps during Q# compilation module AssemblyConstants = diff --git a/src/QsCompiler/LanguageServer/EditorState.cs b/src/QsCompiler/LanguageServer/EditorState.cs index 8c49c54015..1bdc953c75 100644 --- a/src/QsCompiler/LanguageServer/EditorState.cs +++ b/src/QsCompiler/LanguageServer/EditorState.cs @@ -137,22 +137,18 @@ internal bool QsProjectLoader(Uri projectFile, [NotNullWhen(true)] out ProjectIn this.sendTelemetry("project-load", telemetryProps, telemetryMeas); // does not send anything unless the corresponding flag is defined upon compilation // project properties - var outputPath = projectInstance.GetPropertyValue(MSBuildProperties.TargetPath); - var processorArchitecture = projectInstance.GetPropertyValue(MSBuildProperties.ResolvedProcessorArchitecture); - var runtimeCapability = projectInstance.GetPropertyValue(MSBuildProperties.ResolvedRuntimeCapabilities); - var sdkPath = projectInstance.GetPropertyValue(MSBuildProperties.QuantumSdkPath); - var version = projectInstance.GetPropertyValue(MSBuildProperties.QsharpLangVersion); - var outputType = projectInstance.GetPropertyValue(MSBuildProperties.ResolvedQsharpOutputType); - var exposeRefsViaTestNames = projectInstance.GetPropertyValue(MSBuildProperties.ExposeReferencesViaTestNames); + void AddProperty(IDictionary props, string property) => + props.Add(property, projectInstance.GetPropertyValue(property)); var buildProperties = ImmutableDictionary.CreateBuilder(); - buildProperties.Add(MSBuildProperties.QuantumSdkPath, sdkPath); - buildProperties.Add(MSBuildProperties.QsharpLangVersion, version); - buildProperties.Add(MSBuildProperties.TargetPath, outputPath); - buildProperties.Add(MSBuildProperties.ResolvedProcessorArchitecture, processorArchitecture); - buildProperties.Add(MSBuildProperties.ResolvedRuntimeCapabilities, runtimeCapability); - buildProperties.Add(MSBuildProperties.ResolvedQsharpOutputType, outputType); - buildProperties.Add(MSBuildProperties.ExposeReferencesViaTestNames, exposeRefsViaTestNames); + AddProperty(buildProperties, MSBuildProperties.TargetPath); + AddProperty(buildProperties, MSBuildProperties.ResolvedProcessorArchitecture); + AddProperty(buildProperties, MSBuildProperties.QuantumSdkPath); + AddProperty(buildProperties, MSBuildProperties.QsharpLangVersion); + AddProperty(buildProperties, MSBuildProperties.ResolvedRuntimeCapabilities); + AddProperty(buildProperties, MSBuildProperties.ResolvedQsharpOutputType); + AddProperty(buildProperties, MSBuildProperties.ExposeReferencesViaTestNames); + AddProperty(buildProperties, MSBuildProperties.QsFmtExe); info = new ProjectInformation( sourceFiles: sourceFiles, diff --git a/src/QsCompiler/TestProjects/test15/test15.csproj b/src/QsCompiler/TestProjects/test15/test15.csproj index 79d9020527..d1ee672734 100644 --- a/src/QsCompiler/TestProjects/test15/test15.csproj +++ b/src/QsCompiler/TestProjects/test15/test15.csproj @@ -6,7 +6,7 @@ - dotnet $(MSBuildThisFileDirectory)../../../QsFmt/App/bin/$(Configuration)/netcoreapp3.1/qsfmt.dll + dotnet $(MSBuildProjectDirectory)/../../qsfmt.dll diff --git a/src/QsCompiler/Tests.CSharpGeneration/SimulationCodeTests.fs b/src/QsCompiler/Tests.CSharpGeneration/SimulationCodeTests.fs index 0e2a5c9492..7c177a7ea3 100644 --- a/src/QsCompiler/Tests.CSharpGeneration/SimulationCodeTests.fs +++ b/src/QsCompiler/Tests.CSharpGeneration/SimulationCodeTests.fs @@ -114,7 +114,7 @@ namespace N1 let file = CompilationUnitManager.InitializeFileManager(fileId, File.ReadAllText fileName) mgr.AddOrUpdateSourceFileAsync file |> ignore // TODO: catch compilation errors and fail - let mgr = new CompilationUnitManager(null, (fun ps -> ps.Diagnostics |> Array.iter addError)) + let mgr = new CompilationUnitManager(ProjectProperties.Empty, (fun ex -> raise ex), (fun (ps: PublishDiagnosticParams) -> ps.Diagnostics |> Array.iter addError)) files |> List.iter (addSourceFile mgr) try diff --git a/src/QsCompiler/Tests.Compiler/CallGraphTests.fs b/src/QsCompiler/Tests.Compiler/CallGraphTests.fs index fc1dbdecd5..95f85c19e0 100644 --- a/src/QsCompiler/Tests.Compiler/CallGraphTests.fs +++ b/src/QsCompiler/Tests.Compiler/CallGraphTests.fs @@ -9,7 +9,6 @@ open System.IO open System.Linq open Microsoft.Quantum.QsCompiler open Microsoft.Quantum.QsCompiler.CompilationBuilder -open Microsoft.Quantum.QsCompiler.DataTypes open Microsoft.Quantum.QsCompiler.DependencyAnalysis open Microsoft.Quantum.QsCompiler.Diagnostics open Microsoft.Quantum.QsCompiler.ReservedKeywords @@ -21,16 +20,14 @@ open Xunit.Abstractions type CallGraphTests(output: ITestOutputHelper) = - let compilationManager = new CompilationUnitManager(new Action(fun ex -> failwith ex.Message)) + let compilationManager = new CompilationUnitManager(ProjectProperties.Empty, (fun ex -> failwith ex.Message)) let compilationManagerExe = + let props = ImmutableDictionary.CreateBuilder(); + props.Add(MSBuildProperties.ResolvedQsharpOutputType, AssemblyConstants.QsharpExe) new CompilationUnitManager( - Action<_>(fun ex -> failwith ex.Message), - null, - false, - FullComputation, - isExecutable = true - ) + new ProjectProperties(props), + (fun ex -> failwith ex.Message)) let getTempFile () = new Uri(Path.GetFullPath(Path.GetRandomFileName())) diff --git a/src/QsCompiler/Tests.Compiler/ClassicalControlTests.fs b/src/QsCompiler/Tests.Compiler/ClassicalControlTests.fs index ae943dfac6..4e52a75824 100644 --- a/src/QsCompiler/Tests.Compiler/ClassicalControlTests.fs +++ b/src/QsCompiler/Tests.Compiler/ClassicalControlTests.fs @@ -18,7 +18,7 @@ open Xunit type ClassicalControlTests() = - let compilationManager = new CompilationUnitManager(new Action(fun ex -> failwith ex.Message)) + let compilationManager = new CompilationUnitManager(ProjectProperties.Empty, (fun ex -> failwith ex.Message)) let getTempFile () = new Uri(Path.GetFullPath(Path.GetRandomFileName())) diff --git a/src/QsCompiler/Tests.Compiler/CompilationLoaderTests.fs b/src/QsCompiler/Tests.Compiler/CompilationLoaderTests.fs index c4daf53cb3..be72b66b70 100644 --- a/src/QsCompiler/Tests.Compiler/CompilationLoaderTests.fs +++ b/src/QsCompiler/Tests.Compiler/CompilationLoaderTests.fs @@ -36,7 +36,7 @@ type CompilationLoaderTests(output: ITestOutputHelper) = /// Compiles a snippet of Q# source code. /// let compileQSharp source = - use compilationManager = new CompilationUnitManager() + use compilationManager = new CompilationUnitManager(ProjectProperties.Empty) let fileManager uri content = CompilationUnitManager.InitializeFileManager(uri, content) diff --git a/src/QsCompiler/Tests.Compiler/LinkingTests.fs b/src/QsCompiler/Tests.Compiler/LinkingTests.fs index 81b4c0048c..c740522be6 100644 --- a/src/QsCompiler/Tests.Compiler/LinkingTests.fs +++ b/src/QsCompiler/Tests.Compiler/LinkingTests.fs @@ -7,6 +7,7 @@ open Microsoft.Quantum.QsCompiler open Microsoft.Quantum.QsCompiler.CompilationBuilder open Microsoft.Quantum.QsCompiler.DataTypes open Microsoft.Quantum.QsCompiler.Diagnostics +open Microsoft.Quantum.QsCompiler.ReservedKeywords open Microsoft.Quantum.QsCompiler.SyntaxExtensions open Microsoft.Quantum.QsCompiler.SyntaxTokens open Microsoft.Quantum.QsCompiler.SyntaxTree @@ -27,7 +28,9 @@ type LinkingTests() = inherit CompilerTests(LinkingTests.Compile()) let compilationManager = - new CompilationUnitManager(new Action(fun ex -> failwith ex.Message), isExecutable = true) + let props = ImmutableDictionary.CreateBuilder(); + props.Add(MSBuildProperties.ResolvedQsharpOutputType, AssemblyConstants.QsharpExe) + new CompilationUnitManager(new ProjectProperties(props), (fun ex -> failwith ex.Message)) // The file name needs to end in ".qs" so that it isn't ignored by the References.Headers class during the internal renaming tests. let getTempFile () = @@ -118,7 +121,7 @@ type LinkingTests() = compilation member private this.BuildReference(source: string, content) = - let comp = this.BuildContent(new CompilationUnitManager(), content) + let comp = this.BuildContent(new CompilationUnitManager(ProjectProperties.Empty), content) Assert.Empty(comp.Diagnostics() |> Seq.filter (fun d -> d.Severity = Nullable DiagnosticSeverity.Error)) struct (source, comp.BuiltCompilation.Namespaces) @@ -171,7 +174,7 @@ type LinkingTests() = /// been renamed across the compilation unit. member private this.RunInternalRenamingTest num renamed notRenamed = let chunks = LinkingTests.ReadAndChunkSourceFile "InternalRenaming.qs" - let manager = new CompilationUnitManager() + let manager = new CompilationUnitManager(ProjectProperties.Empty) let addOrUpdateSourceFile filePath = getManager (new Uri(filePath)) (File.ReadAllText filePath) @@ -465,11 +468,13 @@ type LinkingTests() = let tests = LinkingTests.ReadAndChunkSourceFile "EntryPointDiagnostics.qs" + let props = ImmutableDictionary.CreateBuilder(); + props.Add(MSBuildProperties.ResolvedQsharpOutputType, AssemblyConstants.QsharpExe) + props.Add(MSBuildProperties.ResolvedRuntimeCapabilities, BasicQuantumFunctionality.Name) let compilationManager = new CompilationUnitManager( - Action<_>(fun ex -> failwith ex.Message), - isExecutable = true, - capability = BasicQuantumFunctionality + new ProjectProperties(props), + Action<_>(fun (ex: exn) -> failwith ex.Message) ) let addOrUpdateSourceFile filePath = @@ -612,7 +617,7 @@ type LinkingTests() = [] member this.``Group internal specializations by source file``() = let chunks = LinkingTests.ReadAndChunkSourceFile "InternalRenaming.qs" - let manager = new CompilationUnitManager() + let manager = new CompilationUnitManager(ProjectProperties.Empty) let sourceCompilation = this.BuildContent(manager, chunks.[7]) diff --git a/src/QsCompiler/Tests.Compiler/OptimizationTests.fs b/src/QsCompiler/Tests.Compiler/OptimizationTests.fs index 88f20c6b1e..98cbc04733 100644 --- a/src/QsCompiler/Tests.Compiler/OptimizationTests.fs +++ b/src/QsCompiler/Tests.Compiler/OptimizationTests.fs @@ -15,7 +15,7 @@ open Xunit /// Given a string of valid Q# code, outputs the AST and the callables dictionary let private buildCompilation code = let fileId = new Uri(Path.GetFullPath "test-file.qs") - let compilationUnit = new CompilationUnitManager(fun ex -> failwith ex.Message) + let compilationUnit = new CompilationUnitManager(ProjectProperties.Empty, fun ex -> failwith ex.Message) let file = CompilationUnitManager.InitializeFileManager(fileId, code) // spawns a task that modifies the current compilation compilationUnit.AddOrUpdateSourceFileAsync file |> ignore diff --git a/src/QsCompiler/Tests.Compiler/TestUtils/SetupVerificationTests.fs b/src/QsCompiler/Tests.Compiler/TestUtils/SetupVerificationTests.fs index c21eb671dc..10fb7bcfcf 100644 --- a/src/QsCompiler/Tests.Compiler/TestUtils/SetupVerificationTests.fs +++ b/src/QsCompiler/Tests.Compiler/TestUtils/SetupVerificationTests.fs @@ -15,6 +15,7 @@ open Microsoft.Quantum.QsCompiler.Diagnostics open Microsoft.Quantum.QsCompiler.SyntaxTree open Microsoft.VisualStudio.LanguageServer.Protocol open Xunit +open Microsoft.Quantum.QsCompiler.ReservedKeywords type CompilerTests(compilation: CompilationUnitManager.Compilation) = @@ -149,8 +150,10 @@ type CompilerTests(compilation: CompilationUnitManager.Compilation) = let references = defaultArg references [] let capability = defaultArg capability FullComputation let paths = fileNames |> Seq.map (fun file -> Path.Combine(srcFolder, file) |> Path.GetFullPath) + let props = ImmutableDictionary.CreateBuilder(); + props.Add(MSBuildProperties.ResolvedRuntimeCapabilities, capability.Name) let mutable exceptions = [] - use manager = new CompilationUnitManager((fun e -> exceptions <- e :: exceptions), capability = capability) + use manager = new CompilationUnitManager(new ProjectProperties(props), (fun e -> exceptions <- e :: exceptions)) paths.ToImmutableDictionary(Uri, File.ReadAllText) |> CompilationUnitManager.InitializeFileManagers diff --git a/src/QsCompiler/Tests.Compiler/TransformationTests.fs b/src/QsCompiler/Tests.Compiler/TransformationTests.fs index b23145aa35..6ac6716426 100644 --- a/src/QsCompiler/Tests.Compiler/TransformationTests.fs +++ b/src/QsCompiler/Tests.Compiler/TransformationTests.fs @@ -76,7 +76,7 @@ and private SyntaxCounterExpressionKinds(parent: SyntaxCounter) = let private buildSyntaxTree code = let fileId = new Uri(Path.GetFullPath "test-file.qs") - let compilationUnit = new CompilationUnitManager(fun ex -> failwith ex.Message) + let compilationUnit = new CompilationUnitManager(ProjectProperties.Empty, fun ex -> failwith ex.Message) let file = CompilationUnitManager.InitializeFileManager(fileId, code) // spawns a task that modifies the current compilation compilationUnit.AddOrUpdateSourceFileAsync file |> ignore diff --git a/src/QsCompiler/Tests.Compiler/TypeParameterTests.fs b/src/QsCompiler/Tests.Compiler/TypeParameterTests.fs index 5af12f13de..3f8160f318 100644 --- a/src/QsCompiler/Tests.Compiler/TypeParameterTests.fs +++ b/src/QsCompiler/Tests.Compiler/TypeParameterTests.fs @@ -67,7 +67,7 @@ type TypeParameterTests() = let success = CheckCombinedResolution expected resolutions Assert.False(success, "Combining type resolutions should have failed.") - let compilationManager = new CompilationUnitManager(new Action(fun ex -> failwith ex.Message)) + let compilationManager = new CompilationUnitManager(ProjectProperties.Empty, (fun ex -> failwith ex.Message)) let getTempFile () = new Uri(Path.GetFullPath(Path.GetRandomFileName())) diff --git a/src/QsCompiler/Tests.LanguageServer/Tests.LanguageServer.csproj b/src/QsCompiler/Tests.LanguageServer/Tests.LanguageServer.csproj index 43f3e7a3e5..67257f74d2 100644 --- a/src/QsCompiler/Tests.LanguageServer/Tests.LanguageServer.csproj +++ b/src/QsCompiler/Tests.LanguageServer/Tests.LanguageServer.csproj @@ -21,12 +21,10 @@ + true - - false - From dad64f532b049d7e31b2e3f6a9810049690cc6d3 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Mon, 25 Oct 2021 22:31:31 -0700 Subject: [PATCH 20/37] style and additional warning codes to trigger code actions --- src/QsCompiler/CompilationManager/ProjectManager.cs | 8 ++++++-- .../Tests.CSharpGeneration/SimulationCodeTests.fs | 8 +++++++- src/QsCompiler/Tests.Compiler/CallGraphTests.fs | 6 ++---- src/QsCompiler/Tests.Compiler/LinkingTests.fs | 10 ++++------ src/QsCompiler/Tests.Compiler/OptimizationTests.fs | 2 +- .../Tests.Compiler/TestUtils/SetupVerificationTests.fs | 2 +- src/QsCompiler/Tests.Compiler/TransformationTests.fs | 2 +- 7 files changed, 22 insertions(+), 16 deletions(-) diff --git a/src/QsCompiler/CompilationManager/ProjectManager.cs b/src/QsCompiler/CompilationManager/ProjectManager.cs index cbbf142160..372a67d8fa 100644 --- a/src/QsCompiler/CompilationManager/ProjectManager.cs +++ b/src/QsCompiler/CompilationManager/ProjectManager.cs @@ -797,7 +797,7 @@ public ProjectManager(Action? exceptionLogger, Action(); - this.defaultManager = new CompilationUnitManager(exceptionLogger, publishDiagnostics, syntaxCheckOnly: true); + this.defaultManager = new CompilationUnitManager(ProjectProperties.Empty, exceptionLogger, publishDiagnostics, syntaxCheckOnly: true); this.publishDiagnostics = publishDiagnostics; this.logException = exceptionLogger; this.log = log; @@ -1265,7 +1265,11 @@ static IEnumerable if (diagnostics != null && diagnostics.Any(DiagnosticTools.WarningType( WarningCode.DeprecatedTupleBrackets, WarningCode.DeprecatedUnitType, - WarningCode.DeprecatedQubitBindingKeyword))) + WarningCode.DeprecatedQubitBindingKeyword, + WarningCode.DeprecatedANDoperator, + WarningCode.DeprecatedNOToperator, + WarningCode.DeprecatedORoperator, + WarningCode.DeprecatedNewArray))) { var formattingEdits = this.Manager( param?.TextDocument?.Uri)?.Formatting(param?.TextDocument, update: true, format: false, timeout: 2000); diff --git a/src/QsCompiler/Tests.CSharpGeneration/SimulationCodeTests.fs b/src/QsCompiler/Tests.CSharpGeneration/SimulationCodeTests.fs index 7c177a7ea3..325be12fc3 100644 --- a/src/QsCompiler/Tests.CSharpGeneration/SimulationCodeTests.fs +++ b/src/QsCompiler/Tests.CSharpGeneration/SimulationCodeTests.fs @@ -114,7 +114,13 @@ namespace N1 let file = CompilationUnitManager.InitializeFileManager(fileId, File.ReadAllText fileName) mgr.AddOrUpdateSourceFileAsync file |> ignore // TODO: catch compilation errors and fail - let mgr = new CompilationUnitManager(ProjectProperties.Empty, (fun ex -> raise ex), (fun (ps: PublishDiagnosticParams) -> ps.Diagnostics |> Array.iter addError)) + let mgr = + new CompilationUnitManager( + ProjectProperties.Empty, + (fun ex -> raise ex), + (fun (ps: PublishDiagnosticParams) -> ps.Diagnostics |> Array.iter addError) + ) + files |> List.iter (addSourceFile mgr) try diff --git a/src/QsCompiler/Tests.Compiler/CallGraphTests.fs b/src/QsCompiler/Tests.Compiler/CallGraphTests.fs index 95f85c19e0..8d9152276c 100644 --- a/src/QsCompiler/Tests.Compiler/CallGraphTests.fs +++ b/src/QsCompiler/Tests.Compiler/CallGraphTests.fs @@ -23,11 +23,9 @@ type CallGraphTests(output: ITestOutputHelper) = let compilationManager = new CompilationUnitManager(ProjectProperties.Empty, (fun ex -> failwith ex.Message)) let compilationManagerExe = - let props = ImmutableDictionary.CreateBuilder(); + let props = ImmutableDictionary.CreateBuilder() props.Add(MSBuildProperties.ResolvedQsharpOutputType, AssemblyConstants.QsharpExe) - new CompilationUnitManager( - new ProjectProperties(props), - (fun ex -> failwith ex.Message)) + new CompilationUnitManager(new ProjectProperties(props), (fun ex -> failwith ex.Message)) let getTempFile () = new Uri(Path.GetFullPath(Path.GetRandomFileName())) diff --git a/src/QsCompiler/Tests.Compiler/LinkingTests.fs b/src/QsCompiler/Tests.Compiler/LinkingTests.fs index c740522be6..b77bbc9082 100644 --- a/src/QsCompiler/Tests.Compiler/LinkingTests.fs +++ b/src/QsCompiler/Tests.Compiler/LinkingTests.fs @@ -28,7 +28,7 @@ type LinkingTests() = inherit CompilerTests(LinkingTests.Compile()) let compilationManager = - let props = ImmutableDictionary.CreateBuilder(); + let props = ImmutableDictionary.CreateBuilder() props.Add(MSBuildProperties.ResolvedQsharpOutputType, AssemblyConstants.QsharpExe) new CompilationUnitManager(new ProjectProperties(props), (fun ex -> failwith ex.Message)) @@ -468,14 +468,12 @@ type LinkingTests() = let tests = LinkingTests.ReadAndChunkSourceFile "EntryPointDiagnostics.qs" - let props = ImmutableDictionary.CreateBuilder(); + let props = ImmutableDictionary.CreateBuilder() props.Add(MSBuildProperties.ResolvedQsharpOutputType, AssemblyConstants.QsharpExe) props.Add(MSBuildProperties.ResolvedRuntimeCapabilities, BasicQuantumFunctionality.Name) + let compilationManager = - new CompilationUnitManager( - new ProjectProperties(props), - Action<_>(fun (ex: exn) -> failwith ex.Message) - ) + new CompilationUnitManager(new ProjectProperties(props), Action<_>(fun (ex: exn) -> failwith ex.Message)) let addOrUpdateSourceFile filePath = getManager (new Uri(filePath)) (File.ReadAllText filePath) diff --git a/src/QsCompiler/Tests.Compiler/OptimizationTests.fs b/src/QsCompiler/Tests.Compiler/OptimizationTests.fs index 98cbc04733..20c9aa7296 100644 --- a/src/QsCompiler/Tests.Compiler/OptimizationTests.fs +++ b/src/QsCompiler/Tests.Compiler/OptimizationTests.fs @@ -15,7 +15,7 @@ open Xunit /// Given a string of valid Q# code, outputs the AST and the callables dictionary let private buildCompilation code = let fileId = new Uri(Path.GetFullPath "test-file.qs") - let compilationUnit = new CompilationUnitManager(ProjectProperties.Empty, fun ex -> failwith ex.Message) + let compilationUnit = new CompilationUnitManager(ProjectProperties.Empty, (fun ex -> failwith ex.Message)) let file = CompilationUnitManager.InitializeFileManager(fileId, code) // spawns a task that modifies the current compilation compilationUnit.AddOrUpdateSourceFileAsync file |> ignore diff --git a/src/QsCompiler/Tests.Compiler/TestUtils/SetupVerificationTests.fs b/src/QsCompiler/Tests.Compiler/TestUtils/SetupVerificationTests.fs index 10fb7bcfcf..dcf2781d93 100644 --- a/src/QsCompiler/Tests.Compiler/TestUtils/SetupVerificationTests.fs +++ b/src/QsCompiler/Tests.Compiler/TestUtils/SetupVerificationTests.fs @@ -150,7 +150,7 @@ type CompilerTests(compilation: CompilationUnitManager.Compilation) = let references = defaultArg references [] let capability = defaultArg capability FullComputation let paths = fileNames |> Seq.map (fun file -> Path.Combine(srcFolder, file) |> Path.GetFullPath) - let props = ImmutableDictionary.CreateBuilder(); + let props = ImmutableDictionary.CreateBuilder() props.Add(MSBuildProperties.ResolvedRuntimeCapabilities, capability.Name) let mutable exceptions = [] use manager = new CompilationUnitManager(new ProjectProperties(props), (fun e -> exceptions <- e :: exceptions)) diff --git a/src/QsCompiler/Tests.Compiler/TransformationTests.fs b/src/QsCompiler/Tests.Compiler/TransformationTests.fs index 6ac6716426..cec6cd5e2b 100644 --- a/src/QsCompiler/Tests.Compiler/TransformationTests.fs +++ b/src/QsCompiler/Tests.Compiler/TransformationTests.fs @@ -76,7 +76,7 @@ and private SyntaxCounterExpressionKinds(parent: SyntaxCounter) = let private buildSyntaxTree code = let fileId = new Uri(Path.GetFullPath "test-file.qs") - let compilationUnit = new CompilationUnitManager(ProjectProperties.Empty, fun ex -> failwith ex.Message) + let compilationUnit = new CompilationUnitManager(ProjectProperties.Empty, (fun ex -> failwith ex.Message)) let file = CompilationUnitManager.InitializeFileManager(fileId, code) // spawns a task that modifies the current compilation compilationUnit.AddOrUpdateSourceFileAsync file |> ignore From 667d3f799e5b39a6a8b3b79724142a8f9bfb9034 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Tue, 26 Oct 2021 09:06:59 -0700 Subject: [PATCH 21/37] adding some logging --- .../CompilationUnitManager.cs | 37 ++++++++++++++----- .../CompilationManager/ProjectManager.cs | 5 ++- src/QsCompiler/Tests.LanguageServer/Tests.cs | 3 +- 3 files changed, 33 insertions(+), 12 deletions(-) diff --git a/src/QsCompiler/CompilationManager/CompilationUnitManager.cs b/src/QsCompiler/CompilationManager/CompilationUnitManager.cs index 5e2086d4b0..24b13a9763 100644 --- a/src/QsCompiler/CompilationManager/CompilationUnitManager.cs +++ b/src/QsCompiler/CompilationManager/CompilationUnitManager.cs @@ -52,6 +52,11 @@ public class CompilationUnitManager : IDisposable /// public Action PublishDiagnostics { get; } + /// + /// General purpose logging routine. + /// + public Action Log { get; } + /// /// Null if a global type checking has been queued but is not yet running. /// If not null, then a global type checking may be running and can be cancelled via this token source. @@ -76,6 +81,7 @@ public class CompilationUnitManager : IDisposable /// public CompilationUnitManager( ProjectProperties buildProperties, + Action? log = null, Action? exceptionLogger = null, Action? publishDiagnostics = null, bool syntaxCheckOnly = false) @@ -85,6 +91,7 @@ public CompilationUnitManager( this.fileContentManagers = new ConcurrentDictionary(); this.changedFiles = new ManagedHashSet(new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion)); this.PublishDiagnostics = publishDiagnostics ?? (_ => { }); + this.Log = log ?? ((_, __) => { }); this.LogException = exceptionLogger ?? Console.Error.WriteLine; this.Processing = new ProcessingQueue(this.LogException); this.waitForTypeCheck = new CancellationTokenSource(); @@ -105,6 +112,7 @@ public CompilationUnitManager( new KeyValuePair(MSBuildProperties.ResolvedProcessorArchitecture, processorArchitecture), new KeyValuePair(MSBuildProperties.ResolvedQsharpOutputType, isExecutable ? AssemblyConstants.QsharpExe : AssemblyConstants.QsharpLibrary), })), + null, exceptionLogger, publishDiagnostics) { @@ -813,7 +821,15 @@ private void TypeCheckFile(string fileId, CancellationToken cancellationToken) var qsFmtExe = this.compilationUnit.BuildProperties.QsFmtExe; var sdkPath = this.compilationUnit.BuildProperties.SdkPath; - if (verb == null || (string.IsNullOrWhiteSpace(qsFmtExe) && string.IsNullOrWhiteSpace(sdkPath))) + + var fmtCommand = qsFmtExe?.Split(); + var (command, dllPath) = fmtCommand != null && fmtCommand.Length > 0 + ? (fmtCommand[0], string.Join(" ", fmtCommand[1..])) + : ("dotnet", Path.Combine(sdkPath ?? "", "tools", "qsfmt", "qsfmt.dll")); + + this.Log($"qsfmt path: {dllPath} (exists: {File.Exists(dllPath)})", MessageType.Info); + this.Log($"sdk path: {sdkPath} (exists: {Directory.Exists(sdkPath)})", MessageType.Info); + if (verb == null || (fmtCommand?.Length == 2 && !File.Exists(dllPath) && !Directory.Exists(sdkPath))) { return null; } @@ -824,14 +840,10 @@ private void TypeCheckFile(string fileId, CancellationToken cancellationToken) var currentContent = file.GetFileContent(); File.WriteAllText(tempFile, currentContent); - var fmtCommand = qsFmtExe?.Split(); - var (command, dllPath) = fmtCommand != null && fmtCommand.Length > 0 - ? (fmtCommand[0], string.Join(" ", fmtCommand[1..])) - : ("dotnet", Path.Combine(sdkPath, "tools", "qsfmt", "qsfmt.dll")); + this.Log($"command: {command}, dllPath: {dllPath} (exists: {File.Exists(dllPath)})", MessageType.Info); var commandArgs = $"{dllPath} {verb} --input {tempFile}"; - var succeeded = - ProcessRunner.Run("dotnet", commandArgs, out var _, out var _, out var exitCode, out var ex, timeout: timeout) + ProcessRunner.Run(command, commandArgs, out var _, out var _, out var exitCode, out var ex, timeout: timeout) && exitCode == 0 && ex == null; if (succeeded) @@ -841,9 +853,16 @@ private void TypeCheckFile(string fileId, CancellationToken cancellationToken) File.Delete(tempFile); return new[] { edit }; } - else if (ex != null && (File.Exists(qsFmtExe) || File.Exists(dllPath))) + else if (ex != null) { - this.LogException(ex); + if (qsFmtExe != null) + { + this.Log($"Failed to format document. Formatter may be unavailable.", MessageType.Error); + } + else + { + this.LogException(ex); + } } return null; diff --git a/src/QsCompiler/CompilationManager/ProjectManager.cs b/src/QsCompiler/CompilationManager/ProjectManager.cs index 372a67d8fa..c6ddcd0fd3 100644 --- a/src/QsCompiler/CompilationManager/ProjectManager.cs +++ b/src/QsCompiler/CompilationManager/ProjectManager.cs @@ -269,12 +269,13 @@ internal Project( // We track the file contents for unsupported projects in case the files are migrated to newer projects while editing, // but we don't do any semantic verification, and we don't publish diagnostics for them. this.processing = new ProcessingQueue(onException); + this.log = log ?? ((msg, severity) => Console.WriteLine($"{severity}: {msg}")); this.Manager = new CompilationUnitManager( this.Properties, + ignore ? null : this.log, onException, ignore ? null : publishDiagnostics, syntaxCheckOnly: ignore); - this.log = log ?? ((msg, severity) => Console.WriteLine($"{severity}: {msg}")); this.loadedSourceFiles = ImmutableHashSet.Empty; this.loadedReferences = References.Empty; @@ -797,7 +798,7 @@ public ProjectManager(Action? exceptionLogger, Action(); - this.defaultManager = new CompilationUnitManager(ProjectProperties.Empty, exceptionLogger, publishDiagnostics, syntaxCheckOnly: true); + this.defaultManager = new CompilationUnitManager(ProjectProperties.Empty, log, exceptionLogger, publishDiagnostics, syntaxCheckOnly: true); this.publishDiagnostics = publishDiagnostics; this.logException = exceptionLogger; this.log = log; diff --git a/src/QsCompiler/Tests.LanguageServer/Tests.cs b/src/QsCompiler/Tests.LanguageServer/Tests.cs index 193967119c..bef3da76ba 100644 --- a/src/QsCompiler/Tests.LanguageServer/Tests.cs +++ b/src/QsCompiler/Tests.LanguageServer/Tests.cs @@ -321,8 +321,9 @@ public async Task CodeContentTrackingAsync() public async Task UpdateAndFormatAsync() { ManualResetEvent eventSignal = new ManualResetEvent(false); - void CheckForLoadingCompleted(string msg, MessageType messageType) + async void CheckForLoadingCompleted(string msg, MessageType messageType) { + await Console.Error.WriteLineAsync(msg); if (msg.StartsWith("Done loading project")) { eventSignal.Set(); From 72a0c66a435e01f9250c06426596afdba568d2ec Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Tue, 26 Oct 2021 09:20:57 -0700 Subject: [PATCH 22/37] forgot to update some things --- src/QsCompiler/CompilationManager/CompilationUnitManager.cs | 4 ++-- src/QsCompiler/CompilationManager/ProjectManager.cs | 4 ++-- src/QsCompiler/Tests.CSharpGeneration/SimulationCodeTests.fs | 1 + 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/QsCompiler/CompilationManager/CompilationUnitManager.cs b/src/QsCompiler/CompilationManager/CompilationUnitManager.cs index 24b13a9763..5895093426 100644 --- a/src/QsCompiler/CompilationManager/CompilationUnitManager.cs +++ b/src/QsCompiler/CompilationManager/CompilationUnitManager.cs @@ -81,8 +81,8 @@ public class CompilationUnitManager : IDisposable /// public CompilationUnitManager( ProjectProperties buildProperties, - Action? log = null, Action? exceptionLogger = null, + Action? log = null, Action? publishDiagnostics = null, bool syntaxCheckOnly = false) { @@ -112,8 +112,8 @@ public CompilationUnitManager( new KeyValuePair(MSBuildProperties.ResolvedProcessorArchitecture, processorArchitecture), new KeyValuePair(MSBuildProperties.ResolvedQsharpOutputType, isExecutable ? AssemblyConstants.QsharpExe : AssemblyConstants.QsharpLibrary), })), - null, exceptionLogger, + null, publishDiagnostics) { } diff --git a/src/QsCompiler/CompilationManager/ProjectManager.cs b/src/QsCompiler/CompilationManager/ProjectManager.cs index c6ddcd0fd3..82268f96d1 100644 --- a/src/QsCompiler/CompilationManager/ProjectManager.cs +++ b/src/QsCompiler/CompilationManager/ProjectManager.cs @@ -272,8 +272,8 @@ internal Project( this.log = log ?? ((msg, severity) => Console.WriteLine($"{severity}: {msg}")); this.Manager = new CompilationUnitManager( this.Properties, - ignore ? null : this.log, onException, + ignore ? null : this.log, ignore ? null : publishDiagnostics, syntaxCheckOnly: ignore); @@ -798,7 +798,7 @@ public ProjectManager(Action? exceptionLogger, Action(); - this.defaultManager = new CompilationUnitManager(ProjectProperties.Empty, log, exceptionLogger, publishDiagnostics, syntaxCheckOnly: true); + this.defaultManager = new CompilationUnitManager(ProjectProperties.Empty, exceptionLogger, log, publishDiagnostics, syntaxCheckOnly: true); this.publishDiagnostics = publishDiagnostics; this.logException = exceptionLogger; this.log = log; diff --git a/src/QsCompiler/Tests.CSharpGeneration/SimulationCodeTests.fs b/src/QsCompiler/Tests.CSharpGeneration/SimulationCodeTests.fs index 325be12fc3..c6492816af 100644 --- a/src/QsCompiler/Tests.CSharpGeneration/SimulationCodeTests.fs +++ b/src/QsCompiler/Tests.CSharpGeneration/SimulationCodeTests.fs @@ -118,6 +118,7 @@ namespace N1 new CompilationUnitManager( ProjectProperties.Empty, (fun ex -> raise ex), + (fun msg _ -> printf "%s" msg), (fun (ps: PublishDiagnosticParams) -> ps.Diagnostics |> Array.iter addError) ) From b1b25c3da06f976b1cec332762742fe2435dd3b7 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Tue, 26 Oct 2021 09:37:33 -0700 Subject: [PATCH 23/37] additional logs --- src/QsCompiler/CompilationManager/CompilationUnitManager.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/QsCompiler/CompilationManager/CompilationUnitManager.cs b/src/QsCompiler/CompilationManager/CompilationUnitManager.cs index 5895093426..336ecf33fe 100644 --- a/src/QsCompiler/CompilationManager/CompilationUnitManager.cs +++ b/src/QsCompiler/CompilationManager/CompilationUnitManager.cs @@ -839,6 +839,7 @@ private void TypeCheckFile(string fileId, CancellationToken cancellationToken) var tempFile = Path.ChangeExtension(Path.GetTempFileName(), ".qs"); var currentContent = file.GetFileContent(); File.WriteAllText(tempFile, currentContent); + this.Log($"initial file content: \n{File.ReadAllText(currentContent)}", MessageType.Info); this.Log($"command: {command}, dllPath: {dllPath} (exists: {File.Exists(dllPath)})", MessageType.Info); var commandArgs = $"{dllPath} {verb} --input {tempFile}"; @@ -850,6 +851,7 @@ private void TypeCheckFile(string fileId, CancellationToken cancellationToken) { var range = DataTypes.Range.Create(DataTypes.Position.Zero, file.End()); var edit = new TextEdit { Range = range.ToLsp(), NewText = File.ReadAllText(tempFile) }; + this.Log($"formatted file content: \n{edit.NewText}", MessageType.Info); File.Delete(tempFile); return new[] { edit }; } @@ -858,6 +860,7 @@ private void TypeCheckFile(string fileId, CancellationToken cancellationToken) if (qsFmtExe != null) { this.Log($"Failed to format document. Formatter may be unavailable.", MessageType.Error); + this.Log($"Caught exception: {ex}", MessageType.Error); } else { From 75ecb429ce71b2df423686847c9fee8c3b2e4f4c Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Tue, 26 Oct 2021 09:41:44 -0700 Subject: [PATCH 24/37] ... and one more --- src/QsCompiler/CompilationManager/CompilationUnitManager.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/QsCompiler/CompilationManager/CompilationUnitManager.cs b/src/QsCompiler/CompilationManager/CompilationUnitManager.cs index 336ecf33fe..a09b6678b1 100644 --- a/src/QsCompiler/CompilationManager/CompilationUnitManager.cs +++ b/src/QsCompiler/CompilationManager/CompilationUnitManager.cs @@ -837,6 +837,7 @@ private void TypeCheckFile(string fileId, CancellationToken cancellationToken) TextEdit[]? FormatFile(FileContentManager file) { var tempFile = Path.ChangeExtension(Path.GetTempFileName(), ".qs"); + this.Log($"temp file: {tempFile} (exists: {File.Exists(tempFile)})", MessageType.Info); var currentContent = file.GetFileContent(); File.WriteAllText(tempFile, currentContent); this.Log($"initial file content: \n{File.ReadAllText(currentContent)}", MessageType.Info); From 1f8b8c1bcdca829e794972940bd64f1cb0a68089 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Tue, 26 Oct 2021 10:05:16 -0700 Subject: [PATCH 25/37] typo --- src/QsCompiler/CompilationManager/CompilationUnitManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/QsCompiler/CompilationManager/CompilationUnitManager.cs b/src/QsCompiler/CompilationManager/CompilationUnitManager.cs index a09b6678b1..737aece53e 100644 --- a/src/QsCompiler/CompilationManager/CompilationUnitManager.cs +++ b/src/QsCompiler/CompilationManager/CompilationUnitManager.cs @@ -840,7 +840,7 @@ private void TypeCheckFile(string fileId, CancellationToken cancellationToken) this.Log($"temp file: {tempFile} (exists: {File.Exists(tempFile)})", MessageType.Info); var currentContent = file.GetFileContent(); File.WriteAllText(tempFile, currentContent); - this.Log($"initial file content: \n{File.ReadAllText(currentContent)}", MessageType.Info); + this.Log($"initial file content: \n{currentContent}", MessageType.Info); this.Log($"command: {command}, dllPath: {dllPath} (exists: {File.Exists(dllPath)})", MessageType.Info); var commandArgs = $"{dllPath} {verb} --input {tempFile}"; From 8ea3c21d56197edb47a8b8b05b4216418fd9ce03 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Tue, 26 Oct 2021 11:57:08 -0700 Subject: [PATCH 26/37] checking for time out --- .../CompilationManager/CompilationUnitManager.cs | 7 ++++++- src/QsCompiler/CompilationManager/Process.cs | 5 +++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/QsCompiler/CompilationManager/CompilationUnitManager.cs b/src/QsCompiler/CompilationManager/CompilationUnitManager.cs index 737aece53e..dd2f9c76a6 100644 --- a/src/QsCompiler/CompilationManager/CompilationUnitManager.cs +++ b/src/QsCompiler/CompilationManager/CompilationUnitManager.cs @@ -844,8 +844,9 @@ private void TypeCheckFile(string fileId, CancellationToken cancellationToken) this.Log($"command: {command}, dllPath: {dllPath} (exists: {File.Exists(dllPath)})", MessageType.Info); var commandArgs = $"{dllPath} {verb} --input {tempFile}"; + var exitCodeOnTimeout = -250; var succeeded = - ProcessRunner.Run(command, commandArgs, out var _, out var _, out var exitCode, out var ex, timeout: timeout) + ProcessRunner.Run(command, commandArgs, out var _, out var _, out var exitCode, out var ex, timeout: timeout, exitCodeOnTimeOut: exitCodeOnTimeout) && exitCode == 0 && ex == null; if (succeeded) @@ -868,6 +869,10 @@ private void TypeCheckFile(string fileId, CancellationToken cancellationToken) this.LogException(ex); } } + else if (exitCode == exitCodeOnTimeout) + { + this.Log($"Exist code indicated a time out", MessageType.Info); + } return null; } diff --git a/src/QsCompiler/CompilationManager/Process.cs b/src/QsCompiler/CompilationManager/Process.cs index 7172cecef4..7606abc33a 100644 --- a/src/QsCompiler/CompilationManager/Process.cs +++ b/src/QsCompiler/CompilationManager/Process.cs @@ -87,7 +87,8 @@ public static bool Run( out int exitCode, out Exception? ex, IDictionary? envVariables = null, - int timeout = 10000) + int timeout = 10000, + int exitCodeOnTimeOut = 1) { var process = new Process(); process.StartInfo = new ProcessStartInfo @@ -112,7 +113,7 @@ public static bool Run( try { var exited = Run(process, outstream, errstream, out ex, timeout); - exitCode = process.HasExited ? process.ExitCode : 1; + exitCode = process.HasExited ? process.ExitCode : exitCodeOnTimeOut; return exited; } finally From efa02cc2646fda34241638bd1b2cd422bef3a83a Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Tue, 26 Oct 2021 11:57:43 -0700 Subject: [PATCH 27/37] printing exit code --- src/QsCompiler/CompilationManager/CompilationUnitManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/QsCompiler/CompilationManager/CompilationUnitManager.cs b/src/QsCompiler/CompilationManager/CompilationUnitManager.cs index dd2f9c76a6..75eb8b5320 100644 --- a/src/QsCompiler/CompilationManager/CompilationUnitManager.cs +++ b/src/QsCompiler/CompilationManager/CompilationUnitManager.cs @@ -871,7 +871,7 @@ private void TypeCheckFile(string fileId, CancellationToken cancellationToken) } else if (exitCode == exitCodeOnTimeout) { - this.Log($"Exist code indicated a time out", MessageType.Info); + this.Log($"Exist code indicated a time out (exit code: {exitCode})", MessageType.Info); } return null; From f15e9eaf8c2db7a6467a7f00c9a288078871ce63 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Tue, 26 Oct 2021 11:59:58 -0700 Subject: [PATCH 28/37] printing output and error stream --- .../CompilationManager/CompilationUnitManager.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/QsCompiler/CompilationManager/CompilationUnitManager.cs b/src/QsCompiler/CompilationManager/CompilationUnitManager.cs index 75eb8b5320..4d57518239 100644 --- a/src/QsCompiler/CompilationManager/CompilationUnitManager.cs +++ b/src/QsCompiler/CompilationManager/CompilationUnitManager.cs @@ -846,7 +846,7 @@ private void TypeCheckFile(string fileId, CancellationToken cancellationToken) var commandArgs = $"{dllPath} {verb} --input {tempFile}"; var exitCodeOnTimeout = -250; var succeeded = - ProcessRunner.Run(command, commandArgs, out var _, out var _, out var exitCode, out var ex, timeout: timeout, exitCodeOnTimeOut: exitCodeOnTimeout) + ProcessRunner.Run(command, commandArgs, out var outstream, out var errstream, out var exitCode, out var ex, timeout: timeout, exitCodeOnTimeOut: exitCodeOnTimeout) && exitCode == 0 && ex == null; if (succeeded) @@ -872,6 +872,14 @@ private void TypeCheckFile(string fileId, CancellationToken cancellationToken) else if (exitCode == exitCodeOnTimeout) { this.Log($"Exist code indicated a time out (exit code: {exitCode})", MessageType.Info); + this.Log($"produced output stream: \n{outstream}", MessageType.Info); + this.Log($"produced error stream: \n{errstream}", MessageType.Info); + } + else + { + this.Log("Unknown error during formatting", MessageType.Error); + this.Log($"produced output stream: \n{outstream}", MessageType.Info); + this.Log($"produced error stream: \n{errstream}", MessageType.Info); } return null; From 95a95ae8fab8163b69bb1ab495977a5d6cefdf4b Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Tue, 26 Oct 2021 13:31:12 -0700 Subject: [PATCH 29/37] Updating the Microsoft.NET.Test.Sdk since it looks like that might be the issue --- src/QsCompiler/Tests.LanguageServer/Tests.LanguageServer.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/QsCompiler/Tests.LanguageServer/Tests.LanguageServer.csproj b/src/QsCompiler/Tests.LanguageServer/Tests.LanguageServer.csproj index 67257f74d2..575d6cb704 100644 --- a/src/QsCompiler/Tests.LanguageServer/Tests.LanguageServer.csproj +++ b/src/QsCompiler/Tests.LanguageServer/Tests.LanguageServer.csproj @@ -13,7 +13,7 @@ - + From e6c4c8a0dad54eccd36cda4c2f9562233a0e1010 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Tue, 26 Oct 2021 19:03:39 -0700 Subject: [PATCH 30/37] need to publish the formatter for testing --- src/QsCompiler/TestProjects/test15/test15.csproj | 2 +- .../Tests.LanguageServer.csproj | 14 +++++++++++++- src/QuantumSdk/Sdk/Sdk.targets | 4 ++-- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/QsCompiler/TestProjects/test15/test15.csproj b/src/QsCompiler/TestProjects/test15/test15.csproj index d1ee672734..6ee168a3bb 100644 --- a/src/QsCompiler/TestProjects/test15/test15.csproj +++ b/src/QsCompiler/TestProjects/test15/test15.csproj @@ -6,7 +6,7 @@ - dotnet $(MSBuildProjectDirectory)/../../qsfmt.dll + dotnet $(MSBuildProjectDirectory)/../../qsfmt/qsfmt.dll diff --git a/src/QsCompiler/Tests.LanguageServer/Tests.LanguageServer.csproj b/src/QsCompiler/Tests.LanguageServer/Tests.LanguageServer.csproj index 575d6cb704..d829ca7b3a 100644 --- a/src/QsCompiler/Tests.LanguageServer/Tests.LanguageServer.csproj +++ b/src/QsCompiler/Tests.LanguageServer/Tests.LanguageServer.csproj @@ -21,7 +21,6 @@ - true @@ -38,4 +37,17 @@ + + + + + + + + + + + + + diff --git a/src/QuantumSdk/Sdk/Sdk.targets b/src/QuantumSdk/Sdk/Sdk.targets index 71ddba13df..2ccfa9e219 100644 --- a/src/QuantumSdk/Sdk/Sdk.targets +++ b/src/QuantumSdk/Sdk/Sdk.targets @@ -185,8 +185,8 @@ - - + + From 0e7721cdce0d38513a2fab21b3428ee4dc29c360 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Tue, 26 Oct 2021 19:46:33 -0700 Subject: [PATCH 31/37] cleaning up --- .../CompilationUnitManager.cs | 38 +++++++------------ .../CompilationManager/ProjectManager.cs | 10 +++++ .../DataStructures/ReservedKeywords.fs | 1 + src/QsCompiler/LanguageServer/EditorState.cs | 1 + .../Tests.LanguageServer.csproj | 6 ++- 5 files changed, 30 insertions(+), 26 deletions(-) diff --git a/src/QsCompiler/CompilationManager/CompilationUnitManager.cs b/src/QsCompiler/CompilationManager/CompilationUnitManager.cs index 4d57518239..bb010474b2 100644 --- a/src/QsCompiler/CompilationManager/CompilationUnitManager.cs +++ b/src/QsCompiler/CompilationManager/CompilationUnitManager.cs @@ -827,9 +827,9 @@ private void TypeCheckFile(string fileId, CancellationToken cancellationToken) ? (fmtCommand[0], string.Join(" ", fmtCommand[1..])) : ("dotnet", Path.Combine(sdkPath ?? "", "tools", "qsfmt", "qsfmt.dll")); - this.Log($"qsfmt path: {dllPath} (exists: {File.Exists(dllPath)})", MessageType.Info); - this.Log($"sdk path: {sdkPath} (exists: {Directory.Exists(sdkPath)})", MessageType.Info); - if (verb == null || (fmtCommand?.Length == 2 && !File.Exists(dllPath) && !Directory.Exists(sdkPath))) + // It is possible for File.Exists/Directory.Exists to return false for both dllPath and sdkPath + // despite that the command can indeed be successfully executed. + if (verb == null) { return null; } @@ -837,49 +837,39 @@ private void TypeCheckFile(string fileId, CancellationToken cancellationToken) TextEdit[]? FormatFile(FileContentManager file) { var tempFile = Path.ChangeExtension(Path.GetTempFileName(), ".qs"); - this.Log($"temp file: {tempFile} (exists: {File.Exists(tempFile)})", MessageType.Info); var currentContent = file.GetFileContent(); File.WriteAllText(tempFile, currentContent); - this.Log($"initial file content: \n{currentContent}", MessageType.Info); - this.Log($"command: {command}, dllPath: {dllPath} (exists: {File.Exists(dllPath)})", MessageType.Info); + // The exit code is selected looking at this: https://tldp.org/LDP/abs/html/exitcodes.html + // Code 130 usually indicates "Script terminated by Control-C", and seem appropriate in this case. + var exitCodeOnTimeout = 130; var commandArgs = $"{dllPath} {verb} --input {tempFile}"; - var exitCodeOnTimeout = -250; + this.Log($"Invoking formatting command: {command} {commandArgs}", MessageType.Info); var succeeded = - ProcessRunner.Run(command, commandArgs, out var outstream, out var errstream, out var exitCode, out var ex, timeout: timeout, exitCodeOnTimeOut: exitCodeOnTimeout) + ProcessRunner.Run(command, commandArgs, out var _, out var errstream, out var exitCode, out var ex, timeout: timeout, exitCodeOnTimeOut: exitCodeOnTimeout) && exitCode == 0 && ex == null; if (succeeded) { var range = DataTypes.Range.Create(DataTypes.Position.Zero, file.End()); var edit = new TextEdit { Range = range.ToLsp(), NewText = File.ReadAllText(tempFile) }; - this.Log($"formatted file content: \n{edit.NewText}", MessageType.Info); File.Delete(tempFile); return new[] { edit }; } + else if (exitCode == exitCodeOnTimeout) + { + this.Log($"Formatting command timed out.", MessageType.Info); + } else if (ex != null) { - if (qsFmtExe != null) - { - this.Log($"Failed to format document. Formatter may be unavailable.", MessageType.Error); - this.Log($"Caught exception: {ex}", MessageType.Error); - } - else + if (qsFmtExe == null && this.compilationUnit.BuildProperties.SdkVersion > new Version(0, 21)) { this.LogException(ex); } } - else if (exitCode == exitCodeOnTimeout) - { - this.Log($"Exist code indicated a time out (exit code: {exitCode})", MessageType.Info); - this.Log($"produced output stream: \n{outstream}", MessageType.Info); - this.Log($"produced error stream: \n{errstream}", MessageType.Info); - } else { - this.Log("Unknown error during formatting", MessageType.Error); - this.Log($"produced output stream: \n{outstream}", MessageType.Info); - this.Log($"produced error stream: \n{errstream}", MessageType.Info); + this.Log($"Unknown error during formatting (exit code {exitCode}): \n{errstream}", MessageType.Error); } return null; diff --git a/src/QsCompiler/CompilationManager/ProjectManager.cs b/src/QsCompiler/CompilationManager/ProjectManager.cs index 82268f96d1..917f2c5dab 100644 --- a/src/QsCompiler/CompilationManager/ProjectManager.cs +++ b/src/QsCompiler/CompilationManager/ProjectManager.cs @@ -35,6 +35,16 @@ public class ProjectProperties ? version : new Version(DefaultAssemblyVersion.Major, DefaultAssemblyVersion.Minor); + /// + /// Returns the value specified by , + /// or null if no valid value is specified. + /// + public Version? SdkVersion => + this.BuildProperties.TryGetValue(MSBuildProperties.QuantumSdkVersion, out var versionProp) + && Version.TryParse(versionProp, out Version version) + ? version + : null; + /// /// Returns the value specified by , /// or an empty string if no value is specified. diff --git a/src/QsCompiler/DataStructures/ReservedKeywords.fs b/src/QsCompiler/DataStructures/ReservedKeywords.fs index 7e5540deed..5c2da35c21 100644 --- a/src/QsCompiler/DataStructures/ReservedKeywords.fs +++ b/src/QsCompiler/DataStructures/ReservedKeywords.fs @@ -242,6 +242,7 @@ module GeneratedAttributes = module MSBuildProperties = let QuantumSdkPath = "QuantumSdkPath" let QsharpLangVersion = "QSharpLangVersion" + let QuantumSdkVersion = "QuantumSdkVersion" let TargetPath = "TargetPath" let ResolvedProcessorArchitecture = "ResolvedProcessorArchitecture" let ResolvedRuntimeCapabilities = "ResolvedRuntimeCapabilities" diff --git a/src/QsCompiler/LanguageServer/EditorState.cs b/src/QsCompiler/LanguageServer/EditorState.cs index 1bdc953c75..c418d0fee7 100644 --- a/src/QsCompiler/LanguageServer/EditorState.cs +++ b/src/QsCompiler/LanguageServer/EditorState.cs @@ -144,6 +144,7 @@ void AddProperty(IDictionary props, string property) => AddProperty(buildProperties, MSBuildProperties.TargetPath); AddProperty(buildProperties, MSBuildProperties.ResolvedProcessorArchitecture); AddProperty(buildProperties, MSBuildProperties.QuantumSdkPath); + AddProperty(buildProperties, MSBuildProperties.QuantumSdkVersion); AddProperty(buildProperties, MSBuildProperties.QsharpLangVersion); AddProperty(buildProperties, MSBuildProperties.ResolvedRuntimeCapabilities); AddProperty(buildProperties, MSBuildProperties.ResolvedQsharpOutputType); diff --git a/src/QsCompiler/Tests.LanguageServer/Tests.LanguageServer.csproj b/src/QsCompiler/Tests.LanguageServer/Tests.LanguageServer.csproj index d829ca7b3a..88d0727c6d 100644 --- a/src/QsCompiler/Tests.LanguageServer/Tests.LanguageServer.csproj +++ b/src/QsCompiler/Tests.LanguageServer/Tests.LanguageServer.csproj @@ -6,6 +6,7 @@ Tests.Microsoft.Quantum.QsLanguageServer Library $(NoWarn);NU1701 + $([MSBuild]::NormalizeDirectory($(MSBuildThisFileDirectory)..\..\..\)) @@ -39,12 +40,13 @@ - + - + + From 4b96fe09c3a3e66310a47dccb735c9383cd5a9d8 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Tue, 26 Oct 2021 20:14:46 -0700 Subject: [PATCH 32/37] removing temporary project --- .gitignore | 1 - src/QsCompiler/LanguageServer/EditorState.cs | 30 +------------------ .../LanguageServer/LanguageServer.cs | 6 +--- .../TemporaryProject.cs.v.template | 18 ----------- src/QsCompiler/TestProjects/TestProjects.sln | 10 +++---- .../TestProjects/test14/Operation14.qs | 10 ------- .../TestProjects/{test15 => test14}/README.md | 0 .../{test15 => test14}/format/Formatted.qs | 0 .../{test15 => test14}/format/Unformatted.qs | 0 .../test15.csproj => test14/test14.csproj} | 0 .../ProjectLoaderTests.cs | 19 +----------- src/QsCompiler/Tests.LanguageServer/Tests.cs | 5 ++-- 12 files changed, 11 insertions(+), 88 deletions(-) delete mode 100644 src/QsCompiler/LanguageServer/TemporaryProject.cs.v.template delete mode 100644 src/QsCompiler/TestProjects/test14/Operation14.qs rename src/QsCompiler/TestProjects/{test15 => test14}/README.md (100%) rename src/QsCompiler/TestProjects/{test15 => test14}/format/Formatted.qs (100%) rename src/QsCompiler/TestProjects/{test15 => test14}/format/Unformatted.qs (100%) rename src/QsCompiler/TestProjects/{test15/test15.csproj => test14/test14.csproj} (100%) diff --git a/.gitignore b/.gitignore index 32318eee28..db0688c2ef 100644 --- a/.gitignore +++ b/.gitignore @@ -342,7 +342,6 @@ ASALocalRun/ src/ProjectTemplates/Quantum.App1/Quantum.App1.csproj src/ProjectTemplates/Quantum.Library1/Quantum.Library1.csproj src/ProjectTemplates/Quantum.Test1/Quantum.Test1.csproj -src/QsCompiler/LanguageServer/TemporaryProject.cs src/QuantumSdk/DefaultItems/DefaultItems.props src/ProjectTemplates/Quantum.App1/.template.config/template.json src/ProjectTemplates/Quantum.Library1/.template.config/template.json diff --git a/src/QsCompiler/LanguageServer/EditorState.cs b/src/QsCompiler/LanguageServer/EditorState.cs index c418d0fee7..fb7ae1750d 100644 --- a/src/QsCompiler/LanguageServer/EditorState.cs +++ b/src/QsCompiler/LanguageServer/EditorState.cs @@ -10,7 +10,6 @@ using System.Linq; using System.Threading.Tasks; using Microsoft.Build.Execution; -using Microsoft.Quantum.QsCompiler; using Microsoft.Quantum.QsCompiler.CompilationBuilder; using Microsoft.Quantum.QsCompiler.ReservedKeywords; using Microsoft.VisualStudio.LanguageServer.Protocol; @@ -30,7 +29,6 @@ internal class EditorState : IDisposable private readonly Action publish; private readonly Action, Dictionary> sendTelemetry; - private readonly Action? onTemporaryProjectLoaded; /// /// needed to determine if the reality of a source file that has changed on disk is indeed given by the content on disk, @@ -62,8 +60,7 @@ internal EditorState( Action? publishDiagnostics, Action, Dictionary>? sendTelemetry, Action? log, - Action? onException, - Action? onTemporaryProjectLoaded) + Action? onException) { this.ignoreEditorUpdatesForFiles = new ConcurrentDictionary(); this.sendTelemetry = sendTelemetry ?? ((eventName, properties, measurements) => { }); @@ -88,7 +85,6 @@ internal EditorState( this.projectLoader = projectLoader; this.projects = new ProjectManager(onException, log, this.publish); - this.onTemporaryProjectLoaded = onTemporaryProjectLoaded; } /// @@ -159,30 +155,6 @@ void AddProperty(IDictionary props, string property) => return true; } - internal Uri QsTemporaryProjectLoader(Uri sourceFileUri, string? sdkVersion) - { - var sourceFolderPath = Path.GetDirectoryName(sourceFileUri.LocalPath) ?? ""; - var projectFileName = string.Join( - "_x2f_", // arbitrary string to help avoid collisions - sourceFolderPath - .Replace("_", "_x5f_") // arbitrary string to help avoid collisions - .Split(Path.GetInvalidFileNameChars())); - var projectFolderPath = Directory.CreateDirectory(Path.Combine( - Path.GetTempPath(), - "qsharp", - projectFileName)).FullName; - var projectFilePath = Path.Combine(projectFolderPath, $"generated.csproj"); - using (var outputFile = new StreamWriter(projectFilePath)) - { - outputFile.WriteLine( - TemporaryProject.GetFileContents( - compilationScope: Path.Combine(sourceFolderPath, "*.qs"), - sdkVersion: sdkVersion)); - } - - return new Uri(projectFilePath); - } - /// /// For each given uri, loads the corresponding project if the uri contains the project file for a Q# project, /// and publishes suitable diagnostics for it. diff --git a/src/QsCompiler/LanguageServer/LanguageServer.cs b/src/QsCompiler/LanguageServer/LanguageServer.cs index 2ddf35f5db..f0e6ea5e9f 100644 --- a/src/QsCompiler/LanguageServer/LanguageServer.cs +++ b/src/QsCompiler/LanguageServer/LanguageServer.cs @@ -100,8 +100,7 @@ void ProcessFileEvents(IEnumerable e) => diagnostics => this.PublishDiagnosticsAsync(diagnostics), (name, props, meas) => this.SendTelemetryAsync(name, props, meas), this.LogToWindow, - this.OnInternalError, - this.OnTemporaryProjectLoaded); + this.OnInternalError); this.waitForInit.Set(); } @@ -214,9 +213,6 @@ internal void OnInternalError(Exception ex) } } - private void OnTemporaryProjectLoaded(Uri projectUri) => - this.fileWatcher.ListenAsync(Path.GetDirectoryName(projectUri.LocalPath), false, null, "*.csproj").Wait(); - /* jsonrpc methods for initialization and shut down */ private Task InitializeWorkspaceAsync(ImmutableDictionary> folders) diff --git a/src/QsCompiler/LanguageServer/TemporaryProject.cs.v.template b/src/QsCompiler/LanguageServer/TemporaryProject.cs.v.template deleted file mode 100644 index 13d6aad7ec..0000000000 --- a/src/QsCompiler/LanguageServer/TemporaryProject.cs.v.template +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -namespace Microsoft.Quantum.QsLanguageServer -{ - internal static class TemporaryProject - { - internal static string GetFileContents(string compilationScope, string? sdkVersion = null) => $@" - - - netstandard2.1 - - - - - "; - } -} diff --git a/src/QsCompiler/TestProjects/TestProjects.sln b/src/QsCompiler/TestProjects/TestProjects.sln index 64c34cffbd..44b3f7b77f 100644 --- a/src/QsCompiler/TestProjects/TestProjects.sln +++ b/src/QsCompiler/TestProjects/TestProjects.sln @@ -29,7 +29,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "test12", "test12\test12.csp EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "test13", "test13\test13.csproj", "{3EDCBFE8-4EAD-4B2A-A899-10511A5721D3}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "test15", "test15\test15.csproj", "{956FE0B5-CDBA-4865-BBCC-3AF72FC7C791}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "test14", "test14\test14.csproj", "{F87BFA01-FE9C-4AD0-8769-4758C1370B9C}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -89,10 +89,10 @@ Global {3EDCBFE8-4EAD-4B2A-A899-10511A5721D3}.Debug|Any CPU.Build.0 = Debug|Any CPU {3EDCBFE8-4EAD-4B2A-A899-10511A5721D3}.Release|Any CPU.ActiveCfg = Release|Any CPU {3EDCBFE8-4EAD-4B2A-A899-10511A5721D3}.Release|Any CPU.Build.0 = Release|Any CPU - {956FE0B5-CDBA-4865-BBCC-3AF72FC7C791}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {956FE0B5-CDBA-4865-BBCC-3AF72FC7C791}.Debug|Any CPU.Build.0 = Debug|Any CPU - {956FE0B5-CDBA-4865-BBCC-3AF72FC7C791}.Release|Any CPU.ActiveCfg = Release|Any CPU - {956FE0B5-CDBA-4865-BBCC-3AF72FC7C791}.Release|Any CPU.Build.0 = Release|Any CPU + {F87BFA01-FE9C-4AD0-8769-4758C1370B9C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F87BFA01-FE9C-4AD0-8769-4758C1370B9C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F87BFA01-FE9C-4AD0-8769-4758C1370B9C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F87BFA01-FE9C-4AD0-8769-4758C1370B9C}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/QsCompiler/TestProjects/test14/Operation14.qs b/src/QsCompiler/TestProjects/test14/Operation14.qs deleted file mode 100644 index 4b01284140..0000000000 --- a/src/QsCompiler/TestProjects/test14/Operation14.qs +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -namespace test14 { - open Microsoft.Quantum.Canon; - open Microsoft.Quantum.Intrinsic; - - operation Operation () : Unit { - } -} diff --git a/src/QsCompiler/TestProjects/test15/README.md b/src/QsCompiler/TestProjects/test14/README.md similarity index 100% rename from src/QsCompiler/TestProjects/test15/README.md rename to src/QsCompiler/TestProjects/test14/README.md diff --git a/src/QsCompiler/TestProjects/test15/format/Formatted.qs b/src/QsCompiler/TestProjects/test14/format/Formatted.qs similarity index 100% rename from src/QsCompiler/TestProjects/test15/format/Formatted.qs rename to src/QsCompiler/TestProjects/test14/format/Formatted.qs diff --git a/src/QsCompiler/TestProjects/test15/format/Unformatted.qs b/src/QsCompiler/TestProjects/test14/format/Unformatted.qs similarity index 100% rename from src/QsCompiler/TestProjects/test15/format/Unformatted.qs rename to src/QsCompiler/TestProjects/test14/format/Unformatted.qs diff --git a/src/QsCompiler/TestProjects/test15/test15.csproj b/src/QsCompiler/TestProjects/test14/test14.csproj similarity index 100% rename from src/QsCompiler/TestProjects/test15/test15.csproj rename to src/QsCompiler/TestProjects/test14/test14.csproj diff --git a/src/QsCompiler/Tests.LanguageServer/ProjectLoaderTests.cs b/src/QsCompiler/Tests.LanguageServer/ProjectLoaderTests.cs index 0dbfb1167f..3b18ee3cdd 100644 --- a/src/QsCompiler/Tests.LanguageServer/ProjectLoaderTests.cs +++ b/src/QsCompiler/Tests.LanguageServer/ProjectLoaderTests.cs @@ -310,20 +310,6 @@ public void LoadQSharpMultiFrameworkLibrary() Assert.IsTrue(context.UsesProject("test3.csproj")); CollectionAssert.AreEquivalent(qsFiles, context.SourceFiles.ToArray()); } - - [TestMethod] - public void LoadQSharpTemporaryProject() - { - var sourceFile = Path.GetFullPath(SourceFileName("test14", "Operation14.qs")); - var projectUri = CompilationContext.CreateTemporaryProject(new Uri(sourceFile), "0.12.20072031"); - Assert.IsNotNull(projectUri); - - var qsFiles = new string[] { sourceFile }; - var projectInformation = CompilationContext.Load(projectUri); - Assert.IsNotNull(projectInformation); - Assert.IsTrue(projectInformation!.UsesCanon()); - CollectionAssert.AreEquivalent(qsFiles, projectInformation!.SourceFiles.ToArray()); - } } internal static class CompilationContext @@ -332,14 +318,11 @@ private static void LogOutput(string msg, MessageType level) => Console.WriteLine($"[{level}]: {msg}"); internal static EditorState Editor => - new EditorState(new ProjectLoader(LogOutput), null, null, null, null, null); + new EditorState(new ProjectLoader(LogOutput), null, null, null, null); internal static ProjectInformation? Load(Uri projectFile) => Editor.QsProjectLoader(projectFile, out var loaded) ? loaded : null; - internal static Uri CreateTemporaryProject(Uri sourceFile, string sdkVersion) => - Editor.QsTemporaryProjectLoader(sourceFile, sdkVersion); - internal static bool UsesDll(this ProjectInformation info, string dll) => info.References.Any(r => r.EndsWith(dll)); internal static bool UsesProject(this ProjectInformation info, string projectFileName) => info.ProjectReferences.Any(r => r.EndsWith(projectFileName)); diff --git a/src/QsCompiler/Tests.LanguageServer/Tests.cs b/src/QsCompiler/Tests.LanguageServer/Tests.cs index bef3da76ba..4c03f5e4fe 100644 --- a/src/QsCompiler/Tests.LanguageServer/Tests.cs +++ b/src/QsCompiler/Tests.LanguageServer/Tests.cs @@ -277,9 +277,10 @@ async Task RunTest(bool emptyLastLine, bool useQsExtension) } } + await RunTest(emptyLastLine: true, useQsExtension: true); await RunTest(emptyLastLine: true, useQsExtension: false); + await RunTest(emptyLastLine: false, useQsExtension: true); await RunTest(emptyLastLine: false, useQsExtension: false); - await RunTest(emptyLastLine: true, useQsExtension: true); } [TestMethod] @@ -361,7 +362,7 @@ await projectManager.LoadProjectsAsync( } await RunFormattingTestAsync("test12"); - await RunFormattingTestAsync("test15"); + await RunFormattingTestAsync("test14"); } } } From 20f2c33ec7e437793cf70cd23899fca89b8f8de3 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Tue, 26 Oct 2021 20:16:16 -0700 Subject: [PATCH 33/37] fixing incorrect detection of nullable --- src/QsCompiler/CompilationManager/CompilationUnitManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/QsCompiler/CompilationManager/CompilationUnitManager.cs b/src/QsCompiler/CompilationManager/CompilationUnitManager.cs index bb010474b2..0457101b74 100644 --- a/src/QsCompiler/CompilationManager/CompilationUnitManager.cs +++ b/src/QsCompiler/CompilationManager/CompilationUnitManager.cs @@ -823,7 +823,7 @@ private void TypeCheckFile(string fileId, CancellationToken cancellationToken) var sdkPath = this.compilationUnit.BuildProperties.SdkPath; var fmtCommand = qsFmtExe?.Split(); - var (command, dllPath) = fmtCommand != null && fmtCommand.Length > 0 + (string command, string dllPath) = fmtCommand != null && fmtCommand.Length > 0 ? (fmtCommand[0], string.Join(" ", fmtCommand[1..])) : ("dotnet", Path.Combine(sdkPath ?? "", "tools", "qsfmt", "qsfmt.dll")); From 0fe7cc2cf80a7bdbf2e73ab588bb9306842478e9 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Tue, 26 Oct 2021 20:17:34 -0700 Subject: [PATCH 34/37] Revert "removing temporary project" This reverts commit 4b96fe09c3a3e66310a47dccb735c9383cd5a9d8. --- .gitignore | 1 + src/QsCompiler/LanguageServer/EditorState.cs | 30 ++++++++++++++++++- .../LanguageServer/LanguageServer.cs | 6 +++- .../TemporaryProject.cs.v.template | 18 +++++++++++ src/QsCompiler/TestProjects/TestProjects.sln | 10 +++---- .../TestProjects/test14/Operation14.qs | 10 +++++++ .../TestProjects/{test14 => test15}/README.md | 0 .../{test14 => test15}/format/Formatted.qs | 0 .../{test14 => test15}/format/Unformatted.qs | 0 .../test14.csproj => test15/test15.csproj} | 0 .../ProjectLoaderTests.cs | 19 +++++++++++- src/QsCompiler/Tests.LanguageServer/Tests.cs | 5 ++-- 12 files changed, 88 insertions(+), 11 deletions(-) create mode 100644 src/QsCompiler/LanguageServer/TemporaryProject.cs.v.template create mode 100644 src/QsCompiler/TestProjects/test14/Operation14.qs rename src/QsCompiler/TestProjects/{test14 => test15}/README.md (100%) rename src/QsCompiler/TestProjects/{test14 => test15}/format/Formatted.qs (100%) rename src/QsCompiler/TestProjects/{test14 => test15}/format/Unformatted.qs (100%) rename src/QsCompiler/TestProjects/{test14/test14.csproj => test15/test15.csproj} (100%) diff --git a/.gitignore b/.gitignore index db0688c2ef..32318eee28 100644 --- a/.gitignore +++ b/.gitignore @@ -342,6 +342,7 @@ ASALocalRun/ src/ProjectTemplates/Quantum.App1/Quantum.App1.csproj src/ProjectTemplates/Quantum.Library1/Quantum.Library1.csproj src/ProjectTemplates/Quantum.Test1/Quantum.Test1.csproj +src/QsCompiler/LanguageServer/TemporaryProject.cs src/QuantumSdk/DefaultItems/DefaultItems.props src/ProjectTemplates/Quantum.App1/.template.config/template.json src/ProjectTemplates/Quantum.Library1/.template.config/template.json diff --git a/src/QsCompiler/LanguageServer/EditorState.cs b/src/QsCompiler/LanguageServer/EditorState.cs index fb7ae1750d..c418d0fee7 100644 --- a/src/QsCompiler/LanguageServer/EditorState.cs +++ b/src/QsCompiler/LanguageServer/EditorState.cs @@ -10,6 +10,7 @@ using System.Linq; using System.Threading.Tasks; using Microsoft.Build.Execution; +using Microsoft.Quantum.QsCompiler; using Microsoft.Quantum.QsCompiler.CompilationBuilder; using Microsoft.Quantum.QsCompiler.ReservedKeywords; using Microsoft.VisualStudio.LanguageServer.Protocol; @@ -29,6 +30,7 @@ internal class EditorState : IDisposable private readonly Action publish; private readonly Action, Dictionary> sendTelemetry; + private readonly Action? onTemporaryProjectLoaded; /// /// needed to determine if the reality of a source file that has changed on disk is indeed given by the content on disk, @@ -60,7 +62,8 @@ internal EditorState( Action? publishDiagnostics, Action, Dictionary>? sendTelemetry, Action? log, - Action? onException) + Action? onException, + Action? onTemporaryProjectLoaded) { this.ignoreEditorUpdatesForFiles = new ConcurrentDictionary(); this.sendTelemetry = sendTelemetry ?? ((eventName, properties, measurements) => { }); @@ -85,6 +88,7 @@ internal EditorState( this.projectLoader = projectLoader; this.projects = new ProjectManager(onException, log, this.publish); + this.onTemporaryProjectLoaded = onTemporaryProjectLoaded; } /// @@ -155,6 +159,30 @@ void AddProperty(IDictionary props, string property) => return true; } + internal Uri QsTemporaryProjectLoader(Uri sourceFileUri, string? sdkVersion) + { + var sourceFolderPath = Path.GetDirectoryName(sourceFileUri.LocalPath) ?? ""; + var projectFileName = string.Join( + "_x2f_", // arbitrary string to help avoid collisions + sourceFolderPath + .Replace("_", "_x5f_") // arbitrary string to help avoid collisions + .Split(Path.GetInvalidFileNameChars())); + var projectFolderPath = Directory.CreateDirectory(Path.Combine( + Path.GetTempPath(), + "qsharp", + projectFileName)).FullName; + var projectFilePath = Path.Combine(projectFolderPath, $"generated.csproj"); + using (var outputFile = new StreamWriter(projectFilePath)) + { + outputFile.WriteLine( + TemporaryProject.GetFileContents( + compilationScope: Path.Combine(sourceFolderPath, "*.qs"), + sdkVersion: sdkVersion)); + } + + return new Uri(projectFilePath); + } + /// /// For each given uri, loads the corresponding project if the uri contains the project file for a Q# project, /// and publishes suitable diagnostics for it. diff --git a/src/QsCompiler/LanguageServer/LanguageServer.cs b/src/QsCompiler/LanguageServer/LanguageServer.cs index f0e6ea5e9f..2ddf35f5db 100644 --- a/src/QsCompiler/LanguageServer/LanguageServer.cs +++ b/src/QsCompiler/LanguageServer/LanguageServer.cs @@ -100,7 +100,8 @@ void ProcessFileEvents(IEnumerable e) => diagnostics => this.PublishDiagnosticsAsync(diagnostics), (name, props, meas) => this.SendTelemetryAsync(name, props, meas), this.LogToWindow, - this.OnInternalError); + this.OnInternalError, + this.OnTemporaryProjectLoaded); this.waitForInit.Set(); } @@ -213,6 +214,9 @@ internal void OnInternalError(Exception ex) } } + private void OnTemporaryProjectLoaded(Uri projectUri) => + this.fileWatcher.ListenAsync(Path.GetDirectoryName(projectUri.LocalPath), false, null, "*.csproj").Wait(); + /* jsonrpc methods for initialization and shut down */ private Task InitializeWorkspaceAsync(ImmutableDictionary> folders) diff --git a/src/QsCompiler/LanguageServer/TemporaryProject.cs.v.template b/src/QsCompiler/LanguageServer/TemporaryProject.cs.v.template new file mode 100644 index 0000000000..13d6aad7ec --- /dev/null +++ b/src/QsCompiler/LanguageServer/TemporaryProject.cs.v.template @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace Microsoft.Quantum.QsLanguageServer +{ + internal static class TemporaryProject + { + internal static string GetFileContents(string compilationScope, string? sdkVersion = null) => $@" + + + netstandard2.1 + + + + + "; + } +} diff --git a/src/QsCompiler/TestProjects/TestProjects.sln b/src/QsCompiler/TestProjects/TestProjects.sln index 44b3f7b77f..64c34cffbd 100644 --- a/src/QsCompiler/TestProjects/TestProjects.sln +++ b/src/QsCompiler/TestProjects/TestProjects.sln @@ -29,7 +29,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "test12", "test12\test12.csp EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "test13", "test13\test13.csproj", "{3EDCBFE8-4EAD-4B2A-A899-10511A5721D3}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "test14", "test14\test14.csproj", "{F87BFA01-FE9C-4AD0-8769-4758C1370B9C}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "test15", "test15\test15.csproj", "{956FE0B5-CDBA-4865-BBCC-3AF72FC7C791}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -89,10 +89,10 @@ Global {3EDCBFE8-4EAD-4B2A-A899-10511A5721D3}.Debug|Any CPU.Build.0 = Debug|Any CPU {3EDCBFE8-4EAD-4B2A-A899-10511A5721D3}.Release|Any CPU.ActiveCfg = Release|Any CPU {3EDCBFE8-4EAD-4B2A-A899-10511A5721D3}.Release|Any CPU.Build.0 = Release|Any CPU - {F87BFA01-FE9C-4AD0-8769-4758C1370B9C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F87BFA01-FE9C-4AD0-8769-4758C1370B9C}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F87BFA01-FE9C-4AD0-8769-4758C1370B9C}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F87BFA01-FE9C-4AD0-8769-4758C1370B9C}.Release|Any CPU.Build.0 = Release|Any CPU + {956FE0B5-CDBA-4865-BBCC-3AF72FC7C791}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {956FE0B5-CDBA-4865-BBCC-3AF72FC7C791}.Debug|Any CPU.Build.0 = Debug|Any CPU + {956FE0B5-CDBA-4865-BBCC-3AF72FC7C791}.Release|Any CPU.ActiveCfg = Release|Any CPU + {956FE0B5-CDBA-4865-BBCC-3AF72FC7C791}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/QsCompiler/TestProjects/test14/Operation14.qs b/src/QsCompiler/TestProjects/test14/Operation14.qs new file mode 100644 index 0000000000..4b01284140 --- /dev/null +++ b/src/QsCompiler/TestProjects/test14/Operation14.qs @@ -0,0 +1,10 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace test14 { + open Microsoft.Quantum.Canon; + open Microsoft.Quantum.Intrinsic; + + operation Operation () : Unit { + } +} diff --git a/src/QsCompiler/TestProjects/test14/README.md b/src/QsCompiler/TestProjects/test15/README.md similarity index 100% rename from src/QsCompiler/TestProjects/test14/README.md rename to src/QsCompiler/TestProjects/test15/README.md diff --git a/src/QsCompiler/TestProjects/test14/format/Formatted.qs b/src/QsCompiler/TestProjects/test15/format/Formatted.qs similarity index 100% rename from src/QsCompiler/TestProjects/test14/format/Formatted.qs rename to src/QsCompiler/TestProjects/test15/format/Formatted.qs diff --git a/src/QsCompiler/TestProjects/test14/format/Unformatted.qs b/src/QsCompiler/TestProjects/test15/format/Unformatted.qs similarity index 100% rename from src/QsCompiler/TestProjects/test14/format/Unformatted.qs rename to src/QsCompiler/TestProjects/test15/format/Unformatted.qs diff --git a/src/QsCompiler/TestProjects/test14/test14.csproj b/src/QsCompiler/TestProjects/test15/test15.csproj similarity index 100% rename from src/QsCompiler/TestProjects/test14/test14.csproj rename to src/QsCompiler/TestProjects/test15/test15.csproj diff --git a/src/QsCompiler/Tests.LanguageServer/ProjectLoaderTests.cs b/src/QsCompiler/Tests.LanguageServer/ProjectLoaderTests.cs index 3b18ee3cdd..0dbfb1167f 100644 --- a/src/QsCompiler/Tests.LanguageServer/ProjectLoaderTests.cs +++ b/src/QsCompiler/Tests.LanguageServer/ProjectLoaderTests.cs @@ -310,6 +310,20 @@ public void LoadQSharpMultiFrameworkLibrary() Assert.IsTrue(context.UsesProject("test3.csproj")); CollectionAssert.AreEquivalent(qsFiles, context.SourceFiles.ToArray()); } + + [TestMethod] + public void LoadQSharpTemporaryProject() + { + var sourceFile = Path.GetFullPath(SourceFileName("test14", "Operation14.qs")); + var projectUri = CompilationContext.CreateTemporaryProject(new Uri(sourceFile), "0.12.20072031"); + Assert.IsNotNull(projectUri); + + var qsFiles = new string[] { sourceFile }; + var projectInformation = CompilationContext.Load(projectUri); + Assert.IsNotNull(projectInformation); + Assert.IsTrue(projectInformation!.UsesCanon()); + CollectionAssert.AreEquivalent(qsFiles, projectInformation!.SourceFiles.ToArray()); + } } internal static class CompilationContext @@ -318,11 +332,14 @@ private static void LogOutput(string msg, MessageType level) => Console.WriteLine($"[{level}]: {msg}"); internal static EditorState Editor => - new EditorState(new ProjectLoader(LogOutput), null, null, null, null); + new EditorState(new ProjectLoader(LogOutput), null, null, null, null, null); internal static ProjectInformation? Load(Uri projectFile) => Editor.QsProjectLoader(projectFile, out var loaded) ? loaded : null; + internal static Uri CreateTemporaryProject(Uri sourceFile, string sdkVersion) => + Editor.QsTemporaryProjectLoader(sourceFile, sdkVersion); + internal static bool UsesDll(this ProjectInformation info, string dll) => info.References.Any(r => r.EndsWith(dll)); internal static bool UsesProject(this ProjectInformation info, string projectFileName) => info.ProjectReferences.Any(r => r.EndsWith(projectFileName)); diff --git a/src/QsCompiler/Tests.LanguageServer/Tests.cs b/src/QsCompiler/Tests.LanguageServer/Tests.cs index 4c03f5e4fe..bef3da76ba 100644 --- a/src/QsCompiler/Tests.LanguageServer/Tests.cs +++ b/src/QsCompiler/Tests.LanguageServer/Tests.cs @@ -277,10 +277,9 @@ async Task RunTest(bool emptyLastLine, bool useQsExtension) } } - await RunTest(emptyLastLine: true, useQsExtension: true); await RunTest(emptyLastLine: true, useQsExtension: false); - await RunTest(emptyLastLine: false, useQsExtension: true); await RunTest(emptyLastLine: false, useQsExtension: false); + await RunTest(emptyLastLine: true, useQsExtension: true); } [TestMethod] @@ -362,7 +361,7 @@ await projectManager.LoadProjectsAsync( } await RunFormattingTestAsync("test12"); - await RunFormattingTestAsync("test14"); + await RunFormattingTestAsync("test15"); } } } From 8badee90fb568f80e51a2d0145aa9728eacb3b43 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Tue, 26 Oct 2021 21:18:36 -0700 Subject: [PATCH 35/37] minor tweak --- src/QsCompiler/CompilationManager/CompilationUnitManager.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/QsCompiler/CompilationManager/CompilationUnitManager.cs b/src/QsCompiler/CompilationManager/CompilationUnitManager.cs index 0457101b74..96d0f0f45b 100644 --- a/src/QsCompiler/CompilationManager/CompilationUnitManager.cs +++ b/src/QsCompiler/CompilationManager/CompilationUnitManager.cs @@ -844,7 +844,7 @@ private void TypeCheckFile(string fileId, CancellationToken cancellationToken) // Code 130 usually indicates "Script terminated by Control-C", and seem appropriate in this case. var exitCodeOnTimeout = 130; var commandArgs = $"{dllPath} {verb} --input {tempFile}"; - this.Log($"Invoking formatting command: {command} {commandArgs}", MessageType.Info); + this.Log($"Invoking {verb} command: {command} {commandArgs}", MessageType.Info); var succeeded = ProcessRunner.Run(command, commandArgs, out var _, out var errstream, out var exitCode, out var ex, timeout: timeout, exitCodeOnTimeOut: exitCodeOnTimeout) && exitCode == 0 && ex == null; @@ -858,7 +858,7 @@ private void TypeCheckFile(string fileId, CancellationToken cancellationToken) } else if (exitCode == exitCodeOnTimeout) { - this.Log($"Formatting command timed out.", MessageType.Info); + this.Log($"{verb} command timed out.", MessageType.Info); } else if (ex != null) { @@ -869,7 +869,7 @@ private void TypeCheckFile(string fileId, CancellationToken cancellationToken) } else { - this.Log($"Unknown error during formatting (exit code {exitCode}): \n{errstream}", MessageType.Error); + this.Log($"Unknown error during {verb} (exit code {exitCode}): \n{errstream}", MessageType.Error); } return null; From 31b5e3e9a6663f2fa18176234c1fb40ba83e19a4 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Wed, 27 Oct 2021 07:14:31 -0700 Subject: [PATCH 36/37] resolving merge conflict --- .../ProjectLoaderTests.cs | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/src/QsCompiler/Tests.LanguageServer/ProjectLoaderTests.cs b/src/QsCompiler/Tests.LanguageServer/ProjectLoaderTests.cs index 0dbfb1167f..3b18ee3cdd 100644 --- a/src/QsCompiler/Tests.LanguageServer/ProjectLoaderTests.cs +++ b/src/QsCompiler/Tests.LanguageServer/ProjectLoaderTests.cs @@ -310,20 +310,6 @@ public void LoadQSharpMultiFrameworkLibrary() Assert.IsTrue(context.UsesProject("test3.csproj")); CollectionAssert.AreEquivalent(qsFiles, context.SourceFiles.ToArray()); } - - [TestMethod] - public void LoadQSharpTemporaryProject() - { - var sourceFile = Path.GetFullPath(SourceFileName("test14", "Operation14.qs")); - var projectUri = CompilationContext.CreateTemporaryProject(new Uri(sourceFile), "0.12.20072031"); - Assert.IsNotNull(projectUri); - - var qsFiles = new string[] { sourceFile }; - var projectInformation = CompilationContext.Load(projectUri); - Assert.IsNotNull(projectInformation); - Assert.IsTrue(projectInformation!.UsesCanon()); - CollectionAssert.AreEquivalent(qsFiles, projectInformation!.SourceFiles.ToArray()); - } } internal static class CompilationContext @@ -332,14 +318,11 @@ private static void LogOutput(string msg, MessageType level) => Console.WriteLine($"[{level}]: {msg}"); internal static EditorState Editor => - new EditorState(new ProjectLoader(LogOutput), null, null, null, null, null); + new EditorState(new ProjectLoader(LogOutput), null, null, null, null); internal static ProjectInformation? Load(Uri projectFile) => Editor.QsProjectLoader(projectFile, out var loaded) ? loaded : null; - internal static Uri CreateTemporaryProject(Uri sourceFile, string sdkVersion) => - Editor.QsTemporaryProjectLoader(sourceFile, sdkVersion); - internal static bool UsesDll(this ProjectInformation info, string dll) => info.References.Any(r => r.EndsWith(dll)); internal static bool UsesProject(this ProjectInformation info, string projectFileName) => info.ProjectReferences.Any(r => r.EndsWith(projectFileName)); From 6e1a3f75bcfc5380d446834126982bf36feba395 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Wed, 3 Nov 2021 21:04:24 -0700 Subject: [PATCH 37/37] updating formatter readme --- src/QsFmt/README.md | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/QsFmt/README.md b/src/QsFmt/README.md index 9cd6d8ce56..c12a2ffaed 100644 --- a/src/QsFmt/README.md +++ b/src/QsFmt/README.md @@ -64,23 +64,22 @@ Updating rules currently in use: ## Update vs. Format -Currently the formatter only supports rules that update deprecated syntax +Currently the formatter primarily supports rules that update deprecated syntax through the use of the `update` command. A `format` command is being worked on -but is not yet ready for release. This command, when ready, will format Q# -code by running such rules like: +but while it can be invoked, the formatting support is still very limited and the command is currently hidden, meaning it is not suggested in the command line help. +The implemented rules for formatting Q# code may still have gaps and are limited to: - Collapsed Spaces - removes duplicate spaces so that there is only a single space where spaces are used - Operator Spacing - ensure that operators have the appropriate spaces in them - New Lines - removes excessive new lines - Indentation - ensures that there are appropriate indentation for the text +Formatting, to the extend that it is implemented, is supported in the Q# extensions for Visual Studio and VS Code starting with extensions newer than 0.20.2110171573. That support relies on QsFmt, and any contributions/improvements to the QsFmt will automatically be available in the extensions as well. + ## Limitations -- Currently the formatter only supports rules that update deprecated syntax through - the use of the `update` command. The `format` command is not yet ready for release. -- Many types of syntax are not yet supported and will remain unchanged by the formatter. -- There are several bugs related to other types of syntax, especially callable declaration syntax like - attributes, type parameters, and specializations. These may be swallowed by the formatter, - resulting in incorrect output. +- Currently the formatter primarily supports rules that update deprecated syntax through + the use of the `update` command. The `format` command provides limited support for code formatting, and we welcome contributions. +- Some syntax patterns are not yet supported and will remain unchanged by the formatter. Please file issues for code that should be updated but remains unchanged. ## Design