Partilhar via


Trabalhar com texto no editor

Seu código de extensão pode ser configurado para ser executado em resposta a vários pontos de entrada (situações que ocorrem quando um usuário interage com o Visual Studio). Atualmente, a extensibilidade do editor suporta três pontos de entrada: ouvintes, o objeto de serviço EditorExtensibility, e comandos.

Os ouvintes de eventos são acionados quando determinadas ações ocorrem em uma janela do editor, representada no código por um TextView. Por exemplo, quando um usuário digita algo no editor, ocorre um evento TextViewChanged. Quando uma janela do editor é aberta ou fechada, ocorrem eventos TextViewOpened e TextViewClosed.

O objeto de serviço do editor é uma instância da classe EditorExtensibility, que expõe a funcionalidade do editor em tempo real, como a execução de edições de texto.

Comandos são iniciados pelo usuário clicando em um item, que você pode colocar em um menu, menu de contexto ou barra de ferramentas.

Adicionar um listener de visualização de texto

Há dois tipos de ouvintes, ITextViewChangedListener e ITextViewOpenClosedListener. Juntos, esses ouvintes podem ser usados para observar o abrir, fechar e modificar editores de texto.

Em seguida, crie uma nova classe, implementando a ExtensionPart classe base e ITextViewChangedListener, ITextViewOpenClosedListenerou ambos os dois, e adicione um VisualStudioContribution atributo.

Em seguida, implemente a propriedade TextViewExtensionConfiguration, conforme exigido por ITextViewChangedListener e ITextViewOpenClosedListener, fazendo com que o listener seja aplicado ao editar arquivos C#:

public TextViewExtensionConfiguration TextViewExtensionConfiguration => new()
{
    AppliesTo = new[] { DocumentFilter.FromDocumentType("CSharp") },
};

Os tipos de documento disponíveis para outras linguagens de programação e tipos de arquivo são listados mais adiante neste artigo, e os tipos de arquivo personalizados também podem ser definidos quando necessário.

Supondo que você decida implementar ambos os escutadores, a declaração de classe concluída deve ficar assim:

  [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") },
      };
      ...

Como ITextViewOpenClosedListener e ITextViewChangedListener declaram a propriedade TextViewExtensionConfiguration, a configuração aplica-se a ambos os ouvintes.

Ao executar sua extensão, você deve ver:

Cada um desses métodos é passado para um ITextViewSnapshot que contém o estado da exibição de texto e do documento de texto no momento em que o usuário invocou a ação e um CancellationToken que terá IsCancellationRequested == true quando a IDE desejar cancelar uma ação pendente.

Definir quando a sua extensão é relevante

Sua extensão geralmente é relevante apenas para determinados tipos de documentos e cenários suportados, e por isso é importante definir claramente sua aplicabilidade. Você pode usar a configuração AppliesTode várias maneiras para definir claramente a aplicabilidade de uma extensão. Você pode especificar quais tipos de arquivo, como linguagens de código, a extensão suporta e/ou refinar ainda mais a aplicabilidade de uma extensão fazendo a correspondência em um padrão com base no nome do arquivo ou caminho.

Especificar linguagens de programação com a configuração AppliesTo

A configuração AppliesTo indica os cenários de linguagem de programação nos quais a extensão deve ser ativada. Escreve-se como AppliesTo = new[] { DocumentFilter.FromDocumentType("CSharp") }, onde o tipo de documento é um nome bem conhecido de uma linguagem integrada no Visual Studio, ou definido de forma personalizada numa extensão do Visual Studio.

Alguns tipos de documentos conhecidos são mostrados na tabela a seguir:

Tipo de Documento Descrição
"CSharp" C#
"C/C++" C, C++, cabeçalhos e IDL
"TypeScript" Linguagens de tipo TypeScript e JavaScript.
"HTML" HTML
"JSON" JSON
"texto" Ficheiros de texto, incluindo descendentes hierárquicos de "código", que é descendente de "texto".
"Código" C, C++, C# e assim por diante.

DocumentTypes são hierárquicos. Ou seja, C# e C++ descendem de "código", portanto, declarar "código" faz com que sua extensão seja ativada para todas as linguagens de código, C#, C, C++ e assim por diante.

Definir um novo tipo de documento

Você pode definir um novo tipo de documento, por exemplo, para dar suporte a uma linguagem de código personalizada, adicionando uma propriedade estática DocumentTypeConfiguration a qualquer classe no projeto de extensão e marcando a propriedade com o atributo VisualStudioContribution.

DocumentTypeConfiguration permite definir um novo tipo de documento, especificar que ele herda um ou mais tipos de documento e especificar uma ou mais extensões de arquivo usadas para identificar o tipo de arquivo:

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,
    };
}

As definições de tipo de documento são mescladas com as definições de tipo de conteúdo fornecidas pela extensibilidade herdada do Visual Studio, que permite mapear extensões de arquivo adicionais para tipos de documento existentes.

Seletores de documentos

Além de DocumentFilter.FromDocumentType , DocumentFilter.FromGlobPattern permite limitar ainda mais a aplicabilidade da extensão, ativando-a somente quando o caminho do arquivo do documento corresponder a um padrão glob (curinga):

[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),
        },
    };

O parâmetro pattern representa um padrão glob que é correspondido no caminho absoluto do documento.

Os padrões Glob podem ter a seguinte sintaxe:

  • * corresponder a zero ou mais caracteres num segmento de caminho
  • ? corresponder em um caractere em um segmento de caminho
  • ** corresponder a qualquer número de segmentos de caminho, incluindo nenhum
  • {} para condições de grupo (por exemplo, **​/*.{ts,js} corresponde a todos os arquivos TypeScript e JavaScript)
  • [] declarar um intervalo de caracteres para corresponder num segmento de caminho (por exemplo, example.[0-9] corresponder a example.0, example.1, ...)
  • [!...] negar um intervalo de caracteres a corresponder num segmento de caminho (por exemplo, example.[!0-9] corresponder a example.a, example.b, mas não example.0)

Uma barra invertida (\) não é válida dentro de um padrão glob. Certifique-se de converter qualquer barra invertida em barra ao criar o padrão glob.

Aceder à funcionalidade do editor

Suas classes de extensão do editor herdam de ExtensionPart. A classe ExtensionPart expõe a propriedade Extensibilidade . Usando essa propriedade, você pode solicitar uma instância do objeto EditorExtensibility. Você pode usar esse objeto para acessar a funcionalidade do editor em tempo real, como a execução de edições.

EditorExtensibility editorService = this.Extensibility.Editor();

Acesso ao estado do editor dentro de um comando

ExecuteCommandAsync() em cada Command é passado um IClientContext que contém um instantâneo do estado do IDE no momento em que o comando foi invocado. Você pode acessar o documento ativo através da interface ITextViewSnapshot, que você obtém a partir do objeto EditorExtensibility chamando o método assíncrono GetActiveTextViewAsync:

using ITextViewSnapshot textView = await this.Extensibility.Editor().GetActiveTextViewAsync(clientContext, cancellationToken);

Depois de ter ITextViewSnapshot, podes aceder ao estado do editor. ITextViewSnapshot é uma exibição imutável do estado do editor em um determinado momento, portanto, você precisa usar as outras interfaces no modelo de objeto do Editor para fazer edições.