Reconfigured folders for github.io page
This commit is contained in:
Родитель
3aae000ae9
Коммит
47f263a646
|
@ -0,0 +1,11 @@
|
|||
---
|
||||
title: 404 Page not found
|
||||
description: You got the wrong URL.
|
||||
date: 2021-6-16
|
||||
permalink: /404.html
|
||||
---
|
||||
|
||||
## Ooops
|
||||
The page you are looking for could not be found. It might have been renamed or moved.
|
||||
|
||||
Try finding it from the menu.
|
|
@ -1 +1,16 @@
|
|||
theme: jekyll-theme-tactile
|
||||
title: Visual Studio Extensibility
|
||||
description: Visual Studio Extensibility
|
||||
#theme: jekyll-theme-tactile
|
||||
layout: default
|
||||
|
||||
css:
|
||||
syntax: true
|
||||
|
||||
defaults:
|
||||
- scope:
|
||||
path: ""
|
||||
values:
|
||||
layout: default
|
||||
|
||||
sass:
|
||||
style: compressed
|
|
@ -0,0 +1,142 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
|
||||
|
||||
<title>{{ page.title }} | {{ site.title }}</title>
|
||||
<meta name="description" content="{{ page.description }}" />
|
||||
<link rel="stylesheet" href="/assets/css/style.css" />
|
||||
<link rel=apple-touch-icon href="/assets/img/icon-180x180.png" />
|
||||
<link rel=icon type=image/png sizes=32x32 href="/assets/img/icon-32x32.png" />
|
||||
<link rel=icon type=image/png sizes=16x16 href="/assets/img/icon-16x16.png" />
|
||||
<link rel="canonical" href="{{ site.url }}{{ site.baseurl }}{{ page.url }}" />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:site_name" content="{{ site.title }}" />
|
||||
<meta property="og:title" content="{{ page.title }}" />
|
||||
<meta property="og:description" content="{{ page.description }}" />
|
||||
<meta property="og:url" content="{{ site.url }}{{ site.baseurl }}{{ page.url }}" />
|
||||
<meta property="og:image" content="{{ site.url }}{{ site.baseurl }}/assets/img/icon-180x180.png" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<header>
|
||||
<h1>{{ page.title }}</h1>
|
||||
<a href="/">{{ site.description }}</a>
|
||||
</header>
|
||||
<main>
|
||||
<nav id="menu">
|
||||
<label for="hamburger">☰</label>
|
||||
<input type="checkbox" id="hamburger" />
|
||||
<ul>
|
||||
<li><a href="/" tabindex="0">Home</a></li>
|
||||
<li class="parent">
|
||||
<a href="/new-extensibility-model/" aria-haspopup="menu">Out-of-Proc Extensibility</a>
|
||||
<ul>
|
||||
<li class="parent">
|
||||
<a href="/new-extensibility-model/getting-started/" aria-haspopup="menu">Getting Started</a></li>
|
||||
<ul>
|
||||
<li><a href="/new-extensibility-model/getting-started/Create_Your_First_Extension.html"></a></li>
|
||||
</ul>
|
||||
<li class="parent">
|
||||
<a href="/new-extensibility-model/extension-guides/" aria-haspopup="menu">Extension Guides</a></li>
|
||||
<li class="parent">
|
||||
<a href="/new-extensibility-model/inside-the-sdk" aria-haspopup="menu">Inside the SDK</a></li>
|
||||
<li class="parent">
|
||||
<a href="/new-extensibility-model/api" aria-haspopup="menu">API</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="parent">
|
||||
<a href="/recipes/" aria-haspopup="menu">Recipes</a>
|
||||
<ul>
|
||||
<li><a href="/recipes/menus-buttons-commands.html">Menus & commands</a></li>
|
||||
<li><a href="/recipes/custom-tool-windows.html">Tool windows</a></li>
|
||||
<li><a href="/recipes/theming.html">Theming</a></li>
|
||||
<li><a href="/recipes/settings-and-options.html">Settings & options</a></li>
|
||||
<li><a href="/recipes/notifications.html">Notifications</a></li>
|
||||
<li><a href="/recipes/error-handling.html">Error handling</a></li>
|
||||
<li><a href="/recipes/showing-progress.html">Showing progress</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="parent">
|
||||
<a href="/tips/" aria-haspopup="menu">Tips & tricks</a>
|
||||
<ul>
|
||||
<li><a href="/tips/files.html">Files & documents</a></li>
|
||||
<li><a href="/tips/projects.html">Projects</a></li>
|
||||
<li><a href="/tips/solutions.html">Solutions</a></li>
|
||||
<li><a href="/tips/build.html">Build</a></li>
|
||||
<li><a href="/tips/events.html">Events</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="parent">
|
||||
<a href="/publish/" aria-haspopup="menu">Publishing</a>
|
||||
<ul>
|
||||
<li><a href="/publish/checklist.html">Checklist</a></li>
|
||||
<li><a href="/publish/create-extension-pack.html">Extension packs</a></li>
|
||||
<li><a href="/publish/marketplace.html">Marketplace</a></li>
|
||||
<li><a href="/publish/private-galleries.html">Private galleries</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="/api.html">API</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
<article>
|
||||
{{ content }}
|
||||
{% if page.date %}
|
||||
<div class="meta">Last updated: <time datetime="{{ page.date }}">{{ page.date | date_to_string: "ordinal",
|
||||
"US" }}</time></div>
|
||||
{% endif %}
|
||||
</article>
|
||||
</main>
|
||||
<footer>
|
||||
<ul>
|
||||
<li><a rel="noopener" href="{{site.github.repository_url}}/blob/main/docs/{{page.path}}"
|
||||
target="_blank">Edit this page</a></li>
|
||||
<li><a rel="noopener" href="https://github.com/VsixCommunity/Community.VisualStudio.Toolkit/discussions"
|
||||
target="_blank">Ask for help</a></li>
|
||||
<li><a rel="noopener" href="https://github.com/VsixCommunity/docs/issues/" target="_blank">Report issues</a>
|
||||
</li>
|
||||
</ul>
|
||||
<p>Made with <strong>❤</strong> by fellow extenders</p>
|
||||
</footer>
|
||||
|
||||
<script>
|
||||
window.requestAnimationFrame(function (e) {
|
||||
|
||||
// Expand menu on load
|
||||
var navLinks = document.querySelectorAll("nav a");
|
||||
navLinks.forEach(function (a) {
|
||||
if (location.pathname.endsWith(a.pathname)) {
|
||||
a.setAttribute("aria-current", "page")
|
||||
if (a.parentNode && a.parentNode.parentNode) {
|
||||
a.parentNode.classList.add("open");
|
||||
a.parentNode.parentNode.parentNode.classList.add("open");
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
// Open menu items on click
|
||||
var menu = document.getElementById("menu");
|
||||
menu.addEventListener("click", function (e) {
|
||||
if (e.target.nextElementSibling !== null && e.target.tagName === "A") {
|
||||
e.preventDefault();
|
||||
e.target.parentNode.classList.toggle("open")
|
||||
}
|
||||
});
|
||||
|
||||
// Display alt text as caption under the image
|
||||
var images = document.querySelectorAll("main img[alt]:not([alt=''])");
|
||||
images.forEach(function (img) {
|
||||
var caption = document.createElement("span");
|
||||
caption.classList.add("caption");
|
||||
caption.textContent = img.getAttribute("alt");
|
||||
img.insertAdjacentElement("afterend", caption);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -0,0 +1,16 @@
|
|||
---
|
||||
title: API reference
|
||||
description: A reference for the Visual Studio and Community Toolkit APIs
|
||||
date: 2021-5-25
|
||||
---
|
||||
|
||||
There are several sets of API reference documentation that you might find relevant to check out.
|
||||
|
||||
## The Community Visual Studio Toolkit
|
||||
|
||||
When using the Community Visual Studio Toolkit, make sure to check out its [API reference](https://vsixcommunity.github.io/Community.VisualStudio.Toolkit/v1/api/).
|
||||
|
||||
## The official SDK reference
|
||||
|
||||
The official API reference contain basic information about all the types found in the entire public SDK. Find it at
|
||||
[Visual Studio SDK reference](https://docs.microsoft.com/visualstudio/extensibility/visual-studio-sdk-reference)
|
|
@ -0,0 +1,342 @@
|
|||
---
|
||||
---
|
||||
|
||||
:root {
|
||||
--border-color: #d8d8d8;
|
||||
--background: #efefef;
|
||||
--foreground: #000;
|
||||
--link-color: #0A4F94;
|
||||
--code-c1: green;
|
||||
--code-k: blue;
|
||||
--code-s: #A31515;
|
||||
--code-n: #22728a;
|
||||
--code-nf: #74531F;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark)
|
||||
{
|
||||
:root {
|
||||
--background: #121212;
|
||||
--foreground: #ddd;
|
||||
--link-color: #5AADFF;
|
||||
--code-c1: #57A64A;
|
||||
--code-k: #569CD6;
|
||||
--code-s: #D69D85;
|
||||
--code-n: #4EC9B0;
|
||||
--code-nf: #DCDCAA;
|
||||
}
|
||||
|
||||
article img {
|
||||
opacity: .85;
|
||||
}
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
max-width: 1100px;
|
||||
margin: 0 auto;
|
||||
min-height: 100vh;
|
||||
padding: 0 10px;
|
||||
display: flex;
|
||||
background: var(--background);
|
||||
color: var(--foreground);
|
||||
flex-direction: column;
|
||||
text-size-adjust: none;
|
||||
-webkit-text-size-adjust: none;
|
||||
font: 18px/1.6 "Open Sans",Ubuntu,"Nimbus Sans L",Avenir,AvenirNext,"Segoe UI",Helvetica,Arial,sans-serif;
|
||||
counter-reset: figure;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--link-color);
|
||||
}
|
||||
|
||||
article > p {
|
||||
padding-bottom: 1.5em;
|
||||
}
|
||||
|
||||
img,
|
||||
video {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
border-radius: 3px;
|
||||
border: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.caption {
|
||||
display: block;
|
||||
font-style: italic;
|
||||
font-size: .85em;
|
||||
}
|
||||
|
||||
.caption::before {
|
||||
counter-increment: figure;
|
||||
content: "Figure " counter(figure) ": ";
|
||||
}
|
||||
|
||||
blockquote {
|
||||
padding: .6em 1em .6em 3em;
|
||||
font-size: 1.2em;
|
||||
margin-bottom: 1em;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
blockquote::before {
|
||||
content: "”";
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
font-size: 7em;
|
||||
opacity: .5;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: -.4em;
|
||||
}
|
||||
|
||||
pre {
|
||||
background: rgba(180, 180, 180, .1);
|
||||
color: var(--foreground);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 3px;
|
||||
overflow: auto;
|
||||
padding: .5em .7em;
|
||||
margin: -1em 0 2em 0;
|
||||
font-size: .9em;
|
||||
width: 100%;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: SFMono-Regular,Consolas,Liberation Mono,Menlo,monospace;
|
||||
}
|
||||
|
||||
code .c1 {
|
||||
color: var(--code-c1);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
code .s {
|
||||
color: var(--code-s);
|
||||
}
|
||||
|
||||
code .n,
|
||||
code .na,
|
||||
code .nc {
|
||||
color: var(--code-n);
|
||||
}
|
||||
|
||||
code .k,
|
||||
code .kt {
|
||||
color: var(--code-k);
|
||||
}
|
||||
|
||||
code .nf,
|
||||
code .nt {
|
||||
color: var(--code-nf);
|
||||
}
|
||||
|
||||
p > code {
|
||||
background: rgba(180, 180, 180, .1);
|
||||
border-radius: 6px;
|
||||
font-size: 85%;
|
||||
padding: .1em .3em;
|
||||
border: 1px solid rgba(180, 180, 180, .3);
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
header h1 {
|
||||
padding: .2em 0 0 0;
|
||||
}
|
||||
|
||||
header a {
|
||||
font-variant: small-caps;
|
||||
letter-spacing: 1px;
|
||||
font-size: 1.2em;
|
||||
text-decoration: none;
|
||||
background-size: 20px;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
main {
|
||||
margin: 1em 0 0 0;
|
||||
display: flex;;
|
||||
flex: 1;
|
||||
border-top: 1px solid var(--border-color);
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
#menu {
|
||||
font-size: .85em;
|
||||
min-width: fit-content;
|
||||
border-right: 1px solid var(--border-color);
|
||||
padding: 1em 1.5em 1em .7em;
|
||||
margin-right: 2em;
|
||||
}
|
||||
|
||||
#menu label,
|
||||
#menu input {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#menu a {
|
||||
color: var(--foreground);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#menu a:hover,
|
||||
#menu a:focus,
|
||||
#menu a:active {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
#menu > a {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#menu a[aria-current] {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
#menu ul {
|
||||
list-style: none;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#menu a[aria-haspopup]::before {
|
||||
content: "›";
|
||||
position:absolute;
|
||||
left: -10px;
|
||||
}
|
||||
|
||||
#menu .open a[aria-haspopup]::before {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
#menu li:not(.open) ul {
|
||||
visibility: collapse;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
#menu > ul > li {
|
||||
margin-bottom: .3em;
|
||||
}
|
||||
|
||||
#menu ul ul li {
|
||||
padding: .3em 0 0 1em;
|
||||
}
|
||||
|
||||
article {
|
||||
//margin: 0 1em 0 1.5em;
|
||||
padding: 1em 0 2em 0;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
min-width: 0; // needed for <pre> not to be too wide
|
||||
}
|
||||
|
||||
article h2 a,
|
||||
article h3 a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
article h2 a:hover:after,
|
||||
article h3 a:hover:after {
|
||||
content: " 🔗";
|
||||
font-size: .7em;
|
||||
}
|
||||
|
||||
article .meta {
|
||||
font-size: .8em;
|
||||
opacity: 0.7;
|
||||
position: absolute;
|
||||
bottom: 1em;
|
||||
right: 0
|
||||
}
|
||||
|
||||
article ol,
|
||||
article ul {
|
||||
padding-left: 2.5em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
article li::marker {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
footer {
|
||||
padding: 1em 0;
|
||||
text-align: center;
|
||||
line-height: 1.5em;
|
||||
}
|
||||
|
||||
footer strong {
|
||||
color: crimson;
|
||||
}
|
||||
|
||||
footer li {
|
||||
display: inline-block;
|
||||
margin-bottom: .3em;
|
||||
}
|
||||
|
||||
footer li:not(:last-child):after{
|
||||
content: ' - '
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 1023px) {
|
||||
body {
|
||||
margin-left: unquote("max(env(safe-area-inset-left), 10px)");
|
||||
}
|
||||
#menu {
|
||||
padding: 1em 0 0 0;
|
||||
border: none;
|
||||
margin: 0;
|
||||
}
|
||||
#menu label {
|
||||
display: block;
|
||||
cursor: pointer;
|
||||
color: darkgray;
|
||||
position: absolute;
|
||||
top: .4em;
|
||||
right: .8em;
|
||||
font-size: 1.5em;
|
||||
background-color: var(--background);
|
||||
}
|
||||
#hamburger {
|
||||
display: none;
|
||||
}
|
||||
#menu > ul {
|
||||
display: none;
|
||||
background: var(--background);
|
||||
position: absolute;
|
||||
right: 0;
|
||||
left: 10px;
|
||||
z-index: 1;
|
||||
margin-left: unquote("max(env(safe-area-inset-left), 10px)");
|
||||
}
|
||||
#menu input:checked + ul {
|
||||
display: block;
|
||||
}
|
||||
|
||||
main > section {
|
||||
margin: 0
|
||||
}
|
||||
}
|
||||
|
||||
@media print {
|
||||
#menu {
|
||||
display: none;
|
||||
}
|
||||
main > section {
|
||||
margin: 0
|
||||
}
|
||||
footer ul {
|
||||
display: none
|
||||
}
|
||||
}
|
|
@ -1,3 +1,27 @@
|
|||
# This is the **HOMEPAGE**.
|
||||
Refer to [Markdown](http://daringfireball.net/projects/markdown/) for how to write markdown files.
|
||||
## Quick Start Notes:
|
||||
---
|
||||
title: VS Extensibility
|
||||
description: Welcome to Visual Studio Extensibility
|
||||
date: 2021-8-16
|
||||
---
|
||||
|
||||
This site will help you write successful Visual Studio extensions and there's something for absolute beginners to experts alike.
|
||||
|
||||
<video controls poster="../assets/img/intro-poster.png">
|
||||
<source src="../assets/video/intro.mp4" type="video/mp4" />
|
||||
</video>
|
||||
|
||||
## Pick your starting point
|
||||
|
||||
Based on your experience level, pick where to begin.
|
||||
|
||||
### Learn how to get started
|
||||
|
||||
If you are new to extension development, you want to start at the beginning to ensure you're not missing any information up front. Head on over to the [getting started guide](getting-started/).
|
||||
|
||||
### Walk me through typical scenarios
|
||||
|
||||
Once you know the basics of the Visual Studio extensibility model, it is time to explore what type of features to extend. To do that, check out the [recipe section](recipes/) for inspiration and step-by-step walkthroughs.
|
||||
|
||||
### Publishing the finished extension
|
||||
|
||||
When you've written your extension, it is time to publish it. Whether you want to share it with just a few friends, your company, or the whole world, you want to check out the [publishing section](publish/).
|
|
@ -0,0 +1,19 @@
|
|||
# Visual Studio Out-Of-Process Extensibility SDK
|
||||
|
||||
## Getting Started
|
||||
* [Introduction to new SDK](Getting_Started/Introduction.md)
|
||||
* [Create your first extension](Getting_Started/Create_Your_First_Extension.md)
|
||||
|
||||
## Extension Guides
|
||||
* [Parts of a new Visual Studio extension](Inside_the_SDK/ExtensionAnatomy.md)
|
||||
* [Nuget packages](Inside_the_SDK/Packages.md)
|
||||
* [Commands](...)
|
||||
* [Editor components](...)
|
||||
* [Rule based conditions](Inside_the_SDK/Activation_Constraints.md)
|
||||
* [Parts of the SDK](Inside_the_SDK/InsideTheSDK.md)
|
||||
|
||||
## Sample Walkthroughs
|
||||
* [Simple command handler](Extension_Guides/SimpleCommandSample.md)
|
||||
* [Format spaces editor sample](TBD)
|
||||
* [Markdown Linter](Extension_Guides/MarkdownLinterSample.md)
|
||||
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,118 @@
|
|||
# Commands
|
||||
|
||||
Commands trigger actions in Visual Studio. They manifest as buttons parented to menus and/or toolbars in the IDE with their execution tied to user interaction. Commands in the new Extensibility Model run asynchronously and so the user can continue to interact with IDE while commands are executing.
|
||||
|
||||
## Creating new commands
|
||||
|
||||
To get started:
|
||||
|
||||
* Add a reference to the [Microsoft.VisualStudio.Extensibility](https://www.nuget.org/TODO-add-real-link) and [Microsoft.VisualStudio.Extensibility.Build](https://www.nuget.org/TODO-add-real-link) NuGet packages to your project.
|
||||
* Change your project's `TargetFramework` from `net6.0` to `net6.0-windows`.
|
||||
|
||||
### Registering a command
|
||||
|
||||
Creating a command with the new Extensibility Model is as simple as extending the base class `Microsoft.VisualStudio.Extensibility.Commands.Command` and adorning your class with the attribute `Microsoft.VisualStudio.Extensibility.Commands.CommandAttribute`.
|
||||
|
||||
The attribute `Microsoft.VisualStudio.Extensibility.Commands.CommandAttribute` has a few parameters that you should become familiar with:
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- |----- | -------- | ----------- |
|
||||
| Name | String | Yes | A globally unique identifier for the command. It is recommended to use the full class name of your command here. |
|
||||
| Id | ushort | Yes | A locally unique identifier for your command within your extension. Each command within your extension should use a different value. |
|
||||
| DisplayName | String | Yes | The default display name of your command. Surround this string with the '%' character to enable localizing this string. See more on this at [Localizing a command](#localizing-a-command). |
|
||||
| ContainerType | Type? | No | The type that is to act as the CommandSet for this command. Setting this parameter to null automatically generates a default CommandSet for your command. |
|
||||
| Placement | KnownCommandPlacement | No | Indicates where within Visual Studio your command should be parented. If no placement is provided the command defaults to being parented to the Standard toolbar. |
|
||||
| ClientContext | String | No | Client contexts requested by the command, separated by ','. By default the Shell and Editor contexts are returned. A client context is a snapshot of specific IDE states at the time a command was originally executed. Since these commands are executed asynchronously this state could change between the time the user executed the command and the command handler running. See more on this at [Client contexts](./../../Inside_the_SDK/Activation_Constraints.md/#client-contexts). |
|
||||
|
||||
```csharp
|
||||
[Command(CommandName, CommandId, "Sample Remote Command", placement: KnownCommandPlacement.ToolsMenu)]
|
||||
public class CommandHandler : Command
|
||||
{
|
||||
private const ushort CommandId = 1;
|
||||
private const string CommandName = "SimpleRemoteCommandSample.Command";
|
||||
|
||||
public CommandHandler(VisualStudioExtensibility extensibility, TraceSource traceSource, ushort id)
|
||||
: base(extensibility, id)
|
||||
{
|
||||
}
|
||||
|
||||
public override Task ExecuteCommandAsync(IClientContext context, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
See the [InsertGuidSample](./../InsertGuidSample.md) sample to get started with creating an extension with a command.
|
||||
|
||||
### Adding an icon
|
||||
|
||||
Commands support adding icons to their menu item in addition to or instead of the display name of the command. To add an icon to your command, add the attribute `Microsoft.VisualStudio.Extensibility.Commands.CommandIconAttribute` to your command class. Currently you can use any of the [KnownMonikers](https://docs.microsoft.com/en-us/dotnet/api/microsoft.visualstudio.imaging.knownmonikers?view=visualstudiosdk-2022) currently supported by Visual Studio. Custom monikers are not supported at this time.
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- |----- | -------- | ----------- |
|
||||
| ImageMoniker | String | Yes | The name of any of the [KnownMonikers](https://docs.microsoft.com/en-us/dotnet/api/microsoft.visualstudio.imaging.knownmonikers?view=visualstudiosdk-2022) currently supported by Visual Studio, with or without the `KnownMonikers` type name. |
|
||||
| IconSettings | IconSettings | Yes | Configures how the command will be displayed. For example `IconSettings.IconAndText` displays the icon alongside the command's display name, whereas `IconSettings.IconOnly` will only show the command's icon and not its DisplayName if parented to a toolbar. |
|
||||
|
||||
```csharp
|
||||
[CommandIcon("Extension", IconSettings.IconAndText)]
|
||||
```
|
||||
|
||||
### Controlling command visibility
|
||||
|
||||
The visibility of a command can be controlled by adding the attribute `Microsoft.VisualStudio.Extensibility.Commands.CommandVisibleWhenAttribute` to your command class. This attribute supports specifying an expression, defining a set of terms used in the expression, and what values those terms should be replaced with upon evaluation. Note: Term names and values are mapped to their index in the array. i.e. the term name at index 0 corresponds with the term value at that same index. The command would be visible when the expression evaluates to true, and invisible when it is false.
|
||||
|
||||
If this attribute is omitted from your command, the default is for the command to always be visible.
|
||||
|
||||
An example of such an expression can be seen here:
|
||||
```csharp
|
||||
// This command would become visible when an editor for a file with any file extension is active.
|
||||
[CommandVisibleWhen(
|
||||
expression: "AnyFile",
|
||||
termNames: new string[] { "AnyFile" },
|
||||
termValues: new string[] { "ClientContext:Shell.ActiveEditorContentType=.+" })]
|
||||
```
|
||||
|
||||
To see more information on valid term values:
|
||||
- [Using rule based activation constraints](./../../Inside_the_SDK/Activation_Constraints.md/#rule-based-activation-constraints)
|
||||
|
||||
### Controlling command Enabled/Disabled state
|
||||
|
||||
The visibility of a command can be controlled by adding the attribute `Microsoft.VisualStudio.Extensibility.Commands.CommandEnabledWhenAttribute` to your command class. This attribute supports specifying an expression, defining a set of terms used in the expression, and what values those terms should be replaced with upon evaluation. Note: Term names and values are mapped to their index in the array. i.e. the term name at index 0 corresponds with the term value at that same index. The command would be enabled when the expression evaluates to true, and disabled when it is false.
|
||||
|
||||
If this attribute is omitted from your command, the default is for the command to always be enabled. You can also automatically have your command be disabled if it is currently executing by setting `this.DisableDuringExecution = true;` in the constructor of your command class. Setting this property will override the enabled/disabled state defined by the `Microsoft.VisualStudio.Extensibility.Commands.CommandEnabledWhenAttribute` while the command is being executed.
|
||||
|
||||
An example of such an expression can be seen here:
|
||||
```csharp
|
||||
// This command would become enabled when a solution is loaded in the IDE and a file with the file extension ".jpg", ".jpeg", or ".txt" is selected in the Solution Explorer.
|
||||
[CommandEnabledWhen(
|
||||
expression: "SolutionLoaded & IsValidFile",
|
||||
termNames: new string[] { "SolutionLoaded", "IsValidFile" },
|
||||
termValues: new string[] { "SolutionState:Exists", "ClientContext:Shell.ActiveSelectionFileName=(.jpg|.jpeg|.txt)$" })]
|
||||
```
|
||||
|
||||
To see more information on valid term values:
|
||||
- [Using rule based activation constraints](./../../Inside_the_SDK/Activation_Constraints.md/#rule-based-activation-constraints)
|
||||
|
||||
### Localizing a command
|
||||
|
||||
The text displayed on a command can be localized by including `string-resources.json` files with your extension and formatting the DisplayName parameter with the '%' character on either end in your `Microsoft.VisualStudio.Extensibility.Commands.CommandAttribute`.
|
||||
|
||||
Localized Command DisplayName
|
||||
```csharp
|
||||
[Command(CommandName, CommandId, "%Microsoft.VisualStudio.MyExtension.SampleRemoteCommand.DisplayName%", placement: KnownCommandPlacement.ToolsMenu)]
|
||||
```
|
||||
|
||||
#### string-resources.json
|
||||
|
||||
Your extension should provide a `string-resources.json` file for every language that your extension supports. This JSON file is a dictionary of key/value pairs where the key is a globally (all of Visual Studio) unique identifier for a string resource and the value is the localized string resource. These JSON files should be deployed with your extension under the ".vsextension" directory, with each language you support being shipped in a folder matching the name of the locale i.e. "de" for German, "it" for Italian, etc. The `string-resources.json` deployed at the root of the ".vsextension" directory is used as the default if your extension does not support the language that Visual Studio is currently set to. An example of what this directory structure would look like can be seen here:
|
||||
|
||||
![Localization directory structure](localizing-a-command.PNG "Localization directory structure")
|
||||
|
||||
string-resources.json sample:
|
||||
```json
|
||||
{
|
||||
"Microsoft.VisualStudio.MyExtension.SampleRemoteCommand.DisplayName": "Sample Remote Command",
|
||||
"Microsoft.VisualStudio.MyExtension.OutputWindowTest.DisplayName": "Output Window Test"
|
||||
}
|
||||
```
|
Двоичные данные
docs/new-extensibility-model/extension-guides/Command/localizing-a-command.PNG
Normal file
Двоичные данные
docs/new-extensibility-model/extension-guides/Command/localizing-a-command.PNG
Normal file
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 6.1 KiB |
Двоичные данные
docs/new-extensibility-model/extension-guides/Editor/DocumentOpenFlow.PNG
Normal file
Двоичные данные
docs/new-extensibility-model/extension-guides/Editor/DocumentOpenFlow.PNG
Normal file
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 42 KiB |
|
@ -0,0 +1,220 @@
|
|||
# Editor Extensions
|
||||
|
||||
The Visual Studio editor supports extensions that add to its capabilities. For the initial release of the new
|
||||
Visual Studio extensibility model, only the following capabilities are supported:
|
||||
|
||||
- Listening for text views being opened and closed.
|
||||
- Listening for text view (editor) state changes.
|
||||
- Reading the text of the document and the caret locations.
|
||||
- Performing text edits.
|
||||
|
||||
## Editor Extensibility Entrypoints
|
||||
|
||||
Editor extensibility currently supports 3 entry points: listeners, the EditorExtensibility service object, and
|
||||
commands.
|
||||
|
||||
### Adding a Listener
|
||||
|
||||
There are two types of listeners, ITextViewChangedListener, and ITextViewLifetimeListener.
|
||||
Together, these listeners can be used to observe the open, close, and modification of text editors.
|
||||
|
||||
To get started:
|
||||
|
||||
* Add a reference to the [Microsoft.VisualStudio.Extensibility.Editor](https://www.nuget.org/TODO-add-real-link)
|
||||
and [Microsoft.VisualStudio.Extensibility.Build](https://www.nuget.org/TODO-add-real-link) NuGet packages to your
|
||||
project.
|
||||
* Change your project's `TargetFramework` from `net6.0` to `net6.0-windows`.
|
||||
|
||||
Then, create a new class, implementing `ExtensionPart` base class and `ITextViewChangedListener`,
|
||||
`ITextViewLifetimeListener`, or both. Then, add an `[ExtensionPart(typeof(ITextViewChangedListener))]` attribute for
|
||||
each listener interface you implemented and an `[AppliesTo(ContentType = "CSharp")]` attribute to your class.
|
||||
ExtensionPart indicates to Visual Studio that this class can be instantiated by interested IDE services, and triggers
|
||||
registration of the part in your extension's manifest.
|
||||
|
||||
Assuming you decide to implement both listeners, the finished class declaration should look like the following:
|
||||
|
||||
```csharp
|
||||
[ExtensionPart(typeof(ITextViewLifetimeListener))]
|
||||
[ExtensionPart(typeof(ITextViewChangedListener))]
|
||||
[AppliesTo(ContentType = "CSharp")]
|
||||
public sealed class TextViewOperationListener : ExtensionPart, ITextViewLifetimeListener, ITextViewChangedListener
|
||||
{
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
When you run your extension, you should see:
|
||||
|
||||
- ITextViewLifetimeListener.TextViewCreatedAsync() called anytime an editor is opened by the user.
|
||||
- ITextViewLifetimeListener.TextViewClosedAsync() called anytime an editor is closed by the user.
|
||||
- ITextViewChangedListener.TextViewChangedAsync() called anytime a user makes a text change in the editor.
|
||||
|
||||
Each of these methods are passed an ITextView containing the state of the text editor at the time the
|
||||
user invoked the action and a CancellationToken that will have `IsCancellationRequested == true` when
|
||||
the IDE wishes to cancel a pending action.
|
||||
|
||||
#### AppliesTo Attribute
|
||||
|
||||
AppliesTo attribute indicates the programming language scenarios in which the extension should activate. It is written as
|
||||
`[AppliesTo(ContentType = "CSharp")]`, where ContentType is a well known name of a language built into Visual Studio,
|
||||
or custom defined in a Visual Studio extension.
|
||||
|
||||
Note that registering custom content types is not yet supported in the
|
||||
new extensibility model.
|
||||
|
||||
**Some Well Known Content Types**:
|
||||
- "CSharp" - C#
|
||||
- "C/C++" - C, C++, headers, and IDL
|
||||
- "TypeScript" - TypeScript and JavaScript type languages.
|
||||
- "HTML" - HTML
|
||||
- "JSON" - JSON
|
||||
- "text" - text files, including hierarchical descendents of "code", which descends from "text".
|
||||
- "code" - C, C++, C#, etc.
|
||||
|
||||
ContentTypes are hierarchical. e.g.: C# and C++ both descend from "code", so declaring "code" will cause your extension
|
||||
to activate for C#, C, C++, etc.
|
||||
|
||||
### EditorExtensibility
|
||||
|
||||
Visual Studio ExtensionParts all expose a `this.Extensibility` property. Using this property, you can
|
||||
request an instance of the EditorExtensibility object, which exposes on demand editor functionality, such as
|
||||
performing text edits.
|
||||
|
||||
```csharp
|
||||
EditorExtensibility editorService = this.Extensibility.Editor();
|
||||
```
|
||||
|
||||
### Getting an ITextView within a Command
|
||||
|
||||
`ExecuteCommandAsync()` in each Command is passed an IClientContext that contains a snapshot of the state of the IDE
|
||||
at the time the command was invoked. You can access the active ITextView from this using EditorExtensibility.
|
||||
|
||||
```csharp
|
||||
using ITextView textView = await this.Extensibility.Editor().GetActiveTextViewAsync(clientContext, cancellationToken);
|
||||
```
|
||||
|
||||
## Object Model
|
||||
|
||||
The Visual Studio Editor extensibility object model is composed of a few integral parts.
|
||||
|
||||
### ITextView
|
||||
ITextView contains the URI and version information necessary to acquire an ITextDocument as well
|
||||
as some properties of the text view, such as selections.
|
||||
|
||||
- This object is immutable and will never change after it is created.
|
||||
- You can use `ITextView.GetTextDocumentAsync()` to get the content from the document. Note that calling this method is
|
||||
expensive and only should be done if you need the document content.
|
||||
- ITextView cannot be changed directly. All changes are requested via Mutation. See Mutation Section below.
|
||||
|
||||
### ITextDocument
|
||||
ITextDocument contains the content of the text document from a point in time or version.
|
||||
|
||||
- This object is immutable and will never change after it is created.
|
||||
- ITextDocument cannot be changed directly. All changes are requested via Mutation. See Mutation Section below.
|
||||
|
||||
If you are familiar with legacy Visual Studio extensions, ITextDocument is almost 1:1 with
|
||||
[ITextSnapshot](https://docs.microsoft.com/en-us/dotnet/api/microsoft.visualstudio.text.itextsnapshot?view=visualstudiosdk-2019)
|
||||
and supports most of the same methods for accessing the text.
|
||||
|
||||
Best Practices:
|
||||
- Avoid calling `.GetText()`.
|
||||
- You can use Position and Span to represent substrings in the document without expending resources copying or allocating strings. Most APIs will operate in terms of these primitives.
|
||||
- You can use the indexer syntax, `textDocument[0]`, to read character by character in the document without copying it to a string.
|
||||
- If you must create a string such as for use as a dictionary key, use the overload that takes a Span, to avoid creating a large throwaway string from the entire line or document.
|
||||
- Avoid assuming lines or documents will be short. Many languages minify into huge lines or consume very large files.
|
||||
- ITextDocument references large data structures that may consume memory if an old enough version is stored. Best practice is to periodically update Positions and Spans that you are storing long term to the latest document version via their `TranslateTo()` method so the old ITextDocument version can be garbage collected.
|
||||
|
||||
### Position
|
||||
Represents a position within the text document. As opposed to `int` positions, the Position type
|
||||
is aware of the ITextDocument it came from and supports `GetChar()` to directly get the character at that point.
|
||||
|
||||
If you are familiar with legacy Visual Studio extensions, Position is almost 1:1 with
|
||||
[SnapshotPoint](https://docs.microsoft.com/en-us/dotnet/api/microsoft.visualstudio.text.snapshotpoint?view=visualstudiosdk-2019)
|
||||
and supports most of the same methods.
|
||||
|
||||
|
||||
### Span
|
||||
Represents a contiguous substring of characters within an ITextDocument. As opposed to a string created with
|
||||
`string.Substring()` or `ITextDocument.GetText()`, creating a span doesn't require any allocations or additional
|
||||
memory. You can later call `Span.GetText()` to realize it into a string in a deferred fashion.
|
||||
|
||||
If you are familiar with legacy Visual Studio extensions, Position is almost 1:1 with
|
||||
[SnapshotSpan](https://docs.microsoft.com/en-us/dotnet/api/microsoft.visualstudio.text.snapshotSpan?view=visualstudiosdk-2019)
|
||||
and supports most of the same methods.
|
||||
|
||||
## Mutation and Asynchronicity
|
||||
|
||||
### Mutation
|
||||
|
||||
In the new Visual Studio extensibility model, the extension is second class relative to the user: it cannot directly
|
||||
modify the editor or the text document. All state changes are asynchronous and cooperative, with Visual Studio IDE performing
|
||||
the requested change on the extension's behalf. The extension can request one or more changes on on a specific version of
|
||||
the document or text view.
|
||||
|
||||
Mutations are requested using the `MutateAsync()` method on `EditorExtensibility`.
|
||||
|
||||
If you are familiar with legacy Visual Studio extensions, ITextDocumentMutator is almost 1:1 with the state changing
|
||||
methods from
|
||||
[ITextBuffer](https://docs.microsoft.com/en-us/dotnet/api/microsoft.visualstudio.text.itextbuffer?view=visualstudiosdk-2019)
|
||||
and [ITextDocument](https://docs.microsoft.com/en-us/dotnet/api/microsoft.visualstudio.text.itextdocument?view=visualstudiosdk-2019)
|
||||
and supports most of the same capabilities.
|
||||
|
||||
```csharp
|
||||
MutationResult result = await this.Extensibility.Editor().MutateAsync(
|
||||
m =>
|
||||
{
|
||||
var mutator = m.GetMutator(document);
|
||||
mutator.Replace(textView.Selection.Extent, newGuidString);
|
||||
},
|
||||
cancellationToken);
|
||||
```
|
||||
|
||||
To avoid misplaced edits, editor extension edits are applied like so:
|
||||
|
||||
- Extension requests an edit be made, based on its most recent version of the document.
|
||||
- That request may contain one or more text edits, caret position changes, etc. Any type implementing `IMutatable`
|
||||
can be changed in a single `MutateAsync()` request, including ITextView and ITextDocument. Mutations
|
||||
are done by mutators, which can be requested on a specific class via `m.GetMutator()`.
|
||||
- Mutation request is sent to Visual Studio IDE, where it succeeds only if the object being mutated hasn't changed
|
||||
since the version the request was made one. If the document has changed, the change may be rejected, requiring
|
||||
the extension to retry on newer version. Outcome of mutation operation is stored in `result`.
|
||||
- Mutations are applied atomically. The best practice is to do all changes that should occur within a narrow time
|
||||
frame into a single MutateAsync() call, to reduce the likelihood of unexpected behavior arising from user edits,
|
||||
or language service actions that occur between mutations (e.g.: extension edits getting interleaved with Roslyn C#
|
||||
moving the caret).
|
||||
|
||||
### Asynchronicity
|
||||
|
||||
GetTextDocumentAsync() opens a copy of the text document in the Visual Studio extension. Since extensions run in a
|
||||
separate process, all extension interactions are asynchronous, cooperative, and have some caveats:
|
||||
|
||||
- GetTextDocumentAsync() may fail if called on a really old ITextDocument because it may no longer be cached by the
|
||||
Visual Studio client, if the user has made many changes since it was created. For
|
||||
this reason, if you plan to store an ITextView to access its document later, and cannot tolerate failure, it may
|
||||
be a good idea to call GetTextDocumentAsync() immediately. Doing so fetches the text content for that version of
|
||||
the document into your extension, ensuring that a copy of that version is sent to your extension before it expires.
|
||||
- GetTextDocumentAsync() or MutateAsync() may fail if the user closes the document.
|
||||
|
||||
![Document Open Diagram](DocumentOpenFlow.PNG)
|
||||
|
||||
### RPC Support
|
||||
|
||||
Since the new Visual Studio extensibility model is entirely in a separate process, all APIs have to at some
|
||||
level operate with serializable data types. Typically, extensions can ignore these implementation details,
|
||||
however, in some scenarios, an extension may need to interface directly with RPC services acquired from
|
||||
`this.Extensibility.ServiceBroker`. To facilitate interactions with RPC services, the object model exposes
|
||||
`RpcContract` properties on most core types, and the following serializable RPC types.
|
||||
|
||||
- VersionedTextDocumentRange - 1:1 serializable version of Span, accessible via `.RpcContract`. This type should be used in most RPC contracts between processes.
|
||||
- VersionedTextDocumentPosition - 1:1 serializable version of Position, accessible via `.RpcContract`. This type should be used in most RPC contracts between processes.
|
||||
- Range - Serializable version of Span, omitting the Uri and version number.
|
||||
- Microsoft.VisualStudio.RpcContracts.Utilities.Position - Serializable version of Position, omitting the Uri and version number.
|
||||
- TextView - 1:1 serialized form of ITextView, accessible via `.RpcContract`.
|
||||
- TextDocument - 1:1 serialized form of ITextDocument via `.RpcContract`.
|
||||
|
||||
As opposed to VersionedTextDocumentRange and VersionedTextDocumentPosition, Range and Microsoft.VisualStudio.RpcContracts.Utilities.Position
|
||||
omit the Uri and document version, making for a smaller serializable representation. This type should be used in RPC contracts that contain
|
||||
lots of span/range equivalents that need to reduce their payload size for performance. These RPC contracts will need to pass the document
|
||||
Uri and version for the spans/range to be rehydrated into Spans and Positions by the IEditorHostService.
|
||||
IEditorHostService interfaces with extension-local copies of the text buffer, and manages opening and closing of
|
||||
documents described by the RPC types.
|
|
@ -0,0 +1,51 @@
|
|||
# Walkthrough: Insert Guid Extension Sample
|
||||
|
||||
This extension is a simple extension that shows how a command that modifies an open editor window can be quickly added to Visual Studio.
|
||||
|
||||
Source code: TBD
|
||||
|
||||
## Command definition
|
||||
|
||||
The extension consist of a single code file that defines a command and its properties starting with class attributes as seen below:
|
||||
|
||||
```csharp
|
||||
[Command(
|
||||
"Microsoft.VisualStudio.InsertGuidExtension.InsertGuidCommand",
|
||||
1,
|
||||
"Insert new guid",
|
||||
placement: KnownCommandPlacement.ExtensionsMenu)]
|
||||
[CommandIcon("OfficeWebExtension", IconSettings.IconAndText)]
|
||||
[CommandVisibleWhen("AnyFile", new string[] { "AnyFile" }, new string[] { "ClientContext:Shell.ActiveEditorContentType=.+" })]
|
||||
```
|
||||
|
||||
The first `Command` attribute registers the command using the unique name `Microsoft.VisualStudio.InsertGuidExtension.InsertGuidCommand` and id `1`. The command is placed in `Extensions` top menu and uses `OfficeWebExtension` icon moniker.
|
||||
|
||||
`CommandVisibleWhen` attribute defines when the command is visible in `Extensions` menu. You can refer to [Activation Constraints]() to learn about different options that you can use to determine command visibility and state. In this case, the command is enabled anytime there is an active editor in the IDE.
|
||||
|
||||
## Getting the active editor view
|
||||
|
||||
Once user executes the command, SDK will route execution to `ExecuteCommandAsync` method. `IClientContext` instance contains information about the state of IDE at the time of command execution and can be used in conjuction with `VisualStudioExtensibility` object.
|
||||
|
||||
In our example, we utilize `GetActiveTextViewAsync` method to get the active text view at the time of command execution which includes information about document being open, version of the document and the selection.
|
||||
|
||||
```csharp
|
||||
using var textView = await context.GetActiveTextViewAsync(cancellationToken);
|
||||
```
|
||||
## Mutating the text in active view
|
||||
|
||||
Once we have the active text view, we can mutate the document attached to the view to replace the selection with a new guid string as below.
|
||||
|
||||
```csharp
|
||||
var document = await textView.GetTextDocumentAsync(cancellationToken);
|
||||
await this.Extensibility.Editor().MutateAsync(
|
||||
m =>
|
||||
{
|
||||
var mutator = m.GetMutator(document);
|
||||
mutator.Replace(textView.Selection.Extent, newGuidString);
|
||||
},
|
||||
cancellationToken);
|
||||
```
|
||||
|
||||
## Logging errors
|
||||
|
||||
Each extension part including command sets is assigned a `TraceSource` instance that can be utilized to log diagnostic errors. Please see [Logging](../Inside_the_SDK/Logging.md) section for more information.
|
|
@ -0,0 +1,63 @@
|
|||
# Walkthrough: Markdown Linter Extension
|
||||
|
||||
This extension shows how multiple components can interact together inside an extension and how different areas of Visual Studio can be extended.
|
||||
|
||||
## Summary
|
||||
Markdown Linter extension showcases samples of:
|
||||
|
||||
* Creating a command handler
|
||||
* Creating text view change and creation listeners
|
||||
* Introducing local services that are shared across extension parts
|
||||
* Interacting with output window and Error List.
|
||||
|
||||
## Prerequisites
|
||||
This extension assumes `markdownlint-cli` npm package is installed globally in order to successfully run linter on markdown files.
|
||||
|
||||
You can run `npm install -g markdownlint-cli` to install the package before running the sample.
|
||||
|
||||
## MarkdownLinterExtension instance
|
||||
Different to previous samples, this extension implements its own class that inherits from `ExtensionWithCommand` as our extension contains commands and also want to introduce local services.
|
||||
|
||||
There are 2 interesting points in the implementation of `MarkdownLinterExtension`:
|
||||
|
||||
* `ResourceManager` property points to the resource dictionary that contains localized entries that would be used for creating an output window pane.
|
||||
* `InitializeServices` method is used to add local services to the dependency injection graph. As noted in [local services section](../Inside_the_SDK/ExtensionAnatomy.md#local-extension-services), the extension utilizes a scoped `MarkdownDiagnosticsService` as the service instance injects `VisualStudioExtensibility` object.
|
||||
|
||||
## MarkdownDiagnosticsService local service
|
||||
In this example, this local service acts as the central for managing markdown file diagnostics. It is responsible for running markdown linter on the files requested and forward errors to diagnostic service.
|
||||
|
||||
For more information on individual APIs used please refer to:
|
||||
|
||||
* Views API for output window
|
||||
* Languages API for diagnostics
|
||||
|
||||
## RunLinterOnCurrentFileCommand
|
||||
One part of the extension is a command that can run markdown linter on the current selected file in Solution Explorer.
|
||||
|
||||
The command retrieves the selection from `IClientContext` instance and forwards the request to local `MarkdownDiagnosticsService` service instance.
|
||||
|
||||
```csharp
|
||||
// Get the selected item URIs from IDE context that reprents the state when command was executed.
|
||||
var selectedItemPaths = new Uri[] { await context.GetSelectedPathAsync(cancellationToken) };
|
||||
|
||||
// Enumerate through each selection and run linter on each selected item.
|
||||
foreach (var selectedItem in selectedItemPaths.Where(p => p.IsFile))
|
||||
{
|
||||
await this.diagnosticsProvider.ProcessFileAsync(selectedItem, cancellationToken);
|
||||
}
|
||||
```
|
||||
|
||||
## TextViewEventListener
|
||||
Another part of the extension is an editor component that listens for new editor view creation and changes to open views. This component monitors for events on `.md` files and routes the request to `MarkdownDiagnosticsService` as contents change.
|
||||
|
||||
Note that because this extension part implements two contract interfaces, it must ensure that both contracts are specified as an extension part:
|
||||
|
||||
```csharp
|
||||
[ExtensionPart(typeof(ITextViewLifetimeListener))]
|
||||
[ExtensionPart(typeof(ITextViewChangedListener))]
|
||||
[AppliesTo(ContentType = "text")]
|
||||
```
|
||||
|
||||
The extension part also utilizes `AppliesTo` attribute to indicate that it is interested in events from views with `text` content type. (Note, this is required as there is no `markdown` content type in Visual Studio)
|
||||
|
||||
Even though this class implements 2 different contracts, a single instance of it will be created so that state can be shared between different editor components that interact together.
|
|
@ -0,0 +1,65 @@
|
|||
# Creating your first Visual Studio extension
|
||||
|
||||
## Prerequisites
|
||||
|
||||
* Visual Studio 2022 Preview 3 with managed languages workload.
|
||||
|
||||
* Visual Studio Extensibility Project Extension: This extension will allow you to debug extension projects using F5. There is currently no other deployment mechanism supported.
|
||||
|
||||
## Create the extension project
|
||||
|
||||
* The extensibility APIs are distributed via nuget packages, so you can start with an empty .NET 6.0 C# class library project.
|
||||
|
||||
* Once project is created change `TargetFramework` from `net6.0` to `net6.0-windows`.
|
||||
|
||||
* Add references to `Microsoft.VisualStudio.Extensibility` and `Microsoft.VisualStudio.Extensibility.Build` packages.
|
||||
|
||||
At this point you are ready to start extending Visual Studio by adding commands and editor components to your extension.
|
||||
|
||||
## Add a command handler
|
||||
|
||||
* Create a new `.cs` file and include the following code:
|
||||
|
||||
```csharp
|
||||
namespace SimpleRemoteCommandSample
|
||||
{
|
||||
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.Definitions;
|
||||
|
||||
[CommandIcon("Extension", IconSettings.IconAndText)]
|
||||
[Command(
|
||||
"SimpleRemoteCommandSample.Command",
|
||||
1 /* command id */,
|
||||
"Sample Remote Command",
|
||||
placement: KnownCommandPlacement.ToolsMenu)]
|
||||
public class CommandHandler : Command
|
||||
{
|
||||
private TraceSource traceSource;
|
||||
|
||||
public CommandHandler(VisualStudioExtensibility extensibility, TraceSource traceSource, ushort id)
|
||||
: base(extensibility, id)
|
||||
{
|
||||
this.traceSource = Requires.NotNull(traceSource, nameof(traceSource));
|
||||
}
|
||||
|
||||
public override Task ExecuteCommandAsync(IClientContext context, CancellationToken cancellationToken)
|
||||
{
|
||||
this.traceSource.TraceInformation($"Executing command {CommandName}");
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
For more information on how to add commands, please refer to `TBD`.
|
||||
|
||||
## Debug your extension
|
||||
|
||||
* Making sure that your extension project is selected as startup projet in Visual Studio, press `F5` to start debugging.
|
||||
|
||||
* This will build your extension and deploy it to experimental instance of Visual Studio version your are using. The debugger should attach once your extension is loaded.
|
|
@ -0,0 +1,15 @@
|
|||
# Visual Studio OOP extensibility model
|
||||
|
||||
## Benefits
|
||||
The new extensibility model is designed with the following goals in mind:
|
||||
|
||||
* Ease of use: We like to have well documented, simple paths for most common operations that extensions perform in Visual Studio. This should translate to shorter development time for small productivity extensions that work with existing languages in Visual Studio.
|
||||
|
||||
* Reliability: As we move extensions and components out-of-process, we can isolate components from which other increasing overall reliability of Visual Studio and also responsiveness of IDE in certain cases.
|
||||
|
||||
* Cross-product: While this is not possible today, a long term goal for this extensibility model is to be cross product compatible as it is primarily based on JsonRPC protocols to communicate with host IDE process.
|
||||
|
||||
## Technology
|
||||
The new extensibility model is primarily built on top remote RPC contracts that are provided as brokered services from Visual Studio. The extensions are hosted in an external ServiceHub process that communicates with Visual Studio via RPC and both utilize services provided by Visual Studio and may also provide services to Visual Studio process in certain cases.
|
||||
|
||||
Extensibility packages that are provided as part of the SDK consist of base classes, utility methods and wrapper objects around these RPC contracts with the goal of making extensibility surface area easier to use, easier to discover and also to be able to quickly contribute to Visual Studio ecosystem such as creating a new command handler.
|
|
@ -0,0 +1,32 @@
|
|||
# New Extensibility Model Overview
|
||||
|
||||
While the existing model loads extensions in-process, the new extensibility model brings Visual Studio extensions out-of-process. This out-of-proc model gives you the opportunity to create more reliable, secure, and easier-to-write extensions while still providing the in-depth functionality the old model provides. The following documentation describes:
|
||||
|
||||
* The general architecture of the new extensibility model
|
||||
* How to take advantage of the new extensibility model’s APIs
|
||||
* How to compile and F5 debug an extension with the new model
|
||||
* Resources and code samples to get started writing an extension with the new model
|
||||
|
||||
## What is the new extensibility model & why should you use it?
|
||||
|
||||
The new, out-of-proc extensibility model aims to address many of the problems both developers experience when using and writing extensions in Visual Studio using the old model. These issues include:
|
||||
|
||||
* Extension-caused crashes and hangs of Visual Studio and other installed extensions
|
||||
* Inconsistent hard-to-find docs and APIs, tribal knowledge requirements, and overwhelming architecture
|
||||
* Lack of secure extensions
|
||||
* Having to restart Visual Studio when installing extensions
|
||||
|
||||
Writing extensions using the new model provides the following benefits:
|
||||
|
||||
* **Increased reliability** – Extensions written with the new model are out-of-proc and will not block the Visual Studio UI. This means that if the extension crashes, VS will not crash as a result.
|
||||
* **Reduced API complexity** – The new model’s wrapper API has been built from the ground-up to be more cohesive and simpler to implement while retaining the old model’s advanced functionality.
|
||||
* **Low-trust extensions (Coming Soon)** – Since extensions in the new model run in a separate process, they can be isolated from devenv and ran in a low-trust sandbox. This environment will provide the extensions with read-only access to the file system and controlled writing access via the model’s APIs.
|
||||
* **Hot-reloading functionality (Coming Soon)** – Excluding ones requiring VS component prerequisites that haven’t been installed yet, extensions written using the new model can be installed and loaded without needing to restart Visual Studio.
|
||||
* **.NET Core Only** – With VS transitioning primarily to .NET core, all new model extensions will be run on .NET core. All APIs will also be built on .NET core and will be .NET 6 compatible.
|
||||
* **Cross-CPU Platform** – Since APIs will be IServiceBroker and .NET core based, all Gladstone extensions will run on all CPU platforms supported by .net core runtime.
|
||||
|
||||
## Next Steps
|
||||
|
||||
To get started writing an extension using the new model, here are some docs, walkthroughs, samples, and videos to get you started:
|
||||
|
||||
**[TODO: Add links to docs here]**
|
|
@ -0,0 +1,74 @@
|
|||
# Rule based activation constraints
|
||||
|
||||
One of the common concepts in Visual Studio Extensibility SDK is use of context based activation rules in code attributes. An example of these would `CommandVisibleWhen` attribute declaring when a command is made visible.
|
||||
|
||||
Our goal is to provide a common way to create such contexts, the current method is based on existing [Rule-based UI contexts](https://docs.microsoft.com/en-us/visualstudio/extensibility/how-to-use-rule-based-ui-context-for-visual-studio-extensions) with a different set of context terms.
|
||||
|
||||
## Constraint attribute arguments
|
||||
|
||||
Each constraint attribute will contain at least 3 required arguments that defines the expression:
|
||||
|
||||
* Expression string: A boolean expression using `and`, `or`, `not` operations and term names that are defined in later arguments. Each term must be a single word (without spaces) and expression can utilize parantheses for grouping and `&`, `|`, `!` operators for `and`, `or`, `not` operations.
|
||||
|
||||
* Term names: An array that contains the name of the terms used in the expression above.
|
||||
|
||||
* Term definitions: An array that defines the each term in the order terms are defined in the names array above.
|
||||
|
||||
## Example definition
|
||||
|
||||
In the example below, the code attributes defines when a command is in enabled state.
|
||||
|
||||
* The expression indicates that command is enabled when both `SolutionLoaded` and `IsValidFile` terms are true.
|
||||
|
||||
* `SolutionLoaded` term is defined as `SolutionState:Exists` which indicates, the term evaluates to `true` when a solution exists in the IDE.
|
||||
|
||||
* `IsValidFile` term is true when selected file in Solution Explorer is a jpeg or text file as defined by the file extension.
|
||||
|
||||
```csharp
|
||||
[CommandEnabledWhen(
|
||||
"SolutionLoaded & IsValidFile",
|
||||
new string[] {
|
||||
"SolutionLoaded",
|
||||
"IsValidFile" },
|
||||
new string[] {
|
||||
"SolutionState:Exists",
|
||||
"ClientContext:Shell.ActiveSelectionFileName=(.jpg|.jpeg|.txt)$" })]
|
||||
```
|
||||
|
||||
## Available terms
|
||||
|
||||
Following is the list of terms currently supported by expression engine.
|
||||
|
||||
| Term | Description
|
||||
| -- | -- |
|
||||
| SolutionHasProjectBuildProperty:\<property>=\<regex> | The term is true when solution has a loaded project with the specified build property and property value matches to regex filter provided. |
|
||||
| SolutionHasProjectCapability:\<expression> | True whenever solution has a project with capabilities matching the provided subexpression. An expression can be something like VB | CSharp. |
|
||||
| SolutionHasProjectFlavor:\<guid> | True whenever a solution has project that is flavored (aggregated) and has a flavor matching the given project type GUID. |
|
||||
| SolutionState:\<state> | True when solution state matches to provided value, see [solution states](#solution-states) for list of values. |
|
||||
| ProjectAddedItem:\<pattern> | The term is true when a file matching the "pattern" is added to a project in the soluion that is opened. |
|
||||
| ClientContext:\<key>=\<pattern> | True when the provided client context key matches to regular expression. See [client contexts](#client-contexts) for more details. |
|
||||
|
||||
## Solution states
|
||||
|
||||
| State | Description |
|
||||
| -- | -- |
|
||||
| NoSolution | No solution loaded. |
|
||||
| Exists | A solution is opened but may be in loaded or loading state. |
|
||||
| FullyLoaded | A solution is opened and fully loaded. |
|
||||
| Empty | Solution contains no projects but may contain solution items. |
|
||||
| SingleProject | Solution contains a single project. |
|
||||
| MultipleProject | Solution contains multiple projects. |
|
||||
| Building | Solution is building. |
|
||||
|
||||
## Client contexts
|
||||
Activation rules can also utilize the [client context](ExtensionAnatomy.md#client-context) contents as parts of its expression.
|
||||
|
||||
Currently, the client context is limited to a small set of values in IDE state:
|
||||
|
||||
| Context key | Definition |
|
||||
| -- | -- |
|
||||
| Shell.ActiveSelectionUri | Full URI for the selected item in solution explorer. |
|
||||
| Shell.ActiveSelectionPath | Full path for the selected item in solution explorer. |
|
||||
| Shell.ActiveSelectionFileName | File name of the selected item in solution explorer. |
|
||||
| Shell.ActiveEditorContentType | Content type of the active editor view. |
|
||||
| Shell.ActiveEditorFileName | File name for the document that belongs to active editor view. |
|
|
@ -0,0 +1,63 @@
|
|||
# Anatomy of a new Visual Studio Extension
|
||||
An extension utilizing the new extensibility SDK will have several components that interact together and also with Visual Studio.
|
||||
|
||||
## Extension instance
|
||||
The starting point for each extension is an instance of `Microsoft.VisualStudio.Extensibility.Extension`. This instance contains the necessary methods for Visual Studio to query services provided by the extension and also provides virtual methods for extension to provide localized resourcs and extension owned local services to be shared between the components of the extension.
|
||||
|
||||
For simple extensions that don't require such services, a default implementation will be created by code generators at build time. See `SimpleRemoteCommandSample` extension for such an example.
|
||||
|
||||
In other cases, such as `MarkdownLinter` extension project will have its own class that derives from `Microsoft.VisualStudio.Extensibility.Extension` to customize certain aspects of the extension.
|
||||
|
||||
For extension developers that are familiar with the existing VS SDK APIs, this class is very similar to `AsyncPackage` class that is used in the in-process extensibility model.
|
||||
|
||||
## VisualStudioExtensibility object
|
||||
The object `Microsoft.VisualStudio.Extensibility.VisualStudioExtensibility` acts as the entry point for extensibility features exposed by Visual Studio. This class will have various extension methods, properties to quickly enumerate through available features in extensibility SDK. Please see the (TBD:link) API documentation for available methods.
|
||||
|
||||
## Extension parts
|
||||
For features where an extension contributes components to Visual Studio such as commands, editor listeners, extensions will utilize attributed classes. Build process will generate the correct metadata to ensure these components can be discovered by Visual Studio.
|
||||
|
||||
Currently the SDK supports a limited set of components to be exported:
|
||||
|
||||
* [Command handlers](TBD)
|
||||
* [Text view lifetime listeners](TBD) for tracking text view created, closed events.
|
||||
* [Text view change listeners](TBD) for tracking changes to an open text view.
|
||||
|
||||
Instances for these classes will be created as part of the extensibility framework provided by the SDK using dependency injection library and constructors can be used to retrieve instances of services provided by either the SDK or by the extension itself to share state across components.
|
||||
|
||||
### Lifetime of extension parts
|
||||
Lifetime of each part is managed by the respective commponent that loads those parts inside Visual Studio IDE process.
|
||||
|
||||
* Command handlers will be initialized when the corresponding command set is activated which can be during the first execution of the command. Once activated, command handlers should only be disposed when IDE is shutdown.
|
||||
|
||||
* Similarly text view event listeners will be initialized when the first text view matching the content type specified is loaded in the IDE. Currently, such listeners will be active until IDE is shutdown but this behavior may change in future.
|
||||
|
||||
In general, for complex extensions we recommend extension to provide local services that parts can import in their constructor and using those services to share state across parts and across instances of the same part. This will ensure that extension state is not affected by lifetime changes of extension parts.
|
||||
|
||||
### Services provided by SDK for injection
|
||||
The following services are provided by the SDK that can be used in constructor for any extension part:
|
||||
|
||||
* `VisualStudioExtensibility`: Every extension part can inject an instance of `VisualStudioExtensibility` to interact with Visual Studio IDE.
|
||||
|
||||
* `Extension`: Parts can inject `Microsoft.VisualStudio.Extensibility.Extension` type or the extensions own type inheriting from it to extension parts.
|
||||
|
||||
* `TraceSource`: A trace source instance is created on demand for each extension that can be used to record diagnostic information. These instances are registered with Visual Studio diagnostics provider which can be used to merge logs from multiple services and utilize future tooling to access real time logging. See [Logging](Logging.md) section for more information.
|
||||
|
||||
* Local services: Any local services provided by the extension itself will also be available for dependency injection.
|
||||
|
||||
## Local extension services
|
||||
In certain scenarios an extension may want to share state between different components such as a command handler and a text view change listener, as it can be seen in `MarkdownLinter` example. These services can be added to in-process service collection via overriding `Extension.InitializeServices` method and as instances of extension parts are created, the services will be injected based on the constructor arguments.
|
||||
|
||||
There are 3 options that a service can be added:
|
||||
|
||||
* `AddTransient`: A new instance of the service is created for each part that ingests it.
|
||||
* `AddScoped`: A new instance of the service is created within a certain scope. In context of Visual Studio extensibility, scope refers to a single extension part.
|
||||
* `AddSingleton`: There is a single shared instance of service that is created on first ingestion.
|
||||
|
||||
Due to lifetime of `VisualStudioExtensibility` object being bound to the scope of a single extension part, any local service that ingests it has to be a scoped or transient service. Trying to create a singleton service that injects `VisualStudioExtensibility` will result in failure.
|
||||
|
||||
For an example of how local services are used please see [MarkdownLinte extension guide](../Extension_Guides/MarkdownLinterSample.md).
|
||||
|
||||
## Client context
|
||||
As all extensions in the new SDK runs out of process, we introduce the concept of client context for various extension parts to represent the state of the IDE as when the event or method is invoked. This context is represented by `IClientContext` instance in the SDK and is passed in to various operations such as command execution handlers. The SDK provides extension methods on `IClientContext` that can be utilized to retrieve objects from the context. For example extensions can get the active text view or the URI for the selected items at the time of command execution utilizing `IClientContext` instance.
|
||||
|
||||
Some components such as commands, will also have allow extension developers to declare which contexts they are interested. This is done to optimize the amount of data transferred in each remote execution since client context can get large in the future. In the initial preview, there are only two available contexts `Shell` and `Editor` and both are included by default when declaring a command using `CommandAttribute`.
|
|
@ -0,0 +1,11 @@
|
|||
# Inside Visual Studio Extensibility SDK
|
||||
|
||||
The new Visual Studio Extensibility SDK is built on top of brokered services infrastructure that was introduced in Visual Studio 2019. It primarily involves of:
|
||||
|
||||
* A `VisualStudioExtensibility` object that acts as the entry point for the extensibility API. This object will have extension methods on it such as `Views()` or `Languages()` that exposes extensibility points of the IDE.
|
||||
|
||||
* Wrappers and extension methods around brokered services exposed by [IServiceBroker](https://docs.microsoft.com/en-us/dotnet/api/microsoft.servicehub.framework.iservicebroker?view=visualstudiosdk-2019) infrastructure with the goal of making services easy to use. In some cases, extension methods will return the contract interface directly.
|
||||
|
||||
* Base classes for extension parts, commands and the extension itself which are used to expose brokered services from extension to Visual Studio IDE. These services are the primary method that Visual Studio calls in to the extension.
|
||||
|
||||
* A code generator framework to generate `extension.json` metadata file based on extension parts and its attributes. This file declares the services, extension parts and other services exposed by the extension.
|
|
@ -0,0 +1,7 @@
|
|||
# Logging extension diagnostics
|
||||
|
||||
Each extension part can inject a `TraceSource` instance that is created as part of the extensibility framework. Extension developers are strongly recommended to utilize this instance for diagnostics as it will allow integration with future tools on Visual Studio diagnostics.
|
||||
|
||||
## Accessing log entries
|
||||
|
||||
Currently the logs are output to `%TEMP%\VSLogs` directory in `svclog` XML format. This format allows logs from multiple processes to be correlated to each other and can be opened by [Microsoft Service Trace Viewer](https://docs.microsoft.com/en-us/dotnet/framework/wcf/service-trace-viewer-tool-svctraceviewer-exe) tool. This tool should already be available on machines with Visual Studio as it is part of .Net Framework tools.
|
|
@ -0,0 +1,17 @@
|
|||
# Visual Studio Extensibility Nuget Packages
|
||||
The new extensibilty model for Visual Studio depends on a set of new nuget packages to provide APIs, build tooling, code generation and analyzers.
|
||||
|
||||
## Microsoft.VisualStudio.Extensibility
|
||||
Link: <TBD>
|
||||
|
||||
This package contains the SDK APIs and utility libraries to help develop extensions using the out-of-process extensibility model. Every extension should include a reference to this package as a starting point.
|
||||
|
||||
## Microsoft.VisualStudio.Extensibility.Build
|
||||
Link: <TBD>
|
||||
|
||||
This package contains the build tooling, code generators that is necessary for extension metadata to be generated during build. Without this package, a compiled extension may not work correctly as it will not contain the necessary metadata files.
|
||||
|
||||
## Microsoft.VisualStudio.Extensibility.Editor
|
||||
This package contains the editor related SDK APIs and utilities and is included as a dependency from `Microsoft.VisualStudio.Extensibility`.
|
||||
|
||||
In future previews, we may have similar nuget packages that are not included as a required dependency but can be added as needed to provide APIs for certain feature areas such as debugger, source control.
|
|
@ -0,0 +1,2 @@
|
|||
User-agent: *
|
||||
Disallow:
|
Загрузка…
Ссылка в новой задаче