* Add a galley extension sample

---------

Co-authored-by: Matteo Prosperi <maprospe@microsoft.com>
This commit is contained in:
Matteo Prosperi 2023-11-17 08:30:42 -08:00 коммит произвёл GitHub
Родитель 3c73510eed
Коммит f24073093f
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
25 изменённых файлов: 1057 добавлений и 0 удалений

Просмотреть файл

@ -0,0 +1,4 @@
{
"FeatureGallery.MainToolWindowCommand.DisplayName": "VisualStudio.Extensibility Feature Gallery",
"FeatureGallery.LanguageServer.TodoLanguageServerProvider.DisplayName": "TODO language server"
}

Просмотреть файл

@ -0,0 +1,60 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#if INPROC
namespace InProcFeatureGallery;
#else
namespace FeatureGallery;
#endif
using System;
using System.Runtime.Serialization;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.VisualStudio.Extensibility;
using System.IO;
using Microsoft.VisualStudio.RpcContracts.Documents;
using Microsoft.VisualStudio.Extensibility.Shell;
[DataContract]
internal class DocumentEditingTest : TestData
{
public DocumentEditingTest(VisualStudioExtensibility extensibility)
: base(extensibility)
{
}
[DataMember]
public override string ButtonText => "Edit a document";
[DataMember]
public override string Description => "This command opens a new file and writes a random GUID in it. A prompt will ask to close the file.";
protected override async Task RunAsync(IClientContext clientContext, CancellationToken cancellationToken)
{
var filePath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName() + ".md");
File.Create(filePath).Close();
bool keepFile = false;
try
{
var textDocumentSnapshot = await this.Extensibility.Documents().OpenTextDocumentAsync(new(filePath), cancellationToken);
await this.Extensibility.Editor().EditAsync(
batch => textDocumentSnapshot.AsEditable(batch).Insert(0, Guid.NewGuid().ToString()),
cancellationToken);
if (!await this.Extensibility.Shell().ShowPromptAsync("Press OK to close and delete the file.", PromptOptions.OKCancel, cancellationToken))
{
keepFile = true;
}
}
finally
{
if (!keepFile)
{
await this.Extensibility.Documents().CloseDocumentAsync(new(filePath), SaveDocumentOption.NoSave, cancellationToken);
File.Delete(filePath);
}
}
}
}

Просмотреть файл

@ -0,0 +1,78 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#if INPROC
namespace InProcFeatureGallery;
#else
namespace FeatureGallery;
#endif
using System.IO;
using System.Runtime.Serialization;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.VisualStudio.Extensibility;
using Microsoft.VisualStudio.Extensibility.Editor;
using Microsoft.VisualStudio.Extensibility.Shell;
using Microsoft.VisualStudio.RpcContracts.Documents;
[DataContract]
internal class EditorMarginTest : TestData
{
#if INPROC
public const string FileExtension = "mywords";
#else
public const string FileExtension = "words";
#endif
public EditorMarginTest(VisualStudioExtensibility extensibility)
: base(extensibility)
{
}
[VisualStudioContribution]
public static DocumentTypeConfiguration WordsDocumentType => new(FileExtension)
{
FileExtensions = new[] { "." + FileExtension },
BaseDocumentType = DocumentType.KnownValues.Text,
};
[DataMember]
public override string ButtonText => "Editor margin";
[DataMember]
public override string Description => $"This command opens a text document with a custom editor margin counting the words in the document. A prompt will ask to close the file.";
protected override async Task RunAsync(IClientContext clientContext, CancellationToken cancellationToken)
{
var filePath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName() + "." + FileExtension);
bool keepFile = false;
using var file = new StreamWriter(File.Create(filePath));
try
{
await file.WriteAsync("""
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Ut enim ad minim veniam, quis knostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute
irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat
cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
""");
file.Close();
var textDocumentSnapshot = await this.Extensibility.Documents().OpenTextDocumentAsync(new(filePath), cancellationToken);
if (!await this.Extensibility.Shell().ShowPromptAsync("Press OK to close and delete the file.", PromptOptions.OKCancel, cancellationToken))
{
keepFile = true;
}
}
finally
{
if (!keepFile)
{
await this.Extensibility.Documents().CloseDocumentAsync(new(filePath), SaveDocumentOption.NoSave, cancellationToken);
File.Delete(filePath);
}
}
}
}

Просмотреть файл

@ -0,0 +1,27 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#if INPROC
namespace InProcFeatureGallery.EditorMargin;
#else
namespace FeatureGallery.EditorMargin;
#endif
using Microsoft.VisualStudio.Extensibility.UI;
/// <summary>
/// A sample remote user control to use as the margin content.
/// </summary>
internal class WordCountControl : RemoteUserControl
{
/// <summary>
/// Initializes a new instance of the <see cref="WordCountControl" /> class.
/// </summary>
/// <param name="dataContext">
/// Data context of the remote control which can be referenced from xaml through data binding.
/// </param>
public WordCountControl(object? dataContext)
: base(dataContext)
{
}
}

Просмотреть файл

@ -0,0 +1,10 @@
<DataTemplate xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vs="http://schemas.microsoft.com/visualstudio/extensibility/2022/xaml">
<DockPanel Background="LightSkyBlue" TextBlock.FontSize="11" Height="16">
<StackPanel Orientation="Horizontal">
<TextBlock Padding="2, 0, 2, 0">Words: </TextBlock>
<TextBlock x:Name="WordCountText" Text="{Binding WordCount}"/>
</StackPanel>
</DockPanel>
</DataTemplate>

Просмотреть файл

@ -0,0 +1,24 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#if INPROC
namespace InProcFeatureGallery.EditorMargin;
#else
namespace FeatureGallery.EditorMargin;
#endif
using System.Runtime.Serialization;
using Microsoft.VisualStudio.Extensibility.UI;
[DataContract]
public class WordCountData : NotifyPropertyChangedObject
{
private int wordCount;
[DataMember]
public int WordCount
{
get => this.wordCount;
set => this.SetProperty(ref this.wordCount, value);
}
}

Просмотреть файл

@ -0,0 +1,91 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#if INPROC
namespace InProcFeatureGallery.EditorMargin;
#else
namespace FeatureGallery.EditorMargin;
#endif
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.VisualStudio.Extensibility;
using Microsoft.VisualStudio.Extensibility.Editor;
using Microsoft.VisualStudio.RpcContracts.RemoteUI;
/// <summary>
/// A sample text view margin provider, which adds a margin to the Visual Studio editor status bar, to the left
/// of the built-in line number margin, indicating number of words in the current text document.
/// </summary>
[VisualStudioContribution]
internal class WordCountMarginProvider : ExtensionPart, ITextViewMarginProvider, ITextViewOpenClosedListener, ITextViewChangedListener
{
private readonly Dictionary<Uri, WordCountData> dataModels = new();
/// <summary>
/// Configures this extension part to be applied to any text view.
/// </summary>
public TextViewExtensionConfiguration TextViewExtensionConfiguration => new()
{
AppliesTo = new[]
{
DocumentFilter.FromDocumentType(EditorMarginTest.WordsDocumentType),
},
};
/// <summary>
/// Configures the margin to be placed to the left of built-in Visual Studio line number margin.
/// </summary>
public TextViewMarginProviderConfiguration TextViewMarginProviderConfiguration =>
new(marginContainer: ContainerMarginPlacement.KnownValues.BottomRightCorner)
{
Before = new[] { MarginPlacement.KnownValues.RowMargin },
};
/// <summary>
/// Creates a remotable visual element representing the content of the margin.
/// </summary>
public Task<IRemoteUserControl> CreateVisualElementAsync(ITextViewSnapshot textView, CancellationToken cancellationToken)
{
var dataModel = new WordCountData();
dataModel.WordCount = CountWords(textView.Document);
this.dataModels[textView.Uri] = dataModel;
return Task.FromResult<IRemoteUserControl>(new WordCountControl(dataModel));
}
/// <inheritdoc />
public Task TextViewChangedAsync(TextViewChangedArgs args, CancellationToken cancellationToken)
{
this.dataModels[args.AfterTextView.Uri].WordCount = CountWords(args.AfterTextView.Document);
return Task.CompletedTask;
}
/// <inheritdoc />
public Task TextViewClosedAsync(ITextViewSnapshot textView, CancellationToken cancellationToken)
{
this.dataModels.Remove(textView.Uri);
return Task.CompletedTask;
}
/// <inheritdoc />
public Task TextViewOpenedAsync(ITextViewSnapshot textView, CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
private static int CountWords(ITextDocumentSnapshot documentSnapshot)
{
int wordCount = 0;
for (int i = 1; i < documentSnapshot.Length; i++)
{
if (char.IsWhiteSpace(documentSnapshot[i - 1]) && char.IsLetterOrDigit(documentSnapshot[i]))
{
wordCount++;
}
}
return wordCount;
}
}

Просмотреть файл

@ -0,0 +1,26 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0-windows8.0</TargetFramework>
<Nullable>enable</Nullable>
<LangVersion>11</LangVersion>
<NeutralLanguage>en-US</NeutralLanguage>
<NoWarn>$(NoWarn);CS1591;CA1812;CA1303;SA1600</NoWarn>
<!-- 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>
<ItemGroup>
<PackageReference Include="Microsoft.VisualStudio.Extensibility.Sdk" Version="17.9.52-preview-1" />
<PackageReference Include="Microsoft.VisualStudio.Extensibility.Build" Version="17.9.52-preview-1" />
<PackageReference Include="Microsoft.VisualStudio.LanguageServer.Protocol" Version="17.2.8" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="EditorMargin\WordCountControl.xaml" />
<EmbeddedResource Include="MainToolWindowControl.xaml" />
<EmbeddedResource Include="ModalDialog\ModalDialogControl.xaml" />
<Page Remove="MainToolWindowControl.xaml" />
<Page Remove="EditorMargin\WordCountControl.xaml" />
<Page Remove="ModalDialog\ModalDialogControl.xaml" />
</ItemGroup>
</Project>

Просмотреть файл

@ -0,0 +1,39 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#if INPROC
namespace InProcFeatureGallery;
#else
namespace FeatureGallery;
#endif
using Microsoft.Extensions.DependencyInjection;
using Microsoft.VisualStudio.Extensibility;
/// <summary>
/// Extension entry point for the ToolWindowSample.
/// </summary>
[VisualStudioContribution]
public class FeatureGalleryExtension : Extension
{
/// <inheritdoc/>
public override ExtensionConfiguration ExtensionConfiguration => new()
{
#if INPROC
RequiresInProcessHosting = true,
#else
Metadata = new(
id: "FeatureGalleryExtension.f83f7a5e-61aa-4bb6-8f25-1cd2764e0123",
version: this.ExtensionAssemblyVersion,
publisherName: "Microsoft",
displayName: "VisualStudio.Extensibility Feature Gallery",
description: "An extension demonstrating multiple Visual Studio extension points."),
#endif
};
/// <inheritdoc/>
protected override void InitializeServices(IServiceCollection serviceCollection)
{
base.InitializeServices(serviceCollection);
}
}

Просмотреть файл

@ -0,0 +1,112 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#if INPROC
namespace InProcFeatureGallery.LanguageServer;
#else
namespace FeatureGallery.LanguageServer;
#endif
using System;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
using Microsoft.VisualStudio.LanguageServer.Protocol;
using Newtonsoft.Json.Linq;
using StreamJsonRpc;
using System.Threading;
using System.Linq;
using Microsoft.ServiceHub.Resources;
internal class LanguageServer
{
private static readonly Regex TodoRegex = new(@"TODO:\s*(.+)$", RegexOptions.Compiled);
private readonly JsonRpc rpc;
public LanguageServer(JsonRpc rpc)
{
this.rpc = rpc;
}
[JsonRpcMethod(Methods.InitializeName)]
public InitializeResult Initialize(JToken arg)
{
return new()
{
Capabilities = new()
{
TextDocumentSync = new()
{
OpenClose = true,
Change = TextDocumentSyncKind.Full,
},
},
};
}
[JsonRpcMethod(Methods.TextDocumentDidOpenName, UseSingleObjectParameterDeserialization = true)]
public async Task OnTextDocumentOpenedAsync(DidOpenTextDocumentParams messageParams, CancellationToken cancellationToken)
{
await this.VerifyDocumentAsync(messageParams.TextDocument.Uri, messageParams.TextDocument.Text);
}
[JsonRpcMethod(Methods.TextDocumentDidChangeName, UseSingleObjectParameterDeserialization = true)]
public async Task OnTextDocumentChangedAsync(DidChangeTextDocumentParams messageParams, CancellationToken cancellationToken)
{
await this.VerifyDocumentAsync(messageParams.TextDocument.Uri, messageParams.ContentChanges.Single().Text);
}
[JsonRpcMethod(Methods.TextDocumentDidCloseName, UseSingleObjectParameterDeserialization = true)]
public async Task OnTextDocumentClosedAsync(DidCloseTextDocumentParams messageParams, CancellationToken cancellationToken)
{
PublishDiagnosticParams notificationParams = new() { Uri = messageParams.TextDocument.Uri, Diagnostics = Array.Empty<Diagnostic>() };
await this.rpc.NotifyWithParameterObjectAsync(Methods.TextDocumentPublishDiagnosticsName, notificationParams);
}
private async Task VerifyDocumentAsync(Uri uri, string text)
{
List<Diagnostic> diagnostics = new();
using StringReader reader = new(text);
string line;
for (int lineCounter = 0;
(line = await reader.ReadLineAsync()) != null;
lineCounter++)
{
var match = TodoRegex.Match(line);
if (match.Success)
{
var group = match.Groups[1];
diagnostics.Add(new()
{
Severity = DiagnosticSeverity.Error,
Code = "TODO",
#if INPROC
Source = "In-proc TODO Language Server",
#else
Source = "TODO Language Server",
#endif
Message = group.Value,
Range = new()
{
Start = new()
{
Line = lineCounter,
Character = group.Index,
},
End = new()
{
Line = lineCounter,
Character = group.Index + group.Length,
},
},
});
}
}
PublishDiagnosticParams notificationParams = new() { Uri = uri, Diagnostics = diagnostics.ToArray() };
await this.rpc.NotifyWithParameterObjectAsync(Methods.TextDocumentPublishDiagnosticsName, notificationParams);
}
}

Просмотреть файл

@ -0,0 +1,80 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#if INPROC
namespace InProcFeatureGallery;
#else
namespace FeatureGallery;
#endif
using System.Runtime.Serialization;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.VisualStudio.Extensibility;
using System.IO;
using Microsoft.VisualStudio.RpcContracts.Documents;
using Microsoft.VisualStudio.Extensibility.Shell;
using Microsoft.VisualStudio.Extensibility.Editor;
using Microsoft.VisualStudio.Extensibility.LanguageServer;
[DataContract]
internal class LanguageServerTest : TestData
{
#if INPROC
public const string FileExtension = "todo";
#else
public const string FileExtension = "todos";
#endif
public LanguageServerTest(VisualStudioExtensibility extensibility)
: base(extensibility)
{
}
[VisualStudioContribution]
#pragma warning disable VSEXTAPI0001 // This API is marked as Preview.
public static DocumentTypeConfiguration TodosDocumentType => new(FileExtension)
{
FileExtensions = new[] { "." + FileExtension },
BaseDocumentType = LanguageServerProvider.LanguageServerBaseDocumentType,
};
#pragma warning restore VSEXTAPI0001 // This API is marked as Preview.
[DataMember]
public override string ButtonText => "Language Server";
[DataMember]
public override string Description => $"This command opens a new file, warning diagnostics will be generated for each line containing `TODO:`. A prompt will ask to close the file.";
protected override async Task RunAsync(IClientContext clientContext, CancellationToken cancellationToken)
{
var filePath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName() + "." + FileExtension);
bool keepFile = false;
using var file = new StreamWriter(File.Create(filePath));
try
{
await file.WriteAsync("""
TODO: My first todo.
Some other text.
TODO: My second reminder.
""");
file.Close();
var textDocumentSnapshot = await this.Extensibility.Documents().OpenTextDocumentAsync(new(filePath), cancellationToken);
if (!await this.Extensibility.Shell().ShowPromptAsync("Press OK to close and delete the file.", PromptOptions.OKCancel, cancellationToken))
{
keepFile = true;
}
}
finally
{
if (!keepFile)
{
await this.Extensibility.Documents().CloseDocumentAsync(new(filePath), SaveDocumentOption.NoSave, cancellationToken);
File.Delete(filePath);
}
}
}
}

Просмотреть файл

@ -0,0 +1,51 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#if INPROC
namespace InProcFeatureGallery.LanguageServer;
#else
namespace FeatureGallery.LanguageServer;
using System;
#endif
using System.IO.Pipelines;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.VisualStudio.Extensibility;
using Microsoft.VisualStudio.Extensibility.LanguageServer;
using Nerdbank.Streams;
using StreamJsonRpc;
/// <inheritdoc/>
[VisualStudioContribution]
#pragma warning disable VSEXTAPI0001 // This API is marked as Preview.
internal class TodoLanguageServerProvider : LanguageServerProvider
{
/// <inheritdoc/>
public override LanguageServerProviderConfiguration LanguageServerProviderConfiguration => new(
#if INPROC
"%InProcFeatureGallery.LanguageServer.TodoLanguageServerProvider.DisplayName%",
#else
"%FeatureGallery.LanguageServer.TodoLanguageServerProvider.DisplayName%",
#endif
new[]
{
DocumentFilter.FromDocumentType(LanguageServerTest.TodosDocumentType),
});
/// <inheritdoc/>
public override Task<IDuplexPipe?> CreateServerConnectionAsync(CancellationToken cancellationToken)
{
var streams = FullDuplexStream.CreatePair();
JsonRpc rpc = new(streams.Item2);
rpc.AddLocalRpcTarget(new LanguageServer(rpc));
rpc.StartListening();
return Task.FromResult<IDuplexPipe?>(new DuplexPipe(
PipeReader.Create(streams.Item1),
PipeWriter.Create(streams.Item1)));
}
}
#pragma warning restore VSEXTAPI0001 // This API is marked as Preview.

Просмотреть файл

@ -0,0 +1,56 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#if INPROC
namespace InProcFeatureGallery;
#else
namespace FeatureGallery;
#endif
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.VisualStudio.Extensibility;
using Microsoft.VisualStudio.Extensibility.ToolWindows;
using Microsoft.VisualStudio.RpcContracts.RemoteUI;
/// <summary>
/// A sample tool window.
/// </summary>
[VisualStudioContribution]
public class MainToolWindow : ToolWindow
{
private readonly IReadOnlyList<TestData> tests;
/// <summary>
/// Initializes a new instance of the <see cref="MainToolWindow" /> class.
/// </summary>
public MainToolWindow()
{
#if INPROC
this.Title = "In-proc VisualStudio.Extensibility Feature Gallery";
#else
this.Title = "VisualStudio.Extensibility Feature Gallery";
#endif
this.tests = new TestData[]
{
new DocumentEditingTest(this.Extensibility),
new ModalDialogTest(this.Extensibility),
new EditorMarginTest(this.Extensibility),
new LanguageServerTest(this.Extensibility),
};
}
/// <inheritdoc />
public override ToolWindowConfiguration ToolWindowConfiguration => new()
{
Placement = ToolWindowPlacement.Floating,
AllowAutoCreation = true,
};
/// <inheritdoc />
public override Task<IRemoteUserControl> GetContentAsync(CancellationToken cancellationToken)
{
return Task.FromResult<IRemoteUserControl>(new MainToolWindowControl(this.tests));
}
}

Просмотреть файл

@ -0,0 +1,46 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#if INPROC
namespace InProcFeatureGallery;
#else
namespace FeatureGallery;
#endif
using System.Threading;
using System.Threading.Tasks;
using Microsoft.VisualStudio.Extensibility;
using Microsoft.VisualStudio.Extensibility.Commands;
/// <summary>
/// A sample command for showing a tool window.
/// </summary>
[VisualStudioContribution]
public class MainToolWindowCommand : Command
{
/// <summary>
/// Initializes a new instance of the <see cref="MainToolWindowCommand" /> class.
/// </summary>
public MainToolWindowCommand()
{
}
/// <inheritdoc />
public override CommandConfiguration CommandConfiguration => new(
#if INPROC
"%InProcFeatureGallery.MainToolWindowCommand.DisplayName%"
#else
"%FeatureGallery.MainToolWindowCommand.DisplayName%"
#endif
)
{
Placements = new[] { CommandPlacement.KnownPlacements.ExtensionsMenu },
Icon = new(ImageMoniker.KnownValues.Extension, IconSettings.IconAndText),
};
/// <inheritdoc />
public override async Task ExecuteCommandAsync(IClientContext context, CancellationToken cancellationToken)
{
await this.Extensibility.Shell().ShowToolWindowAsync<MainToolWindow>(activate: true, cancellationToken);
}
}

Просмотреть файл

@ -0,0 +1,27 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#if INPROC
namespace InProcFeatureGallery;
#else
namespace FeatureGallery;
#endif
using Microsoft.VisualStudio.Extensibility.UI;
/// <summary>
/// A sample remote user control to use as tool window UI content.
/// </summary>
internal class MainToolWindowControl : RemoteUserControl
{
/// <summary>
/// Initializes a new instance of the <see cref="MainToolWindowControl" /> class.
/// </summary>
/// <param name="dataContext">
/// Data context of the remote control which can be referenced from xaml through data binding.
/// </param>
public MainToolWindowControl(object? dataContext)
: base(dataContext)
{
}
}

Просмотреть файл

@ -0,0 +1,33 @@
<DataTemplate xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vs="http://schemas.microsoft.com/visualstudio/extensibility/2022/xaml"
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">
<Grid x:Name="RootGrid">
<Grid.Resources>
<Style TargetType="ListView" BasedOn="{StaticResource {x:Static styles:VsResourceKeys.ThemedDialogListViewStyleKey}}" />
<Style TargetType="Button" BasedOn="{StaticResource {x:Static styles:VsResourceKeys.ButtonStyleKey}}" />
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="{DynamicResource {x:Static styles:VsBrushes.WindowTextKey}}" />
</Style>
</Grid.Resources>
<ListView ItemsSource="{Binding .}" HorizontalContentAlignment="Stretch" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Button Content="{Binding ButtonText}" Command="{Binding Command}" IsEnabled="{Binding Command.RunningCommandsCount.IsZero}" />
<TextBlock Text="{Binding Description}" TextWrapping="Wrap" Grid.Row="1" Grid.ColumnSpan="2" />
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</DataTemplate>

Просмотреть файл

@ -0,0 +1,24 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#if INPROC
namespace InProcFeatureGallery.ModalDialog;
#else
namespace FeatureGallery.ModalDialog;
#endif
using Microsoft.VisualStudio.Extensibility.UI;
/// <summary>
/// A sample remote user control to use as tool window UI content.
/// </summary>
internal class ModalDialogControl : RemoteUserControl
{
/// <summary>
/// Initializes a new instance of the <see cref="ModalDialogControl" /> class.
/// </summary>
public ModalDialogControl()
: base(new ModalDialogData())
{
}
}

Просмотреть файл

@ -0,0 +1,45 @@
<DataTemplate xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vs="http://schemas.microsoft.com/visualstudio/extensibility/2022/xaml"
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">
<Grid x:Name="RootGrid">
<Grid.Resources>
<Style TargetType="ListView" BasedOn="{StaticResource {x:Static styles:VsResourceKeys.ThemedDialogListViewStyleKey}}" />
<Style TargetType="Button" BasedOn="{StaticResource {x:Static styles:VsResourceKeys.ButtonStyleKey}}" />
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="{DynamicResource {x:Static styles:VsBrushes.WindowTextKey}}" />
</Style>
</Grid.Resources>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ListView ItemsSource="{Binding Colors}" HorizontalContentAlignment="Stretch" MinWidth="200" MinHeight="400">
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding ColorText}" />
<Rectangle Fill="{Binding Color}" Width="50px" Grid.Column="1" />
<Button Content="Remove" Grid.Column="2" IsEnabled="{Binding Path=(vs:ExtensibilityUICommands.RunningCommandsCount).IsZero, RelativeSource={RelativeSource Self}}">
<vs:ExtensibilityUICommands.EventHandlers>
<vs:EventHandlerCollection>
<vs:EventHandler Event="Click"
Command="{Binding DataContext.RemoveColorCommand, ElementName=RootGrid}"
CommandParameter="{Binding}"
CounterTarget="{Binding RelativeSource={RelativeSource Self}}" />
</vs:EventHandlerCollection>
</vs:ExtensibilityUICommands.EventHandlers>
</Button>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Button Content="Add color" Command="{Binding AddColorCommand}" IsEnabled="{Binding AddColorCommand.RunningCommandsCount.IsZero}" Grid.Row="1" />
</Grid>
</DataTemplate>

Просмотреть файл

@ -0,0 +1,66 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#if INPROC
namespace InProcFeatureGallery.ModalDialog;
#else
namespace FeatureGallery.ModalDialog;
#endif
using System.Runtime.Serialization;
using System.Threading.Tasks;
using System;
using Microsoft.VisualStudio.Extensibility.UI;
[DataContract]
internal class ModalDialogData
{
private Random random = new();
public ModalDialogData()
{
this.AddColorCommand = new AsyncCommand((parameter, cancellationToken) =>
{
var color = new byte[3];
this.random.NextBytes(color);
this.Colors.Add(new MyColor(color[0], color[1], color[2]));
return Task.CompletedTask;
});
this.RemoveColorCommand = new AsyncCommand((parameter, cancellationToken) =>
{
this.Colors.Remove((MyColor)parameter!);
return Task.CompletedTask;
});
}
[DataMember]
public ObservableList<MyColor> Colors { get; } = new();
[DataMember]
public AsyncCommand AddColorCommand { get; }
[DataMember]
public AsyncCommand RemoveColorCommand { get; }
[DataContract]
public class MyColor
{
public MyColor(byte r, byte g, byte b)
{
this.ColorText = $"#{r:X2}{g:X2}{b:X2}";
this.Color = new($"""
<LinearGradientBrush xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" StartPoint="0,0" EndPoint="1,1">
<GradientStop Color="Black" Offset="0.0" />
<GradientStop Color="{this.ColorText}" Offset="0.7" />
</LinearGradientBrush>
""");
}
[DataMember]
public string ColorText { get; }
[DataMember]
public XamlFragment Color { get; }
}
}

Просмотреть файл

@ -0,0 +1,35 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#if INPROC
namespace InProcFeatureGallery;
#else
namespace FeatureGallery;
#endif
using System.Runtime.Serialization;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.VisualStudio.Extensibility;
[DataContract]
internal class ModalDialogTest : TestData
{
public ModalDialogTest(VisualStudioExtensibility extensibility)
: base(extensibility)
{
}
[DataMember]
public override string ButtonText => "Modal dialog";
[DataMember]
public override string Description => "This command opens a modal dialog where random colors can be added or removed from a list.";
protected override async Task RunAsync(IClientContext clientContext, CancellationToken cancellationToken)
{
#pragma warning disable CA2000 // Dispose objects before losing scope. ModalDialogControl is passed to Visual Studio which will take care of disposing it
await this.Extensibility.Shell().ShowDialogAsync(new ModalDialog.ModalDialogControl(), cancellationToken);
#pragma warning restore CA2000 // Dispose objects before losing scope
}
}

Просмотреть файл

@ -0,0 +1,38 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#if INPROC
namespace InProcFeatureGallery;
#else
namespace FeatureGallery;
#endif
using System.Runtime.Serialization;
using System.Threading;
using System.Threading.Tasks;
using Microsoft;
using Microsoft.VisualStudio.Extensibility;
using Microsoft.VisualStudio.Extensibility.UI;
[DataContract]
internal abstract class TestData
{
public TestData(VisualStudioExtensibility extensibility)
{
this.Extensibility = extensibility;
this.Command = new AsyncCommand((_, cc, ct) => this.RunAsync(cc, ct));
}
[DataMember]
public abstract string ButtonText { get; }
[DataMember]
public abstract string Description { get; }
[DataMember]
public IAsyncCommand Command { get; }
protected VisualStudioExtensibility Extensibility { get; }
protected abstract Task RunAsync(IClientContext clientContext, CancellationToken cancellationToken);
}

Просмотреть файл

@ -0,0 +1,4 @@
{
"InProcFeatureGallery.MainToolWindowCommand.DisplayName": "In-proc VisualStudio.Extensibility Feature Gallery",
"InProcFeatureGallery.LanguageServer.TodoLanguageServerProvider.DisplayName": "In-proc TODO language server"
}

Просмотреть файл

@ -0,0 +1,35 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net472</TargetFramework>
<Nullable>enable</Nullable>
<LangVersion>11</LangVersion>
<NeutralLanguage>en-US</NeutralLanguage>
<NoWarn>$(NoWarn);CS1591;CA1812;CA1303;SA1600</NoWarn>
<!-- 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>
<VssdkCompatibleExtension>true</VssdkCompatibleExtension>
<DefineConstants>$(DefineConstants);INPROC</DefineConstants>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.VisualStudio.Extensibility.Sdk" Version="17.9.52-preview-1" />
<PackageReference Include="Microsoft.VisualStudio.Extensibility.Build" Version="17.9.52-preview-1" />
<PackageReference Include="Microsoft.VisualStudio.LanguageServer.Protocol" Version="17.2.8" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="..\FeatureGallery\MainToolWindowControl.xaml" />
<EmbeddedResource Include="..\FeatureGallery\EditorMargin\WordCountControl.xaml" Link="EditorMargin\WordCountControl.xaml" />
<EmbeddedResource Include="..\FeatureGallery\ModalDialog\ModalDialogControl.xaml" Link="ModalDialog\ModalDialogControl.xaml" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\FeatureGallery\**\*.cs" />
</ItemGroup>
<ItemGroup>
<Content Include=".vsextension\string-resources.json" />
</ItemGroup>
</Project>

Просмотреть файл

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<PackageManifest Version="2.0.0" xmlns="http://schemas.microsoft.com/developer/vsx-schema/2011" xmlns:d="http://schemas.microsoft.com/developer/vsx-schema-design/2011">
<Metadata>
<Identity Id="InProcFeatureGalleryExtension.8856bea4-d1fd-4b49-9f3a-c99ea5f66e57" Version="1.0" Language="en-US" Publisher="Microsoft" />
<DisplayName>In-proc VisualStudio.Extensibility Feature Gallery</DisplayName>
<Description xml:space="preserve">An extension demonstrating multiple Visual Studio extension points.</Description>
</Metadata>
<Installation ExtensionType="VSSDK+VisualStudio.Extensibility">
<InstallationTarget Id="Microsoft.VisualStudio.Community" Version="[17.9,18.0)">
<ProductArchitecture>amd64</ProductArchitecture>
</InstallationTarget>
<InstallationTarget Id="Microsoft.VisualStudio.Community" Version="[17.9,18.0)">
<ProductArchitecture>arm64</ProductArchitecture>
</InstallationTarget>
</Installation>
<Prerequisites>
<Prerequisite Id="Microsoft.VisualStudio.Component.CoreEditor" Version="[17.0,)" DisplayName="Visual Studio core editor" />
</Prerequisites>
<Assets>
<Asset Type="Microsoft.VisualStudio.VsPackage" d:Source="Project" d:ProjectName="%CurrentProject%" Path="|%CurrentProject%;PkgdefProjectOutputGroup|" />
</Assets>
</PackageManifest>

Просмотреть файл

@ -47,6 +47,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CommentRemover", "CommentRe
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RustLanguageServerProvider", "RustLanguageServerProvider\RustLanguageServerProvider.csproj", "{99684185-2C81-45B3-A69F-0852AD0E4F9E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FeatureGallery", "FeatureGallery\FeatureGallery.csproj", "{7411CD7E-B825-4E7A-91A9-99A65EEE4073}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "InProcFeatureGallery", "InProcFeatureGallery\InProcFeatureGallery.csproj", "{713DB53E-4099-42FF-B6D9-ACA84F6136D9}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -199,6 +203,26 @@ Global
{99684185-2C81-45B3-A69F-0852AD0E4F9E}.Release|x86.ActiveCfg = Release|Any CPU
{99684185-2C81-45B3-A69F-0852AD0E4F9E}.Release|x86.Build.0 = Release|Any CPU
{99684185-2C81-45B3-A69F-0852AD0E4F9E}.Release|x86.Deploy.0 = Release|Any CPU
{7411CD7E-B825-4E7A-91A9-99A65EEE4073}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7411CD7E-B825-4E7A-91A9-99A65EEE4073}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7411CD7E-B825-4E7A-91A9-99A65EEE4073}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
{7411CD7E-B825-4E7A-91A9-99A65EEE4073}.Debug|x86.ActiveCfg = Debug|Any CPU
{7411CD7E-B825-4E7A-91A9-99A65EEE4073}.Debug|x86.Build.0 = Debug|Any CPU
{7411CD7E-B825-4E7A-91A9-99A65EEE4073}.Debug|x86.Deploy.0 = Debug|Any CPU
{7411CD7E-B825-4E7A-91A9-99A65EEE4073}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7411CD7E-B825-4E7A-91A9-99A65EEE4073}.Release|Any CPU.Build.0 = Release|Any CPU
{7411CD7E-B825-4E7A-91A9-99A65EEE4073}.Release|Any CPU.Deploy.0 = Release|Any CPU
{7411CD7E-B825-4E7A-91A9-99A65EEE4073}.Release|x86.ActiveCfg = Release|Any CPU
{7411CD7E-B825-4E7A-91A9-99A65EEE4073}.Release|x86.Build.0 = Release|Any CPU
{7411CD7E-B825-4E7A-91A9-99A65EEE4073}.Release|x86.Deploy.0 = Release|Any CPU
{713DB53E-4099-42FF-B6D9-ACA84F6136D9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{713DB53E-4099-42FF-B6D9-ACA84F6136D9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{713DB53E-4099-42FF-B6D9-ACA84F6136D9}.Debug|x86.ActiveCfg = Debug|Any CPU
{713DB53E-4099-42FF-B6D9-ACA84F6136D9}.Debug|x86.Build.0 = Debug|Any CPU
{713DB53E-4099-42FF-B6D9-ACA84F6136D9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{713DB53E-4099-42FF-B6D9-ACA84F6136D9}.Release|Any CPU.Build.0 = Release|Any CPU
{713DB53E-4099-42FF-B6D9-ACA84F6136D9}.Release|x86.ActiveCfg = Release|Any CPU
{713DB53E-4099-42FF-B6D9-ACA84F6136D9}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE