Skip to content
Merged
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
2 changes: 1 addition & 1 deletion .github/workflows/workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ on:
- 'feature/**'

env:
version: '10.2.${{ github.run_number }}'
version: '10.3.${{ github.run_number }}'
dotnetVersion: '8'
repoUrl: ${{ github.server_url }}/${{ github.repository }}
vsixPath: ${{ github.workspace }}/src/CodeNav/bin/Release/net472/CodeNav.vsix
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ Visual Studio extension to show the code structure of your current document
- Hide, ignore or change opacity of items based on item kind (method, property), access (public, private), empty state
- Customizable fonts
- Bookmarks
- Synced collapsing/expanding outline regions


## Supported Visual Studio versions

Expand Down
3 changes: 3 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ Read all on how to modify the appearance of code items usin [filter rules](filte
## Settings
Read all on the [settings](settings.md) available to further customize CodeNav

## Spans
Read all about the different kind of [spans](spans.md) that are being used in CodeNav

## Version 10
Read all about the new [version 10](version10.md) of CodeNav

Expand Down
7 changes: 5 additions & 2 deletions docs/links.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,8 @@ Here is a list of links to helpful pages I needed when developing this extension
- [Setting WPF border visible or not depending on ScrollViewer's VerticalScrollBarVisibility property](https://stackoverflow.com/questions/73199311/setting-wpf-border-visible-or-not-depending-on-scrollviewers-verticalscrollbarv/73199480#73199480)
- [CommentRemover Sample](https://github.com/microsoft/VSExtensibility/tree/main/New_Extensibility_Model/Samples/CommentRemover)
- [CompositeExtension Sample](https://github.com/microsoft/VSExtensibility/tree/main/New_Extensibility_Model/Samples/CompositeExtension)
- [VSExtensibility - #554 - Feature request: Text Editor: Scroll to line](https://github.com/microsoft/VSExtensibility/issues/554)
- [The CheckBox control](https://wpf-tutorial.com/basic-controls/the-checkbox-control/)
- [The CheckBox control](https://wpf-tutorial.com/basic-controls/the-checkbox-control/)

## VSExtensibility Issues
- [#545 - Feature request: Text Editor: Collapse/Expand ranges](https://github.com/microsoft/VSExtensibility/issues/545)
- [#554 - Feature request: Text Editor: Scroll to line](https://github.com/microsoft/VSExtensibility/issues/554)
65 changes: 65 additions & 0 deletions docs/spans.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Spans

A code item has a set of different span, each with their own start, end and length.
Each of these spans have their own specific purpose in CodeNav.
This document tries to clarify the differences.

## Span

The main span encapsulates the entire code member.

The main span is used when determining if a code item is part of a region or implemented interface.
And when selecting the code from the code item context menu.

<pre>
<b>[[namespace CodeNav.Test.Files;

internal class TestSpans
{
private int Counter = 0;
}]]</b>
</pre>

## Identifier span

The idemtifier span starts over the access and modifiers and runs to the end of the line,
including any parameters.

The identifier span is used when clicking on a code item and scrolling to and selecting a
member in the text view.

<pre>
namespace CodeNav.Test.Files;

internal class <b>[[TestSpans]]</b>
{
private int Counter = 0;
}
</pre>

## Outline span

The outline span starts after the identifier has ended and runs to the end of the span.

The outline span is used when expanding and collapsing a code item,
to find the matching outline region in the text view.

<pre>
namespace CodeNav.Test.Files;

internal class TestSpans<b>
[[{
private int Counter = 0;
}]]</b>
</pre>

<pre>
namespace CodeNav.Test.Files;

<b>[[#region Classes
internal class TestSpans
{
private int Counter = 0;
}
#endregion]]</b>
</pre>
3 changes: 0 additions & 3 deletions docs/version10.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,6 @@ developing the extension.

Last but not least everything is written with async programming in mind.

## Features (currently) broken in v10
- Synced collapsing/expanding ranges [VSExtensibility - #545 - Feature request: Text Editor: Collapse/Expand ranges](https://github.com/microsoft/VSExtensibility/issues/545)

## Features dropped in v10
- Top Margin
- Toggle visibility by double-clicking the splitter bar
Expand Down
2 changes: 1 addition & 1 deletion src/CodeNav.OutOfProc/CodeNav.OutOfProc.csproj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0-windows8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ private async Task MoveFilterUp(object? commandParameter, IClientContext clientC
}

int currentIndex = FilterRules.IndexOf(SelectedFilterRule);

if (currentIndex == 0)
{
return;
Expand All @@ -64,10 +65,7 @@ private async Task MoveFilterUp(object? commandParameter, IClientContext clientC
}

[DataMember]
public AsyncCommand MoveFilterDownCommand
{
get;
}
public AsyncCommand MoveFilterDownCommand { get; }
private async Task MoveFilterDown(object? commandParameter, IClientContext clientContext, CancellationToken cancellationToken)
{
if (SelectedFilterRule == null)
Expand All @@ -76,6 +74,7 @@ private async Task MoveFilterDown(object? commandParameter, IClientContext clien
}

int currentIndex = FilterRules.IndexOf(SelectedFilterRule);

if (currentIndex == FilterRules.Count - 1)
{
return;
Expand Down
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>();
}
}
2 changes: 1 addition & 1 deletion src/CodeNav.OutOfProc/Extensions/EnumExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using System.ComponentModel;

namespace CodeNav.Extensions;
namespace CodeNav.OutOfProc.Extensions;

public static class EnumExtensions
{
Expand Down
168 changes: 164 additions & 4 deletions src/CodeNav.OutOfProc/Helpers/OutliningHelper.cs
Original file line number Diff line number Diff line change
@@ -1,18 +1,160 @@
using CodeNav.OutOfProc.Extensions;
using CodeNav.OutOfProc.Interfaces;
using CodeNav.OutOfProc.Models;
using CodeNav.OutOfProc.ViewModels;
using CodeNav.Services;
using Microsoft;
using Microsoft.VisualStudio.Extensibility;
using Microsoft.VisualStudio.Extensibility.Helpers;
using System.Text.Json;

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);
}

/// <summary>
/// Subscribe to region events and retrieve the current state of all regions.
/// </summary>
/// <remarks>
/// Uses the InProc service.
/// </remarks>
/// <returns></returns>
public async Task SubscribeToRegionEvents(CodeDocumentViewModel codeDocumentViewModel)
{
try
{
Assumes.NotNull(_inProcService);

// Subscribe to outline region events and get all outline regions
var outlineRegionsJsonString = await _inProcService.SubscribeToRegionEvents();

// Synchronize all outline regions with the code items
var outlineRegions = JsonSerializer.Deserialize<List<OutlineRegion>>(outlineRegionsJsonString);

if (outlineRegions!.Any() != true)
{
return;
}

outlineRegions!.ForEach(outlineRegion =>
SetIsExpanded(codeDocumentViewModel, outlineRegion.SpanStart, outlineRegion.SpanEnd, outlineRegion.IsExpanded));
}
catch (Exception e)

Check warning on line 52 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 52 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 65 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 65 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 78 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 78 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.OutlineSpan.Start, codeItem.OutlineSpan.Length);
}

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

await codeItem
.CodeDocumentViewModel
.CodeDocumentService
.OutliningHelper
.ExpandOutlineRegion(codeItem.OutlineSpan.Start, codeItem.OutlineSpan.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)
/// <summary>
/// Set IsExpanded property for a matching code item
/// </summary>
/// <remarks>A code item matches when its outline span has the same start and end as the outline region that changed</remarks>
/// <param name="codeDocumentViewModel"></param>
/// <param name="spanStart"></param>
/// <param name="spanEnd"></param>
/// <param name="isExpanded"></param>
public static void SetIsExpanded(CodeDocumentViewModel? codeDocumentViewModel, int spanStart, int spanEnd, bool isExpanded)
{
codeDocumentViewModel?
.CodeItems
.Flatten()
.FilterNull()
.Where(item => item is IMembers)
.Where(codeItem => codeItem.OutlineSpan.Start == spanStart &&
codeItem.OutlineSpan.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 +165,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();
}
}
}
Loading