Search code examples
c#visual-studiovisual-studio-2022visual-studio-extensionscode-navigation

How to integrate programmatically a custom navigation into Visual Studio "Navigate Backwards" list


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:

Navigate Backward screenshot

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:

    • Code Map doesn't have a text under caret that is used by the "Go To Definition" command as a sort of input.
    • In general, I need a custom navigation, not just a navigation to the definition. My extension has other functionality that requires custom navigation rules.

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.


Solution

  • 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