Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/CodeNav.OutOfProc/ExtensionEntrypoint.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using CodeNav.OutOfProc.Services;
using CodeNav.OutOfProc.Helpers;
using CodeNav.OutOfProc.Services;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.VisualStudio.Extensibility;

Expand Down Expand Up @@ -36,5 +37,6 @@ protected override void InitializeServices(IServiceCollection serviceCollection)
// As of now, any instance that ingests VisualStudioExtensibility is required to be added as a scoped
// service.
serviceCollection.AddScoped<OutputWindowService>();
serviceCollection.AddScoped<OutliningHelper>();
}
}
130 changes: 126 additions & 4 deletions src/CodeNav.OutOfProc/Helpers/OutliningHelper.cs
Original file line number Diff line number Diff line change
@@ -1,18 +1,122 @@
using CodeNav.OutOfProc.Extensions;
using CodeNav.OutOfProc.Interfaces;
using CodeNav.OutOfProc.ViewModels;
using CodeNav.Services;
using Microsoft;
using Microsoft.VisualStudio.Extensibility;
using Microsoft.VisualStudio.Extensibility.Helpers;

namespace CodeNav.OutOfProc.Helpers;

public static class OutliningHelper
public class OutliningHelper : DisposableObject
{
private readonly VisualStudioExtensibility _extensibility;
private readonly Task _initializationTask;
private IInProcService? _inProcService;

public OutliningHelper(VisualStudioExtensibility extensibility)
{
_extensibility = extensibility;
_initializationTask = Task.Run(InitializeAsync);
}

public async Task SubscribeToRegionEvents()
{
try
{
Assumes.NotNull(_inProcService);
await _inProcService.SubscribeToRegionEvents();
}
catch (Exception e)

Check warning on line 30 in src/CodeNav.OutOfProc/Helpers/OutliningHelper.cs

View workflow job for this annotation

GitHub Actions / 🛠️ Build

The variable 'e' is declared but never used

Check warning on line 30 in src/CodeNav.OutOfProc/Helpers/OutliningHelper.cs

View workflow job for this annotation

GitHub Actions / 🛠️ Build

The variable 'e' is declared but never used
{
// TODO: Add logging
}
}

public async Task CollapseOutlineRegion(int start, int length)
{
try
{
Assumes.NotNull(_inProcService);
await _inProcService.CollapseOutlineRegion(start, length);
}
catch (Exception e)

Check warning on line 43 in src/CodeNav.OutOfProc/Helpers/OutliningHelper.cs

View workflow job for this annotation

GitHub Actions / 🛠️ Build

The variable 'e' is declared but never used

Check warning on line 43 in src/CodeNav.OutOfProc/Helpers/OutliningHelper.cs

View workflow job for this annotation

GitHub Actions / 🛠️ Build

The variable 'e' is declared but never used
{
// TODO: Add logging
}
}

public async Task ExpandOutlineRegion(int start, int length)
{
try
{
Assumes.NotNull(_inProcService);
await _inProcService.ExpandOutlineRegion(start, length);
}
catch (Exception e)

Check warning on line 56 in src/CodeNav.OutOfProc/Helpers/OutliningHelper.cs

View workflow job for this annotation

GitHub Actions / 🛠️ Build

The variable 'e' is declared but never used

Check warning on line 56 in src/CodeNav.OutOfProc/Helpers/OutliningHelper.cs

View workflow job for this annotation

GitHub Actions / 🛠️ Build

The variable 'e' is declared but never used
{
// TODO: Add logging
}
}

public static async Task CollapseOutlineRegion(CodeItem codeItem)
{
if (codeItem.CodeDocumentViewModel?.CodeDocumentService?.OutliningHelper == null)
{
return;
}

await codeItem.CodeDocumentViewModel.CodeDocumentService.OutliningHelper.CollapseOutlineRegion(codeItem.Span.Start, codeItem.Span.Length);
}

public static async Task ExpandOutlineRegion(CodeItem codeItem)
{
if (codeItem.CodeDocumentViewModel?.CodeDocumentService?.OutliningHelper == null)
{
return;
}

await codeItem.CodeDocumentViewModel.CodeDocumentService.OutliningHelper.ExpandOutlineRegion(codeItem.Span.Start, codeItem.Span.Length);
}

/// <summary>
/// Set IsExpanded property to false on all code items
/// </summary>
/// <remarks>Used in the main toolbar and in the code item context menu</remarks>
/// <param name="codeDocumentViewModel">The code document view model whose nodes will be collapsed.</param>
public static void CollapseAll(CodeDocumentViewModel? codeDocumentViewModel)
=> SetIsExpanded(codeDocumentViewModel, isExpanded: false);
=> SetAllIsExpanded(codeDocumentViewModel, isExpanded: false);

/// <summary>
/// Set IsExpanded property to true on all code items
/// </summary>
/// <remarks>Used in the main toolbar and in the code item context menu</remarks>
/// <param name="codeDocumentViewModel">The code document view model whose nodes will be expanded.</param>
public static void ExpandAll(CodeDocumentViewModel? codeDocumentViewModel)
=> SetIsExpanded(codeDocumentViewModel, isExpanded: true);
=> SetAllIsExpanded(codeDocumentViewModel, isExpanded: true);

private static void SetIsExpanded(CodeDocumentViewModel? codeDocumentViewModel, bool isExpanded)
public static void SetIsExpanded(CodeDocumentViewModel? codeDocumentViewModel, int spanStart, int spanEnd, bool isExpanded)
{
codeDocumentViewModel?
.CodeItems
.Flatten()
.FilterNull()
.Where(item => item is IMembers)
.Where(codeItem => codeItem.Span.Start == spanStart ||
codeItem.Span.End == spanEnd)
.Cast<IMembers>()
.ToList()
.ForEach(codeItem => codeItem.IsExpanded = isExpanded);
}

/// <summary>
/// Sets the expanded state for all member items within the specified code document view model.
/// </summary>
/// <remarks>Only items that implement the IMembers interface are affected.</remarks>
/// <param name="codeDocumentViewModel">The code document view model containing the code items to update. If null, no action is taken.</param>
/// <param name="isExpanded">A value indicating whether the member items should be expanded (<see langword="true"/>) or collapsed (<see
/// langword="false"/>).</param>
private static void SetAllIsExpanded(CodeDocumentViewModel? codeDocumentViewModel, bool isExpanded)
{
codeDocumentViewModel?
.CodeItems
Expand All @@ -23,4 +127,22 @@
.ToList()
.ForEach(item => item.IsExpanded = isExpanded);
}

private async Task InitializeAsync()
{
(_inProcService as IDisposable)?.Dispose();
_inProcService = await _extensibility
.ServiceBroker
.GetProxyAsync<IInProcService>(IInProcService.Configuration.ServiceDescriptor, cancellationToken: default);
}

protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);

if (isDisposing)
{
(_inProcService as IDisposable)?.Dispose();
}
}
}
6 changes: 5 additions & 1 deletion src/CodeNav.OutOfProc/Services/CodeDocumentService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@

namespace CodeNav.OutOfProc.Services;

public class CodeDocumentService(OutputWindowService logService)
public class CodeDocumentService(
OutputWindowService logService,
OutliningHelper outliningHelper)
{
/// <summary>
/// DataContext for the tool window.
Expand All @@ -31,6 +33,8 @@ public class CodeDocumentService(OutputWindowService logService)

public OutputWindowService LogService => logService;

public OutliningHelper OutliningHelper => outliningHelper;

public async Task<CodeDocumentViewModel> UpdateCodeDocumentViewModel(
VisualStudioExtensibility? extensibility,
ITextViewSnapshot? textView,
Expand Down
2 changes: 2 additions & 0 deletions src/CodeNav.OutOfProc/Services/IOutOfProcService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ public interface IOutOfProcService
{
Task DoSomethingAsync(CancellationToken cancellationToken);

Task SetCodeItemIsExpanded(int spanStart, int spanEnd, bool isExpanded);

public static class Configuration
{
public const string ServiceName = "CodeNav.OutOfProcService";
Expand Down
20 changes: 15 additions & 5 deletions src/CodeNav.OutOfProc/Services/OutOfProcService.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Microsoft.ServiceHub.Framework;
using CodeNav.OutOfProc.Helpers;
using Microsoft.ServiceHub.Framework;
using Microsoft.VisualStudio.Extensibility;
using Microsoft.VisualStudio.Extensibility.Shell;

Expand All @@ -7,11 +8,15 @@ namespace CodeNav.OutOfProc.Services;
[VisualStudioContribution]
internal class OutOfProcService : IOutOfProcService, IBrokeredService
{
private readonly VisualStudioExtensibility extensibility;
private readonly VisualStudioExtensibility _extensibility;
private readonly CodeDocumentService _codeDocumentService;

public OutOfProcService(VisualStudioExtensibility extensibility)
public OutOfProcService(
VisualStudioExtensibility extensibility,
CodeDocumentService codeDocumentService)
{
this.extensibility = extensibility;
_extensibility = extensibility;
_codeDocumentService = codeDocumentService;
}

public static BrokeredServiceConfiguration BrokeredServiceConfiguration
Expand All @@ -22,8 +27,13 @@ public static BrokeredServiceConfiguration BrokeredServiceConfiguration

public static ServiceRpcDescriptor ServiceDescriptor => IOutOfProcService.Configuration.ServiceDescriptor;

public async Task SetCodeItemIsExpanded(int spanStart, int spanEnd, bool isExpanded)
{
OutliningHelper.SetIsExpanded(_codeDocumentService.CodeDocumentViewModel, spanStart, spanEnd, isExpanded);
}

public async Task DoSomethingAsync(CancellationToken cancellationToken)
{
await this.extensibility.Shell().ShowPromptAsync("Hello from in-proc! (Showing this message from (out-of-proc)", PromptOptions.OK, cancellationToken);
await _extensibility.Shell().ShowPromptAsync("Hello from in-proc! (Showing this message from (out-of-proc)", PromptOptions.OK, cancellationToken);
}
}
19 changes: 17 additions & 2 deletions src/CodeNav.OutOfProc/ViewModels/CodeClassItem.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using CodeNav.OutOfProc.Interfaces;
using CodeNav.OutOfProc.Helpers;
using CodeNav.OutOfProc.Interfaces;
using Microsoft.VisualStudio.Extensibility;
using Microsoft.VisualStudio.Extensibility.UI;
using System.Runtime.Serialization;
Expand Down Expand Up @@ -35,8 +36,18 @@ public bool IsExpanded
{
if (_isExpanded != value)
{
SetProperty(ref _isExpanded, value);
SetProperty(ref _isExpanded, value);

IsExpandedChanged?.Invoke(this, EventArgs.Empty);

if (value)
{
_ = OutliningHelper.ExpandOutlineRegion(this);
}
else
{
_ = OutliningHelper.CollapseOutlineRegion(this);
}
}
}
}
Expand All @@ -51,6 +62,10 @@ public Visibility HasMembersVisibility
? Visibility.Visible
: Visibility.Collapsed;

/// <summary>
/// Command use to collapse and expand class/region/namespace code items
/// when double-clicking on the expander header
/// </summary>
[DataMember]
public AsyncCommand ToggleExpandCollapseCommand { get; }
public async Task ToggleExpandCollapse(object? commandParameter, IClientContext clientContext, CancellationToken cancellationToken)
Expand Down
12 changes: 12 additions & 0 deletions src/CodeNav.OutOfProc/ViewModels/CodeItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,20 @@ public CodeItem()
[DataMember]
public string Tooltip { get; set; } = string.Empty;

/// <summary>
/// Path to the file containing the code item
/// </summary>
/// <remarks>
/// Used for opening the file if it's different from the currently active one
/// </remarks>
public Uri? FilePath { get; set; }

/// <summary>
/// Full name of the code item
/// </summary>
/// <remarks>
/// Used in constructing a unique id
/// </remarks>
internal string FullName = string.Empty;

public CodeItemKindEnum Kind;
Expand Down
6 changes: 6 additions & 0 deletions src/CodeNav/Services/IInProcService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ public interface IInProcService

Task TextViewScrollToSpan(int start, int length);

Task ExpandOutlineRegion(int start, int length);

Task CollapseOutlineRegion(int start, int length);

Task SubscribeToRegionEvents();

public static class Configuration
{
public const string ServiceName = "CodeNav.InProcService";
Expand Down
Loading