Skip to content

Commit

Permalink
Update samples for VS 17.12 (#401)
Browse files Browse the repository at this point in the history
* Update samples for VS 17.12

---------

Co-authored-by: Matteo Prosperi <[email protected]>
Co-authored-by: Bertan Aygun <[email protected]>
Co-authored-by: Jasmine Woon <[email protected]>
Co-authored-by: Jasmine Woon <[email protected]>
Co-authored-by: Oleg Tkachenko <[email protected]>
Co-authored-by: Tina Schrepfer (LI) <[email protected]>
Co-authored-by: Tina Schrepfer <[email protected]>
  • Loading branch information
8 people authored Nov 13, 2024
1 parent 10aa102 commit 1759322
Show file tree
Hide file tree
Showing 36 changed files with 574 additions and 77 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace CodeLensSample;

using System.Threading;
using System.Threading.Tasks;
using Microsoft.VisualStudio.Extensibility;
using Microsoft.VisualStudio.Extensibility.Editor;

/// <summary>
/// A sample CodeLens provider that provides invokable CodeLens and shows the number of times it has been clicked.
/// </summary>
#pragma warning disable VSEXTPREVIEW_CODELENS // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
[VisualStudioContribution]
internal class ClickCountCodeLensProvider : ExtensionPart, ICodeLensProvider
{
public TextViewExtensionConfiguration TextViewExtensionConfiguration => new()
{
AppliesTo = new[]
{
DocumentFilter.FromDocumentType(DocumentType.KnownValues.Code),
},
};

#pragma warning disable CEE0027 // String not localized
public CodeLensProviderConfiguration CodeLensProviderConfiguration =>
new("Invokable CodeLens Sample Provider")
{
Priority = 500,
};
#pragma warning restore CEE0027 // String not localized

public Task<CodeLens?> TryCreateCodeLensAsync(CodeElement codeElement, CodeElementContext codeElementContext, CancellationToken token)
{
if (codeElement.Kind == CodeElementKind.KnownValues.Method)
{
return Task.FromResult<CodeLens?>(new ClickableCodeLens(codeElement, this.Extensibility));
}

return Task.FromResult<CodeLens?>(null);
}
}
#pragma warning restore VSEXTPREVIEW_CODELENS // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace CodeLensSample;

using System.Threading;
using System.Threading.Tasks;
using Microsoft.VisualStudio.Extensibility;
using Microsoft.VisualStudio.Extensibility.Editor;

#pragma warning disable VSEXTPREVIEW_CODELENS // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
internal class ClickableCodeLens : InvokableCodeLens
{
private readonly CodeElement codeElement;
private readonly VisualStudioExtensibility extensibility;
private int clickCount;

public ClickableCodeLens(CodeElement codeElement, VisualStudioExtensibility extensibility)
{
this.codeElement = codeElement;
this.extensibility = extensibility;
}

public override void Dispose()
{
}

public override Task ExecuteAsync(CodeElementContext codeElementContext, IClientContext clientContext, CancellationToken cancelToken)
{
this.clickCount++;
this.Invalidate();
return Task.CompletedTask;
}

public override Task<CodeLensLabel> GetLabelAsync(CodeElementContext codeElementContext, CancellationToken token)
{
return Task.FromResult(new CodeLensLabel()
{
Text = this.clickCount == 0 ? "Click me" : $"Clicked {this.clickCount} times",
Tooltip = "Invokable CodeLens Sample Tooltip",
});
}
}
#pragma warning restore VSEXTPREVIEW_CODELENS // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0-windows8.0</TargetFramework>
<Nullable>enable</Nullable>
<LangVersion>12</LangVersion>
<NeutralLanguage>en-US</NeutralLanguage>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.VisualStudio.Extensibility.Sdk" Version="17.12.40390" />
<PackageReference Include="Microsoft.VisualStudio.Extensibility.Build" Version="17.12.40390" />
</ItemGroup>

<ItemGroup>
<EmbeddedResource Include="WordCountCodeLensVisual.xaml" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace CodeLensSample;

using Microsoft.VisualStudio.Extensibility;

/// <summary>
/// A sample demonstrating how to extend CodeLens.
/// </summary>
[VisualStudioContribution]
internal class ExtensionEntrypoint : Extension
{
/// <inheritdoc/>
public override ExtensionConfiguration ExtensionConfiguration => new()
{
Metadata = new(
"C43CCA96-C391-42B0-95A5-5B1090909605.CodeLensSample",
this.ExtensionAssemblyVersion,
"Microsoft",
"Codelens Sample",
"Sample extension demonstrating the CodeLens capabilities."),
};
}
137 changes: 137 additions & 0 deletions New_Extensibility_Model/Samples/CodeLensSample/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
---
title: Code Lens Sample Reference
description: A reference for Code Lens Sample
date: 2023-10-14
---

# Walkthrough: Code Lens Sample

This extension is a sample extension which contributes two Code Lens providers - one that provides clickable Code Lens displaying number of times user clicked on it and another that provides Code Lens displaying the number of words in a method and custom UI displayed when user clicks on the Code Lens. It is a good starting point for learning how to create a Code Lens provider.

## ClickCountCodeLensProvider and ClickableCodeLens

`ClickCountCodeLensProvider` is a part of the extension that provides an invokable Code Lens that allows execution by user clicking on it and indicating number of times it was executed.

`ClickCountCodeLensProvider` utilizes the `AppliesTo` configuration to indicate that it is interested in events from text views whose documents are of `DocumentType.KnownValues.Code`, which are all text based documents containing code.

```csharp
/// <inheritdoc />
public TextViewExtensionConfiguration TextViewExtensionConfiguration => new()
{
AppliesTo = new[]
{
DocumentFilter.FromDocumentType(DocumentType.KnownValues.Code),
},
};
```

`ClickCountCodeLensProvider` utilizes the `CodeLensProviderConfiguration` to declare its display name and that the Code Lens it provides should be placed after other existing Code Lenses with a priority of 500.

```csharp
public CodeLensProviderConfiguration CodeLensProviderConfiguration =>
new("Invokable CodeLens Sample Provider")
{
Priority = 500,
};
```

When requested, `ClickCountCodeLensProvider` provides a `ClickableCodeLens` instance:

```csharp
public Task<CodeLens?> TryCreateCodeLensAsync(CodeElement codeElement, CodeElementContext codeElementContext, CancellationToken token)
{
if (codeElement.Kind == CodeElementKind.KnownValues.Method)
{
return Task.FromResult<CodeLens?>(new ClickableCodeLens(codeElement, this.Extensibility));
}

return Task.FromResult<CodeLens?>(null);
}
```

`ClickableCodeLens` provides `CodeLensLabel` that is being displayed above methods and implements `InvokableCodeLens` abstract class to increment click count on each invocation.
```csharp
public override Task ExecuteAsync(CodeElementContext codeElementContext, IClientContext clientContext, CancellationToken cancelToken)
{
this.clickCount++;
this.Invalidate();
return Task.CompletedTask;
}

public override Task<CodeLensLabel> GetLabelAsync(CodeElementContext codeElementContext, CancellationToken token)
{
return Task.FromResult(new CodeLensLabel()
{
Text = this.clickCount == 0 ? "Click me" : $"Clicked {this.clickCount} times",
Tooltip = "Invokable CodeLens Sample Tooltip",
});
}
```

## WordCountCodeLensProvider and WordCountCodeLens

`WordCountCodeLensProvider` is a part of the extension that provides a visual Code Lens indicating number of words in a method body and that listens for new editor text view creation and changes to open text views to update the word count.

`WordCountCodeLensProvider` utilizes the `AppliesTo` configuration to indicate that it is interested in events from text views whose documents are of `DocumentType.KnownValues.Code`, which are all text based documents containing code.

```csharp
/// <inheritdoc />
public TextViewExtensionConfiguration TextViewExtensionConfiguration => new()
{
AppliesTo = new[]
{
DocumentFilter.FromDocumentType(DocumentType.KnownValues.Code),
},
};
```

`WordCountCodeLensProvider` utilizes the `CodeLensProviderConfiguration` to declare its display name and that the Code Lens it provides should be placed after other existing Code Lenses with a priority of 500.

```csharp
public CodeLensProviderConfiguration CodeLensProviderConfiguration =>
new("Invokable CodeLens Sample Provider")
{
Priority = 500,
};
```

When requested, `WordCountCodeLensProvider` provides `WordCountCodeLens` instance:

```csharp
public Task<CodeLens?> TryCreateCodeLensAsync(CodeElement codeElement, CodeElementContext codeElementContext, CancellationToken token)
{
if (codeElement.Kind == CodeElementKind.KnownValues.Method || codeElement.Kind.IsOfKind(CodeElementKind.KnownValues.Type))
{
return Task.FromResult<CodeLens?>(new WordCountCodeLens(codeElement, codeElementContext, this.Extensibility, this));
}

return Task.FromResult<CodeLens?>(null);
}
```

`WordCountCodeLens` provides `CodeLensLabel` that is being displayed above methods and implements `VisualCodeLens` to provide custom UI displayed when user clicks on the Code Lens.

```csharp
public override Task<CodeLensLabel> GetLabelAsync(CodeElementContext codeElementContext, CancellationToken token)
{
this.wordCountData.WordCount = CountWords(codeElementContext.Range);
return Task.FromResult(new CodeLensLabel()
{
Text = $"Words: {this.wordCountData.WordCount}",
Tooltip = "Number of words in this code element",
});
}

public override Task<IRemoteUserControl> GetVisualizationAsync(CodeElementContext codeElementContext, IClientContext clientContext, CancellationToken token)
{
return Task.FromResult<IRemoteUserControl>(new WordCountCodeLensVisual(this.wordCountData));
}
```

## Usage

Once deployed, the Code Lens Sample will add two Code Lenses displayed above each method, one that can be clicked and displays the number of times it was clicked and another that displays the number of words in the method and provides custom UI displayed when user clicks on the Code Lens.

## See also

- [Use Editor extensibility](https://learn.microsoft.com/visualstudio/extensibility/visualstudio.extensibility/editor/editor)
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace CodeLensSample;

using System;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.VisualStudio.Extensibility;
using Microsoft.VisualStudio.Extensibility.Editor;
using Microsoft.VisualStudio.RpcContracts.RemoteUI;

#pragma warning disable VSEXTPREVIEW_CODELENS // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
internal class WordCountCodeLens : VisualCodeLens
{
private readonly WordCountData wordCountData;

public WordCountCodeLens(CodeElement codeElement, CodeElementContext codeElementContext, VisualStudioExtensibility extensibility, WordCountCodeLensProvider wordCountCodeLensProvider)
{
this.wordCountData = new WordCountData(this);
this.wordCountData.WordCount = this.CountWords(codeElementContext.Range);
wordCountCodeLensProvider.TextViewChanged += this.OnTextViewChanged;
}

public override void Dispose()
{
}

public override Task<CodeLensLabel> GetLabelAsync(CodeElementContext codeElementContext, CancellationToken token)
{
this.wordCountData.WordCount = this.CountWords(codeElementContext.Range);
return Task.FromResult(new CodeLensLabel()
{
Text = $"Occurrences of \"{this.wordCountData.WordToMatch}\": {this.wordCountData.WordCount}",
Tooltip = "Number of occurrences of a word in the text",
});
}

public override Task<IRemoteUserControl> GetVisualizationAsync(CodeElementContext codeElementContext, IClientContext clientContext, CancellationToken token)
{
return Task.FromResult<IRemoteUserControl>(new WordCountCodeLensVisual(this.wordCountData, this));
}

internal void UpdateWordCount()
{
this.Invalidate();
}

private int CountWords(TextRange range)
{
var rangeText = range.CopyToString();
var wordToMatch = this.wordCountData.WordToMatch;

// Use Regex to find all matches of wordToMatch in rangeText
var matches = Regex.Matches(rangeText, $@"\b{Regex.Escape(wordToMatch)}\b", RegexOptions.IgnoreCase);
return matches.Count;
}

private void OnTextViewChanged(object? sender, TextViewChangedArgs e)
{
this.Invalidate();
}
}
#pragma warning restore VSEXTPREVIEW_CODELENS // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace CodeLensSample;

using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.VisualStudio.Extensibility;
using Microsoft.VisualStudio.Extensibility.Editor;

/// <summary>
/// A sample CodeLens provider that shows the number of words in a method or type and provides custom UI when clicked.
/// </summary>
#pragma warning disable VSEXTPREVIEW_CODELENS // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
[VisualStudioContribution]
internal class WordCountCodeLensProvider : ExtensionPart, ICodeLensProvider, ITextViewChangedListener
{
public event EventHandler<TextViewChangedArgs>? TextViewChanged;

public TextViewExtensionConfiguration TextViewExtensionConfiguration => new()
{
AppliesTo = new[]
{
DocumentFilter.FromDocumentType(DocumentType.KnownValues.Code),
},
};

#pragma warning disable CEE0027 // String not localized
public CodeLensProviderConfiguration CodeLensProviderConfiguration =>
new("Remote UI CodeLens Sample Provider")
{
Priority = 600,
};
#pragma warning restore CEE0027 // String not localized

public Task<CodeLens?> TryCreateCodeLensAsync(CodeElement codeElement, CodeElementContext codeElementContext, CancellationToken token)
{
if (codeElement.Kind == CodeElementKind.KnownValues.Method)
{
return Task.FromResult<CodeLens?>(new WordCountCodeLens(codeElement, codeElementContext, this.Extensibility, this));
}

return Task.FromResult<CodeLens?>(null);
}

public Task TextViewChangedAsync(TextViewChangedArgs args, CancellationToken cancellationToken)
{
this.TextViewChanged?.Invoke(this, args);
return Task.CompletedTask;
}
}
#pragma warning restore VSEXTPREVIEW_CODELENS // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
Loading

0 comments on commit 1759322

Please sign in to comment.