未验证 提交 83bebeb6 编写于 作者: F Fred Silberberg 提交者: GitHub

Revert "Extract the RDT implementation for Misc files and VS open file tracker"

上级 3ac1a7dc
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.Text;
namespace Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem
{
/// <summary>
/// Defines the methods that get called by the <see cref="RunningDocumentTableEventTracker"/>
/// for getting notified about running document table events.
/// </summary>
internal interface IRunningDocumentTableEventListener
{
/// <summary>
/// Triggered when a document is opened.
/// </summary>
/// <param name="moniker">the non-null moniker of the opened document.</param>
/// <param name="textBuffer">the non-null text buffer of the opened document)</param>
/// <param name="hierarchy">the hierarchy of the text buffer if available.</param>
void OnOpenDocument(string moniker, ITextBuffer textBuffer, IVsHierarchy hierarchy);
/// <summary>
/// Triggered when a document is closed.
/// </summary>
/// <param name="moniker">the non-null moniker of the closed document.</param>
void OnCloseDocument(string moniker);
/// <summary>
/// Triggered when a document context is refreshed with a new hierarchy.
/// </summary>
/// <param name="moniker">the non-null moniker of the document that changed.</param>
/// <param name="hierarchy">the hierarchy of the text buffer if available.</param>
void OnRefreshDocumentContext(string moniker, IVsHierarchy hierarchy);
/// <summary>
/// Triggered when a document moniker changes.
/// </summary>
/// <param name="newMoniker">the document's new moniker.</param>
/// <param name="oldMoniker">the document's old moniker.</param>
/// <param name="textBuffer">the document's buffer.</param>
void OnRenameDocument(string newMoniker, string oldMoniker, ITextBuffer textBuffer);
}
}
......@@ -25,28 +25,29 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem
using Workspace = Microsoft.CodeAnalysis.Workspace;
[Export(typeof(MiscellaneousFilesWorkspace))]
internal sealed partial class MiscellaneousFilesWorkspace : Workspace, IRunningDocumentTableEventListener
internal sealed partial class MiscellaneousFilesWorkspace : Workspace, IVsRunningDocTableEvents2
{
private readonly IVsEditorAdaptersFactoryService _editorAdaptersFactoryService;
private readonly IMetadataAsSourceFileService _fileTrackingMetadataAsSourceService;
private readonly IVsRunningDocumentTable4 _runningDocumentTable;
private readonly IVsTextManager _textManager;
private readonly RunningDocumentTableEventTracker _runningDocumentTableEventTracker;
private readonly Dictionary<Guid, LanguageInformation> _languageInformationByLanguageGuid = new Dictionary<Guid, LanguageInformation>();
/// <summary>
/// <see cref="WorkspaceRegistration"/> instances for all open buffers being tracked by by this object
/// for possible inclusion into this workspace.
/// </summary>
private IBidirectionalMap<string, WorkspaceRegistration> _monikerToWorkspaceRegistration = BidirectionalMap<string, WorkspaceRegistration>.Empty;
private IBidirectionalMap<uint, WorkspaceRegistration> _docCookieToWorkspaceRegistration = BidirectionalMap<uint, WorkspaceRegistration>.Empty;
/// <summary>
/// The mapping of all monikers in the RDT and the <see cref="ProjectId"/> of the project and <see cref="SourceTextContainer"/> of the open
/// file we have created for that open buffer. An entry should only be in here if it's also already in <see cref="_monikerToWorkspaceRegistration"/>.
/// The mapping of all doc cookies in the RDT and the <see cref="ProjectId"/> of the project and <see cref="SourceTextContainer"/> of the open
/// file we have created for that open buffer. An entry should only be in here if it's also already in <see cref="_docCookieToWorkspaceRegistration"/>.
/// </summary>
private readonly Dictionary<string, (ProjectId projectId, SourceTextContainer textContainer)> _monikersToProjectIdAndContainer = new Dictionary<string, (ProjectId, SourceTextContainer)>();
private readonly Dictionary<uint, (ProjectId projectId, SourceTextContainer textContainer)> _docCookiesToProjectIdAndContainer = new Dictionary<uint, (ProjectId, SourceTextContainer)>();
private readonly ImmutableArray<MetadataReference> _metadataReferences;
private uint _runningDocumentTableEventsCookie;
private readonly ForegroundThreadAffinitizedObject _foregroundThreadAffinitization;
......@@ -63,41 +64,17 @@ internal sealed partial class MiscellaneousFilesWorkspace : Workspace, IRunningD
{
_foregroundThreadAffinitization = new ForegroundThreadAffinitizedObject(threadingContext, assertIsForeground: true);
_editorAdaptersFactoryService = editorAdaptersFactoryService;
_fileTrackingMetadataAsSourceService = fileTrackingMetadataAsSourceService;
_runningDocumentTable = (IVsRunningDocumentTable4)serviceProvider.GetService(typeof(SVsRunningDocumentTable));
_textManager = (IVsTextManager)serviceProvider.GetService(typeof(SVsTextManager));
var runningDocumentTable = (IVsRunningDocumentTable)serviceProvider.GetService(typeof(SVsRunningDocumentTable));
_runningDocumentTableEventTracker = new RunningDocumentTableEventTracker(threadingContext, editorAdaptersFactoryService, runningDocumentTable, this);
((IVsRunningDocumentTable)_runningDocumentTable).AdviseRunningDocTableEvents(this, out _runningDocumentTableEventsCookie);
_metadataReferences = ImmutableArray.CreateRange(CreateMetadataReferences());
saveEventsService.StartSendingSaveEvents();
}
void IRunningDocumentTableEventListener.OnOpenDocument(string moniker, ITextBuffer textBuffer, IVsHierarchy _) => TrackOpenedDocument(moniker, textBuffer);
void IRunningDocumentTableEventListener.OnCloseDocument(string moniker) => TryUntrackClosingDocument(moniker);
/// <summary>
/// File hierarchy events are not relevant to the misc workspace.
/// </summary>
void IRunningDocumentTableEventListener.OnRefreshDocumentContext(string moniker, IVsHierarchy hierarchy)
{
}
void IRunningDocumentTableEventListener.OnRenameDocument(string newMoniker, string oldMoniker, ITextBuffer buffer)
{
// We want to consider this file to be added in one of two situations:
//
// 1) the old file already was a misc file, at which point we might just be doing a rename from
// one name to another with the same extension
// 2) the old file was a different extension that we weren't tracking, which may have now changed
if (TryUntrackClosingDocument(oldMoniker) || TryGetLanguageInformation(oldMoniker) == null)
{
// Add the new one, if appropriate.
TrackOpenedDocument(newMoniker, buffer);
}
}
public void RegisterLanguage(Guid languageGuid, string languageName, string scriptExtension)
{
_languageInformationByLanguageGuid.Add(languageGuid, new LanguageInformation(languageName, scriptExtension));
......@@ -138,11 +115,88 @@ private IEnumerable<MetadataReference> CreateMetadataReferences()
select manager.CreateMetadataReferenceSnapshot(fullPath, MetadataReferenceProperties.Assembly);
}
private void TrackOpenedDocument(string moniker, ITextBuffer textBuffer)
public int OnAfterAttributeChange(uint docCookie, uint grfAttribs)
{
return VSConstants.S_OK;
}
public int OnAfterAttributeChangeEx(uint docCookie, uint grfAttribs, IVsHierarchy pHierOld, uint itemidOld, string pszMkDocumentOld, IVsHierarchy pHierNew, uint itemidNew, string pszMkDocumentNew)
{
// Did we rename?
if ((grfAttribs & (uint)__VSRDTATTRIB.RDTA_MkDocument) != 0)
{
// We want to consider this file to be added in one of two situations:
//
// 1) the old file already was a misc file, at which point we might just be doing a rename from
// one name to another with the same extension
// 2) the old file was a different extension that we weren't tracking, which may have now changed
if (TryUntrackClosingDocument(docCookie, pszMkDocumentOld) || TryGetLanguageInformation(pszMkDocumentOld) == null)
{
// Add the new one, if appropriate.
TrackOpenedDocument(docCookie, pszMkDocumentNew);
}
}
// When starting a diff, the RDT doesn't call OnBeforeDocumentWindowShow, but it does call
// OnAfterAttributeChangeEx for the temporary buffer. The native IDE used this even to
// add misc files, so we'll do the same.
if ((grfAttribs & (uint)__VSRDTATTRIB.RDTA_DocDataReloaded) != 0)
{
var moniker = _runningDocumentTable.GetDocumentMoniker(docCookie);
if (moniker != null && TryGetLanguageInformation(moniker) != null && !_docCookiesToProjectIdAndContainer.ContainsKey(docCookie))
{
TrackOpenedDocument(docCookie, moniker);
}
}
if ((grfAttribs & (uint)__VSRDTATTRIB3.RDTA_DocumentInitialized) != 0)
{
// The document is now initialized, we should try tracking it
TrackOpenedDocument(docCookie, _runningDocumentTable.GetDocumentMoniker(docCookie));
}
return VSConstants.S_OK;
}
public int OnAfterDocumentWindowHide(uint docCookie, IVsWindowFrame pFrame)
{
return VSConstants.E_NOTIMPL;
}
public int OnAfterFirstDocumentLock(uint docCookie, uint dwRDTLockType, uint dwReadLocksRemaining, uint dwEditLocksRemaining)
{
return VSConstants.E_NOTIMPL;
}
public int OnAfterSave(uint docCookie)
{
return VSConstants.E_NOTIMPL;
}
public int OnBeforeDocumentWindowShow(uint docCookie, int fFirstShow, IVsWindowFrame pFrame)
{
return VSConstants.E_NOTIMPL;
}
public int OnBeforeLastDocumentUnlock(uint docCookie, uint dwRDTLockType, uint dwReadLocksRemaining, uint dwEditLocksRemaining)
{
_foregroundThreadAffinitization.AssertIsForeground();
if (dwReadLocksRemaining + dwEditLocksRemaining == 0)
{
TryUntrackClosingDocument(docCookie, _runningDocumentTable.GetDocumentMoniker(docCookie));
}
return VSConstants.S_OK;
}
private void TrackOpenedDocument(uint docCookie, string moniker)
{
_foregroundThreadAffinitization.AssertIsForeground();
var languageInformation = TryGetLanguageInformation(moniker);
if (languageInformation == null)
{
// We can never put this document in a workspace, so just bail
......@@ -151,16 +205,25 @@ private void TrackOpenedDocument(string moniker, ITextBuffer textBuffer)
// We don't want to realize the document here unless it's already initialized. Document initialization is watched in
// OnAfterAttributeChangeEx and will retrigger this if it wasn't already done.
if (!_monikerToWorkspaceRegistration.ContainsKey(moniker))
if (_runningDocumentTable.IsDocumentInitialized(docCookie) && !_docCookieToWorkspaceRegistration.ContainsKey(docCookie))
{
var registration = Workspace.GetWorkspaceRegistration(textBuffer.AsTextContainer());
// GetDocumentData returns dynamic, and casting directly to IVsTextBuffer means we trigger a cast through the dyanmic
// binder. Since it's already a managed object, we can double cast to avoid loading the dynamic binder.
var vsTextBuffer = (IVsTextBuffer)(object)_runningDocumentTable.GetDocumentData(docCookie);
var textBuffer = _editorAdaptersFactoryService.GetDocumentBuffer(vsTextBuffer);
registration.WorkspaceChanged += Registration_WorkspaceChanged;
_monikerToWorkspaceRegistration = _monikerToWorkspaceRegistration.Add(moniker, registration);
if (!IsClaimedByAnotherWorkspace(registration))
// As long as the buffer is initialized, then we should see if we should attach
if (textBuffer != null)
{
AttachToDocument(moniker, textBuffer);
var registration = Workspace.GetWorkspaceRegistration(textBuffer.AsTextContainer());
registration.WorkspaceChanged += Registration_WorkspaceChanged;
_docCookieToWorkspaceRegistration = _docCookieToWorkspaceRegistration.Add(docCookie, registration);
if (!IsClaimedByAnotherWorkspace(registration))
{
AttachToDocument(docCookie, moniker);
}
}
}
}
......@@ -183,21 +246,23 @@ private void Registration_WorkspaceChanged(object sender, EventArgs e)
// Since WorkspaceChanged notifications may be asynchronous and happened on a different thread,
// we might have already unsubscribed for this synchronously from the RDT while we were in the process of sending this
// request back to the UI thread.
if (!_monikerToWorkspaceRegistration.TryGetKey(workspaceRegistration, out var moniker))
if (!_docCookieToWorkspaceRegistration.TryGetKey(workspaceRegistration, out var docCookie))
{
return;
}
// It's also theoretically possible that we are getting notified about a workspace change to a document that has
// been simultaneously removed from the RDT but we haven't gotten the notification. In that case, also bail.
if (!_runningDocumentTableEventTracker.IsFileOpen(moniker))
if (!_runningDocumentTable.IsCookieValid(docCookie))
{
return;
}
var moniker = _runningDocumentTable.GetDocumentMoniker(docCookie);
if (workspaceRegistration.Workspace == null)
{
if (_monikersToProjectIdAndContainer.TryGetValue(moniker, out var projectIdAndSourceTextContainer))
if (_docCookiesToProjectIdAndContainer.TryGetValue(docCookie, out var projectIdAndSourceTextContainer))
{
// The workspace was taken from us and released and we have only asynchronously found out now.
// We already have the file open in our workspace, but the global mapping of source text container
......@@ -212,19 +277,16 @@ private void Registration_WorkspaceChanged(object sender, EventArgs e)
// the moniker. Once we observe the rename later in OnAfterAttributeChangeEx we'll completely disconnect.
if (TryGetLanguageInformation(moniker) != null)
{
if (_runningDocumentTableEventTracker.TryGetBufferFromMoniker(moniker, out var buffer))
{
AttachToDocument(moniker, buffer);
}
AttachToDocument(docCookie, moniker);
}
}
}
else if (IsClaimedByAnotherWorkspace(workspaceRegistration))
{
// It's now claimed by another workspace, so we should unclaim it
if (_monikersToProjectIdAndContainer.ContainsKey(moniker))
if (_docCookiesToProjectIdAndContainer.ContainsKey(docCookie))
{
DetachFromDocument(moniker);
DetachFromDocument(docCookie, moniker);
}
}
}
......@@ -233,22 +295,20 @@ private void Registration_WorkspaceChanged(object sender, EventArgs e)
/// Stops tracking a document in the RDT for whether we should attach to it.
/// </summary>
/// <returns>true if we were previously tracking it.</returns>
private bool TryUntrackClosingDocument(string moniker)
private bool TryUntrackClosingDocument(uint docCookie, string moniker)
{
_foregroundThreadAffinitization.AssertIsForeground();
bool unregisteredRegistration = false;
// Remove our registration changing handler before we call DetachFromDocument. Otherwise, calling DetachFromDocument
// causes us to set the workspace to null, which we then respond to as an indication that we should
// attach again.
if (_monikerToWorkspaceRegistration.TryGetValue(moniker, out var registration))
if (_docCookieToWorkspaceRegistration.TryGetValue(docCookie, out var registration))
{
registration.WorkspaceChanged -= Registration_WorkspaceChanged;
_monikerToWorkspaceRegistration = _monikerToWorkspaceRegistration.RemoveKey(moniker);
_docCookieToWorkspaceRegistration = _docCookieToWorkspaceRegistration.RemoveKey(docCookie);
unregisteredRegistration = true;
}
DetachFromDocument(moniker);
DetachFromDocument(docCookie, moniker);
return unregisteredRegistration;
}
......@@ -260,10 +320,15 @@ private bool IsClaimedByAnotherWorkspace(WorkspaceRegistration registration)
return registration.Workspace != null && registration.Workspace.Kind != WorkspaceKind.MetadataAsSource && registration.Workspace.Kind != WorkspaceKind.MiscellaneousFiles;
}
private void AttachToDocument(string moniker, ITextBuffer textBuffer)
private void AttachToDocument(uint docCookie, string moniker)
{
_foregroundThreadAffinitization.AssertIsForeground();
// The cast from dynamic to object doesn't change semantics, but avoids loading the dynamic binder
// which saves us JIT time in this method.
var vsTextBuffer = (IVsTextBuffer)(object)_runningDocumentTable.GetDocumentData(docCookie);
var textBuffer = _editorAdaptersFactoryService.GetDocumentBuffer(vsTextBuffer);
if (_fileTrackingMetadataAsSourceService.TryAddDocumentToWorkspace(moniker, textBuffer))
{
// We already added it, so we will keep it excluded from the misc files workspace
......@@ -277,7 +342,7 @@ private void AttachToDocument(string moniker, ITextBuffer textBuffer)
var sourceTextContainer = textBuffer.AsTextContainer();
OnDocumentOpened(projectInfo.Documents.Single().Id, sourceTextContainer);
_monikersToProjectIdAndContainer.Add(moniker, (projectInfo.Id, sourceTextContainer));
_docCookiesToProjectIdAndContainer.Add(docCookie, (projectInfo.Id, sourceTextContainer));
}
/// <summary>
......@@ -372,7 +437,7 @@ private ProjectInfo CreateProjectInfoForDocument(string filePath)
SourceCodeKind.Script : SourceCodeKind.Regular;
}
private void DetachFromDocument(string moniker)
private void DetachFromDocument(uint docCookie, string moniker)
{
_foregroundThreadAffinitization.AssertIsForeground();
if (_fileTrackingMetadataAsSourceService.TryRemoveDocumentFromWorkspace(moniker))
......@@ -380,7 +445,7 @@ private void DetachFromDocument(string moniker)
return;
}
if (_monikersToProjectIdAndContainer.TryGetValue(moniker, out var projectIdAndContainer))
if (_docCookiesToProjectIdAndContainer.TryGetValue(docCookie, out var projectIdAndContainer))
{
var document = this.CurrentSolution.GetProject(projectIdAndContainer.projectId).Documents.Single();
......@@ -388,7 +453,7 @@ private void DetachFromDocument(string moniker)
OnDocumentClosed(document.Id, new FileTextLoader(document.FilePath, defaultEncoding: null));
OnProjectRemoved(document.Project.Id);
_monikersToProjectIdAndContainer.Remove(moniker);
_docCookiesToProjectIdAndContainer.Remove(docCookie);
return;
}
......@@ -397,6 +462,10 @@ private void DetachFromDocument(string moniker)
protected override void Dispose(bool finalize)
{
StopSolutionCrawler();
var runningDocumentTableForEvents = (IVsRunningDocumentTable)_runningDocumentTable;
runningDocumentTableForEvents.UnadviseRunningDocTableEvents(_runningDocumentTableEventsCookie);
_runningDocumentTableEventsCookie = 0;
base.Dispose(finalize);
}
......@@ -414,7 +483,7 @@ public override bool CanApplyChange(ApplyChangesKind feature)
protected override void ApplyDocumentTextChanged(DocumentId documentId, SourceText newText)
{
foreach (var projectIdAndSourceTextContainer in _monikersToProjectIdAndContainer.Values)
foreach (var projectIdAndSourceTextContainer in _docCookiesToProjectIdAndContainer.Values)
{
if (projectIdAndSourceTextContainer.projectId == documentId.ProjectId)
{
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using Analyzer.Utilities.PooledObjects;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
using Microsoft.VisualStudio.Editor;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.TextManager.Interop;
using Roslyn.Utilities;
namespace Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem
{
/// <summary>
/// Class to register with the RDT and forward RDT events.
/// Handles common conditions and delegates implementation to the <see cref="IRunningDocumentTableEventListener"/>
/// </summary>
internal sealed class RunningDocumentTableEventTracker : IVsRunningDocTableEvents3, IDisposable
{
private bool _isDisposed = false; // To detect redundant calls
private readonly ForegroundThreadAffinitizedObject _foregroundAffinitization;
private readonly IVsEditorAdaptersFactoryService _editorAdaptersFactoryService;
private readonly IVsRunningDocumentTable4 _runningDocumentTable;
private readonly IRunningDocumentTableEventListener _listener;
private uint _runningDocumentTableEventsCookie;
public RunningDocumentTableEventTracker(IThreadingContext threadingContext, IVsEditorAdaptersFactoryService editorAdaptersFactoryService, IVsRunningDocumentTable runningDocumentTable,
IRunningDocumentTableEventListener listener)
{
Contract.ThrowIfNull(threadingContext);
Contract.ThrowIfNull(editorAdaptersFactoryService);
Contract.ThrowIfNull(runningDocumentTable);
Contract.ThrowIfNull(listener);
// Advise / Unadvise for the RDT is free threaded past 16.0
_foregroundAffinitization = new ForegroundThreadAffinitizedObject(threadingContext, assertIsForeground: false);
_runningDocumentTable = (IVsRunningDocumentTable4)runningDocumentTable;
_editorAdaptersFactoryService = editorAdaptersFactoryService;
_listener = listener;
((IVsRunningDocumentTable)_runningDocumentTable).AdviseRunningDocTableEvents(this, out _runningDocumentTableEventsCookie);
}
public int OnAfterFirstDocumentLock(uint docCookie, uint dwRDTLockType, uint dwReadLocksRemaining, uint dwEditLocksRemaining)
{
return VSConstants.E_NOTIMPL;
}
public int OnBeforeLastDocumentUnlock(uint docCookie, uint dwRDTLockType, uint dwReadLocksRemaining, uint dwEditLocksRemaining)
{
if (dwReadLocksRemaining + dwEditLocksRemaining == 0)
{
_foregroundAffinitization.AssertIsForeground();
if (_runningDocumentTable.IsDocumentInitialized(docCookie))
{
_listener.OnCloseDocument(_runningDocumentTable.GetDocumentMoniker(docCookie));
}
}
return VSConstants.S_OK;
}
public int OnAfterSave(uint docCookie)
{
return VSConstants.E_NOTIMPL;
}
public int OnAfterAttributeChange(uint docCookie, uint grfAttribs)
{
return VSConstants.E_NOTIMPL;
}
public int OnAfterAttributeChangeEx(uint docCookie, uint grfAttribs, IVsHierarchy pHierOld, uint itemidOld, string pszMkDocumentOld, IVsHierarchy pHierNew, uint itemidNew, string pszMkDocumentNew)
{
// Did we rename?
if ((grfAttribs & (uint)__VSRDTATTRIB.RDTA_MkDocument) != 0)
{
_foregroundAffinitization.AssertIsForeground();
if (_runningDocumentTable.IsDocumentInitialized(docCookie) && TryGetBuffer(docCookie, out var buffer))
{
_listener.OnRenameDocument(newMoniker: pszMkDocumentNew, oldMoniker: pszMkDocumentOld, textBuffer: buffer);
}
}
// Doc data reloaded is the most reliable way to know when a document has been loaded and may have a text buffer we can get.
if ((grfAttribs & (uint)__VSRDTATTRIB.RDTA_DocDataReloaded) != 0)
{
_foregroundAffinitization.AssertIsForeground();
if (_runningDocumentTable.IsDocumentInitialized(docCookie) && TryGetMoniker(docCookie, out var moniker) && TryGetBuffer(docCookie, out var buffer))
{
_runningDocumentTable.GetDocumentHierarchyItem(docCookie, out var hierarchy, out _);
_listener.OnOpenDocument(moniker, buffer, hierarchy);
}
}
if ((grfAttribs & (uint)__VSRDTATTRIB.RDTA_Hierarchy) != 0)
{
_foregroundAffinitization.AssertIsForeground();
if (_runningDocumentTable.IsDocumentInitialized(docCookie) && TryGetMoniker(docCookie, out var moniker))
{
_runningDocumentTable.GetDocumentHierarchyItem(docCookie, out var hierarchy, out _);
_listener.OnRefreshDocumentContext(moniker, hierarchy);
}
}
return VSConstants.S_OK;
}
public int OnBeforeDocumentWindowShow(uint docCookie, int fFirstShow, IVsWindowFrame pFrame)
{
return VSConstants.E_NOTIMPL;
}
public int OnAfterDocumentWindowHide(uint docCookie, IVsWindowFrame pFrame)
{
return VSConstants.E_NOTIMPL;
}
public int OnBeforeSave(uint docCookie)
{
return VSConstants.E_NOTIMPL;
}
public bool IsFileOpen(string fileName) => _runningDocumentTable.IsMonikerValid(fileName);
/// <summary>
/// Attempts to get a text buffer from the specified moniker.
/// </summary>
/// <param name="moniker">the moniker to retrieve the text buffer for.</param>
/// <param name="textBuffer">the output text buffer or null if the moniker is invalid / document is not initialized.</param>
/// <returns>true if the buffer was found with a non null value.</returns>
public bool TryGetBufferFromMoniker(string moniker, out ITextBuffer textBuffer)
{
_foregroundAffinitization.AssertIsForeground();
textBuffer = null;
if (!IsFileOpen(moniker))
{
return false;
}
var cookie = _runningDocumentTable.GetDocumentCookie(moniker);
if (!_runningDocumentTable.IsDocumentInitialized(cookie))
{
return false;
}
return TryGetBuffer(cookie, out textBuffer);
}
public IVsHierarchy GetDocumentHierarchy(string moniker)
{
if (!IsFileOpen(moniker))
{
return null;
}
var cookie = _runningDocumentTable.GetDocumentCookie(moniker);
_runningDocumentTable.GetDocumentHierarchyItem(cookie, out var hierarchy, out _);
return hierarchy;
}
/// <summary>
/// Enumerates the running document table to retrieve all initialized files.
/// </summary>
public IEnumerable<(string moniker, ITextBuffer textBuffer, IVsHierarchy hierarchy)> EnumerateDocumentSet()
{
_foregroundAffinitization.AssertIsForeground();
var documents = ArrayBuilder<(string, ITextBuffer, IVsHierarchy)>.GetInstance();
foreach (var cookie in GetInitializedRunningDocumentTableCookies())
{
if (TryGetMoniker(cookie, out var moniker) && TryGetBuffer(cookie, out var buffer))
{
_runningDocumentTable.GetDocumentHierarchyItem(cookie, out var hierarchy, out _);
documents.Add((moniker, buffer, hierarchy));
}
}
return documents.ToArray();
}
private IEnumerable<uint> GetInitializedRunningDocumentTableCookies()
{
// Some methods we need here only exist in IVsRunningDocumentTable and not the IVsRunningDocumentTable4 that we
// hold onto as a field
var runningDocumentTable = (IVsRunningDocumentTable)_runningDocumentTable;
ErrorHandler.ThrowOnFailure(runningDocumentTable.GetRunningDocumentsEnum(out var enumRunningDocuments));
uint[] cookies = new uint[16];
while (ErrorHandler.Succeeded(enumRunningDocuments.Next((uint)cookies.Length, cookies, out var cookiesFetched))
&& cookiesFetched > 0)
{
for (int cookieIndex = 0; cookieIndex < cookiesFetched; cookieIndex++)
{
var cookie = cookies[cookieIndex];
if (_runningDocumentTable.IsDocumentInitialized(cookie))
{
yield return cookie;
}
}
}
}
private bool TryGetMoniker(uint docCookie, out string moniker)
{
moniker = _runningDocumentTable.GetDocumentMoniker(docCookie);
return !string.IsNullOrEmpty(moniker);
}
private bool TryGetBuffer(uint docCookie, out ITextBuffer textBuffer)
{
textBuffer = null;
// The cast from dynamic to object doesn't change semantics, but avoids loading the dynamic binder
// which saves us JIT time in this method and an assembly load.
if ((object)_runningDocumentTable.GetDocumentData(docCookie) is IVsTextBuffer bufferAdapter)
{
textBuffer = _editorAdaptersFactoryService.GetDocumentBuffer(bufferAdapter);
return textBuffer != null;
}
return false;
}
#region IDisposable Support
private void Dispose(bool disposing)
{
if (!_isDisposed)
{
if (disposing)
{
var runningDocumentTableForEvents = (IVsRunningDocumentTable)_runningDocumentTable;
runningDocumentTableForEvents.UnadviseRunningDocTableEvents(_runningDocumentTableEventsCookie);
_runningDocumentTableEventsCookie = 0;
}
_isDisposed = true;
}
}
public void Dispose()
{
Dispose(true);
}
#endregion
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册