未验证 提交 4ad577f1 编写于 作者: K Kevin Ransom (msft) 提交者: GitHub

Minor cleanup (#3854)

上级 0991a3b5
......@@ -15,17 +15,16 @@ open System.Linq
open System.Runtime.CompilerServices
open System.Runtime.InteropServices
open System.Threading
open Microsoft.FSharp.Compiler.CompileOps
open Microsoft.FSharp.Compiler.SourceCodeServices
open Microsoft.VisualStudio.FSharp.LanguageService.SiteProvider
open Microsoft.CodeAnalysis
open Microsoft.CodeAnalysis.Diagnostics
open Microsoft.CodeAnalysis.Completion
open Microsoft.CodeAnalysis.Options
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.TextManager.Interop
open Microsoft.VisualStudio.LanguageServices
open Microsoft.VisualStudio.LanguageServices.Implementation.LanguageService
......@@ -34,7 +33,6 @@ open Microsoft.VisualStudio.LanguageServices.Implementation.TaskList
open Microsoft.VisualStudio.LanguageServices.ProjectSystem
open Microsoft.VisualStudio.Shell
open Microsoft.VisualStudio.Shell.Interop
open Microsoft.VisualStudio.FSharp.LanguageService
open Microsoft.VisualStudio.ComponentModelHost
// Exposes FSharpChecker as MEF export
......@@ -89,7 +87,7 @@ type internal FSharpCheckerProvider
// It also allows a 'cheaper' route to get the project options relevant to parsing (e.g. the #define values).
// The main entrypoints are TryGetOptionsForDocumentOrProject and TryGetOptionsForEditingDocumentOrProject.
[<Export(typeof<FSharpProjectOptionsManager>); Composition.Shared>]
type internal FSharpProjectOptionsManager
type internal FSharpProjectOptionsManager
[<ImportingConstructor>]
(
checkerProvider: FSharpCheckerProvider,
......@@ -114,33 +112,36 @@ type internal FSharpProjectOptionsManager
/// Clear a project from the project table
member this.ClearInfoForProject(projectId:ProjectId) = projectOptionsTable.ClearInfoForProject(projectId)
/// Clear a project from the single file project table
member this.ClearInfoForSingleFileProject(projectId) =
singleFileProjectTable.TryRemove(projectId) |> ignore
/// Update a project in the single file project table
member this.AddOrUpdateSingleFileProject(projectId, data) = singleFileProjectTable.[projectId] <- data
/// Get the exact options for a single-file script
member this.ComputeSingleFileOptions (tryGetOrCreateProjectId, fileName, loadTime, fileContents, workspace: Workspace) = async {
let extraProjectInfo = Some(box workspace)
let tryGetOptionsForReferencedProject f = f |> tryGetOrCreateProjectId |> Option.bind this.TryGetOptionsForProject |> Option.map(fun (_, _, projectOptions) -> projectOptions)
if SourceFile.MustBeSingleFileProject(fileName) then
// NOTE: we don't use a unique stamp for single files, instead comparing options structurally.
// This is because we repeatedly recompute the options.
let optionsStamp = None
let! options, _diagnostics = checkerProvider.Checker.GetProjectOptionsFromScript(fileName, fileContents, loadTime, [| |], ?extraProjectInfo=extraProjectInfo, ?optionsStamp=optionsStamp)
// NOTE: we don't use FCS cross-project references from scripts to projects. THe projects must have been
// compiled and #r will refer to files on disk
let referencedProjectFileNames = [| |]
let site = ProjectSitesAndFiles.CreateProjectSiteForScript(fileName, referencedProjectFileNames, options)
let deps, projectOptions = ProjectSitesAndFiles.GetProjectOptionsForProjectSite(Settings.LanguageServicePerformance.EnableInMemoryCrossProjectReferences, tryGetOptionsForReferencedProject, site, serviceProvider, (tryGetOrCreateProjectId fileName), fileName, options.ExtraProjectInfo, Some projectOptionsTable, true)
let parsingOptions, _ = checkerProvider.Checker.GetParsingOptionsFromProjectOptions(projectOptions)
return (deps, parsingOptions, projectOptions)
else
let site = ProjectSitesAndFiles.ProjectSiteOfSingleFile(fileName)
let deps, projectOptions = ProjectSitesAndFiles.GetProjectOptionsForProjectSite(Settings.LanguageServicePerformance.EnableInMemoryCrossProjectReferences, tryGetOptionsForReferencedProject, site, serviceProvider, (tryGetOrCreateProjectId fileName), fileName, extraProjectInfo, Some projectOptionsTable, true)
let parsingOptions, _ = checkerProvider.Checker.GetParsingOptionsFromProjectOptions(projectOptions)
return (deps, parsingOptions, projectOptions)
}
member this.ComputeSingleFileOptions (tryGetOrCreateProjectId, fileName, loadTime, fileContents) =
async {
let extraProjectInfo = Some(box workspace)
let tryGetOptionsForReferencedProject f = f |> tryGetOrCreateProjectId |> Option.bind this.TryGetOptionsForProject |> Option.map(fun (_, _, projectOptions) -> projectOptions)
if SourceFile.MustBeSingleFileProject(fileName) then
// NOTE: we don't use a unique stamp for single files, instead comparing options structurally.
// This is because we repeatedly recompute the options.
let optionsStamp = None
let! options, _diagnostics = checkerProvider.Checker.GetProjectOptionsFromScript(fileName, fileContents, loadTime, [| |], ?extraProjectInfo=extraProjectInfo, ?optionsStamp=optionsStamp)
// NOTE: we don't use FCS cross-project references from scripts to projects. THe projects must have been
// compiled and #r will refer to files on disk
let referencedProjectFileNames = [| |]
let site = ProjectSitesAndFiles.CreateProjectSiteForScript(fileName, referencedProjectFileNames, options)
let deps, projectOptions = ProjectSitesAndFiles.GetProjectOptionsForProjectSite(Settings.LanguageServicePerformance.EnableInMemoryCrossProjectReferences, tryGetOptionsForReferencedProject, site, serviceProvider, (tryGetOrCreateProjectId fileName), fileName, options.ExtraProjectInfo, Some projectOptionsTable, true)
let parsingOptions, _ = checkerProvider.Checker.GetParsingOptionsFromProjectOptions(projectOptions)
return (deps, parsingOptions, projectOptions)
else
let site = ProjectSitesAndFiles.ProjectSiteOfSingleFile(fileName)
let deps, projectOptions = ProjectSitesAndFiles.GetProjectOptionsForProjectSite(Settings.LanguageServicePerformance.EnableInMemoryCrossProjectReferences, tryGetOptionsForReferencedProject, site, serviceProvider, (tryGetOrCreateProjectId fileName), fileName, extraProjectInfo, Some projectOptionsTable, true)
let parsingOptions, _ = checkerProvider.Checker.GetParsingOptionsFromProjectOptions(projectOptions)
return (deps, parsingOptions, projectOptions)
}
/// Update the info for a project in the project table
member this.UpdateProjectInfo(tryGetOrCreateProjectId, projectId, site, userOpName) =
......@@ -164,32 +165,34 @@ type internal FSharpProjectOptionsManager
| _ -> FSharpParsingOptions.Default
CompilerEnvironment.GetCompilationDefinesForEditing(document.Name, parsingOptions)
/// Try and get the Options for a project
member this.TryGetOptionsForProject(projectId:ProjectId) = projectOptionsTable.TryGetOptionsForProject(projectId)
/// Get the exact options for a document or project
member this.TryGetOptionsForDocumentOrProject(document: Document) = async {
let projectId = document.Project.Id
// The options for a single-file script project are re-requested each time the file is analyzed. This is because the
// single-file project may contain #load and #r references which are changing as the user edits, and we may need to re-analyze
// to determine the latest settings. FCS keeps a cache to help ensure these are up-to-date.
match singleFileProjectTable.TryGetValue(projectId) with
| true, (loadTime, _, _) ->
try
let fileName = document.FilePath
let! cancellationToken = Async.CancellationToken
let! sourceText = document.GetTextAsync(cancellationToken) |> Async.AwaitTask
// NOTE: we don't use FCS cross-project references from scripts to projects. The projects must have been
// compiled and #r will refer to files on disk.
let tryGetOrCreateProjectId _ = None
let! _referencedProjectFileNames, parsingOptions, projectOptions = this.ComputeSingleFileOptions (tryGetOrCreateProjectId, fileName, loadTime, sourceText.ToString(), document.Project.Solution.Workspace)
this.AddOrUpdateSingleFileProject(projectId, (loadTime, parsingOptions, projectOptions))
return Some (parsingOptions, None, projectOptions)
with ex ->
Assert.Exception(ex)
return None
| _ -> return this.TryGetOptionsForProject(projectId)
}
member this.TryGetOptionsForDocumentOrProject(document: Document) =
async {
let projectId = document.Project.Id
// The options for a single-file script project are re-requested each time the file is analyzed. This is because the
// single-file project may contain #load and #r references which are changing as the user edits, and we may need to re-analyze
// to determine the latest settings. FCS keeps a cache to help ensure these are up-to-date.
match singleFileProjectTable.TryGetValue(projectId) with
| true, (loadTime, _, _) ->
try
let fileName = document.FilePath
let! cancellationToken = Async.CancellationToken
let! sourceText = document.GetTextAsync(cancellationToken) |> Async.AwaitTask
// NOTE: we don't use FCS cross-project references from scripts to projects. The projects must have been
// compiled and #r will refer to files on disk.
let tryGetOrCreateProjectId _ = None
let! _referencedProjectFileNames, parsingOptions, projectOptions = this.ComputeSingleFileOptions (tryGetOrCreateProjectId, fileName, loadTime, sourceText.ToString())
this.AddOrUpdateSingleFileProject(projectId, (loadTime, parsingOptions, projectOptions))
return Some (parsingOptions, None, projectOptions)
with ex ->
Assert.Exception(ex)
return None
| _ -> return this.TryGetOptionsForProject(projectId)
}
/// Get the options for a document or project relevant for syntax processing.
/// Quicker then TryGetOptionsForDocumentOrProject as it doesn't need to recompute the exact project options for a script.
......@@ -540,7 +543,7 @@ type
let projectDisplayName = projectDisplayNameOf projectFileName
let projectId = workspace.ProjectTracker.GetOrCreateProjectIdForPath(projectFileName, projectDisplayName)
let _referencedProjectFileNames, parsingOptions, projectOptions = projectInfoManager.ComputeSingleFileOptions (tryGetOrCreateProjectId workspace, fileName, loadTime, fileContents, workspace) |> Async.RunSynchronously
let _referencedProjectFileNames, parsingOptions, projectOptions = projectInfoManager.ComputeSingleFileOptions (tryGetOrCreateProjectId workspace, fileName, loadTime, fileContents) |> Async.RunSynchronously
projectInfoManager.AddOrUpdateSingleFileProject(projectId, (loadTime, parsingOptions, projectOptions))
if isNull (workspace.ProjectTracker.GetProject projectId) then
......
......@@ -75,7 +75,7 @@ type private ProjectSiteOfScriptFile(filename:string, referencedProjectFileNames
|> Array.choose (fun flag -> if flag.StartsWith("-r:") then Some flag.[3..] else None)
override this.CompilationBinOutputPath = None
override this.ProjectFileName = checkOptions.ProjectFileName
override this.BuildErrorReporter with get() = None and set _v = ()
override this.BuildErrorReporter with get() = None and set _ = ()
override this.AdviseProjectSiteChanges(_,_) = ()
override this.AdviseProjectSiteCleaned(_,_) = ()
override this.AdviseProjectSiteClosed(_,_) = ()
......@@ -128,11 +128,12 @@ type private ProjectSiteOfSingleFile(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<ProjectId, Refreshable<ProjectId[] * FSharpParsingOptions * IProjectSite option * FSharpProjectOptions>>()
let commandLineOptions = new ConcurrentDictionary<ProjectId, string[]*string[]*string[]>()
/// Go and re-get all of the options for everything that references projectId
/// 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
......@@ -161,69 +162,74 @@ type internal FSharpProjectOptionsTable () =
| true, (sources, references, options) -> sources, references, options
| _ -> [||], [||], [||]
/// Store the command line options for a projectId
member this.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()
{
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
[<Sealed>]
type internal ProjectSitesAndFiles() =
static let sourceUserDataGuid = new Guid("{55F834FD-B950-4C61-BBAA-0511ABAF4AE2}") // Guid for source user data on text buffer
static let mutable stamp = 0L
static let tryGetProjectSite(hierarchy:IVsHierarchy) =
......@@ -285,7 +291,8 @@ type internal ProjectSitesAndFiles() =
let solutionService = try Some (serviceProvider.GetService(typeof<SVsSolution>) :?> IVsSolution) with _ -> None
seq { match solutionService with
| Some solutionService ->
for reference in getReferencesForSolutionService solutionService do yield reference
for reference in getReferencesForSolutionService solutionService do
yield reference
| None -> ()
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册