Partilhar via


Alterar texto no editor

As edições, ou seja, as alterações em um documento de texto aberto no editor do Visual Studio, podem surgir de interações do usuário no Visual Studio ou alterações programáticas de serviços de linguagem e outras extensões. Sua extensão deve estar preparada para lidar com alterações no texto do documento que ocorram em tempo real.

As extensões executadas fora do processo IDE principal do Visual Studio usam padrões de design assíncronos para se comunicar com o processo IDE do Visual Studio. Isso significa o uso de chamadas de método assíncronas, conforme indicado pela palavra-chave async em C# e reforçado pelo sufixo Async em nomes de método. A assincronicidade é uma vantagem significativa no contexto de um editor que se espera que responda às ações do usuário. Uma chamada de API síncrona tradicional, se demorar mais do que o esperado, deixará de responder à entrada do usuário, criando um congelamento da interface do usuário que dura até que a chamada da API seja concluída. As expectativas dos usuários dos aplicativos interativos modernos são que os editores de texto sempre permaneçam responsivos e nunca os impeçam de funcionar. Ter extensões assíncronas é, portanto, essencial para atender às expectativas do usuário.

Saiba mais sobre programação assíncrona em Programação assíncrona com async e await.

No novo modelo de extensibilidade do Visual Studio, a extensão é de segunda classe em relação ao usuário: ela não pode modificar diretamente o editor ou o documento de texto. Todas as alterações de estado são assíncronas e cooperativas, com o IDE do Visual Studio executando a alteração solicitada em nome da extensão. A extensão pode solicitar uma ou mais alterações em uma versão específica do documento ou exibição de texto, mas as alterações de uma extensão podem ser rejeitadas, como se essa área do documento foi alterada.

As edições são solicitadas usando o método EditAsync() em EditorExtensibility.

Se você estiver familiarizado com extensões herdadas do Visual Studio, ITextDocumentEditor é quase o mesmo que os métodos de alteração de estado de ITextBuffer e ITextDocument e oferece suporte à maioria dos mesmos recursos.

MutationResult result = await this.Extensibility.Editor().EditAsync(
batch =>
{
    var editor = document.AsEditable(batch);
    editor.Replace(textView.Selection.Extent, newGuidString);
},
cancellationToken);

Para evitar edições mal colocadas, as edições das extensões do editor são aplicadas da seguinte forma:

  1. A extensão solicita que seja feita uma edição, com base na sua versão mais recente do documento.
  2. Essa solicitação pode conter uma ou mais edições de texto, alterações de posição de cursor e assim por diante. Qualquer tipo que implemente IEditable pode ser alterado numa única solicitação EditAsync(), incluindo ITextViewSnapshot e ITextDocumentSnapshot. As edições são feitas pelo editor, que podem ser solicitadas numa classe específica via AsEditable().
  3. As solicitações de edição são enviadas para o IDE do Visual Studio, onde ele terá êxito somente se o objeto que está sendo mutado não tiver sido alterado desde a versão em que a solicitação foi feita. Se o documento tiver sido alterado, a alteração poderá ser rejeitada, exigindo que a extensão seja repetida na versão mais recente. O resultado da operação de mutação é armazenado em result.
  4. As edições são aplicadas atomicamente, ou seja, sem interrupção de outros threads em execução. A prática recomendada é realizar todas as alterações que devem ocorrer num curto período de tempo numa única chamada EditAsync(), para reduzir a probabilidade de comportamento inesperado decorrente de edições do utilizador ou ações do serviço de linguagem que ocorrem entre edições (por exemplo, edições de extensão sendo intercaladas com o Roslyn C# a mover o ponteiro).

Alterar a posição do cursor ou selecionar texto de uma extensão

A edição de um documento de texto a partir de uma extensão afeta implicitamente a posição do cursor. Por exemplo, inserir algum texto no cursor moverá o cursor para o final do texto inserido. As extensões também podem usar ITextViewSnapshot.AsEditable().SetSelections() para definir explicitamente o cursor numa posição diferente ou fazer seleções de texto. Para ilustrar, o código a seguir inseriria algum texto, mas manteria o cursor na posição original:

await this.Extensibility.Editor().EditAsync(batch =>
{
    var caret = textView.Selection.Extent.Start;
    textView.Document.AsEditable(batch).Replace(textView.Selection.Extent, newGuidString);
    textView.AsEditable(batch).SetSelections([new Selection(activePosition: caret, anchorPosition: caret, insertionPosition: caret)]);
},
cancellationToken);

Execução simultânea

️ ⚠ Às vezes, as extensões do editor podem ser executadas simultaneamente

A versão inicial tem um problema conhecido que pode resultar na execução simultânea do código de extensão do editor. Garante-se que cada método assíncrono seja chamado na ordem correta, embora as continuações após o primeiro await possam surgir intercaladas. Se sua extensão depender de ordem de execução, considere manter uma fila de solicitações de entrada para preservar a ordem, até que esse problema seja corrigido.

Para obter mais informações, consulte StreamJsonRpc Default Ordering and Concurrency.