I develop a Visual Studio extension with a custom Code Map. It is a tree view control which nodes represent specific code elements. When you click on the tree node you navigate to the declaration of the corresponding code element in the code editor.
A simplified version of the navigation looks like this:
public static async Task NavigateAsync(
Microsoft.VisualStudio.Shell.IAsyncServiceProvider serviceProvider,
string filePath,int lineNumber, int character)
{
// first I obtain IWpfTextView for the active document
IWpfTextView wpfTextView = await OpenCodeWindowAsync(serviceProvider, filePath);
// next, generate the position to navigate to
ITextSnapshotLine textLine = wpfTextView.TextSnapshot.GetLineFromLineNumber(lineNumber);
int absoluteOffset = textLine.Start + character;
// expand all collapsed regions in case we navigate inside them
// with a call to the helper method
SnapshotPoint point = wpfTextView.TextSnapshot.GetPoint(absoluteOffset);
SnapshotSpan span = new SnapshotSpan(point, length: 0);
await serviceProvider.ExpandAllRegionsContainingSpanAsync(span, wpfTextView);
// move the caret to a new position and scroll the code editor to it
CaretPosition newCaretPosition = wpfTextView.MoveCaretTo(absoluteOffset);
wpfTextView.ViewScroller.EnsureSpanVisible(span, EnsureSpanVisibleOptions.AlwaysCenter);
}
Everything works fine, except the list of visited location in Visual Studio is not updated. I mean the "Navigate Backward" journal located in the top left corner of Visual Studio:
I have found this description of this functionality on MSDN:
https://learn.microsoft.com/en-us/archive/blogs/zainnab/navigate-backward-and-navigate-forward
It mentions that VS creates so-called "go-back markers" which are basically entries in the navigation journal. In some other places they could be also referred to as "way points". They are created under the following conditions:
A go-back marker is dropped under the following conditions:
An incremental search (including reverse) leaves a go-back marker at the beginning of the search and another one at the end.
A Go To Line action, like CTRL + G, or a mouse-click that moves the cursor 11 lines or more from the current position drops a go-back marker at the new location.
A destructive action (like hitting Backspace) after having moved the cursor to a new location drops a go-back marker.
Doing a search, like Ctrl+F, drops a go-back marker at the found location.
Opening a file drops a go-back marker wherever the cursor was on the old file and drops another on the opened file.
I believe this is what I need. I want to programmatically add such go-back markers but can't find a way to do it. I have looked for them in Google, MSDN, VS Extensibility examples from Mads Kristensen, open source projects on GitHub but so far I haven't managed to find any API that would allow to use them. What I already found:
Implementations very close to what I use in other projects that have custom Code Maps for VS
Implementations using old VS COM interfaces like this:
EnvDTE.TextSelection ts = dte.ActiveDocument.Selection as EnvDTE.TextSelection;
ts.MoveToLineAndOffset(…);
The navigation works but the navigation journal is still not updated.
The navigation implementations that internally call "Go To Definition" command like this. I can't reuse the "Go To Definition" command because:
Currently, I'm stuck with this issue and I would really appreciate any help.
UPDATE: I've also tried to check VS open source layers which are stored on GitHub: https://github.com/microsoft/vs-editor-api/tree/main Unfortunately, I haven't found anything I can use, or the implementation of the "Navigate Backward" functionality. Maybe I've just missed it.
There are internal IDocumentNavigationService and IGoToDefinitionService in Roslyn, currently I try to see if I can use them either with a reflection or by using a similar logic. If this approach works our I'll add an answer describing it.
Still, I would always prefer a public API over the usage of the internal services, so my question is still relevant. I would mark answer that uses public API and solved my issue as correct over any answer that relies on calls to internal services.
Actually, a comment from @JasonMalinowski in my other question also resolved this issue. Since there wasn't any answers for a long time, I will post the link to that comment as an answer: How to navigate to a definition of a Symbol located in the external assembly
And to provide more context - to navigate correctly in Visual Studio you should use a special helper method from VisualStudioWorkspace
:
https://github.com/dotnet/roslyn/blob/e1b57268c58d0ccd93992ef3495cd22b8ad07412/src/VisualStudio/Core/Def/Implementation/ProjectSystem/VisualStudioWorkspace.cs#L103
In addition to the navigation this helper will also add entries to the VS "navigation journal" I described in the question.
You can also read about this method from another SO answer by @JasonMalinowski: https://stackoverflow.com/a/69668770/2893207