From 351d06b6c184e5fa4176e336ac7dff2b89047ec0 Mon Sep 17 00:00:00 2001 From: Phillip Carter Date: Sat, 16 Dec 2017 16:16:07 -0800 Subject: [PATCH] Remove dependency on FSharp.LanguageService (#4108) * Move ProjectSitesAndFiles to FSharp.Editor * Remove commented out code * Fix build that wasn't reported when running build.cmd * Remove weird stuff and add the VSLang proj reference like it is in FSharp.LanguageService * Remove dependency on FSharp.LanguageService * remove commented out reference --- .../src/FSharp.Editor/Common/RoslynHelpers.fs | 1 - vsintegration/src/FSharp.Editor/Common/Vs.fs | 70 +++- .../src/FSharp.Editor/FSharp.Editor.fsproj | 19 +- .../LanguageService/IProjectSite.fs | 61 ++++ .../LanguageService/LanguageService.fs | 3 +- .../LanguageService/ProjectSitesAndFiles.fs | 323 ++++++++++++++++++ .../FSharp.ProjectSystem.FSharp/Project.fs | 4 +- .../Salsa/FSharpLanguageServiceTestable.fs | 10 +- 8 files changed, 468 insertions(+), 23 deletions(-) create mode 100644 vsintegration/src/FSharp.Editor/LanguageService/IProjectSite.fs create mode 100644 vsintegration/src/FSharp.Editor/LanguageService/ProjectSitesAndFiles.fs diff --git a/vsintegration/src/FSharp.Editor/Common/RoslynHelpers.fs b/vsintegration/src/FSharp.Editor/Common/RoslynHelpers.fs index feb1e89e5..a1c7b8f57 100644 --- a/vsintegration/src/FSharp.Editor/Common/RoslynHelpers.fs +++ b/vsintegration/src/FSharp.Editor/Common/RoslynHelpers.fs @@ -14,7 +14,6 @@ open Microsoft.FSharp.Compiler open Microsoft.FSharp.Compiler.Layout open Microsoft.FSharp.Compiler.SourceCodeServices open Microsoft.FSharp.Compiler.Range -open Microsoft.VisualStudio.FSharp.LanguageService [] module internal RoslynHelpers = diff --git a/vsintegration/src/FSharp.Editor/Common/Vs.fs b/vsintegration/src/FSharp.Editor/Common/Vs.fs index 33fd16e69..526166f05 100644 --- a/vsintegration/src/FSharp.Editor/Common/Vs.fs +++ b/vsintegration/src/FSharp.Editor/Common/Vs.fs @@ -3,16 +3,84 @@ namespace Microsoft.VisualStudio.FSharp.Editor open System +open System.Runtime.InteropServices open Microsoft.VisualStudio +open Microsoft.VisualStudio.Editor open Microsoft.VisualStudio.Shell.Interop open Microsoft.VisualStudio.TextManager.Interop /// Helper methods for interoperating with COM -module internal Com = +module internal Com = + let ThrowOnFailure0(hr) = + ErrorHandler.ThrowOnFailure(hr) |> ignore + + let ThrowOnFailure1(hr,res) = + ErrorHandler.ThrowOnFailure(hr) |> ignore; + res + + let ThrowOnFailure2(hr,res1,res2) = + ErrorHandler.ThrowOnFailure(hr) |> ignore; + res1,res2 + + let ThrowOnFailure3(hr,res1,res2,res3) = + ErrorHandler.ThrowOnFailure(hr) |> ignore; + res1,res2,res3 + + let ThrowOnFailure4(hr,res1,res2,res3,res4) = + ErrorHandler.ThrowOnFailure(hr) |> ignore; + res1,res2,res3,res4 + let Succeeded hr = // REVIEW: Not the correct check for succeeded hr = VSConstants.S_OK +module internal VsUserData = + + let vsBufferMoniker = Guid("978A8E17-4DF8-432A-9623-D530A26452BC") + + // This is the file name of the buffer. + let GetBufferMonker(ud:IVsUserData) : string = + downcast Com.ThrowOnFailure1(ud.GetData(ref vsBufferMoniker)) + +module internal VsTextLines = + /// Get the length of the given line. + let LengthOfLine (buffer:IVsTextBuffer) (line:int) : int = + Com.ThrowOnFailure1(buffer.GetLengthOfLine(line)) + + /// Get the text for a particular line. + let LineText (buffer:IVsTextLines) line = + Com.ThrowOnFailure1(buffer.GetLineText(line, 0, line, LengthOfLine buffer line)) + + /// Get the color state + let TextColorState (buffer:IVsTextLines) : IVsTextColorState= unbox(box(buffer)) + + /// Get the filename of the given buffer (via IVsUserData). Not all buffers have a file. This will be an exception. + let GetFilename(buffer : IVsTextLines) = + let ud = (box buffer) :?> IVsUserData + VsUserData.GetBufferMonker(ud) + + /// Get the string contents of a given buffer (the current snapshot). + let GetFileContents(buffer: IVsTextBuffer, editorAdaptersFactoryService: IVsEditorAdaptersFactoryService) = + let dataBuffer = editorAdaptersFactoryService.GetDataBuffer(buffer) + dataBuffer.CurrentSnapshot.GetText() + +module internal VsRunningDocumentTable = + let FindDocumentWithoutLocking(rdt:IVsRunningDocumentTable, url:string) : (IVsHierarchy * IVsTextLines) option = + let (hr:int, hier:IVsHierarchy, _itemid:uint32, unkData:IntPtr, _cookie:uint32) = rdt.FindAndLockDocument(uint32 _VSRDTFLAGS.RDT_NoLock, url) + try + if Com.Succeeded(hr) then + let bufferObject = + if unkData=IntPtr.Zero then null + else Marshal.GetObjectForIUnknown(unkData) + let buffer = + match bufferObject with + | :? IVsTextLines as tl -> tl + | _ -> null + Some(hier, buffer) + else None + finally + if IntPtr.Zero <> unkData then Marshal.Release(unkData)|>ignore + [] module internal ServiceProviderExtensions = type internal System.IServiceProvider with diff --git a/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj b/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj index 0f11702c6..2563745ea 100644 --- a/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj +++ b/vsintegration/src/FSharp.Editor/FSharp.Editor.fsproj @@ -32,8 +32,6 @@ - - true Microsoft.VisualStudio.FSharp.Editor.SR @@ -42,11 +40,11 @@ + - @@ -54,6 +52,8 @@ + + @@ -107,11 +107,6 @@ {DED3BBD7-53F4-428A-8C9F-27968E768605} FSharp.Core - - FSharp.LanguageService - {ee85aab7-cda0-4c4e-bda0-a64ccc413e3f} - True - FSharp.LanguageService.Base {1c5c163c-37ea-4a3c-8ccc-0d34b74bf8ef} @@ -132,8 +127,6 @@ {991dcf75-c2eb-42b6-9a0d-aa1d2409d519} True - - @@ -146,8 +139,6 @@ - - $(FSharpSourcesRoot)\..\packages\EnvDTE.8.0.1\lib\net10\EnvDTE.dll True @@ -156,6 +147,10 @@ $(FSharpSourcesRoot)\..\packages\EnvDTE80.8.0.1\lib\net10\EnvDTE80.dll True + + $(FSharpSourcesRoot)\..\packages\VSSDK.VSLangProj.7.0.4\lib\net20\VSLangProj.dll + True + $(FSharpSourcesRoot)\..\packages\Microsoft.VisualStudio.Threading.$(MicrosoftVisualStudioThreadingVersion)\lib\net45\Microsoft.VisualStudio.Threading.dll diff --git a/vsintegration/src/FSharp.Editor/LanguageService/IProjectSite.fs b/vsintegration/src/FSharp.Editor/LanguageService/IProjectSite.fs new file mode 100644 index 000000000..71d53fe40 --- /dev/null +++ b/vsintegration/src/FSharp.Editor/LanguageService/IProjectSite.fs @@ -0,0 +1,61 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +namespace Microsoft.VisualStudio.FSharp.Editor + +open System.Runtime.InteropServices + +/// Narrow abstraction over the project system. +type internal AdviseProjectSiteChanges = delegate of unit -> unit + +[] +type internal IProvideProjectSite = + abstract GetProjectSite : unit -> IProjectSite + +/// Represents known F#-specific information about a project. +and internal IProjectSite = + + /// List of files in the project. In the correct order. + abstract CompilationSourceFiles : string[] + + /// Flags that the compiler would need to understand how to compile. Includes '-r' + /// options but not source files + abstract CompilationOptions : string[] + + /// The normalized '-r:' assembly references, without the '-r:' + abstract CompilationReferences : string [] + + /// The '-o:' output bin path, without the '-o:' + abstract CompilationBinOutputPath : string option + + /// The name of the project file. + abstract ProjectFileName : string + + /// Register for notifications for when the above change + abstract AdviseProjectSiteChanges : callbackOwnerKey: string * AdviseProjectSiteChanges -> unit + + /// Register for notifications when project is cleaned/rebuilt (and thus any live TypeProviders should be refreshed) + abstract AdviseProjectSiteCleaned : callbackOwnerKey: string * AdviseProjectSiteChanges -> unit + + // Register for notifications when project is closed. + abstract AdviseProjectSiteClosed : callbackOwnerKey: string * AdviseProjectSiteChanges -> unit + + /// A user-friendly description of the project. Used only for developer/DEBUG tooltips and such. + abstract Description : string + + /// The error list task reporter + abstract BuildErrorReporter : Microsoft.VisualStudio.Shell.Interop.IVsLanguageServiceBuildErrorReporter2 option with get, set + + /// False type resolution errors are invalid. This occurs with orphaned source files. The prior + /// type checking state is unknown. In this case we don't want to squiggle the type checking files. + abstract IsIncompleteTypeCheckEnvironment : bool + + /// target framework moniker + abstract TargetFrameworkMoniker : string + + /// Project Guid + abstract ProjectGuid : string + + /// timestamp the site was last loaded + abstract LoadTime : System.DateTime + + abstract ProjectProvider : IProvideProjectSite option \ No newline at end of file diff --git a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs index a3f8bc4de..ac9aa7cc2 100644 --- a/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs +++ b/vsintegration/src/FSharp.Editor/LanguageService/LanguageService.fs @@ -23,8 +23,7 @@ open Microsoft.FSharp.Compiler.CompileOps open Microsoft.FSharp.Compiler.SourceCodeServices open Microsoft.VisualStudio open Microsoft.VisualStudio.Editor -open Microsoft.VisualStudio.FSharp.LanguageService -open Microsoft.VisualStudio.FSharp.LanguageService.SiteProvider +open Microsoft.VisualStudio.FSharp.Editor.SiteProvider open Microsoft.VisualStudio.TextManager.Interop open Microsoft.VisualStudio.LanguageServices open Microsoft.VisualStudio.LanguageServices.Implementation.LanguageService diff --git a/vsintegration/src/FSharp.Editor/LanguageService/ProjectSitesAndFiles.fs b/vsintegration/src/FSharp.Editor/LanguageService/ProjectSitesAndFiles.fs new file mode 100644 index 000000000..6c5a2c61c --- /dev/null +++ b/vsintegration/src/FSharp.Editor/LanguageService/ProjectSitesAndFiles.fs @@ -0,0 +1,323 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +module internal rec Microsoft.VisualStudio.FSharp.Editor.SiteProvider + +open System +open System.IO +open System.Collections.Concurrent +open System.Diagnostics + +open Microsoft.FSharp.Compiler.SourceCodeServices +open Microsoft.CodeAnalysis +open Microsoft.VisualStudio +open Microsoft.VisualStudio.LanguageServices +open Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem +open Microsoft.VisualStudio.LanguageServices.Implementation.TaskList +open Microsoft.VisualStudio.Shell.Interop +open Microsoft.VisualStudio.TextManager.Interop + +open VSLangProj + +/// An additional interface that an IProjectSite object can implement to indicate it has an FSharpProjectOptions +/// already available, so we don't have to recreate it +type private IHaveCheckOptions = + abstract OriginalCheckOptions : unit -> string[] * FSharpProjectOptions + +let projectDisplayNameOf projectFileName = + if String.IsNullOrWhiteSpace projectFileName then projectFileName + else Path.GetFileNameWithoutExtension projectFileName + +/// A value and a function to recompute/refresh the value. The function is passed a flag indicating if a refresh is happening. +type Refreshable<'T> = 'T * (bool -> 'T) + +/// Convert from FSharpProjectOptions into IProjectSite. +type private ProjectSiteOfScriptFile(filename:string, referencedProjectFileNames, checkOptions: FSharpProjectOptions) = + interface IProjectSite with + override __.Description = sprintf "Script Closure at Root %s" filename + override __.CompilationSourceFiles = checkOptions.SourceFiles + override __.CompilationOptions = checkOptions.OtherOptions + override __.CompilationReferences = + checkOptions.OtherOptions + |> Array.choose (fun flag -> if flag.StartsWith("-r:") then Some flag.[3..] else None) + override __.CompilationBinOutputPath = None + override __.ProjectFileName = checkOptions.ProjectFileName + override __.BuildErrorReporter with get() = None and set _ = () + override __.AdviseProjectSiteChanges(_,_) = () + override __.AdviseProjectSiteCleaned(_,_) = () + override __.AdviseProjectSiteClosed(_,_) = () + override __.IsIncompleteTypeCheckEnvironment = checkOptions.IsIncompleteTypeCheckEnvironment + override __.TargetFrameworkMoniker = "" + override __.ProjectGuid = "" + override __.LoadTime = checkOptions.LoadTime + override __.ProjectProvider = None + + interface IHaveCheckOptions with + override __.OriginalCheckOptions() = (referencedProjectFileNames, checkOptions) + + override __.ToString() = sprintf "ProjectSiteOfScriptFile(%s)" filename + +/// An orphan file project is a .fs, .ml, .fsi, .mli that is not associated with a .fsproj. +/// By design, these are never going to typecheck because there is no affiliated references. +/// We show many squiggles in this case because they're not particularly informational. +type private ProjectSiteOfSingleFile(sourceFile) = + // CompilerFlags() gets called a lot, so pre-compute what we can + static let compilerFlags = + let flags = ["--noframework";"--warn:3"] + let assumeDotNetFramework = true + let defaultReferences = + [ for r in CompilerEnvironment.DefaultReferencesForOrphanSources(assumeDotNetFramework) do + yield sprintf "-r:%s%s" r (if r.EndsWith(".dll",StringComparison.OrdinalIgnoreCase) then "" else ".dll") ] + (flags @ defaultReferences) + |> List.toArray + |> Array.choose (fun flag -> if flag.StartsWith("-r:") then Some flag.[3..] elif flag.StartsWith("--reference:") then Some flag.[12..] else None) + + let projectFileName = sourceFile + ".orphan.fsproj" + + interface IProjectSite with + override __.Description = projectFileName + override __.CompilationSourceFiles = [|sourceFile|] + override __.CompilationOptions = compilerFlags + override __.CompilationReferences = compilerFlags + override __.CompilationBinOutputPath = None + override __.ProjectFileName = projectFileName + override __.BuildErrorReporter with get() = None and set _v = () + override __.AdviseProjectSiteChanges(_,_) = () + override __.AdviseProjectSiteCleaned(_,_) = () + override __.AdviseProjectSiteClosed(_,_) = () + override __.IsIncompleteTypeCheckEnvironment = true + override __.TargetFrameworkMoniker = "" + override __.ProjectGuid = "" + override __.LoadTime = new DateTime(2000,1,1) // any constant time is fine, orphan files do not interact with reloading based on update time + override __.ProjectProvider = None + + override __.ToString() = sprintf "ProjectSiteOfSingleFile(%s)" sourceFile + +/// Manage Storage of FSharpProjectOptions the options for a project +type internal FSharpProjectOptionsTable () = + + // A table of information about projects, excluding single-file projects. + let projectTable = ConcurrentDictionary>() + let commandLineOptions = new ConcurrentDictionary() + + /// Re-fetch all of the options for everything that references projectId + let refreshInfoForProjectsThatReferenceThisProject (projectId:ProjectId) = + for KeyValue(otherProjectId, ((referencedProjectIds, _parsingOptions, _site, _options), refresh)) in projectTable.ToArray() do + for referencedProjectId in referencedProjectIds do + if referencedProjectId = projectId then + projectTable.[otherProjectId] <- (refresh true, refresh) + + /// Add or update a project in the project table + member __.AddOrUpdateProject(projectId:ProjectId, refresh) = + projectTable.[projectId] <- (refresh false, refresh) + refreshInfoForProjectsThatReferenceThisProject(projectId) + + /// Clear a project from the project table + member __.ClearInfoForProject(projectId:ProjectId) = + projectTable.TryRemove(projectId) |> ignore + refreshInfoForProjectsThatReferenceThisProject projectId + + /// Get the options for a project + member __.TryGetOptionsForProject(projectId:ProjectId) = + match projectTable.TryGetValue(projectId) with + | true, ((_referencedProjects, parsingOptions, site, projectOptions), _) -> Some (parsingOptions, site, projectOptions) + | _ -> None + + /// Given a projectId return the most recent set of command line options for it + member __.GetCommandLineOptionsWithProjectId(projectId:ProjectId) = + match commandLineOptions.TryGetValue projectId with + | true, (sources, references, options) -> sources, references, options + | _ -> [||], [||], [||] + + /// Store the command line options for a projectId + member __.SetOptionsWithProjectId(projectId:ProjectId, sourcePaths:string[], referencePaths:string[], options:string[]) = + commandLineOptions.[projectId] <- (sourcePaths, referencePaths, options) + + +let internal provideProjectSiteProvider(workspace:VisualStudioWorkspaceImpl, project:Project, serviceProvider:System.IServiceProvider, projectOptionsTable:FSharpProjectOptionsTable option) = + let hier = workspace.GetHierarchy(project.Id) + let getCommandLineOptionsWithProjectId (projectId) = + match projectOptionsTable with + | Some (options) -> options.GetCommandLineOptionsWithProjectId(projectId) + | None -> [||], [||], [||] + { + new IProvideProjectSite with + member x.GetProjectSite() = + let fst (a, _, _) = a + let snd (_, b, _) = b + let mutable errorReporter = + let reporter = ProjectExternalErrorReporter(project.Id, "FS", serviceProvider) + Some(reporter:> IVsLanguageServiceBuildErrorReporter2) + + { + new IProjectSite with + member __.Description = project.Name + member __.CompilationSourceFiles = getCommandLineOptionsWithProjectId(project.Id) |> fst + member __.CompilationOptions = + let _,references,options = getCommandLineOptionsWithProjectId(project.Id) + Array.concat [options; references |> Array.map(fun r -> "-r:" + r)] + member __.CompilationReferences = getCommandLineOptionsWithProjectId(project.Id) |> snd + member site.CompilationBinOutputPath = site.CompilationOptions |> Array.tryPick (fun s -> if s.StartsWith("-o:") then Some s.[3..] else None) + member __.ProjectFileName = project.FilePath + member __.AdviseProjectSiteChanges(_,_) = () + member __.AdviseProjectSiteCleaned(_,_) = () + member __.AdviseProjectSiteClosed(_,_) = () + member __.IsIncompleteTypeCheckEnvironment = false + member __.TargetFrameworkMoniker = "" + member __.ProjectGuid = project.Id.Id.ToString() + member __.LoadTime = System.DateTime.Now + member __.ProjectProvider = Some (x) + member __.BuildErrorReporter with get () = errorReporter and set (v) = errorReporter <- v + } + interface IVsHierarchy with + member __.SetSite(psp) = hier.SetSite(psp) + member __.GetSite(psp) = hier.GetSite(ref psp) + member __.QueryClose(pfCanClose)= hier.QueryClose(ref pfCanClose) + member __.Close() = hier.Close() + member __.GetGuidProperty(itemid, propid, pguid) = hier.GetGuidProperty(itemid, propid, ref pguid) + member __.SetGuidProperty(itemid, propid, rguid) = hier.SetGuidProperty(itemid, propid, ref rguid) + member __.GetProperty(itemid, propid, pvar) = hier.GetProperty(itemid, propid, ref pvar) + member __.SetProperty(itemid, propid, var) = hier.SetProperty(itemid, propid, var) + member __.GetNestedHierarchy(itemid, iidHierarchyNested, ppHierarchyNested, pitemidNested) = + hier.GetNestedHierarchy(itemid, ref iidHierarchyNested, ref ppHierarchyNested, ref pitemidNested) + member __.GetCanonicalName(itemid, pbstrName) = hier.GetCanonicalName(itemid, ref pbstrName) + member __.ParseCanonicalName(pszName, pitemid) = hier.ParseCanonicalName(pszName, ref pitemid) + member __.Unused0() = hier.Unused0() + member __.AdviseHierarchyEvents(pEventSink, pdwCookie) = hier.AdviseHierarchyEvents(pEventSink, ref pdwCookie) + member __.UnadviseHierarchyEvents(dwCookie) = hier.UnadviseHierarchyEvents(dwCookie) + member __.Unused1() = hier.Unused1() + member __.Unused2() = hier.Unused2() + member __.Unused3() = hier.Unused3() + member __.Unused4() = hier.Unused4() + } + +/// Information about projects, open files and other active artifacts in visual studio. +/// Keeps track of the relationship between IVsTextLines buffers, IFSharpSource_DEPRECATED objects, IProjectSite objects and FSharpProjectOptions +[] +type internal ProjectSitesAndFiles() = + static let mutable stamp = 0L + + static let fullOutputAssemblyPath (p:EnvDTE.Project) = + let getProperty tag = + try Some (p.Properties.[tag].Value.ToString()) with _ -> None + getProperty "FullPath" + |> Option.bind (fun fullPath -> + (try Some (p.ConfigurationManager.ActiveConfiguration.Properties.["OutputPath"].Value.ToString()) with _ -> None) + |> Option.bind (fun outputPath -> + getProperty "OutputFileName" + |> Option.map (fun outputFileName -> Path.Combine(fullPath, outputPath, outputFileName)))) + |> Option.bind (fun path -> try Some (Path.GetFullPath path) with _ -> None) + + static let referencedProjects (projectSite:IProjectSite) = + match projectSite.ProjectProvider with + | None -> None + | Some (:? IVsHierarchy as hier) -> + match hier.GetProperty(VSConstants.VSITEMID_ROOT, int __VSHPROPID.VSHPROPID_ExtObject) with + | VSConstants.S_OK, (:? EnvDTE.Project as p) when not (isNull p) -> + Some ((p.Object :?> VSLangProj.VSProject).References + |> Seq.cast + |> Seq.choose (fun r -> + Option.ofObj r + |> Option.bind (fun r -> try Option.ofObj r.SourceProject with _ -> None)) ) + | _ -> None + | Some _ -> None + + static let rec referencedProvideProjectSites(projectSite:IProjectSite, serviceProvider:System.IServiceProvider, extraProjectInfo:obj option, projectOptionsTable:FSharpProjectOptionsTable option) = + let getReferencesForSolutionService (solutionService:IVsSolution) = + [| + match referencedProjects projectSite, extraProjectInfo with + | None, Some (:? VisualStudioWorkspaceImpl as workspace) when not (isNull workspace.CurrentSolution)-> + let path = projectSite.ProjectFileName + if not (String.IsNullOrWhiteSpace(path)) then + let projectId = workspace.ProjectTracker.GetOrCreateProjectIdForPath(path, projectDisplayNameOf path) + let project = workspace.CurrentSolution.GetProject(projectId) + if not (isNull project) then + for reference in project.ProjectReferences do + let project = workspace.CurrentSolution.GetProject(reference.ProjectId) + if not (isNull project) && project.Language = FSharpConstants.FSharpLanguageName then + let siteProvider = provideProjectSiteProvider (workspace, project, serviceProvider, projectOptionsTable) + let referenceProject = workspace.ProjectTracker.GetProject(reference.ProjectId) + let outputPath = referenceProject.BinOutputPath + yield Some projectId, project.FilePath, outputPath, siteProvider + + | (Some references), _ -> + for p in references do + match solutionService.GetProjectOfUniqueName(p.UniqueName) with + | VSConstants.S_OK, (:? IProvideProjectSite as ps) -> + yield None, p.FileName, (fullOutputAssemblyPath p) |> Option.defaultValue "", ps + | _ -> () + | None, _ -> () + |] + let solutionService = try Some (serviceProvider.GetService(typeof) :?> IVsSolution) with _ -> None + seq { match solutionService with + | Some solutionService -> + for reference in getReferencesForSolutionService solutionService do + yield reference + | None -> () + } + + static let rec referencedProjectsOf(enableInMemoryCrossProjectReferences, tryGetOptionsForReferencedProject, projectSite, serviceProvider, extraProjectInfo, projectOptionsTable, useUniqueStamp) = + [| for (projectId, projectFileName, outputPath, projectSiteProvider) in referencedProvideProjectSites (projectSite, serviceProvider, extraProjectInfo, projectOptionsTable) do + let referencedProjectOptions = + // Lookup may not succeed if the project has not been established yet + // In this case we go and compute the options recursively. + match tryGetOptionsForReferencedProject projectFileName with + | None -> getProjectOptionsForProjectSite (enableInMemoryCrossProjectReferences, tryGetOptionsForReferencedProject, projectSiteProvider.GetProjectSite(), serviceProvider, projectId, projectFileName, extraProjectInfo, projectOptionsTable, useUniqueStamp) |> snd + | Some options -> options + yield projectFileName, (outputPath, referencedProjectOptions) |] + + and getProjectOptionsForProjectSite(enableInMemoryCrossProjectReferences, tryGetOptionsForReferencedProject, projectSite, serviceProvider, projectId, fileName, extraProjectInfo, projectOptionsTable, useUniqueStamp) = + let referencedProjectFileNames, referencedProjectOptions = + if enableInMemoryCrossProjectReferences then + referencedProjectsOf(enableInMemoryCrossProjectReferences, tryGetOptionsForReferencedProject, projectSite, serviceProvider, extraProjectInfo, projectOptionsTable, useUniqueStamp) + |> Array.unzip + else [| |], [| |] + let option = + let newOption () = { + ProjectFileName = projectSite.ProjectFileName + SourceFiles = projectSite.CompilationSourceFiles + OtherOptions = projectSite.CompilationOptions + ReferencedProjects = referencedProjectOptions + IsIncompleteTypeCheckEnvironment = projectSite.IsIncompleteTypeCheckEnvironment + UseScriptResolutionRules = SourceFile.MustBeSingleFileProject fileName + LoadTime = projectSite.LoadTime + UnresolvedReferences = None + OriginalLoadReferences = [] + ExtraProjectInfo=extraProjectInfo + Stamp = if useUniqueStamp then (stamp <- stamp + 1L; Some stamp) else None + } + match projectId, projectOptionsTable with + | Some id, Some optionsTable -> + // Get options from cache + match optionsTable.TryGetOptionsForProject(id) with + | Some (_parsingOptions, _site, projectOptions) -> + if projectSite.CompilationSourceFiles <> projectOptions.SourceFiles || + projectSite.CompilationOptions <> projectOptions.OtherOptions || + referencedProjectOptions <> projectOptions.ReferencedProjects then + newOption() + else + projectOptions + | _ -> newOption() + | _ -> newOption() + referencedProjectFileNames, option + + /// Construct a project site for a single file. May be a single file project (for scripts) or an orphan project site (for everything else). + static member ProjectSiteOfSingleFile(filename:string) : IProjectSite = + if SourceFile.MustBeSingleFileProject(filename) then + Debug.Assert(false, ".fsx or .fsscript should have been treated as implicit project") + failwith ".fsx or .fsscript should have been treated as implicit project" + new ProjectSiteOfSingleFile(filename) :> IProjectSite + + static member GetReferencedProjectSites(projectSite:IProjectSite, serviceProvider:System.IServiceProvider, extraProjectInfo, projectOptions) = + referencedProvideProjectSites (projectSite, serviceProvider, extraProjectInfo, projectOptions) + |> Seq.map (fun (_, _, _, ps) -> ps.GetProjectSite()) + |> Seq.toArray + + /// Create project options for this project site. + static member GetProjectOptionsForProjectSite(enableInMemoryCrossProjectReferences, tryGetOptionsForReferencedProject, projectSite:IProjectSite, serviceProvider, projectId, filename, extraProjectInfo, projectOptionsTable, useUniqueStamp) = + match projectSite with + | :? IHaveCheckOptions as hco -> hco.OriginalCheckOptions() + | _ -> getProjectOptionsForProjectSite(enableInMemoryCrossProjectReferences, tryGetOptionsForReferencedProject, projectSite, serviceProvider, projectId, filename, extraProjectInfo, projectOptionsTable, useUniqueStamp) + + /// Create project site for these project options + static member CreateProjectSiteForScript (filename, referencedProjectFileNames, checkOptions) = + ProjectSiteOfScriptFile (filename, referencedProjectFileNames, checkOptions) :> IProjectSite \ No newline at end of file diff --git a/vsintegration/src/FSharp.ProjectSystem.FSharp/Project.fs b/vsintegration/src/FSharp.ProjectSystem.FSharp/Project.fs index 9e91f0c05..fab0c09c4 100644 --- a/vsintegration/src/FSharp.ProjectSystem.FSharp/Project.fs +++ b/vsintegration/src/FSharp.ProjectSystem.FSharp/Project.fs @@ -1471,7 +1471,7 @@ namespace rec Microsoft.VisualStudio.FSharp.ProjectSystem member __.TargetFrameworkMoniker = x.GetTargetFrameworkMoniker() member __.ProjectGuid = x.GetProjectGuid() member __.LoadTime = creationTime - member __.ProjectProvider = Some (x :> IProvideProjectSite) + member __.ProjectProvider = Some (x :> Microsoft.VisualStudio.FSharp.LanguageService.IProvideProjectSite) } @@ -1507,7 +1507,7 @@ namespace rec Microsoft.VisualStudio.FSharp.ProjectSystem member __.TargetFrameworkMoniker = targetFrameworkMoniker member __.ProjectGuid = x.GetProjectGuid() member __.LoadTime = creationTime - member __.ProjectProvider = Some (x :> IProvideProjectSite) + member __.ProjectProvider = Some (x :> Microsoft.VisualStudio.FSharp.LanguageService.IProvideProjectSite) } // let the language service ask us questions diff --git a/vsintegration/tests/Salsa/FSharpLanguageServiceTestable.fs b/vsintegration/tests/Salsa/FSharpLanguageServiceTestable.fs index 21b290c38..912461947 100644 --- a/vsintegration/tests/Salsa/FSharpLanguageServiceTestable.fs +++ b/vsintegration/tests/Salsa/FSharpLanguageServiceTestable.fs @@ -113,7 +113,7 @@ type internal FSharpLanguageServiceTestable() as this = serviceProvider <- None /// Respond to project settings changes - member this.OnProjectSettingsChanged(site:IProjectSite) = + member this.OnProjectSettingsChanged(site: Microsoft.VisualStudio.FSharp.LanguageService.IProjectSite) = // The project may have changed its references. These would be represented as 'dependency files' of each source file. Each source file will eventually start listening // for changes to those dependencies, at which point we'll get OnDependencyFileCreateOrDelete notifications. Until then, though, we just 'make a note' that this project is out of date. bgRequests.AddOutOfDateProjectFileName(site.ProjectFileName) @@ -126,7 +126,7 @@ type internal FSharpLanguageServiceTestable() as this = | None -> () /// Respond to project being cleaned/rebuilt (any live type providers in the project should be refreshed) - member this.OnProjectCleaned(projectSite:IProjectSite) = + member this.OnProjectCleaned(projectSite:Microsoft.VisualStudio.FSharp.LanguageService.IProjectSite) = let enableInMemoryCrossProjectReferences = true let _, checkOptions = ProjectSitesAndFiles.GetProjectOptionsForProjectSite(enableInMemoryCrossProjectReferences, (fun _ -> None), projectSite, serviceProvider.Value, None(*projectId*), "" ,None, None, false) this.FSharpChecker.NotifyProjectCleaned(checkOptions) |> Async.RunSynchronously @@ -147,12 +147,12 @@ type internal FSharpLanguageServiceTestable() as this = match result with | Some(hier,_) -> match hier with - | :? IProvideProjectSite as siteProvider -> + | :? Microsoft.VisualStudio.FSharp.LanguageService.IProvideProjectSite as siteProvider -> let site = siteProvider.GetProjectSite() site.AdviseProjectSiteChanges(FSharpConstants.FSharpLanguageServiceCallbackName, - new AdviseProjectSiteChanges(fun () -> this.OnProjectSettingsChanged(site))) + new Microsoft.VisualStudio.FSharp.LanguageService.AdviseProjectSiteChanges(fun () -> this.OnProjectSettingsChanged(site))) site.AdviseProjectSiteCleaned(FSharpConstants.FSharpLanguageServiceCallbackName, - new AdviseProjectSiteChanges(fun () -> this.OnProjectCleaned(site))) + new Microsoft.VisualStudio.FSharp.LanguageService.AdviseProjectSiteChanges(fun () -> this.OnProjectCleaned(site))) | _ -> // This can happen when the file is in a solution folder or in, say, a C# project. () -- GitLab