diff --git a/New_Extensibility_Model/Samples/ClassificationSample/ClassificationSample.csproj b/New_Extensibility_Model/Samples/ClassificationSample/ClassificationSample.csproj
new file mode 100644
index 00000000..388495cb
--- /dev/null
+++ b/New_Extensibility_Model/Samples/ClassificationSample/ClassificationSample.csproj
@@ -0,0 +1,13 @@
+
+
+ net8.0-windows8.0
+ enable
+ 12
+ en-US
+
+
+
+
+
+
+
diff --git a/New_Extensibility_Model/Samples/ClassificationSample/CsvTagger.cs b/New_Extensibility_Model/Samples/ClassificationSample/CsvTagger.cs
new file mode 100644
index 00000000..bc80fbb1
--- /dev/null
+++ b/New_Extensibility_Model/Samples/ClassificationSample/CsvTagger.cs
@@ -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
+{
+ 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 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 requestedRanges)
+ {
+ List> tags = new();
+ List 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);
+ }
+}
diff --git a/New_Extensibility_Model/Samples/ClassificationSample/CsvTaggerProvider.cs b/New_Extensibility_Model/Samples/ClassificationSample/CsvTaggerProvider.cs
new file mode 100644
index 00000000..2c7bcf8b
--- /dev/null
+++ b/New_Extensibility_Model/Samples/ClassificationSample/CsvTaggerProvider.cs
@@ -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, ITextViewChangedListener
+{
+ private readonly object lockObject = new();
+ private readonly Dictionary> 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 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> ITextViewTaggerProvider.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>(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);
+ }
+ }
+ }
+ }
+}
diff --git a/New_Extensibility_Model/Samples/ClassificationSample/ExtensionEntrypoint.cs b/New_Extensibility_Model/Samples/ClassificationSample/ExtensionEntrypoint.cs
new file mode 100644
index 00000000..1f6fb41e
--- /dev/null
+++ b/New_Extensibility_Model/Samples/ClassificationSample/ExtensionEntrypoint.cs
@@ -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;
+
+///
+/// Extension entry point for the ClassificationSample extension.
+///
+[VisualStudioContribution]
+public class ExtensionEntrypoint : Extension
+{
+ ///
+ 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"),
+ };
+
+ ///
+ protected override void InitializeServices(IServiceCollection serviceCollection)
+ {
+ base.InitializeServices(serviceCollection);
+ }
+}
diff --git a/New_Extensibility_Model/Samples/ClassificationSample/README.md b/New_Extensibility_Model/Samples/ClassificationSample/README.md
new file mode 100644
index 00000000..9b21bdbc
--- /dev/null
+++ b/New_Extensibility_Model/Samples/ClassificationSample/README.md
@@ -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` and have the `TextViewTagger<>`
+generate `ClassificationTag` values.
+
+```cs
+tags.Add(
+ new TaggedTrackingTextRange(
+ 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).
diff --git a/New_Extensibility_Model/Samples/CodeLensSample/CodeLensSample.csproj b/New_Extensibility_Model/Samples/CodeLensSample/CodeLensSample.csproj
index 2d21a8dd..e44a747c 100644
--- a/New_Extensibility_Model/Samples/CodeLensSample/CodeLensSample.csproj
+++ b/New_Extensibility_Model/Samples/CodeLensSample/CodeLensSample.csproj
@@ -7,8 +7,8 @@
-
-
+
+
diff --git a/New_Extensibility_Model/Samples/CommandParentingSample/CommandParentingSample.csproj b/New_Extensibility_Model/Samples/CommandParentingSample/CommandParentingSample.csproj
index de6950d4..e5567038 100644
--- a/New_Extensibility_Model/Samples/CommandParentingSample/CommandParentingSample.csproj
+++ b/New_Extensibility_Model/Samples/CommandParentingSample/CommandParentingSample.csproj
@@ -5,13 +5,10 @@
enableenable12
-
-
- https://pkgs.dev.azure.com/azure-public/vside/_packaging/msft_consumption/nuget/v3/index.json;$(RestoreAdditionalProjectSources)
-
+
-
-
+
+
diff --git a/New_Extensibility_Model/Samples/CommentRemover/CommentRemover.csproj b/New_Extensibility_Model/Samples/CommentRemover/CommentRemover.csproj
index 355ec26a..49776900 100644
--- a/New_Extensibility_Model/Samples/CommentRemover/CommentRemover.csproj
+++ b/New_Extensibility_Model/Samples/CommentRemover/CommentRemover.csproj
@@ -11,8 +11,8 @@
-
-
+
+
diff --git a/New_Extensibility_Model/Samples/CompositeExtension/CompositeExtension/CompositeExtension.csproj b/New_Extensibility_Model/Samples/CompositeExtension/CompositeExtension/CompositeExtension.csproj
index b442cc82..8b911041 100644
--- a/New_Extensibility_Model/Samples/CompositeExtension/CompositeExtension/CompositeExtension.csproj
+++ b/New_Extensibility_Model/Samples/CompositeExtension/CompositeExtension/CompositeExtension.csproj
@@ -9,8 +9,8 @@
-
-
+
+
diff --git a/New_Extensibility_Model/Samples/CompositeExtension/OutOfProcComponent/OutOfProcComponent.csproj b/New_Extensibility_Model/Samples/CompositeExtension/OutOfProcComponent/OutOfProcComponent.csproj
index 5fcc945b..47969e1e 100644
--- a/New_Extensibility_Model/Samples/CompositeExtension/OutOfProcComponent/OutOfProcComponent.csproj
+++ b/New_Extensibility_Model/Samples/CompositeExtension/OutOfProcComponent/OutOfProcComponent.csproj
@@ -17,8 +17,8 @@
-
-
+
+
diff --git a/New_Extensibility_Model/Samples/DialogSample/DialogSample.csproj b/New_Extensibility_Model/Samples/DialogSample/DialogSample.csproj
index b2c63962..65f0705d 100644
--- a/New_Extensibility_Model/Samples/DialogSample/DialogSample.csproj
+++ b/New_Extensibility_Model/Samples/DialogSample/DialogSample.csproj
@@ -7,8 +7,8 @@
-
-
+
+
diff --git a/New_Extensibility_Model/Samples/DocumentSelectorSample/DocumentSelectorSample.csproj b/New_Extensibility_Model/Samples/DocumentSelectorSample/DocumentSelectorSample.csproj
index 65fa78ba..be068225 100644
--- a/New_Extensibility_Model/Samples/DocumentSelectorSample/DocumentSelectorSample.csproj
+++ b/New_Extensibility_Model/Samples/DocumentSelectorSample/DocumentSelectorSample.csproj
@@ -7,8 +7,8 @@
-
-
+
+
diff --git a/New_Extensibility_Model/Samples/EncodeDecodeBase64/EncodeDecodeBase64.csproj b/New_Extensibility_Model/Samples/EncodeDecodeBase64/EncodeDecodeBase64.csproj
index c8e240df..f4534705 100644
--- a/New_Extensibility_Model/Samples/EncodeDecodeBase64/EncodeDecodeBase64.csproj
+++ b/New_Extensibility_Model/Samples/EncodeDecodeBase64/EncodeDecodeBase64.csproj
@@ -7,7 +7,7 @@
-
-
+
+
diff --git a/New_Extensibility_Model/Samples/ExtensionPublisher/ExtensionPublisher.csproj b/New_Extensibility_Model/Samples/ExtensionPublisher/ExtensionPublisher.csproj
index 2a877a05..c3f5c507 100644
--- a/New_Extensibility_Model/Samples/ExtensionPublisher/ExtensionPublisher.csproj
+++ b/New_Extensibility_Model/Samples/ExtensionPublisher/ExtensionPublisher.csproj
@@ -6,8 +6,8 @@
-
-
+
+
diff --git a/New_Extensibility_Model/Samples/ExtensionWithTraditionalComponents/Container/AssemblyInfo.cs b/New_Extensibility_Model/Samples/ExtensionWithTraditionalComponents/Container/AssemblyInfo.cs
new file mode 100644
index 00000000..173c3199
--- /dev/null
+++ b/New_Extensibility_Model/Samples/ExtensionWithTraditionalComponents/Container/AssemblyInfo.cs
@@ -0,0 +1,9 @@
+using System.Runtime.InteropServices;
+using Microsoft.VisualStudio.Shell;
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+[assembly: ProvideCodeBase]
diff --git a/New_Extensibility_Model/Samples/ExtensionWithTraditionalComponents/Container/Container.csproj b/New_Extensibility_Model/Samples/ExtensionWithTraditionalComponents/Container/Container.csproj
new file mode 100644
index 00000000..004f7086
--- /dev/null
+++ b/New_Extensibility_Model/Samples/ExtensionWithTraditionalComponents/Container/Container.csproj
@@ -0,0 +1,37 @@
+
+
+ net472
+ enable
+ 12
+ en-US
+ true
+
+ true
+ true
+ true
+ True
+
+ sgKey.snk
+
+
+
+
+
+
+
+
+
+
+ false
+ true
+ true
+ ExtensionFilesOutputGroup
+
+
+
+
+
+
+
diff --git a/New_Extensibility_Model/Samples/ExtensionWithTraditionalComponents/Container/MyUserControl.xaml b/New_Extensibility_Model/Samples/ExtensionWithTraditionalComponents/Container/MyUserControl.xaml
new file mode 100644
index 00000000..37533b42
--- /dev/null
+++ b/New_Extensibility_Model/Samples/ExtensionWithTraditionalComponents/Container/MyUserControl.xaml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/New_Extensibility_Model/Samples/ExtensionWithTraditionalComponents/Container/MyUserControl.xaml.cs b/New_Extensibility_Model/Samples/ExtensionWithTraditionalComponents/Container/MyUserControl.xaml.cs
new file mode 100644
index 00000000..f63722ce
--- /dev/null
+++ b/New_Extensibility_Model/Samples/ExtensionWithTraditionalComponents/Container/MyUserControl.xaml.cs
@@ -0,0 +1,17 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+namespace Container;
+
+using System.Windows.Controls;
+
+///
+/// Interaction logic for MyUserControl.xaml
+///
+public partial class MyUserControl : UserControl
+{
+ public MyUserControl()
+ {
+ this.InitializeComponent();
+ }
+}
diff --git a/New_Extensibility_Model/Samples/ExtensionWithTraditionalComponents/Container/sgKey.snk b/New_Extensibility_Model/Samples/ExtensionWithTraditionalComponents/Container/sgKey.snk
new file mode 100644
index 00000000..3379c82a
Binary files /dev/null and b/New_Extensibility_Model/Samples/ExtensionWithTraditionalComponents/Container/sgKey.snk differ
diff --git a/New_Extensibility_Model/Samples/ExtensionWithTraditionalComponents/Container/source.extension.vsixmanifest b/New_Extensibility_Model/Samples/ExtensionWithTraditionalComponents/Container/source.extension.vsixmanifest
new file mode 100644
index 00000000..671ee9a8
--- /dev/null
+++ b/New_Extensibility_Model/Samples/ExtensionWithTraditionalComponents/Container/source.extension.vsixmanifest
@@ -0,0 +1,20 @@
+
+
+
+
+ Extension with Traditional Components
+ Shows how to implement an out-of-process extension that packages traditional content.
+ true
+
+
+
+ amd64
+
+
+ arm64
+
+
+
+
+
+
diff --git a/New_Extensibility_Model/Samples/ExtensionWithTraditionalComponents/Extension/.vsextension/string-resources.json b/New_Extensibility_Model/Samples/ExtensionWithTraditionalComponents/Extension/.vsextension/string-resources.json
new file mode 100644
index 00000000..4e591eaf
--- /dev/null
+++ b/New_Extensibility_Model/Samples/ExtensionWithTraditionalComponents/Extension/.vsextension/string-resources.json
@@ -0,0 +1,3 @@
+{
+ "ExtensionWithTraditionalComponents.MyToolWindowCommand.DisplayName": "Out-of-process tool window with custom WPF control"
+}
diff --git a/New_Extensibility_Model/Samples/ExtensionWithTraditionalComponents/Extension/Extension.csproj b/New_Extensibility_Model/Samples/ExtensionWithTraditionalComponents/Extension/Extension.csproj
new file mode 100644
index 00000000..9fabdd84
--- /dev/null
+++ b/New_Extensibility_Model/Samples/ExtensionWithTraditionalComponents/Extension/Extension.csproj
@@ -0,0 +1,27 @@
+
+
+ net8.0-windows8.0
+ enable
+ 12
+ en-US
+
+ ExtensionWithTraditionalComponents
+
+
+ OutOfProc
+
+
+ false
+
+
+
+
+
+
+
+
+
+
+
diff --git a/New_Extensibility_Model/Samples/ExtensionWithTraditionalComponents/Extension/ExtensionEntrypoint.cs b/New_Extensibility_Model/Samples/ExtensionWithTraditionalComponents/Extension/ExtensionEntrypoint.cs
new file mode 100644
index 00000000..4a18d7f1
--- /dev/null
+++ b/New_Extensibility_Model/Samples/ExtensionWithTraditionalComponents/Extension/ExtensionEntrypoint.cs
@@ -0,0 +1,26 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+namespace ExtensionWithTraditionalComponents;
+
+using Microsoft.VisualStudio.Extensibility;
+
+///
+/// Extension entry point for the InsertGuid extension.
+///
+[VisualStudioContribution]
+internal class ExtensionEntrypoint : Extension
+{
+ ///
+ public override ExtensionConfiguration ExtensionConfiguration => new()
+ {
+ // The following is irrelevant since OutOfProcComponent is being packaged as part of
+ // CompositeExtension which has its own vsixmanifest.
+ Metadata = new(
+ id: "ExtensionWithTraditionalComponents.1ccd148a-fa3a-414b-929b-85537d7a48b0",
+ version: this.ExtensionAssemblyVersion,
+ publisherName: "Microsoft",
+ displayName: "Extension with Traditional Components",
+ description: "Shows how to implement an out-of-process extension that packages traditional content."),
+ };
+}
diff --git a/New_Extensibility_Model/Samples/ExtensionWithTraditionalComponents/Extension/MyToolWindow.cs b/New_Extensibility_Model/Samples/ExtensionWithTraditionalComponents/Extension/MyToolWindow.cs
new file mode 100644
index 00000000..4c13a43f
--- /dev/null
+++ b/New_Extensibility_Model/Samples/ExtensionWithTraditionalComponents/Extension/MyToolWindow.cs
@@ -0,0 +1,58 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+namespace ExtensionWithTraditionalComponents;
+
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.VisualStudio.Extensibility;
+using Microsoft.VisualStudio.Extensibility.ToolWindows;
+using Microsoft.VisualStudio.RpcContracts.RemoteUI;
+
+///
+/// A sample tool window.
+///
+[VisualStudioContribution]
+public class MyToolWindow : ToolWindow
+{
+ private readonly MyToolWindowContent content = new();
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public MyToolWindow()
+ {
+ this.Title = "My Tool Window";
+ }
+
+ ///
+ public override ToolWindowConfiguration ToolWindowConfiguration => new()
+ {
+ // Use this object initializer to set optional parameters for the tool window.
+ Placement = ToolWindowPlacement.Floating,
+ };
+
+ ///
+ public override Task InitializeAsync(CancellationToken cancellationToken)
+ {
+ // Use InitializeAsync for any one-time setup or initialization.
+ return Task.CompletedTask;
+ }
+
+ ///
+ public override Task GetContentAsync(CancellationToken cancellationToken)
+ {
+ return Task.FromResult(this.content);
+ }
+
+ ///
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ this.content.Dispose();
+ }
+
+ base.Dispose(disposing);
+ }
+}
diff --git a/New_Extensibility_Model/Samples/ExtensionWithTraditionalComponents/Extension/MyToolWindowCommand.cs b/New_Extensibility_Model/Samples/ExtensionWithTraditionalComponents/Extension/MyToolWindowCommand.cs
new file mode 100644
index 00000000..1cffc4a3
--- /dev/null
+++ b/New_Extensibility_Model/Samples/ExtensionWithTraditionalComponents/Extension/MyToolWindowCommand.cs
@@ -0,0 +1,32 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+namespace ExtensionWithTraditionalComponents;
+
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.VisualStudio.Extensibility;
+using Microsoft.VisualStudio.Extensibility.Commands;
+
+///
+/// A command for showing a tool window.
+///
+[VisualStudioContribution]
+public class MyToolWindowCommand : Command
+{
+ ///
+ public override CommandConfiguration CommandConfiguration => new("%ExtensionWithTraditionalComponents.MyToolWindowCommand.DisplayName%")
+ {
+ // Use this object initializer to set optional parameters for the command. The required parameter,
+ // displayName, is set above. To localize the displayName, add an entry in .vsextension\string-resources.json
+ // and reference it here by passing "%ExtensionWithTraditionalComponents.MyToolWindowCommand.DisplayName%" as a constructor parameter.
+ Placements = [CommandPlacement.KnownPlacements.ExtensionsMenu],
+ Icon = new(ImageMoniker.KnownValues.Extension, IconSettings.IconAndText),
+ };
+
+ ///
+ public override async Task ExecuteCommandAsync(IClientContext context, CancellationToken cancellationToken)
+ {
+ await this.Extensibility.Shell().ShowToolWindowAsync(activate: true, cancellationToken);
+ }
+}
diff --git a/New_Extensibility_Model/Samples/ExtensionWithTraditionalComponents/Extension/MyToolWindowContent.cs b/New_Extensibility_Model/Samples/ExtensionWithTraditionalComponents/Extension/MyToolWindowContent.cs
new file mode 100644
index 00000000..5d07847b
--- /dev/null
+++ b/New_Extensibility_Model/Samples/ExtensionWithTraditionalComponents/Extension/MyToolWindowContent.cs
@@ -0,0 +1,20 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+namespace ExtensionWithTraditionalComponents;
+
+using Microsoft.VisualStudio.Extensibility.UI;
+
+///
+/// A remote user control to use as tool window UI content.
+///
+internal class MyToolWindowContent : RemoteUserControl
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public MyToolWindowContent()
+ : base(dataContext: new MyToolWindowData())
+ {
+ }
+}
diff --git a/New_Extensibility_Model/Samples/ExtensionWithTraditionalComponents/Extension/MyToolWindowContent.xaml b/New_Extensibility_Model/Samples/ExtensionWithTraditionalComponents/Extension/MyToolWindowContent.xaml
new file mode 100644
index 00000000..b3f51650
--- /dev/null
+++ b/New_Extensibility_Model/Samples/ExtensionWithTraditionalComponents/Extension/MyToolWindowContent.xaml
@@ -0,0 +1,9 @@
+
+
+
+
diff --git a/New_Extensibility_Model/Samples/ExtensionWithTraditionalComponents/Extension/MyToolWindowData.cs b/New_Extensibility_Model/Samples/ExtensionWithTraditionalComponents/Extension/MyToolWindowData.cs
new file mode 100644
index 00000000..b346a76f
--- /dev/null
+++ b/New_Extensibility_Model/Samples/ExtensionWithTraditionalComponents/Extension/MyToolWindowData.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 ExtensionWithTraditionalComponents;
+
+using System.Runtime.Serialization;
+using System.Threading.Tasks;
+using Microsoft.VisualStudio.Extensibility.UI;
+
+///
+/// ViewModel for the MyToolWindowContent remote user control.
+///
+[DataContract]
+internal class MyToolWindowData : NotifyPropertyChangedObject
+{
+ private string name = string.Empty;
+ private string text = string.Empty;
+
+ public MyToolWindowData()
+ {
+ this.HelloCommand = new AsyncCommand((parameter, clientContext, cancellationToken) =>
+ {
+ this.Text = $"Hello {parameter as string}!";
+ return Task.CompletedTask;
+ });
+ }
+
+ [DataMember]
+ public string Name
+ {
+ get => this.name;
+ set => this.SetProperty(ref this.name, value);
+ }
+
+ [DataMember]
+ public string Text
+ {
+ get => this.text;
+ set => this.SetProperty(ref this.text, value);
+ }
+
+ [DataMember]
+ public AsyncCommand HelloCommand { get; }
+}
diff --git a/New_Extensibility_Model/Samples/ExtensionWithTraditionalComponents/README.md b/New_Extensibility_Model/Samples/ExtensionWithTraditionalComponents/README.md
new file mode 100644
index 00000000..8e08aa7b
--- /dev/null
+++ b/New_Extensibility_Model/Samples/ExtensionWithTraditionalComponents/README.md
@@ -0,0 +1,160 @@
+---
+title: out-of-proc Extension with VSSDK components
+description: A reference sample for out-of-proc extensions that package traditional VSSDK components
+date: 2025-01-15
+---
+
+# Walkthrough: In-proc/out-of-proc Composite Extension Sample
+
+This is sample of how to write an out-of-proc VisualStudio.Extensibility extensions that
+packages some traditional VSSDK components. This sample implements a Remote UI-based tool window
+and packages a WPF user control to be loaded in-proc in VS.
+
+This sample is similar to [CompositeExtension](../CompositeExtension) but it doesn't contribute any
+in-proc VisualStudio.Extensibility components.
+
+`Extension.csproj` targets .NET 8.0 and uses the VisualStudio.Extensibility packages.
+
+`Container.csproj` targets .NET Framework and packages the output of `Extension.csproj` into a VSIX
+while also packaging its own assembly containing the WPF user control.
+
+The same structure can be applied to package any traditional VSSDK content (like classification type
+definitions, editor format definitions, project templates, TextMate files, ect.)
+
+Because the out-of-proc extension is packaged together with VSSDK components, hot-load is not available
+and the extension's installatio will require VS to restart.
+
+## Implementing the extension component
+
+The extension starts as a [standard VisualStudio.Extensibility project](https://learn.microsoft.com/visualstudio/extensibility/visualstudio.extensibility/get-started/create-your-first-extension).
+
+### Updating the out-of-proc component's csproj file
+
+Since the out-of-proc component will be packaged into the `CompositeExtension` VSIX, we don't need to
+create a VSIX package. Let's add the following to the csproj:
+
+```xml
+false
+```
+
+As mentioned above, `Extension.csproj` and `Container.csproj` have different target frameworks.
+This results in them potentially having incompatible versions of the same dependencies. To avoid issues, we
+will package the `Extension` assembly and it's dependencies in a subfolder of the VSIX. Let's add
+the following to the csproj:
+
+```xml
+OutOfProc
+```
+
+This is not as important as for [CompositeExtension](../CompositeExtension), since `Container.csproj`
+doesn't share dependencies with `Container.csproj`, but it's still better to keep them separate.
+
+### Other changes
+
+One of the steps in creating an out-of-proc VisualStudio.Extensibility extension is to fill in the
+`ExtensionConfiguration`'s `Metadata` in the `Extension` class. While it's a good practice to do this, it's
+worth noting that most of this information will be ignored since the `Container` will provide this
+information for the entire VSIX using a `vsixmanifest` file.
+
+## Implementing the in-proc component (`CompositeExtension`)
+
+The `Container` project starts as a [standard VSSDK-compatible VisualStudio.Extensibility extension](https://learn.microsoft.com/visualstudio/extensibility/visualstudio.extensibility/get-started/in-proc-extensions)
+but it is stripped of most of its content. The `Extension` class and the project reference to
+`Microsoft.VisualStudio.Extensibility.Sdk` are removed.
+
+### Updating the `Container`'s csproj file
+
+#### Packaging the `Extension` in the `Container`'s VSIX
+
+The first step after creating the `Container` and filling in the information in the `source.extension.vsixmanifest`
+file is to add a reference to `Extension.csproj` so that it's output is packaged into the `Container`'s VSIX:
+
+```xml
+
+ false
+ true
+ true
+ ExtensionFilesOutputGroup
+
+```
+
+You can see that `Container` doesn't reference the out-of-proc component's assembly directly, since they
+have different target frameworks.
+
+Including the `ExtensionFilesOutputGroup` output in the VSIX ensures that all the `Extension`'s
+dependencies are correctly packaged (in a separate subfolder, since `Extension` specifies an
+`AssemblyVSIXSubPath`).
+
+`SetTargetFramework` allows the out-of-proc component to correctly build as a dependency of `CompositeExtension`
+even if the two project have different target frameworks.
+
+#### Including the `Extension`'s `extension.json`
+
+VisualStudio.Extensibility extension projects generate a file named `extension.json` which is packaged in the
+VSIX and contains information about what features the extension contributes to VS.
+
+Since the `Extension` is a separate project, the `Container`'s csproj needs to package it into its own
+vsix:
+
+```xml
+
+
+
+```
+
+You may need to adjust the path to match your project structure. The `ProjectReference`
+added [before](#packaging-the-extension-in-the-containers-vsix) guarantees that the
+out-of-proc component's `extension.json` is generated before the `Container`'s compilation starts.
+
+Note that this is different than what we did in the [CompositeExtension sample](../CompositeExtension/README.md#merging-the-out-of-proc-components-extensionjson)
+since `Container` doesn't generate its own `extension.json` file. So we need to include `Extension`'s one
+instead of merging the two `extension.json` files together.
+
+## Exposing the WPF user control
+
+After adding `true` to `Container.csproj`, we add the [MyUserControl.xaml](./Container/MyUserControl.xaml)
+WPF user control as we would to to any class library.
+
+Since `Container.csproj` has a package reference to `Microsoft.VisualStudio.SDK` and this assembly is meant
+to be loaded inside the Visual Studio process, the xaml file can reference Visual Studio types:
+
+```xml
+xmlns:styles="clr-namespace:Microsoft.VisualStudio.Shell;assembly=Microsoft.VisualStudio.Shell.15.0"
+xmlns:colors="clr-namespace:Microsoft.VisualStudio.PlatformUI;assembly=Microsoft.VisualStudio.Shell.15.0"
+```
+
+We also need to add a `ProvideCodeBase` attribute to the project:
+
+```cs
+[assembly: ProvideCodeBase]
+```
+As well as configuring `Container.csproj` to generate a `.pkgdef` file, use codebases for the pkgdef generation,
+and sign the assembly with a strong name.
+
+```xml
+true
+true
+True
+sgKey.snk
+```
+
+These steps allow the `Container.dll` to be available to Visual Studio to load, which will allow us to reference
+the WPF user control from the extension. Only strong-named assemblies can be loaded by Visual Studio in this
+manner.
+
+## Referencing the WPF user control
+
+The extension can now reference the WPF control from Remote UI XAML:
+
+```xml
+
+
+
+
+```
+
+Note that the implementation in this sample of the tool window is the same as the one in the [tool window sample](../ToolWindowSample/).
+You can refer to that [readme](../ToolWindowSample/README.md) for step-by-step instructions on how to create a
+Remote-UI based tool window.
diff --git a/New_Extensibility_Model/Samples/FeatureGallery/FeatureGallery.csproj b/New_Extensibility_Model/Samples/FeatureGallery/FeatureGallery.csproj
index a0a12680..d1a488f5 100644
--- a/New_Extensibility_Model/Samples/FeatureGallery/FeatureGallery.csproj
+++ b/New_Extensibility_Model/Samples/FeatureGallery/FeatureGallery.csproj
@@ -6,8 +6,8 @@
en-US
-
-
+
+
diff --git a/New_Extensibility_Model/Samples/FeatureGallery/Taggers/MarkdownCodeLensTagger.cs b/New_Extensibility_Model/Samples/FeatureGallery/Taggers/MarkdownCodeLensTagger.cs
index b5e762b0..dc55d844 100644
--- a/New_Extensibility_Model/Samples/FeatureGallery/Taggers/MarkdownCodeLensTagger.cs
+++ b/New_Extensibility_Model/Samples/FeatureGallery/Taggers/MarkdownCodeLensTagger.cs
@@ -73,7 +73,7 @@ public async Task TextViewChangedAsync(ITextViewSnapshot textView, IReadOnlyList
.Any(l => l.StartsWith("#")) ||
Lines(
documentAfter,
- edits.Select(e => e.Range.TranslateTo(documentAfter, TextRangeTrackingMode.EdgeInclusive)))
+ edits.Select(e => e.Range.TranslateTo(documentAfter, TextRangeTrackingMode.ExtendForwardAndBackward)))
.Any(l => l.StartsWith("#")))
{
this.currentDocumentSnapshot = documentAfter;
@@ -179,7 +179,7 @@ private async Task CreateTagsAsync(ITextDocumentSnapshot document)
sections.Add(identifier, 1);
}
- tags.Add(new(new(document, line.Text.Start, line.Text.Length, TextRangeTrackingMode.EdgeInclusive), new(SectionCodeElementKind)
+ tags.Add(new(new(document, line.Text.Start, line.Text.Length, TextRangeTrackingMode.ExtendForwardAndBackward), new(SectionCodeElementKind)
{
UniqueIdentifier = identifier,
Description = $"Level {level} section: {description}",
diff --git a/New_Extensibility_Model/Samples/FeatureGallery/Taggers/MarkdownTextMarkerTagger.cs b/New_Extensibility_Model/Samples/FeatureGallery/Taggers/MarkdownTextMarkerTagger.cs
index 3759e330..fa2ac194 100644
--- a/New_Extensibility_Model/Samples/FeatureGallery/Taggers/MarkdownTextMarkerTagger.cs
+++ b/New_Extensibility_Model/Samples/FeatureGallery/Taggers/MarkdownTextMarkerTagger.cs
@@ -44,7 +44,7 @@ public async Task TextViewChangedAsync(ITextViewSnapshot textView, IReadOnlyList
await this.CreateTagsAsync(
textView.Document,
allRequestedRanges
- .Intersect(edits.Select(e => EnsureNotEmpty(e.Range.TranslateTo(textView.Document, TextRangeTrackingMode.EdgeInclusive)))));
+ .Intersect(edits.Select(e => EnsureNotEmpty(e.Range.TranslateTo(textView.Document, TextRangeTrackingMode.ExtendForwardAndBackward)))));
}
protected override async Task RequestTagsAsync(NormalizedTextRangeCollection requestedRanges, bool recalculateAll, CancellationToken cancellationToken)
@@ -87,7 +87,7 @@ private async Task CreateTagsAsync(ITextDocumentSnapshot document, IEnumerable 0)
{
- tags.Add(new(new(document, line.Text.Start, len, TextRangeTrackingMode.EdgeInclusive), new("MarkerFormatDefinition/FindHighlight")));
+ tags.Add(new(new(document, line.Text.Start, len, TextRangeTrackingMode.ExtendForwardAndBackward), new("MarkerFormatDefinition/FindHighlight")));
}
}
diff --git a/New_Extensibility_Model/Samples/FilePickerSample/FilePickerSample.csproj b/New_Extensibility_Model/Samples/FilePickerSample/FilePickerSample.csproj
index c8e240df..f4534705 100644
--- a/New_Extensibility_Model/Samples/FilePickerSample/FilePickerSample.csproj
+++ b/New_Extensibility_Model/Samples/FilePickerSample/FilePickerSample.csproj
@@ -7,7 +7,7 @@
-
-
+
+
diff --git a/New_Extensibility_Model/Samples/InProcFeatureGallery/InProcFeatureGallery.csproj b/New_Extensibility_Model/Samples/InProcFeatureGallery/InProcFeatureGallery.csproj
index af8b5724..f66c8594 100644
--- a/New_Extensibility_Model/Samples/InProcFeatureGallery/InProcFeatureGallery.csproj
+++ b/New_Extensibility_Model/Samples/InProcFeatureGallery/InProcFeatureGallery.csproj
@@ -11,8 +11,8 @@
-
-
+
+
diff --git a/New_Extensibility_Model/Samples/InsertGuid/InsertGuid.csproj b/New_Extensibility_Model/Samples/InsertGuid/InsertGuid.csproj
index a4320afe..1ea451de 100644
--- a/New_Extensibility_Model/Samples/InsertGuid/InsertGuid.csproj
+++ b/New_Extensibility_Model/Samples/InsertGuid/InsertGuid.csproj
@@ -7,8 +7,8 @@
-
-
+
+
diff --git a/New_Extensibility_Model/Samples/MarkdownLinter/MarkdownLinter.csproj b/New_Extensibility_Model/Samples/MarkdownLinter/MarkdownLinter.csproj
index 2281b382..699524b2 100644
--- a/New_Extensibility_Model/Samples/MarkdownLinter/MarkdownLinter.csproj
+++ b/New_Extensibility_Model/Samples/MarkdownLinter/MarkdownLinter.csproj
@@ -7,8 +7,8 @@
-
-
+
+
diff --git a/New_Extensibility_Model/Samples/MemoryStreamDebugVisualizer/MemoryStreamVisualizer/MemoryStreamVisualizer.csproj b/New_Extensibility_Model/Samples/MemoryStreamDebugVisualizer/MemoryStreamVisualizer/MemoryStreamVisualizer.csproj
index 62c29eb1..4c3152a9 100644
--- a/New_Extensibility_Model/Samples/MemoryStreamDebugVisualizer/MemoryStreamVisualizer/MemoryStreamVisualizer.csproj
+++ b/New_Extensibility_Model/Samples/MemoryStreamDebugVisualizer/MemoryStreamVisualizer/MemoryStreamVisualizer.csproj
@@ -7,8 +7,8 @@
-
-
+
+
diff --git a/New_Extensibility_Model/Samples/OutputWindowSample/OutputWindowSample.csproj b/New_Extensibility_Model/Samples/OutputWindowSample/OutputWindowSample.csproj
index 623606b0..58864928 100644
--- a/New_Extensibility_Model/Samples/OutputWindowSample/OutputWindowSample.csproj
+++ b/New_Extensibility_Model/Samples/OutputWindowSample/OutputWindowSample.csproj
@@ -7,8 +7,8 @@
-
-
+
+
diff --git a/New_Extensibility_Model/Samples/README.md b/New_Extensibility_Model/Samples/README.md
index 34021f53..1442b21b 100644
--- a/New_Extensibility_Model/Samples/README.md
+++ b/New_Extensibility_Model/Samples/README.md
@@ -13,7 +13,7 @@ Samples include:
| [Document selector](./DocumentSelectorSample) | Shows how to create an editor extension that is only applicable to files matching a file path pattern. |
| [Output window](./OutputWindowSample) | Shows the most basic use of the [Output Window API](https://learn.microsoft.com/visualstudio/extensibility/visualstudio.extensibility/output-window/output-window)|
| [Tool window](./ToolWindowSample) | Shows how to create a tool window and populate it with content. |
-| [User prompt](./UserPromptSample) | Shows how to display a prompt to the user. |
+| [User prompt](./UserPromptSample) | Shows how to display a prompt to the user and gather simple input. |
| [Dialog](./DialogSample) | Shows how to display a dialog with custom UI to the user. |
| [Word count margin](./WordCountMargin) | Shows how to create an editor margin extension that displays the word count in a document. |
| [Markdown linter](./MarkdownLinter) | A complete extension with many moving parts interacting to provide an enhanced experience in the editor for a certain file type. |
diff --git a/New_Extensibility_Model/Samples/RegexMatchDebugVisualizer/RegexMatchDebugVisualizer/RegexMatchDebugVisualizer.csproj b/New_Extensibility_Model/Samples/RegexMatchDebugVisualizer/RegexMatchDebugVisualizer/RegexMatchDebugVisualizer.csproj
index 2ff6581f..c3d74994 100644
--- a/New_Extensibility_Model/Samples/RegexMatchDebugVisualizer/RegexMatchDebugVisualizer/RegexMatchDebugVisualizer.csproj
+++ b/New_Extensibility_Model/Samples/RegexMatchDebugVisualizer/RegexMatchDebugVisualizer/RegexMatchDebugVisualizer.csproj
@@ -9,8 +9,8 @@
-
-
+
+
diff --git a/New_Extensibility_Model/Samples/RustLanguageServerProvider/RustLanguageServerProvider.csproj b/New_Extensibility_Model/Samples/RustLanguageServerProvider/RustLanguageServerProvider.csproj
index 42961f9f..f4d5c715 100644
--- a/New_Extensibility_Model/Samples/RustLanguageServerProvider/RustLanguageServerProvider.csproj
+++ b/New_Extensibility_Model/Samples/RustLanguageServerProvider/RustLanguageServerProvider.csproj
@@ -7,8 +7,8 @@
-
-
+
+
diff --git a/New_Extensibility_Model/Samples/Samples.sln b/New_Extensibility_Model/Samples/Samples.sln
index 5741aef0..c4c34243 100644
--- a/New_Extensibility_Model/Samples/Samples.sln
+++ b/New_Extensibility_Model/Samples/Samples.sln
@@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 17
-VisualStudioVersion = 17.9.34329.44
+# Visual Studio Version 18
+VisualStudioVersion = 18.0.10520.38 main
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{070F0AEA-C0A0-4B5D-9286-55574A37BE7A}"
ProjectSection(SolutionItems) = preProject
@@ -75,6 +75,16 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TaggersSample", "TaggersSam
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EncodeDecodeBase64", "EncodeDecodeBase64\EncodeDecodeBase64.csproj", "{4E6E513B-0690-4B75-8F45-8DFB73D630CC}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ExtensionWithTraditionalComponents", "ExtensionWithTraditionalComponents", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}"
+ ProjectSection(SolutionItems) = preProject
+ ExtensionWithTraditionalComponents\README.md = ExtensionWithTraditionalComponents\README.md
+ EndProjectSection
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Extension", "ExtensionWithTraditionalComponents\Extension\Extension.csproj", "{15B554E7-D240-072F-F5F0-EBD4C2CA5BFB}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Container", "ExtensionWithTraditionalComponents\Container\Container.csproj", "{D99787A2-36E1-FE47-40E9-62CF6BE72BFB}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClassificationSample", "ClassificationSample\ClassificationSample.csproj", "{781AC13C-B948-F6C0-FAE7-179CC49641F2}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -191,6 +201,18 @@ Global
{4E6E513B-0690-4B75-8F45-8DFB73D630CC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4E6E513B-0690-4B75-8F45-8DFB73D630CC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4E6E513B-0690-4B75-8F45-8DFB73D630CC}.Release|Any CPU.Build.0 = Release|Any CPU
+ {15B554E7-D240-072F-F5F0-EBD4C2CA5BFB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {15B554E7-D240-072F-F5F0-EBD4C2CA5BFB}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {15B554E7-D240-072F-F5F0-EBD4C2CA5BFB}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {15B554E7-D240-072F-F5F0-EBD4C2CA5BFB}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D99787A2-36E1-FE47-40E9-62CF6BE72BFB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D99787A2-36E1-FE47-40E9-62CF6BE72BFB}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D99787A2-36E1-FE47-40E9-62CF6BE72BFB}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D99787A2-36E1-FE47-40E9-62CF6BE72BFB}.Release|Any CPU.Build.0 = Release|Any CPU
+ {781AC13C-B948-F6C0-FAE7-179CC49641F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {781AC13C-B948-F6C0-FAE7-179CC49641F2}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {781AC13C-B948-F6C0-FAE7-179CC49641F2}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {781AC13C-B948-F6C0-FAE7-179CC49641F2}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -202,6 +224,8 @@ Global
{AA31B852-5E22-4CAC-AF38-B62FA592E747} = {D235C6BF-C068-4459-A5B6-9C508B8A9FEB}
{B7567363-A2B8-421A-86C2-795E884C4114} = {D4AAF3ED-EC76-4323-A507-FE7797741A91}
{6B508C7E-DA3D-46C0-821C-358DCD1DC99E} = {D4AAF3ED-EC76-4323-A507-FE7797741A91}
+ {15B554E7-D240-072F-F5F0-EBD4C2CA5BFB} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
+ {D99787A2-36E1-FE47-40E9-62CF6BE72BFB} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {40A38C8A-61B7-427B-A430-DEB75BE34F22}
diff --git a/New_Extensibility_Model/Samples/SettingsSample/SettingsSample.csproj b/New_Extensibility_Model/Samples/SettingsSample/SettingsSample.csproj
index fd3f7820..a1bb0ef8 100644
--- a/New_Extensibility_Model/Samples/SettingsSample/SettingsSample.csproj
+++ b/New_Extensibility_Model/Samples/SettingsSample/SettingsSample.csproj
@@ -7,8 +7,8 @@
-
-
+
+
diff --git a/New_Extensibility_Model/Samples/SimpleRemoteCommandSample/SimpleRemoteCommandSample.csproj b/New_Extensibility_Model/Samples/SimpleRemoteCommandSample/SimpleRemoteCommandSample.csproj
index d2f2c4fc..388495cb 100644
--- a/New_Extensibility_Model/Samples/SimpleRemoteCommandSample/SimpleRemoteCommandSample.csproj
+++ b/New_Extensibility_Model/Samples/SimpleRemoteCommandSample/SimpleRemoteCommandSample.csproj
@@ -7,7 +7,7 @@
-
-
+
+
diff --git a/New_Extensibility_Model/Samples/TaggersSample/MarkdownCodeLensTagger.cs b/New_Extensibility_Model/Samples/TaggersSample/MarkdownCodeLensTagger.cs
index 820b5da5..64a18ca0 100644
--- a/New_Extensibility_Model/Samples/TaggersSample/MarkdownCodeLensTagger.cs
+++ b/New_Extensibility_Model/Samples/TaggersSample/MarkdownCodeLensTagger.cs
@@ -70,7 +70,7 @@ public async Task TextViewChangedAsync(ITextViewSnapshot textView, IReadOnlyList
.Any(l => l.StartsWith("#")) ||
GetLines(
documentAfter,
- edits.Select(e => e.Range.TranslateTo(documentAfter, TextRangeTrackingMode.EdgeInclusive)))
+ edits.Select(e => e.Range.TranslateTo(documentAfter, TextRangeTrackingMode.ExtendForwardAndBackward)))
.Any(l => l.StartsWith("#")))
{
this.currentDocumentSnapshot = documentAfter;
@@ -179,7 +179,7 @@ private async Task CreateTagsAsync(ITextDocumentSnapshot document)
sections.Add(identifier, 1);
}
- tags.Add(new(new(document, line.Text.Start, line.Text.Length, TextRangeTrackingMode.EdgeInclusive), new(SectionCodeElementKind)
+ tags.Add(new(new(document, line.Text.Start, line.Text.Length, TextRangeTrackingMode.ExtendForwardAndBackward), new(SectionCodeElementKind)
{
UniqueIdentifier = identifier,
Description = $"Level {level} section: {description}",
diff --git a/New_Extensibility_Model/Samples/TaggersSample/MarkdownTextMarkerTagger.cs b/New_Extensibility_Model/Samples/TaggersSample/MarkdownTextMarkerTagger.cs
index 88d1a774..06f5d7eb 100644
--- a/New_Extensibility_Model/Samples/TaggersSample/MarkdownTextMarkerTagger.cs
+++ b/New_Extensibility_Model/Samples/TaggersSample/MarkdownTextMarkerTagger.cs
@@ -43,7 +43,7 @@ await this.CreateTagsAsync(
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.EdgeInclusive))))); // Translate the range to the new document version.
+ 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)
@@ -91,7 +91,7 @@ private async Task CreateTagsAsync(ITextDocumentSnapshot document, IEnumerable
EnsureNotEmpty(
- e.Range.TranslateTo(textView.Document, TextRangeTrackingMode.EdgeInclusive)))));
+ e.Range.TranslateTo(textView.Document, TextRangeTrackingMode.ExtendForwardAndBackward)))));
}
```
@@ -112,7 +112,7 @@ private async Task CreateTagsAsync(ITextDocumentSnapshot document, IEnumerable
-
-
+
+
diff --git a/New_Extensibility_Model/Samples/ToolWindowSample/ToolWindowSample.csproj b/New_Extensibility_Model/Samples/ToolWindowSample/ToolWindowSample.csproj
index 9aa5c06f..7f951ae1 100644
--- a/New_Extensibility_Model/Samples/ToolWindowSample/ToolWindowSample.csproj
+++ b/New_Extensibility_Model/Samples/ToolWindowSample/ToolWindowSample.csproj
@@ -7,8 +7,8 @@
-
-
+
+
diff --git a/New_Extensibility_Model/Samples/UserPromptSample/.vsextension/string-resources.json b/New_Extensibility_Model/Samples/UserPromptSample/.vsextension/string-resources.json
index e1b60489..f720555b 100644
--- a/New_Extensibility_Model/Samples/UserPromptSample/.vsextension/string-resources.json
+++ b/New_Extensibility_Model/Samples/UserPromptSample/.vsextension/string-resources.json
@@ -1,4 +1,5 @@
{
"UserPromptSample.SampleCommand.DisplayName": "User Prompt",
- "UserPromptSample.SampleCommand.ToolTip": "Click for a prompt"
+ "UserPromptSample.SampleCommand.ToolTip": "Click for a prompt",
+ "UserPromptSample.AdvancedConfigurationCommand.DisplayName": "Advanced User Prompt"
}
\ No newline at end of file
diff --git a/New_Extensibility_Model/Samples/UserPromptSample/AdvancedConfigurationCommand.cs b/New_Extensibility_Model/Samples/UserPromptSample/AdvancedConfigurationCommand.cs
new file mode 100644
index 00000000..b60ed2f7
--- /dev/null
+++ b/New_Extensibility_Model/Samples/UserPromptSample/AdvancedConfigurationCommand.cs
@@ -0,0 +1,138 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+namespace UserPromptSample;
+
+using System.Diagnostics;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft;
+using Microsoft.VisualStudio.Extensibility;
+using Microsoft.VisualStudio.Extensibility.Commands;
+using Microsoft.VisualStudio.Extensibility.Shell;
+
+///
+/// AdvancedConfigurationCommand handler.
+///
+[VisualStudioContribution]
+internal class AdvancedConfigurationCommand : Command
+{
+ private readonly TraceSource logger;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Trace source instance to utilize.
+ public AdvancedConfigurationCommand(TraceSource traceSource)
+ {
+ // This optional TraceSource can be used for logging in the command. You can use dependency injection to access
+ // other services here as well.
+ this.logger = Requires.NotNull(traceSource, nameof(traceSource));
+ }
+
+ internal enum Systems
+ {
+ CoreSystem,
+ AuxiliarySystem,
+ MonitoringSystem,
+ }
+
+ ///
+ public override CommandConfiguration CommandConfiguration => new(displayName: "%UserPromptSample.AdvancedConfigurationCommand.DisplayName%")
+ {
+ Placements = [CommandPlacement.KnownPlacements.ToolsMenu],
+ Icon = new(ImageMoniker.KnownValues.Extension, IconSettings.IconAndText),
+ };
+
+ ///
+ public override Task InitializeAsync(CancellationToken cancellationToken)
+ {
+ // Use InitializeAsync for any one-time setup or initialization.
+ return base.InitializeAsync(cancellationToken);
+ }
+
+ ///
+ public override async Task ExecuteCommandAsync(IClientContext context, CancellationToken cancellationToken)
+ {
+ const string Title = "Configuration Assistant";
+ ShellExtensibility shell = this.Extensibility.Shell();
+
+ // Show a prompt that accepts string input from the user, with a custom title.
+ string? projectName = await shell.ShowPromptAsync(
+ "Enter the name of the project to configure?",
+ InputPromptOptions.Default with { Title = Title },
+ cancellationToken);
+
+ if (string.IsNullOrEmpty(projectName))
+ {
+ this.logger.TraceInformation("User did not provide project name.");
+
+ // Show a confirmation prompt (one 'OK' button) with error icon and title.
+ await shell.ShowPromptAsync(
+ "Project name is required to proceed. Exiting the configuration process.",
+ PromptOptions.ErrorConfirm with { Title = Title },
+ cancellationToken);
+
+ return;
+ }
+
+ // Show a prompt that accepts one of a predefined set of choices from the user, with a custom title and icon.
+ Systems selectedSystem = await shell.ShowPromptAsync(
+ "Select the system to configure:",
+ new PromptOptions
+ {
+ Choices =
+ {
+ { "Core", Systems.CoreSystem },
+ { "Auxiliary", Systems.AuxiliarySystem },
+ { "Monitoring", Systems.MonitoringSystem },
+ },
+ DismissedReturns = Systems.CoreSystem,
+ Title = Title,
+ Icon = ImageMoniker.KnownValues.Settings,
+ },
+ cancellationToken);
+
+ // Show a prompt that accepts a boolean response from the user ('OK' or 'Cancel'), with a custom title and icon.
+ bool confirmConfiguration = await shell.ShowPromptAsync(
+ $"The selected system ({selectedSystem}) may require additional resources. Do you want to proceed?",
+ PromptOptions.OKCancel with
+ {
+ Title = Title,
+ Icon = ImageMoniker.KnownValues.StatusSecurityWarning,
+ },
+ cancellationToken);
+
+ if (!confirmConfiguration)
+ {
+ this.logger.TraceInformation($"User canceled configuration for project ({projectName}) system ({selectedSystem}).");
+ await shell.ShowPromptAsync(
+ "Configuration process canceled.",
+ PromptOptions.ErrorConfirm with { Title = Title },
+ cancellationToken);
+
+ return;
+ }
+
+ // Show a confirmation prompt with an alert icon and title.
+ await shell.ShowPromptAsync(
+ "Configuration completed successfully! Thank you for using the Configuration Assistant.",
+ PromptOptions.AlertConfirm with { Title = Title },
+ cancellationToken);
+
+ const string feedbackPrompt = "Everything looks great!";
+
+ // Show a prompt that accepts string input from the user, with a default value, custom title and custom icon.
+ string? feedback = await shell.ShowPromptAsync(
+ $"Thank you for configuring {projectName}. Do you have any feedback?",
+ new InputPromptOptions
+ {
+ DefaultText = feedbackPrompt,
+ Icon = ImageMoniker.KnownValues.Feedback,
+ Title = Title,
+ },
+ cancellationToken);
+
+ this.logger.TraceInformation($"Feedback received: {feedback}");
+ }
+}
diff --git a/New_Extensibility_Model/Samples/UserPromptSample/README.md b/New_Extensibility_Model/Samples/UserPromptSample/README.md
index 05123b63..65c214d7 100644
--- a/New_Extensibility_Model/Samples/UserPromptSample/README.md
+++ b/New_Extensibility_Model/Samples/UserPromptSample/README.md
@@ -1,7 +1,7 @@
---
title: User Prompt Sample reference
description: A reference for User Prompt sample
-date: 2023-10-25
+date: 2025-04-25
---
# User Prompt Extension Sample
@@ -10,7 +10,7 @@ This is a simple extension that shows how to display user prompts during executi
## Command definition
-The extension contains a code file that defines a command and its properties starting with the `VisualStudioContribution` class attribute which makes the command available to Visual Studio:
+The extension contains code files that define commands and their properties starting with the `VisualStudioContribution` class attribute which makes the command available to Visual Studio:
```csharp
[VisualStudioContribution]
@@ -30,11 +30,11 @@ public override CommandConfiguration CommandConfiguration => new("%UserPromptSam
};
```
-The command is placed in the `Tools` top-level menu.
+The commands are placed in the `Tools` top-level menu.
## Getting the ShellExtensibility helpers
-Once user executes the command, the SDK will route execution to the `ExecuteCommandAsync` method.
+Once the user executes the command, the SDK will route execution to the `ExecuteCommandAsync` method.
In our example, we utilize the `VisualStudioExtensibility` object to acquire the `ShellExtensibility` helpers.
@@ -71,7 +71,8 @@ if (!await shell.ShowPromptAsync("Continue with executing the command?", PromptO
## Asking the user to select from a set of custom options
-In addition to the built-in `PromptOptions`, we can define a set of custom options using `PromptOptions`. `ShowPromptAsync` will return the value of type `T` that we map to each option label. In this example, if the user selects the button with the label 'GruvBox Is Groovy', `ShowPrompAsync` will return the enum value `TokenThemeResult.GruvBox`.
+In addition to the built-in `PromptOptions`, we can define a set of custom options using `PromptOptions`. `ShowPromptAsync` will return the value of type `T` that we map to each option label.
+In this example, if the user selects the button with the label 'GruvBox Is Groovy', `ShowPrompAsync` will return the enum value `TokenThemeResult.GruvBox`.
The example also demonstrates configuring the return value if the user dismisses the prompt without
making a selection using the `PromptOptions.DismissedReturns` property, and setting the default
@@ -95,8 +96,71 @@ var themeResult = await shell.ShowPromptAsync(
ct);
```
+## Additional Customization
+
+Prompts can be further customized with icons and custom titles. This example uses the built-in `PromptOptions.ErrorConfirm` to create
+a prompt with a single 'OK' button and the default error icon. The title is customized by overriding the default `Title` property using `with`.
+
+```csharp
+if (string.IsNullOrEmpty(projectName))
+{
+ this.logger.TraceInformation("User did not provide project name.");
+
+ // Show a confirmation prompt (one 'OK' button) with error icon and title.
+ await shell.ShowPromptAsync(
+ "Project name is required to proceed. Exiting the configuration process.",
+ PromptOptions.ErrorConfirm with { Title = Title },
+ cancellationToken);
+
+ return;
+}
+```
+
+Choose your own icon by providing an `ImageMoniker`.
+
+```csharp
+bool confirmConfiguration = await shell.ShowPromptAsync(
+ $"The selected system ({selectedSystem}) may require additional resources. Do you want to proceed?",
+ PromptOptions.OKCancel with
+ {
+ Title = Title,
+ Icon = ImageMoniker.KnownValues.StatusSecurityWarning,
+ },
+ cancellationToken);
+```
+
+## Input Prompts
+
+Using `InputPromptOptions`, you can ask the user to provide a single-line string response, instead of choosing from a defined list of options.
+This example shows a prompt asking the user to provide feedback, and provides a default value they can accept as is, or overwrite.
+
+```csharp
+string? feedback = await shell.ShowPromptAsync(
+ $"Thank you for configuring {projectName}. Do you have any feedback?",
+ new InputPromptOptions
+ {
+ DefaultText = "Works as expected.",
+ Icon = ImageMoniker.KnownValues.Feedback,
+ Title = Title,
+ },
+ cancellationToken);
+```
+
+If you don't want to provide `DefaultText`, you can use the simpler built-in `InputPromptOptions.Default` as a starting point.
+
+```csharp
+string? projectName = await shell.ShowPromptAsync(
+ "Enter the name of the project to configure?",
+ InputPromptOptions.Default with { Title = Title },
+ cancellationToken);
+```
+
+Input prompts always show a dismiss button, and can be also be dismssed by the user using the `Esc` key. When the user dismisses
+an input prompt, the return value will be `null`. If the user accepts the `DefaultText` using 'OK' or by pressing `Enter`, the return
+value will be the default text.
+
## Usage
-Once deployed, the User Prompt Sample command can be invoked anytime from the Tools menu.
+Once deployed, the User Prompt Sample commands can be invoked anytime from the Tools menu.
See also, [Create Visual Studio user prompts](https://learn.microsoft.com/visualstudio/extensibility/visualstudio.extensibility/user-prompt/user-prompts).
\ No newline at end of file
diff --git a/New_Extensibility_Model/Samples/UserPromptSample/UserPromptSample.csproj b/New_Extensibility_Model/Samples/UserPromptSample/UserPromptSample.csproj
index 09ff824b..6b274f05 100644
--- a/New_Extensibility_Model/Samples/UserPromptSample/UserPromptSample.csproj
+++ b/New_Extensibility_Model/Samples/UserPromptSample/UserPromptSample.csproj
@@ -8,7 +8,7 @@
-
-
+
+
diff --git a/New_Extensibility_Model/Samples/VSProjectQueryAPISample/.vsextension/string-resources.json b/New_Extensibility_Model/Samples/VSProjectQueryAPISample/.vsextension/string-resources.json
index 6a6b2767..7840e6ca 100644
--- a/New_Extensibility_Model/Samples/VSProjectQueryAPISample/.vsextension/string-resources.json
+++ b/New_Extensibility_Model/Samples/VSProjectQueryAPISample/.vsextension/string-resources.json
@@ -1,6 +1,6 @@
{
- "VSProjectQueryAPISample.CreateFileCommand.DisplayName": "Create File",
"VSProjectQueryAPISample.AddSolutionConfigurationCommand.DisplayName": "Add Solution Configuration",
+ "VSProjectQueryAPISample.CreateFileCommand.DisplayName": "Create File",
"VSProjectQueryAPISample.DeleteSolutionConfigurationCommand.DisplayName": "Delete Solution Configuration",
"VSProjectQueryAPISample.MoveFileCommand.DisplayName": "Move File",
"VSProjectQueryAPISample.ProjectBuildCommand.DisplayName": "Project Build",
@@ -17,6 +17,7 @@
"VSProjectQueryAPISample.SkipOfNCommand.DisplayName": "Query Project And Skip Over 1",
"VSProjectQueryAPISample.SolutionBuildCommand.DisplayName": "Solution Build",
"VSProjectQueryAPISample.SolutionSaveCommand.DisplayName": "Solution Save",
+ "VSProjectQueryAPISample.SubscribeQS.DisplayName": "Subscribe to Queryable Space Changed Events",
"VSProjectQueryAPISample.TrackProjectQueryCommand.DisplayName": "Track Project Query",
"VSProjectQueryAPISample.UnloadProjectCommand.DisplayName": "Unload Project"
}
diff --git a/New_Extensibility_Model/Samples/VSProjectQueryAPISample/ProjectQueryableSpaceEventCommand.cs b/New_Extensibility_Model/Samples/VSProjectQueryAPISample/ProjectQueryableSpaceEventCommand.cs
new file mode 100644
index 00000000..aa26a0ce
--- /dev/null
+++ b/New_Extensibility_Model/Samples/VSProjectQueryAPISample/ProjectQueryableSpaceEventCommand.cs
@@ -0,0 +1,58 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+namespace VSProjectQueryAPISample
+{
+ using System.Diagnostics;
+ using Microsoft;
+ using Microsoft.ServiceHub.Framework;
+ using Microsoft.VisualStudio.Extensibility;
+ using Microsoft.VisualStudio.Extensibility.Commands;
+ using Microsoft.VisualStudio.Extensibility.Shell;
+ using Microsoft.VisualStudio.ProjectSystem.Query;
+
+ [VisualStudioContribution]
+ internal class ProjectQueryableSpaceEventCommand : Command
+ {
+ private readonly TraceSource logger;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Trace source instance to utilize.
+ public ProjectQueryableSpaceEventCommand(TraceSource traceSource)
+ {
+ this.logger = Requires.NotNull(traceSource, nameof(traceSource));
+ }
+
+ ///
+ public override CommandConfiguration CommandConfiguration => new(displayName: "%VSProjectQueryAPISample.SubscribeQS.DisplayName%")
+ {
+ Placements = [CommandPlacement.KnownPlacements.ExtensionsMenu],
+ Icon = new(ImageMoniker.KnownValues.Extension, IconSettings.IconAndText),
+ };
+
+ ///
+ public override Task InitializeAsync(CancellationToken cancellationToken)
+ {
+ return base.InitializeAsync(cancellationToken);
+ }
+
+ ///
+ public override async Task ExecuteCommandAsync(IClientContext context, CancellationToken cancellationToken)
+ {
+ IServiceBroker serviceBroker = context.Extensibility.ServiceBroker;
+ ProjectQueryableSpace workspace = new(serviceBroker: serviceBroker, joinableTaskContext: null);
+ workspace.QueryableSpaceChanged += this.EventCalledAsync;
+
+ await this.Extensibility.Shell().ShowPromptAsync($"Project Queryable Space Changed Event subscribed.", PromptOptions.OK, CancellationToken.None);
+ }
+
+ private Task EventCalledAsync(ProjectQueryableSpaceChangedEventArgs e)
+ {
+ var solutionPath = e.SolutionPath;
+ var version = e.QueryableSpaceVersion;
+ return this.Extensibility.Shell().ShowPromptAsync($"Project queryable space changed. Solution path is \"{solutionPath}\", queryable space version is {version}", PromptOptions.OK, CancellationToken.None);
+ }
+ }
+}
diff --git a/New_Extensibility_Model/Samples/VSProjectQueryAPISample/VSProjectQueryAPISample.csproj b/New_Extensibility_Model/Samples/VSProjectQueryAPISample/VSProjectQueryAPISample.csproj
index 0476b667..de4f497f 100644
--- a/New_Extensibility_Model/Samples/VSProjectQueryAPISample/VSProjectQueryAPISample.csproj
+++ b/New_Extensibility_Model/Samples/VSProjectQueryAPISample/VSProjectQueryAPISample.csproj
@@ -8,7 +8,7 @@
-
-
+
+
diff --git a/New_Extensibility_Model/Samples/WordCountMargin/WordCountMarginSample.csproj b/New_Extensibility_Model/Samples/WordCountMargin/WordCountMarginSample.csproj
index 1cd1f3a9..4a5113b5 100644
--- a/New_Extensibility_Model/Samples/WordCountMargin/WordCountMarginSample.csproj
+++ b/New_Extensibility_Model/Samples/WordCountMargin/WordCountMarginSample.csproj
@@ -7,8 +7,8 @@
-
-
+
+
diff --git a/README.md b/README.md
index 001b1ea5..4e823995 100644
--- a/README.md
+++ b/README.md
@@ -98,10 +98,12 @@ You can find a Visual Studio solution that contains all samples at [Samples.sln]
| [Markdown linter](New_Extensibility_Model/Samples/MarkdownLinter) | Shows how multiple components can interact together inside an extension and how different areas of Visual Studio can be extended.|
| [Project Query](New_Extensibility_Model/Samples/VSProjectQueryAPISample) | Shows several different kinds of project system queries you can make. |
| [Taggers](New_Extensibility_Model/Samples/TaggersSample) | Shows how to create taggers to highlight portions of code or enable new Code Lenses. |
+| [Classification](New_Extensibility_Model/Samples/ClassificationSample) | Shows how to create classification taggers to color text in different colors based on syntax. |
| [Comment remover](New_Extensibility_Model/Samples/CommentRemover) | Shows how to consume [Visual Studio SDK](https://www.nuget.org/packages/Microsoft.VisualStudio.SDK) services through .NET dependency injection and use VisualStudio.Extensibility APIs for commands, prompts, and progress report.|
| [RegexMatchDebugVisualizer](New_Extensibility_Model/Samples/RegexMatchDebugVisualizer) | Shows how to use [Remote UI](https://learn.microsoft.com/visualstudio/extensibility/visualstudio.extensibility/inside-the-sdk/remote-ui) to create a [Debugger Visualizer](https://learn.microsoft.com/visualstudio/extensibility/visualstudio.extensibility/debugger-visualizer/debugger-visualizers) to visualize regular expression matches that will launch in a modal dialog window. |
| [MemoryStreamDebugVisualizer](New_Extensibility_Model/Samples/MemoryStreamDebugVisualizer) | Shows how to create a [Debugger Visualizer](https://learn.microsoft.com/visualstudio/extensibility/visualstudio.extensibility/debugger-visualizer/debugger-visualizers) to visualize MemoryStream objects that launches in a non-modal tool window. |
| [CompositeExtension](New_Extensibility_Model/Samples/CompositeExtension) | Shows how to create an extension with in-proc and out-of-proc components that communicate using brokered services. . |
+| [ExtensionWithTraditionalComponents](New_Extensibility_Model/Samples/ExtensionWithTraditionalComponents) | Shows how to create an out-of-proc extension that packages VSSDK components. |
## Experimental APIs and Breaking Changes
Starting with our 17.9 release, we're ready to label the vast majority of our APIs as stable. That is, we don't plan to make any breaking changes to these APIs. Any breaking changes that might need to be made, for example in response to user feedback about usability, will be communicated formally and with plenty of notice on our [breaking changes](./docs/breaking_changes.md) page.
diff --git a/docs/breaking_changes.md b/docs/breaking_changes.md
index 891f1951..55caf375 100644
--- a/docs/breaking_changes.md
+++ b/docs/breaking_changes.md
@@ -10,6 +10,69 @@ We work hard to minimize breaking changes between versions to help minimize disr
For more information how our policy and guidance towards breaking changes, please review [here](#Guidance-and-Expectations-Around-Breaking-Changes).
+# Breaking Changes for Visual Studio 2022 17.14
+The following breaking changes apply to Visual Studio 2022 17.14 and above.
+
+## VisualStudio.Extensibility
+These breaking changes are associated with VisualStudio.Extensibility
+
+### .NET Runtime
+Out of process VisualStudio.Extensibility extensions will need to update their target .NET version when the .NET runtime is updated in future releases of Visual Studio. Please read this blog for more details: https://devblogs.microsoft.com/visualstudio/visualstudio-extensibility-managing-net-runtime-versions/
+
+### Editor tracking span
+We are improving the Tracking Span API in VisualStudio.Extensibility by making it easier to understand. The following APIs have been marked obsolete in favor of better named ones. The old APIs have not been removed, but we encourage to adopt the new enums:
+- Microsoft.VisualStudio.Extensibility.Editor.TextRangeTrackingMode
+ - EdgeExclusive >> ExtendNone
+ - EdgeInclusive >> ExtendForwardAndBackward
+ - EdgePositive >> ExtendForward
+ - EdgeNegative >> ExtendBackward
+- Microsoft.VisualStudio.Extensibility.Editor.TextPositionTrackingMode
+ - Positive >> Forward
+ - Negative >> Backward
+
+### Setting ID validation
+We are adding validation of settings ID when building VisualStudio.Extensibility extensions.
+
+For example, this is a valid setting
+```csharp
+public static SettingCategory MySettingCategory => new("settingsSample", "Settings Sample")
+```
+
+The following are not valid
+```csharp
+public static SettingCategory MySettingCategory => new("SettingsSample", "Settings Sample")
+public static SettingCategory MySettingCategory => new("0SettingsSample", "Settings Sample")
+public static SettingCategory MySettingCategory => new("a", "Settings Sample")
+```
+The most likely scenario to be impacted is a working extension that uses a capitalized identifiers (like "SettingsSample"). Since setting identifiers are not case sensitive, you can simply change your ID to start with a lower case letter (like "settingsSample").
+
+### Command icon behavior change for IconSettings.None property
+An [issue](https://github.com/microsoft/VSExtensibility/issues/476) reported to our GitHub repo exposed a bug in that commands can't be placed in a toolbar as icon only. Upon investigation, we discovered that None was assigned a VSCT option when it shouldn't have. "None" option indicates "no command configuration (flag) should be applied".
+
+#### Current Behavior
+Commands with icons configured with IconSettings.None display with only their display name visible when parented to both menus and toolbars.
+
+
+
+Menu Placement
+
+
+
+
+Toolbar Placement
+
+#### New Behavior
+Commands with icons configured with IconSettings.None display with their display name and icon visible when parented to menus, and with only their icon visible when parented to toolbars.
+
+
+
+Menu Placement
+
+
+
+
+Toolbar Placement
+
# Breaking Changes for Visual Studio 2022 17.12
The following breaking changes apply to Visual Studio 2022 17.12.
diff --git a/docs/menu-placement.png b/docs/menu-placement.png
new file mode 100644
index 00000000..134e585f
Binary files /dev/null and b/docs/menu-placement.png differ
diff --git a/docs/toolbar-placement-after.png b/docs/toolbar-placement-after.png
new file mode 100644
index 00000000..958f4fad
Binary files /dev/null and b/docs/toolbar-placement-after.png differ
diff --git a/docs/toolbar-placement-before.png b/docs/toolbar-placement-before.png
new file mode 100644
index 00000000..f87c0fd6
Binary files /dev/null and b/docs/toolbar-placement-before.png differ