From df6263ae3629f516a2122fc0cd9e39f09431661b Mon Sep 17 00:00:00 2001 From: Maia Kelner Date: Sun, 7 Aug 2022 15:33:06 -0700 Subject: [PATCH] Add docs and a sample for the output window API. --- .../OutputWindowSample.csproj | 33 +++++ .../OutputWindowSampleExtension.cs | 16 +++ .../Samples/OutputWindowSample/README.md | 41 ++++++ .../OutputWindowSample/Strings.Designer.cs | 72 ++++++++++ .../Samples/OutputWindowSample/Strings.resx | 123 ++++++++++++++++++ .../TestOutputWindowCommand.cs | 58 +++++++++ New_Extensibility_Model/Samples/Samples.sln | 22 ++-- .../outputWindow/outputWindow.md | 113 ++++++++++++++++ 8 files changed, 470 insertions(+), 8 deletions(-) create mode 100644 New_Extensibility_Model/Samples/OutputWindowSample/OutputWindowSample.csproj create mode 100644 New_Extensibility_Model/Samples/OutputWindowSample/OutputWindowSampleExtension.cs create mode 100644 New_Extensibility_Model/Samples/OutputWindowSample/README.md create mode 100644 New_Extensibility_Model/Samples/OutputWindowSample/Strings.Designer.cs create mode 100644 New_Extensibility_Model/Samples/OutputWindowSample/Strings.resx create mode 100644 New_Extensibility_Model/Samples/OutputWindowSample/TestOutputWindowCommand.cs create mode 100644 docs/new-extensibility-model/extension-guides/outputWindow/outputWindow.md diff --git a/New_Extensibility_Model/Samples/OutputWindowSample/OutputWindowSample.csproj b/New_Extensibility_Model/Samples/OutputWindowSample/OutputWindowSample.csproj new file mode 100644 index 0000000..29e40c0 --- /dev/null +++ b/New_Extensibility_Model/Samples/OutputWindowSample/OutputWindowSample.csproj @@ -0,0 +1,33 @@ + + + net6.0-windows + enable + 10 + true + en-US + $(NoWarn);CS1591;IDE0008;CA1812 + + + https://pkgs.dev.azure.com/azure-public/vside/_packaging/vssdk/nuget/v3/index.json;$(RestoreAdditionalProjectSources) + + + + + + + + + + True + True + Strings.resx + + + + + + ResXFileCodeGenerator + Strings.Designer.cs + + + diff --git a/New_Extensibility_Model/Samples/OutputWindowSample/OutputWindowSampleExtension.cs b/New_Extensibility_Model/Samples/OutputWindowSample/OutputWindowSampleExtension.cs new file mode 100644 index 0000000..920d10e --- /dev/null +++ b/New_Extensibility_Model/Samples/OutputWindowSample/OutputWindowSampleExtension.cs @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace OutputWindowSample; + +using System.Resources; +using Microsoft.VisualStudio.Extensibility; + +/// +/// Extension entry point for the OutputWindowSampleExtension. +/// +public class OutputWindowSampleExtension : Extension +{ + /// + protected override ResourceManager? ResourceManager => Strings.ResourceManager; +} diff --git a/New_Extensibility_Model/Samples/OutputWindowSample/README.md b/New_Extensibility_Model/Samples/OutputWindowSample/README.md new file mode 100644 index 0000000..ce14868 --- /dev/null +++ b/New_Extensibility_Model/Samples/OutputWindowSample/README.md @@ -0,0 +1,41 @@ +--- +title: Output Window Sample Extension reference +description: A reference for Output Window Sample extension +date: 2022-08-01 +--- + +# Walkthrough: Output Window Sample Extension + +This extension demonstrates the most basic usage of the [Output Window API](./../../../docs/new-extensibility-model/extension-guides/outputWindow/outputWindow.md) + +## Summary + +This extension adds a [command](./../../../docs/new-extensibility-model/extension-guides/command/command.md) to the Tools menu called "Test the Output Window". When invoked, the command will print `"This is a test of the output window."` to the Output pane in a Channel called "MyOutputWindow" (look for the Channel name in the "Show output from:" dropdown in the Output pane). + +## TestOutputWindowCommand + +[`TestOutputWindowCommand.cs`](TestOutputWindowCommand.cs) contains a command that demonstrates two parts of the Output Window API. + +### Getting an Output Window Channel + +The `GetOutputWindowAsync()` method creates an Output Window Channel by calling `Extensibility.Views().Output.GetChannelAsync()`. `GetChannelAsync()` takes the name of a resource from the ResourceManager set up in the [`Extension` class](#extension-class). + +See [Getting an Output Window Channel](./../../../docs/new-extensibility-model/extension-guides/outputWindow/outputWindow.md#getting-an-output-window-channel) for more information. + +### Writing to the Output Window + +In the `ExecuteCommandAsync()` method, the `Writer` property of the `OutputWindow` instance is used to write a message to the Output pane in the IDE. + +See [Writing to the Output Window](./../../../docs/new-extensibility-model/extension-guides/outputWindow/outputWindow.md#writing-to-the-output-window) for more information. + +## Extension Class + +As described in the [Output Window Display Name Resource ID requirements](./../../../docs/new-extensibility-model/extension-guides/outputWindow/outputWindow.md#display-name-resource-id-requirements), the current version of the Output Window API requires that the display name for the Output Window Channel be stored in a [Resource File](https://docs.microsoft.com/en-us/dotnet/core/extensions/resources) (such as a [`.resx`](https://docs.microsoft.com/en-us/dotnet/core/extensions/resources) resource file.) + +In order for the call to `OutputExtensibility.GetChannelAsync()` to find the diaplay name, the resource file must be associated with the [`Extension` Instance](../../inside-the-sdk/extension-anatomy.md#extension-instance) by overriding the `ResourceManager` property. + +[`OutputWindowSampleExtension.cs`](OutputWindowSampleExtension.cs) demonstrates this functionality. + +## Resources File + +The [`Strings.resx`](Strings.resx) file is a [Resource File](https://docs.microsoft.com/en-us/dotnet/core/extensions/resources) that contains the display name of the Output Window Channel for this extension. \ No newline at end of file diff --git a/New_Extensibility_Model/Samples/OutputWindowSample/Strings.Designer.cs b/New_Extensibility_Model/Samples/OutputWindowSample/Strings.Designer.cs new file mode 100644 index 0000000..e8d8f7e --- /dev/null +++ b/New_Extensibility_Model/Samples/OutputWindowSample/Strings.Designer.cs @@ -0,0 +1,72 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace OutputWindowSample { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Strings { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Strings() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("OutputWindowSample.Strings", typeof(Strings).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to My Output Window. + /// + internal static string OutputWindowDisplayName { + get { + return ResourceManager.GetString("OutputWindowDisplayName", resourceCulture); + } + } + } +} diff --git a/New_Extensibility_Model/Samples/OutputWindowSample/Strings.resx b/New_Extensibility_Model/Samples/OutputWindowSample/Strings.resx new file mode 100644 index 0000000..9ed973d --- /dev/null +++ b/New_Extensibility_Model/Samples/OutputWindowSample/Strings.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + My Output Window + + \ No newline at end of file diff --git a/New_Extensibility_Model/Samples/OutputWindowSample/TestOutputWindowCommand.cs b/New_Extensibility_Model/Samples/OutputWindowSample/TestOutputWindowCommand.cs new file mode 100644 index 0000000..17d1488 --- /dev/null +++ b/New_Extensibility_Model/Samples/OutputWindowSample/TestOutputWindowCommand.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 OutputWindowSample; + +using System.Threading; +using System.Threading.Tasks; +using Microsoft.VisualStudio.Extensibility; +using Microsoft.VisualStudio.Extensibility.Commands; +using Microsoft.VisualStudio.Extensibility.Definitions; +using Microsoft.VisualStudio.Extensibility.Documents; + +/// +/// A sample command for displaying a test message in the output window. +/// +[Command("TestOutputWindowCommand", "Test the Output Window", placement: CommandPlacement.ToolsMenu)] +[CommandIcon(KnownMonikers.ToolWindow, IconSettings.IconAndText)] +public class TestOutputWindowCommand : Command +{ + private OutputWindow? outputWindow; + + /// + /// Initializes a new instance of the class. + /// + /// + /// Extensibility object instance. + /// + /// + /// Command identifier. + /// + public TestOutputWindowCommand(VisualStudioExtensibility extensibility, string id) + : base(extensibility, id) + { + } + + /// + public override async Task InitializeAsync(CancellationToken cancellationToken) + { + this.outputWindow = await this.GetOutputWindowAsync(cancellationToken); + await base.InitializeAsync(cancellationToken); + } + + /// + public override async Task ExecuteCommandAsync(IClientContext context, CancellationToken cancellationToken) + { + if (this.outputWindow != null) + { + await this.outputWindow.Writer.WriteLineAsync("This is a test of the output window."); + } + } + + private async Task GetOutputWindowAsync(CancellationToken cancellationToken) + { + string id = "MyOutputWindow"; + string displayNameResourceId = nameof(Strings.OutputWindowDisplayName); + return await this.Extensibility.Views().Output.GetChannelAsync(id, displayNameResourceId, cancellationToken); + } +} diff --git a/New_Extensibility_Model/Samples/Samples.sln b/New_Extensibility_Model/Samples/Samples.sln index 2c4db33..57bb44c 100644 --- a/New_Extensibility_Model/Samples/Samples.sln +++ b/New_Extensibility_Model/Samples/Samples.sln @@ -23,20 +23,14 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ToolWindowExtension", "Tool EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UserPromptSample", "UserPromptSample\UserPromptSample.csproj", "{6AAB3137-2974-45F1-A426-E28231F6FE13}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OutputWindowSample", "OutputWindowSample\OutputWindowSample.csproj", "{053DB39C-56F5-41DC-9C85-EA14FE3F49B7}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {11EB62B3-79FD-450D-BC86-6FF414178B7E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {11EB62B3-79FD-450D-BC86-6FF414178B7E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {11EB62B3-79FD-450D-BC86-6FF414178B7E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {11EB62B3-79FD-450D-BC86-6FF414178B7E}.Release|Any CPU.Build.0 = Release|Any CPU - {F005B03E-66DC-4145-8BBA-9E21ABE582C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F005B03E-66DC-4145-8BBA-9E21ABE582C2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F005B03E-66DC-4145-8BBA-9E21ABE582C2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F005B03E-66DC-4145-8BBA-9E21ABE582C2}.Release|Any CPU.Build.0 = Release|Any CPU {26601396-0E9A-4B38-8FE9-D5107B77D445}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {26601396-0E9A-4B38-8FE9-D5107B77D445}.Debug|Any CPU.Build.0 = Debug|Any CPU {26601396-0E9A-4B38-8FE9-D5107B77D445}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -49,6 +43,14 @@ Global {6D16A4D7-4CF1-4162-9538-06F2D8499821}.Debug|Any CPU.Build.0 = Debug|Any CPU {6D16A4D7-4CF1-4162-9538-06F2D8499821}.Release|Any CPU.ActiveCfg = Release|Any CPU {6D16A4D7-4CF1-4162-9538-06F2D8499821}.Release|Any CPU.Build.0 = Release|Any CPU + {F005B03E-66DC-4145-8BBA-9E21ABE582C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F005B03E-66DC-4145-8BBA-9E21ABE582C2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F005B03E-66DC-4145-8BBA-9E21ABE582C2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F005B03E-66DC-4145-8BBA-9E21ABE582C2}.Release|Any CPU.Build.0 = Release|Any CPU + {11EB62B3-79FD-450D-BC86-6FF414178B7E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {11EB62B3-79FD-450D-BC86-6FF414178B7E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {11EB62B3-79FD-450D-BC86-6FF414178B7E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {11EB62B3-79FD-450D-BC86-6FF414178B7E}.Release|Any CPU.Build.0 = Release|Any CPU {4605F69C-8179-409E-A3FA-431DD41F35CB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4605F69C-8179-409E-A3FA-431DD41F35CB}.Debug|Any CPU.Build.0 = Debug|Any CPU {4605F69C-8179-409E-A3FA-431DD41F35CB}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -57,6 +59,10 @@ Global {6AAB3137-2974-45F1-A426-E28231F6FE13}.Debug|Any CPU.Build.0 = Debug|Any CPU {6AAB3137-2974-45F1-A426-E28231F6FE13}.Release|Any CPU.ActiveCfg = Release|Any CPU {6AAB3137-2974-45F1-A426-E28231F6FE13}.Release|Any CPU.Build.0 = Release|Any CPU + {053DB39C-56F5-41DC-9C85-EA14FE3F49B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {053DB39C-56F5-41DC-9C85-EA14FE3F49B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {053DB39C-56F5-41DC-9C85-EA14FE3F49B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {053DB39C-56F5-41DC-9C85-EA14FE3F49B7}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/docs/new-extensibility-model/extension-guides/outputWindow/outputWindow.md b/docs/new-extensibility-model/extension-guides/outputWindow/outputWindow.md new file mode 100644 index 0000000..43b759b --- /dev/null +++ b/docs/new-extensibility-model/extension-guides/outputWindow/outputWindow.md @@ -0,0 +1,113 @@ +--- +title: Output Window Reference +description: A reference for output window extensibility +date: 2022-08-01 +--- + +# Output Window Overview + +The Output Window in the Visual Studio IDE is a [Tool Window](./../toolWindow/toolWindow.md) that can be used to deliver status, diagnostics/logging, or any other informational text to the user. Unlike [User Prompts](./../userPrompts/userPrompts.md), which might display a message box, the messages written to the Output Window are only displayed if the user has the Output pane visible in the IDE (Output in the View menu) and your Channel selected in the "Show output from:" dropdown menu. + +# Getting Started + +To get started, follow the [create the project](./../../getting-started/create-your-first-extension.md) section in Getting Started section. + +Next, see the [OutputWindowSample](./../../../../New_Extensibility_Model/Samples/OutputWindowSample) sample for a full example of creating an extension that uses the output window. + +# Working with the Output Window + +This guide is designed to cover the top user scenarios when working with the Output Window: + +- [Getting an Output Window Channel](#getting-an-output-window-channel) +- [Writing to the Output Window](#writing-to-the-output-window) + +# Getting an Output Window Channel + +In order to write to the Output Window, you need an Output Window Channel which can be created by calling `VisualStudioExtensibility.Views().Output.GetChannelAsync()`. + +## `OutputWindowExtensibility.GetChannelAsync()` + +The `GetChannelAsync()` method has three parameters: + +| Parameter | Type | Required | Description | +| --------- |----- | -------- | ----------- | +| `identifier` | `string` | yes | A unique identifier for the channel. | +| `displayNameResourceId` | `string` | yes | The name of the [resource](https://docs.microsoft.com/en-us/dotnet/core/extensions/resources) that contains the display name of the output window. This is what will be visible in the "Show output from:" dropdown menu in the Output pane.

For example, if you had a [`.resx`](https://docs.microsoft.com/en-us/dotnet/core/extensions/resources) resource file called `MyStrings.resx` with a resource named "OutputWindowDisplayName", you would use `nameof(MyStrings.OutputWindowDisplayName)` for this parameter. | +| `cancellationToken` | [`CancellationToken`](https://docs.microsoft.com/en-us/dotnet/api/system.threading.cancellationtoken) | yes | The [`CancellationToken`](https://docs.microsoft.com/en-us/dotnet/api/system.threading.cancellationtoken) for the async operation. | + +## Display Name Resource ID Requirements + +The current version of the Output Window API requires that the display name for the Output Window Channel be stored in a Resource File: + + - Add a [`.resx` file](https://docs.microsoft.com/en-us/dotnet/core/extensions/resources) ([sample](./../../../../New_Extensibility_Model/Samples/OutputWindowSample/Strings.resx)) and make sure it is configured with the ResXFileCodeGenerator in your project ([sample](./../../../../New_Extensibility_Model/Samples/OutputWindowSample/OutputWindowSample.csproj)). + - Add an [Extension class](../../inside-the-sdk/extension-anatomy.md#extension-instance) to your project, if it doesn't have one already ([sample](./../../../../New_Extensibility_Model/Samples/OutputWindowSample/OutputWindowSampleExtension.cs)) + - In the Extension class override the `ResourceManager` property to return the ResourceManager corresponding to your .resx file. + +## Example + +### `.resx` [Resource File](https://docs.microsoft.com/en-us/dotnet/core/extensions/resources) + +In this case, `MyStrings.resx`: + +```xml + + My Output Window + +``` + +### `.csproj` Project File + +```xml + + + True + True + MyStrings.resx + + + + + + ResXFileCodeGenerator + MyStrings.Designer.cs + + +``` + +### Extension Class + +In this case, `MyExtension.cs`: + +```csharp +public class MyExtension : Extension +{ + protected override ResourceManager? ResourceManager => MyStrings.ResourceManager; +} +``` + +**Note:** Make sure that the MyExtension class is in the same namespace as the `MyStrings` resource class, which defaults to the name of the project, unless you have overridden it. + +### Extension code: + +```csharp +string id = "MyOutputWindow"; +string displayNameResourceId = nameof(Strings.OutputWindowDisplayName); +OutputWindow? outputWindow = await this.Extensibility.Views().Output.GetChannelAsync(id, displayNameResourceId, cancellationToken); +``` + +# Writing to the Output Window + +The [`OutputWindow`](./../api/../../api/Microsoft.VisualStudio.Extensibility.md#outputwindow-type) instance obtained in [Getting an Output Window Channel](#getting-an-output-window-channel) has a [`System.IO.TextWriter`](https://docs.microsoft.com/en-us/dotnet/api/system.io.textwriter) property called `Writer`, which supports familiar operations for writing text, such as: +- [`Write()`](https://docs.microsoft.com/en-us/dotnet/api/system.io.textwriter.write) +- [`WriteAsync()`](https://docs.microsoft.com/en-us/dotnet/api/system.io.textwriter.writeasync) +- [`WriteLine()`](https://docs.microsoft.com/en-us/dotnet/api/system.io.textwriter.writeline) +- [`WriteLineAsync()`](https://docs.microsoft.com/en-us/dotnet/api/system.io.textwriter.writelineasync) + +## Example + +```csharp +if (this.outputWindow != null) +{ + await this.outputWindow.Writer.WriteLineAsync("This is a test of the output window."); +} +``` \ No newline at end of file