From ee9edece9a1afbd20aeb562aeaf1cecee9937440 Mon Sep 17 00:00:00 2001 From: nojaf Date: Wed, 14 Feb 2024 15:59:21 +0100 Subject: [PATCH 01/22] After call --- .../src/Checker/FcsCheckerService.fs | 25 ++++++++++++------- .../src/Checker/FcsProjectProvider.fs | 2 ++ .../src/Settings/FSharpOptions.fs | 10 +++++++- .../src/Shim/FileSystem/FSharpSourceCache.fs | 3 +++ .../src/Search/FSharpSearchGuru.fs | 0 .../fsharp/llm/FSharpFileSummarizer.kt | 3 ++- 6 files changed, 32 insertions(+), 11 deletions(-) create mode 100644 ReSharper.FSharp/src/FSharp/FSharp.Psi.Features/src/Search/FSharpSearchGuru.fs diff --git a/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsCheckerService.fs b/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsCheckerService.fs index 178eb0518f..f30965da17 100644 --- a/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsCheckerService.fs +++ b/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsCheckerService.fs @@ -39,6 +39,7 @@ module FcsCheckerService = type FcsProject = { OutputPath: VirtualFileSystemPath ProjectOptions: FSharpProjectOptions + ProjectSnapshot: FSharpProjectSnapshot option ParsingOptions: FSharpParsingOptions FileIndices: IDictionary ImplementationFilesWithSignatures: ISet @@ -88,25 +89,27 @@ type FcsProjectInvalidationType = type FcsCheckerService(lifetime: Lifetime, logger: ILogger, onSolutionCloseNotifier: OnSolutionCloseNotifier, settingsStore: ISettingsStore, locks: IShellLocks, configurations: RunsProducts.ProductConfigurations) = - let checker = - Environment.SetEnvironmentVariable("FCS_CheckFileInProjectCacheSize", "20") - - let settingsStoreLive = settingsStore.BindToContextLive(lifetime, ContextRange.ApplicationWide) + let settingsStoreLive = settingsStore.BindToContextLive(lifetime, ContextRange.ApplicationWide) - let getSettingProperty name = - let setting = SettingsUtil.getEntry settingsStore name - settingsStoreLive.GetValueProperty(lifetime, setting, null) + let getSettingProperty name = + let setting = SettingsUtil.getEntry settingsStore name + settingsStoreLive.GetValueProperty(lifetime, setting, null) + let useTransparentCompiler = (getSettingProperty "UseTransparentCompiler").Value + + let checker = + Environment.SetEnvironmentVariable("FCS_CheckFileInProjectCacheSize", "20") let skipImpl = getSettingProperty "SkipImplementationAnalysis" let analyzerProjectReferencesInParallel = getSettingProperty "ParallelProjectReferencesAnalysis" - + lazy let checker = FSharpChecker.Create(projectCacheSize = 200, keepAllBackgroundResolutions = false, keepAllBackgroundSymbolUses = false, enablePartialTypeChecking = skipImpl.Value, - parallelReferenceResolution = analyzerProjectReferencesInParallel.Value) + parallelReferenceResolution = analyzerProjectReferencesInParallel.Value, + useTransparentCompiler = useTransparentCompiler) checker @@ -156,11 +159,15 @@ type FcsCheckerService(lifetime: Lifetime, logger: ILogger, onSolutionCloseNotif | Some(parseResults, checkResults) -> Some({ ParseResults = parseResults; CheckResults = checkResults }) | _ -> + + ProhibitTypeCheckCookie.AssertTypeCheckIsAllowed() locks.AssertReadAccessAllowed() x.AssertFcsAccessThread() let psiModule = sourceFile.PsiModule + // check if is active ... + if useTransparentCompiler then () match x.FcsProjectProvider.GetFcsProject(psiModule) with | None -> None | Some fcsProject -> diff --git a/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsProjectProvider.fs b/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsProjectProvider.fs index 7a9ac94e43..e7a7aef2b2 100644 --- a/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsProjectProvider.fs +++ b/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsProjectProvider.fs @@ -285,6 +285,8 @@ type FcsProjectProvider(lifetime: Lifetime, solution: ISolution, changeManager: match scriptFcsProjectProvider.GetScriptOptions(sourceFile) with | None -> None | Some projectOptions -> + + // let snapshot = FSharpProjectSnapshot.FromOptions projectOptions let parsingOptions = { FSharpParsingOptions.Default with diff --git a/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Settings/FSharpOptions.fs b/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Settings/FSharpOptions.fs index f180cba49b..bdbe336a1e 100644 --- a/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Settings/FSharpOptions.fs +++ b/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Settings/FSharpOptions.fs @@ -29,6 +29,7 @@ module FSharpOptions = let [] nonFSharpProjectInMemoryReferences = "Analyze C# and VB.NET project references in-memory" let [] outOfScopeCompletion = "Enable out of scope items completion" let [] topLevelOpenCompletion = "Add 'open' declarations to top level module or namespace" + let [] useTransparentCompilerDescription = "Use TransparentCompiler" [, "FSharpOptions")>] @@ -46,7 +47,10 @@ type FSharpOptions = mutable EnableOutOfScopeCompletion: bool [] - mutable TopLevelOpenCompletion: bool } + mutable TopLevelOpenCompletion: bool + + [] + mutable UseTransparentCompiler: bool } type FantomasLocationSettings = | AutoDetected = 0 @@ -154,6 +158,9 @@ type FSharpOptionsProvider(lifetime, solution, settings, settingsSchema) = member val NonFSharpProjectInMemoryReferences = base.GetValueProperty("NonFSharpProjectInMemoryReferences").Value with get, set + + member val UseTransparentCompiler = + base.GetValueProperty("UseTransparentCompiler").Value member this.UpdateAssemblyReaderSetting() = this.NonFSharpProjectInMemoryReferences <- @@ -210,6 +217,7 @@ type FSharpOptionsPage(lifetime: Lifetime, optionsPageContext, settings, this.AddBoolOptionWithComment((fun key -> key.SkipImplementationAnalysis), skipImplementationAnalysis, "Requires restart") |> ignore this.AddBoolOptionWithComment((fun key -> key.ParallelProjectReferencesAnalysis), parallelProjectReferencesAnalysis, "Requires restart") |> ignore this.AddBoolOptionWithComment((fun key -> key.OutOfProcessTypeProviders), FSharpExperimentalFeatures.outOfProcessTypeProviders, "Requires restart") |> ignore + this.AddBoolOptionWithComment((fun key -> key.UseTransparentCompiler), useTransparentCompilerDescription, "Requires restart") |> ignore do use indent = this.Indent() diff --git a/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Shim/FileSystem/FSharpSourceCache.fs b/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Shim/FileSystem/FSharpSourceCache.fs index 60158687a7..cbbc429073 100644 --- a/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Shim/FileSystem/FSharpSourceCache.fs +++ b/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Shim/FileSystem/FSharpSourceCache.fs @@ -25,6 +25,9 @@ type FSharpSource = | Exists(source, timestamp) -> RdFSharpSource(Encoding.UTF8.GetString(source), timestamp) | _ -> RdFSharpSource("NotExists", DateTime.MinValue) +// Potentially drop this cache if transparent compiler is used? +// Might not be used if transparent compiler + [] type FSharpSourceCache(lifetime: Lifetime, solution: ISolution, changeManager, documentManager: DocumentManager, solutionDocumentChangeProvider: SolutionDocumentChangeProvider, fileExtensions: IProjectFileExtensions, diff --git a/ReSharper.FSharp/src/FSharp/FSharp.Psi.Features/src/Search/FSharpSearchGuru.fs b/ReSharper.FSharp/src/FSharp/FSharp.Psi.Features/src/Search/FSharpSearchGuru.fs new file mode 100644 index 0000000000..e69de29bb2 diff --git a/rider-fsharp/src/main/java/com/jetbrains/rider/plugins/fsharp/llm/FSharpFileSummarizer.kt b/rider-fsharp/src/main/java/com/jetbrains/rider/plugins/fsharp/llm/FSharpFileSummarizer.kt index a44ce217e1..a7c2247cad 100644 --- a/rider-fsharp/src/main/java/com/jetbrains/rider/plugins/fsharp/llm/FSharpFileSummarizer.kt +++ b/rider-fsharp/src/main/java/com/jetbrains/rider/plugins/fsharp/llm/FSharpFileSummarizer.kt @@ -1,5 +1,6 @@ package com.jetbrains.rider.plugins.fsharp.llm +/* import com.intellij.ml.llm.smartChat.psiSummarization.LanguageSummaryProvider import com.intellij.psi.PsiDocumentManager import com.intellij.psi.PsiElement @@ -27,4 +28,4 @@ class FSharpFileSummarizer: LanguageSummaryProvider { return null } } - +*/ \ No newline at end of file From f1ad0dbbe2e120864e615b543d59e9d23a20341f Mon Sep 17 00:00:00 2001 From: nojaf Date: Wed, 14 Feb 2024 17:22:42 +0100 Subject: [PATCH 02/22] Use new API for ParseAndCheckDocument --- .../src/Checker/FSharpCheckerExtensions.fs | 60 ++++++++++--------- .../src/Checker/FcsCheckerService.fs | 46 +++++++------- 2 files changed, 57 insertions(+), 49 deletions(-) diff --git a/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FSharpCheckerExtensions.fs b/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FSharpCheckerExtensions.fs index 8c29824881..bd4123fd41 100644 --- a/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FSharpCheckerExtensions.fs +++ b/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FSharpCheckerExtensions.fs @@ -1,6 +1,8 @@ [] module JetBrains.ReSharper.Plugins.FSharp.Checker.FSharpCheckerExtensions +#nowarn "57" + open System.Threading open System.Threading.Tasks open FSharp.Compiler.CodeAnalysis @@ -19,13 +21,13 @@ type CheckResults = | StillRunning of Task<(FSharpParseFileResults * FSharpCheckFileResults) option> type FSharpChecker with - member internal x.ParseAndCheckDocument(path, source: ISourceText, options, allowStale: bool, opName) = - let version = source.GetHashCode() + member internal x.ParseAndCheckDocument(path, projectSnapshot: FSharpProjectSnapshot, allowStale: bool, opName) = + // let version = source.GetHashCode() let parseAndCheckFile = async { let! parseResults, checkFileAnswer = - x.ParseAndCheckFileInProject(path, version, source, options, userOpName = opName) + x.ParseAndCheckFileInProject(path, projectSnapshot, userOpName = opName) return match checkFileAnswer with @@ -60,28 +62,30 @@ type FSharpChecker with Some (parseResults, checkResults) | _ -> None - async { - match x.TryGetRecentCheckResultsForFile(path, options, source) with - | None -> - // No stale results available, wait for fresh results - return! parseAndCheckFile - - | Some (parseResults, checkFileResults, cachedVersion) when allowStale && cachedVersion = int64 version -> - // Avoid queueing on the reactor thread by using the recent results - return Some (parseResults, checkFileResults) - - | Some (staleParseResults, staleCheckFileResults, _) -> - - match! tryGetFreshResultsWithTimeout() with - | Ready x -> - // Fresh results were ready quickly enough - return x - - | StillRunning _ when allowStale -> - // Still waiting for fresh results - just use the stale ones for now - return Some (staleParseResults, staleCheckFileResults) - - | StillRunning worker -> - return! Async.AwaitTask worker - } - |> map bindParsedInput + map bindParsedInput parseAndCheckFile + // async { + // // TODO: TryGetRecentCheckResultsForFile is missing on the Transparent compiler + // match x.TryGetRecentCheckResultsForFile(path, options, source) with + // | None -> + // // No stale results available, wait for fresh results + // return! parseAndCheckFile + // + // | Some (parseResults, checkFileResults, cachedVersion) when allowStale && cachedVersion = int64 version -> + // // Avoid queueing on the reactor thread by using the recent results + // return Some (parseResults, checkFileResults) + // + // | Some (staleParseResults, staleCheckFileResults, _) -> + // + // match! tryGetFreshResultsWithTimeout() with + // | Ready x -> + // // Fresh results were ready quickly enough + // return x + // + // | StillRunning _ when allowStale -> + // // Still waiting for fresh results - just use the stale ones for now + // return Some (staleParseResults, staleCheckFileResults) + // + // | StillRunning worker -> + // return! Async.AwaitTask worker + // } + // |> map bindParsedInput diff --git a/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsCheckerService.fs b/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsCheckerService.fs index f30965da17..dd03ca5065 100644 --- a/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsCheckerService.fs +++ b/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsCheckerService.fs @@ -1,5 +1,7 @@ namespace rec JetBrains.ReSharper.Plugins.FSharp.Checker +#nowarn "57" + open System open System.Collections.Generic open System.IO @@ -38,8 +40,7 @@ module FcsCheckerService = type FcsProject = { OutputPath: VirtualFileSystemPath - ProjectOptions: FSharpProjectOptions - ProjectSnapshot: FSharpProjectSnapshot option + ProjectSnapshot: FSharpProjectSnapshot ParsingOptions: FSharpParsingOptions FileIndices: IDictionary ImplementationFilesWithSignatures: ISet @@ -54,23 +55,26 @@ type FcsProject = tryGetValue path x.FileIndices |> Option.defaultValue -1 member x.TestDump(writer: TextWriter) = - let projectOptions = x.ProjectOptions - - writer.WriteLine($"Project file: {projectOptions.ProjectFileName}") - writer.WriteLine($"Stamp: {projectOptions.Stamp}") - writer.WriteLine($"Load time: {projectOptions.LoadTime}") - - writer.WriteLine("Source files:") - for sourceFile in projectOptions.SourceFiles do - writer.WriteLine($" {sourceFile}") - - writer.WriteLine("Other options:") - for option in projectOptions.OtherOptions do - writer.WriteLine($" {option}") + let projectSnapshot = x.ProjectSnapshot + let (ProjectSnapshot.FSharpProjectIdentifier(projectFileName, _)) = projectSnapshot.Identifier - writer.WriteLine("Referenced projects:") - for referencedProject in projectOptions.ReferencedProjects do - writer.WriteLine($" {referencedProject.OutputFile}") + writer.WriteLine($"Project file: {projectFileName}") + // TODO: the ProjectSnapshot exposes less information than the ProjectOptions + + // writer.WriteLine($"Stamp: {projectSnapshot.}") + // writer.WriteLine($"Load time: {projectSnapshot.LoadTime}") + // + // writer.WriteLine("Source files:") + // for sourceFile in projectSnapshot.SourceFiles do + // writer.WriteLine($" {sourceFile}") + // + // writer.WriteLine("Other options:") + // for option in projectSnapshot.OtherOptions do + // writer.WriteLine($" {option}") + // + // writer.WriteLine("Referenced projects:") + // for referencedProject in projectSnapshot.ReferencedProjects do + // writer.WriteLine($" {referencedProject.OutputFile}") writer.WriteLine() @@ -172,8 +176,8 @@ type FcsCheckerService(lifetime: Lifetime, logger: ILogger, onSolutionCloseNotif | None -> None | Some fcsProject -> - let options = fcsProject.ProjectOptions - if not (fcsProject.IsKnownFile(sourceFile)) && not options.UseScriptResolutionRules then None else + let snapshot = fcsProject.ProjectSnapshot + // if not (fcsProject.IsKnownFile(sourceFile)) && not options.UseScriptResolutionRules then None else x.FcsProjectProvider.PrepareAssemblyShim(psiModule) @@ -182,7 +186,7 @@ type FcsCheckerService(lifetime: Lifetime, logger: ILogger, onSolutionCloseNotif logger.Trace("ParseAndCheckFile: start {0}, {1}", path, opName) // todo: don't cancel the computation when file didn't change - match x.Checker.ParseAndCheckDocument(path, source, options, allowStaleResults, opName).RunAsTask() with + match x.Checker.ParseAndCheckDocument(path, snapshot, allowStaleResults, opName).RunAsTask() with | Some (parseResults, checkResults) -> logger.Trace("ParseAndCheckFile: finish {0}, {1}", path, opName) Some { ParseResults = parseResults; CheckResults = checkResults } From 12bf829fe96ab760d489bf43d1b8ee42e0446808 Mon Sep 17 00:00:00 2001 From: nojaf Date: Thu, 15 Feb 2024 14:32:07 +0100 Subject: [PATCH 03/22] Use local JFCS --- ReSharper.FSharp/Directory.Build.props | 7 +++++++ .../FSharp.TypeProviders.Host.NetCore.csproj | 3 +-- .../FSharp.TypeProviders.Host.csproj | 4 ++-- .../src/FSharp/FSharp.Common/FSharp.Common.fsproj | 1 - .../FSharp.ProjectModelBase/FSharp.ProjectModelBase.csproj | 1 - .../src/FSharp/FSharp.Psi.Daemon/FSharp.Psi.Daemon.fsproj | 3 +-- .../FSharp/FSharp.Psi.Features/FSharp.Psi.Features.fsproj | 3 +-- .../FSharp.Psi.Intentions/FSharp.Psi.Intentions.fsproj | 1 - .../FSharp/FSharp.Psi.Services/FSharp.Psi.Services.fsproj | 3 +-- ReSharper.FSharp/src/FSharp/FSharp.Psi/FSharp.Psi.csproj | 3 +-- .../src/FSharp/FSharp.Tests.Host/FSharp.Tests.Host.fsproj | 1 - .../FSharp.TypeProviders.Protocol.csproj | 1 - .../src/FSharp.Common.Tests/FSharp.Common.Tests.fsproj | 1 - .../FSharp.Intentions.Tests/FSharp.Intentions.Tests.fsproj | 1 - .../src/FSharp.Tests.Common/FSharp.Tests.Common.fsproj | 1 - ReSharper.FSharp/test/src/FSharp.Tests/FSharp.Tests.fsproj | 1 - 16 files changed, 14 insertions(+), 21 deletions(-) diff --git a/ReSharper.FSharp/Directory.Build.props b/ReSharper.FSharp/Directory.Build.props index db6c9e9455..d3485b9485 100644 --- a/ReSharper.FSharp/Directory.Build.props +++ b/ReSharper.FSharp/Directory.Build.props @@ -33,5 +33,12 @@ $(Subplatform).Psi.Features_test_Framework.Props $(Subplatform).Rider_RdBackend.Common.Props $(Subplatform).Rider_Rider.Backend.Props + true + $(MSBuildThisFileDirectory)../../fsharp/src/Compiler/FSharp.Compiler.Service.fsproj + + + + + diff --git a/ReSharper.FSharp/src/FSharp.TypeProviders.Host/FSharp.TypeProviders.Host.NetCore/FSharp.TypeProviders.Host.NetCore.csproj b/ReSharper.FSharp/src/FSharp.TypeProviders.Host/FSharp.TypeProviders.Host.NetCore/FSharp.TypeProviders.Host.NetCore.csproj index aa48a1b6db..3f8fb7eb90 100644 --- a/ReSharper.FSharp/src/FSharp.TypeProviders.Host/FSharp.TypeProviders.Host.NetCore/FSharp.TypeProviders.Host.NetCore.csproj +++ b/ReSharper.FSharp/src/FSharp.TypeProviders.Host/FSharp.TypeProviders.Host.NetCore/FSharp.TypeProviders.Host.NetCore.csproj @@ -25,7 +25,6 @@ - @@ -46,4 +45,4 @@ - \ No newline at end of file + diff --git a/ReSharper.FSharp/src/FSharp.TypeProviders.Host/FSharp.TypeProviders.Host/FSharp.TypeProviders.Host.csproj b/ReSharper.FSharp/src/FSharp.TypeProviders.Host/FSharp.TypeProviders.Host/FSharp.TypeProviders.Host.csproj index 00b4dc0d6a..400ee2cd7b 100644 --- a/ReSharper.FSharp/src/FSharp.TypeProviders.Host/FSharp.TypeProviders.Host/FSharp.TypeProviders.Host.csproj +++ b/ReSharper.FSharp/src/FSharp.TypeProviders.Host/FSharp.TypeProviders.Host/FSharp.TypeProviders.Host.csproj @@ -26,7 +26,7 @@ - + @@ -39,4 +39,4 @@ - \ No newline at end of file + diff --git a/ReSharper.FSharp/src/FSharp/FSharp.Common/FSharp.Common.fsproj b/ReSharper.FSharp/src/FSharp/FSharp.Common/FSharp.Common.fsproj index 4e64e7f372..c483cf5296 100644 --- a/ReSharper.FSharp/src/FSharp/FSharp.Common/FSharp.Common.fsproj +++ b/ReSharper.FSharp/src/FSharp/FSharp.Common/FSharp.Common.fsproj @@ -73,7 +73,6 @@ - diff --git a/ReSharper.FSharp/src/FSharp/FSharp.ProjectModelBase/FSharp.ProjectModelBase.csproj b/ReSharper.FSharp/src/FSharp/FSharp.ProjectModelBase/FSharp.ProjectModelBase.csproj index 8675fb00d2..34437a84d5 100644 --- a/ReSharper.FSharp/src/FSharp/FSharp.ProjectModelBase/FSharp.ProjectModelBase.csproj +++ b/ReSharper.FSharp/src/FSharp/FSharp.ProjectModelBase/FSharp.ProjectModelBase.csproj @@ -16,7 +16,6 @@ - diff --git a/ReSharper.FSharp/src/FSharp/FSharp.Psi.Daemon/FSharp.Psi.Daemon.fsproj b/ReSharper.FSharp/src/FSharp/FSharp.Psi.Daemon/FSharp.Psi.Daemon.fsproj index d09f9474b0..19cda6c7ed 100644 --- a/ReSharper.FSharp/src/FSharp/FSharp.Psi.Daemon/FSharp.Psi.Daemon.fsproj +++ b/ReSharper.FSharp/src/FSharp/FSharp.Psi.Daemon/FSharp.Psi.Daemon.fsproj @@ -83,7 +83,6 @@ - @@ -92,4 +91,4 @@ - \ No newline at end of file + diff --git a/ReSharper.FSharp/src/FSharp/FSharp.Psi.Features/FSharp.Psi.Features.fsproj b/ReSharper.FSharp/src/FSharp/FSharp.Psi.Features/FSharp.Psi.Features.fsproj index b37f139a1d..3796339eec 100644 --- a/ReSharper.FSharp/src/FSharp/FSharp.Psi.Features/FSharp.Psi.Features.fsproj +++ b/ReSharper.FSharp/src/FSharp/FSharp.Psi.Features/FSharp.Psi.Features.fsproj @@ -124,7 +124,6 @@ - @@ -165,4 +164,4 @@ - \ No newline at end of file + diff --git a/ReSharper.FSharp/src/FSharp/FSharp.Psi.Intentions/FSharp.Psi.Intentions.fsproj b/ReSharper.FSharp/src/FSharp/FSharp.Psi.Intentions/FSharp.Psi.Intentions.fsproj index fc0dbc626e..f849ff1c12 100644 --- a/ReSharper.FSharp/src/FSharp/FSharp.Psi.Intentions/FSharp.Psi.Intentions.fsproj +++ b/ReSharper.FSharp/src/FSharp/FSharp.Psi.Intentions/FSharp.Psi.Intentions.fsproj @@ -142,7 +142,6 @@ - diff --git a/ReSharper.FSharp/src/FSharp/FSharp.Psi.Services/FSharp.Psi.Services.fsproj b/ReSharper.FSharp/src/FSharp/FSharp.Psi.Services/FSharp.Psi.Services.fsproj index 54176c82f2..dbdbdeb6f3 100644 --- a/ReSharper.FSharp/src/FSharp/FSharp.Psi.Services/FSharp.Psi.Services.fsproj +++ b/ReSharper.FSharp/src/FSharp/FSharp.Psi.Services/FSharp.Psi.Services.fsproj @@ -88,7 +88,6 @@ - @@ -97,4 +96,4 @@ - \ No newline at end of file + diff --git a/ReSharper.FSharp/src/FSharp/FSharp.Psi/FSharp.Psi.csproj b/ReSharper.FSharp/src/FSharp/FSharp.Psi/FSharp.Psi.csproj index 8689b3c193..ff44f3d6a7 100644 --- a/ReSharper.FSharp/src/FSharp/FSharp.Psi/FSharp.Psi.csproj +++ b/ReSharper.FSharp/src/FSharp/FSharp.Psi/FSharp.Psi.csproj @@ -32,7 +32,6 @@ - @@ -51,4 +50,4 @@ - \ No newline at end of file + diff --git a/ReSharper.FSharp/src/FSharp/FSharp.Tests.Host/FSharp.Tests.Host.fsproj b/ReSharper.FSharp/src/FSharp/FSharp.Tests.Host/FSharp.Tests.Host.fsproj index 9c4ca23083..1b060eee8a 100644 --- a/ReSharper.FSharp/src/FSharp/FSharp.Tests.Host/FSharp.Tests.Host.fsproj +++ b/ReSharper.FSharp/src/FSharp/FSharp.Tests.Host/FSharp.Tests.Host.fsproj @@ -21,7 +21,6 @@ - diff --git a/ReSharper.FSharp/src/FSharp/FSharp.TypeProviders.Protocol/FSharp.TypeProviders.Protocol.csproj b/ReSharper.FSharp/src/FSharp/FSharp.TypeProviders.Protocol/FSharp.TypeProviders.Protocol.csproj index 096eb79234..b215d4fd02 100644 --- a/ReSharper.FSharp/src/FSharp/FSharp.TypeProviders.Protocol/FSharp.TypeProviders.Protocol.csproj +++ b/ReSharper.FSharp/src/FSharp/FSharp.TypeProviders.Protocol/FSharp.TypeProviders.Protocol.csproj @@ -17,7 +17,6 @@ - diff --git a/ReSharper.FSharp/test/src/FSharp.Common.Tests/FSharp.Common.Tests.fsproj b/ReSharper.FSharp/test/src/FSharp.Common.Tests/FSharp.Common.Tests.fsproj index 94e775834b..40be2ad749 100644 --- a/ReSharper.FSharp/test/src/FSharp.Common.Tests/FSharp.Common.Tests.fsproj +++ b/ReSharper.FSharp/test/src/FSharp.Common.Tests/FSharp.Common.Tests.fsproj @@ -20,7 +20,6 @@ - diff --git a/ReSharper.FSharp/test/src/FSharp.Intentions.Tests/FSharp.Intentions.Tests.fsproj b/ReSharper.FSharp/test/src/FSharp.Intentions.Tests/FSharp.Intentions.Tests.fsproj index 641af1b8a8..4b9ffb53b9 100644 --- a/ReSharper.FSharp/test/src/FSharp.Intentions.Tests/FSharp.Intentions.Tests.fsproj +++ b/ReSharper.FSharp/test/src/FSharp.Intentions.Tests/FSharp.Intentions.Tests.fsproj @@ -142,7 +142,6 @@ - diff --git a/ReSharper.FSharp/test/src/FSharp.Tests.Common/FSharp.Tests.Common.fsproj b/ReSharper.FSharp/test/src/FSharp.Tests.Common/FSharp.Tests.Common.fsproj index 844bd07cf0..f5c57209df 100644 --- a/ReSharper.FSharp/test/src/FSharp.Tests.Common/FSharp.Tests.Common.fsproj +++ b/ReSharper.FSharp/test/src/FSharp.Tests.Common/FSharp.Tests.Common.fsproj @@ -24,7 +24,6 @@ - diff --git a/ReSharper.FSharp/test/src/FSharp.Tests/FSharp.Tests.fsproj b/ReSharper.FSharp/test/src/FSharp.Tests/FSharp.Tests.fsproj index c99878c9be..c5f7b82480 100644 --- a/ReSharper.FSharp/test/src/FSharp.Tests/FSharp.Tests.fsproj +++ b/ReSharper.FSharp/test/src/FSharp.Tests/FSharp.Tests.fsproj @@ -63,7 +63,6 @@ - From 5dfc5157414d58756ab9847e2b177e8ec6757ef3 Mon Sep 17 00:00:00 2001 From: nojaf Date: Fri, 16 Feb 2024 10:29:53 +0100 Subject: [PATCH 04/22] FcsProjectBuilder with Snapshot --- .../src/Checker/FSharpCheckerExtensions.fs | 16 ++-- .../src/Checker/FcsCheckerService.fs | 16 ++-- .../src/Checker/FcsProjectBuilder.fs | 81 ++++++++++++------- .../src/Checker/FcsProjectProvider.fs | 16 ++-- .../src/Checker/ScriptFcsProjectProvider.fs | 11 +-- .../src/FSharp.Tests.Common/src/Common.fs | 2 +- 6 files changed, 88 insertions(+), 54 deletions(-) diff --git a/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FSharpCheckerExtensions.fs b/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FSharpCheckerExtensions.fs index bd4123fd41..c93bf35b94 100644 --- a/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FSharpCheckerExtensions.fs +++ b/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FSharpCheckerExtensions.fs @@ -56,13 +56,17 @@ type FSharpChecker with return StillRunning t } - let bindParsedInput(results: (FSharpParseFileResults * FSharpCheckFileResults) option) = - match results with - | Some(parseResults, checkResults) -> - Some (parseResults, checkResults) - | _ -> None + // let bindParsedInput(results: (FSharpParseFileResults * FSharpCheckFileResults) option) = + // match results with + // | Some(parseResults, checkResults) -> + // Some (parseResults, checkResults) + // | _ -> None + + // Additional map seems weird here? + // map bindParsedInput + + parseAndCheckFile - map bindParsedInput parseAndCheckFile // async { // // TODO: TryGetRecentCheckResultsForFile is missing on the Transparent compiler // match x.TryGetRecentCheckResultsForFile(path, options, source) with diff --git a/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsCheckerService.fs b/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsCheckerService.fs index dd03ca5065..93fe4885d2 100644 --- a/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsCheckerService.fs +++ b/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsCheckerService.fs @@ -223,15 +223,16 @@ type FcsCheckerService(lifetime: Lifetime, logger: ILogger, onSolutionCloseNotif checker.Value.GetCachedScriptOptions(path) else None - member x.InvalidateFcsProject(projectOptions: FSharpProjectOptions, invalidationType: FcsProjectInvalidationType) = + member x.InvalidateFcsProject(projectSnapshot: FSharpProjectSnapshot, invalidationType: FcsProjectInvalidationType) = if checker.IsValueCreated then match invalidationType with | FcsProjectInvalidationType.Invalidate -> - logger.Trace("Remove FcsProject in FCS: {0}", projectOptions.ProjectFileName) - checker.Value.ClearCache(Seq.singleton projectOptions) + logger.Trace("Remove FcsProject in FCS: {0}", projectSnapshot.ProjectFileName) + checker.Value.ClearCache(Seq.singleton projectSnapshot.Identifier) | FcsProjectInvalidationType.Remove -> - logger.Trace("Invalidate FcsProject in FCS: {0}", projectOptions.ProjectFileName) - checker.Value.InvalidateConfiguration(projectOptions) + logger.Trace("Invalidate FcsProject in FCS: {0}", projectSnapshot.ProjectFileName) + // See: https://github.com/dotnet/fsharp/pull/16718 + checker.Value.InvalidateConfiguration(projectSnapshot) /// Use with care: returns wrong symbol inside its non-recursive declaration, see dotnet/fsharp#7694. member x.ResolveNameAtLocation(sourceFile: IPsiSourceFile, names, coords, resolveExpr: bool, opName) = @@ -300,8 +301,9 @@ type IFcsProjectProvider = type IScriptFcsProjectProvider = abstract GetFcsProject: IPsiSourceFile -> FcsProject option - abstract GetScriptOptions: IPsiSourceFile -> FSharpProjectOptions option - abstract GetScriptOptions: VirtualFileSystemPath * string -> FSharpProjectOptions option + abstract GetScriptSnapshot: IPsiSourceFile -> FSharpProjectSnapshot option + // TODO: unused? + // abstract GetScriptOptions: VirtualFileSystemPath * string -> FSharpProjectOptions option abstract OptionsUpdated: Signal abstract SyncUpdate: bool diff --git a/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsProjectBuilder.fs b/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsProjectBuilder.fs index abd141e22b..c68de2644d 100644 --- a/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsProjectBuilder.fs +++ b/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsProjectBuilder.fs @@ -1,7 +1,10 @@ namespace JetBrains.ReSharper.Plugins.FSharp.Checker +#nowarn "57" + open System open System.Collections.Generic +open System.Threading.Tasks open FSharp.Compiler.CodeAnalysis open JetBrains.Application open JetBrains.Application.BuildScript.Application.Zones @@ -193,26 +196,61 @@ type FcsProjectBuilder(checkerService: FcsCheckerService, itemsContainer: IFShar let fileIndices = Dictionary() Array.iteri (fun i p -> fileIndices[p] <- i) filePaths - let projectOptions = - { ProjectFileName = $"{project.ProjectFileLocation}.{targetFrameworkId}.fsproj" - ProjectId = None - SourceFiles = Array.map (fun (p: VirtualFileSystemPath ) -> p.FullPath) filePaths - OtherOptions = otherOptions.ToArray() - ReferencedProjects = Array.empty - IsIncompleteTypeCheckEnvironment = false - UseScriptResolutionRules = false - LoadTime = DateTime.Now - OriginalLoadReferences = List.empty - UnresolvedReferences = None - Stamp = None } + let psiModule = psiModules.GetPrimaryPsiModule(project, targetFrameworkId) + + let sourceFiles = + psiModule.SourceFiles + |> Seq.map (fun psiSourceFile -> + // TODO: I assume the Create will expect the full path of the file? + let name = psiSourceFile.Name + let version = string psiSourceFile.Document.LastModificationStamp.Value + let getSource () = psiSourceFile.Document.GetText() |> FSharp.Compiler.Text.SourceTextNew.ofString |> Task.FromResult + ProjectSnapshot.FSharpFileSnapshot.Create(name, version, getSource) + ) + |> Seq.toList + + let references = projectKey.Project.GetModuleReferences(projectKey.TargetFrameworkId) + let referencesOnDisk: ProjectSnapshot.ReferenceOnDisk list = + references + |> Seq.choose (fun projectToModuleReference -> + projectToModuleReference + |> modulePathProvider.GetModulePath + |> Option.bind (fun path -> + if path.IsEmpty then + None + else + Some ({ + Path = path.FullPath + LastModified = path.FileModificationTimeUtc + } : ProjectSnapshot.ReferenceOnDisk)) + ) + |> Seq.toList + + let otherOptions = Seq.toList otherOptions + + let projectSnapshot = + FSharpProjectSnapshot.Create( + projectFileName = $"{project.ProjectFileLocation}.{targetFrameworkId}.fsproj", + projectId = None, + sourceFiles = sourceFiles, + referencesOnDisk = referencesOnDisk, + otherOptions = Seq.toList otherOptions, + referencedProjects = List.empty, + isIncompleteTypeCheckEnvironment = false, + useScriptResolutionRules = false, + loadTime = DateTime.Now, + unresolvedReferences = None, + originalLoadReferences = List.empty, + stamp = None + ) let parsingOptions, errors = - checkerService.Checker.GetParsingOptionsFromCommandLineArgs(List.ofArray projectOptions.OtherOptions) + checkerService.Checker.GetParsingOptionsFromCommandLineArgs(otherOptions) let defines = ImplicitDefines.sourceDefines @ parsingOptions.ConditionalDefines let parsingOptions = { parsingOptions with - SourceFiles = projectOptions.SourceFiles + SourceFiles = sourceFiles |> List.map (fun sf -> sf.FileName) |> Array.ofList ConditionalDefines = defines } if not errors.IsEmpty then @@ -220,21 +258,10 @@ type FcsProjectBuilder(checkerService: FcsCheckerService, itemsContainer: IFShar let fcsProject = { OutputPath = outPath - ProjectOptions = projectOptions + ProjectSnapshot = projectSnapshot ParsingOptions = parsingOptions FileIndices = fileIndices ImplementationFilesWithSignatures = implsWithSig ReferencedModules = HashSet() } - - let references = projectKey.Project.GetModuleReferences(projectKey.TargetFrameworkId) - let paths = - references - |> Array.ofSeq - |> Array.choose modulePathProvider.GetModulePath - |> Array.choose (fun path -> if path.IsEmpty then None else Some("-r:" + path.FullPath)) - - let projectOptions = - { fcsProject.ProjectOptions with - OtherOptions = Array.append fcsProject.ProjectOptions.OtherOptions paths } - { fcsProject with ProjectOptions = projectOptions } + fcsProject diff --git a/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsProjectProvider.fs b/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsProjectProvider.fs index e7a7aef2b2..db1df06ec8 100644 --- a/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsProjectProvider.fs +++ b/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsProjectProvider.fs @@ -144,7 +144,7 @@ type FcsProjectProvider(lifetime: Lifetime, solution: ISolution, changeManager: | _ -> false ) - loop newProject.ProjectOptions oldProject.ProjectOptions + loop newProject.ProjectSnapshot oldProject.ProjectSnapshot let tryGetFcsProject (psiModule: IPsiModule): FcsProject option = locks.AssertReadAccessAllowed() @@ -282,9 +282,9 @@ type FcsProjectProvider(lifetime: Lifetime, solution: ISolution, changeManager: | :? FSharpScriptPsiModule as scriptModule -> let path = scriptModule.Path let sourceFile = scriptModule.SourceFile - match scriptFcsProjectProvider.GetScriptOptions(sourceFile) with + match scriptFcsProjectProvider.GetScriptSnapshot(sourceFile) with | None -> None - | Some projectOptions -> + | Some projectSnapshot -> // let snapshot = FSharpProjectSnapshot.FromOptions projectOptions @@ -298,7 +298,7 @@ type FcsProjectProvider(lifetime: Lifetime, solution: ISolution, changeManager: let indices = Dictionary() { OutputPath = path - ProjectOptions = projectOptions + ProjectSnapshot = projectSnapshot ParsingOptions = parsingOptions FileIndices = indices ImplementationFilesWithSignatures = EmptySet.Instance @@ -486,18 +486,18 @@ type FcsProjectProvider(lifetime: Lifetime, solution: ISolution, changeManager: let psiModule = sourceFile.PsiModule match psiModule with | :? FSharpScriptPsiModule -> - scriptFcsProjectProvider.GetScriptOptions(sourceFile) + scriptFcsProjectProvider.GetScriptSnapshot(sourceFile) | :? SandboxPsiModule -> let settings = sourceFile.GetSettingsStore() if not (settings.GetValue(fun (s: FSharpExperimentalFeatures) -> s.FsiInteractiveEditor)) then None else - scriptFcsProjectProvider.GetScriptOptions(sourceFile) + scriptFcsProjectProvider.GetScriptSnapshot(sourceFile) | _ -> match tryGetFcsProject psiModule with - | Some fcsProject when fcsProject.IsKnownFile(sourceFile) -> Some fcsProject.ProjectOptions + | Some fcsProject when fcsProject.IsKnownFile(sourceFile) -> Some fcsProject.ProjectSnapshot | _ -> None member x.GetProjectOptions(psiModule: IPsiModule) = @@ -505,7 +505,7 @@ type FcsProjectProvider(lifetime: Lifetime, solution: ISolution, changeManager: processInvalidatedFcsProjects () match tryGetFcsProject psiModule with - | Some fcsProject -> Some fcsProject.ProjectOptions + | Some fcsProject -> Some fcsProject.ProjectSnapshot | _ -> None member x.HasPairFile(sourceFile) = diff --git a/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/ScriptFcsProjectProvider.fs b/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/ScriptFcsProjectProvider.fs index 9d1a95aa9b..039bcc1004 100644 --- a/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/ScriptFcsProjectProvider.fs +++ b/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/ScriptFcsProjectProvider.fs @@ -205,14 +205,15 @@ type ScriptFcsProjectProvider(lifetime: Lifetime, logger: ILogger, checkerServic else getDefaultOptions path |> createFcsProject path - let getOptions path source : FSharpProjectOptions option = - getFcsProject path source true |> Option.map (fun fcsProject -> fcsProject.ProjectOptions) + let getOptions path source : FSharpProjectSnapshot option = + getFcsProject path source true |> Option.map (fun fcsProject -> fcsProject.ProjectSnapshot) interface IScriptFcsProjectProvider with - member x.GetScriptOptions(path: VirtualFileSystemPath, source) = - getOptions path source + // TODO: unused ? + // member x.GetScriptOptions(path: VirtualFileSystemPath, source) = + // getOptions path source - member x.GetScriptOptions(file: IPsiSourceFile) = + member x.GetScriptSnapshot(file: IPsiSourceFile) = let path = file.GetLocation() let source = file.Document.GetText() getOptions path source diff --git a/ReSharper.FSharp/test/src/FSharp.Tests.Common/src/Common.fs b/ReSharper.FSharp/test/src/FSharp.Tests.Common/src/Common.fs index facb10136a..e3a81d27fd 100644 --- a/ReSharper.FSharp/test/src/FSharp.Tests.Common/src/Common.fs +++ b/ReSharper.FSharp/test/src/FSharp.Tests.Common/src/Common.fs @@ -271,7 +271,7 @@ type TestFcsProjectProvider(lifetime: Lifetime, checkerService: FcsCheckerServic member x.GetProjectOptions(sourceFile: IPsiSourceFile) = if sourceFile.LanguageType.Is() then - scriptFcsProjectProvider.GetScriptOptions(sourceFile) else + scriptFcsProjectProvider.GetScriptSnapshot(sourceFile) else getProjectOptions sourceFile From 09c897f1bf90b233fbd4ce2cc3c95d43c1b92107 Mon Sep 17 00:00:00 2001 From: nojaf Date: Fri, 16 Feb 2024 11:47:49 +0100 Subject: [PATCH 05/22] Use snapshot in FcsProjectProvider --- .../src/Checker/FcsProjectProvider.fs | 61 ++++++++++++++----- 1 file changed, 47 insertions(+), 14 deletions(-) diff --git a/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsProjectProvider.fs b/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsProjectProvider.fs index db1df06ec8..b8e5fa1973 100644 --- a/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsProjectProvider.fs +++ b/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsProjectProvider.fs @@ -1,10 +1,13 @@ namespace JetBrains.ReSharper.Plugins.FSharp.Checker +#nowarn "57" + open System.Collections.Concurrent open System.Collections.Generic open System.IO open FSharp.Compiler.AbstractIL.ILBinaryReader open FSharp.Compiler.CodeAnalysis +open FSharp.Compiler.CodeAnalysis.ProjectSnapshot open JetBrains.Annotations open JetBrains.Application.BuildScript.Application.Zones open JetBrains.Application.Settings @@ -128,22 +131,22 @@ type FcsProjectProvider(lifetime: Lifetime, solution: ISolution, changeManager: projectsToProjectKeys.Remove(projectKey.Project, projectKey) |> ignore let areSameForChecking (newProject: FcsProject) (oldProject: FcsProject) = - let rec loop (newOptions: FSharpProjectOptions) (oldOptions: FSharpProjectOptions) = + let rec loop (newOptions: FSharpProjectSnapshot) (oldOptions: FSharpProjectSnapshot) = newOptions.ProjectFileName = oldOptions.ProjectFileName && newOptions.SourceFiles = oldOptions.SourceFiles && newOptions.OtherOptions = oldOptions.OtherOptions && newOptions.ReferencedProjects.Length = oldOptions.ReferencedProjects.Length && (newOptions.ReferencedProjects, oldOptions.ReferencedProjects) - ||> Array.forall2 (fun r1 r2 -> + ||> List.forall2 (fun r1 r2 -> match r1, r2 with - | FSharpReferencedProject.FSharpReference (_, r1), - FSharpReferencedProject.FSharpReference (_, r2) -> loop r1 r2 - | FSharpReferencedProject.ILModuleReference(_, _, getReader1), - FSharpReferencedProject.ILModuleReference(_, _, getReader2) -> getReader1 () = getReader2 () + | FSharpReferencedProjectSnapshot.FSharpReference (_, r1), + FSharpReferencedProjectSnapshot.FSharpReference (_, r2) -> loop r1 r2 + | FSharpReferencedProjectSnapshot.ILModuleReference(_, _, getReader1), + FSharpReferencedProjectSnapshot.ILModuleReference(_, _, getReader2) -> getReader1 () = getReader2 () | _ -> false ) - + loop newProject.ProjectSnapshot oldProject.ProjectSnapshot let tryGetFcsProject (psiModule: IPsiModule): FcsProject option = @@ -192,7 +195,22 @@ type FcsProjectProvider(lifetime: Lifetime, solution: ISolution, changeManager: | None -> let stamp = Some(getNextStamp ()) - let fcsProject = { fcsProject with ProjectOptions = { fcsProject.ProjectOptions with Stamp = stamp } } + let updatedSnapshot = + FSharpProjectSnapshot.Create( + fcsProject.ProjectSnapshot.ProjectFileName, + fcsProject.ProjectSnapshot.ProjectId, + fcsProject.ProjectSnapshot.SourceFiles, + fcsProject.ProjectSnapshot.ReferencesOnDisk, + fcsProject.ProjectSnapshot.OtherOptions, + fcsProject.ProjectSnapshot.ReferencedProjects, + fcsProject.ProjectSnapshot.IsIncompleteTypeCheckEnvironment, + fcsProject.ProjectSnapshot.UseScriptResolutionRules, + fcsProject.ProjectSnapshot.LoadTime, + fcsProject.ProjectSnapshot.UnresolvedReferences, + fcsProject.ProjectSnapshot.OriginalLoadReferences, + stamp + ) + let fcsProject = { fcsProject with ProjectSnapshot = updatedSnapshot } if logger.IsTraceEnabled() then use writer = new StringWriter() @@ -249,24 +267,39 @@ type FcsProjectProvider(lifetime: Lifetime, solution: ISolution, changeManager: if isFSharpProject referencedProject then let referencedFcsProject = getOrCreateFcsProject referencedProjectKey let path = referencedFcsProject.OutputPath.FullPath - Some(FSharpReferencedProject.FSharpReference(path, referencedFcsProject.ProjectOptions)) + Some(FSharpReferencedProjectSnapshot.FSharpReference(path, referencedFcsProject.ProjectSnapshot)) elif fcsAssemblyReaderShim.IsEnabled && AssemblyReaderShim.isSupportedProject referencedProject then fcsAssemblyReaderShim.TryGetModuleReader(referencedProjectKey) |> Option.map (fun reader -> let getTimestamp () = reader.Timestamp let getReader () = reader :> ILModuleReader - FSharpReferencedProject.ILModuleReference(reader.Path.FullPath, getTimestamp, getReader) + FSharpReferencedProjectSnapshot.ILModuleReference(reader.Path.FullPath, getTimestamp, getReader) ) else None ) - |> Seq.toArray + |> Seq.toList fcsProject.ReferencedModules.AddRange(moduleReferences |> Seq.choose tryGetReferencedProject) - let optionsWithReferences = { fcsProject.ProjectOptions with ReferencedProjects = referencedFcsProjects } - let fcsProject = { fcsProject with ProjectOptions = optionsWithReferences } + let updatedSnapshot = + FSharpProjectSnapshot.Create( + fcsProject.ProjectSnapshot.ProjectFileName, + fcsProject.ProjectSnapshot.ProjectId, + fcsProject.ProjectSnapshot.SourceFiles, + fcsProject.ProjectSnapshot.ReferencesOnDisk, + fcsProject.ProjectSnapshot.OtherOptions, + referencedFcsProjects, + fcsProject.ProjectSnapshot.IsIncompleteTypeCheckEnvironment, + fcsProject.ProjectSnapshot.UseScriptResolutionRules, + fcsProject.ProjectSnapshot.LoadTime, + fcsProject.ProjectSnapshot.UnresolvedReferences, + fcsProject.ProjectSnapshot.OriginalLoadReferences, + fcsProject.ProjectSnapshot.Stamp + ) + + let fcsProject = { fcsProject with ProjectSnapshot = updatedSnapshot } if projectKey <> initialProjectKey then addProject projectKey fcsProject |> ignore @@ -401,7 +434,7 @@ type FcsProjectProvider(lifetime: Lifetime, solution: ISolution, changeManager: if invalidated.Contains(fcsProjectToInvalidate) then () else let fcsProject, invalidationType = fcsProjectToInvalidate - checkerService.InvalidateFcsProject(fcsProject.ProjectOptions, invalidationType) + checkerService.InvalidateFcsProject(fcsProject.ProjectSnapshot, invalidationType) invalidated.Add(fcsProjectToInvalidate) |> ignore ) From 902d77885d24ee14dd8579d1c287b2e9cc9f8cbb Mon Sep 17 00:00:00 2001 From: nojaf Date: Fri, 16 Feb 2024 14:12:54 +0100 Subject: [PATCH 06/22] More WIP, try and use snapshot for script stuff. --- .../src/Checker/FcsCheckerService.fs | 32 ++--- .../src/Checker/FcsProjectProvider.fs | 2 - .../src/Checker/ScriptFcsProjectProvider.fs | 115 +++++++++++------- .../FSharpScriptPsiModuleFactory.fs | 24 ++-- .../FSharp.Tests.Host/FSharpTestHost.fs | 4 +- 5 files changed, 103 insertions(+), 74 deletions(-) diff --git a/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsCheckerService.fs b/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsCheckerService.fs index 93fe4885d2..358b51ae98 100644 --- a/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsCheckerService.fs +++ b/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsCheckerService.fs @@ -202,25 +202,27 @@ type FcsCheckerService(lifetime: Lifetime, logger: ILogger, onSolutionCloseNotif | _ -> { new IDisposable with member this.Dispose() = () } member x.TryGetStaleCheckResults([] file: IPsiSourceFile, opName) = - match x.FcsProjectProvider.GetProjectOptions(file) with + match x.FcsProjectProvider.GetProjectSnapshot(file) with | None -> None - | Some options -> + | Some snapshot -> let path = file.GetLocation().FullPath logger.Trace("TryGetStaleCheckResults: start {0}, {1}", path, opName) - match x.Checker.TryGetRecentCheckResultsForFile(path, options) with - | Some (_, checkResults, _) -> - logger.Trace("TryGetStaleCheckResults: finish {0}, {1}", path, opName) - Some checkResults - - | _ -> - logger.Trace("TryGetStaleCheckResults: fail {0}, {1}", path, opName) - None + // TODO: TryGetRecentCheckResultsForFile for Snapshot + Unchecked.defaultof + // match x.Checker.TryGetRecentCheckResultsForFile(path, snapshot) with + // | Some (_, checkResults, _) -> + // logger.Trace("TryGetStaleCheckResults: finish {0}, {1}", path, opName) + // Some checkResults + // + // | _ -> + // logger.Trace("TryGetStaleCheckResults: fail {0}, {1}", path, opName) + // None - member x.GetCachedScriptOptions(path) = + member x.GetCachedScriptSnapshot(path) = if checker.IsValueCreated then - checker.Value.GetCachedScriptOptions(path) + checker.Value.GetCachedScriptSnapshot(path) else None member x.InvalidateFcsProject(projectSnapshot: FSharpProjectSnapshot, invalidationType: FcsProjectInvalidationType) = @@ -275,8 +277,8 @@ type IFcsProjectProvider = abstract GetFcsProject: psiModule: IPsiModule -> FcsProject option abstract GetPsiModule: outputPath: VirtualFileSystemPath -> IPsiModule option - abstract GetProjectOptions: sourceFile: IPsiSourceFile -> FSharpProjectOptions option - abstract GetProjectOptions: psiModule: IPsiModule -> FSharpProjectOptions option + abstract GetProjectSnapshot: sourceFile: IPsiSourceFile -> FSharpProjectSnapshot option + abstract GetProjectSnapshot: psiModule: IPsiModule -> FSharpProjectSnapshot option abstract GetFileIndex: IPsiSourceFile -> int abstract GetParsingOptions: sourceFile: IPsiSourceFile -> FSharpParsingOptions @@ -304,7 +306,7 @@ type IScriptFcsProjectProvider = abstract GetScriptSnapshot: IPsiSourceFile -> FSharpProjectSnapshot option // TODO: unused? // abstract GetScriptOptions: VirtualFileSystemPath * string -> FSharpProjectOptions option - abstract OptionsUpdated: Signal + abstract SnapshotUpdated: Signal abstract SyncUpdate: bool diff --git a/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsProjectProvider.fs b/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsProjectProvider.fs index b8e5fa1973..f1bfc1c0ec 100644 --- a/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsProjectProvider.fs +++ b/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsProjectProvider.fs @@ -319,8 +319,6 @@ type FcsProjectProvider(lifetime: Lifetime, solution: ISolution, changeManager: | None -> None | Some projectSnapshot -> - // let snapshot = FSharpProjectSnapshot.FromOptions projectOptions - let parsingOptions = { FSharpParsingOptions.Default with SourceFiles = [| sourceFile.GetLocation().FullPath |] diff --git a/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/ScriptFcsProjectProvider.fs b/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/ScriptFcsProjectProvider.fs index 039bcc1004..b66e70ad5f 100644 --- a/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/ScriptFcsProjectProvider.fs +++ b/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/ScriptFcsProjectProvider.fs @@ -1,7 +1,10 @@ namespace JetBrains.ReSharper.Plugins.FSharp.Checker +#nowarn "57" + open System open System.Collections.Generic +open System.Threading.Tasks open FSharp.Compiler.CodeAnalysis open FSharp.Compiler.Text open JetBrains.DataFlow @@ -24,13 +27,13 @@ type ScriptFcsProjectProvider(lifetime: Lifetime, logger: ILogger, checkerServic let scriptFcsProjects = Dictionary() - let mutable defaultOptions: FSharpProjectOptions option option = None + let mutable defaultSnapshot: FSharpProjectSnapshot option option = None let currentRequests = HashSet() let dirtyPaths = HashSet() - let optionsUpdated = - new Signal("ScriptFcsProjectProvider.optionsUpdated") + let snapshotUpdated = + new Signal("ScriptFcsProjectProvider.optionsUpdated") let isHeadless = let var = Environment.GetEnvironmentVariable("JET_HEADLESS_MODE") |> Option.ofObj |> Option.defaultValue "false" @@ -63,30 +66,33 @@ type ScriptFcsProjectProvider(lifetime: Lifetime, logger: ILogger, checkerServic IPropertyEx.FlowInto(languageVersion, lifetime, flags, getOtherFlags) flags - let getOptionsImpl (path: VirtualFileSystemPath) source = + let getSnapshotImpl (path: VirtualFileSystemPath) source : FSharpProjectSnapshot option = let path = path.FullPath let source = SourceText.ofString source let targetNetFramework = not PlatformUtil.IsRunningOnCore && scriptSettings.TargetNetFramework.Value let toolset = toolset.GetDotNetCoreToolset() - let getScriptOptionsAsync = + let getScriptSnapshotAsync: Async = if isNotNull toolset && isNotNull toolset.Sdk then let sdkRootFolder = toolset.Cli.NotNull("cli").SdkRootFolder.NotNull("sdkRootFolder") let sdkFolderPath = sdkRootFolder / toolset.Sdk.NotNull("sdk").FolderName.NotNull("sdkFolderName") - checkerService.Checker.GetProjectOptionsFromScript(path, source, - otherFlags = otherFlags.Value.Value, - assumeDotNetFramework = targetNetFramework, - sdkDirOverride = sdkFolderPath.FullPath) + // TODO: GetProjectOptionsFromScript does not exists for Snapshot + Unchecked.defaultof> + // checkerService.Checker.GetProjectOptionsFromScript(path, source, + // otherFlags = otherFlags.Value.Value, + // assumeDotNetFramework = targetNetFramework, + // sdkDirOverride = sdkFolderPath.FullPath) else - checkerService.Checker.GetProjectOptionsFromScript(path, source, - otherFlags = otherFlags.Value.Value, - assumeDotNetFramework = targetNetFramework) + Unchecked.defaultof> + // checkerService.Checker.GetProjectOptionsFromScript(path, source, + // otherFlags = otherFlags.Value.Value, + // assumeDotNetFramework = targetNetFramework) try - let options, errors = getScriptOptionsAsync.RunAsTask() + let snapshot, errors = getScriptSnapshotAsync.RunAsTask() if not errors.IsEmpty then logErrors logger (sprintf "Script options for %s" path) errors - Some options + Some snapshot with | OperationCanceled -> reraise() | exn -> @@ -94,29 +100,50 @@ type ScriptFcsProjectProvider(lifetime: Lifetime, logger: ILogger, checkerServic logger.LogExceptionSilently(exn) None - let getDefaultOptions (path: VirtualFileSystemPath) = - let withPath (options: FSharpProjectOptions option) = - match options with - | Some options -> Some { options with SourceFiles = [| path.FullPath |] } - | _ -> None - - match defaultOptions with + let getDefaultSnapshot (path: VirtualFileSystemPath) (source: string): ProjectSnapshot.FSharpProjectSnapshot option = + let withPath (snapshot: FSharpProjectSnapshot option) = + snapshot + |> Option.map (fun snapshot -> + let name = path.Name + let version = string path.Info.ModificationTimeUtc.Ticks + let getSource () = SourceTextNew.ofString source |> Task.FromResult + let sourceFiles = + ProjectSnapshot.FSharpFileSnapshot.Create(name, version, getSource) + |> List.singleton + + FSharpProjectSnapshot.Create( + snapshot.ProjectFileName, + snapshot.ProjectId, + sourceFiles, + snapshot.ReferencesOnDisk, + snapshot.OtherOptions, + snapshot.ReferencedProjects, + snapshot.IsIncompleteTypeCheckEnvironment, + snapshot.UseScriptResolutionRules, + snapshot.LoadTime, + snapshot.UnresolvedReferences, + snapshot.OriginalLoadReferences, + snapshot.Stamp + ) + ) + + match defaultSnapshot with | Some options -> withPath options | _ -> lock defaultOptionsLock (fun _ -> - match defaultOptions with + match defaultSnapshot with | Some options -> withPath options | _ -> - let newOptions = getOptionsImpl path "" - defaultOptions <- Some newOptions + let newOptions = getSnapshotImpl path "" + defaultSnapshot <- Some newOptions newOptions ) - let createFcsProject (path: VirtualFileSystemPath) options = - options - |> Option.map (fun options -> + let createFcsProject (path: VirtualFileSystemPath) snapshot = + snapshot + |> Option.map (fun snapshot -> let parsingOptions = { FSharpParsingOptions.Default with SourceFiles = [| path.FullPath |] @@ -125,7 +152,7 @@ type ScriptFcsProjectProvider(lifetime: Lifetime, logger: ILogger, checkerServic IsExe = true } { OutputPath = path - ProjectOptions = options + ProjectSnapshot = snapshot ParsingOptions = parsingOptions FileIndices = dict [path, 0] ImplementationFilesWithSignatures = EmptySet.Instance @@ -143,11 +170,11 @@ type ScriptFcsProjectProvider(lifetime: Lifetime, logger: ILogger, checkerServic currentRequests.Add(path) |> ignore dirtyPaths.Remove(path) |> ignore let oldOptions = tryGetValue path scriptFcsProjects |> Option.bind id - let newOptions = getOptionsImpl path source + let newSnapshot = getSnapshotImpl path source scriptFcsProjects[path] <- - newOptions - |> Option.map (fun options -> + newSnapshot + |> Option.map (fun snapshot -> let parsingOptions = { FSharpParsingOptions.Default with SourceFiles = [| path.FullPath |] @@ -158,27 +185,27 @@ type ScriptFcsProjectProvider(lifetime: Lifetime, logger: ILogger, checkerServic let indices = Dictionary() { OutputPath = path - ProjectOptions = options + ProjectSnapshot = snapshot ParsingOptions = parsingOptions FileIndices = indices ImplementationFilesWithSignatures = EmptySet.Instance ReferencedModules = EmptySet.Instance } ) - match oldOptions, newOptions with - | Some oldOptions, Some newOptions -> - let areEqualForChecking (options1: FSharpProjectOptions) (options2: FSharpProjectOptions) = - let arrayEq a1 a2 = - Array.length a1 = Array.length a2 && Array.forall2 (=) a1 a2 + match oldOptions, newSnapshot with + | Some oldOptions, Some newSnapshot -> + let areEqualForChecking (options1: FSharpProjectSnapshot) (options2: FSharpProjectSnapshot) = + let listEq l1 l2 = + List.length l1 = List.length l2 && List.forall2 (=) l1 l2 - arrayEq options1.OtherOptions options2.OtherOptions && - arrayEq options1.SourceFiles options2.SourceFiles + listEq options1.OtherOptions options2.OtherOptions && + listEq options1.SourceFiles options2.SourceFiles - if not (areEqualForChecking oldOptions.ProjectOptions newOptions) then - optionsUpdated.Fire((path, newOptions)) + if not (areEqualForChecking oldOptions.ProjectSnapshot newSnapshot) then + snapshotUpdated.Fire((path, newSnapshot)) | _, Some newOptions -> - optionsUpdated.Fire((path, newOptions)) + snapshotUpdated.Fire((path, newOptions)) | _ -> () @@ -203,7 +230,7 @@ type ScriptFcsProjectProvider(lifetime: Lifetime, logger: ILogger, checkerServic if isHeadless && allowRetry then getFcsProject path source false else - getDefaultOptions path |> createFcsProject path + getDefaultSnapshot path source |> createFcsProject path let getOptions path source : FSharpProjectSnapshot option = getFcsProject path source true |> Option.map (fun fcsProject -> fcsProject.ProjectSnapshot) @@ -223,5 +250,5 @@ type ScriptFcsProjectProvider(lifetime: Lifetime, logger: ILogger, checkerServic let source = sourceFile.Document.GetText() getFcsProject path source true - member this.OptionsUpdated = optionsUpdated + member this.SnapshotUpdated = snapshotUpdated member this.SyncUpdate = isHeadless diff --git a/ReSharper.FSharp/src/FSharp/FSharp.Common/src/ProjectModel/FSharpScriptPsiModuleFactory.fs b/ReSharper.FSharp/src/FSharp/FSharp.Common/src/ProjectModel/FSharpScriptPsiModuleFactory.fs index 2c049d4689..3e1f7d4a9d 100644 --- a/ReSharper.FSharp/src/FSharp/FSharp.Common/src/ProjectModel/FSharpScriptPsiModuleFactory.fs +++ b/ReSharper.FSharp/src/FSharp/FSharp.Common/src/ProjectModel/FSharpScriptPsiModuleFactory.fs @@ -1,5 +1,7 @@ namespace rec JetBrains.ReSharper.Plugins.FSharp.ProjectModel.Scripts +#nowarn "57" + open System open System.Collections.Generic open System.IO @@ -64,16 +66,16 @@ type FSharpScriptPsiModulesProvider(lifetime: Lifetime, solution: ISolution, cha let platformInfo = platformInfos |> Seq.maxBy (fun info -> info.TargetFrameworkId.Version) platformInfo.TargetFrameworkId - let getScriptReferences (scriptPath: VirtualFileSystemPath) scriptOptions = + let getScriptReferences (scriptPath: VirtualFileSystemPath) (scriptSnapshot: FSharpProjectSnapshot) = let assembliesPaths = HashSet() - for o in scriptOptions.OtherOptions do + for o in scriptSnapshot.OtherOptions do if o.StartsWith("-r:", StringComparison.Ordinal) then let path = VirtualFileSystemPath.TryParse(o.Substring(3), InteractionContext.SolutionContext) if not path.IsEmpty then assembliesPaths.Add(path) |> ignore let filesPaths = HashSet() - for file in scriptOptions.SourceFiles do - let path = VirtualFileSystemPath.TryParse(file, InteractionContext.SolutionContext) + for file in scriptSnapshot.SourceFiles do + let path = VirtualFileSystemPath.TryParse(file.FileName, InteractionContext.SolutionContext) if not path.IsEmpty && not (path.Equals(scriptPath)) then filesPaths.Add(path) |> ignore @@ -148,7 +150,7 @@ type FSharpScriptPsiModulesProvider(lifetime: Lifetime, solution: ISolution, cha locks.QueueReadLock(lifetime, "AssemblyGC after removing F# script reference", fun _ -> solution.GetComponent().ForceGC()) - and queueUpdateReferences (path: VirtualFileSystemPath) (newOptions: FSharpProjectOptions) = + and queueUpdateReferences (path: VirtualFileSystemPath) (newSnapshot: FSharpProjectSnapshot) = locks.QueueReadLock(lifetime, "Request new F# script references", fun _ -> let oldReferences = let mutable oldReferences = Unchecked.defaultof @@ -161,7 +163,7 @@ type FSharpScriptPsiModulesProvider(lifetime: Lifetime, solution: ISolution, cha ira.FuncRun <- fun _ -> - let newReferences = getScriptReferences path newOptions + let newReferences = getScriptReferences path newSnapshot Interruption.Current.CheckAndThrow() let getDiff oldPaths newPaths = @@ -181,7 +183,7 @@ type FSharpScriptPsiModulesProvider(lifetime: Lifetime, solution: ISolution, cha ira.FuncCancelled <- // Reschedule again - fun _ -> queueUpdateReferences path newOptions + fun _ -> queueUpdateReferences path newSnapshot ira.DoStart() |> ignore ) @@ -190,7 +192,7 @@ type FSharpScriptPsiModulesProvider(lifetime: Lifetime, solution: ISolution, cha changeManager.RegisterChangeProvider(lifetime, this) if not scriptOptionsProvider.SyncUpdate then - scriptOptionsProvider.OptionsUpdated.Advise(lifetime, fun (path, options) -> + scriptOptionsProvider.SnapshotUpdated.Advise(lifetime, fun (path, options) -> queueUpdateReferences path options ) @@ -230,7 +232,7 @@ type FSharpScriptPsiModulesProvider(lifetime: Lifetime, solution: ISolution, cha if scriptOptionsProvider.SyncUpdate then scriptOptionsProvider.GetFcsProject(psiModule.SourceFile) |> Option.iter (fun fcsProject -> - let references = getScriptReferences path fcsProject.ProjectOptions + let references = getScriptReferences path fcsProject.ProjectSnapshot updateReferences path references references.Assemblies [] changeBuilder ) @@ -261,8 +263,8 @@ type FSharpScriptPsiModulesProvider(lifetime: Lifetime, solution: ISolution, cha scriptsFromProjectFiles.GetValuesSafe(path) |> Seq.tryFind (fun psiModule -> psiModule.Path = moduleToRemove.Path) |> Option.iter (fun psiModule -> - match checkerService.GetCachedScriptOptions(path.FullPath) with - | Some options -> checkerService.InvalidateFcsProject(options, FcsProjectInvalidationType.Remove) + match checkerService.GetCachedScriptSnapshot(path.FullPath) with + | Some snapshot -> checkerService.InvalidateFcsProject(snapshot, FcsProjectInvalidationType.Remove) | None -> () scriptsFromProjectFiles.RemoveValue(path, psiModule) |> ignore diff --git a/ReSharper.FSharp/src/FSharp/FSharp.Tests.Host/FSharpTestHost.fs b/ReSharper.FSharp/src/FSharp/FSharp.Tests.Host/FSharpTestHost.fs index 36cd90a53e..c6873e8e55 100644 --- a/ReSharper.FSharp/src/FSharp/FSharp.Tests.Host/FSharpTestHost.fs +++ b/ReSharper.FSharp/src/FSharp/FSharp.Tests.Host/FSharpTestHost.fs @@ -52,7 +52,7 @@ type FSharpTestHost(solution: ISolution, sourceCache: FSharpSourceCache, itemsCo psiModule.SourceFiles |> Seq.find (fun sourceFile -> sourceFile.LanguageType.Is()) - projectProvider.GetProjectOptions(sourceFile) + projectProvider.GetProjectSnapshot(sourceFile) |> Option.map (fun options -> options.OtherOptions |> Array.choose (fun o -> if o.StartsWith("-r:") then Some (o.Substring("-r:".Length)) else None) @@ -67,7 +67,7 @@ type FSharpTestHost(solution: ISolution, sourceCache: FSharpSourceCache, itemsCo let project = projectModelViewHost.GetItemById(projectModelId) let psiModule = psiModules.GetPsiModules(project) |> Seq.exactlyOne - projectProvider.GetProjectOptions(psiModule).Value + projectProvider.GetProjectSnapshot(psiModule).Value let dumpFcsProjectStamp (projectModelId: int) = let projectOptions = getProjectOptions projectModelId From 6ba5ba22f9c741218b8a02e0c97b2e8b9365fe75 Mon Sep 17 00:00:00 2001 From: nojaf Date: Thu, 22 Feb 2024 09:39:50 +0100 Subject: [PATCH 07/22] Buildable solution --- ReSharper.FSharp/Directory.Build.props | 4 ++-- ReSharper.FSharp/TypeProviders.Host.targets | 2 +- .../FSharp.TypeProviders.Host.csproj | 1 - .../src/Checker/FcsCheckerService.fs | 5 +++-- .../src/Checker/FcsProjectProvider.fs | 4 ++-- .../src/Stages/ScriptLoadPathsStage.fs | 4 +++- .../src/Search/FSharpPathReference.fs | 8 +++++--- .../FSharp.Tests.Host/FSharpTestHost.fs | 12 ++++++----- .../src/FSharp.Tests.Common/src/Common.fs | 20 ++++++++++--------- 9 files changed, 34 insertions(+), 26 deletions(-) diff --git a/ReSharper.FSharp/Directory.Build.props b/ReSharper.FSharp/Directory.Build.props index d3485b9485..17598c2dfd 100644 --- a/ReSharper.FSharp/Directory.Build.props +++ b/ReSharper.FSharp/Directory.Build.props @@ -33,8 +33,8 @@ $(Subplatform).Psi.Features_test_Framework.Props $(Subplatform).Rider_RdBackend.Common.Props $(Subplatform).Rider_Rider.Backend.Props - true - $(MSBuildThisFileDirectory)../../fsharp/src/Compiler/FSharp.Compiler.Service.fsproj + true + $(MSBuildThisFileDirectory)../../fsharp/src/Compiler/FSharp.Compiler.Service.fsproj diff --git a/ReSharper.FSharp/TypeProviders.Host.targets b/ReSharper.FSharp/TypeProviders.Host.targets index 0ab3216525..b5db342ab0 100644 --- a/ReSharper.FSharp/TypeProviders.Host.targets +++ b/ReSharper.FSharp/TypeProviders.Host.targets @@ -14,7 +14,7 @@ - + diff --git a/ReSharper.FSharp/src/FSharp.TypeProviders.Host/FSharp.TypeProviders.Host/FSharp.TypeProviders.Host.csproj b/ReSharper.FSharp/src/FSharp.TypeProviders.Host/FSharp.TypeProviders.Host/FSharp.TypeProviders.Host.csproj index 400ee2cd7b..fe58864288 100644 --- a/ReSharper.FSharp/src/FSharp.TypeProviders.Host/FSharp.TypeProviders.Host/FSharp.TypeProviders.Host.csproj +++ b/ReSharper.FSharp/src/FSharp.TypeProviders.Host/FSharp.TypeProviders.Host/FSharp.TypeProviders.Host.csproj @@ -26,7 +26,6 @@ - diff --git a/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsCheckerService.fs b/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsCheckerService.fs index 358b51ae98..0742b1c263 100644 --- a/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsCheckerService.fs +++ b/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsCheckerService.fs @@ -201,7 +201,7 @@ type FcsCheckerService(lifetime: Lifetime, logger: ILogger, onSolutionCloseNotif new PinTypeCheckResultsCookie(sourceFile, parseAndCheckResults.ParseResults, parseAndCheckResults.CheckResults, prohibitTypeCheck) :> IDisposable | _ -> { new IDisposable with member this.Dispose() = () } - member x.TryGetStaleCheckResults([] file: IPsiSourceFile, opName) = + member x.TryGetStaleCheckResults([] file: IPsiSourceFile, opName) : FSharpCheckFileResults option = match x.FcsProjectProvider.GetProjectSnapshot(file) with | None -> None | Some snapshot -> @@ -210,7 +210,8 @@ type FcsCheckerService(lifetime: Lifetime, logger: ILogger, onSolutionCloseNotif logger.Trace("TryGetStaleCheckResults: start {0}, {1}", path, opName) // TODO: TryGetRecentCheckResultsForFile for Snapshot - Unchecked.defaultof + // https://github.com/dotnet/fsharp/pull/16720 + None // match x.Checker.TryGetRecentCheckResultsForFile(path, snapshot) with // | Some (_, checkResults, _) -> // logger.Trace("TryGetStaleCheckResults: finish {0}, {1}", path, opName) diff --git a/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsProjectProvider.fs b/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsProjectProvider.fs index f1bfc1c0ec..7a11477221 100644 --- a/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsProjectProvider.fs +++ b/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsProjectProvider.fs @@ -510,7 +510,7 @@ type FcsProjectProvider(lifetime: Lifetime, solution: ISolution, changeManager: dirtyProjects.Clear() interface IFcsProjectProvider with - member x.GetProjectOptions(sourceFile: IPsiSourceFile) = + member x.GetProjectSnapshot(sourceFile: IPsiSourceFile) = locks.AssertReadAccessAllowed() processInvalidatedFcsProjects () @@ -531,7 +531,7 @@ type FcsProjectProvider(lifetime: Lifetime, solution: ISolution, changeManager: | Some fcsProject when fcsProject.IsKnownFile(sourceFile) -> Some fcsProject.ProjectSnapshot | _ -> None - member x.GetProjectOptions(psiModule: IPsiModule) = + member x.GetProjectSnapshot(psiModule: IPsiModule) = locks.AssertReadAccessAllowed() processInvalidatedFcsProjects () diff --git a/ReSharper.FSharp/src/FSharp/FSharp.Psi.Daemon/src/Stages/ScriptLoadPathsStage.fs b/ReSharper.FSharp/src/FSharp/FSharp.Psi.Daemon/src/Stages/ScriptLoadPathsStage.fs index 14ebb0c84b..807a695e49 100644 --- a/ReSharper.FSharp/src/FSharp/FSharp.Psi.Daemon/src/Stages/ScriptLoadPathsStage.fs +++ b/ReSharper.FSharp/src/FSharp/FSharp.Psi.Daemon/src/Stages/ScriptLoadPathsStage.fs @@ -1,5 +1,7 @@ namespace rec JetBrains.ReSharper.Plugins.FSharp.Psi.Daemon.Stages +#nowarn "57" + open System.Collections.Generic open JetBrains.Application open JetBrains.ReSharper.Daemon.Stages @@ -51,7 +53,7 @@ type ScriptLoadPathsStageProcess(fsFile, daemonProcess) = fsFile.Accept(visitor) if allDirectives.IsEmpty() then () else - match fsFile.CheckerService.FcsProjectProvider.GetProjectOptions(daemonProcess.SourceFile) with + match fsFile.CheckerService.FcsProjectProvider.GetProjectSnapshot(daemonProcess.SourceFile) with | Some options when not options.OriginalLoadReferences.IsEmpty -> let document = daemonProcess.Document let linesCount = document.GetLineCount() |> int diff --git a/ReSharper.FSharp/src/FSharp/FSharp.Psi.Features/src/Search/FSharpPathReference.fs b/ReSharper.FSharp/src/FSharp/FSharp.Psi.Features/src/Search/FSharpPathReference.fs index 0dcad8d42c..c94321177d 100644 --- a/ReSharper.FSharp/src/FSharp/FSharp.Psi.Features/src/Search/FSharpPathReference.fs +++ b/ReSharper.FSharp/src/FSharp/FSharp.Psi.Features/src/Search/FSharpPathReference.fs @@ -1,5 +1,7 @@ namespace rec JetBrains.ReSharper.Plugins.FSharp.Psi.Features.Searching +#nowarn "57" + open JetBrains.DataFlow open JetBrains.ReSharper.Plugins.FSharp.Psi.Tree open JetBrains.ReSharper.Plugins.FSharp.Util @@ -48,9 +50,9 @@ type FSharpPathReference(owner, sourceFile) = let fsFile = owner.GetContainingFile() :?> IFSharpFile let document = sourceFile.Document let tokenStartOffset = owner.Parent.GetTreeStartOffset() - fsFile.CheckerService.FcsProjectProvider.GetProjectOptions(sourceFile) - |> Option.bind (fun options -> - options.OriginalLoadReferences + fsFile.CheckerService.FcsProjectProvider.GetProjectSnapshot(sourceFile) + |> Option.bind (fun snapshot -> + snapshot.OriginalLoadReferences |> List.tryFind (fun (range, _, _) -> getTreeStartOffset document range = tokenStartOffset) |> Option.bind (fun (_, _, path) -> let path = VirtualFileSystemPath.TryParse(path, InteractionContext.SolutionContext) diff --git a/ReSharper.FSharp/src/FSharp/FSharp.Tests.Host/FSharpTestHost.fs b/ReSharper.FSharp/src/FSharp/FSharp.Tests.Host/FSharpTestHost.fs index c6873e8e55..dd2e9eebd2 100644 --- a/ReSharper.FSharp/src/FSharp/FSharp.Tests.Host/FSharpTestHost.fs +++ b/ReSharper.FSharp/src/FSharp/FSharp.Tests.Host/FSharpTestHost.fs @@ -1,5 +1,7 @@ namespace JetBrains.ReSharper.Plugins.FSharp.Tests.Host +#nowarn "57" + open System.Collections.Generic open System.Globalization open System.Linq @@ -55,10 +57,10 @@ type FSharpTestHost(solution: ISolution, sourceCache: FSharpSourceCache, itemsCo projectProvider.GetProjectSnapshot(sourceFile) |> Option.map (fun options -> options.OtherOptions - |> Array.choose (fun o -> if o.StartsWith("-r:") then Some (o.Substring("-r:".Length)) else None) - |> Array.map (fun p -> VirtualFileSystemPath.TryParse(p, InteractionContext.SolutionContext)) - |> Array.filter (fun p -> not p.IsEmpty && directory.IsPrefixOf(p)) - |> Array.map (fun p -> p.Name) + |> List.choose (fun o -> if o.StartsWith("-r:") then Some (o.Substring("-r:".Length)) else None) + |> List.map (fun p -> VirtualFileSystemPath.TryParse(p, InteractionContext.SolutionContext)) + |> List.filter (fun p -> not p.IsEmpty && directory.IsPrefixOf(p)) + |> List.map (fun p -> p.Name) |> List) |> Option.defaultWith (fun _ -> List()) @@ -76,7 +78,7 @@ type FSharpTestHost(solution: ISolution, sourceCache: FSharpSourceCache, itemsCo let dumpFcsProjectReferences (projectModelId: int) = let projectOptions = getProjectOptions projectModelId projectOptions.ReferencedProjects - |> Array.map (fun project -> + |> List.map (fun project -> let outputPath = VirtualFileSystemPath.Parse(project.OutputFile, InteractionContext.SolutionContext) outputPath.NameWithoutExtension) |> List diff --git a/ReSharper.FSharp/test/src/FSharp.Tests.Common/src/Common.fs b/ReSharper.FSharp/test/src/FSharp.Tests.Common/src/Common.fs index e3a81d27fd..7163d4094b 100644 --- a/ReSharper.FSharp/test/src/FSharp.Tests.Common/src/Common.fs +++ b/ReSharper.FSharp/test/src/FSharp.Tests.Common/src/Common.fs @@ -1,5 +1,7 @@ namespace JetBrains.ReSharper.Plugins.FSharp.Tests +#nowarn "57" + open System open System.Collections.Generic open System.IO @@ -236,11 +238,11 @@ type TestFcsProjectProvider(lifetime: Lifetime, checkerService: FcsCheckerServic // todo: referenced projects // todo: unify with FcsProjectProvider check let areSameForChecking (newProject: FcsProject) (oldProject: FcsProject) = - let getReferencedProjectOutputs (options: FSharpProjectOptions) = - options.ReferencedProjects |> Array.map (fun project -> project.OutputFile) + let getReferencedProjectOutputs (options: FSharpProjectSnapshot) = + options.ReferencedProjects |> List.map (fun project -> project.OutputFile) - let newOptions = newProject.ProjectOptions - let oldOptions = oldProject.ProjectOptions + let newOptions = newProject.ProjectSnapshot + let oldOptions = oldProject.ProjectSnapshot newOptions.ProjectFileName = oldOptions.ProjectFileName && newOptions.SourceFiles = oldOptions.SourceFiles && @@ -258,9 +260,9 @@ type TestFcsProjectProvider(lifetime: Lifetime, checkerService: FcsCheckerServic newFcsProject ) - let getProjectOptions (sourceFile: IPsiSourceFile) = + let getProjectSnapshot (sourceFile: IPsiSourceFile) = let fcsProject = getFcsProject sourceFile.PsiModule - Some fcsProject.ProjectOptions + Some fcsProject.ProjectSnapshot interface IHideImplementation @@ -269,11 +271,11 @@ type TestFcsProjectProvider(lifetime: Lifetime, checkerService: FcsCheckerServic let fcsProject = getFcsProject sourceFile.PsiModule fcsProject.ImplementationFilesWithSignatures.Contains(sourceFile.GetLocation()) - member x.GetProjectOptions(sourceFile: IPsiSourceFile) = + member x.GetProjectSnapshot(sourceFile: IPsiSourceFile) = if sourceFile.LanguageType.Is() then scriptFcsProjectProvider.GetScriptSnapshot(sourceFile) else - getProjectOptions sourceFile + getProjectSnapshot sourceFile member x.GetParsingOptions(sourceFile) = if isNull sourceFile then sandboxParsingOptions else @@ -328,7 +330,7 @@ type TestFcsProjectProvider(lifetime: Lifetime, checkerService: FcsCheckerServic member x.HasFcsProjects = false member this.GetAllFcsProjects() = [] - member this.GetProjectOptions(_: IPsiModule): FSharpProjectOptions option = failwith "todo" + member this.GetProjectSnapshot(_: IPsiModule): FSharpProjectSnapshot option = failwith "todo" member this.GetFcsProject(psiModule) = Some (getFcsProject psiModule) member this.PrepareAssemblyShim _ = () member this.GetReferencedModule _ = None From 5de1edbddffc734745ed5cf135b8b0956bdce68c Mon Sep 17 00:00:00 2001 From: nojaf Date: Thu, 22 Feb 2024 15:57:24 +0100 Subject: [PATCH 08/22] I was able to open Rider with this --- ReSharper.FSharp/Directory.Build.props | 6 ++++-- .../FSharp/FSharp.Common/src/Checker/FcsCheckerService.fs | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/ReSharper.FSharp/Directory.Build.props b/ReSharper.FSharp/Directory.Build.props index 17598c2dfd..1ec0b8c36e 100644 --- a/ReSharper.FSharp/Directory.Build.props +++ b/ReSharper.FSharp/Directory.Build.props @@ -34,11 +34,13 @@ $(Subplatform).Rider_RdBackend.Common.Props $(Subplatform).Rider_Rider.Backend.Props true - $(MSBuildThisFileDirectory)../../fsharp/src/Compiler/FSharp.Compiler.Service.fsproj + $(MSBuildThisFileDirectory)../../fsharp - + + + diff --git a/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsCheckerService.fs b/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsCheckerService.fs index 0742b1c263..e0afe2d691 100644 --- a/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsCheckerService.fs +++ b/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsCheckerService.fs @@ -99,7 +99,8 @@ type FcsCheckerService(lifetime: Lifetime, logger: ILogger, onSolutionCloseNotif let setting = SettingsUtil.getEntry settingsStore name settingsStoreLive.GetValueProperty(lifetime, setting, null) - let useTransparentCompiler = (getSettingProperty "UseTransparentCompiler").Value + let useTransparentCompiler = true + // (getSettingProperty "UseTransparentCompiler").Value let checker = Environment.SetEnvironmentVariable("FCS_CheckFileInProjectCacheSize", "20") From ae1089ab24ec956450e0057318eee3c392a9f22a Mon Sep 17 00:00:00 2001 From: nojaf Date: Mon, 26 Feb 2024 09:21:27 +0100 Subject: [PATCH 09/22] Use TryGetRecentCheckResultsForFile --- .../src/Checker/FSharpCheckerExtensions.fs | 68 ++++++++----------- 1 file changed, 30 insertions(+), 38 deletions(-) diff --git a/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FSharpCheckerExtensions.fs b/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FSharpCheckerExtensions.fs index c93bf35b94..db29c58724 100644 --- a/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FSharpCheckerExtensions.fs +++ b/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FSharpCheckerExtensions.fs @@ -22,8 +22,6 @@ type CheckResults = type FSharpChecker with member internal x.ParseAndCheckDocument(path, projectSnapshot: FSharpProjectSnapshot, allowStale: bool, opName) = - // let version = source.GetHashCode() - let parseAndCheckFile = async { let! parseResults, checkFileAnswer = @@ -56,40 +54,34 @@ type FSharpChecker with return StillRunning t } - // let bindParsedInput(results: (FSharpParseFileResults * FSharpCheckFileResults) option) = - // match results with - // | Some(parseResults, checkResults) -> - // Some (parseResults, checkResults) - // | _ -> None - - // Additional map seems weird here? - // map bindParsedInput - - parseAndCheckFile + let bindParsedInput(results: (FSharpParseFileResults * FSharpCheckFileResults) option) = + match results with + | Some(parseResults, checkResults) -> + Some (parseResults, checkResults) + | _ -> None - // async { - // // TODO: TryGetRecentCheckResultsForFile is missing on the Transparent compiler - // match x.TryGetRecentCheckResultsForFile(path, options, source) with - // | None -> - // // No stale results available, wait for fresh results - // return! parseAndCheckFile - // - // | Some (parseResults, checkFileResults, cachedVersion) when allowStale && cachedVersion = int64 version -> - // // Avoid queueing on the reactor thread by using the recent results - // return Some (parseResults, checkFileResults) - // - // | Some (staleParseResults, staleCheckFileResults, _) -> - // - // match! tryGetFreshResultsWithTimeout() with - // | Ready x -> - // // Fresh results were ready quickly enough - // return x - // - // | StillRunning _ when allowStale -> - // // Still waiting for fresh results - just use the stale ones for now - // return Some (staleParseResults, staleCheckFileResults) - // - // | StillRunning worker -> - // return! Async.AwaitTask worker - // } - // |> map bindParsedInput + async { + match x.TryGetRecentCheckResultsForFile(path, projectSnapshot) with + | None -> + // No stale results available, wait for fresh results + return! parseAndCheckFile + + // | Some (parseResults, checkFileResults) when allowStale && cachedVersion = int64 version -> + // // Avoid queueing on the reactor thread by using the recent results + // return Some (parseResults, checkFileResults) + + | Some (staleParseResults, staleCheckFileResults) -> + + match! tryGetFreshResultsWithTimeout() with + | Ready x -> + // Fresh results were ready quickly enough + return x + + | StillRunning _ when allowStale -> + // Still waiting for fresh results - just use the stale ones for now + return Some (staleParseResults, staleCheckFileResults) + + | StillRunning worker -> + return! Async.AwaitTask worker + } + |> map bindParsedInput From eee2df92019a34e12d332091ac5f60a49bad9a3c Mon Sep 17 00:00:00 2001 From: nojaf Date: Mon, 26 Feb 2024 09:36:49 +0100 Subject: [PATCH 10/22] Restore TestDump --- .../src/Checker/FcsCheckerService.fs | 28 +++++++++---------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsCheckerService.fs b/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsCheckerService.fs index e0afe2d691..65ee3fa621 100644 --- a/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsCheckerService.fs +++ b/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsCheckerService.fs @@ -59,22 +59,20 @@ type FcsProject = let (ProjectSnapshot.FSharpProjectIdentifier(projectFileName, _)) = projectSnapshot.Identifier writer.WriteLine($"Project file: {projectFileName}") - // TODO: the ProjectSnapshot exposes less information than the ProjectOptions + writer.WriteLine($"Stamp: {projectSnapshot.Stamp}") + writer.WriteLine($"Load time: {projectSnapshot.LoadTime}") - // writer.WriteLine($"Stamp: {projectSnapshot.}") - // writer.WriteLine($"Load time: {projectSnapshot.LoadTime}") - // - // writer.WriteLine("Source files:") - // for sourceFile in projectSnapshot.SourceFiles do - // writer.WriteLine($" {sourceFile}") - // - // writer.WriteLine("Other options:") - // for option in projectSnapshot.OtherOptions do - // writer.WriteLine($" {option}") - // - // writer.WriteLine("Referenced projects:") - // for referencedProject in projectSnapshot.ReferencedProjects do - // writer.WriteLine($" {referencedProject.OutputFile}") + writer.WriteLine("Source files:") + for sourceFile in projectSnapshot.SourceFiles do + writer.WriteLine($" {sourceFile}") + + writer.WriteLine("Other options:") + for option in projectSnapshot.OtherOptions do + writer.WriteLine($" {option}") + + writer.WriteLine("Referenced projects:") + for referencedProject in projectSnapshot.ReferencedProjects do + writer.WriteLine($" {referencedProject.OutputFile}") writer.WriteLine() From 17999f2fd3d7f26b9eb24cda88a12aa11478487a Mon Sep 17 00:00:00 2001 From: nojaf Date: Mon, 26 Feb 2024 09:39:51 +0100 Subject: [PATCH 11/22] Reuse some more added APIs. --- .../src/Checker/FcsCheckerService.fs | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsCheckerService.fs b/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsCheckerService.fs index 65ee3fa621..30ff149d7f 100644 --- a/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsCheckerService.fs +++ b/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsCheckerService.fs @@ -176,7 +176,7 @@ type FcsCheckerService(lifetime: Lifetime, logger: ILogger, onSolutionCloseNotif | Some fcsProject -> let snapshot = fcsProject.ProjectSnapshot - // if not (fcsProject.IsKnownFile(sourceFile)) && not options.UseScriptResolutionRules then None else + if not (fcsProject.IsKnownFile(sourceFile)) && not snapshot.UseScriptResolutionRules then None else x.FcsProjectProvider.PrepareAssemblyShim(psiModule) @@ -208,17 +208,14 @@ type FcsCheckerService(lifetime: Lifetime, logger: ILogger, onSolutionCloseNotif let path = file.GetLocation().FullPath logger.Trace("TryGetStaleCheckResults: start {0}, {1}", path, opName) - // TODO: TryGetRecentCheckResultsForFile for Snapshot - // https://github.com/dotnet/fsharp/pull/16720 - None - // match x.Checker.TryGetRecentCheckResultsForFile(path, snapshot) with - // | Some (_, checkResults, _) -> - // logger.Trace("TryGetStaleCheckResults: finish {0}, {1}", path, opName) - // Some checkResults - // - // | _ -> - // logger.Trace("TryGetStaleCheckResults: fail {0}, {1}", path, opName) - // None + match x.Checker.TryGetRecentCheckResultsForFile(path, snapshot) with + | Some (_, checkResults) -> + logger.Trace("TryGetStaleCheckResults: finish {0}, {1}", path, opName) + Some checkResults + + | _ -> + logger.Trace("TryGetStaleCheckResults: fail {0}, {1}", path, opName) + None member x.GetCachedScriptSnapshot(path) = if checker.IsValueCreated then From 60b7fe6711048fe898b0bb44d1fcd461c58a3f7a Mon Sep 17 00:00:00 2001 From: nojaf Date: Mon, 26 Feb 2024 09:44:51 +0100 Subject: [PATCH 12/22] Use GetProjectSnapshotFromScript --- .../src/Checker/ScriptFcsProjectProvider.fs | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/ScriptFcsProjectProvider.fs b/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/ScriptFcsProjectProvider.fs index b66e70ad5f..efcec4e381 100644 --- a/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/ScriptFcsProjectProvider.fs +++ b/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/ScriptFcsProjectProvider.fs @@ -68,7 +68,7 @@ type ScriptFcsProjectProvider(lifetime: Lifetime, logger: ILogger, checkerServic let getSnapshotImpl (path: VirtualFileSystemPath) source : FSharpProjectSnapshot option = let path = path.FullPath - let source = SourceText.ofString source + let source = SourceTextNew.ofString source let targetNetFramework = not PlatformUtil.IsRunningOnCore && scriptSettings.TargetNetFramework.Value let toolset = toolset.GetDotNetCoreToolset() @@ -76,17 +76,14 @@ type ScriptFcsProjectProvider(lifetime: Lifetime, logger: ILogger, checkerServic if isNotNull toolset && isNotNull toolset.Sdk then let sdkRootFolder = toolset.Cli.NotNull("cli").SdkRootFolder.NotNull("sdkRootFolder") let sdkFolderPath = sdkRootFolder / toolset.Sdk.NotNull("sdk").FolderName.NotNull("sdkFolderName") - // TODO: GetProjectOptionsFromScript does not exists for Snapshot - Unchecked.defaultof> - // checkerService.Checker.GetProjectOptionsFromScript(path, source, - // otherFlags = otherFlags.Value.Value, - // assumeDotNetFramework = targetNetFramework, - // sdkDirOverride = sdkFolderPath.FullPath) + checkerService.Checker.GetProjectSnapshotFromScript(path, source, + otherFlags = otherFlags.Value.Value, + assumeDotNetFramework = targetNetFramework, + sdkDirOverride = sdkFolderPath.FullPath) else - Unchecked.defaultof> - // checkerService.Checker.GetProjectOptionsFromScript(path, source, - // otherFlags = otherFlags.Value.Value, - // assumeDotNetFramework = targetNetFramework) + checkerService.Checker.GetProjectSnapshotFromScript(path, source, + otherFlags = otherFlags.Value.Value, + assumeDotNetFramework = targetNetFramework) try let snapshot, errors = getScriptSnapshotAsync.RunAsTask() From f0f4a197219b8317313f34880c957e49402a4d4e Mon Sep 17 00:00:00 2001 From: nojaf Date: Mon, 26 Feb 2024 10:06:31 +0100 Subject: [PATCH 13/22] This happens internally now. --- .../src/Checker/FSharpCheckerExtensions.fs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FSharpCheckerExtensions.fs b/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FSharpCheckerExtensions.fs index db29c58724..e711285010 100644 --- a/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FSharpCheckerExtensions.fs +++ b/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FSharpCheckerExtensions.fs @@ -65,11 +65,11 @@ type FSharpChecker with | None -> // No stale results available, wait for fresh results return! parseAndCheckFile - - // | Some (parseResults, checkFileResults) when allowStale && cachedVersion = int64 version -> - // // Avoid queueing on the reactor thread by using the recent results - // return Some (parseResults, checkFileResults) - + + | Some (parseResults, checkFileResults) when allowStale -> + // Avoid queueing on the reactor thread by using the recent results + return Some (parseResults, checkFileResults) + | Some (staleParseResults, staleCheckFileResults) -> match! tryGetFreshResultsWithTimeout() with From b2219d71794663de62051b183cfdc1b04c29bb34 Mon Sep 17 00:00:00 2001 From: nojaf Date: Mon, 26 Feb 2024 11:21:38 +0100 Subject: [PATCH 14/22] Filter psi source files on F#. --- .../FSharp.Common/src/Checker/FcsProjectBuilder.fs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsProjectBuilder.fs b/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsProjectBuilder.fs index c68de2644d..7d6d41c379 100644 --- a/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsProjectBuilder.fs +++ b/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsProjectBuilder.fs @@ -15,6 +15,7 @@ open JetBrains.ProjectModel.ProjectsHost open JetBrains.ProjectModel.ProjectsHost.MsBuild.Strategies open JetBrains.ProjectModel.ProjectsHost.SolutionHost open JetBrains.ProjectModel.Properties.Managed +open JetBrains.RdBackend.Common.Features.Documents open JetBrains.ReSharper.Plugins.FSharp.ProjectModel open JetBrains.ReSharper.Plugins.FSharp.ProjectModel.Host.ProjectItems.ItemsContainer open JetBrains.ReSharper.Plugins.FSharp.Shim.AssemblyReader @@ -200,9 +201,16 @@ type FcsProjectBuilder(checkerService: FcsCheckerService, itemsContainer: IFShar let sourceFiles = psiModule.SourceFiles + |> Seq.filter(fun psiSourceFile -> + isNotNull psiSourceFile.PrimaryPsiLanguage + && psiSourceFile.PrimaryPsiLanguage.Name = "F#" + ) |> Seq.map (fun psiSourceFile -> - // TODO: I assume the Create will expect the full path of the file? - let name = psiSourceFile.Name + let name = + match psiSourceFile.Document with + | :? RiderDocument as rd -> rd.FullPath + | _ -> psiSourceFile.Name + let version = string psiSourceFile.Document.LastModificationStamp.Value let getSource () = psiSourceFile.Document.GetText() |> FSharp.Compiler.Text.SourceTextNew.ofString |> Task.FromResult ProjectSnapshot.FSharpFileSnapshot.Create(name, version, getSource) From 0df8700fe45d5fef7070e5fa9a94e8218cfe5637 Mon Sep 17 00:00:00 2001 From: nojaf Date: Mon, 26 Feb 2024 11:37:18 +0100 Subject: [PATCH 15/22] Add StandaloneDocument and use cookie when getting text. --- .../FSharp.Common/src/Checker/FcsProjectBuilder.fs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsProjectBuilder.fs b/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsProjectBuilder.fs index 7d6d41c379..9cab0e8917 100644 --- a/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsProjectBuilder.fs +++ b/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsProjectBuilder.fs @@ -9,6 +9,7 @@ open FSharp.Compiler.CodeAnalysis open JetBrains.Application open JetBrains.Application.BuildScript.Application.Zones open JetBrains.Diagnostics +open JetBrains.DocumentModel open JetBrains.ProjectModel open JetBrains.ProjectModel.MSBuild open JetBrains.ProjectModel.ProjectsHost @@ -209,10 +210,17 @@ type FcsProjectBuilder(checkerService: FcsCheckerService, itemsContainer: IFShar let name = match psiSourceFile.Document with | :? RiderDocument as rd -> rd.FullPath + | :? StandaloneDocument as sd -> sd.Moniker | _ -> psiSourceFile.Name let version = string psiSourceFile.Document.LastModificationStamp.Value - let getSource () = psiSourceFile.Document.GetText() |> FSharp.Compiler.Text.SourceTextNew.ofString |> Task.FromResult + + let getSource () = + use cookie = ReadLockCookie.Create() + psiSourceFile.Document.GetText() + |> FSharp.Compiler.Text.SourceTextNew.ofString + |> Task.FromResult + ProjectSnapshot.FSharpFileSnapshot.Create(name, version, getSource) ) |> Seq.toList From 375c774c50beef46399bbf672a6ae7717775baba Mon Sep 17 00:00:00 2001 From: nojaf Date: Mon, 26 Feb 2024 14:13:25 +0100 Subject: [PATCH 16/22] Only use FileModificationTimeUtc when the file exists. --- .../src/FSharp/FSharp.Common/src/Checker/FcsProjectBuilder.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsProjectBuilder.fs b/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsProjectBuilder.fs index 9cab0e8917..0867d81c05 100644 --- a/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsProjectBuilder.fs +++ b/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsProjectBuilder.fs @@ -237,7 +237,7 @@ type FcsProjectBuilder(checkerService: FcsCheckerService, itemsContainer: IFShar else Some ({ Path = path.FullPath - LastModified = path.FileModificationTimeUtc + LastModified = if path.ExistsFile then path.FileModificationTimeUtc else DateTime.MinValue } : ProjectSnapshot.ReferenceOnDisk)) ) |> Seq.toList From a61e8ba04b40fb3110e69cd824445949dd788efe Mon Sep 17 00:00:00 2001 From: nojaf Date: Tue, 27 Feb 2024 14:00:34 +0100 Subject: [PATCH 17/22] Use FullPath as name. --- .../src/Checker/FcsProjectBuilder.fs | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsProjectBuilder.fs b/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsProjectBuilder.fs index 0867d81c05..5cbed87aba 100644 --- a/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsProjectBuilder.fs +++ b/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsProjectBuilder.fs @@ -21,6 +21,7 @@ open JetBrains.ReSharper.Plugins.FSharp.ProjectModel open JetBrains.ReSharper.Plugins.FSharp.ProjectModel.Host.ProjectItems.ItemsContainer open JetBrains.ReSharper.Plugins.FSharp.Shim.AssemblyReader open JetBrains.ReSharper.Plugins.FSharp.Util +open JetBrains.ReSharper.Psi open JetBrains.ReSharper.Psi.Modules open JetBrains.ReSharper.Resources.Shell open JetBrains.Util @@ -207,19 +208,15 @@ type FcsProjectBuilder(checkerService: FcsCheckerService, itemsContainer: IFShar && psiSourceFile.PrimaryPsiLanguage.Name = "F#" ) |> Seq.map (fun psiSourceFile -> - let name = - match psiSourceFile.Document with - | :? RiderDocument as rd -> rd.FullPath - | :? StandaloneDocument as sd -> sd.Moniker - | _ -> psiSourceFile.Name - + let name = psiSourceFile.GetLocation().FullPath let version = string psiSourceFile.Document.LastModificationStamp.Value let getSource () = - use cookie = ReadLockCookie.Create() - psiSourceFile.Document.GetText() - |> FSharp.Compiler.Text.SourceTextNew.ofString - |> Task.FromResult + task { + use cookie = ReadLockCookie.Create() + let text = psiSourceFile.Document.GetText() + return FSharp.Compiler.Text.SourceTextNew.ofString text + } ProjectSnapshot.FSharpFileSnapshot.Create(name, version, getSource) ) From e22fbfa7c353f4ce69462dc23e87452638d91c2c Mon Sep 17 00:00:00 2001 From: nojaf Date: Wed, 28 Feb 2024 14:31:53 +0100 Subject: [PATCH 18/22] Thanks Alex! --- .../src/Checker/FcsProjectBuilder.fs | 27 ++++++++++++++++--- .../src/FSharp.Tests.Common/src/Common.fs | 4 +-- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsProjectBuilder.fs b/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsProjectBuilder.fs index 5cbed87aba..d278629853 100644 --- a/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsProjectBuilder.fs +++ b/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsProjectBuilder.fs @@ -8,6 +8,7 @@ open System.Threading.Tasks open FSharp.Compiler.CodeAnalysis open JetBrains.Application open JetBrains.Application.BuildScript.Application.Zones +open JetBrains.Application.Threading open JetBrains.Diagnostics open JetBrains.DocumentModel open JetBrains.ProjectModel @@ -17,6 +18,7 @@ open JetBrains.ProjectModel.ProjectsHost.MsBuild.Strategies open JetBrains.ProjectModel.ProjectsHost.SolutionHost open JetBrains.ProjectModel.Properties.Managed open JetBrains.RdBackend.Common.Features.Documents +open JetBrains.ReSharper.Plugins.FSharp open JetBrains.ReSharper.Plugins.FSharp.ProjectModel open JetBrains.ReSharper.Plugins.FSharp.ProjectModel.Host.ProjectItems.ItemsContainer open JetBrains.ReSharper.Plugins.FSharp.Shim.AssemblyReader @@ -68,7 +70,7 @@ module FcsProjectBuilder = [] [)>] type FcsProjectBuilder(checkerService: FcsCheckerService, itemsContainer: IFSharpItemsContainer, - modulePathProvider: ModulePathProvider, logger: ILogger, psiModules: IPsiModules) = + modulePathProvider: ModulePathProvider, logger: ILogger, psiModules: IPsiModules, locks: IShellLocks) = let defaultOptions = [| "--noframework" @@ -209,16 +211,35 @@ type FcsProjectBuilder(checkerService: FcsCheckerService, itemsContainer: IFShar ) |> Seq.map (fun psiSourceFile -> let name = psiSourceFile.GetLocation().FullPath + (* + + In order to create the snapshot, we need to ensure that Resharper read lock rules are respected when getting the source. + Today, this happens in DelegatingFileSystemShim.cs. + So we can rely on the file system (that is shimmed) and use FSharpFileSnapshot.CreateFromFileSystem. + + Alternatively we can construct the snapshot via getSource: + ```fsharp let version = string psiSourceFile.Document.LastModificationStamp.Value let getSource () = task { - use cookie = ReadLockCookie.Create() - let text = psiSourceFile.Document.GetText() + let mutable text = "" + FSharpAsyncUtil.UsingReadLockInsideFcs(locks, fun () -> + text <- psiSourceFile.Document.GetText() + ) return FSharp.Compiler.Text.SourceTextNew.ofString text } ProjectSnapshot.FSharpFileSnapshot.Create(name, version, getSource) + ``` + + This also worked but for now going with the FileSystemShim seems better? + I favour the getSource option (or a better version of it) over the FileSystemShim + as it makes it more explicit where the source is really coming from. + However, I don't have enough understanding about the plugin to really make this judgement call. + + *) + ProjectSnapshot.FSharpFileSnapshot.CreateFromFileSystem(name) ) |> Seq.toList diff --git a/ReSharper.FSharp/test/src/FSharp.Tests.Common/src/Common.fs b/ReSharper.FSharp/test/src/FSharp.Tests.Common/src/Common.fs index 7163d4094b..84e1e03d86 100644 --- a/ReSharper.FSharp/test/src/FSharp.Tests.Common/src/Common.fs +++ b/ReSharper.FSharp/test/src/FSharp.Tests.Common/src/Common.fs @@ -209,8 +209,8 @@ type TestFSharpResolvedSymbolsCache(lifetime, checkerService, psiModules, fcsPro [] [)>] -type TestFcsProjectBuilder(checkerService, modulePathProvider, logger, psiModules) = - inherit FcsProjectBuilder(checkerService, Mock<_>().Object, modulePathProvider, logger, psiModules) +type TestFcsProjectBuilder(checkerService, modulePathProvider, logger, psiModules, locks) = + inherit FcsProjectBuilder(checkerService, Mock<_>().Object, modulePathProvider, logger, psiModules, locks) override x.GetProjectItemsPaths(project, targetFrameworkId) = project.GetAllProjectFiles() From fd321d82901a98afcdff023c030f2f10a05a6458 Mon Sep 17 00:00:00 2001 From: nojaf Date: Mon, 4 Mar 2024 15:20:56 +0100 Subject: [PATCH 19/22] options.ReferencesOnDisk are not part of options.OtherOptions. --- .../FSharp/FSharp.Common/src/Checker/FcsCheckerService.fs | 6 ++++++ .../FSharp/FSharp.Common/src/Checker/FcsProjectProvider.fs | 1 + .../FSharp.Common/src/Checker/ScriptFcsProjectProvider.fs | 1 + .../src/ProjectModel/FSharpScriptPsiModuleFactory.fs | 7 +++---- .../src/FSharp/FSharp.Tests.Host/FSharpTestHost.fs | 5 ++--- .../test/src/FSharp.Tests.Common/src/Common.fs | 1 + 6 files changed, 14 insertions(+), 7 deletions(-) diff --git a/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsCheckerService.fs b/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsCheckerService.fs index 30ff149d7f..05700a3b92 100644 --- a/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsCheckerService.fs +++ b/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsCheckerService.fs @@ -70,6 +70,10 @@ type FcsProject = for option in projectSnapshot.OtherOptions do writer.WriteLine($" {option}") + writer.WriteLine("References on disk:") + for r in projectSnapshot.ReferencesOnDisk do + writer.WriteLine($" %s{r.Path}") + writer.WriteLine("Referenced projects:") for referencedProject in projectSnapshot.ReferencedProjects do writer.WriteLine($" {referencedProject.OutputFile}") @@ -97,6 +101,7 @@ type FcsCheckerService(lifetime: Lifetime, logger: ILogger, onSolutionCloseNotif let setting = SettingsUtil.getEntry settingsStore name settingsStoreLive.GetValueProperty(lifetime, setting, null) + // Hard coded for now. let useTransparentCompiler = true // (getSettingProperty "UseTransparentCompiler").Value @@ -180,6 +185,7 @@ type FcsCheckerService(lifetime: Lifetime, logger: ILogger, onSolutionCloseNotif x.FcsProjectProvider.PrepareAssemblyShim(psiModule) + // TODO: should this be the non virtual path? let path = sourceFile.GetLocation().FullPath let source = FcsCheckerService.getSourceText sourceFile.Document logger.Trace("ParseAndCheckFile: start {0}, {1}", path, opName) diff --git a/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsProjectProvider.fs b/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsProjectProvider.fs index 7a11477221..8ac241d919 100644 --- a/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsProjectProvider.fs +++ b/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsProjectProvider.fs @@ -135,6 +135,7 @@ type FcsProjectProvider(lifetime: Lifetime, solution: ISolution, changeManager: newOptions.ProjectFileName = oldOptions.ProjectFileName && newOptions.SourceFiles = oldOptions.SourceFiles && newOptions.OtherOptions = oldOptions.OtherOptions && + newOptions.ReferencesOnDisk = oldOptions.ReferencesOnDisk && newOptions.ReferencedProjects.Length = oldOptions.ReferencedProjects.Length && (newOptions.ReferencedProjects, oldOptions.ReferencedProjects) diff --git a/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/ScriptFcsProjectProvider.fs b/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/ScriptFcsProjectProvider.fs index efcec4e381..2211c1f1f2 100644 --- a/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/ScriptFcsProjectProvider.fs +++ b/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/ScriptFcsProjectProvider.fs @@ -196,6 +196,7 @@ type ScriptFcsProjectProvider(lifetime: Lifetime, logger: ILogger, checkerServic List.length l1 = List.length l2 && List.forall2 (=) l1 l2 listEq options1.OtherOptions options2.OtherOptions && + listEq options1.ReferencesOnDisk options2.ReferencesOnDisk && listEq options1.SourceFiles options2.SourceFiles if not (areEqualForChecking oldOptions.ProjectSnapshot newSnapshot) then diff --git a/ReSharper.FSharp/src/FSharp/FSharp.Common/src/ProjectModel/FSharpScriptPsiModuleFactory.fs b/ReSharper.FSharp/src/FSharp/FSharp.Common/src/ProjectModel/FSharpScriptPsiModuleFactory.fs index 3e1f7d4a9d..6a0cba7300 100644 --- a/ReSharper.FSharp/src/FSharp/FSharp.Common/src/ProjectModel/FSharpScriptPsiModuleFactory.fs +++ b/ReSharper.FSharp/src/FSharp/FSharp.Common/src/ProjectModel/FSharpScriptPsiModuleFactory.fs @@ -68,10 +68,9 @@ type FSharpScriptPsiModulesProvider(lifetime: Lifetime, solution: ISolution, cha let getScriptReferences (scriptPath: VirtualFileSystemPath) (scriptSnapshot: FSharpProjectSnapshot) = let assembliesPaths = HashSet() - for o in scriptSnapshot.OtherOptions do - if o.StartsWith("-r:", StringComparison.Ordinal) then - let path = VirtualFileSystemPath.TryParse(o.Substring(3), InteractionContext.SolutionContext) - if not path.IsEmpty then assembliesPaths.Add(path) |> ignore + for { Path = r } in scriptSnapshot.ReferencesOnDisk do + let path = VirtualFileSystemPath.TryParse(r, InteractionContext.SolutionContext) + if not path.IsEmpty then assembliesPaths.Add(path) |> ignore let filesPaths = HashSet() for file in scriptSnapshot.SourceFiles do diff --git a/ReSharper.FSharp/src/FSharp/FSharp.Tests.Host/FSharpTestHost.fs b/ReSharper.FSharp/src/FSharp/FSharp.Tests.Host/FSharpTestHost.fs index dd2e9eebd2..be86e9e164 100644 --- a/ReSharper.FSharp/src/FSharp/FSharp.Tests.Host/FSharpTestHost.fs +++ b/ReSharper.FSharp/src/FSharp/FSharp.Tests.Host/FSharpTestHost.fs @@ -56,9 +56,8 @@ type FSharpTestHost(solution: ISolution, sourceCache: FSharpSourceCache, itemsCo projectProvider.GetProjectSnapshot(sourceFile) |> Option.map (fun options -> - options.OtherOptions - |> List.choose (fun o -> if o.StartsWith("-r:") then Some (o.Substring("-r:".Length)) else None) - |> List.map (fun p -> VirtualFileSystemPath.TryParse(p, InteractionContext.SolutionContext)) + options.ReferencesOnDisk + |> List.map (fun { Path = p } -> VirtualFileSystemPath.TryParse(p, InteractionContext.SolutionContext)) |> List.filter (fun p -> not p.IsEmpty && directory.IsPrefixOf(p)) |> List.map (fun p -> p.Name) |> List) diff --git a/ReSharper.FSharp/test/src/FSharp.Tests.Common/src/Common.fs b/ReSharper.FSharp/test/src/FSharp.Tests.Common/src/Common.fs index 84e1e03d86..9f7894f215 100644 --- a/ReSharper.FSharp/test/src/FSharp.Tests.Common/src/Common.fs +++ b/ReSharper.FSharp/test/src/FSharp.Tests.Common/src/Common.fs @@ -247,6 +247,7 @@ type TestFcsProjectProvider(lifetime: Lifetime, checkerService: FcsCheckerServic newOptions.ProjectFileName = oldOptions.ProjectFileName && newOptions.SourceFiles = oldOptions.SourceFiles && newOptions.OtherOptions = oldOptions.OtherOptions && + newOptions.ReferencesOnDisk = oldOptions.ReferencesOnDisk && getReferencedProjectOutputs newOptions = getReferencedProjectOutputs oldOptions let getFcsProject (psiModule: IPsiModule) = From 7e024607c118ddd5c46c45d9e86f15ded31a8ecd Mon Sep 17 00:00:00 2001 From: nojaf Date: Mon, 11 Mar 2024 16:25:57 +0100 Subject: [PATCH 20/22] Did you know that file order matters in F#? --- .../src/Checker/FcsProjectBuilder.fs | 90 +++++++++++-------- .../src/Checker/FcsProjectProvider.fs | 36 +++++++- .../src/FSharp.Tests.Common/src/Common.fs | 25 +++--- 3 files changed, 102 insertions(+), 49 deletions(-) diff --git a/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsProjectBuilder.fs b/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsProjectBuilder.fs index d278629853..a7d0be2d18 100644 --- a/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsProjectBuilder.fs +++ b/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsProjectBuilder.fs @@ -203,43 +203,59 @@ type FcsProjectBuilder(checkerService: FcsCheckerService, itemsContainer: IFShar let psiModule = psiModules.GetPrimaryPsiModule(project, targetFrameworkId) - let sourceFiles = + let projectItems: (VirtualFileSystemPath * BuildAction) array = x.GetProjectItemsPaths(project, targetFrameworkId) + + let tryFindPsiSourceFile name = psiModule.SourceFiles - |> Seq.filter(fun psiSourceFile -> - isNotNull psiSourceFile.PrimaryPsiLanguage - && psiSourceFile.PrimaryPsiLanguage.Name = "F#" - ) - |> Seq.map (fun psiSourceFile -> - let name = psiSourceFile.GetLocation().FullPath - (* - - In order to create the snapshot, we need to ensure that Resharper read lock rules are respected when getting the source. - Today, this happens in DelegatingFileSystemShim.cs. - So we can rely on the file system (that is shimmed) and use FSharpFileSnapshot.CreateFromFileSystem. - - Alternatively we can construct the snapshot via getSource: - ```fsharp - let version = string psiSourceFile.Document.LastModificationStamp.Value - - let getSource () = - task { - let mutable text = "" - FSharpAsyncUtil.UsingReadLockInsideFcs(locks, fun () -> - text <- psiSourceFile.Document.GetText() - ) - return FSharp.Compiler.Text.SourceTextNew.ofString text - } - - ProjectSnapshot.FSharpFileSnapshot.Create(name, version, getSource) - ``` - - This also worked but for now going with the FileSystemShim seems better? - I favour the getSource option (or a better version of it) over the FileSystemShim - as it makes it more explicit where the source is really coming from. - However, I don't have enough understanding about the plugin to really make this judgement call. - - *) - ProjectSnapshot.FSharpFileSnapshot.CreateFromFileSystem(name) + |> Seq.tryFind (fun sf -> sf.GetLocation().FullPath = name) + + let sourceFiles = + projectItems + |> Seq.choose (fun (virtualFileSystemPath, buildAction) -> + match buildAction, tryFindPsiSourceFile virtualFileSystemPath.FullPath with + | SourceFile, Some psiSourceFile -> + let name = virtualFileSystemPath.FullPath + (* + + In order to create the snapshot, we need to ensure that Resharper read lock rules are respected when getting the source. + Today, this happens in DelegatingFileSystemShim.cs. + So we can rely on the file system (that is shimmed) and use FSharpFileSnapshot.CreateFromFileSystem. + + Alternatively we can construct the snapshot via getSource: + ```fsharp + let version = string psiSourceFile.Document.LastModificationStamp.Value + + let getSource () = + task { + let mutable text = "" + FSharpAsyncUtil.UsingReadLockInsideFcs(locks, fun () -> + text <- psiSourceFile.Document.GetText() + ) + return FSharp.Compiler.Text.SourceTextNew.ofString text + } + + ProjectSnapshot.FSharpFileSnapshot.Create(name, version, getSource) + ``` + + This also worked but for now going with the FileSystemShim seems better? + I favour the getSource option (or a better version of it) over the FileSystemShim + as it makes it more explicit where the source is really coming from. + However, I don't have enough understanding about the plugin to really make this judgement call. + + *) + let getSource () = + task { + let mutable text = "" + FSharpAsyncUtil.UsingReadLockInsideFcs(locks, fun () -> + text <- psiSourceFile.Document.GetText() + ) + return FSharp.Compiler.Text.SourceTextNew.ofString text + } + + let hash = string (psiSourceFile.Document.GetText().GetHashCode()) + // let sf = ProjectSnapshot.FSharpFileSnapshot.CreateFromFileSystem(name) + Some(ProjectSnapshot.FSharpFileSnapshot(name, hash, getSource)) + | _ -> None ) |> Seq.toList @@ -277,7 +293,7 @@ type FcsProjectBuilder(checkerService: FcsCheckerService, itemsContainer: IFShar originalLoadReferences = List.empty, stamp = None ) - + let parsingOptions, errors = checkerService.Checker.GetParsingOptionsFromCommandLineArgs(otherOptions) diff --git a/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsProjectProvider.fs b/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsProjectProvider.fs index 8ac241d919..0fe225631a 100644 --- a/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsProjectProvider.fs +++ b/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsProjectProvider.fs @@ -15,6 +15,9 @@ open JetBrains.Application.Threading open JetBrains.Application.changes open JetBrains.DataFlow open JetBrains.Diagnostics +open JetBrains.DocumentManagers +open JetBrains.DocumentManagers.impl +open JetBrains.DocumentModel open JetBrains.Lifetimes open JetBrains.ProjectModel open JetBrains.ProjectModel.Build @@ -72,7 +75,8 @@ type FcsProjectProvider(lifetime: Lifetime, solution: ISolution, changeManager: scriptFcsProjectProvider: IScriptFcsProjectProvider, fsFileService: IFSharpFileService, fsItemsContainer: IFSharpItemsContainer, locks: IShellLocks, logger: ILogger, fcsAssemblyReaderShim: IFcsAssemblyReaderShim, psiModules: IPsiModules, - moduleReferencesResolveStore: IModuleReferencesResolveStore) as this = + moduleReferencesResolveStore: IModuleReferencesResolveStore, + solutionDocumentChangeProvider: SolutionDocumentChangeProvider) as this = inherit RecursiveProjectModelChangeDeltaVisitor() /// The main cache for FCS project model and related things. @@ -464,6 +468,36 @@ type FcsProjectProvider(lifetime: Lifetime, solution: ISolution, changeManager: if isNotNull change && not change.IsClosingSolution then x.VisitDelta(change) + let documentChange = obj.ChangeMap.GetChange(solutionDocumentChangeProvider) + if isNotNull documentChange then + let projectFile = + match documentChange with + | :? ProjectFileDocumentChange as change -> change.ProjectFile + | :? ProjectFileDocumentCopyChange as change -> change.ProjectFile + | _ -> null + + if isNotNull projectFile then + // call .Replace() ? + let impactedProjects = + fcsProjects + |> Seq.filter(fun (KeyValue(_, fcsProject)) -> + fcsProject.ProjectSnapshot.SourceFiles + |> List.exists (fun sf -> sf.FileName = projectFile.Location.FullPath)) + + for KeyValue(fcsProjectKey, fcsProject) in impactedProjects do + let updatedFiles = + fcsProject.ProjectSnapshot.SourceFiles + |> List.map (fun sf -> + if sf.FileName <> projectFile.Location.FullPath then + sf + else + FSharpFileSnapshot.CreateFromFileSystem(projectFile.Location.FullPath) + ) + + let updatedProject = + { fcsProject with ProjectSnapshot = fcsProject.ProjectSnapshot.Replace(updatedFiles) } + fcsProjects.Add(fcsProjectKey, updatedProject) + if dirtyProjects.Count = 0 then () else use cookie = WriteLockCookie.Create() diff --git a/ReSharper.FSharp/test/src/FSharp.Tests.Common/src/Common.fs b/ReSharper.FSharp/test/src/FSharp.Tests.Common/src/Common.fs index 9f7894f215..4b3a4616b3 100644 --- a/ReSharper.FSharp/test/src/FSharp.Tests.Common/src/Common.fs +++ b/ReSharper.FSharp/test/src/FSharp.Tests.Common/src/Common.fs @@ -228,6 +228,7 @@ type TestFcsProjectProvider(lifetime: Lifetime, checkerService: FcsCheckerServic do checkerService.FcsProjectProvider <- this lifetime.OnTermination(fun _ -> checkerService.FcsProjectProvider <- Unchecked.defaultof<_>) |> ignore + let mutable currentFcsProject = None @@ -238,19 +239,21 @@ type TestFcsProjectProvider(lifetime: Lifetime, checkerService: FcsCheckerServic // todo: referenced projects // todo: unify with FcsProjectProvider check let areSameForChecking (newProject: FcsProject) (oldProject: FcsProject) = - let getReferencedProjectOutputs (options: FSharpProjectSnapshot) = - options.ReferencedProjects |> List.map (fun project -> project.OutputFile) - - let newOptions = newProject.ProjectSnapshot - let oldOptions = oldProject.ProjectSnapshot - - newOptions.ProjectFileName = oldOptions.ProjectFileName && - newOptions.SourceFiles = oldOptions.SourceFiles && - newOptions.OtherOptions = oldOptions.OtherOptions && - newOptions.ReferencesOnDisk = oldOptions.ReferencesOnDisk && - getReferencedProjectOutputs newOptions = getReferencedProjectOutputs oldOptions + false + // let getReferencedProjectOutputs (options: FSharpProjectSnapshot) = + // options.ReferencedProjects |> List.map (fun project -> project.OutputFile) + // + // let newOptions = newProject.ProjectSnapshot + // let oldOptions = oldProject.ProjectSnapshot + // + // newOptions.ProjectFileName = oldOptions.ProjectFileName && + // newOptions.SourceFiles = oldOptions.SourceFiles && + // newOptions.OtherOptions = oldOptions.OtherOptions && + // newOptions.ReferencesOnDisk = oldOptions.ReferencesOnDisk && + // getReferencedProjectOutputs newOptions = getReferencedProjectOutputs oldOptions let getFcsProject (psiModule: IPsiModule) = + lock this (fun _ -> let newFcsProject = getNewFcsProject psiModule match currentFcsProject with From e138d3ec57ddba9fe87e122e2355c6f12bbea5d3 Mon Sep 17 00:00:00 2001 From: nojaf Date: Mon, 11 Mar 2024 17:41:01 +0100 Subject: [PATCH 21/22] Revert workarounds --- .../src/Checker/FcsProjectBuilder.fs | 15 ++---------- .../src/FSharp.Tests.Common/src/Common.fs | 23 +++++++++---------- 2 files changed, 13 insertions(+), 25 deletions(-) diff --git a/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsProjectBuilder.fs b/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsProjectBuilder.fs index a7d0be2d18..44b8903b09 100644 --- a/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsProjectBuilder.fs +++ b/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsProjectBuilder.fs @@ -242,19 +242,8 @@ type FcsProjectBuilder(checkerService: FcsCheckerService, itemsContainer: IFShar as it makes it more explicit where the source is really coming from. However, I don't have enough understanding about the plugin to really make this judgement call. - *) - let getSource () = - task { - let mutable text = "" - FSharpAsyncUtil.UsingReadLockInsideFcs(locks, fun () -> - text <- psiSourceFile.Document.GetText() - ) - return FSharp.Compiler.Text.SourceTextNew.ofString text - } - - let hash = string (psiSourceFile.Document.GetText().GetHashCode()) - // let sf = ProjectSnapshot.FSharpFileSnapshot.CreateFromFileSystem(name) - Some(ProjectSnapshot.FSharpFileSnapshot(name, hash, getSource)) + *) + Some (ProjectSnapshot.FSharpFileSnapshot.CreateFromFileSystem(name)) | _ -> None ) |> Seq.toList diff --git a/ReSharper.FSharp/test/src/FSharp.Tests.Common/src/Common.fs b/ReSharper.FSharp/test/src/FSharp.Tests.Common/src/Common.fs index 4b3a4616b3..c7ee3243af 100644 --- a/ReSharper.FSharp/test/src/FSharp.Tests.Common/src/Common.fs +++ b/ReSharper.FSharp/test/src/FSharp.Tests.Common/src/Common.fs @@ -239,18 +239,17 @@ type TestFcsProjectProvider(lifetime: Lifetime, checkerService: FcsCheckerServic // todo: referenced projects // todo: unify with FcsProjectProvider check let areSameForChecking (newProject: FcsProject) (oldProject: FcsProject) = - false - // let getReferencedProjectOutputs (options: FSharpProjectSnapshot) = - // options.ReferencedProjects |> List.map (fun project -> project.OutputFile) - // - // let newOptions = newProject.ProjectSnapshot - // let oldOptions = oldProject.ProjectSnapshot - // - // newOptions.ProjectFileName = oldOptions.ProjectFileName && - // newOptions.SourceFiles = oldOptions.SourceFiles && - // newOptions.OtherOptions = oldOptions.OtherOptions && - // newOptions.ReferencesOnDisk = oldOptions.ReferencesOnDisk && - // getReferencedProjectOutputs newOptions = getReferencedProjectOutputs oldOptions + let getReferencedProjectOutputs (options: FSharpProjectSnapshot) = + options.ReferencedProjects |> List.map (fun project -> project.OutputFile) + + let newOptions = newProject.ProjectSnapshot + let oldOptions = oldProject.ProjectSnapshot + + newOptions.ProjectFileName = oldOptions.ProjectFileName && + newOptions.SourceFiles = oldOptions.SourceFiles && + newOptions.OtherOptions = oldOptions.OtherOptions && + newOptions.ReferencesOnDisk = oldOptions.ReferencesOnDisk && + getReferencedProjectOutputs newOptions = getReferencedProjectOutputs oldOptions let getFcsProject (psiModule: IPsiModule) = From 580d353e5cf72055ceba069c9406c607977f248b Mon Sep 17 00:00:00 2001 From: nojaf Date: Tue, 12 Mar 2024 10:03:14 +0100 Subject: [PATCH 22/22] Update referenced projects --- .../src/Checker/FcsProjectProvider.fs | 37 ++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsProjectProvider.fs b/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsProjectProvider.fs index 0fe225631a..feeedf0dbe 100644 --- a/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsProjectProvider.fs +++ b/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsProjectProvider.fs @@ -496,7 +496,42 @@ type FcsProjectProvider(lifetime: Lifetime, solution: ISolution, changeManager: let updatedProject = { fcsProject with ProjectSnapshot = fcsProject.ProjectSnapshot.Replace(updatedFiles) } - fcsProjects.Add(fcsProjectKey, updatedProject) + fcsProjects.[fcsProjectKey] <- updatedProject + + let referencedModule: ReferencedModule = referencedModules.TryGetValue(fcsProjectKey) + + if isNull referencedModule then () else + + for referencingProject in referencedModule.ReferencingProjects do + let referencedFcsProject = fcsProjects.[referencingProject] + let referencedProjects = + referencedFcsProject.ProjectSnapshot.ReferencedProjects + |> List.map (function + | FSharpReferencedProjectSnapshot.PEReference _ + | FSharpReferencedProjectSnapshot.ILModuleReference _ as r -> r + | FSharpReferencedProjectSnapshot.FSharpReference(projectOutputFile = projectOutputFile) -> + FSharpReferencedProjectSnapshot.FSharpReference(projectOutputFile, updatedProject.ProjectSnapshot)) + + fcsProjects.[referencingProject] <- + { referencedFcsProject with + ProjectSnapshot = + FSharpProjectSnapshot.Create( + referencedFcsProject.ProjectSnapshot.ProjectFileName, + referencedFcsProject.ProjectSnapshot.ProjectId, + referencedFcsProject.ProjectSnapshot.SourceFiles, + referencedFcsProject.ProjectSnapshot.ReferencesOnDisk, + referencedFcsProject.ProjectSnapshot.OtherOptions, + referencedProjects, + referencedFcsProject.ProjectSnapshot.IsIncompleteTypeCheckEnvironment, + referencedFcsProject.ProjectSnapshot.UseScriptResolutionRules, + referencedFcsProject.ProjectSnapshot.LoadTime, + referencedFcsProject.ProjectSnapshot.UnresolvedReferences, + referencedFcsProject.ProjectSnapshot.OriginalLoadReferences, + referencedFcsProject.ProjectSnapshot.Stamp + ) + } + + // TODO: we need to replace the reference snapshots (recursively) if dirtyProjects.Count = 0 then () else