Создание простого расширения

В разделе "Создание первого расширения" вы узнали, как использовать шаблон проекта VisualStudio.Extensibility для создания проекта расширения и узнали, как создать его и отладить в экспериментальном экземпляре Visual Studio.

В этом руководстве описано, как создать расширение с помощью простой команды, которая выполняет что-то в редакторе Visual Studio. В этом случае он вставляет только что созданный GUID. Вы также узнаете, как сообщить Visual Studio о том, какие типы файлов включено расширение GUID, и как создать новую команду в виде панели инструментов или элемента меню.

Полный пример для этого руководства можно найти здесь.

В этом руководстве приведены следующие действия.

Настройка команды

На этом шаге вы узнаете о параметрах настройки и размещения команды. Цель размещения команды заключается в том, чтобы предоставить его пользователю каким-то образом, например добавить пункт меню или кнопку панели команд.

Шаблон проекта или пример, созданный в руководстве по созданию первого расширения , состоит из одного файла C#, который уже включает Command класс. Это можно обновить.

  1. Переименуйте Command1.cs файл InsertGuidCommand.csв , переименуйте класс InsertGuidCommand, обновите CommandConfiguration свойство.

    public override CommandConfiguration CommandConfiguration => new("%InsertGuidCommand.DisplayName%")
    {
        Placements = new[] { CommandPlacement.KnownPlacements.ExtensionsMenu },
    };
    

    Placements указывает, где команда должна отображаться в интегрированной среде разработки. В этом случае команда помещается в меню "Расширения", одно из меню верхнего уровня в Visual Studio.

    Аргумент CommandConfiguration конструктора — отображаемое имя команды, которое является текстом меню. Отображаемое имя заключено символами % , так как ссылается на строковый ресурс из .vsextension/string-resources.json поддержки локализации.

  2. Обновите .vsextension/string-resources.json отображаемое имя InsertGuidCommand.

    {
      "InsertGuidCommand.DisplayName": "Insert new guid"
    }
    
  3. Добавление свойства Icon

    public override CommandConfiguration CommandConfiguration => new("%InsertGuidCommand.DisplayName%")
    {
        Placements = new[] { CommandPlacement.KnownPlacements.ExtensionsMenu },
        Icon = new(ImageMoniker.KnownValues.OfficeWebExtension, IconSettings.IconAndText),
    };
    

    В этом случае OfficeWebExtensionможно указать известный встроенный значок или отправить изображения для значка, как описано в командах Add Visual Studio. Второй аргумент — это перечисление, определяющее, как команда должна отображаться на панелях инструментов (помимо его места в меню). IconSettings.IconAndText Параметр означает отображение значка и отображаемого имени рядом друг с другом.

  4. VisibleWhen Добавьте свойство, указывающее условия, которые должны применяться для элемента, отображаемого пользователю.

    public override CommandConfiguration CommandConfiguration => new("%InsertGuidCommand.DisplayName%")
    {
        Placements = new[] { CommandPlacement.KnownPlacements.ExtensionsMenu },
        Icon = new(ImageMoniker.KnownValues.OfficeWebExtension, IconSettings.IconAndText),
        VisibleWhen = ActivationConstraint.ClientContext(ClientContextKey.Shell.ActiveEditorContentType, ".+"),
    };
    

Дополнительные сведения см. в разделе об использовании ограничений активации на основе правил.

Создание метода выполнения

На этом шаге вы реализуете метод команды ExecuteCommandAsync , который определяет, что происходит, когда пользователь выбирает пункт меню или нажимает элемент на панели инструментов для команды.

Скопируйте следующий код для реализации метода.

public override async Task ExecuteCommandAsync(IClientContext context, CancellationToken cancellationToken)
    {
        Requires.NotNull(context, nameof(context));
        var newGuidString = Guid.NewGuid().ToString("N", CultureInfo.CurrentCulture);

        using var textView = await context.GetActiveTextViewAsync(cancellationToken);
        if (textView is null)
        {
            this.logger.TraceInformation("There was no active text view when command is executed.");
            return;
        }

        await this.Extensibility.Editor().EditAsync(
            batch =>
            {
                textView.Document.AsEditable(batch).Replace(textView.Selection.Extent, newGuidString);
            },
            cancellationToken);
    }

Первая строка проверяет аргументы, а затем мы создадим новый Guid для последующего использования.

Затем мы создадим ITextViewSnapshot объект ( textView здесь) путем вызова асинхронного метода GetActiveTextViewAsync. Маркер отмены передается для сохранения возможности отмены асинхронного запроса, но эта часть не показана в этом примере. Если мы не получаем текстовое представление успешно, мы записываем в журнал и завершаем работу, не выполняя ничего другого.

Теперь мы готовы вызвать асинхронный метод, который отправляет запрос на изменение в редактор Visual Studio. Метод, который мы хотим, — EditAsyncэто . Это член EditorExtensibility класса, который позволяет взаимодействовать с запущенным редактором Visual Studio в интегрированной среде разработки. Тип Command , от которого наследуется собственный InsertGuidCommand класс, имеет член Extensibility , предоставляющий доступ к EditorExtensibility объекту, поэтому мы можем перейти к EditorExtensibility классу с вызовом this.Extensibility.Editor().

Метод EditAsync принимает в Action<IEditBatch> качестве параметра. Этот параметр вызывается editorSource,

Вызов использования EditAsync лямбда-выражения. Чтобы разбить это немного, вы также можете написать этот вызов следующим образом:

await this.Extensibility.Editor().EditAsync(
    batch =>
    {
        var editor = textView.Document.AsEditable(batch);
        // specify the desired changes here:
        editor.Replace(textView.Selection.Extent, newGuidString);
    },
    cancellationToken);

Этот вызов можно рассматривать как указание кода, который требуется запустить в процессе редактора Visual Studio. Лямбда-выражение указывает, что нужно изменить в редакторе. IEditBatchТипbatch, который подразумевает, что лямбда-выражение, определенное здесь, делает небольшой набор изменений, которые должны быть выполнены как единица, а не прерваны другими изменениями пользователем или языковой службой. Если код выполняется слишком долго, это может привести к неответственности, поэтому важно сохранить изменения в этом лямбда-выражении ограничено и понять, что может привести к задержкам.

AsEditable Используя метод документа, вы получите временный объект редактора, который можно использовать для указания нужных изменений. Думайте обо всем в лямбда-выражении как запрос на выполнение Visual Studio, а не как фактическое выполнение, так как описано в расширяемости редактора Visual Studio, существует определенный протокол для обработки этих асинхронных запросов на редактирование из расширений, и есть возможность принятия изменений, например, если пользователь вносит изменения в то же время, что создает конфликт.

Шаблон EditAsync можно использовать для изменения текста в целом, указав изменения после комментария "указать нужные изменения здесь".