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;
diff --git a/src/CodeNav/Services/IInProcService.cs b/src/CodeNav/Services/IInProcService.cs
index 8b233aa..a5f2173 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 = "CodeNav.InProcService";
diff --git a/src/CodeNav/Services/InProcService.cs b/src/CodeNav/Services/InProcService.cs
index 4b322fc..8aa36b8 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;
@@ -16,15 +17,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;
}
@@ -40,6 +44,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
@@ -47,7 +120,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();
@@ -62,7 +135,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;