From fe598dd6f017109b5734abb252c13aba320b3335 Mon Sep 17 00:00:00 2001 From: Jimmy Byrd Date: Sun, 19 Feb 2023 12:37:07 -0500 Subject: [PATCH] Fix Tests for AdaptiveLspServer (#1053) --- .github/workflows/build.yml | 1 + paket.lock | 54 ++-- src/FsAutoComplete.Core/Commands.fs | 55 ++-- src/FsAutoComplete.Core/Consts.fs | 10 + .../FsAutoComplete.Core.fsproj | 3 +- .../LspServers/AdaptiveFSharpLspServer.fs | 297 ++++++++++++++---- src/FsAutoComplete/Parser.fs | 11 +- .../FsAutoComplete.Tests.Lsp/CodeLensTests.fs | 1 + .../CompletionTests.fs | 4 +- test/FsAutoComplete.Tests.Lsp/CoreTests.fs | 12 +- .../DependentFileCheckingTests.fs | 142 ++++++--- .../DetectUnitTests.fs | 4 +- .../ExtensionsTests.fs | 2 +- .../FindReferencesTests.fs | 1 + test/FsAutoComplete.Tests.Lsp/GoToTests.fs | 1 + test/FsAutoComplete.Tests.Lsp/Helpers.fs | 51 ++- .../HighlightingTests.fs | 1 + .../InfoPanelTests.fs | 1 + .../InlayHintTests.fs | 4 +- .../InteractiveDirectivesTests.fs | 1 + test/FsAutoComplete.Tests.Lsp/Program.fs | 27 +- test/FsAutoComplete.Tests.Lsp/RenameTests.fs | 3 +- test/FsAutoComplete.Tests.Lsp/ScriptTests.fs | 1 + .../SignatureHelpTests.fs | 1 + .../TemplatesTests.fs | 4 +- .../CrossProject-net6.0/App/App.fsproj | 16 + .../CrossProject-net6.0/App/B.fs | 5 + .../CrossProject-net6.0/Library1/A.fs | 5 + .../Library1/Library1.fsproj | 12 + .../CrossProject-net7.0/App/App.fsproj | 16 + .../CrossProject-net7.0/App/B.fs | 5 + .../CrossProject-net7.0/Library1/A.fs | 5 + .../Library1/Library1.fsproj | 12 + .../UnsedDeclarationsTests.fs | 1 + test/FsAutoComplete.Tests.Lsp/Utils/Server.fs | 45 ++- .../XmlGenerationTests.fs | 1 + .../FsAutoComplete.Tests.Lsp/paket.references | 2 +- 37 files changed, 609 insertions(+), 208 deletions(-) create mode 100644 src/FsAutoComplete.Core/Consts.fs create mode 100644 test/FsAutoComplete.Tests.Lsp/TestCases/DependentFileChecking/CrossProject-net6.0/App/App.fsproj create mode 100644 test/FsAutoComplete.Tests.Lsp/TestCases/DependentFileChecking/CrossProject-net6.0/App/B.fs create mode 100644 test/FsAutoComplete.Tests.Lsp/TestCases/DependentFileChecking/CrossProject-net6.0/Library1/A.fs create mode 100644 test/FsAutoComplete.Tests.Lsp/TestCases/DependentFileChecking/CrossProject-net6.0/Library1/Library1.fsproj create mode 100644 test/FsAutoComplete.Tests.Lsp/TestCases/DependentFileChecking/CrossProject-net7.0/App/App.fsproj create mode 100644 test/FsAutoComplete.Tests.Lsp/TestCases/DependentFileChecking/CrossProject-net7.0/App/B.fs create mode 100644 test/FsAutoComplete.Tests.Lsp/TestCases/DependentFileChecking/CrossProject-net7.0/Library1/A.fs create mode 100644 test/FsAutoComplete.Tests.Lsp/TestCases/DependentFileChecking/CrossProject-net7.0/Library1/Library1.fsproj diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e40763829..68d1e5c67 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -10,6 +10,7 @@ jobs: build: env: TEST_TIMEOUT_MINUTES: 30 + FSAC_TEST_DEFAULT_TIMEOUT : 120000 #ms, individual test timeouts timeout-minutes: 30 # we have a locking issue, so cap the runs at ~20m to account for varying build times, etc strategy: matrix: diff --git a/paket.lock b/paket.lock index 0bbcd5a8a..41d78d4c1 100644 --- a/paket.lock +++ b/paket.lock @@ -917,20 +917,6 @@ NUGET Microsoft.Win32.Registry (>= 4.3) - restriction: || (&& (< net472) (>= netstandard2.0)) (>= net6.0) System.Runtime.CompilerServices.Unsafe (>= 6.0) - restriction: >= net472 System.Security.Permissions (>= 4.7) - restriction: || (&& (< net472) (>= netstandard2.0)) (>= net6.0) - Microsoft.Build.Tasks.Core (17.2) - restriction: >= netstandard2.0 - Microsoft.Build.Framework (>= 17.2) - restriction: >= netstandard2.0 - Microsoft.Build.Utilities.Core (>= 17.2) - restriction: >= netstandard2.0 - Microsoft.IO.Redist (>= 6.0) - restriction: >= net472 - Microsoft.NET.StringTools (>= 1.0) - restriction: >= netstandard2.0 - Microsoft.Win32.Registry (>= 4.3) - restriction: || (&& (< net472) (>= netstandard2.0)) (>= net6.0) - System.CodeDom (>= 4.4) - restriction: || (&& (< net472) (>= netstandard2.0)) (>= net6.0) - System.Collections.Immutable (>= 5.0) - restriction: >= netstandard2.0 - System.Reflection.Metadata (>= 1.6) - restriction: || (&& (< net472) (>= netstandard2.0)) (>= net6.0) - System.Resources.Extensions (>= 4.6) - restriction: >= netstandard2.0 - System.Security.Cryptography.Pkcs (>= 4.7) - restriction: || (&& (< net472) (>= netstandard2.0)) (>= net6.0) - System.Security.Cryptography.Xml (>= 4.7) - restriction: || (&& (< net472) (>= netstandard2.0)) (>= net6.0) - System.Security.Permissions (>= 4.7) - restriction: || (&& (< net472) (>= netstandard2.0)) (>= net6.0) - System.Threading.Tasks.Dataflow (>= 6.0) - restriction: >= netstandard2.0 Microsoft.Build.Utilities.Core (17.2) - restriction: >= netstandard2.0 Microsoft.Build.Framework (>= 17.2) - restriction: >= netstandard2.0 Microsoft.IO.Redist (>= 6.0) - restriction: >= net472 @@ -946,7 +932,7 @@ NUGET Microsoft.NET.StringTools (1.0) - restriction: >= netstandard2.0 System.Memory (>= 4.5.4) - restriction: >= netstandard2.0 System.Runtime.CompilerServices.Unsafe (>= 5.0) - restriction: >= netstandard2.0 - Microsoft.NETCore.Platforms (6.0.5) - restriction: || (&& (< monoandroid) (< net45) (< netcoreapp3.1) (>= netstandard2.0) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (&& (< monoandroid) (< net45) (< netstandard1.2) (>= netstandard2.0) (< win8)) (&& (< monoandroid) (< net45) (< netstandard1.3) (>= netstandard2.0) (< win8) (< wpa81)) (&& (< monoandroid) (< net45) (< netstandard1.5) (>= netstandard2.0) (< win8) (< wpa81)) (&& (< monoandroid) (>= net6.0) (< netcoreapp2.1) (< netstandard2.1) (< xamarintvos) (< xamarinwatchos)) (&& (< monoandroid) (>= netcoreapp2.0) (< netcoreapp2.1) (< netstandard2.1) (< xamarintvos) (< xamarinwatchos)) (&& (< monoandroid) (< netcoreapp2.1) (>= netcoreapp3.1) (< netstandard2.1) (< xamarintvos) (< xamarinwatchos)) + Microsoft.NETCore.Platforms (6.0.5) - restriction: || (&& (< monoandroid) (< net45) (< netcoreapp3.1) (>= netstandard2.0) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (&& (< monoandroid) (< net45) (< netstandard1.2) (>= netstandard2.0) (< win8)) (&& (< monoandroid) (< net45) (< netstandard1.3) (>= netstandard2.0) (< win8) (< wpa81)) (&& (< monoandroid) (< net45) (< netstandard1.5) (>= netstandard2.0) (< win8) (< wpa81)) (&& (< monoandroid) (>= net5.0) (< netcoreapp2.1) (< netstandard2.1) (< xamarintvos) (< xamarinwatchos)) (&& (< monoandroid) (>= netcoreapp2.0) (< netcoreapp2.1) (< netstandard2.1) (< xamarintvos) (< xamarinwatchos)) Microsoft.NETCore.Targets (5.0) - restriction: || (&& (< monoandroid) (< net45) (< netcoreapp3.1) (>= netstandard2.0) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (&& (< monoandroid) (< net45) (< netstandard1.2) (>= netstandard2.0) (< win8)) (&& (< monoandroid) (< net45) (< netstandard1.3) (>= netstandard2.0) (< win8) (< wpa81)) (&& (< monoandroid) (< net45) (< netstandard1.5) (>= netstandard2.0) (< win8) (< wpa81)) Microsoft.Win32.Registry (5.0) - restriction: || (&& (< net45) (>= netstandard2.0)) (&& (< net472) (>= netstandard2.0)) (>= net6.0) System.Buffers (>= 4.5.1) - restriction: || (&& (>= monoandroid) (< netstandard1.3)) (>= monotouch) (&& (< net46) (< netcoreapp2.0) (>= netstandard2.0)) (>= xamarinios) (>= xamarinmac) (>= xamarintvos) (>= xamarinwatchos) @@ -954,10 +940,8 @@ NUGET System.Security.Principal.Windows (>= 5.0) - restriction: || (&& (>= monoandroid) (< netstandard1.3)) (&& (< monoandroid) (>= netcoreapp2.0)) (>= monotouch) (&& (< net46) (< netcoreapp2.0) (>= netstandard2.0)) (>= net461) (>= netcoreapp2.1) (>= uap10.1) (>= xamarinios) (>= xamarinmac) (>= xamarintvos) (>= xamarinwatchos) Microsoft.Win32.SystemEvents (6.0.1) - restriction: || (&& (>= net472) (>= netcoreapp3.1)) (>= net6.0) Mono.Posix.NETStandard (1.0) - restriction: >= netstandard2.0 - MSBuild.StructuredLogger (2.1.669) - Microsoft.Build (>= 16.10) - restriction: >= netstandard2.0 + MSBuild.StructuredLogger (2.1.768) Microsoft.Build.Framework (>= 16.10) - restriction: >= netstandard2.0 - Microsoft.Build.Tasks.Core (>= 16.10) - restriction: >= netstandard2.0 Microsoft.Build.Utilities.Core (>= 16.10) - restriction: >= netstandard2.0 Newtonsoft.Json (13.0.1) - restriction: >= netstandard2.0 NuGet.Common (6.3) - restriction: >= netstandard2.0 @@ -976,8 +960,7 @@ NUGET NuGet.Packaging (>= 6.3) - restriction: >= netstandard2.0 NuGet.Versioning (6.3) - restriction: >= netstandard2.0 Octokit (0.48) - System.Buffers (4.5.1) - restriction: || (&& (>= monoandroid) (>= net6.0) (< netstandard1.3)) (&& (>= monotouch) (>= net6.0)) (&& (>= net461) (>= netstandard2.1)) (&& (< net461) (< net6.0) (>= netstandard2.0)) (&& (< net461) (>= netstandard2.0) (< netstandard2.1)) (>= net472) (&& (>= net6.0) (< netcoreapp2.0)) (&& (>= net6.0) (< netstandard2.1)) (&& (>= net6.0) (>= xamarinios)) (&& (>= net6.0) (>= xamarinmac)) (&& (>= net6.0) (>= xamarintvos)) (&& (>= net6.0) (>= xamarinwatchos)) (&& (< net6.0) (>= netstandard2.1)) - System.CodeDom (6.0) - restriction: || (&& (< net472) (>= netstandard2.0)) (>= net6.0) + System.Buffers (4.5.1) - restriction: || (&& (>= monoandroid) (>= net6.0) (< netstandard1.3)) (&& (>= monotouch) (>= net6.0)) (&& (>= net461) (>= netstandard2.1)) (&& (< net461) (< net6.0) (>= netstandard2.0)) (&& (< net461) (>= netstandard2.0) (< netstandard2.1)) (>= net472) (&& (>= net5.0) (< netstandard2.1)) (&& (>= net6.0) (< netcoreapp2.0)) (&& (>= net6.0) (>= xamarinios)) (&& (>= net6.0) (>= xamarinmac)) (&& (>= net6.0) (>= xamarintvos)) (&& (>= net6.0) (>= xamarinwatchos)) (&& (< net6.0) (>= netstandard2.1)) System.Collections.Immutable (6.0) - restriction: >= netstandard2.0 System.Memory (>= 4.5.4) - restriction: || (>= net461) (&& (< net6.0) (>= netstandard2.0)) System.Runtime.CompilerServices.Unsafe (>= 6.0) - restriction: || (>= net461) (>= netstandard2.0) @@ -986,10 +969,11 @@ NUGET System.Security.Permissions (>= 6.0) - restriction: || (>= net461) (>= netstandard2.0) System.Drawing.Common (6.0) - restriction: || (&& (>= net472) (>= netcoreapp3.1)) (>= net6.0) Microsoft.Win32.SystemEvents (>= 6.0) - restriction: >= netcoreapp3.1 - System.Formats.Asn1 (6.0) - restriction: || (&& (< net461) (>= netstandard2.0)) (&& (>= net6.0) (< xamarintvos) (< xamarinwatchos)) (&& (< net6.0) (>= netcoreapp3.1) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (&& (>= netcoreapp3.0) (< xamarintvos) (< xamarinwatchos)) (>= netstandard2.1) + System.Formats.Asn1 (6.0) - restriction: || (&& (< net461) (>= netstandard2.0)) (&& (>= net5.0) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (&& (>= netcoreapp3.0) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (>= netstandard2.1) System.Buffers (>= 4.5.1) - restriction: || (>= net461) (&& (< net6.0) (>= netstandard2.0)) System.Memory (>= 4.5.4) - restriction: || (>= net461) (&& (< net6.0) (>= netstandard2.0)) - System.Memory (4.5.5) - restriction: || (&& (>= net6.0) (< netstandard2.1)) (>= netstandard2.0) + System.IO (4.3) - restriction: || (&& (< monoandroid) (< net46) (< netstandard1.4) (>= netstandard2.0)) (&& (< monoandroid) (< net46) (< netstandard1.6) (>= netstandard2.0)) (&& (< monoandroid) (>= net5.0) (< netstandard1.4)) (&& (< monoandroid) (>= net5.0) (< netstandard1.6)) (&& (< monoandroid) (>= net5.0) (< netstandard2.0) (< xamarintvos) (< xamarinwatchos)) (&& (>= net46) (>= net5.0) (< netstandard1.4)) (&& (< net46) (>= net461) (< netstandard1.4) (>= netstandard2.0)) (&& (< net46) (>= net461) (< netstandard1.6) (>= netstandard2.0)) (&& (< net46) (>= net462) (< netstandard1.4) (>= netstandard2.0)) (&& (< net46) (>= net462) (< netstandard1.6) (>= netstandard2.0)) (&& (< net46) (>= net47) (>= netstandard2.0)) (&& (>= net461) (>= net5.0) (< netstandard1.4)) (&& (>= net461) (>= net5.0) (< netstandard1.6)) (&& (>= net462) (>= net5.0) (< netstandard1.4)) (&& (>= net462) (>= net5.0) (< netstandard1.6)) (&& (>= net463) (>= net5.0) (< netstandard1.4)) (&& (>= net463) (>= net5.0) (< netstandard1.6)) (&& (>= net463) (>= net5.0) (< netstandard2.0)) (&& (>= net463) (< netstandard1.4) (>= netstandard2.0)) (&& (>= net463) (< netstandard1.6) (>= netstandard2.0)) (&& (>= net47) (< net472) (>= netstandard2.0)) (&& (>= net47) (>= net5.0)) + System.Memory (4.5.5) - restriction: || (&& (>= net461) (>= netstandard2.1)) (&& (>= net5.0) (< netstandard2.1)) (&& (< net6.0) (>= netstandard2.1)) (>= netstandard2.0) System.Buffers (>= 4.5.1) - restriction: || (&& (>= monoandroid) (< netstandard1.1)) (&& (< monoandroid) (< net45) (>= netstandard1.1) (< netstandard2.0) (< win8) (< wpa81)) (&& (< monoandroid) (< netstandard1.1) (>= portable-net45+win8+wpa81) (< win8)) (>= monotouch) (&& (>= net45) (< netstandard2.0)) (&& (< net45) (< netcoreapp2.0) (>= netstandard2.0)) (>= net461) (&& (< netstandard1.1) (>= win8)) (&& (< netstandard2.0) (< uap10.1) (>= wpa81)) (>= xamarinios) (>= xamarinmac) (>= xamarintvos) (>= xamarinwatchos) System.Numerics.Vectors (>= 4.5) - restriction: >= net461 System.Runtime.CompilerServices.Unsafe (>= 4.5.3) - restriction: || (&& (>= monoandroid) (< netstandard1.1)) (&& (< monoandroid) (< net45) (>= netstandard1.1) (< netstandard2.0) (< win8) (< wpa81)) (&& (< monoandroid) (>= netcoreapp2.0) (< netcoreapp2.1)) (&& (< monoandroid) (< netstandard1.1) (>= portable-net45+win8+wpa81) (< win8)) (>= monotouch) (&& (>= net45) (< netstandard2.0)) (&& (< net45) (< netcoreapp2.0) (>= netstandard2.0)) (>= net461) (&& (< netstandard1.1) (>= win8)) (&& (< netstandard2.0) (>= wpa81)) (>= uap10.1) (>= xamarinios) (>= xamarinmac) (>= xamarintvos) (>= xamarinwatchos) @@ -997,30 +981,32 @@ NUGET System.Reactive (5.0) - restriction: >= netstandard2.0 System.Runtime.InteropServices.WindowsRuntime (>= 4.3) - restriction: && (< net472) (< netcoreapp3.1) (>= netstandard2.0) System.Threading.Tasks.Extensions (>= 4.5.4) - restriction: || (>= net472) (&& (< netcoreapp3.1) (>= netstandard2.0)) (>= uap10.1) - System.Reflection.Metadata (6.0.1) - restriction: || (&& (< net472) (>= netstandard2.0)) (>= net6.0) + System.Reflection.Metadata (6.0.1) - restriction: >= net6.0 System.Collections.Immutable (>= 6.0) - restriction: || (>= net461) (>= netstandard2.0) - System.Resources.Extensions (6.0) - restriction: >= netstandard2.0 - System.Memory (>= 4.5.4) - restriction: || (>= net461) (&& (< net6.0) (>= netstandard2.0)) - System.Runtime (4.3.1) - restriction: && (< monoandroid) (< net45) (< netcoreapp3.1) (>= netstandard2.0) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos) + System.Runtime (4.3.1) - restriction: || (&& (< monoandroid) (< net45) (< netcoreapp3.1) (>= netstandard2.0) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (&& (< monoandroid) (< net46) (< netstandard1.4) (>= netstandard2.0)) (&& (< monoandroid) (< net46) (< netstandard1.6) (>= netstandard2.0)) (&& (< monoandroid) (>= net5.0) (< netstandard1.4)) (&& (< monoandroid) (>= net5.0) (< netstandard1.6)) (&& (< monoandroid) (>= net5.0) (< netstandard2.0) (< xamarintvos) (< xamarinwatchos)) (&& (>= net46) (>= net5.0) (< netstandard1.4)) (&& (< net46) (>= net461) (< netstandard1.4) (>= netstandard2.0)) (&& (< net46) (>= net461) (< netstandard1.6) (>= netstandard2.0)) (&& (< net46) (>= net462) (< netstandard1.4) (>= netstandard2.0)) (&& (< net46) (>= net462) (< netstandard1.6) (>= netstandard2.0)) (&& (< net46) (>= net47) (>= netstandard2.0)) (&& (>= net461) (>= net5.0) (< netstandard1.4)) (&& (>= net461) (>= net5.0) (< netstandard1.6)) (&& (>= net462) (>= net5.0) (< netstandard1.4)) (&& (>= net462) (>= net5.0) (< netstandard1.6)) (&& (>= net463) (>= net5.0) (< netstandard1.4)) (&& (>= net463) (>= net5.0) (< netstandard1.6)) (&& (>= net463) (>= net5.0) (< netstandard2.0)) (&& (>= net463) (< netstandard1.4) (>= netstandard2.0)) (&& (>= net463) (< netstandard1.6) (>= netstandard2.0)) (&& (>= net47) (< net472) (>= netstandard2.0)) (&& (>= net47) (>= net5.0)) Microsoft.NETCore.Platforms (>= 1.1.1) - restriction: || (&& (< monoandroid) (< net45) (>= netstandard1.0) (< netstandard1.2) (< win8) (< wp8)) (&& (< monoandroid) (< net45) (>= netstandard1.2) (< netstandard1.3) (< win8) (< wpa81)) (&& (< monoandroid) (< net45) (>= netstandard1.3) (< netstandard1.5) (< win8) (< wpa81)) (&& (< monotouch) (< net45) (>= netstandard1.5) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) Microsoft.NETCore.Targets (>= 1.1.3) - restriction: || (&& (< monoandroid) (< net45) (>= netstandard1.0) (< netstandard1.2) (< win8) (< wp8)) (&& (< monoandroid) (< net45) (>= netstandard1.2) (< netstandard1.3) (< win8) (< wpa81)) (&& (< monoandroid) (< net45) (>= netstandard1.3) (< netstandard1.5) (< win8) (< wpa81)) (&& (< monotouch) (< net45) (>= netstandard1.5) (< win8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) System.Runtime.CompilerServices.Unsafe (6.0) - restriction: >= netstandard2.0 System.Runtime.InteropServices.WindowsRuntime (4.3) - restriction: && (< net472) (< netcoreapp3.1) (>= netstandard2.0) System.Runtime (>= 4.3) - restriction: && (< monoandroid) (< monotouch) (< net45) (>= netstandard1.0) (< win8) (< wp8) (< wpa81) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos) - System.Security.AccessControl (6.0) - restriction: >= netstandard2.0 - System.Security.Cryptography.Cng (5.0) - restriction: || (&& (< net461) (>= netstandard2.0) (< netstandard2.1)) (&& (< net472) (>= netstandard2.0)) (>= net5.0) (&& (>= net6.0) (< netcoreapp3.1)) (&& (>= net6.0) (< netstandard2.1)) (&& (< net6.0) (>= netcoreapp3.1)) (&& (< netcoreapp3.1) (>= netstandard2.1)) + System.Security.AccessControl (6.0) - restriction: || (>= net472) (>= net6.0) + System.Security.Cryptography.Algorithms (4.3.1) - restriction: || (&& (< monoandroid) (< net46) (< netstandard1.4) (>= netstandard2.0)) (&& (< monoandroid) (< net46) (< netstandard1.6) (>= netstandard2.0)) (&& (< monoandroid) (>= net5.0) (< netstandard1.4)) (&& (< monoandroid) (>= net5.0) (< netstandard1.6)) (&& (< monoandroid) (>= net5.0) (< netstandard2.0) (< xamarintvos) (< xamarinwatchos)) (&& (>= net46) (>= net5.0) (< netstandard1.4)) (&& (>= net46) (< netstandard1.4) (>= netstandard2.0)) (&& (>= net461) (< net462) (< netstandard1.6) (>= netstandard2.0)) (&& (>= net461) (>= net5.0) (< netstandard1.6)) (&& (>= net462) (>= net5.0) (< netstandard1.6)) (&& (>= net462) (< netstandard1.6) (>= netstandard2.0)) (&& (>= net47) (< net472) (>= netstandard2.0)) (&& (>= net47) (>= net5.0)) + System.IO (>= 4.3) - restriction: || (&& (< monoandroid) (< net46) (>= netstandard1.3) (< netstandard1.4)) (&& (< monoandroid) (< net46) (>= netstandard1.4) (< netstandard1.6)) (&& (< monotouch) (< net46) (>= netstandard1.6) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (>= net463) + System.Runtime (>= 4.3) - restriction: || (&& (< monoandroid) (< net46) (>= netstandard1.3) (< netstandard1.4)) (&& (< monoandroid) (< net46) (>= netstandard1.4) (< netstandard1.6)) (&& (< monotouch) (< net46) (>= netstandard1.6) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (>= net463) + System.Security.Cryptography.Encoding (>= 4.3) - restriction: || (&& (< monotouch) (< net46) (>= netstandard1.6) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (>= net463) + System.Security.Cryptography.Primitives (>= 4.3) - restriction: || (&& (< monoandroid) (< net46) (>= netstandard1.3) (< netstandard1.4)) (&& (< monoandroid) (< net46) (>= netstandard1.4) (< netstandard1.6)) (&& (< monotouch) (< net46) (>= netstandard1.6) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (&& (>= net46) (< netstandard1.4)) (&& (>= net461) (< netstandard1.6)) (>= net463) + System.Security.Cryptography.Cng (5.0) - restriction: || (&& (< net461) (>= netstandard2.0) (< netstandard2.1)) (&& (< net472) (>= netstandard2.0)) (>= net5.0) (&& (< net6.0) (>= netcoreapp3.1)) (&& (< netcoreapp3.1) (>= netstandard2.1)) Microsoft.NETCore.Platforms (>= 5.0) - restriction: && (< monoandroid) (>= netcoreapp2.0) (< netcoreapp2.1) (< netstandard2.1) (< xamarintvos) (< xamarinwatchos) System.Formats.Asn1 (>= 5.0) - restriction: && (>= netcoreapp3.0) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos) - System.Security.Cryptography.Pkcs (6.0.1) - restriction: || (&& (< net461) (>= netstandard2.0)) (&& (< net472) (>= netstandard2.0)) (>= net5.0) + System.Security.Cryptography.Algorithms (>= 4.3.1) - restriction: || (&& (< monoandroid) (< monotouch) (< net46) (>= netstandard1.6) (< netstandard2.0) (< xamarintvos) (< xamarinwatchos)) (&& (< monoandroid) (< net46) (>= netstandard1.3) (< netstandard1.4)) (&& (< monoandroid) (< net46) (>= netstandard1.4) (< netstandard1.6) (< uap10.1)) (&& (>= net46) (< netstandard1.4)) (&& (>= net461) (< net462) (< netstandard1.6)) (&& (>= net462) (< netstandard1.6)) (>= net47) + System.Security.Cryptography.Encoding (4.3) - restriction: || (&& (< monoandroid) (< net46) (< netstandard1.4) (>= netstandard2.0)) (&& (< monoandroid) (< net46) (< netstandard1.6) (>= netstandard2.0)) (&& (< monoandroid) (>= net5.0) (< netstandard1.4)) (&& (< monoandroid) (>= net5.0) (< netstandard1.6)) (&& (< monoandroid) (>= net5.0) (< netstandard2.0) (< xamarintvos) (< xamarinwatchos)) (&& (>= net46) (>= net5.0) (< netstandard1.4)) (&& (< net46) (>= net461) (< netstandard1.6) (>= netstandard2.0)) (&& (< net46) (>= net462) (< netstandard1.6) (>= netstandard2.0)) (&& (< net46) (>= net47) (>= netstandard2.0)) (&& (>= net461) (>= net5.0) (< netstandard1.6)) (&& (>= net462) (>= net5.0) (< netstandard1.6)) (&& (>= net463) (>= net5.0) (< netstandard1.4)) (&& (>= net463) (>= net5.0) (< netstandard1.6)) (&& (>= net463) (>= net5.0) (< netstandard2.0)) (&& (>= net463) (< netstandard1.4) (>= netstandard2.0)) (&& (>= net463) (< netstandard1.6) (>= netstandard2.0)) (&& (>= net47) (< net472) (>= netstandard2.0)) (&& (>= net47) (>= net5.0)) + System.Security.Cryptography.Pkcs (6.0.1) - restriction: || (&& (< net472) (>= netstandard2.0)) (>= net5.0) System.Buffers (>= 4.5.1) - restriction: && (< net461) (>= netstandard2.0) (< netstandard2.1) System.Formats.Asn1 (>= 6.0) - restriction: || (&& (< net461) (>= netstandard2.0)) (>= netstandard2.1) System.Memory (>= 4.5.4) - restriction: && (< net461) (>= netstandard2.0) (< netstandard2.1) System.Security.Cryptography.Cng (>= 5.0) - restriction: || (&& (< net461) (>= netstandard2.0) (< netstandard2.1)) (&& (< net6.0) (>= netcoreapp3.1)) (&& (< netcoreapp3.1) (>= netstandard2.1)) + System.Security.Cryptography.Primitives (4.3) - restriction: || (&& (< monoandroid) (< net46) (< netstandard1.4) (>= netstandard2.0)) (&& (< monoandroid) (< net46) (< netstandard1.6) (>= netstandard2.0)) (&& (< monoandroid) (>= net5.0) (< netstandard1.4)) (&& (< monoandroid) (>= net5.0) (< netstandard1.6)) (&& (< monoandroid) (>= net5.0) (< netstandard2.0) (< xamarintvos) (< xamarinwatchos)) (&& (>= net46) (>= net5.0) (< netstandard1.4)) (&& (>= net46) (< netstandard1.4) (>= netstandard2.0)) (&& (< net46) (>= net461) (< netstandard1.6) (>= netstandard2.0)) (&& (< net46) (>= net47) (>= netstandard2.0)) (&& (>= net461) (< net462) (< netstandard1.6) (>= netstandard2.0)) (&& (>= net461) (>= net5.0) (< netstandard1.4)) (&& (>= net461) (>= net5.0) (< netstandard1.6)) (&& (>= net461) (< netstandard1.4) (>= netstandard2.0)) (&& (>= net462) (>= net5.0) (< netstandard1.4)) (&& (>= net462) (>= net5.0) (< netstandard1.6)) (&& (>= net462) (< netstandard1.4) (>= netstandard2.0)) (&& (>= net462) (< netstandard1.6) (>= netstandard2.0)) (&& (>= net463) (>= net5.0) (< netstandard1.4)) (&& (>= net463) (>= net5.0) (< netstandard1.6)) (&& (>= net463) (>= net5.0) (< netstandard2.0)) (&& (>= net463) (< netstandard1.4) (>= netstandard2.0)) (&& (>= net463) (< netstandard1.6) (>= netstandard2.0)) (&& (>= net47) (< net472) (>= netstandard2.0)) (&& (>= net47) (>= net5.0)) (&& (>= net47) (< netstandard1.4) (>= netstandard2.0)) (&& (>= net47) (< netstandard1.6) (>= netstandard2.0)) System.Security.Cryptography.ProtectedData (6.0) - restriction: || (&& (< net461) (>= net472)) (&& (< net472) (>= netstandard2.0)) (>= net6.0) - System.Security.Cryptography.Xml (6.0.1) - restriction: || (&& (< net472) (>= netstandard2.0)) (>= net6.0) - System.Memory (>= 4.5.4) - restriction: && (< net461) (< net6.0) (>= netstandard2.0) - System.Security.AccessControl (>= 6.0) - restriction: || (>= net461) (>= netstandard2.0) - System.Security.Cryptography.Pkcs (>= 6.0.1) - restriction: || (&& (< net461) (>= netstandard2.0)) (>= net6.0) System.Security.Permissions (6.0) - restriction: >= netstandard2.0 System.Security.AccessControl (>= 6.0) - restriction: || (>= net461) (>= netstandard2.0) System.Windows.Extensions (>= 6.0) - restriction: >= netcoreapp3.1 @@ -1040,7 +1026,7 @@ NUGET System.Text.Encodings.Web (>= 6.0) - restriction: || (>= net461) (>= netstandard2.0) System.Threading.Tasks.Extensions (>= 4.5.4) - restriction: || (>= net461) (&& (< netcoreapp3.1) (>= netstandard2.0)) System.ValueTuple (>= 4.5) - restriction: >= net461 - System.Threading.Tasks.Dataflow (6.0) - restriction: >= netstandard2.0 + System.Threading.Tasks.Dataflow (6.0) - restriction: || (>= net472) (>= net6.0) System.Threading.Tasks.Extensions (4.5.4) - restriction: || (&& (>= net461) (>= net6.0)) (>= net472) (&& (>= net6.0) (< netcoreapp3.1)) (&& (< netcoreapp3.1) (>= netstandard2.0)) (&& (>= netstandard2.0) (>= uap10.1)) System.Runtime.CompilerServices.Unsafe (>= 4.5.3) - restriction: || (&& (< monoandroid) (< monotouch) (< net45) (>= netstandard1.0) (< netstandard2.0) (< win8) (< wpa81) (< xamarintvos) (< xamarinwatchos)) (&& (< monoandroid) (< netstandard1.0) (>= portable-net45+win8+wp8+wpa81) (< win8)) (&& (>= net45) (< netstandard2.0)) (&& (< net45) (< netcoreapp2.1) (>= netstandard2.0) (< xamarinios) (< xamarinmac) (< xamarintvos) (< xamarinwatchos)) (>= net461) (&& (< netstandard1.0) (>= win8)) (&& (< netstandard2.0) (>= wpa81)) (>= wp8) System.ValueTuple (4.5) - restriction: || (&& (>= net45) (>= netstandard2.0)) (&& (>= net461) (>= net6.0)) (>= net472) diff --git a/src/FsAutoComplete.Core/Commands.fs b/src/FsAutoComplete.Core/Commands.fs index fe230985e..2c4cfa25d 100644 --- a/src/FsAutoComplete.Core/Commands.fs +++ b/src/FsAutoComplete.Core/Commands.fs @@ -88,6 +88,7 @@ type NotificationEvent = | TestDetected of file: string * tests: TestAdapter.TestAdapterEntry[] module Commands = + open System.Collections.Concurrent let fantomasLogger = LogProvider.getLoggerByName "Fantomas" let commandsLogger = LogProvider.getLoggerByName "Commands" @@ -836,7 +837,7 @@ module Commands = return Choice1Of2(declarationRanges, usageRanges) | SymbolDeclarationLocation.Projects (projects, isInternalToProject) -> - let symbolUseRanges = ImmutableArray.CreateBuilder() + let symbolUseRanges = ConcurrentBag<_>() let symbolRange = symbol.DefinitionRange.NormalizeDriveLetterCasing() let symbolFile = symbolRange.TaggedFileName @@ -967,30 +968,6 @@ module Commands = highlights |> Array.collect expandParents) |> Array.sortBy startToken -type Commands(checker: FSharpCompilerServiceChecker, state: State, hasAnalyzers: bool, rootPath: string option) = - let fileParsed = Event() - - let fileChecked = Event * int>() - - let scriptFileProjectOptions = Event() - - let disposables = ResizeArray() - - let mutable workspaceRoot: string option = rootPath - let mutable linterConfigFileRelativePath: string option = None - // let mutable linterConfiguration: FSharpLint.Application.Lint.ConfigurationParam = FSharpLint.Application.Lint.ConfigurationParam.Default - let mutable lastCheckResult: ParseAndCheckResults option = None - - let notify = Event() - - let fileStateSet = Event() - let commandsLogger = LogProvider.getLoggerByName "Commands" - - let checkerLogger = LogProvider.getLoggerByName "CheckerEvents" - - let fantomasLogger = LogProvider.getLoggerByName "Fantomas" - - let analyzerHandler (file: string, content, pt, tast, symbols, getAllEnts) = @@ -1038,6 +1015,32 @@ type Commands(checker: FSharpCompilerServiceChecker, state: State, hasAnalyzers: [||] + +type Commands(checker: FSharpCompilerServiceChecker, state: State, hasAnalyzers: bool, rootPath: string option) = + let fileParsed = Event() + + let fileChecked = Event * int>() + + let scriptFileProjectOptions = Event() + + let disposables = ResizeArray() + + let mutable workspaceRoot: string option = rootPath + let mutable linterConfigFileRelativePath: string option = None + // let mutable linterConfiguration: FSharpLint.Application.Lint.ConfigurationParam = FSharpLint.Application.Lint.ConfigurationParam.Default + let mutable lastCheckResult: ParseAndCheckResults option = None + + let notify = Event() + + let fileStateSet = Event() + let commandsLogger = LogProvider.getLoggerByName "Commands" + + let checkerLogger = LogProvider.getLoggerByName "CheckerEvents" + + let fantomasLogger = LogProvider.getLoggerByName "Fantomas" + + + do disposables.Add <| state.ProjectController.Notifications.Subscribe(NotificationEvent.Workspace >> notify.Trigger) @@ -1105,7 +1108,7 @@ type Commands(checker: FSharpCompilerServiceChecker, state: State, hasAnalyzers: | true, fileData -> let res = - analyzerHandler ( + Commands.analyzerHandler ( file, fileData.Lines.ToString().Split("\n"), parseAndCheck.GetParseResults.ParseTree, diff --git a/src/FsAutoComplete.Core/Consts.fs b/src/FsAutoComplete.Core/Consts.fs new file mode 100644 index 000000000..423dfffd7 --- /dev/null +++ b/src/FsAutoComplete.Core/Consts.fs @@ -0,0 +1,10 @@ +namespace FsAutoComplete.Core + +module ProjectLoader = + [] + let ProduceReferenceAssembly = "ProduceReferenceAssembly" + + let globalProperties = + [ + // For tooling we don't want to use Reference Assemblies as this doesn't play well with type checking across projects + ProduceReferenceAssembly, "false" ] diff --git a/src/FsAutoComplete.Core/FsAutoComplete.Core.fsproj b/src/FsAutoComplete.Core/FsAutoComplete.Core.fsproj index 81ddede39..366d7b890 100644 --- a/src/FsAutoComplete.Core/FsAutoComplete.Core.fsproj +++ b/src/FsAutoComplete.Core/FsAutoComplete.Core.fsproj @@ -9,6 +9,7 @@ + @@ -45,4 +46,4 @@ - + \ No newline at end of file diff --git a/src/FsAutoComplete/LspServers/AdaptiveFSharpLspServer.fs b/src/FsAutoComplete/LspServers/AdaptiveFSharpLspServer.fs index 17a3e3c55..f2f988828 100644 --- a/src/FsAutoComplete/LspServers/AdaptiveFSharpLspServer.fs +++ b/src/FsAutoComplete/LspServers/AdaptiveFSharpLspServer.fs @@ -181,12 +181,19 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let disposables = new System.Reactive.Disposables.CompositeDisposable() + let rootPath = cval None + let config = cval FSharpConfig.Default - let checker = - config - |> AVal.map (fun c -> c.EnableAnalyzers) // Maps will cache values and we don't want to recreate FSharpCompilerServiceChecker unless only EnableAnalyzers changed - |> AVal.map (FSharpCompilerServiceChecker) + + let analyzersEnabled = config |> AVal.map (fun c -> c.EnableAnalyzers) + + let checker = analyzersEnabled |> AVal.map (FSharpCompilerServiceChecker) + + /// The reality is a file can be in multiple projects + /// This is extracted to make it easier to do some type of customized select + /// in the future + let selectProject projs = projs |> List.tryHead let mutableConfigChanges = let toCompilerToolArgument (path: string) = sprintf "--compilertool:%s" path @@ -194,11 +201,72 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar aval { let! config = config and! checker = checker + and! rootPath = rootPath checker.SetFSIAdditionalArguments [| yield! config.FSICompilerToolLocations |> Array.map toCompilerToolArgument yield! config.FSIExtraParameters |] + if config.EnableAnalyzers then + Loggers.analyzers.info ( + Log.setMessage "Using analyzer roots of {roots}" + >> Log.addContextDestructured "roots" config.AnalyzersPath + ) + + config.AnalyzersPath + |> Array.iter (fun analyzerPath -> + match rootPath with + | None -> () + | Some workspacePath -> + let dir = + if + System.IO.Path.IsPathRooted analyzerPath + // if analyzer is using absolute path, use it as is + then + analyzerPath + // otherwise, it is a relative path and should be combined with the workspace path + else + System.IO.Path.Combine(workspacePath, analyzerPath) + + Loggers.analyzers.info ( + Log.setMessage "Loading analyzers from {dir}" + >> Log.addContextDestructured "dir" dir + ) + + let (n, m) = dir |> FSharp.Analyzers.SDK.Client.loadAnalyzers + + Loggers.analyzers.info ( + Log.setMessage "From {name}: {dllNo} dlls including {analyzersNo} analyzers" + >> Log.addContextDestructured "name" analyzerPath + >> Log.addContextDestructured "dllNo" n + >> Log.addContextDestructured "analyzersNo" m + )) + + else + Loggers.analyzers.info (Log.setMessage "Analyzers disabled") + + let di = DirectoryInfo config.DotNetRoot + + if di.Exists then + let dotnetBinary = + if + System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(Runtime.InteropServices.OSPlatform.Windows) + then + FileInfo(Path.Combine(di.FullName, "dotnet.exe")) + else + FileInfo(Path.Combine(di.FullName, "dotnet")) + + if dotnetBinary.Exists then + checker.SetDotnetRoot(dotnetBinary, defaultArg rootPath System.Environment.CurrentDirectory |> DirectoryInfo) + + else + // if we were mistakenly given the path to a dotnet binary + // then use the parent directory as the dotnet root instead + let fi = FileInfo(di.FullName) + + if fi.Exists && (fi.Name = "dotnet" || fi.Name = "dotnet.exe") then + checker.SetDotnetRoot(fi, defaultArg rootPath System.Environment.CurrentDirectory |> DirectoryInfo) + return () } @@ -233,6 +301,97 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let scriptFileProjectOptions = Event() + let fileParsed = + Event() + + let fileChecked = Event() + + + do + disposables.Add + <| fileParsed.Publish.Subscribe(fun (parseResults, proj, ct) -> + try + logger.info ( + Log.setMessage "Test Detection of {file} started" + >> Log.addContextDestructured "file" parseResults.FileName + ) + + let fn = UMX.tag parseResults.FileName + + let res = + if proj.OtherOptions |> Seq.exists (fun o -> o.Contains "Expecto.dll") then + TestAdapter.getExpectoTests parseResults.ParseTree + elif proj.OtherOptions |> Seq.exists (fun o -> o.Contains "nunit.framework.dll") then + TestAdapter.getNUnitTest parseResults.ParseTree + elif proj.OtherOptions |> Seq.exists (fun o -> o.Contains "xunit.assert.dll") then + TestAdapter.getXUnitTest parseResults.ParseTree + else + [] + + logger.info ( + Log.setMessage "Test Detection of {file} - {res}" + >> Log.addContextDestructured "file" parseResults.FileName + >> Log.addContextDestructured "res" res + ) + + notifications.Trigger(NotificationEvent.TestDetected(fn, res |> List.toArray), ct) + with e -> + logger.info ( + Log.setMessage "Test Detection of {file} failed - {res}" + >> Log.addContextDestructured "file" parseResults.FileName + >> Log.addException e + )) + + do + disposables.Add + <| fileChecked.Publish.Subscribe(fun (parseAndCheck, volatileFile, ct) -> + async { + if analyzersEnabled |> AVal.force then + let file = volatileFile.FileName + + try + Loggers.analyzers.info ( + Log.setMessage "begin analysis of {file}" + >> Log.addContextDestructured "file" file + ) + + match parseAndCheck.GetCheckResults.ImplementationFile with + | Some tast -> + + let res = + Commands.analyzerHandler ( + file, + volatileFile.Lines.ToString().Split("\n"), + parseAndCheck.GetParseResults.ParseTree, + tast, + parseAndCheck.GetCheckResults.PartialAssemblySignature.Entities |> Seq.toList, + parseAndCheck.GetAllEntities + ) + + notifications.Trigger(NotificationEvent.AnalyzerMessage(res, file), ct) + + Loggers.analyzers.info ( + Log.setMessage "end analysis of {file}" + >> Log.addContextDestructured "file" file + ) + + | _ -> + Loggers.analyzers.info ( + Log.setMessage "missing components of {file} to run analyzers, skipped them" + >> Log.addContextDestructured "file" file + ) + + () + with ex -> + Loggers.analyzers.error ( + Log.setMessage "Run failed for {file}" + >> Log.addContextDestructured "file" file + >> Log.addExn ex + ) + } + |> Async.StartWithCT ct) + + let handleCommandEvents (n: NotificationEvent, ct: CancellationToken) = try async { @@ -439,25 +598,27 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar .Subscribe(fun e -> handleCommandEvents e) ) + let getLastUTCChangeForFile (filePath: string) = + AdaptiveFile.GetLastWriteTimeUtc(UMX.untag filePath) + |> AVal.map (fun writeTime -> filePath, writeTime) - let adaptiveFile (filePath: string) = - let file = - AdaptiveFile.GetLastWriteTimeUtc(UMX.untag filePath) - |> AVal.map (fun writeTime -> filePath, writeTime) + let addAValLogging cb (aval: aval<_>) = + let cb = aval.AddMarkingCallback(cb) + aval |> AVal.mapDisposableTuple (fun x -> x, cb) - let cb = - file.AddMarkingCallback(fun () -> - logger.info ( - Log.setMessage "Loading projects because of {delta}" - >> Log.addContextDestructured "delta" filePath - )) + let projectFileChanges (filePath: string) = + let file = getLastUTCChangeForFile filePath + let logMsg () = + logger.info ( + Log.setMessage "Loading projects because of {delta}" + >> Log.addContextDestructured "delta" filePath + ) - file |> AVal.mapDisposableTuple (fun x -> x, cb) + file |> addAValLogging logMsg let loader = cval workspaceLoader - let rootPath = cval None let binlogConfig = @@ -481,7 +642,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar workspacePaths |> AVal.map (fun wsp -> match wsp with - | WorkspaceChosen.Sln v -> adaptiveFile v |> AdaptiveWorkspaceChosen.Sln + | WorkspaceChosen.Sln v -> projectFileChanges v |> AdaptiveWorkspaceChosen.Sln | WorkspaceChosen.Directory d -> failwith "Need to use AdaptiveDirectory" |> AdaptiveWorkspaceChosen.Directory | WorkspaceChosen.Projs projs -> let projChanges = @@ -535,6 +696,12 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar notifications.Trigger(not, CancellationToken.None)) + + use progressReport = new ServerProgressReport(lspClient) + + progressReport.Begin($"Loading {projects.Count} Projects") + |> Async.RunSynchronously + let projectOptions = loader.LoadProjects(projects |> Seq.map (fst >> UMX.untag) |> Seq.toList, [], binlogConfig) |> Seq.toList @@ -549,7 +716,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar [ for p in projectOptions do match p.Properties |> Seq.tryFind (fun x -> x.Name = "ProjectAssetsFile") with - | Some v -> yield adaptiveFile (UMX.tag v.Value) + | Some v -> yield projectFileChanges (UMX.tag v.Value) | None -> () let objPath = @@ -560,14 +727,14 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let isWithinObjFolder (file: string) = match objPath with | None -> true // if no obj folder provided assume we should track this file - | Some v -> file.Contains(v) + | Some objPath -> file.Contains(objPath) match p.Properties |> Seq.tryFind (fun x -> x.Name = "MSBuildAllProjects") with | Some v -> yield! v.Value.Split(';', StringSplitOptions.RemoveEmptyEntries) |> Array.filter (fun x -> x.EndsWith(".props") && isWithinObjFolder x) - |> Array.map (UMX.tag >> adaptiveFile) + |> Array.map (UMX.tag >> projectFileChanges) | None -> () ] projectOptions, additionalDependencies) @@ -583,6 +750,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar // Set some default values as FCS uses these for identification/caching purposes let fso = { fso with + SourceFiles = fso.SourceFiles |> Array.map (Utils.normalizePath >> UMX.untag) Stamp = fso.Stamp |> Option.orElse (Some DateTime.UtcNow.Ticks) ProjectId = fso.ProjectId |> Option.orElse (Some(Guid.NewGuid().ToString())) } @@ -626,6 +794,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar return options |> List.map fst } + let forceLoadProjects () = loadedProjectOptions |> AVal.force let sourceFileToProjectOptions = @@ -869,10 +1038,19 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar return file, projs }) + let allProjectOptions = + let wins = + openFilesToChangesAndProjectOptions |> AMap.map (fun k v -> v |> AVal.map snd) + + let loses = sourceFileToProjectOptions |> AMap.map (fun k v -> AVal.constant v) + AMap.union loses wins + let getProjectOptionsForFile (filePath: string) = - openFilesToChangesAndProjectOptions - |> AMap.tryFindA filePath - |> AVal.map (Option.map snd >> Option.defaultValue []) + aval { + match! allProjectOptions |> AMap.tryFindA filePath with + | Some projs -> return projs + | None -> return [] + } let autoCompleteItems: cmap * (Position -> option) * FSharp.Compiler.Syntax.ParsedInput> = cmap () @@ -950,7 +1128,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar } - let semaphore = new SemaphoreSlim(1, 1) + static let semaphore = new SemaphoreSlim(1, 1) let parseAndCheckFile (checker: FSharpCompilerServiceChecker) (file: VolatileFile) opts config = async { @@ -1004,6 +1182,9 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar Async.Start( async { + + fileParsed.Trigger(parseAndCheck.GetParseResults, opts, ct) + fileChecked.Trigger(parseAndCheck, file, ct) let checkErrors = parseAndCheck.GetParseResults.Diagnostics let parseErrors = parseAndCheck.GetCheckResults.Diagnostics @@ -1056,16 +1237,17 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar return option { - let! opts = List.tryHead projectOptions + let! opts = selectProject projectOptions and! cts = tryGetOpenFileToken file - return! - Debug.measure "parseFile" - <| fun () -> - let opts = Utils.projectOptionsToParseOptions opts + let parseOpts = Utils.projectOptionsToParseOptions opts - checker.ParseFile(file, info.Lines, opts) - |> Async.RunSynchronouslyWithCTSafe(fun () -> cts.Token) + let! result = + checker.ParseFile(file, info.Lines, parseOpts) + |> Async.RunSynchronouslyWithCTSafe(fun () -> cts.Token) + + fileParsed.Trigger(result, opts, cts.Token) + return result } }) @@ -1080,7 +1262,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar return option { - let! opts = List.tryHead projectOptions + let! opts = selectProject projectOptions return! checker.TryGetRecentCheckResultsForFile(file, opts, info.Lines) } }) @@ -1095,7 +1277,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar return option { - let! opts = List.tryHead projectOptions + let! opts = selectProject projectOptions and! cts = tryGetOpenFileToken file return! @@ -1172,7 +1354,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let forceGetProjectOptions filePath = getProjectOptionsForFile filePath |> AVal.force - |> Seq.tryHead + |> selectProject |> Result.ofOption (fun () -> $"Could not find project containing {filePath}") let codeGenServer = @@ -1363,7 +1545,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar else let! projectOptions = getProjectOptionsForFile file - match projectOptions |> Seq.tryHead with + match projectOptions |> selectProject with | None -> return true | Some projectOptions -> if doesNotExist (UMX.tag projectOptions.ProjectFileName) then @@ -1419,7 +1601,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar |> Array.distinct let getDependentProjectsOfProjects ps = - let projectSnapshot = loadedProjectOptions |> AVal.force + let projectSnapshot = forceLoadProjects () let allDependents = System.Collections.Generic.HashSet() @@ -1522,7 +1704,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar checker.FindReferencesForSymbolInFile(file, project, symbol) let getProjectOptions file = - getProjectOptionsForFile file |> AVal.force |> List.tryHead + getProjectOptionsForFile file |> AVal.force |> selectProject let projectsThatContainFile file = getProjectOptionsForFile file |> AVal.force @@ -1801,7 +1983,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar try logger.info (Log.setMessage "Initialized request {p}" >> Log.addContextDestructured "p" p) // Starts getting project options to cache - let _ = loadedProjectOptions |> AVal.force + forceLoadProjects () |> ignore return () with e -> @@ -1921,6 +2103,10 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar do! bypassAdaptiveAndCheckDepenenciesForFile filePath do! lspClient.CodeLensRefresh() + logger.info ( + Log.setMessage "TextDocumentDidSave Request Finished: {parms}" + >> Log.addContextDestructured "parms" p + ) return () with e -> @@ -2223,7 +2409,12 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar and! tyRes = forceGetTypeCheckResultsStale filePath |> Result.ofStringErr match tyRes.TryGetToolTipEnhanced pos lineStr with - | Ok (Some (tip, signature, footer, typeDoc)) -> + | Ok (Some (tip, signature, footer, typeDoc) as x) -> + logger.info ( + Log.setMessage "TryGetToolTipEnhanced : {parms}" + >> Log.addContextDestructured "parms" x + ) + let formatCommentStyle = let config = AVal.force config @@ -2489,12 +2680,12 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar checker.GetUsesOfSymbol(filePath, opts, symbol) let getAllProjects () = - openFilesToChangesAndProjectOptions + allProjectOptions |> AMap.force |> Seq.toList |> List.choose (fun (path, opt) -> option { - let! opt = AVal.force opt |> snd |> List.tryHead + let! opt = AVal.force opt |> selectProject return UMX.untag path, opt }) @@ -3566,7 +3757,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar |> HashSet.ofArray transact (fun () -> workspacePaths.Value <- (WorkspaceChosen.Projs projs)) - let options = loadedProjectOptions |> AVal.force + forceLoadProjects () |> ignore return { Content = CommandResponse.workspaceLoad FsAutoComplete.JsonSerializer.writeJson true } @@ -3639,7 +3830,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar |> HashSet.single |> WorkspaceChosen.Projs) - loadedProjectOptions |> AVal.force |> ignore + forceLoadProjects () |> ignore return! Helpers.notImplemented with e -> @@ -3713,7 +3904,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar ) do! Commands.DotnetAddProject p.Target p.Reference |> AsyncResult.ofCoreResponse - loadedProjectOptions |> AVal.force |> ignore + forceLoadProjects () |> ignore return { Content = "" } with e -> logger.error ( @@ -3734,7 +3925,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar ) do! Commands.DotnetRemoveProject p.Target p.Reference |> AsyncResult.ofCoreResponse - loadedProjectOptions |> AVal.force |> ignore + forceLoadProjects () |> ignore return { Content = "" } with e -> logger.error ( @@ -3755,7 +3946,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar ) do! Commands.DotnetSlnAdd p.Target p.Reference |> AsyncResult.ofCoreResponse - loadedProjectOptions |> AVal.force |> ignore + forceLoadProjects () |> ignore return { Content = "" } with e -> logger.error ( @@ -3900,7 +4091,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar Commands.FsProjMoveFileUp p.FsProj p.FileVirtualPath |> AsyncResult.ofCoreResponse - loadedProjectOptions |> AVal.force |> ignore + forceLoadProjects () |> ignore return { Content = "" } with e -> logger.error ( @@ -3925,7 +4116,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar Commands.FsProjMoveFileDown p.FsProj p.FileVirtualPath |> AsyncResult.ofCoreResponse - loadedProjectOptions |> AVal.force |> ignore + forceLoadProjects () |> ignore return { Content = "" } with e -> logger.error ( @@ -3950,7 +4141,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar Commands.addFileAbove p.FsProj p.FileVirtualPath p.NewFile |> AsyncResult.ofCoreResponse - loadedProjectOptions |> AVal.force |> ignore + forceLoadProjects () |> ignore return { Content = "" } with e -> logger.error ( @@ -3974,7 +4165,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar Commands.addFileBelow p.FsProj p.FileVirtualPath p.NewFile |> AsyncResult.ofCoreResponse - loadedProjectOptions |> AVal.force |> ignore + forceLoadProjects () |> ignore return { Content = "" } with e -> logger.error ( @@ -3996,7 +4187,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar ) do! Commands.addFile p.FsProj p.FileVirtualPath |> AsyncResult.ofCoreResponse - loadedProjectOptions |> AVal.force |> ignore + forceLoadProjects () |> ignore return { Content = "" } with e -> logger.error ( @@ -4018,7 +4209,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar let fullPath = Path.Combine(Path.GetDirectoryName p.FsProj, p.FileVirtualPath) do! Commands.removeFile p.FsProj p.FileVirtualPath |> AsyncResult.ofCoreResponse - loadedProjectOptions |> AVal.force |> ignore + forceLoadProjects () |> ignore let fileUri = Path.FilePathToUri fullPath diagnosticCollections.ClearFor fileUri @@ -4045,7 +4236,7 @@ type AdaptiveFSharpLspServer(workspaceLoader: IWorkspaceLoader, lspClient: FShar Commands.addExistingFile p.FsProj p.FileVirtualPath |> AsyncResult.ofCoreResponse - loadedProjectOptions |> AVal.force |> ignore + forceLoadProjects () |> ignore return { Content = "" } with e -> logger.error ( diff --git a/src/FsAutoComplete/Parser.fs b/src/FsAutoComplete/Parser.fs index 329a5ac45..77851a34d 100644 --- a/src/FsAutoComplete/Parser.fs +++ b/src/FsAutoComplete/Parser.fs @@ -14,6 +14,7 @@ open System.Threading.Tasks open FsAutoComplete.Lsp module Parser = + open FsAutoComplete.Core [] type Pos = { Line: int; Column: int } @@ -110,13 +111,15 @@ module Parser = rootCommand.AddOption stateLocationOption + rootCommand.SetHandler( Func<_, _, _, Task>(fun projectGraphEnabled stateDirectory adaptiveLspEnabled -> let workspaceLoaderFactory = - if projectGraphEnabled then - Ionide.ProjInfo.WorkspaceLoaderViaProjectGraph.Create - else - Ionide.ProjInfo.WorkspaceLoader.Create + fun toolsPath -> + if projectGraphEnabled then + Ionide.ProjInfo.WorkspaceLoaderViaProjectGraph.Create(toolsPath, ProjectLoader.globalProperties) + else + Ionide.ProjInfo.WorkspaceLoader.Create(toolsPath, ProjectLoader.globalProperties) let dotnetPath = if diff --git a/test/FsAutoComplete.Tests.Lsp/CodeLensTests.fs b/test/FsAutoComplete.Tests.Lsp/CodeLensTests.fs index e6176fb51..f5977e55b 100644 --- a/test/FsAutoComplete.Tests.Lsp/CodeLensTests.fs +++ b/test/FsAutoComplete.Tests.Lsp/CodeLensTests.fs @@ -11,6 +11,7 @@ open Utils.Utils open Utils.CursorbasedTests open Utils.Tests.TextEdit open Newtonsoft.Json.Linq +open Helpers.Expecto.ShadowedTimeouts module private CodeLens = let assertNoDiagnostics (ds: Diagnostic []) = diff --git a/test/FsAutoComplete.Tests.Lsp/CompletionTests.fs b/test/FsAutoComplete.Tests.Lsp/CompletionTests.fs index 929b66a0a..18be51439 100644 --- a/test/FsAutoComplete.Tests.Lsp/CompletionTests.fs +++ b/test/FsAutoComplete.Tests.Lsp/CompletionTests.fs @@ -8,6 +8,7 @@ open FsAutoComplete.Utils open FsAutoComplete.Lsp open FsToolkit.ErrorHandling open Utils.Server +open Helpers.Expecto.ShadowedTimeouts let tests state = let server = @@ -27,7 +28,8 @@ let tests state = } |> Async.Cache - testList + testSequenced + <| testList "Completion Tests" [ testCaseAsync "simple module member completion on dot" diff --git a/test/FsAutoComplete.Tests.Lsp/CoreTests.fs b/test/FsAutoComplete.Tests.Lsp/CoreTests.fs index f345ed320..3a643ed26 100644 --- a/test/FsAutoComplete.Tests.Lsp/CoreTests.fs +++ b/test/FsAutoComplete.Tests.Lsp/CoreTests.fs @@ -22,6 +22,7 @@ open Utils.Utils open FsToolkit.ErrorHandling.Operator.AsyncResult open FSharpx.Control open Utils.Tests +open Helpers.Expecto.ShadowedTimeouts ///Test for initialization of the server let initTests createServer = @@ -223,11 +224,11 @@ let tooltipTests state = let concatLines = String.concat Environment.NewLine - let verifyDescription line character expectedDescription = + let verifyDescriptionImpl testCaseAsync line character expectedDescription = let expectedDescription = concatLines expectedDescription testCaseAsync - (sprintf "description for line %d character %d should be '%s" line character expectedDescription) + (sprintf "description for line %d character %d" line character) (async { let! server, scriptPath = server @@ -241,9 +242,12 @@ let tooltipTests state = | Ok response -> failtestf "Should have gotten description but got %A" response | Result.Error errors -> failtestf "Error while getting description: %A" errors }) + let verifyDescription line character expectedDescription = verifyDescriptionImpl testCaseAsync line character expectedDescription + let pverifyDescription reason line character expectedDescription = verifyDescriptionImpl ptestCaseAsync line character expectedDescription + let fverifyDescription line character expectedDescription = verifyDescriptionImpl ftestCaseAsync line character expectedDescription - - testList + testSequenced + <| testList "tooltip evaluation" [ testList "tests" diff --git a/test/FsAutoComplete.Tests.Lsp/DependentFileCheckingTests.fs b/test/FsAutoComplete.Tests.Lsp/DependentFileCheckingTests.fs index b317fc9e8..935b124b2 100644 --- a/test/FsAutoComplete.Tests.Lsp/DependentFileCheckingTests.fs +++ b/test/FsAutoComplete.Tests.Lsp/DependentFileCheckingTests.fs @@ -9,63 +9,107 @@ open Utils.Server open System open FSharp.Control.Reactive open FSharpx.Control +open Helpers.Expecto.ShadowedTimeouts + +let () (path1 : string) path2 = Path.Join(path1, path2) let tests state = - let root = Path.Combine(__SOURCE_DIRECTORY__, "TestCases", "DependentFileChecking", "SameProject") + let sameProjectRoot = Path.Combine(__SOURCE_DIRECTORY__, "TestCases", "DependentFileChecking", "SameProject") + let crossProjectRoot tfm = Path.Combine(__SOURCE_DIRECTORY__, "TestCases", "DependentFileChecking", $"CrossProject-{tfm}") + let tfms = [ +#if NET6_0_OR_GREATER + "net6.0" +#endif +#if NET7_0_OR_GREATER + "net7.0" +#endif + ] let aFile, bFile = "A.fs", "B.fs" testList (nameof DependentFileChecking) [ - // Separate server for each test - // Otherwise share diag stream -> second test must skip diags of first tests. But only when first test runs (-> running all tests vs. running test alone) - serverTestList "single" state defaultConfigDto (Some root) (fun server -> [ - testCaseAsync "When A is modified B is re-checked" (async { - // open the files as they are on-disk and verify things are good - let! (aDoc, aDiags) = Server.openDocument aFile server - let! (bDoc, bDiags) = Server.openDocument bFile server - use aDoc = aDoc - use bDoc = bDoc - Expect.isEmpty aDiags $"There should be no diagnostics in {aFile}" - Expect.isEmpty bDiags $"There should be no diagnostics in {bFile}" - let bDiagsStream = bDoc.CompilerDiagnostics - // make a change to A (that is clearly incorrect) - let! startAChange = Document.changeTextTo "farts" aDoc |> Async.StartChild - // start listening for new diagnostics for B - let! diagnosticsForBWaiter = - bDiagsStream - |> Observable.timeoutSpan (TimeSpan.FromSeconds 15.) - |> Async.AwaitObservable - |> Async.StartChild - let! aDiags = startAChange - Expect.isNonEmpty aDiags $"Should have had some compilation errors for {aFile} after erroneous changes" - // observe that compilation errors are reported for B - let! bDiags = diagnosticsForBWaiter - Expect.isNonEmpty bDiags $"Should have some compilation errors for {bFile} after erroneous change to {aFile}" - }) - ]) - serverTestList "multi" state defaultConfigDto (Some root) (fun server -> [ - testCaseAsync "When A is modified repeatedly, B is re-checked after each modification" (async { - // open the files as they are on-disk and verify things are good - let! (aDoc, aDiags) = Server.openDocument aFile server - let! (bDoc, bDiags) = Server.openDocument bFile server - use aDoc = aDoc - use bDoc = bDoc - Expect.isEmpty aDiags $"There should be no diagnostics in {aFile}" - Expect.isEmpty bDiags $"There should be no diagnostics in {bFile}" - let bDiagsStream = bDoc.CompilerDiagnostics - for i in 0..10 do + testList "SameProject" [ + // Separate server for each test + // Otherwise share diag stream -> second test must skip diags of first tests. But only when first test runs (-> running all tests vs. running test alone) + serverTestList "single" state defaultConfigDto (Some sameProjectRoot) (fun server -> [ + testCaseAsync "When A is modified B is re-checked" (async { + // open the files as they are on-disk and verify things are good + let! (aDoc, aDiags) = Server.openDocument aFile server + let! (bDoc, bDiags) = Server.openDocument bFile server + use aDoc = aDoc + use bDoc = bDoc + Expect.isEmpty aDiags $"There should be no diagnostics in {aFile}" + Expect.isEmpty bDiags $"There should be no diagnostics in {bFile}" + let bDiagsStream = bDoc.CompilerDiagnostics // make a change to A (that is clearly incorrect) - let! startAChange = Document.changeTextTo "farts" aDoc |> Async.StartChild + let! startAChange = Document.saveText "farts" aDoc |> Async.StartChild // start listening for new diagnostics for B - let! diagnosticsForBWaiter = - bDiagsStream - |> Observable.skip i - |> Observable.timeoutSpan (TimeSpan.FromSeconds 15.) - |> Async.AwaitObservable + let! diagnosticsForBWaiter = + bDiagsStream + |> Observable.timeoutSpan (TimeSpan.FromSeconds 15.) + |> Async.AwaitObservable |> Async.StartChild let! aDiags = startAChange - Expect.isNonEmpty aDiags $"Should have had some compilation errors for {aFile} after erroneous change #%d{i}" + Expect.isNonEmpty aDiags $"Should have had some compilation errors for {aFile} after erroneous changes" // observe that compilation errors are reported for B let! bDiags = diagnosticsForBWaiter - Expect.isNonEmpty bDiags $"Should have some compilation errors for {bFile} after erroneous change #%d{i} to {aFile}" - }) - ]) + Expect.isNonEmpty bDiags $"Should have some compilation errors for {bFile} after erroneous change to {aFile}" + }) + ]) + serverTestList "multi" state defaultConfigDto (Some sameProjectRoot) (fun server -> [ + testCaseAsync "When A is modified repeatedly, B is re-checked after each modification" (async { + // open the files as they are on-disk and verify things are good + let! (aDoc, aDiags) = Server.openDocument aFile server + let! (bDoc, bDiags) = Server.openDocument bFile server + use aDoc = aDoc + use bDoc = bDoc + Expect.isEmpty aDiags $"There should be no diagnostics in {aFile}" + Expect.isEmpty bDiags $"There should be no diagnostics in {bFile}" + let bDiagsStream = bDoc.CompilerDiagnostics + for i in 0..10 do + // make a change to A (that is clearly incorrect) + let! startAChange = Document.saveText "farts" aDoc |> Async.StartChild + // start listening for new diagnostics for B + let! diagnosticsForBWaiter = + bDiagsStream + |> Observable.skip i + |> Observable.timeoutSpan (TimeSpan.FromSeconds 15.) + |> Async.AwaitObservable + |> Async.StartChild + let! aDiags = startAChange + Expect.isNonEmpty aDiags $"Should have had some compilation errors for {aFile} after erroneous change #%d{i}" + // observe that compilation errors are reported for B + let! bDiags = diagnosticsForBWaiter + Expect.isNonEmpty bDiags $"Should have some compilation errors for {bFile} after erroneous change #%d{i} to {aFile}" + }) + ]) + ] + for tfm in tfms do + let crossProject = crossProjectRoot tfm + let aFile, bFile = Path.Join("Library1" "A.fs"), ("App" "B.fs") + testList $"CrossProject-{tfm}" [ + serverTestList "single" state defaultConfigDto (Some crossProject) (fun server -> [ + testCaseAsync "When A is modified B is re-checked" (async { + // open the files as they are on-disk and verify things are good + let! (aDoc, aDiags) = Server.openDocument aFile server + let! (bDoc, bDiags) = Server.openDocument bFile server + use aDoc = aDoc + use bDoc = bDoc + Expect.isEmpty aDiags $"There should be no diagnostics in {aFile}" + Expect.isEmpty bDiags $"There should be no diagnostics in {bFile}" + let bDiagsStream = bDoc.CompilerDiagnostics + // make a change to A (that is clearly incorrect) + let! startAChange = Document.saveText "farts" aDoc |> Async.StartChild + // start listening for new diagnostics for B + let! diagnosticsForBWaiter = + bDiagsStream + |> Observable.timeoutSpan (TimeSpan.FromSeconds 15.) + |> Async.AwaitObservable + |> Async.StartChild + let! aDiags = startAChange + Expect.isNonEmpty aDiags $"Should have had some compilation errors for {aFile} after erroneous changes" + // observe that compilation errors are reported for B + let! bDiags = diagnosticsForBWaiter + Expect.isNonEmpty bDiags $"Should have some compilation errors for {bFile} after erroneous change to {aFile}" + }) + ]) + ] ] diff --git a/test/FsAutoComplete.Tests.Lsp/DetectUnitTests.fs b/test/FsAutoComplete.Tests.Lsp/DetectUnitTests.fs index 1b7c1d9a8..729ff4358 100644 --- a/test/FsAutoComplete.Tests.Lsp/DetectUnitTests.fs +++ b/test/FsAutoComplete.Tests.Lsp/DetectUnitTests.fs @@ -7,6 +7,7 @@ open FsAutoComplete open FsAutoComplete.LspHelpers open Helpers open FsToolkit.ErrorHandling +open Helpers.Expecto.ShadowedTimeouts let tests state = let geTestNotification projectFolder fileName = @@ -16,8 +17,9 @@ let tests state = do! waitForWorkspaceFinishedParsing events let path = Path.Combine(path, fileName) let tdop: DidOpenTextDocumentParams = { TextDocument = loadDocument path } - do! server.TextDocumentDidOpen tdop + let! child = Async.StartChild(server.TextDocumentDidOpen tdop) let! _diagnostics = waitForParseResultsForFile fileName events + do! child return! waitForTestDetected fileName events } |> Async.Cache diff --git a/test/FsAutoComplete.Tests.Lsp/ExtensionsTests.fs b/test/FsAutoComplete.Tests.Lsp/ExtensionsTests.fs index 735d8be0e..a2af6a9ec 100644 --- a/test/FsAutoComplete.Tests.Lsp/ExtensionsTests.fs +++ b/test/FsAutoComplete.Tests.Lsp/ExtensionsTests.fs @@ -8,7 +8,7 @@ open FsAutoComplete open FsAutoComplete.LspHelpers open Helpers open FsAutoComplete.Lsp - +open Helpers.Expecto.ShadowedTimeouts let fsdnTest state = diff --git a/test/FsAutoComplete.Tests.Lsp/FindReferencesTests.fs b/test/FsAutoComplete.Tests.Lsp/FindReferencesTests.fs index 4e182fdaa..e004d7080 100644 --- a/test/FsAutoComplete.Tests.Lsp/FindReferencesTests.fs +++ b/test/FsAutoComplete.Tests.Lsp/FindReferencesTests.fs @@ -5,6 +5,7 @@ open System.IO open FsAutoComplete open Helpers open Ionide.LanguageServerProtocol.Types +open Helpers.Expecto.ShadowedTimeouts let tests state = testList diff --git a/test/FsAutoComplete.Tests.Lsp/GoToTests.fs b/test/FsAutoComplete.Tests.Lsp/GoToTests.fs index d84e4f4b8..41c1d1598 100644 --- a/test/FsAutoComplete.Tests.Lsp/GoToTests.fs +++ b/test/FsAutoComplete.Tests.Lsp/GoToTests.fs @@ -11,6 +11,7 @@ open Utils.ServerTests open Utils.Server open Utils.Utils open Utils.TextEdit +open Helpers.Expecto.ShadowedTimeouts ///GoTo tests let private gotoTest state = diff --git a/test/FsAutoComplete.Tests.Lsp/Helpers.fs b/test/FsAutoComplete.Tests.Lsp/Helpers.fs index f663c7599..62e5bbc99 100644 --- a/test/FsAutoComplete.Tests.Lsp/Helpers.fs +++ b/test/FsAutoComplete.Tests.Lsp/Helpers.fs @@ -12,6 +12,51 @@ open FSharp.Control.Reactive open System.Threading open FSharp.UMX +module Expecto = + open System.Threading.Tasks + let inline testBuilderWithTimeout (ts : TimeSpan) name testCase focus = + TestLabel(name, TestCase (Test.timeout (int ts.TotalMilliseconds) (testCase), focus), focus) + + let inline testCaseWithTimeout (ts : TimeSpan) name test = + testBuilderWithTimeout ts name (Sync test) Normal + let inline ftestCaseWithTimeout (ts : TimeSpan) name test = + testBuilderWithTimeout ts name (Sync test) Focused + let inline ptestCaseWithTimeout (ts : TimeSpan) name test = + testBuilderWithTimeout ts name (Sync test) Pending + + let inline testCaseAsyncWithTimeout (ts : TimeSpan) name test = + testBuilderWithTimeout ts name (Async test) Normal + let inline ftestCaseAsyncWithTimeout (ts : TimeSpan) name test = + testBuilderWithTimeout ts name (Async test) Focused + let inline ptestCaseAsyncWithTimeout (ts : TimeSpan) name test = + testBuilderWithTimeout ts name (Async test) Pending + + let inline testCaseTaskWithTimeout (ts : TimeSpan) name (test : unit -> Task) = + testCaseAsyncWithTimeout ts name (async.Delay(fun () -> Async.AwaitTask(test ()))) + let inline ftestCaseTaskWithTimeout (ts : TimeSpan) name (test : unit -> Task) = + ftestCaseAsyncWithTimeout ts name (async.Delay(fun () -> Async.AwaitTask(test ()))) + let inline ptestCaseTaskWithTimeout (ts : TimeSpan) name (test : unit -> Task) = + ptestCaseAsyncWithTimeout ts name (async.Delay(fun () -> Async.AwaitTask(test ()))) + + // millisecond + let DEFAULT_TIMEOUT = + Environment.GetEnvironmentVariable "FSAC_TEST_DEFAULT_TIMEOUT" + |> Option.ofObj + |> Option.bind(fun x -> match Int32.TryParse x with | (true, v) -> Some v | _ -> None ) + |> Option.defaultValue (60000) + |> TimeSpan.FromMilliseconds + + /// Contains testCase functions that have a `DEFAULT_TIMEOUT` set to them + module ShadowedTimeouts = + + let testCase = testCaseWithTimeout DEFAULT_TIMEOUT + let ptestCase = ptestCaseWithTimeout DEFAULT_TIMEOUT + let ftestCase = ptestCaseWithTimeout DEFAULT_TIMEOUT + + let testCaseAsync = testCaseAsyncWithTimeout DEFAULT_TIMEOUT + let ptestCaseAsync = ptestCaseAsyncWithTimeout DEFAULT_TIMEOUT + let ftestCaseAsync = ptestCaseAsyncWithTimeout DEFAULT_TIMEOUT + let rec private copyDirectory sourceDir destDir = // Get the subdirectories for the specified directory. let dir = DirectoryInfo(sourceDir) @@ -428,13 +473,11 @@ let dotnetToolRestore dir = let serverInitialize path (config: FSharpConfigDto) createServer = async { dotnetCleanup path - let files = Directory.GetFiles(path) - if files |> Seq.exists (fun p -> p.EndsWith ".fsproj") then - do! dotnetRestore path + for file in System.IO.Directory.EnumerateFiles(path, "*.fsproj", SearchOption.AllDirectories) do + do! file |> Path.GetDirectoryName |> dotnetRestore let (server: IFSharpLspServer), clientNotifications = createServer () - logger.Value.Error("LOLOLOLOL") clientNotifications |> Observable.add logEvent let p: InitializeParams = diff --git a/test/FsAutoComplete.Tests.Lsp/HighlightingTests.fs b/test/FsAutoComplete.Tests.Lsp/HighlightingTests.fs index 916f9d4ab..2913ce479 100644 --- a/test/FsAutoComplete.Tests.Lsp/HighlightingTests.fs +++ b/test/FsAutoComplete.Tests.Lsp/HighlightingTests.fs @@ -6,6 +6,7 @@ open Helpers open FsAutoComplete.LspHelpers open Ionide.LanguageServerProtocol.Types open FsAutoComplete.Utils +open Helpers.Expecto.ShadowedTimeouts let tests state = let testPath = Path.Combine(__SOURCE_DIRECTORY__, "TestCases", "HighlightingTest") diff --git a/test/FsAutoComplete.Tests.Lsp/InfoPanelTests.fs b/test/FsAutoComplete.Tests.Lsp/InfoPanelTests.fs index 6d2218fc7..e440a2ccf 100644 --- a/test/FsAutoComplete.Tests.Lsp/InfoPanelTests.fs +++ b/test/FsAutoComplete.Tests.Lsp/InfoPanelTests.fs @@ -6,6 +6,7 @@ open Ionide.LanguageServerProtocol.Types open FsAutoComplete open Helpers open FsToolkit.ErrorHandling +open Helpers.Expecto.ShadowedTimeouts let docFormattingTest state = let server = diff --git a/test/FsAutoComplete.Tests.Lsp/InlayHintTests.fs b/test/FsAutoComplete.Tests.Lsp/InlayHintTests.fs index 9bb3ba59e..2a9ded897 100644 --- a/test/FsAutoComplete.Tests.Lsp/InlayHintTests.fs +++ b/test/FsAutoComplete.Tests.Lsp/InlayHintTests.fs @@ -9,6 +9,7 @@ open FsToolkit.ErrorHandling open Utils.ServerTests open FsAutoComplete.Core open FsAutoComplete.Lsp +open Helpers.Expecto.ShadowedTimeouts module private FSharpInlayHints = open Utils.Server @@ -1769,7 +1770,8 @@ let explicitTypeInfoTests = let testExplicitType textWithCursor expected = testExplicitType' textWithCursor (Some expected) - testList + testSequenced + <| testList "detect type and parens" [ testList "Expr" diff --git a/test/FsAutoComplete.Tests.Lsp/InteractiveDirectivesTests.fs b/test/FsAutoComplete.Tests.Lsp/InteractiveDirectivesTests.fs index 511820b86..9d1d91722 100644 --- a/test/FsAutoComplete.Tests.Lsp/InteractiveDirectivesTests.fs +++ b/test/FsAutoComplete.Tests.Lsp/InteractiveDirectivesTests.fs @@ -2,6 +2,7 @@ module FsAutoComplete.Tests.InteractiveDirectivesTests open Expecto open FsAutoComplete.InteractiveDirectives +open Helpers.Expecto.ShadowedTimeouts let interactiveDirectivesUnitTests = testList diff --git a/test/FsAutoComplete.Tests.Lsp/Program.fs b/test/FsAutoComplete.Tests.Lsp/Program.fs index e65e01dc1..c7a1bfc24 100644 --- a/test/FsAutoComplete.Tests.Lsp/Program.fs +++ b/test/FsAutoComplete.Tests.Lsp/Program.fs @@ -31,9 +31,10 @@ let testTimeout = Environment.SetEnvironmentVariable("FSAC_WORKSPACELOAD_DELAY", "250") let loaders = - [ "Ionide WorkspaceLoader", WorkspaceLoader.Create - // "MSBuild Project Graph WorkspaceLoader", WorkspaceLoaderViaProjectGraph.Create - ] + [ + "Ionide WorkspaceLoader", (fun toolpath -> WorkspaceLoader.Create(toolpath, FsAutoComplete.Core.ProjectLoader.globalProperties)) + // "MSBuild Project Graph WorkspaceLoader", (fun toolpath -> WorkspaceLoaderViaProjectGraph.Create(toolpath, FsAutoComplete.Core.ProjectLoader.globalProperties)) + ] let fsharpLspServerFactory toolsPath workspaceLoaderFactory = let testRunDir = @@ -50,8 +51,8 @@ let adaptiveLspServerFactory toolsPath workspaceLoaderFactory = let lspServers = [ - "FSharpLspServer", fsharpLspServerFactory - // "AdaptiveLspServer", adaptiveLspServerFactory + // "FSharpLspServer", fsharpLspServerFactory + "AdaptiveLspServer", adaptiveLspServerFactory ] let mutable toolsPath = @@ -88,13 +89,13 @@ let lspTests = dependencyManagerTests createServer interactiveDirectivesUnitTests - // // commented out because FSDN is down - // //fsdnTest createServer - uriTests + // commented out because FSDN is down + //fsdnTest createServer + //linterTests createServer + uriTests formattingTests createServer - if lspName <> "AdaptiveLspServer" then - analyzerTests createServer // stalling on adaptive + analyzerTests createServer signatureTests createServer SignatureHelp.tests createServer CodeFixTests.Tests.tests createServer @@ -102,8 +103,7 @@ let lspTests = GoTo.tests createServer FindReferences.tests createServer InfoPanelTests.docFormattingTest createServer - if lspName <> "AdaptiveLspServer" then - DetectUnitTests.tests createServer //stalling on adaptive + DetectUnitTests.tests createServer XmlDocumentationGeneration.tests createServer InlayHintTests.tests createServer DependentFileChecking.tests createServer @@ -119,7 +119,8 @@ let tests = testList (nameof (Utils)) [ Utils.Tests.Utils.tests; Utils.Tests.TextEdit.tests ] InlayHintTests.explicitTypeInfoTests - lspTests ] + lspTests + ] [] diff --git a/test/FsAutoComplete.Tests.Lsp/RenameTests.fs b/test/FsAutoComplete.Tests.Lsp/RenameTests.fs index 5be43ab78..5015066b5 100644 --- a/test/FsAutoComplete.Tests.Lsp/RenameTests.fs +++ b/test/FsAutoComplete.Tests.Lsp/RenameTests.fs @@ -11,6 +11,7 @@ open Utils.ServerTests open Utils.Server open Utils.Utils open Utils.TextEdit +open Helpers.Expecto.ShadowedTimeouts let private normalizePathCasing = Path.FilePathToUri @@ -141,7 +142,7 @@ let tests state = NewName = newName } let! res = doc.Server.Server.TextDocumentRename p - let edits = + let edits = match res with | Result.Error e -> failtestf "Request failed: %A" e | Result.Ok None -> failtest "Request none" diff --git a/test/FsAutoComplete.Tests.Lsp/ScriptTests.fs b/test/FsAutoComplete.Tests.Lsp/ScriptTests.fs index 2ef4fa925..d64929744 100644 --- a/test/FsAutoComplete.Tests.Lsp/ScriptTests.fs +++ b/test/FsAutoComplete.Tests.Lsp/ScriptTests.fs @@ -11,6 +11,7 @@ open FsToolkit.ErrorHandling open FSharpx.Control.Observable open FSharp.Control.Reactive open System +open Helpers.Expecto.ShadowedTimeouts let scriptPreviewTests state = let server = diff --git a/test/FsAutoComplete.Tests.Lsp/SignatureHelpTests.fs b/test/FsAutoComplete.Tests.Lsp/SignatureHelpTests.fs index 66c7a29d5..2a51c01ad 100644 --- a/test/FsAutoComplete.Tests.Lsp/SignatureHelpTests.fs +++ b/test/FsAutoComplete.Tests.Lsp/SignatureHelpTests.fs @@ -8,6 +8,7 @@ open Utils.Server open Utils.Utils open Utils.TextEdit open Utils.ServerTests +open Helpers.Expecto.ShadowedTimeouts type private TriggerType = | Manual diff --git a/test/FsAutoComplete.Tests.Lsp/TemplatesTests.fs b/test/FsAutoComplete.Tests.Lsp/TemplatesTests.fs index 9ca1c781e..a9ef7eb51 100644 --- a/test/FsAutoComplete.Tests.Lsp/TemplatesTests.fs +++ b/test/FsAutoComplete.Tests.Lsp/TemplatesTests.fs @@ -1,6 +1,7 @@ module FsAutoComplete.Tests.Templates open Expecto +open Helpers.Expecto.ShadowedTimeouts let withEnvironmentVariable (variable: string) (value: string) (f: unit -> unit) = let priorValue = System.Environment.GetEnvironmentVariable variable @@ -13,7 +14,8 @@ let withEnvironmentVariable (variable: string) (value: string) (f: unit -> unit) System.Environment.SetEnvironmentVariable(variable, priorValue) let tests () = - testList + testSequenced + <| testList "Templates Tests" [ testCase "Templates are not empty" <| fun () -> diff --git a/test/FsAutoComplete.Tests.Lsp/TestCases/DependentFileChecking/CrossProject-net6.0/App/App.fsproj b/test/FsAutoComplete.Tests.Lsp/TestCases/DependentFileChecking/CrossProject-net6.0/App/App.fsproj new file mode 100644 index 000000000..e9a9d8342 --- /dev/null +++ b/test/FsAutoComplete.Tests.Lsp/TestCases/DependentFileChecking/CrossProject-net6.0/App/App.fsproj @@ -0,0 +1,16 @@ + + + + Exe + net6.0 + + + + + + + + + + + diff --git a/test/FsAutoComplete.Tests.Lsp/TestCases/DependentFileChecking/CrossProject-net6.0/App/B.fs b/test/FsAutoComplete.Tests.Lsp/TestCases/DependentFileChecking/CrossProject-net6.0/App/B.fs new file mode 100644 index 000000000..c346d59b6 --- /dev/null +++ b/test/FsAutoComplete.Tests.Lsp/TestCases/DependentFileChecking/CrossProject-net6.0/App/B.fs @@ -0,0 +1,5 @@ +[] +let main _ = + let greet name = Library1.Say.hello name + greet "lol" + 0 diff --git a/test/FsAutoComplete.Tests.Lsp/TestCases/DependentFileChecking/CrossProject-net6.0/Library1/A.fs b/test/FsAutoComplete.Tests.Lsp/TestCases/DependentFileChecking/CrossProject-net6.0/Library1/A.fs new file mode 100644 index 000000000..0d06d4bf0 --- /dev/null +++ b/test/FsAutoComplete.Tests.Lsp/TestCases/DependentFileChecking/CrossProject-net6.0/Library1/A.fs @@ -0,0 +1,5 @@ +namespace Library1 + +module Say = + let hello name = + printfn "Hello %s" name diff --git a/test/FsAutoComplete.Tests.Lsp/TestCases/DependentFileChecking/CrossProject-net6.0/Library1/Library1.fsproj b/test/FsAutoComplete.Tests.Lsp/TestCases/DependentFileChecking/CrossProject-net6.0/Library1/Library1.fsproj new file mode 100644 index 000000000..7af7fd3b6 --- /dev/null +++ b/test/FsAutoComplete.Tests.Lsp/TestCases/DependentFileChecking/CrossProject-net6.0/Library1/Library1.fsproj @@ -0,0 +1,12 @@ + + + + net6.0 + true + + + + + + + diff --git a/test/FsAutoComplete.Tests.Lsp/TestCases/DependentFileChecking/CrossProject-net7.0/App/App.fsproj b/test/FsAutoComplete.Tests.Lsp/TestCases/DependentFileChecking/CrossProject-net7.0/App/App.fsproj new file mode 100644 index 000000000..7b67e56a6 --- /dev/null +++ b/test/FsAutoComplete.Tests.Lsp/TestCases/DependentFileChecking/CrossProject-net7.0/App/App.fsproj @@ -0,0 +1,16 @@ + + + + Exe + net7.0 + + + + + + + + + + + diff --git a/test/FsAutoComplete.Tests.Lsp/TestCases/DependentFileChecking/CrossProject-net7.0/App/B.fs b/test/FsAutoComplete.Tests.Lsp/TestCases/DependentFileChecking/CrossProject-net7.0/App/B.fs new file mode 100644 index 000000000..c346d59b6 --- /dev/null +++ b/test/FsAutoComplete.Tests.Lsp/TestCases/DependentFileChecking/CrossProject-net7.0/App/B.fs @@ -0,0 +1,5 @@ +[] +let main _ = + let greet name = Library1.Say.hello name + greet "lol" + 0 diff --git a/test/FsAutoComplete.Tests.Lsp/TestCases/DependentFileChecking/CrossProject-net7.0/Library1/A.fs b/test/FsAutoComplete.Tests.Lsp/TestCases/DependentFileChecking/CrossProject-net7.0/Library1/A.fs new file mode 100644 index 000000000..0d06d4bf0 --- /dev/null +++ b/test/FsAutoComplete.Tests.Lsp/TestCases/DependentFileChecking/CrossProject-net7.0/Library1/A.fs @@ -0,0 +1,5 @@ +namespace Library1 + +module Say = + let hello name = + printfn "Hello %s" name diff --git a/test/FsAutoComplete.Tests.Lsp/TestCases/DependentFileChecking/CrossProject-net7.0/Library1/Library1.fsproj b/test/FsAutoComplete.Tests.Lsp/TestCases/DependentFileChecking/CrossProject-net7.0/Library1/Library1.fsproj new file mode 100644 index 000000000..5292ad046 --- /dev/null +++ b/test/FsAutoComplete.Tests.Lsp/TestCases/DependentFileChecking/CrossProject-net7.0/Library1/Library1.fsproj @@ -0,0 +1,12 @@ + + + + net7.0 + true + + + + + + + diff --git a/test/FsAutoComplete.Tests.Lsp/UnsedDeclarationsTests.fs b/test/FsAutoComplete.Tests.Lsp/UnsedDeclarationsTests.fs index f0d5456b6..a76e1de5c 100644 --- a/test/FsAutoComplete.Tests.Lsp/UnsedDeclarationsTests.fs +++ b/test/FsAutoComplete.Tests.Lsp/UnsedDeclarationsTests.fs @@ -8,6 +8,7 @@ open Utils.TextEdit open Utils.Utils open Ionide.LanguageServerProtocol.Types open System.IO +open Helpers.Expecto.ShadowedTimeouts type private Expected = | Used diff --git a/test/FsAutoComplete.Tests.Lsp/Utils/Server.fs b/test/FsAutoComplete.Tests.Lsp/Utils/Server.fs index c5a63dd7c..5e2c8116c 100644 --- a/test/FsAutoComplete.Tests.Lsp/Utils/Server.fs +++ b/test/FsAutoComplete.Tests.Lsp/Utils/Server.fs @@ -28,6 +28,7 @@ type CachedServer = Async type Document = { Server: Server + FilePath : string Uri: DocumentUri mutable Version: int } member doc.TextDocumentIdentifier: TextDocumentIdentifier = { Uri = doc.Uri } @@ -61,10 +62,9 @@ module Server = | Some path -> dotnetCleanup path - if System.IO.Directory.EnumerateFiles(path, "*.fsproj") - |> Seq.isEmpty - |> not then - do! dotnetRestore path + for file in System.IO.Directory.EnumerateFiles(path, "*.fsproj", SearchOption.AllDirectories) do + do! file |> Path.GetDirectoryName |> dotnetRestore + let (server : IFSharpLspServer, events : IObservable<_>) = createServer () events |> Observable.add logEvent @@ -113,9 +113,10 @@ module Server = do! server.Server.Shutdown() } - let private createDocument uri server = + let private createDocument fullPath uri server = { Server = server Uri = uri + FilePath = fullPath Version = 0 } let private untitledDocUrif = sprintf "untitled:Untitled-%i" @@ -131,7 +132,7 @@ module Server = let doc = server - |> createDocument (server |> nextUntitledDocUri) + |> createDocument String.Empty (server |> nextUntitledDocUri) let! diags = doc |> Document.openWith initialText @@ -157,13 +158,15 @@ module Server = let doc = server - |> createDocument ( + |> createDocument fullPath - // normalize path is necessary: otherwise might be different lower/upper cases in uri for tests and LSP server: - // on windows `E:\...`: `file:///E%3A/...` (before normalize) vs. `file:///e%3A/..` (after normalize) - |> normalizePath - |> Path.LocalPathToUri - ) + ( + fullPath + // normalize path is necessary: otherwise might be different lower/upper cases in uri for tests and LSP server: + // on windows `E:\...`: `file:///E%3A/...` (before normalize) vs. `file:///e%3A/..` (after normalize) + |> normalizePath + |> Path.LocalPathToUri + ) let! diags = doc @@ -190,9 +193,12 @@ module Server = |> Utils.normalizePath |> FSharp.UMX.UMX.untag + // To avoid hitting the typechecker cache, we need to update the file's timestamp + IO.File.SetLastWriteTimeUtc(fullPath, DateTime.UtcNow) + let doc = server - |> createDocument (Path.FilePathToUri fullPath) + |> createDocument fullPath (Path.FilePathToUri fullPath) let! diags = doc |> Document.openWith initialText @@ -365,6 +371,19 @@ module Document = return! doc |> waitForLatestDiagnostics Helpers.defaultTimeout } + let saveText (text : string) (doc : Document) = + async { + let p : DidSaveTextDocumentParams = { + Text = Some text + TextDocument = doc.TextDocumentIdentifier + } + // Simulate the file being written to disk so we don't hit the typechecker cache + IO.File.SetLastWriteTimeUtc(doc.FilePath, DateTime.UtcNow) + do! doc.Server.Server.TextDocumentDidSave p + do! Async.Sleep(TimeSpan.FromMilliseconds 250.) + return! doc |> waitForLatestDiagnostics Helpers.defaultTimeout + } + let private assertOk result = Expect.isOk result "Expected success" diff --git a/test/FsAutoComplete.Tests.Lsp/XmlGenerationTests.fs b/test/FsAutoComplete.Tests.Lsp/XmlGenerationTests.fs index ca06d0342..242904652 100644 --- a/test/FsAutoComplete.Tests.Lsp/XmlGenerationTests.fs +++ b/test/FsAutoComplete.Tests.Lsp/XmlGenerationTests.fs @@ -7,6 +7,7 @@ open FsToolkit.ErrorHandling open Helpers open Ionide.LanguageServerProtocol.Types open System.IO +open Helpers.Expecto.ShadowedTimeouts open type System.Environment diff --git a/test/FsAutoComplete.Tests.Lsp/paket.references b/test/FsAutoComplete.Tests.Lsp/paket.references index 12261d103..0696c0d64 100644 --- a/test/FsAutoComplete.Tests.Lsp/paket.references +++ b/test/FsAutoComplete.Tests.Lsp/paket.references @@ -1,4 +1,4 @@ -FSharp.Core +FSharp.Core content: once FSharp.Compiler.Service FSharp.Control.Reactive FSharpx.Async