Skip to content

Commit

Permalink
Add a galley extension sample
Browse files Browse the repository at this point in the history
  • Loading branch information
Matteo Prosperi committed Nov 16, 2023
1 parent 3c73510 commit 21e1c01
Show file tree
Hide file tree
Showing 19 changed files with 675 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"FeatureGallery.MainToolWindowCommand.DisplayName": "VisualStudio.Extensibility Feature Gallery"
}
Original file line number Diff line number Diff line change
@@ -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.

namespace FeatureGallery;

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);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -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.

namespace FeatureGallery;

using System;
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
{
public EditorMarginTest(VisualStudioExtensibility extensibility)
: base(extensibility)
{
}

[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() + ".txt");
bool keepFile = false;

using var file = new StreamWriter(File.Create(filePath));
try
{
await file.WriteAsync("""
using System;
public class Class1
{
public Class1()
{
}
}
""");
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);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace FeatureGallery.EditorMargin;

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)
{
}
}
Original file line number Diff line number Diff line change
@@ -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="Lime" 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>
Original file line number Diff line number Diff line change
@@ -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 FeatureGallery.EditorMargin;

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);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace FeatureGallery.EditorMargin;

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(DocumentType.KnownValues.Text),
},
};

/// <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;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<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>
<None Remove="EditorMargin\WordCountControl.xaml" />
<None Remove="ModalDialog\ModalDialogControl.xaml" />
</ItemGroup>

<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" />
</ItemGroup>

<ItemGroup>
<EmbeddedResource Include="EditorMargin\WordCountControl.xaml">
<Generator>MSBuild:Compile</Generator>
</EmbeddedResource>
<EmbeddedResource Include="MainToolWindowControl.xaml" />
<EmbeddedResource Include="ModalDialog\ModalDialogControl.xaml">
<Generator>MSBuild:Compile</Generator>
</EmbeddedResource>
<Page Remove="MainToolWindowControl.xaml" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace FeatureGallery;

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()
{
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."),
};

/// <inheritdoc/>
protected override void InitializeServices(IServiceCollection serviceCollection)
{
base.InitializeServices(serviceCollection);
}
}
48 changes: 48 additions & 0 deletions New_Extensibility_Model/Samples/FeatureGallery/MainToolWindow.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace FeatureGallery;

using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Documents;
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()
{
this.Title = "VisualStudio.Extensibility Feature Gallery";
this.tests = new TestData[]
{
new DocumentEditingTest(this.Extensibility),
new ModalDialogTest(this.Extensibility),
new EditorMarginTest(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));
}
}
Loading

0 comments on commit 21e1c01

Please sign in to comment.