@@ -20,23 +20,23 @@ open Microsoft.VisualStudio.LanguageServices.ProjectSystem
2020open Microsoft.VisualStudio .Shell .Interop
2121
2222[<Sealed>]
23- type internal LegacyProjectWorkspaceMap ( workspace : VisualStudioWorkspaceImpl ,
23+ type internal LegacyProjectWorkspaceMap ( workspace : VisualStudioWorkspaceImpl ,
2424 solution: IVsSolution,
2525 projectInfoManager: FSharpProjectOptionsManager,
26- projectContextFactory: IWorkspaceProjectContextFactory,
27- serviceProvider: IServiceProvider) as this=
26+ projectContextFactory: IWorkspaceProjectContextFactory) as this=
2827
2928let invalidPathChars = set( Path.GetInvalidPathChars())
3029let optionsAssociation = ConditionalWeakTable< IWorkspaceProjectContext, string[]>()
3130let isPathWellFormed ( path : string ) = not ( String.IsNullOrWhiteSpace path) && path|> Seq.forall( fun c -> not ( Set.contains c invalidPathChars))
3231
3332let legacyProjectLookup = ConcurrentDictionary()
33+ let setupQueue = ConcurrentQueue()
3434
3535let tryGetOrCreateProjectId ( workspace : VisualStudioWorkspaceImpl ) ( projectFileName : string ) =
3636let projectDisplayName = projectDisplayNameOf projectFileName
3737 Some( workspace.ProjectTracker.GetOrCreateProjectIdForPath( projectFileName, projectDisplayName))
3838
39- member this.Initialize () =
39+ do
4040 solution.AdviseSolutionEvents( this) |> ignore
4141
4242/// Sync the Roslyn information for the project held in 'projectContext' to match the information given by 'site'.
@@ -110,74 +110,77 @@ type internal LegacyProjectWorkspaceMap(workspace: VisualStudioWorkspaceImpl,
110110
111111member this.SetupLegacyProjectFile ( siteProvider : IProvideProjectSite , workspace : VisualStudioWorkspaceImpl , userOpName ) =
112112let userOpName = userOpName+ " .SetupProjectFile"
113- let rec setup ( site : IProjectSite ) =
113+ let rec setup ( site : IProjectSite ) =
114114let projectGuid = Guid( site.ProjectGuid)
115115let projectFileName = site.ProjectFileName
116116let projectDisplayName = projectDisplayNameOf projectFileName
117117
118- // This projectId is not guaranteed to be the same ProjectId that will actually be created once we call CreateProjectContext
119- // in Roslyn versions once https://github.com/dotnet/roslyn/pull/26931 is merged. Roslyn will still guarantee that once
120- // there is a project in the workspace with the same path, it'll return the ID of that. So this is sufficient to use
121- // in that case as long as we only use it to call GetProject.
122- let fakeProjectId = workspace.ProjectTracker.GetOrCreateProjectIdForPath( projectFileName, projectDisplayName)
123-
124- if isNull( workspace.ProjectTracker.GetProject fakeProjectId) then
125- let hierarchy =
126- site.ProjectProvider
127- |> Option.map( fun p -> p:?> IVsHierarchy)
128- |> Option.toObj
129-
130- // Roslyn is expecting site to be an IVsHierarchy.
131- // It just so happens that the object that implements IProvideProjectSite is also
132- // an IVsHierarchy. This assertion is to ensure that the assumption holds true.
133- Debug.Assert( not ( isNull hierarchy), " About to CreateProjectContext with a non-hierarchy site" )
134-
135- let projectContext =
136- projectContextFactory.CreateProjectContext(
137- FSharpConstants.FSharpLanguageName,
138- projectDisplayName,
139- projectFileName,
140- projectGuid,
141- hierarchy,
142- Option.toObj site.CompilationBinOutputPath)
118+ let hierarchy =
119+ site.ProjectProvider
120+ |> Option.map( fun p -> p:?> IVsHierarchy)
121+ |> Option.toObj
122+
123+ // Roslyn is expecting site to be an IVsHierarchy.
124+ // It just so happens that the object that implements IProvideProjectSite is also
125+ // an IVsHierarchy. This assertion is to ensure that the assumption holds true.
126+ Debug.Assert( not ( isNull hierarchy), " About to CreateProjectContext with a non-hierarchy site" )
127+
128+ let projectContext =
129+ projectContextFactory.CreateProjectContext(
130+ FSharpConstants.FSharpLanguageName,
131+ projectDisplayName,
132+ projectFileName,
133+ projectGuid,
134+ hierarchy,
135+ Option.toObj site.CompilationBinOutputPath)
143136
144- // The real project ID that was actually added. See comments for fakeProjectId why this one is actually good.
145- let realProjectId = workspace.ProjectTracker.GetOrCreateProjectIdForPath( projectFileName, projectDisplayName)
137+ // The real project ID that was actually added. See comments for fakeProjectId why this one is actually good.
138+ let realProjectId = workspace.ProjectTracker.GetOrCreateProjectIdForPath( projectFileName, projectDisplayName)
146139
147- // Sync IProjectSite --> projectContext, and IProjectSite --> ProjectInfoManage
148- this.SyncLegacyProject( realProjectId, projectContext, site, workspace, forceUpdate= true , userOpName= userOpName)
140+ // Sync IProjectSite --> projectContext, and IProjectSite --> ProjectInfoManage
141+ this.SyncLegacyProject( realProjectId, projectContext, site, workspace, forceUpdate= true , userOpName= userOpName)
149142
150- site.BuildErrorReporter<- Some( projectContext:?> Microsoft.VisualStudio.Shell.Interop.IVsLanguageServiceBuildErrorReporter2)
143+ site.BuildErrorReporter<- Some( projectContext:?> Microsoft.VisualStudio.Shell.Interop.IVsLanguageServiceBuildErrorReporter2)
151144
152- // TODO: consider forceUpdate = false here. forceUpdate=true may be causing repeated computation?
153- site.AdviseProjectSiteChanges( FSharpConstants.FSharpLanguageServiceCallbackName,
154- AdviseProjectSiteChanges( fun () -> this.SyncLegacyProject( realProjectId, projectContext, site, workspace, forceUpdate= true , userOpName= " AdviseProjectSiteChanges." + userOpName)))
145+ // TODO: consider forceUpdate = false here. forceUpdate=true may be causing repeated computation?
146+ site.AdviseProjectSiteChanges( FSharpConstants.FSharpLanguageServiceCallbackName,
147+ AdviseProjectSiteChanges( fun () -> this.SyncLegacyProject( realProjectId, projectContext, site, workspace, forceUpdate= true , userOpName= " AdviseProjectSiteChanges." + userOpName)))
155148
156- site.AdviseProjectSiteClosed( FSharpConstants.FSharpLanguageServiceCallbackName,
157- AdviseProjectSiteChanges( fun () ->
158- projectInfoManager.ClearInfoForProject( realProjectId)
159- optionsAssociation.Remove( projectContext) |> ignore
160- projectContext.Dispose()))
161-
162- for referencedSitein ProjectSitesAndFiles.GetReferencedProjectSites( Some realProjectId, site, serviceProvider, Some( workspace:> obj), workspace.CurrentSolution, Some projectInfoManager.FSharpOptions) do
163- setup referencedSite
149+ site.AdviseProjectSiteClosed( FSharpConstants.FSharpLanguageServiceCallbackName,
150+ AdviseProjectSiteChanges( fun () ->
151+ projectInfoManager.ClearInfoForProject( realProjectId)
152+ optionsAssociation.Remove( projectContext) |> ignore
153+ projectContext.Dispose()))
164154
165155 setup( siteProvider.GetProjectSite())
166156
167157interface IVsSolutionEventswith
168158
169- member __.OnAfterCloseSolution ( _ ) = VSConstants.S_ OK
159+ member __.OnAfterCloseSolution ( _ ) =
160+ // Clear
161+ let mutable setup = Unchecked.defaultof<_>
162+ while setupQueue.TryDequeue(& setup) do ()
163+ VSConstants.S_ OK
170164
171165member __.OnAfterLoadProject ( _ , _ ) = VSConstants.S_ OK
172166
173167member __.OnAfterOpenProject ( hier , _ ) =
174168match hierwith
175169| :? IProvideProjectSiteas siteProvider->
176- this.SetupLegacyProjectFile( siteProvider, workspace, " LegacyProjectWorkspaceMap.Initialize" )
170+ let setup = fun () -> this.SetupLegacyProjectFile( siteProvider, workspace, " LegacyProjectWorkspaceMap.OnAfterOpenProject" )
171+ let _ , o = solution.GetProperty( int__ VSPROPID.VSPROPID_ IsSolutionOpen)
172+ if ( match owith | :? boolas isOpen-> isOpen| _ -> false ) then
173+ setup()
174+ else
175+ setupQueue.Enqueue( setup)
177176| _ -> ()
178177 VSConstants.S_ OK
179178
180- member __.OnAfterOpenSolution ( _ , _ ) = VSConstants.S_ OK
179+ member __.OnAfterOpenSolution ( _ , _ ) =
180+ let mutable setup = Unchecked.defaultof<_>
181+ while setupQueue.TryDequeue(& setup) do
182+ setup()
183+ VSConstants.S_ OK
181184
182185member __.OnBeforeCloseProject ( hier , _ ) =
183186match hierwith