Работа с текстом в редакторе
Код расширения можно настроить для выполнения в ответ на различные точки входа (ситуации, возникающие при взаимодействии пользователя с Visual Studio). Расширяемость редактора в настоящее время поддерживает три точки входа: прослушиватели, EditorExtensibility объект сервиса и команды.
Прослушиватели событий активируются при возникновении определенных действий в окне редактора, представленном в коде TextView
. Например, когда пользователь вводит что-то в редактор, происходит событие TextViewChanged
. При открытии или закрытии окна редактора происходят события TextViewOpened
и TextViewClosed
.
Объект службы редактора — это экземпляр класса EditorExtensibility
, который предоставляет функциональные возможности редактора в режиме реального времени, такие как выполнение редактирования текста.
команды инициируются пользователем, щелкнув элемент, который можно разместить в меню, контекстном меню или панели инструментов.
Добавьте слушатель текстового представления
Существует два типа слушателей, ITextViewChangedListener и ITextViewOpenClosedListener. Вместе эти прослушиватели можно использовать для наблюдения за открытием, закрытием и изменением текстовых редакторов.
Затем создайте новый класс, реализуя базовый класс ExtensionPart и ITextViewChangedListener
, ITextViewOpenClosedListener
или оба, и добавьте атрибут VisualStudioContribution.
Затем реализуйте свойство TextViewExtensionConfiguration в соответствии с требованиями ITextViewChangedListener и ITextViewOpenClosedListener, чтобы прослушиватель применялся при редактировании файлов C#.
public TextViewExtensionConfiguration TextViewExtensionConfiguration => new()
{
AppliesTo = new[] { DocumentFilter.FromDocumentType("CSharp") },
};
Доступные типы документов для других языков программирования и типов файлов перечислены далее в этой статье, а настраиваемые типы файлов также могут быть определены при необходимости.
Если вы решили реализовать оба прослушивателя, готовое объявление класса должно выглядеть следующим образом:
[VisualStudioContribution]
public sealed class TextViewOperationListener :
ExtensionPart, // This is the extension part base class containing infrastructure necessary to use VS services.
ITextViewOpenClosedListener, // Indicates this part listens for text view lifetime events.
ITextViewChangedListener // Indicates this part listens to text view changes.
{
public TextViewExtensionConfiguration TextViewExtensionConfiguration => new()
{
// Indicates this part should only light up in C# files.
AppliesTo = new[] { DocumentFilter.FromDocumentType("CSharp") },
};
...
Так как и ITextViewOpenClosedListener, и ITextViewChangedListener объявляют свойство TextViewExtensionConfiguration, конфигурация применяется к обоим прослушивателям.
При запуске расширения вы увидите следующее:
- ITextViewOpenClosedListener.TextViewOpenedAsync вызывается каждый раз, когда пользователь открывает окно для просмотра текста.
- ITextViewOpenClosedListener.TextViewClosedAsync вызывается всякий раз, когда текстовое представление закрывается пользователем.
- ITextViewChangedListener.TextViewChangedAsync вызывается, когда пользователь изменяет текст в документе, отображаемом текстовым представлением.
Каждому из этих методов передается ITextViewSnapshot, содержащий состояние текстового представления и документа на момент вызова действия пользователем, и CancellationToken, который будет иметь IsCancellationRequested == true
, когда интегрированная среда разработки хочет отменить ожидающее действие.
Определите, когда ваше расширение актуально
Расширение обычно относится только к определенным поддерживаемым типам документов и сценариям, поэтому важно четко определить его применимость. Вы можете использовать конфигурацию AppliesTo) несколькими способами, чтобы четко определить применимость расширения. Можно указать, какие типы файлов, такие как языки кода, поддерживает расширение, и (или) дополнительно уточнить применимость расширения путем сопоставления с шаблоном на основе имени файла или пути.
Указание языков программирования с помощью конфигурации AppliesTo
Конфигурация AppliesTo указывает на сценарии программирования, в которых должно активироваться расширение. Он обозначается как AppliesTo = new[] { DocumentFilter.FromDocumentType("CSharp") }
, где тип документа является хорошо известным названием языка, встроенного в Visual Studio, либо пользовательским, определённым в расширении Visual Studio.
Некоторые известные типы документов показаны в следующей таблице:
Тип документа | Описание |
---|---|
"CSharp" | C# |
"C/C++" | C, C++, заголовки и IDL |
TypeScript | Языки типов TypeScript и JavaScript. |
"HTML" | HTML |
JSON | JSON |
"текст" | Текстовые файлы, включая иерархических потомков "кода", который происходит от "текста". |
код | C,C++, C#и т. д. |
DocumentTypes являются иерархическими. То есть C# и C++ происходят от "кода", поэтому объявление "кода" приводит к активации расширения для всех языков кода: C#, C, C++, и т. д.
Определение нового типа документа
Можно определить новый тип документа, например для поддержки пользовательского языка кода, добавив статический DocumentTypeConfiguration свойство в любой класс проекта расширения и пометив свойство атрибутом VisualStudioContribution
.
DocumentTypeConfiguration
позволяет определить новый тип документа, указать, что он наследует один или несколько других типов документов, а также указать одно или несколько расширений файлов, используемых для идентификации типа файла:
using Microsoft.VisualStudio.Extensibility.Editor;
internal static class MyDocumentTypes
{
[VisualStudioContribution]
internal static DocumentTypeConfiguration MarkdownDocumentType => new("markdown")
{
FileExtensions = new[] { ".md", ".mdk", ".markdown" },
BaseDocumentType = DocumentType.KnownValues.Text,
};
}
Определения типов документов объединяются с определениями типов контента, предоставляемыми устаревшей расширяемостью Visual Studio, что позволяет сопоставить дополнительные расширения файлов с существующими типами документов.
Селекторы документов
Помимо DocumentFilter.FromDocumentType, DocumentFilter.FromGlobPattern позволяет дополнительно ограничить применимость расширения, делая его активацией только в том случае, если путь к файлу документа соответствует шаблону глоба (подстановочного знака):
[VisualStudioContribution]
public sealed class TextViewOperationListener
: ExtensionPart, ITextViewOpenClosedListener, ITextViewChangedListener
{
public TextViewExtensionConfiguration TextViewExtensionConfiguration => new()
{
AppliesTo = new[]
{
DocumentFilter.FromDocumentType("CSharp"),
DocumentFilter.FromGlobPattern("**/tests/*.cs"),
},
};
[VisualStudioContribution]
public sealed class TextViewOperationListener
: ExtensionPart, ITextViewOpenClosedListener, ITextViewChangedListener
{
public TextViewExtensionConfiguration TextViewExtensionConfiguration => new()
{
AppliesTo = new[]
{
DocumentFilter.FromDocumentType(MyDocumentTypes.MarkdownDocumentType),
DocumentFilter.FromGlobPattern("docs/*.md", relativePath: true),
},
};
Параметр pattern
представляет шаблон глобов, соответствующий абсолютному пути документа.
Шаблоны glob могут иметь следующий синтаксис:
-
*
используется для соответствия нулю или нескольким символам в сегменте пути -
?
соответствует одному символу в cегменте пути -
**
соответствует любому количеству сегментов пути, включая отсутствие. -
{}
для группирования условий (например,**/*.{ts,js}
соответствует всем файлам TypeScript и JavaScript) -
[]
объявляет диапазон символов для сопоставления в сегменте пути (например,example.[0-9]
сопоставляется сexample.0
,example.1
, ...) -
[!...]
, чтобы исключить диапазон символов из сопоставления в сегменте пути (например,example.[!0-9]
включаетexample.a
иexample.b
, но неexample.0
).
Обратная косая черта (\
) не является допустимой в шаблоне глоба. Обязательно преобразуйте обратную косую черту в косую черту при создании шаблона глобов.
Доступ к функциям редактора
Классы расширений редактора наследуются от ExtensionPart. Класс ExtensionPart
предоставляет свойство расширяемости. С помощью этого свойства можно запросить экземпляр объекта EditorExtensibility. Этот объект можно использовать для доступа к функциям редактора в режиме реального времени, таким как выполнение изменений.
EditorExtensibility editorService = this.Extensibility.Editor();
Доступ к состоянию редактора в команде
ExecuteCommandAsync()
в каждом Command
передается IClientContext
, содержащий снимок состояния IDE на момент вызова команды. Доступ к активному документу можно получить через интерфейс ITextViewSnapshot
, полученный из объекта EditorExtensibility
, вызвав асинхронный метод GetActiveTextViewAsync
:
using ITextViewSnapshot textView = await this.Extensibility.Editor().GetActiveTextViewAsync(clientContext, cancellationToken);
После того как у вас будет ITextViewSnapshot
, вы сможете получить доступ к состоянию редактора.
ITextViewSnapshot
— это неизменяемое представление состояния редактора в определенный момент времени, поэтому для внесения изменений необходимо использовать другие интерфейсы в объектной модели редактора .