private bool IsCurrentContext(DocumentKey documentKey) { AssertIsForeground(); var document = documentKey.HostProject.GetCurrentDocumentFromPath(documentKey.Moniker); return(document != null && LinkedFileUtilities.IsCurrentContextHierarchy(document, _runningDocumentTable)); }
public void AddAdditionalFile(string additionalFilePath) { var document = this.DocumentProvider.TryGetDocumentForFile( this, (uint)VSConstants.VSITEMID.Nil, filePath: additionalFilePath, sourceCodeKind: SourceCodeKind.Regular, canUseTextBuffer: _ => true); if (document == null) { return; } AddAdditionalDocument(document, isCurrentContext: LinkedFileUtilities.IsCurrentContextHierarchy(document, RunningDocumentTable)); }
/// <summary> /// Starts pushing events from the given projects to the workspace hosts and notifies about open documents. /// </summary> /// <remarks>This method must be called on the foreground thread.</remarks> internal void StartPushingToWorkspaceAndNotifyOfOpenDocuments(IEnumerable <AbstractProject> projects) { AssertIsForeground(); // If the solution is closing we shouldn't do anything, because all of our state is // in the process of going away. This can happen if we receive notification that a document has // opened in the middle of the solution close operation. if (_solutionIsClosing) { return; } // We need to push these projects and any project dependencies we already know about. Therefore, compute the // transitive closure of the projects that haven't already been pushed, keeping them in appropriate order. var visited = new HashSet <AbstractProject>(); var inOrderToPush = new List <AbstractProject>(); void addToInOrderToPush(AbstractProject project) { Contract.ThrowIfFalse(ContainsProject(project)); // Bail out if any of the following is true: // 1. We have already started pushing changes for this project OR // 2. We have already visited this project in a prior recursive call if (_pushedProjects.Contains(project) || !visited.Add(project)) { return; } foreach (var projectReference in project.GetCurrentProjectReferences()) { addToInOrderToPush(GetProject(projectReference.ProjectId)); } inOrderToPush.Add(project); } foreach (var project in projects) { addToInOrderToPush(project); } var projectInfos = inOrderToPush.Select(p => p.CreateProjectInfoForCurrentState()).ToImmutableArray(); // We need to enable projects to start pushing changes to the workspace even before we add the solution/project to the host. // This is required because between the point we capture the project info for current state and the point where we start pushing to the workspace, // project system may send new events on the AbstractProject on a background thread, and these won't get pushed over to the workspace hosts as we didn't set the _pushingChangesToWorkspaceHost flag on the AbstractProject. // By invoking StartPushingToWorkspaceHosts upfront, any project state changes on the background thread will enqueue notifications to workspace hosts on foreground scheduled tasks. foreach (var project in inOrderToPush) { project.PushingChangesToWorkspace = true; Logger.Log(FunctionId.AbstractProject_PushedToWorkspace, KeyValueLogMessage.Create(LogType.Trace, m => { m[AbstractProject.ProjectGuidPropertyName] = project.Guid; })); } using (WorkspaceServices.GetService <IGlobalOperationNotificationService>()?.Start("Add Project to Workspace")) { if (!_solutionAdded) { string solutionFilePath = null; VersionStamp?version = default; // Figure out the solution version if (ErrorHandler.Succeeded(_vsSolution.GetSolutionInfo(out var solutionDirectory, out var solutionFileName, out var userOptsFile)) && solutionFileName != null) { solutionFilePath = Path.Combine(solutionDirectory, solutionFileName); if (File.Exists(solutionFilePath)) { version = VersionStamp.Create(File.GetLastWriteTimeUtc(solutionFilePath)); } } if (version == null) { version = VersionStamp.Create(); } var id = SolutionId.CreateNewId(string.IsNullOrWhiteSpace(solutionFileName) ? null : solutionFileName); var solutionInfo = SolutionInfo.Create(id, version.Value, solutionFilePath, projects: projectInfos); NotifyWorkspace(workspace => workspace.OnSolutionAdded(solutionInfo)); _solutionAdded = true; var persistenceService = WorkspaceServices.GetRequiredService <IPersistentStorageLocationService>() as VisualStudioPersistentStorageLocationService; persistenceService?.UpdateForVisualStudioWorkspace(_workspace); } else { // The solution is already added, so we'll just do project added notifications from here foreach (var projectInfo in projectInfos) { NotifyWorkspace(workspace => workspace.OnProjectAdded(projectInfo)); } } foreach (var project in inOrderToPush) { _pushedProjects.Add(project); foreach (var document in project.GetCurrentDocuments()) { if (document.IsOpen) { NotifyWorkspace(workspace => { workspace.OnDocumentOpened( document.Id, document.GetOpenTextBuffer().AsTextContainer(), isCurrentContext: LinkedFileUtilities.IsCurrentContextHierarchy(document, _runningDocumentTable)); (workspace as VisualStudioWorkspaceImpl)?.ConnectToSharedHierarchyEvents(document); }); } } } } }
internal void StartPushingToWorkspaceAndNotifyOfOpenDocuments( IEnumerable <AbstractProject> projects) { AssertIsForeground(); // If the workspace host isn't actually ready yet, we shouldn't do anything. // Also, if the solution is closing we shouldn't do anything either, because all of our state is // in the process of going away. This can happen if we receive notification that a document has // opened in the middle of the solution close operation. if (!this.HostReadyForEvents || _tracker._solutionIsClosing) { return; } // We need to push these projects and any project dependencies we already know about. Therefore, compute the // transitive closure of the projects that haven't already been pushed, keeping them in appropriate order. var visited = new HashSet <AbstractProject>(); var inOrderToPush = new List <AbstractProject>(); foreach (var project in projects) { AddToPushListIfNeeded(project, inOrderToPush, visited); } var projectInfos = inOrderToPush.Select(p => p.CreateProjectInfoForCurrentState()).ToImmutableArray(); // We need to enable projects to start pushing changes to workspace hosts even before we add the solution/project to the host. // This is required because between the point we capture the project info for current state and the point where we start pushing to workspace hosts, // project system may send new events on the AbstractProject on a background thread, and these won't get pushed over to the workspace hosts as we didn't set the _pushingChangesToWorkspaceHost flag on the AbstractProject. // By invoking StartPushingToWorkspaceHosts upfront, any project state changes on the background thread will enqueue notifications to workspace hosts on foreground scheduled tasks. foreach (var project in inOrderToPush) { project.StartPushingToWorkspaceHosts(); } if (!_solutionAdded) { string solutionFilePath = null; VersionStamp?version = default(VersionStamp?); // Figure out the solution version if (ErrorHandler.Succeeded(_tracker._vsSolution.GetSolutionInfo(out var solutionDirectory, out var solutionFileName, out var userOptsFile)) && solutionFileName != null) { solutionFilePath = Path.Combine(solutionDirectory, solutionFileName); if (File.Exists(solutionFilePath)) { version = VersionStamp.Create(File.GetLastWriteTimeUtc(solutionFilePath)); } } if (version == null) { version = VersionStamp.Create(); } var id = SolutionId.CreateNewId(string.IsNullOrWhiteSpace(solutionFileName) ? null : solutionFileName); _tracker.RegisterSolutionProperties(id); var solutionInfo = SolutionInfo.Create(id, version.Value, solutionFilePath, projects: projectInfos); this.Host.OnSolutionAdded(solutionInfo); _solutionAdded = true; } else { // The solution is already added, so we'll just do project added notifications from here foreach (var projectInfo in projectInfos) { this.Host.OnProjectAdded(projectInfo); } } foreach (var project in inOrderToPush) { _pushedProjects.Add(project); foreach (var document in project.GetCurrentDocuments()) { if (document.IsOpen) { this.Host.OnDocumentOpened( document.Id, document.GetOpenTextBuffer(), isCurrentContext: LinkedFileUtilities.IsCurrentContextHierarchy(document, _tracker._runningDocumentTable)); } } } }