From fe8652c5b38f1c00f2caf16091f95c8a4694f71a Mon Sep 17 00:00:00 2001 From: Samir Boulema Date: Wed, 11 Mar 2026 20:59:33 +0100 Subject: [PATCH 1/2] feat: Implement outlining --- src/CodeNav/Services/IInProcService.cs | 4 ++ src/CodeNav/Services/InProcService.cs | 77 +++++++++++++++++++++++++- 2 files changed, 79 insertions(+), 2 deletions(-) diff --git a/src/CodeNav/Services/IInProcService.cs b/src/CodeNav/Services/IInProcService.cs index 9496ff0..aeffc9a 100644 --- a/src/CodeNav/Services/IInProcService.cs +++ b/src/CodeNav/Services/IInProcService.cs @@ -8,6 +8,10 @@ public interface IInProcService Task TextViewScrollToSpan(int start, int length); + Task ExpandOutlineRegion(int start, int length); + + Task CollapseOutlineRegion(int start, int length); + public static class Configuration { public const string ServiceName = "OutOfProcComponent.InProcService"; diff --git a/src/CodeNav/Services/InProcService.cs b/src/CodeNav/Services/InProcService.cs index 0484883..8bf10ad 100644 --- a/src/CodeNav/Services/InProcService.cs +++ b/src/CodeNav/Services/InProcService.cs @@ -7,6 +7,7 @@ using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.Text.Outlining; using Microsoft.VisualStudio.TextManager.Interop; namespace CodeNav.Services; @@ -15,15 +16,18 @@ internal class InProcService : IInProcService { private readonly VisualStudioExtensibility _extensibility; private readonly MefInjection _editorAdaptersFactoryService; + private readonly MefInjection _outliningManagerFactoryService; private readonly AsyncServiceProviderInjection _textManager; public InProcService( VisualStudioExtensibility extensibility, MefInjection editorAdaptersFactoryService, + MefInjection outliningManagerFactoryService, AsyncServiceProviderInjection textManager) { _extensibility = extensibility; _editorAdaptersFactoryService = editorAdaptersFactoryService; + _outliningManagerFactoryService = outliningManagerFactoryService; _textManager = textManager; } @@ -39,6 +43,75 @@ public async Task DoSomethingAsync(CancellationToken cancellationToken) await _extensibility.Shell().ShowPromptAsync("Hello from out-of-proc! (Showing this message from (in-proc)", PromptOptions.OK, cancellationToken); } + public async Task ExpandOutlineRegion(int start, int length) + { + try + { + // Not using context.GetActiveTextViewAsync here because VisualStudio.Extensibility doesn't support outlining yet. + var textView = await GetCurrentTextViewAsync(); + + var outliningManager = await GetOutliningManager(textView); + + var outlineRegion = await GetOutlineRegionForSpan(textView, outliningManager, start, length); + + // Check if the outline region is collapsed before expanding + if (outlineRegion?.IsCollapsed != true || + outlineRegion is not ICollapsed collapsedOutlineRegion) + { + return; + } + + outliningManager.Expand(collapsedOutlineRegion); + } + catch (Exception) + { + // TODO: Implement in-proc error logging + } + } + + public async Task CollapseOutlineRegion(int start, int length) + { + try + { + // Not using context.GetActiveTextViewAsync here because VisualStudio.Extensibility doesn't support outlining yet. + var textView = await GetCurrentTextViewAsync(); + + var outliningManager = await GetOutliningManager(textView); + + var outlineRegion = await GetOutlineRegionForSpan(textView, outliningManager, start, length); + + outliningManager.TryCollapse(outlineRegion); + } + catch (Exception) + { + // TODO: Implement in-proc error logging + } + } + + private async Task GetOutlineRegionForSpan( + IWpfTextView textView, IOutliningManager outliningManager, + int start, int length) + { + // Get all outline regions for the given span + var span = new SnapshotSpan(textView.TextSnapshot, start, length); + + var outlineRegions = outliningManager.GetAllRegions(span); + + // Get the first outline region that has the same span start + return outlineRegions.FirstOrDefault(outlineRegion => GetSpan(outlineRegion).Start == start); + } + + private SnapshotSpan GetSpan(ICollapsible outlineRegion) + => outlineRegion.Extent.GetSpan(outlineRegion.Extent.TextBuffer.CurrentSnapshot); + + private async Task GetOutliningManager(IWpfTextView textView) + { + var outliningManagerService = await _outliningManagerFactoryService.GetServiceAsync(); + var outliningManager = outliningManagerService.GetOutliningManager(textView); + + return outliningManager; + } + public async Task TextViewScrollToSpan(int start, int length) { try @@ -46,7 +119,7 @@ public async Task TextViewScrollToSpan(int start, int length) // Not using context.GetActiveTextViewAsync here because VisualStudio.Extensibility doesn't support viewscroller yet. var textView = await GetCurrentTextViewAsync(); - var span = new SnapshotSpan(textView.TextSnapshot, new Span(start, length)); + var span = new SnapshotSpan(textView.TextSnapshot, start, length); // Switch to the UI thread to ensure we can interact with the view scroller. await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); @@ -61,7 +134,7 @@ public async Task TextViewScrollToSpan(int start, int length) private async Task GetCurrentTextViewAsync() { - IVsEditorAdaptersFactoryService editorAdapter = await _editorAdaptersFactoryService.GetServiceAsync(); + var editorAdapter = await _editorAdaptersFactoryService.GetServiceAsync(); var view = editorAdapter.GetWpfTextView(await GetCurrentNativeTextViewAsync()); Assumes.Present(view); return view; From 9c199551f46c3fdb79c57836856f909fb6040d2f Mon Sep 17 00:00:00 2001 From: Samir Boulema Date: Fri, 13 Mar 2026 08:49:46 +0100 Subject: [PATCH 2/2] docs: Add xml doc --- src/CodeNav.OutOfProc/ViewModels/CodeItem.cs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/CodeNav.OutOfProc/ViewModels/CodeItem.cs b/src/CodeNav.OutOfProc/ViewModels/CodeItem.cs index b817576..d014069 100644 --- a/src/CodeNav.OutOfProc/ViewModels/CodeItem.cs +++ b/src/CodeNav.OutOfProc/ViewModels/CodeItem.cs @@ -1,6 +1,5 @@ using CodeNav.OutOfProc.Constants; using CodeNav.OutOfProc.Helpers; -using CodeNav.OutOfProc.Services; using CodeNav.Services; using Microsoft; using Microsoft.CodeAnalysis.Text; @@ -76,8 +75,20 @@ public CodeItem() [DataMember] public string Tooltip { get; set; } = string.Empty; + /// + /// Path to the file containing the code item + /// + /// + /// Used for opening the file if it's different from the currently active one + /// public Uri? FilePath { get; set; } + /// + /// Full name of the code item + /// + /// + /// Used in constructing a unique id + /// internal string FullName = string.Empty; public CodeItemKindEnum Kind;