Поделиться через


Работа с текстом в редакторе

Код расширения можно настроить для выполнения в ответ на различные точки входа (ситуации, возникающие при взаимодействии пользователя с 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, конфигурация применяется к обоим прослушивателям.

При запуске расширения вы увидите следующее:

Каждому из этих методов передается 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 — это неизменяемое представление состояния редактора в определенный момент времени, поэтому для внесения изменений необходимо использовать другие интерфейсы в объектной модели редактора .