Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
O que é o Protocolo do Servidor de Idiomas?
O suporte a recursos avançados de edição, como preenchimento automático de código-fonte ou Ir para Definição para uma linguagem de programação em um editor ou IDE, é tradicionalmente muito desafiador e demorado. Normalmente, ele requer a escrita de um modelo de domínio (um scanner, um analisador, um verificador de tipos, um construtor e muito mais) na linguagem de programação do editor ou IDE. Por exemplo, o plug-in CDT do Eclipse, que fornece suporte para C/C++ no IDE do Eclipse, é escrito em Java, pois o próprio IDE do Eclipse é escrito em Java. Seguindo essa abordagem, isso significaria implementar um modelo de domínio C/C++ no TypeScript para Visual Studio Code e um modelo de domínio separado em C# para Visual Studio.
A criação de modelos de domínio específicos de linguagem também será muito mais fácil se uma ferramenta de desenvolvimento puder reutilizar bibliotecas específicas de linguagem existentes. No entanto, essas bibliotecas geralmente são implementadas na própria linguagem de programação (por exemplo, bons modelos de domínio C/C++ são implementados em C/C++). A integração de uma biblioteca C/C++ em um editor escrito no TypeScript é tecnicamente possível, mas difícil de fazer.
Servidores de linguagem
Outra abordagem é executar a biblioteca em seu próprio processo e usar a comunicação entre processos para conversar com ela. As mensagens enviadas para frente e para trás formam um protocolo. O protocolo de servidor de linguagem (LSP) é o produto de padronizar as mensagens trocadas entre uma ferramenta de desenvolvimento e um processo de servidor de linguagem. Usar servidores de linguagem ou demônios não é uma ideia nova ou nova. Editores como Vim e Emacs fazem isso há algum tempo para fornecer suporte semântico de preenchimento automático. O objetivo do LSP era simplificar esses tipos de integrações e fornecer uma estrutura útil para expor recursos de linguagem a uma variedade de ferramentas.
Ter um protocolo comum permite a integração de recursos de linguagem de programação em uma ferramenta de desenvolvimento com um mínimo de confusão reutilizando uma implementação existente do modelo de domínio do idioma. Um back-end do servidor de idiomas pode ser escrito em PHP, Python ou Java e o LSP permite que ele seja facilmente integrado a uma variedade de ferramentas. O protocolo funciona em um nível comum de abstração para que uma ferramenta possa oferecer serviços de linguagem avançada sem a necessidade de entender completamente as nuances específicas para o modelo de domínio subjacente.
Como o trabalho no LSP foi iniciado
O LSP evoluiu ao longo do tempo e hoje está na versão 3.0. Ele começou quando o conceito de um servidor de linguagem foi escolhido pelo OmniSharp para fornecer recursos avançados de edição para C#. Inicialmente, o OmniSharp usou o protocolo HTTP com uma carga JSON e foi integrado a vários editores, incluindo o Visual Studio Code.
Na mesma época, a Microsoft começou a trabalhar em um servidor de linguagem TypeScript, com a ideia de dar suporte ao TypeScript em editores como Emacs e Sublime Text. Nesta implementação, um editor se comunica por meio de stdin/stdout com o processo de servidor TypeScript e usa uma carga JSON inspirada no protocolo de depurador V8 para solicitações e respostas. O servidor TypeScript foi integrado ao plug-in TypeScript Sublime e ao VS Code para edição avançada do TypeScript.
Depois de ter integrado dois servidores de linguagem diferentes, a equipe do VS Code começou a explorar um protocolo de servidor de linguagem comum para editores e IDEs. Um protocolo comum permite que um provedor de idiomas crie um único servidor de idioma que possa ser consumido por IDEs diferentes. Um consumidor de servidor de idiomas só precisa implementar o lado do cliente do protocolo uma vez. Isso resulta em uma situação win-win para o provedor de idiomas e o consumidor de idioma.
O protocolo do servidor de idiomas começou com o protocolo usado pelo servidor TypeScript, expandindo-o com mais recursos de linguagem inspirados na API de linguagem do VS Code. O protocolo é apoiado com JSON-RPC para invocação remota devido à sua simplicidade e bibliotecas existentes.
A equipe do VS Code protótipou o protocolo implementando vários servidores de linguagem linter que respondem a solicitações para lint (verificar) um arquivo e retornar um conjunto de avisos e erros detectados. O objetivo era aplicar análises automáticas a um arquivo enquanto o usuário o edita em um documento, o que significa que haverá muitas solicitações de verificação durante uma sessão no editor. Fazia sentido manter um servidor em funcionamento para que um novo processo de linting não precisasse ser iniciado para cada edição de usuário. Vários servidores linter foram implementados, incluindo as extensões ESLint e TSLint do VS Code. Esses dois servidores linter são implementados em TypeScript/JavaScript e executados em Node.js. Eles compartilham uma biblioteca que implementa a parte do cliente e do servidor do protocolo.
Como o LSP funciona
Um servidor de linguagem é executado em seu próprio processo e ferramentas como o Visual Studio ou o VS Code se comunicam com o servidor usando o protocolo de linguagem em JSON-RPC. Outra vantagem do servidor de idiomas que está operando em um processo dedicado é que problemas de desempenho relacionados a um único modelo de processo são evitados. O canal de transporte real pode ser stdio, sockets, named pipes ou node ipc se tanto o cliente quanto o servidor forem escritos em Node.js.
Veja abaixo um exemplo de como uma ferramenta e um servidor de idioma se comunicam durante uma sessão de edição de rotina:
O usuário abre um arquivo (conhecido como um documento) na ferramenta: a ferramenta notifica o servidor de idiomas de que um documento está aberto ('textDocument/didOpen'). De agora em diante, a verdade sobre o conteúdo do documento não está mais no sistema de arquivos, mas mantida pela ferramenta na memória.
O usuário faz edições: a ferramenta notifica o servidor sobre a alteração do documento ('textDocument/didChange') e as informações semânticas do programa são atualizadas pelo servidor de idiomas. Como isso acontece, o servidor de idiomas analisa essas informações e notifica a ferramenta com os erros e avisos detectados ('textDocument/publishDiagnostics').
O usuário executa "Ir para Definição" em um símbolo no editor: a ferramenta envia uma solicitação 'textDocument/definition' com dois parâmetros: (1) o URI do documento e (2) a posição de texto de onde a solicitação Ir para Definição foi iniciada para o servidor. O servidor responde com o URI do documento e a posição da definição do símbolo dentro do documento.
O usuário fecha o documento (arquivo): uma notificação 'textDocument/didClose' é enviada da ferramenta, informando ao servidor de idiomas que o documento agora não está mais na memória e que o conteúdo atual agora está atualizado no sistema de arquivos.
Este exemplo ilustra como o protocolo se comunica com o servidor de idiomas no nível de recursos do editor, como "Ir para Definição", "Localizar todas as Referências". Os tipos de dados usados pelo protocolo são editor ou "tipos de dados" do IDE, como o documento de texto aberto no momento e a posição do cursor. Os tipos de dados não estão no nível de um modelo de domínio de linguagem de programação que normalmente forneceria árvores de sintaxe abstratas e símbolos do compilador (por exemplo, tipos resolvidos, namespaces, ...). Isso simplifica significativamente o protocolo.
Agora vamos examinar a solicitação 'textDocument/definition' mais detalhadamente. Abaixo estão as cargas que vão entre a ferramenta cliente e o servidor de linguagem para a solicitação "Ir para Definição" em um documento C++.
Esta é a solicitação:
{
"jsonrpc": "2.0",
"id" : 1,
"method": "textDocument/definition",
"params": {
"textDocument": {
"uri": "file:///p%3A/mseng/VSCode/Playgrounds/cpp/use.cpp"
},
"position": {
"line": 3,
"character": 12
}
}
}
Esta é a resposta:
{
"jsonrpc": "2.0",
"id": "1",
"result": {
"uri": "file:///p%3A/mseng/VSCode/Playgrounds/cpp/provide.cpp",
"range": {
"start": {
"line": 0,
"character": 4
},
"end": {
"line": 0,
"character": 11
}
}
}
}
Em retrospectiva, descrever os tipos de dados no nível do editor e não no nível do modelo de linguagem de programação é um dos motivos para o sucesso do protocolo do servidor de linguagem. É muito mais simples padronizar um URI de documento de texto ou uma posição de cursor em comparação com padronizar uma árvore de sintaxe abstrata e símbolos de compilador em diferentes linguagens de programação.
Quando um usuário está trabalhando com idiomas diferentes, o VS Code normalmente inicia um servidor de idiomas para cada linguagem de programação. O exemplo a seguir mostra uma sessão em que o usuário trabalha em arquivos Java e SASS.
Capabilities
Nem todos os servidores de idiomas podem dar suporte a todos os recursos definidos pelo protocolo. Portanto, o cliente e o servidor anunciam seu conjunto de recursos com suporte por meio de "funcionalidades". Por exemplo, um servidor anuncia que pode lidar com a solicitação 'textDocument/definition', mas pode não lidar com a solicitação 'workspace/symbol'. Da mesma forma, os clientes podem anunciar que podem fornecer notificações "prestes a salvar" antes que um documento seja salvo, para que um servidor possa computar edições textuais para formatar automaticamente o documento editado.
Integrando um servidor de idiomas
A integração real de um servidor de idiomas em uma ferramenta específica não é definida pelo protocolo do servidor de idiomas e é deixada para os implementadores de ferramentas. Algumas ferramentas integram os servidores de linguagem genericamente por meio de uma extensão que pode iniciar e conversar com qualquer tipo de servidor de linguagem. Outros, como o VS Code, criam uma extensão personalizada por servidor de idioma, para que uma extensão ainda seja capaz de fornecer alguns recursos de linguagem personalizados.
Para simplificar a implementação de servidores de idiomas e clientes, há bibliotecas ou SDKs para as partes do cliente e do servidor. Essas bibliotecas são fornecidas para idiomas diferentes. Por exemplo, há um módulo npm do cliente de idioma para facilitar a integração de um servidor de idiomas a uma extensão do VS Code e outro módulo npm do servidor de idiomas para gravar um servidor de idiomas usando Node.js. Esta é a lista atual de bibliotecas de suporte.
Usando o Protocolo do Servidor de Idiomas no Visual Studio
- Adicionando uma extensão de Protocolo do Servidor de Idiomas – saiba mais sobre como integrar um servidor de idiomas ao Visual Studio.