Skip to content

Commit 0c48c30

Browse files
authored
Merge pull request #45956 from dibarbet/razor_def
Add support for navigation to mapped file paths.
2 parents 14d6c4c + 57872e4 commit 0c48c30

File tree

5 files changed

+209
-104
lines changed

5 files changed

+209
-104
lines changed

src/VisualStudio/Core/Def/Implementation/ProjectSystem/RunningDocumentTableEventTracker.cs

Lines changed: 13 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33
// See the LICENSE file in the project root for more information.
44

5+
#nullable enable
6+
57
using System;
68
using System.Collections.Generic;
9+
using System.Diagnostics.CodeAnalysis;
710
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
811
using Microsoft.CodeAnalysis.PooledObjects;
912
using Microsoft.VisualStudio.Editor;
@@ -125,34 +128,26 @@ public int OnAfterDocumentWindowHide(uint docCookie, IVsWindowFrame pFrame)
125128
public int OnBeforeSave(uint docCookie)
126129
=> VSConstants.E_NOTIMPL;
127130

128-
public bool IsFileOpen(string fileName) => _runningDocumentTable.IsMonikerValid(fileName);
131+
public bool IsFileOpen(string fileName)
132+
{
133+
_foregroundAffinitization.AssertIsForeground();
134+
return _runningDocumentTable.IsFileOpen(fileName);
135+
}
129136

130137
/// <summary>
131138
/// Attempts to get a text buffer from the specified moniker.
132139
/// </summary>
133140
/// <param name="moniker">the moniker to retrieve the text buffer for.</param>
134141
/// <param name="textBuffer">the output text buffer or null if the moniker is invalid / document is not initialized.</param>
135142
/// <returns>true if the buffer was found with a non null value.</returns>
136-
public bool TryGetBufferFromMoniker(string moniker, out ITextBuffer textBuffer)
143+
public bool TryGetBufferFromMoniker(string moniker, [NotNullWhen(true)] out ITextBuffer? textBuffer)
137144
{
138145
_foregroundAffinitization.AssertIsForeground();
139146

140-
textBuffer = null;
141-
if (!IsFileOpen(moniker))
142-
{
143-
return false;
144-
}
145-
146-
var cookie = _runningDocumentTable.GetDocumentCookie(moniker);
147-
if (!_runningDocumentTable.IsDocumentInitialized(cookie))
148-
{
149-
return false;
150-
}
151-
152-
return TryGetBuffer(cookie, out textBuffer);
147+
return _runningDocumentTable.TryGetBufferFromMoniker(_editorAdaptersFactoryService, moniker, out textBuffer);
153148
}
154149

155-
public IVsHierarchy GetDocumentHierarchy(string moniker)
150+
public IVsHierarchy? GetDocumentHierarchy(string moniker)
156151
{
157152
if (!IsFileOpen(moniker))
158153
{
@@ -213,20 +208,8 @@ private bool TryGetMoniker(uint docCookie, out string moniker)
213208
return !string.IsNullOrEmpty(moniker);
214209
}
215210

216-
private bool TryGetBuffer(uint docCookie, out ITextBuffer textBuffer)
217-
{
218-
textBuffer = null;
219-
220-
// The cast from dynamic to object doesn't change semantics, but avoids loading the dynamic binder
221-
// which saves us JIT time in this method and an assembly load.
222-
if ((object)_runningDocumentTable.GetDocumentData(docCookie) is IVsTextBuffer bufferAdapter)
223-
{
224-
textBuffer = _editorAdaptersFactoryService.GetDocumentBuffer(bufferAdapter);
225-
return textBuffer != null;
226-
}
227-
228-
return false;
229-
}
211+
private bool TryGetBuffer(uint docCookie, [NotNullWhen(true)] out ITextBuffer? textBuffer)
212+
=> _runningDocumentTable.TryGetBuffer(_editorAdaptersFactoryService, docCookie, out textBuffer);
230213

231214
public void Dispose()
232215
{
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
#nullable enable
6+
7+
using System.Diagnostics.CodeAnalysis;
8+
using Microsoft.VisualStudio.Editor;
9+
using Microsoft.VisualStudio.Shell.Interop;
10+
using Microsoft.VisualStudio.Text;
11+
using Microsoft.VisualStudio.TextManager.Interop;
12+
13+
namespace Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem
14+
{
15+
/// <summary>
16+
/// Helper extensions for calling into the RDT.
17+
/// These must all be called from the UI thread.
18+
/// </summary>
19+
internal static class RunningDocumentTableExtensions
20+
{
21+
public static bool TryGetBufferFromMoniker(this IVsRunningDocumentTable4 runningDocumentTable,
22+
IVsEditorAdaptersFactoryService editorAdaptersFactoryService,
23+
string moniker, [NotNullWhen(true)] out ITextBuffer? textBuffer)
24+
{
25+
textBuffer = null;
26+
if (!runningDocumentTable.IsFileOpen(moniker))
27+
{
28+
return false;
29+
}
30+
31+
var cookie = runningDocumentTable.GetDocumentCookie(moniker);
32+
if (!runningDocumentTable.IsDocumentInitialized(cookie))
33+
{
34+
return false;
35+
}
36+
37+
return TryGetBuffer(runningDocumentTable, editorAdaptersFactoryService, cookie, out textBuffer);
38+
}
39+
40+
public static bool IsFileOpen(this IVsRunningDocumentTable4 runningDocumentTable, string fileName)
41+
=> runningDocumentTable.IsMonikerValid(fileName);
42+
43+
public static bool TryGetBuffer(this IVsRunningDocumentTable4 runningDocumentTable, IVsEditorAdaptersFactoryService editorAdaptersFactoryService,
44+
uint docCookie, [NotNullWhen(true)] out ITextBuffer? textBuffer)
45+
{
46+
textBuffer = null;
47+
48+
// The cast from dynamic to object doesn't change semantics, but avoids loading the dynamic binder
49+
// which saves us JIT time in this method and an assembly load.
50+
if ((object)runningDocumentTable.GetDocumentData(docCookie) is IVsTextBuffer bufferAdapter)
51+
{
52+
textBuffer = editorAdaptersFactoryService.GetDocumentBuffer(bufferAdapter);
53+
return textBuffer != null;
54+
}
55+
56+
return false;
57+
}
58+
}
59+
}

src/VisualStudio/Core/Def/Implementation/ProjectSystem/VisualStudioWorkspaceImpl.OpenFileTracker.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ public async static Task<OpenFileTracker> CreateAsync(VisualStudioWorkspaceImpl
118118
return new OpenFileTracker(workspace, runningDocumentTable, componentModel);
119119
}
120120

121-
private void TryOpeningDocumentsForMoniker(string moniker, ITextBuffer textBuffer, IVsHierarchy hierarchy)
121+
private void TryOpeningDocumentsForMoniker(string moniker, ITextBuffer textBuffer, IVsHierarchy? hierarchy)
122122
{
123123
_foregroundAffinitization.AssertIsForeground();
124124

@@ -171,7 +171,7 @@ private void TryOpeningDocumentsForMoniker(string moniker, ITextBuffer textBuffe
171171
});
172172
}
173173

174-
private ProjectId GetActiveContextProjectIdAndWatchHierarchies(string moniker, IEnumerable<ProjectId> projectIds, IVsHierarchy hierarchy)
174+
private ProjectId GetActiveContextProjectIdAndWatchHierarchies(string moniker, IEnumerable<ProjectId> projectIds, IVsHierarchy? hierarchy)
175175
{
176176
_foregroundAffinitization.AssertIsForeground();
177177

src/VisualStudio/Core/Def/Implementation/ProjectSystem/VisualStudioWorkspaceImpl.cs

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1002,31 +1002,43 @@ public void OpenDocumentCore(DocumentId documentId, bool activate = true)
10021002
var document = this.CurrentSolution.GetTextDocument(documentId);
10031003
if (document != null)
10041004
{
1005-
if (TryGetFrame(document, out var frame))
1005+
OpenDocumentFromPath(document.FilePath, document.Project.Id, activate);
1006+
}
1007+
}
1008+
1009+
internal void OpenDocumentFromPath(string? filePath, ProjectId projectId, bool activate = true)
1010+
{
1011+
if (TryGetFrame(filePath, projectId, out var frame))
1012+
{
1013+
if (activate)
10061014
{
1007-
if (activate)
1008-
{
1009-
frame.Show();
1010-
}
1011-
else
1012-
{
1013-
frame.ShowNoActivate();
1014-
}
1015+
frame.Show();
1016+
}
1017+
else
1018+
{
1019+
frame.ShowNoActivate();
10151020
}
10161021
}
10171022
}
10181023

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

1023-
if (document.FilePath == null)
1035+
if (filePath == null)
10241036
{
10251037
return false;
10261038
}
10271039

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

10361048
var openDocumentService = ServiceProvider.GlobalProvider.GetService<SVsUIShellOpenDocument, IVsUIShellOpenDocument>();
10371049
return ErrorHandler.Succeeded(openDocumentService.OpenDocumentViaProject(
1038-
document.FilePath,
1050+
filePath,
10391051
VSConstants.LOGVIEWID.TextView_guid,
10401052
out var oleServiceProvider,
10411053
out var uiHierarchy,

0 commit comments

Comments
 (0)