diff --git a/ReSharper.FSharp/Directory.Build.props b/ReSharper.FSharp/Directory.Build.props index db6c9e9455..1ec0b8c36e 100644 --- a/ReSharper.FSharp/Directory.Build.props +++ b/ReSharper.FSharp/Directory.Build.props @@ -33,5 +33,14 @@ $(Subplatform).Psi.Features_test_Framework.Props $(Subplatform).Rider_RdBackend.Common.Props $(Subplatform).Rider_Rider.Backend.Props + true + $(MSBuildThisFileDirectory)../../fsharp + + + + + + + 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.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..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 @@ - @@ -39,4 +38,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.Common/src/Checker/FSharpCheckerExtensions.fs b/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FSharpCheckerExtensions.fs index 8c29824881..e711285010 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,11 @@ 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 parseAndCheckFile = async { let! parseResults, checkFileAnswer = - x.ParseAndCheckFileInProject(path, version, source, options, userOpName = opName) + x.ParseAndCheckFileInProject(path, projectSnapshot, userOpName = opName) return match checkFileAnswer with @@ -61,26 +61,26 @@ type FSharpChecker with | _ -> None async { - match x.TryGetRecentCheckResultsForFile(path, options, source) with + match x.TryGetRecentCheckResultsForFile(path, projectSnapshot) with | None -> // No stale results available, wait for fresh results return! parseAndCheckFile - | Some (parseResults, checkFileResults, cachedVersion) when allowStale && cachedVersion = int64 version -> + | Some (parseResults, checkFileResults) when allowStale -> // Avoid queueing on the reactor thread by using the recent results return Some (parseResults, checkFileResults) - | Some (staleParseResults, staleCheckFileResults, _) -> - + | 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 } 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..05700a3b92 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,7 +40,7 @@ module FcsCheckerService = type FcsProject = { OutputPath: VirtualFileSystemPath - ProjectOptions: FSharpProjectOptions + ProjectSnapshot: FSharpProjectSnapshot ParsingOptions: FSharpParsingOptions FileIndices: IDictionary ImplementationFilesWithSignatures: ISet @@ -53,22 +55,27 @@ 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}") + let projectSnapshot = x.ProjectSnapshot + let (ProjectSnapshot.FSharpProjectIdentifier(projectFileName, _)) = projectSnapshot.Identifier + writer.WriteLine($"Project file: {projectFileName}") + writer.WriteLine($"Stamp: {projectSnapshot.Stamp}") + writer.WriteLine($"Load time: {projectSnapshot.LoadTime}") + writer.WriteLine("Source files:") - for sourceFile in projectOptions.SourceFiles do + for sourceFile in projectSnapshot.SourceFiles do writer.WriteLine($" {sourceFile}") - + writer.WriteLine("Other options:") - for option in projectOptions.OtherOptions do + 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 projectOptions.ReferencedProjects do + for referencedProject in projectSnapshot.ReferencedProjects do writer.WriteLine($" {referencedProject.OutputFile}") writer.WriteLine() @@ -88,25 +95,29 @@ 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) + // Hard coded for now. + let useTransparentCompiler = true + // (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,26 +167,31 @@ 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 -> - 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 snapshot.UseScriptResolutionRules then None else 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) // 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 } @@ -190,37 +206,38 @@ 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) = - match x.FcsProjectProvider.GetProjectOptions(file) with + member x.TryGetStaleCheckResults([] file: IPsiSourceFile, opName) : FSharpCheckFileResults option = + 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, _) -> + 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(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) = @@ -263,8 +280,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 @@ -289,9 +306,10 @@ type IFcsProjectProvider = type IScriptFcsProjectProvider = abstract GetFcsProject: IPsiSourceFile -> FcsProject option - abstract GetScriptOptions: IPsiSourceFile -> FSharpProjectOptions option - abstract GetScriptOptions: VirtualFileSystemPath * string -> FSharpProjectOptions option - abstract OptionsUpdated: Signal + abstract GetScriptSnapshot: IPsiSourceFile -> FSharpProjectSnapshot option + // TODO: unused? + // abstract GetScriptOptions: VirtualFileSystemPath * string -> FSharpProjectOptions option + abstract SnapshotUpdated: 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..44b8903b09 100644 --- a/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsProjectBuilder.fs +++ b/ReSharper.FSharp/src/FSharp/FSharp.Common/src/Checker/FcsProjectBuilder.fs @@ -1,21 +1,29 @@ 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 +open JetBrains.Application.Threading open JetBrains.Diagnostics +open JetBrains.DocumentModel open JetBrains.ProjectModel open JetBrains.ProjectModel.MSBuild 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 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 @@ -62,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" @@ -193,26 +201,95 @@ 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 projectItems: (VirtualFileSystemPath * BuildAction) array = x.GetProjectItemsPaths(project, targetFrameworkId) + + let tryFindPsiSourceFile name = + psiModule.SourceFiles + |> 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. + + *) + Some (ProjectSnapshot.FSharpFileSnapshot.CreateFromFileSystem(name)) + | _ -> None + ) + |> 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 = if path.ExistsFile then path.FileModificationTimeUtc else DateTime.MinValue + } : 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 +297,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 7a9ac94e43..feeedf0dbe 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 @@ -12,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 @@ -69,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. @@ -128,23 +135,24 @@ 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.ReferencesOnDisk = oldOptions.ReferencesOnDisk && 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.ProjectOptions oldProject.ProjectOptions + + loop newProject.ProjectSnapshot oldProject.ProjectSnapshot let tryGetFcsProject (psiModule: IPsiModule): FcsProject option = locks.AssertReadAccessAllowed() @@ -192,7 +200,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 +272,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 @@ -282,10 +320,10 @@ 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 parsingOptions = { FSharpParsingOptions.Default with SourceFiles = [| sourceFile.GetLocation().FullPath |] @@ -296,7 +334,7 @@ type FcsProjectProvider(lifetime: Lifetime, solution: ISolution, changeManager: let indices = Dictionary() { OutputPath = path - ProjectOptions = projectOptions + ProjectSnapshot = projectSnapshot ParsingOptions = parsingOptions FileIndices = indices ImplementationFilesWithSignatures = EmptySet.Instance @@ -399,7 +437,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 ) @@ -430,6 +468,71 @@ 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.[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 use cookie = WriteLockCookie.Create() @@ -477,33 +580,33 @@ 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 () 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) = + member x.GetProjectSnapshot(psiModule: IPsiModule) = locks.AssertReadAccessAllowed() 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..2211c1f1f2 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,30 @@ 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 source = SourceTextNew.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, + checkerService.Checker.GetProjectSnapshotFromScript(path, source, otherFlags = otherFlags.Value.Value, assumeDotNetFramework = targetNetFramework, sdkDirOverride = sdkFolderPath.FullPath) else - checkerService.Checker.GetProjectOptionsFromScript(path, source, + checkerService.Checker.GetProjectSnapshotFromScript(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 +97,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 +149,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 +167,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 +182,28 @@ 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.ReferencesOnDisk options2.ReferencesOnDisk && + 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,16 +228,17 @@ 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 : 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 @@ -222,5 +248,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..6a0cba7300 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,15 @@ 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 - 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 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 +149,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 +162,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 +182,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 +191,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 +231,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 +262,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.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.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.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/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.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.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/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.Tests.Host/FSharpTestHost.fs b/ReSharper.FSharp/src/FSharp/FSharp.Tests.Host/FSharpTestHost.fs index 36cd90a53e..be86e9e164 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 @@ -52,13 +54,12 @@ 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) - |> 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) + 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) |> Option.defaultWith (fun _ -> List()) @@ -67,7 +68,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 @@ -76,7 +77,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/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.Common/src/Common.fs b/ReSharper.FSharp/test/src/FSharp.Tests.Common/src/Common.fs index facb10136a..c7ee3243af 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 @@ -207,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() @@ -226,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 @@ -236,18 +239,20 @@ 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 newOptions = newProject.ProjectOptions - let oldOptions = oldProject.ProjectOptions - + 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 @@ -258,9 +263,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 +274,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.GetScriptOptions(sourceFile) else + scriptFcsProjectProvider.GetScriptSnapshot(sourceFile) else - getProjectOptions sourceFile + getProjectSnapshot sourceFile member x.GetParsingOptions(sourceFile) = if isNull sourceFile then sandboxParsingOptions else @@ -328,7 +333,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 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 @@ - 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