diff --git a/New_Extensibility_Model/Samples/CodeLensSample/ClickCountCodeLensProvider.cs b/New_Extensibility_Model/Samples/CodeLensSample/ClickCountCodeLensProvider.cs new file mode 100644 index 00000000..9ff79ba0 --- /dev/null +++ b/New_Extensibility_Model/Samples/CodeLensSample/ClickCountCodeLensProvider.cs @@ -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; + +/// +/// A sample CodeLens provider that provides invokable CodeLens and shows the number of times it has been clicked. +/// +#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 TryCreateCodeLensAsync(CodeElement codeElement, CodeElementContext codeElementContext, CancellationToken token) + { + if (codeElement.Kind == CodeElementKind.KnownValues.Method) + { + return Task.FromResult(new ClickableCodeLens(codeElement, this.Extensibility)); + } + + return Task.FromResult(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. diff --git a/New_Extensibility_Model/Samples/CodeLensSample/ClickableCodeLens.cs b/New_Extensibility_Model/Samples/CodeLensSample/ClickableCodeLens.cs new file mode 100644 index 00000000..5dc94161 --- /dev/null +++ b/New_Extensibility_Model/Samples/CodeLensSample/ClickableCodeLens.cs @@ -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 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. diff --git a/New_Extensibility_Model/Samples/CodeLensSample/CodeLensSample.csproj b/New_Extensibility_Model/Samples/CodeLensSample/CodeLensSample.csproj new file mode 100644 index 00000000..305286a6 --- /dev/null +++ b/New_Extensibility_Model/Samples/CodeLensSample/CodeLensSample.csproj @@ -0,0 +1,17 @@ + + + net8.0-windows8.0 + enable + 12 + en-US + + + + + + + + + + + diff --git a/New_Extensibility_Model/Samples/CodeLensSample/ExtensionEntrypoint.cs b/New_Extensibility_Model/Samples/CodeLensSample/ExtensionEntrypoint.cs new file mode 100644 index 00000000..d541a21d --- /dev/null +++ b/New_Extensibility_Model/Samples/CodeLensSample/ExtensionEntrypoint.cs @@ -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; + +/// +/// A sample demonstrating how to extend CodeLens. +/// +[VisualStudioContribution] +internal class ExtensionEntrypoint : Extension +{ + /// + public override ExtensionConfiguration ExtensionConfiguration => new() + { + Metadata = new( + "C43CCA96-C391-42B0-95A5-5B1090909605.CodeLensSample", + this.ExtensionAssemblyVersion, + "Microsoft", + "Codelens Sample", + "Sample extension demonstrating the CodeLens capabilities."), + }; +} diff --git a/New_Extensibility_Model/Samples/CodeLensSample/README.md b/New_Extensibility_Model/Samples/CodeLensSample/README.md new file mode 100644 index 00000000..b35467d4 --- /dev/null +++ b/New_Extensibility_Model/Samples/CodeLensSample/README.md @@ -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 +/// +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 TryCreateCodeLensAsync(CodeElement codeElement, CodeElementContext codeElementContext, CancellationToken token) +{ + if (codeElement.Kind == CodeElementKind.KnownValues.Method) + { + return Task.FromResult(new ClickableCodeLens(codeElement, this.Extensibility)); + } + + return Task.FromResult(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 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 +/// +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 TryCreateCodeLensAsync(CodeElement codeElement, CodeElementContext codeElementContext, CancellationToken token) +{ + if (codeElement.Kind == CodeElementKind.KnownValues.Method || codeElement.Kind.IsOfKind(CodeElementKind.KnownValues.Type)) + { + return Task.FromResult(new WordCountCodeLens(codeElement, codeElementContext, this.Extensibility, this)); + } + + return Task.FromResult(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 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 GetVisualizationAsync(CodeElementContext codeElementContext, IClientContext clientContext, CancellationToken token) +{ + return Task.FromResult(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) \ No newline at end of file diff --git a/New_Extensibility_Model/Samples/CodeLensSample/WordCountCodeLens.cs b/New_Extensibility_Model/Samples/CodeLensSample/WordCountCodeLens.cs new file mode 100644 index 00000000..bfcd99b5 --- /dev/null +++ b/New_Extensibility_Model/Samples/CodeLensSample/WordCountCodeLens.cs @@ -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 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 GetVisualizationAsync(CodeElementContext codeElementContext, IClientContext clientContext, CancellationToken token) + { + return Task.FromResult(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. diff --git a/New_Extensibility_Model/Samples/CodeLensSample/WordCountCodeLensProvider.cs b/New_Extensibility_Model/Samples/CodeLensSample/WordCountCodeLensProvider.cs new file mode 100644 index 00000000..ed63c0ed --- /dev/null +++ b/New_Extensibility_Model/Samples/CodeLensSample/WordCountCodeLensProvider.cs @@ -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; + +/// +/// A sample CodeLens provider that shows the number of words in a method or type and provides custom UI when clicked. +/// +#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? 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 TryCreateCodeLensAsync(CodeElement codeElement, CodeElementContext codeElementContext, CancellationToken token) + { + if (codeElement.Kind == CodeElementKind.KnownValues.Method) + { + return Task.FromResult(new WordCountCodeLens(codeElement, codeElementContext, this.Extensibility, this)); + } + + return Task.FromResult(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. diff --git a/New_Extensibility_Model/Samples/CodeLensSample/WordCountCodeLensVisual.cs b/New_Extensibility_Model/Samples/CodeLensSample/WordCountCodeLensVisual.cs new file mode 100644 index 00000000..dced4dcd --- /dev/null +++ b/New_Extensibility_Model/Samples/CodeLensSample/WordCountCodeLensVisual.cs @@ -0,0 +1,23 @@ +// 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.UI; + +internal class WordCountCodeLensVisual : RemoteUserControl +{ + private readonly WordCountCodeLens wordCountCodeLens; + + public WordCountCodeLensVisual(object? dataContext, WordCountCodeLens wordCountCodeLens) + : base(dataContext) + { + this.wordCountCodeLens = wordCountCodeLens; + } + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + this.wordCountCodeLens.UpdateWordCount(); + } +} diff --git a/New_Extensibility_Model/Samples/CodeLensSample/WordCountCodeLensVisual.xaml b/New_Extensibility_Model/Samples/CodeLensSample/WordCountCodeLensVisual.xaml new file mode 100644 index 00000000..11dc6627 --- /dev/null +++ b/New_Extensibility_Model/Samples/CodeLensSample/WordCountCodeLensVisual.xaml @@ -0,0 +1,34 @@ + + + + + + + + Number of occurrences of word "": + + + + Enter new word: + +