Visual Studio Code (VS Code) foi construído com extensibilidade em mente. Quase todas as partes do VS Code podem ser personalizadas e aprimoradas por meio da API de extensão . Na verdade, muitos recursos principais do VS Code são construídos como extensões , e você também pode construir o seu próprio, é claro!
A filosofia de desenvolvimento de produto do VS Code é que o editor é configurado para desenvolvimento web por padrão. Ele cobre HTML, CSS, JavaScript, TypeScript e Markdown. Esses recursos são gerenciados pela Microsoft e aceitam contribuições da comunidade. Tudo fora disso é deixado para a comunidade fornecer como extensões. As extensões podem ser disponibilizadas para outras pessoas publicando-as no VS Code Marketplace .
Agora, você deve estar se perguntando: como faço para escrever um, então? Devo apenas seguir os documentos oficiais ?
Você pode! Mas o VS Code é construído com electron , o que significa que você pode escrever uma extensão em JavaScript ou qualquer coisa que possa ser transpilada para JavaScript, como TypeScript. A base de código do VS Code é escrita em TypeScript, portanto, os documentos são orientados para TypeScript. Todos os exemplos de código fornecidos pela Microsoft são escritos em TypeScript. Portanto, se você não conhece o TypeScript, terá alguns problemas de velocidade ao longo do caminho.
Neste artigo, discutirei o seguinte:
- Forneça informações suplementares para que os desenvolvedores de JavaScript criem extensões
- Expanda alguns tópicos que considero negligenciados nos documentos oficiais
- Discuta como configurar um projeto e escrever sua primeira extensão
- Forneça exemplos para você experimentar por si mesmo
Tipos de extensões de código VS
Existem diferentes tipos de extensões que você pode escrever, e algumas nem requerem JavaScript. Em termos gerais, eles se enquadram nas seguintes categorias:
- Snippets : uma coleção de snippets de código que se destinam a um determinado idioma ou estrutura. Os snippets são declarados em arquivos JSON. Nenhum JavaScript é necessário. Você pode ler o Guia de trechos para mais informações
- Temas de cores : um tema de cores permite que você personalize as cores nos componentes da IU e o texto no editor. As cores são configuradas em arquivos JSON. Nenhum JavaScript é necessário . Você pode ler o artigo Criando um tema de código VS para mais informações
- Pacotes de extensão : um pacote de extensão é uma coleção de extensões que podem ser instaladas como uma única extensão. Eles são úteis para criar uma coleção com curadoria para recomendar a outras pessoas e para facilitar a ativação/desativação de um conjunto relacionado de extensões para determinados projetos. Todo o trabalho é feito no
package.json
. Nenhum JavaScript é necessário . Você pode ler o artigo VSCode: como criar seu próprio pacote de extensão para obter mais informações - Pacotes de idiomas (localização) : um pacote de idiomas permite aos usuários alterar o idioma de exibição do VS Code. Eles consistem em um conjunto específico de arquivos JSON contendo os recursos de string localizados para diferentes partes da IU. Nenhum JavaScript é necessário
- Outro : cobre tudo o mais que você gostaria de personalizar ou aprimorar no VS Code. É aqui que usamos JavaScript. Alguns exemplos do que você pode construir são:
- Adicionando componentes e visualizações personalizados na IU
- Suporte a uma nova linguagem de programação
- Suporta depuração de um tempo de execução específico
Vamos nos concentrar na categoria “outros” neste artigo. A partir de agora, quando me refiro a extensões, esse é o tipo de extensão a que me refiro.
Primeiros passos
Você precisa ter uma configuração típica de ambiente JavaScript . Você precisa ter Node e um gerenciador de pacotes ( yarn ou NPM ) instalado.
Se você já tem, pode ler o Seu primeiro guia de extensão para configurar um novo projeto. O guia recomenda usar Yeoman para gerar um projeto padrão para você. Ele oferece uma experiência semelhante à de um assistente para criar o projeto na linha de comando. Para criar um projeto JavaScript, você seleciona a opção Nova extensão (JavaScript) .
Ele produzirá um projeto padrão com o comando “Hello World” e um conjunto de testes vazio.
Acho que é melhor criar nosso primeiro projeto do zero. Vou criar uma versão mais enxuta do projeto padrão. Isso deve ajudá-lo a ter uma compreensão mais clara do código com menos distrações.
Escrevendo sua primeira extensão
Vamos criar o exemplo “Hello World” do zero. Queremos criar um comando Hello World que mostrará uma mensagem pop-up dizendo (rufar de tambores), “Hello World!”
O manifesto de extensão ( package.json
)
Como um projeto Node típico, usamos o NPM para lidar com a configuração e as dependências do nosso projeto. A configuração do projeto está contida em package.json
. Isso é conhecido como Manifesto de extensão na documentação. Você pode ler este guia em package.json
se você precisar de uma atualização.
Vamos criar nosso package.json
. Sempre precisamos preencher pelo menos meia dúzia de campos, conforme abaixo.
{ "nome":"exemplo", "versão":"0.0.1", "motores": { "vscode":"^ 1.32.0" }, "main":"extension.js", "contribui": { "comandos": [ { "command":"example.helloWorld", "title":"Hello World" } ] }, "activationEvents": [ "onCommand: example.helloWorld" ] }
Você precisa fornecer as propriedades nome
, versão
, engines
e main
como faria para um projeto Node típico. Criaremos um arquivo JavaScript na mesma pasta chamada extension.js
para ser nosso módulo principal em um minuto.
As propriedades específicas de um código VS que devem ser declaradas são:
-
contribui
: isto é para seu Pontos de contribuição . Este é o idioma do VS Code para declarar que parte do aplicativo você está estendendo. Aqui é onde podemos expor nossa funcionalidade por meio de comandos , crie novas visualizações , defina configurações do usuário e assim por diante -
activationEvents
: você declara Eventos de ativação para especificar os eventos que acionam o carregamento (ativação) da extensão. As extensões nem sempre estão ativas! Por exemplo, você pode especificar que uma extensão está ativa apenas quando um arquivo de redução é aberto, por exemplo,"activationEvents": ["onLanguage: markdown"]
. Os mais comumente usados são:onCommand
,onLanguage
eonStartupFinished
Queremos adicionar um novo comando à paleta de comandos. Especificamos isso na propriedade contributes.commands
. Atribuímos um ID exclusivo na subpropriedade command
. A convenção de nomenclatura que a Microsoft parece seguir é
, mas não há restrições. Este ID é referenciado no código para fornecer funcionalidade para o comando.
A subpropriedade title
é o texto que aparece na paleta de comandos para o comando (como abaixo).
Queremos apenas que a extensão esteja ativa quando o comando for executado, então usaremos o evento de ativação onCommand
.
O manifesto da extensão está pronto.
Você pode ler a referência do Código VS do Manifesto de Extensão para obter mais informações sobre as propriedades.
Módulo principal
Vamos criar extension.js
agora.
const vscode=require ("vscode"); module.exports={ ativar, desativar, }; função ativar (contexto) { //Isso deve corresponder à propriedade do comando no package.json const commandID="exemplo.helloWorld"; let disposable=vscode.commands.registerCommand (commandID, digaHello); context.subscriptions.push (descartável); } function sayHello () { vscode.window.showInformationMessage ("Hello World!"); } function deactivate () {}
Você pode tratar seus arquivos da mesma forma que faria em um aplicativo Node.js típico. Cada arquivo é tratado como um módulo separado que segue a sintaxe do módulo commonJS .
Você precisa importar o módulo vscode
para usar a API de extensibilidade do código VS. Ele está disponível por padrão.
Você deve sempre ter uma função activate
em seu módulo principal . A função activate
é executada quando um de seus eventos de ativação declarados acontece. É aqui que você configura as principais tarefas de inicialização da extensão. Vinculamos o comando declarado no package.json
à nossa função sayHello
por meio do commands.registerCommand
.
Se você criar objetos e quiser que os recursos sejam liberados quando a extensão for descarregada (desativada), você pode adicioná-lo à matriz ExtensionContext.subscriptions
. O Código VS refere-se a eles como descartáveis.
A função deactivate
dá a você uma chance de limpar antes que sua extensão seja descarregada. Não precisamos fazer uma limpeza explícita com frequência-você pode omitir o método deactivate
se for o caso. Se você precisar limpar algo de forma assíncrona, certifique-se de retornar um destino Promessa
da função.
Executando a extensão
Execute a extensão iniciando uma sessão de depuração. Você pode pressionar F5
ou selecionar Run> Start Debugging
no menu para iniciar a sessão. Isso irá compilar e executar a extensão em uma nova janela Host de desenvolvimento de extensão se houver uma configuração .vscode/launch.json
no projeto.
Se não houver configuração, pode ser solicitado que você selecione um ambiente para a configuração se o VS Code detectar o tipo de projeto. Caso contrário, você pode ter que criar manualmente um launch.json
.
Você pode clicar no botão Adicionar configuração para fornecer assistência ao preenchimento automático, selecionar Desenvolvimento de extensão de código VS como a opção e preencherá as propriedades.
Execute o comando Hello World da Paleta de comandos ( Ctrl + Shift + P
) e você verá uma mensagem pop-up dizendo “Hello World!”. Muito bem, você escreveu sua primeira extensão do VS Code!
Estrutura de projeto típica para extensões de código VS
Se você usou o Yeoman Generator para criar um projeto para você, crie a estrutura de pastas conforme descrito abaixo. Eu descrevo a função de cada arquivo:
. ├──.vscode │ ├── launch.json//Config para iniciar e depurar a extensão. Isso é criado por padrão quando você executa o projeto. ├── README.md//Descrição de sua extensão. Isso é usado pelo VS Code Marketplace como o ├── extension.js//Código-fonte da extensão ├── teste │ └── runTest.js//Código-fonte para executar seu conjunto de testes │ └── suite │ └── extension.test.js//Aqui é onde você escreve seus casos de teste │ └── index.js//Configuração do Mocha e do conjunto de testes ├── package.json//manifesto de extensão
Uma coisa que eu mudaria é adicionar uma pasta src para colocar os arquivos JavaScript. Aqui, extension.js
está na pasta raiz e isso pode ficar desorganizado rapidamente quando você constrói uma extensão mais substancial.
Familiarizando-se com a API
Leva algum tempo para se familiarizar com qualquer API. Cada API tem suas próprias convenções e idiossincrasias. Acho que alguma orientação e um bom conjunto de exemplos ajudam muito a fornecer um caminho de aprendizagem feliz.
Não achei a API do VS Code intuitiva de aprender. As descrições das funções são curtas e carecem de contexto em algumas áreas. Eu me peguei olhando exemplos e o código-fonte de extensões publicadas e, em seguida, voltando para a API para realizar tarefas na ocasião.
O que eu gostaria de ter à minha disposição inicialmente é uma visão geral da arquitetura e uma maneira de aprender o vocabulário da API. Esse tipo de orientação é vital para ser produtivo rapidamente. Vamos cobrir a arquitetura primeiro.
Visão geral da arquitetura da API
A captura de tela abaixo descreve como são chamados os principais componentes da interface do usuário do aplicativo. Eles são a Barra de atividades , a barra lateral , Editor, Painel e o Status Bar .
Achei a terminologia da documentação um pouco incoerente sobre os componentes da IU além disso.
Você pode considerar referências a visualizações para significar um componente de IU personalizado e atômico. Uma visualização pode ser:
- Uma visualização em árvore ( TreeView ) semelhante para o explorador do projeto
- Ou uma visualização da webview ( WebviewView ) , que é construída como uma página HTML. O exemplo abaixo é de Solicitações e problemas do GitHub Pull extensão.
Uma visão é colocada dentro de um contêiner de visão. Uma visualização pode ser adicionada a um dos seguintes:
- Contêiner de visualização do Explorer na barra de atividades
- Contêiner de visualização do Source Control Management (SCM) na barra de atividades
- Executar e depurar o contêiner de visualização na barra de atividades
- Testar o contêiner de visualização na barra de atividades
- Seu próprio contêiner de visualização
Você pode ver todas as visualizações disponíveis executando o comando Visualizar: Abrir Visualização .
Uma visualização é declarada com propriedade contributes.views
no package.json
.
Um contêiner de visualização pode ser adicionado à barra de atividades ou ao painel. É declarado em contributes.viewsContainers
no package.json
.
Abaixo está um exemplo que mostra um “Package Explorer” personalizado view container adicionado à barra de atividades, que tem duas views personalizadas.
Os outros componentes da IU que você gostaria de criar geralmente são feitos por meio de funções no window
, por exemplo, itens da barra de status. Discutiremos isso na próxima seção.
Se desejar oferecer suporte a outra linguagem de programação, você pode ler o Visão geral das extensões de idioma que cobre a arquitetura por trás disso. Agora, a maioria das linguagens de programação tem extensões, então é improvável que você se aventure por esse caminho!
Visão geral dos namespaces
Vamos discutir os namespaces que você usará com mais frequência.
O namespace mais importante para os componentes da IU é janela
. Refere-se à janela atual do aplicativo. Ele tem funções para acessar e manipular a maioria dos componentes da IU:
- Para acessar alguns componentes da IU, há várias propriedades:
activeTextEditor
é o arquivo aberto que foi focado ou alterado mais recentemente eactiveTerminal
é o painel do terminal que tem foco ou mudou mais recentemente - Para criar um novo componente, ele possui funções
createXXX
comocreateTreeView (..)
,createStatusBarItem (..)
ecreateWebview (..)
- Para mostrar notificações e diálogos, possui funções
showXXX
comoshowInformationMessage (..)
,showQuickpick (..)
eshowInputBox (..)
. Uma exceção a esse padrão é para notificações de progresso, que são criadas com a funçãowithProgress(..)
- Para informações contextuais dentro de um documento, existem funções
registerXXX
comoregisterCodeLensProvider (..)
para lentes de código eregisterHoverProvider
para dicas de ferramentas
Você pode preencher o conteúdo de uma visualização com:
- Um TreeView fornecendo um provedor de dados para
createTreeView (..)
ou registrando o provedor de dados diretamente por meio deregisterTreeDataProvider(..)
- Um WebviewView registrando um provedor com
registerWebviewViewProvider (..) . WebviewViews permite renderizar HTML arbitrário na visualização
O namespace para manipular o projeto aberto no explorador de arquivos é espaço de trabalho
. Você pode executar ações nos arquivos e responder a eventos do sistema de arquivos.
Para editar um documento, os namespaces são TextEditor
and TextDocument
. The text content is available through the TextEditor.document
property. TextDocument
allows you retrieve text through ranges and lines in TextEditor
.
The commands
namespace deals with commands, which are the units of functionality you can reference. Commands can be added to the editor using the registerCommand and registerTextEditorCommand functions. Commands can be executed in the UI through the command palette, menus, and other contribution points. You can also programmatically execute commands.
The namespace for all global, user, and workspace settings is WorkspaceConfiguration
.
The ExtensionContext
namespace provides contextual properties for your extension, such as the global filepath, some logging info, and storage of secrets. An instance of an ExtensionContext
is provided as the first parameter to the activate
function.
The docs lists a few common API patterns also.
Interpreting the API without TypeScript knowledge
In TypeScript, you provide types for variables, and define your own types. When you write JavaScript, you don’t need to have any types, so I guess we can just ignore the types in the API right?
No — you still need to understand what values you need to provide for parameters and as return values. You need to meet the expectations of the API. You still need to know if you should provide a number or a string. The difference is that you do not need to assign types explicitly along the way.
Let’s look at a few examples to clarify how this plays out in reality.
Handling enumerations
Let’s add an item to the status bar, as below. The code for this can be found here.
From our namespace overview, we know that window.createStatusBarItem(..)
is the function we are interested in. The function definition is below.
The first parameter is named alignment and has a type of StatusBarAlignment. So what value do we provide for that?
Looking at the definition, we see it is an enumeration.
An enumeration defines a fixed list of values. The native value of the enumeration is a number. So, if we want our status bar item aligned left: we can provide a value of 1
or vscode.StatusBarAlignment.Left
for this parameter. The latter is more readable.
The second parameter is a number. Simply provide a number of your choosing.
let item=vscode.window.createStatusBarItem( vscode.StatusBarAlignment.Left, 1 ); item.text=`$(megaphone) 0 line(s) selected`; item.show();
We can then consult the StatusBarItem definition to understand how to add a label and display the item. The code above is sufficient to display a status bar item.
How to handle objects and callbacks
Let’s create a progress notification, as below. The code for this can be found here.
From our namespace overview, we know that window.withProgress(..)
is the function we are interested in. The function definition is below.
This definition is more complicated, but don’t be intimidated.
The first parameter, options, is a type of ProgressOptions. You need to check the definition and create an object literal for it.
The properties cancellable and title are primitive types. The location property is an enumeration again.
let progressOptions={ cancellable: true, location: vscode.ProgressLocation.Notification, title:"I am long running!", };
The second parameter is a callback with its own set of parameters. Here, I create an anonymous function with the two parameters I am interested in using. The progress parameter is what we use to report the status of our task.
The return type of this function is Thenable, which is VS Code’s type for a promise. We create some timeouts to simulate a running task and resolve a promise when we are done. We return this promise from the function to satisfy the asynchronous behavior expected.
vscode.window.withProgress(progressOptions, (progress, token)=> { token.onCancellationRequested(()=> { console.log("User canceled the long running operation"); }); progress.report({ increment: 0 }); setTimeout(()=> { progress.report({ increment: 50, message:"Half way done!", }); }, 2000); const p=new Promise((resolve)=> { setTimeout(()=> { resolve(); }, 4000); }); return p; }); }
Examples of VS Code extensions in JavaScript
I gathered a collection of example, which can be found in the GitHub repo.
Testing extensions
You should test your extensions the same as any JavaScript code.
The Yeoman Generator creates a boilerplate test suite for you using the Mocha test framework. You can use whatever testing framework you want. If you are happy with Mocha, the testing extensions guide covers the basics.
Note that if you try to run tests from the command-line, it will throw an error:
Running extension tests from the command line is currently only supported if no other instance of Code is running.
This is a limitation of running potentially different versions of VS Code concurrently. The solution is to use VS Code Insiders for development where you can run tests from the command-line. Or you can launch the extension tests from the debug launch config (as below).
Publishing extensions
If you want to share your extension with others, you can publish it to the VS Code Extension Marketplace. Alternatively, you can package an extension into the installable VSIX format, and distribute it yourself. You can read the publishing extension guide for the rundown on this.
Conclusion
Writing your own VS Code extension can be a fun and rewarding project. It can be incredibly satisfying to build something that you use every day to assist your workflow. It does require some time and effort to get to grips with the development environment and learn how to use the API effectively. If you have experience with Node already, it is not much of a stretch.
The key takeaway is that knowing TypeScript is not a prerequisite — anyone with some JavaScript knowledge should be able to build their own extension without headaches. I hope that this article is able to provide a smooth learning path for building extensions with plain ‘ole JavaScript.
The post Writing VS Code extensions in JavaScript appeared first on LogRocket Blog.