Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
83 commits
Select commit Hold shift + click to select a range
bf51da3
Update samples for VS 17.9 GA
matteo-prosperi Jan 26, 2024
945c9dd
Update samples for VS 17.10
matteo-prosperi Jan 26, 2024
3bd5960
Update packages
matteo-prosperi Feb 7, 2024
78c6695
Merge branch 'main' into preview_17.10
matteo-prosperi Feb 7, 2024
8b0f186
Update package versions
matteo-prosperi Feb 23, 2024
0e17757
Update packages
matteo-prosperi Feb 28, 2024
798a5dc
Update packages
matteo-prosperi Mar 19, 2024
3ebe89d
Update packages
matteo-prosperi Mar 26, 2024
46ee9e1
Add testing of xaml resources
matteo-prosperi Mar 29, 2024
efe9682
Update packages for 17.11
matteo-prosperi May 9, 2024
16b89f9
Merge remote-tracking branch 'remotes/origin/main' into preview_17.11
matteo-prosperi May 22, 2024
50be2b5
Update packages
matteo-prosperi May 22, 2024
4b8ea58
Update packages to 17.11.232 to remove dependency on ServiceHub.Exten…
BertanAygun Jun 3, 2024
98193af
Add samples for tool window toolbar and context menu
matteo-prosperi Jun 4, 2024
5a55300
Fix
matteo-prosperi Jun 5, 2024
0b648b4
Update usage of Image control
matteo-prosperi Jun 11, 2024
49f6e9c
Updating packages
matteo-prosperi Jun 14, 2024
be0aa2d
Unified getting querySpace and fixed wording
Jxwoon Jun 26, 2024
754e045
Merge pull request #399 from microsoft/dev/jasminewoon/17.11_Updates
Jxwoon Jul 2, 2024
2d1a43b
Update packages
matteo-prosperi Jul 8, 2024
d35d73d
Update packages
matteo-prosperi Jul 9, 2024
ed16717
Avoid warnings
matteo-prosperi Jul 9, 2024
481a824
Fix warnings
matteo-prosperi Jul 9, 2024
7e0956b
Adding a test for settings
matteo-prosperi Jul 9, 2024
d705a3f
Adding a test for the new Image control
matteo-prosperi Jul 9, 2024
0a2e66a
Merge branch 'preview_17.11' into preview_17.12
matteo-prosperi Jul 10, 2024
48ee411
Update packages
matteo-prosperi Jul 10, 2024
e268bc6
Update packages
matteo-prosperi Aug 8, 2024
4232f23
Merge remote-tracking branch 'remotes/origin/main' into preview_17.12
matteo-prosperi Aug 13, 2024
d0f83ee
Update packages
matteo-prosperi Sep 6, 2024
8d87089
Update packages
matteo-prosperi Sep 25, 2024
c66d69f
Merge remote-tracking branch 'remotes/origin/main' into preview_17.12
matteo-prosperi Sep 25, 2024
2b4d75f
Add missing project to solution
matteo-prosperi Sep 25, 2024
6a8cc52
Updating packages
matteo-prosperi Oct 4, 2024
139c1ed
Update packages
matteo-prosperi Oct 11, 2024
16084eb
Fix
matteo-prosperi Oct 11, 2024
84abce4
Update packages
matteo-prosperi Nov 5, 2024
27ad5b2
Update packages
matteo-prosperi Nov 12, 2024
fc5e7ff
Merge from main for 17.12
matteo-prosperi Nov 13, 2024
f9d4aaa
Use new event in visualizers
matteo-prosperi Nov 18, 2024
7472bf7
Merge remote-tracking branch 'origin/main' into preview_17.13
matteo-prosperi Nov 18, 2024
0c8590d
Taggers and CodeLens manual tests
matteo-prosperi Nov 22, 2024
ec61954
Update packages
matteo-prosperi Nov 22, 2024
f31ecc0
Update packages
matteo-prosperi Dec 11, 2024
967b4f1
Update the settings sample to use generated observers. (#447)
matteo-prosperi Dec 13, 2024
24ae733
Fix readme
matteo-prosperi Dec 16, 2024
a259e54
Update dependencies
matteo-prosperi Jan 14, 2025
236f3a7
Add a sample for a composite in-proc/out-of-proc extension (#451)
matteo-prosperi Jan 17, 2025
540a47a
Add a sample for taggers (#448)
matteo-prosperi Jan 17, 2025
2c59e21
Fix warning
matteo-prosperi Jan 21, 2025
b6ce2ef
Update packages
matteo-prosperi Jan 23, 2025
707e913
Update packages
matteo-prosperi Feb 5, 2025
b35d890
Merge remote-tracking branch 'remotes/origin/main' into preview_17.14
matteo-prosperi Feb 12, 2025
85f3944
Update packages
matteo-prosperi Feb 12, 2025
cd3e1d4
Add Project Queryable Space Event Sample (#459)
iserrato Feb 14, 2025
b9644a1
Merge remote-tracking branch 'origin/main' into preview_17.14
matteo-prosperi Feb 24, 2025
8d036b2
Update packages
matteo-prosperi Feb 24, 2025
09846fe
Merge from main
matteo-prosperi Feb 26, 2025
91c0b08
Fix bad merge
matteo-prosperi Mar 10, 2025
cb2e87c
Merge remote-tracking branch 'origin/main' into preview_17.14
matteo-prosperi Mar 10, 2025
c1084d7
Update packages
matteo-prosperi Mar 10, 2025
1d76987
Update packages
matteo-prosperi Mar 17, 2025
736cb45
Update packages
matteo-prosperi Mar 21, 2025
25f0bf1
Update packages
matteo-prosperi Mar 24, 2025
e9b4a32
Update packages
matteo-prosperi Apr 7, 2025
00c1358
Add a sample for classification taggers
matteo-prosperi Apr 22, 2025
db5dceb
Update sample
matteo-prosperi Apr 22, 2025
a70828d
Add link
matteo-prosperi Apr 22, 2025
b94e6a3
Update packages
matteo-prosperi Apr 24, 2025
5c85216
Enhance User Prompt Sample and add new command
trippwill Apr 25, 2025
f64d353
Refactor Choices in AdvancedConfigurationCommand
trippwill Apr 25, 2025
590e3db
Update breaking changes with 17.14 content
May 7, 2025
a2e12a2
Addressed feedback
May 7, 2025
08393da
Update packages
matteo-prosperi May 7, 2025
5ee6936
Add additional impactful change
May 12, 2025
690a527
Add proper line breaks
May 12, 2025
e7374ac
Merge pull request #488 from microsoft/dev/tinali/1714_changes
tinaschrepfer May 12, 2025
ab10973
Merge pull request #484 from microsoft/dev/maprospe/classification
tinaschrepfer May 12, 2025
2ed4e65
Merge pull request #487 from microsoft/dev/chawill/userprompts-advanc…
tinaschrepfer May 12, 2025
a8948c9
Add a sample for an out-of-proc extension that uses VSSDK components …
matteo-prosperi May 12, 2025
ef3d969
Update packages
matteo-prosperi May 12, 2025
3797b4c
Merge from main
matteo-prosperi May 12, 2025
fb44c96
Fix
matteo-prosperi May 14, 2025
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<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.14.40254" PrivateAssets="all" />
<PackageReference Include="Microsoft.VisualStudio.Extensibility.Build" Version="17.14.40254" PrivateAssets="all" />
</ItemGroup>
</Project>
147 changes: 147 additions & 0 deletions New_Extensibility_Model/Samples/ClassificationSample/CsvTagger.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace ClassificationSample;

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.VisualStudio.Extensibility.Editor;

#pragma warning disable VSEXTPREVIEW_TAGGERS // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.

internal class CsvTagger : TextViewTagger<ClassificationTag>
{
private const string QuoteMatchName = "quote";
private const string EscapedQuoteMatchName = "escapedQuote";
private const string SeparatorMatchName = "separator";
private const string FieldTextMatchName = "fieldText";

// Matches any sequence of characters, not containing '"' and ','. It also matches any
// sequence of characters enclosed in '"' as long as they don't contain other '"'
// characters, unless they are escaped (doubled).
// Examples of valid matches: xxx, "xxx", "xx""x"
private const string FieldRegex = $@"(((?<{QuoteMatchName}>"")((?<{FieldTextMatchName}>[^""]+)|(?<{EscapedQuoteMatchName}>""""))*(?<{QuoteMatchName}>""))|(?<{FieldTextMatchName}>([^,""])*))";

// Matches multiple fields separated by ','
// This regex supports quoted fields, escaped quotes (inside fields). We are not supporting
// line-breaks inside a field.
// This regex is not terminated with '$' so that as much as possible of the beginning of
// the line is always matched, even in case of syntax error.
private static readonly Regex LineRegex = new($@"^{FieldRegex}((?<{SeparatorMatchName}>,){FieldRegex})*", RegexOptions.Compiled | RegexOptions.ExplicitCapture);

private readonly CsvTaggerProvider provider;
private readonly Uri documentUri;

public CsvTagger(CsvTaggerProvider provider, Uri documentUri)
{
this.provider = provider;
this.documentUri = documentUri;
}

public override void Dispose()
{
this.provider.RemoveTagger(this.documentUri, this);
base.Dispose();
}

public async Task TextViewChangedAsync(ITextViewSnapshot textView, IReadOnlyList<TextEdit> edits, CancellationToken cancellationToken)
{
if (edits.Count == 0)
{
return;
}

var allRequestedRanges = await this.GetAllRequestedRangesAsync(textView.Document, cancellationToken);
await this.CreateTagsAsync(
textView.Document,
allRequestedRanges.Intersect(// Use Intersect to only create tags for ranges that VS has previously expressed interested in.
edits.Select(e =>
EnsureNotEmpty(// Fix empty ranges to be at least 1 character long so that they are not ignored when intersected (empty ranges are the result of text deletion).
e.Range.TranslateTo(textView.Document, TextRangeTrackingMode.ExtendForwardAndBackward))))); // Translate the range to the new document version.
}

protected override async Task RequestTagsAsync(NormalizedTextRangeCollection requestedRanges, bool recalculateAll, CancellationToken cancellationToken)
{
if (requestedRanges.Count == 0)
{
return;
}

await this.CreateTagsAsync(requestedRanges.TextDocumentSnapshot!, requestedRanges);
}

private static TextRange EnsureNotEmpty(TextRange range)
{
if (range.Length > 0)
{
return range;
}

int start = Math.Max(0, range.Start - 1);
int end = Math.Min(range.Document.Length, range.Start + 1);

return new(range.Document, start, end - start);
}

private async Task CreateTagsAsync(ITextDocumentSnapshot document, IEnumerable<TextRange> requestedRanges)
{
List<TaggedTrackingTextRange<ClassificationTag>> tags = new();
List<TextRange> ranges = new();
foreach (var lineNumber in requestedRanges.SelectMany(r =>
{
// Convert the requested range to line numbers.
var startLine = r.Document.GetLineNumberFromPosition(r.Start);
var endLine = r.Document.GetLineNumberFromPosition(r.End);
return Enumerable.Range(startLine, endLine - startLine + 1);

// Use Distinct to avoid processing the same line multiple times.
}).Distinct())
{
var line = document.Lines[lineNumber];
var match = LineRegex.Match(line.Text.CopyToString());

if (match.Success)
{
// VisualStudio.Extensibility doesn't support defining text colors for
// new classification types yet, so we must use existing classification
// types.
foreach (Capture capture in match.Groups[FieldTextMatchName].Captures)
{
AddTag(capture, lineNumber == 0 ? ClassificationType.KnownValues.SymbolDefinition : ClassificationType.KnownValues.String);
}

foreach (Capture capture in match.Groups[QuoteMatchName].Captures)
{
AddTag(capture, ClassificationType.KnownValues.BracePairLevelOne);
}

foreach (Capture capture in match.Groups[EscapedQuoteMatchName].Captures)
{
AddTag(capture, ClassificationType.KnownValues.BracePairLevelTwo);
}

foreach (Capture capture in match.Groups[SeparatorMatchName].Captures)
{
AddTag(capture, ClassificationType.KnownValues.Operator);
}
}

void AddTag(Capture capture, ClassificationType classificationType)
{
tags.Add(new(new(document, line.Text.Start + capture.Index, capture.Length, TextRangeTrackingMode.ExtendNone), new(classificationType)));
}

// Add the range to the list of ranges we have calculated tags for. We add the range even if no tags
// were created for it, this takes care of clearing any tags that were previously created for this
// range and are not valid anymore.
ranges.Add(new(document, line.TextIncludingLineBreak.Start, line.TextIncludingLineBreak.Length));
}

// Return the ranges we have calculated tags for and the tags themselves.
await this.UpdateTagsAsync(ranges, tags, CancellationToken.None);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace ClassificationSample;

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

#pragma warning disable VSEXTPREVIEW_TAGGERS // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.

[VisualStudioContribution]
internal class CsvTaggerProvider : ExtensionPart, ITextViewTaggerProvider<ClassificationTag>, ITextViewChangedListener
{
private readonly object lockObject = new();
private readonly Dictionary<Uri, List<CsvTagger>> taggers = new();

[VisualStudioContribution]
public static DocumentTypeConfiguration CsvDocumentType => new("csv")
{
FileExtensions = new[] { ".csv" },
BaseDocumentType = DocumentType.KnownValues.PlainText,
};

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

public async Task TextViewChangedAsync(TextViewChangedArgs args, CancellationToken cancellationToken)
{
List<Task> tasks = new();
lock (this.lockObject)
{
if (this.taggers.TryGetValue(args.AfterTextView.Uri, out var taggers))
{
foreach (var tagger in taggers)
{
tasks.Add(tagger.TextViewChangedAsync(args.AfterTextView, args.Edits, cancellationToken));
}
}
}

await Task.WhenAll(tasks);
}

Task<TextViewTagger<ClassificationTag>> ITextViewTaggerProvider<ClassificationTag>.CreateTaggerAsync(ITextViewSnapshot textView, CancellationToken cancellationToken)
{
var tagger = new CsvTagger(this, textView.Document.Uri);
lock (this.lockObject)
{
if (!this.taggers.TryGetValue(textView.Document.Uri, out var taggers))
{
taggers = new();
this.taggers[textView.Document.Uri] = taggers;
}

taggers.Add(tagger);
}

return Task.FromResult<TextViewTagger<ClassificationTag>>(tagger);
}

internal void RemoveTagger(Uri documentUri, CsvTagger toBeRemoved)
{
lock (this.lockObject)
{
if (this.taggers.TryGetValue(documentUri, out var taggers))
{
taggers.Remove(toBeRemoved);
if (taggers.Count == 0)
{
this.taggers.Remove(documentUri);
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace ClassificationSample;

using Microsoft.Extensions.DependencyInjection;
using Microsoft.VisualStudio.Extensibility;

/// <summary>
/// Extension entry point for the ClassificationSample extension.
/// </summary>
[VisualStudioContribution]
public class ExtensionEntrypoint : Extension
{
/// <inheritdoc/>
public override ExtensionConfiguration ExtensionConfiguration => new()
{
Metadata = new(
id: "ClassificationSample.2e2edd6e-ccf8-4303-a159-068724c63ab0",
version: this.ExtensionAssemblyVersion,
publisherName: "Microsoft",
displayName: "Classification Sample Extension",
description: "Sample extension demonstrating contributing a classification tagger"),
};

/// <inheritdoc/>
protected override void InitializeServices(IServiceCollection serviceCollection)
{
base.InitializeServices(serviceCollection);
}
}
55 changes: 55 additions & 0 deletions New_Extensibility_Model/Samples/ClassificationSample/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
---
title: Classification Extension Sample reference
description: A reference for Classification sample
date: 2025-04-22
---

# Classification Extension Sample

This extension creates a tagger that classifies the text of CSV files.

A detailed walkthrough of how to create a tagger is available in the
[Taggers sample readme file](../TaggersSample/README.md). Please read
that first.

This sample's `CsvTaggerProvider` and `CsvTagger` are equivalent to `MarkdownTextMarkerTaggerProvider` and `MarkdownTextMarkerTagger`.

## Classification

Classification can be performed by an extension by implementing an
`ITextViewTaggerProvider<ClassificationTag>` and have the `TextViewTagger<>`
generate `ClassificationTag` values.

```cs
tags.Add(
new TaggedTrackingTextRange<ClassificationTag>(
new TrackingTextRange(
document,
tagStartPosition,
tagLength,
TextRangeTrackingMode.ExtendNone),
new ClassificationTag(ClassificationType.KnownValues.Operator)));

```

At this time, VisualStudio.Extensibility doesn't support defining text colors for
new classification types yet, so we must use existing classification types.

VSSDK-compatible extensions, can use [ClassificationTypeDefinition](https://learn.microsoft.com/dotnet/api/microsoft.visualstudio.text.classification.classificationtypedefinition)
to define new classification types. Their name can be referenced using
`ClassificationType.Custom`.

## Performance considerations

Since `CsvTagger` doesn't support CSV fields containing line breaks, the
tagger can perform parsing of any single line of the CSV file independently
from each other. This allows the tagger to only act on the modified lines.

We further optimize the tag generation by intersecting the edited text ranges
with the ranges that have been previously requested (`GetAllRequestedRangesAsync`).
This is particularly useful if the user pastes a large amount of text into
the file resulting in lines being edited that don't fall into the currently
visible portion of the view.

If these optimization were not possible, we would have to avoid generating tags
on each edit of the document (see the [Implementing "slow" taggers](../TaggersSample/README.md#implementing-slow-taggers) chapter of the Taggers sample readme file).
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.VisualStudio.Extensibility.Sdk" Version="17.13.40008" PrivateAssets="all" />
<PackageReference Include="Microsoft.VisualStudio.Extensibility.Build" Version="17.13.40008" PrivateAssets="all" />
<PackageReference Include="Microsoft.VisualStudio.Extensibility.Sdk" Version="17.14.40254" PrivateAssets="all" />
<PackageReference Include="Microsoft.VisualStudio.Extensibility.Build" Version="17.14.40254" PrivateAssets="all" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,10 @@
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<LangVersion>12</LangVersion>

<!-- The VisualStudio.Extensibility preview packages are available from the azure-public/vside/msft_consumption feed -->
<RestoreAdditionalProjectSources>https://pkgs.dev.azure.com/azure-public/vside/_packaging/msft_consumption/nuget/v3/index.json;$(RestoreAdditionalProjectSources)</RestoreAdditionalProjectSources>
</PropertyGroup>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.VisualStudio.Extensibility.Sdk" Version="17.13.40008" PrivateAssets="all" />
<PackageReference Include="Microsoft.VisualStudio.Extensibility.Build" Version="17.13.40008" PrivateAssets="all" />
<PackageReference Include="Microsoft.VisualStudio.Extensibility.Sdk" Version="17.14.40254" PrivateAssets="all" />
<PackageReference Include="Microsoft.VisualStudio.Extensibility.Build" Version="17.14.40254" PrivateAssets="all" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.VisualStudio.Extensibility.Sdk" Version="17.13.40008" PrivateAssets="all" />
<PackageReference Include="Microsoft.VisualStudio.Extensibility.Build" Version="17.13.40008" PrivateAssets="all" />
<PackageReference Include="Microsoft.VisualStudio.Extensibility.Sdk" Version="17.14.40254" PrivateAssets="all" />
<PackageReference Include="Microsoft.VisualStudio.Extensibility.Build" Version="17.14.40254" PrivateAssets="all" />
<PackageReference Include="Microsoft.VisualStudio.SDK" Version="17.6.36389" ExcludeAssets="runtime" />
</ItemGroup>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.VisualStudio.Extensibility.Sdk" Version="17.13.40008" PrivateAssets="all" />
<PackageReference Include="Microsoft.VisualStudio.Extensibility.Build" Version="17.13.40008" PrivateAssets="all" />
<PackageReference Include="Microsoft.VisualStudio.Extensibility.Sdk" Version="17.14.40254" PrivateAssets="all" />
<PackageReference Include="Microsoft.VisualStudio.Extensibility.Build" Version="17.14.40254" PrivateAssets="all" />
<PackageReference Include="Microsoft.VisualStudio.SDK" Version="17.6.36389" ExcludeAssets="runtime" />
</ItemGroup>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.VisualStudio.Extensibility.Sdk" Version="17.13.40008" PrivateAssets="all" />
<PackageReference Include="Microsoft.VisualStudio.Extensibility.Build" Version="17.13.40008" PrivateAssets="all" />
<PackageReference Include="Microsoft.VisualStudio.Extensibility.Sdk" Version="17.14.40254" PrivateAssets="all" />
<PackageReference Include="Microsoft.VisualStudio.Extensibility.Build" Version="17.14.40254" PrivateAssets="all" />
</ItemGroup>

<ItemGroup>
Expand Down
Loading