Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

#nullable enable

using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.VisualStudio.Editor;
Expand Down Expand Up @@ -125,34 +128,26 @@ public int OnAfterDocumentWindowHide(uint docCookie, IVsWindowFrame pFrame)
public int OnBeforeSave(uint docCookie)
=> VSConstants.E_NOTIMPL;

public bool IsFileOpen(string fileName) => _runningDocumentTable.IsMonikerValid(fileName);
public bool IsFileOpen(string fileName)
{
_foregroundAffinitization.AssertIsForeground();
return _runningDocumentTable.IsFileOpen(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)
public bool TryGetBufferFromMoniker(string moniker, [NotNullWhen(true)] 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);
return _runningDocumentTable.TryGetBufferFromMoniker(_editorAdaptersFactoryService, moniker, out textBuffer);
}

public IVsHierarchy GetDocumentHierarchy(string moniker)
public IVsHierarchy? GetDocumentHierarchy(string moniker)
{
if (!IsFileOpen(moniker))
{
Expand Down Expand Up @@ -213,20 +208,8 @@ private bool TryGetMoniker(uint docCookie, out string moniker)
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;
}
private bool TryGetBuffer(uint docCookie, [NotNullWhen(true)] out ITextBuffer? textBuffer)
=> _runningDocumentTable.TryGetBuffer(_editorAdaptersFactoryService, docCookie, out textBuffer);

public void Dispose()
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

#nullable enable

using System.Diagnostics.CodeAnalysis;
using Microsoft.VisualStudio.Editor;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.TextManager.Interop;

namespace Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem
{
/// <summary>
/// Helper extensions for calling into the RDT.
/// These must all be called from the UI thread.
/// </summary>
internal static class RunningDocumentTableExtensions
{
public static bool TryGetBufferFromMoniker(this IVsRunningDocumentTable4 runningDocumentTable,
IVsEditorAdaptersFactoryService editorAdaptersFactoryService,
string moniker, [NotNullWhen(true)] out ITextBuffer? textBuffer)
{
textBuffer = null;
if (!runningDocumentTable.IsFileOpen(moniker))
{
return false;
}

var cookie = runningDocumentTable.GetDocumentCookie(moniker);
if (!runningDocumentTable.IsDocumentInitialized(cookie))
{
return false;
}

return TryGetBuffer(runningDocumentTable, editorAdaptersFactoryService, cookie, out textBuffer);
}

public static bool IsFileOpen(this IVsRunningDocumentTable4 runningDocumentTable, string fileName)
=> runningDocumentTable.IsMonikerValid(fileName);

public static bool TryGetBuffer(this IVsRunningDocumentTable4 runningDocumentTable, IVsEditorAdaptersFactoryService editorAdaptersFactoryService,
uint docCookie, [NotNullWhen(true)] 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;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ public async static Task<OpenFileTracker> CreateAsync(VisualStudioWorkspaceImpl
return new OpenFileTracker(workspace, runningDocumentTable, componentModel);
}

private void TryOpeningDocumentsForMoniker(string moniker, ITextBuffer textBuffer, IVsHierarchy hierarchy)
private void TryOpeningDocumentsForMoniker(string moniker, ITextBuffer textBuffer, IVsHierarchy? hierarchy)
{
_foregroundAffinitization.AssertIsForeground();

Expand Down Expand Up @@ -171,7 +171,7 @@ private void TryOpeningDocumentsForMoniker(string moniker, ITextBuffer textBuffe
});
}

private ProjectId GetActiveContextProjectIdAndWatchHierarchies(string moniker, IEnumerable<ProjectId> projectIds, IVsHierarchy hierarchy)
private ProjectId GetActiveContextProjectIdAndWatchHierarchies(string moniker, IEnumerable<ProjectId> projectIds, IVsHierarchy? hierarchy)
{
_foregroundAffinitization.AssertIsForeground();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1002,31 +1002,43 @@ public void OpenDocumentCore(DocumentId documentId, bool activate = true)
var document = this.CurrentSolution.GetTextDocument(documentId);
if (document != null)
{
if (TryGetFrame(document, out var frame))
OpenDocumentFromPath(document.FilePath, document.Project.Id, activate);
}
}

internal void OpenDocumentFromPath(string? filePath, ProjectId projectId, bool activate = true)
{
if (TryGetFrame(filePath, projectId, out var frame))
{
if (activate)
{
if (activate)
{
frame.Show();
}
else
{
frame.ShowNoActivate();
}
frame.Show();
}
else
{
frame.ShowNoActivate();
}
}
}

private bool TryGetFrame(CodeAnalysis.TextDocument document, [NotNullWhen(returnValue: true)] out IVsWindowFrame? frame)
/// <summary>
/// Opens a file and retrieves the window frame.
/// </summary>
/// <param name="filePath">the file path of the file to open.</param>
/// <param name="projectId">used to retrieve the IVsHierarchy to ensure the file is opened in a matching context.</param>
/// <param name="frame">the window frame.</param>
/// <returns></returns>
private bool TryGetFrame(string? filePath, ProjectId projectId, [NotNullWhen(returnValue: true)] out IVsWindowFrame? frame)
{
frame = null;

if (document.FilePath == null)
if (filePath == null)
{
return false;
}

var hierarchy = GetHierarchy(document.Project.Id);
var itemId = hierarchy?.TryGetItemId(document.FilePath) ?? (uint)VSConstants.VSITEMID.Nil;
var hierarchy = GetHierarchy(projectId);
var itemId = hierarchy?.TryGetItemId(filePath) ?? (uint)VSConstants.VSITEMID.Nil;
if (itemId == (uint)VSConstants.VSITEMID.Nil)
{
// If the ItemId is Nil, then IVsProject would not be able to open the
Expand All @@ -1035,7 +1047,7 @@ private bool TryGetFrame(CodeAnalysis.TextDocument document, [NotNullWhen(return

var openDocumentService = ServiceProvider.GlobalProvider.GetService<IVsUIShellOpenDocument, SVsUIShellOpenDocument>();
return ErrorHandler.Succeeded(openDocumentService.OpenDocumentViaProject(
document.FilePath,
filePath,
VSConstants.LOGVIEWID.TextView_guid,
out var oleServiceProvider,
out var uiHierarchy,
Expand Down
Loading