11using System ;
22using System . Composition ;
3+ using System . Linq ;
34using System . Threading ;
4- using System . Threading . Tasks ;
55using Microsoft . CodeAnalysis ;
66using Microsoft . CodeAnalysis . Host ;
77using Microsoft . CodeAnalysis . Host . Mef ;
88using Microsoft . CodeAnalysis . Navigation ;
99using Microsoft . CodeAnalysis . Options ;
1010using Microsoft . CodeAnalysis . Text ;
1111using Microsoft . VisualStudio . Text ;
12+ using Microsoft . VisualStudio . Text . Projection ;
1213using MonoDevelop . Core ;
1314using MonoDevelop . Ide . TypeSystem ;
1415
@@ -19,11 +20,14 @@ internal sealed class VisualStudioDocumentNavigationServiceFactory : IWorkspaceS
1920 {
2021 private readonly IDocumentNavigationService _singleton ;
2122
23+ [ Import ]
24+ public IBufferGraphFactoryService BufferGraphFactoryService { get ; set ; }
25+
2226 [ ImportingConstructor ]
2327 [ Obsolete ( MefConstruction . ImportingConstructorMessage , error : true ) ]
2428 private VisualStudioDocumentNavigationServiceFactory ( )
2529 {
26- _singleton = new MonoDevelopDocumentNavigationService ( ) ;
30+ _singleton = new MonoDevelopDocumentNavigationService ( this ) ;
2731 }
2832
2933 public IWorkspaceService CreateService ( HostWorkspaceServices workspaceServices )
@@ -34,6 +38,13 @@ public IWorkspaceService CreateService (HostWorkspaceServices workspaceServices)
3438
3539 class MonoDevelopDocumentNavigationService : IDocumentNavigationService
3640 {
41+ private VisualStudioDocumentNavigationServiceFactory factory ;
42+
43+ public MonoDevelopDocumentNavigationService ( VisualStudioDocumentNavigationServiceFactory visualStudioDocumentNavigationServiceFactory )
44+ {
45+ this . factory = visualStudioDocumentNavigationServiceFactory ;
46+ }
47+
3748 public bool CanNavigateToSpan ( Workspace workspace , DocumentId documentId , TextSpan textSpan )
3849 {
3950 // Navigation should not change the context of linked files and Shared Projects.
@@ -99,13 +110,12 @@ public bool TryNavigateToSpan (Workspace workspace, DocumentId documentId, TextS
99110
100111 Runtime . AssertMainThread ( ) ;
101112
102- var document = OpenDocument ( workspace , documentId , options ) ;
113+ var document = workspace . CurrentSolution . GetDocument ( documentId ) ;
103114 if ( document == null ) {
104115 return false ;
105116 }
106117
107118 var text = document . GetTextSynchronously ( CancellationToken . None ) ;
108- var textBuffer = text . Container . GetTextBuffer ( ) ;
109119
110120 var boundedTextSpan = GetSpanWithinDocumentBounds ( textSpan , text . Length ) ;
111121 if ( boundedTextSpan != textSpan ) {
@@ -127,7 +137,7 @@ public bool TryNavigateToLineAndOffset (Workspace workspace, DocumentId document
127137
128138 Runtime . AssertMainThread ( ) ;
129139
130- var document = OpenDocument ( workspace , documentId , options ) ;
140+ var document = workspace . CurrentSolution . GetDocument ( documentId ) ;
131141 if ( document == null ) {
132142 return false ;
133143 }
@@ -149,7 +159,7 @@ public bool TryNavigateToPosition (Workspace workspace, DocumentId documentId, i
149159
150160 Runtime . AssertMainThread ( ) ;
151161
152- var document = OpenDocument ( workspace , documentId , options ) ;
162+ var document = workspace . CurrentSolution . GetDocument ( documentId ) ;
153163 if ( document == null ) {
154164 return false ;
155165 }
@@ -222,13 +232,41 @@ private static Document OpenDocument (Workspace workspace, DocumentId documentId
222232
223233 private bool NavigateTo ( Document document , TextSpan span )
224234 {
235+ string filePath = document . FilePath ;
236+ filePath = GetActualFilePathToOpen ( filePath ) ;
225237 var proj = ( document . Project . Solution . Workspace as MonoDevelopWorkspace ) ? . GetMonoProject ( document . Project ) ;
226- var task = IdeApp . Workbench . OpenDocument ( new Gui . FileOpenInformation ( document . FilePath , proj ) {
238+ var task = IdeApp . Workbench . OpenDocument ( new Gui . FileOpenInformation ( filePath , proj ) {
227239 Offset = span . Start
228240 } ) ;
229241 return true ;
230242 }
231243
244+ /// <summary>
245+ /// Razor: Strip the .g.cs since we want to open the corresponding .cshtml or .razor document.
246+ ///
247+ /// In Visual Studio for Windows the underlying C# buffer is added to the workspace with the
248+ /// .cshtml or .razor extension (without the .g.cs) part, so they don't have to worry about
249+ /// this. In our case we have an assumption somewhere that all C# documents in the workspace
250+ /// have the .cs extension, so we're adding the .g.cs part that we need to strip here.
251+ ///
252+ /// This is not great to hardcode application-specific logic here, but we don't anticipate
253+ /// more scenarios where we want to open a different file than requested, so it doesn't
254+ /// warrant an extension point at this time.
255+ /// </summary>
256+ string GetActualFilePathToOpen ( string filePath )
257+ {
258+ if ( filePath == null ) {
259+ return null ;
260+ }
261+
262+ if ( filePath . EndsWith ( ".cshtml.g.cs" , StringComparison . OrdinalIgnoreCase ) ||
263+ filePath . EndsWith ( ".razor.g.cs" , StringComparison . OrdinalIgnoreCase ) ) {
264+ filePath = filePath . Substring ( 0 , filePath . Length - ".g.cs" . Length ) ;
265+ }
266+
267+ return filePath ;
268+ }
269+
232270 private bool IsSecondaryBuffer ( Workspace workspace , Document document )
233271 {
234272 var containedDocument = MonoDevelopHostDocumentRegistration . FromDocument ( document ) ;
@@ -239,23 +277,33 @@ private bool IsSecondaryBuffer (Workspace workspace, Document document)
239277 return true ;
240278 }
241279
242- public static bool TryMapSpanFromSecondaryBufferToPrimaryBuffer ( TextSpan spanInSecondaryBuffer , Microsoft . CodeAnalysis . Workspace workspace , Document document , out TextSpan spanInPrimaryBuffer )
280+ public bool TryMapSpanFromSecondaryBufferToPrimaryBuffer ( TextSpan spanInSecondaryBuffer , Microsoft . CodeAnalysis . Workspace workspace , Document document , out TextSpan spanInPrimaryBuffer )
243281 {
244282 spanInPrimaryBuffer = default ;
245283
246284 var containedDocument = MonoDevelopHostDocumentRegistration . FromDocument ( document ) ;
247285 if ( containedDocument == null ) {
248286 return false ;
249287 }
250- throw new NotImplementedException ( ) ;
251- //var bufferCoordinator = containedDocument.BufferCoordinator;
252288
253- //var primary = new VsTextSpan [1];
254- //var hresult = bufferCoordinator.MapSecondaryToPrimarySpan (spanInSecondaryBuffer, primary);
289+ var projectionBuffer = containedDocument . TopBuffer ;
290+
291+ var bufferGraph = factory . BufferGraphFactoryService . CreateBufferGraph ( projectionBuffer ) ;
255292
256- //spanInPrimaryBuffer = primary [0];
293+ if ( document . TryGetText ( out var sourceText ) && sourceText . Container . TryGetTextBuffer ( ) is ITextBuffer languageBuffer ) {
294+ var secondarySnapshot = languageBuffer . CurrentSnapshot ;
295+ var snapshotSpanInSecondaryBuffer = new SnapshotSpan ( secondarySnapshot , new Span ( spanInSecondaryBuffer . Start , spanInSecondaryBuffer . Length ) ) ;
296+ var topBufferSnapshotSpan = bufferGraph . MapUpToSnapshot (
297+ snapshotSpanInSecondaryBuffer ,
298+ SpanTrackingMode . EdgeExclusive ,
299+ projectionBuffer . CurrentSnapshot ) . FirstOrDefault ( ) ;
300+ if ( topBufferSnapshotSpan != default ) {
301+ spanInPrimaryBuffer = new TextSpan ( topBufferSnapshotSpan . Start , topBufferSnapshotSpan . Length ) ;
302+ return true ;
303+ }
304+ }
257305
258- // return ErrorHandler.Succeeded (hresult) ;
306+ return false ;
259307 }
260308
261309 private bool CanMapFromSecondaryBufferToPrimaryBuffer ( Workspace workspace , Document document , TextSpan spanInSecondaryBuffer )
0 commit comments