Aide mémoire – vscode – créér une extension

Créer une extension pour vscode est assez simple mais pas très bien documenté. J’ai créé l’extension gimctl code generator afin de m’éviter des saisies fastidieuses. J’ai rencontré quelques difficultés et vais donc tenter de retracer ici les étapes de cette création pour mieux m’en suvenir et ne pas buter à nouveau dedans si l’idée me prend d’en développer d’autres.

Pré-requis

Pour développer un plugin pour VSCode il faut utiliser un package node.js qui s’appelle “VS code extension generator” (generator-code) un autre qui s’appalle “Yeoman” (yo) et un autre qui s’appelle “Visual Studio code Extensions” (@vscode/vsce), les deux premièrs serviront à créer le squelette de l’extension et le troisième à la packager. Tout est documenté sur le site de VSCode respectivement ici et .

sudo npm install -g yo generator-code
sudo npm install -g @vscode/vsce

Ce que je veux que mon extension fasse

Je veux me simplifier la vie pour écrire du code. Pour ce faire je veux créer une squelette de documentation au format markdown dans un répertoire, ainsi qu’un court fichier de description et mon fichier exécutable avec le strict minimum dedans.

De quoi ai-je besoin
  • Du nom du script à créer
  • D’une description de ce qu’il fait
  • D’un répertoire de base sous lequel se trouve mon arborescence de scripts (pour pouvoir l’utiliser d’un contexte à un autre)
  • De pouvoir interragir avec lespace de travail

Si les deux premiers doivent être saisis interractivement, le troisième peut être un paramètre de l’extension. Je veux donc pouvoir lire deux paramètres à chauque exécution de ma primitive et m’ppuyer sur un fichier de paramétrage propre à l’extension. Le quatrième c’est du code.

Génération du squelette

il suffit de taper “yo code” dans la ligne de commande, un script interractif demandera les informations nécessaires à la création du squelette. J’ai créé un squelette javascript. et donc eu un joly squelette de script qui fait hello world … dont je n’ai pas grand chose à carer. J’ai cependant obtenu deux fichiers intéressants:

  1. package.json
  2. extension.js

Modification(s) du ficier extension.js

Le premier décrit l’extension et son paramétrage et le second est le code proprement dit. Comme je le disais j’ai besoin d’interragir avec le workspace, autrement dit avec le système de fichier de VSCode. J’ai donc importé les deux bibliothèques “vscode” et “fs”, il est à noter que la première l’est déjà par le squeltte produit

const vscode = require('vscode');
const fs= require('fs');

ensuite j’ai modifié le nom de la commande créée

let disposable = vscode.commands.registerCommand('gimctl-code-generator.addGIMScript', () => {
...
}

J’ai ensuite enchaînés deux dialogues pour récuprrer mes variables d’exécution:

	vscode.window.showInputBox({
		"placeHolder": 'Enter a for new script '
	  }).then((newScriptName) => {
    vscode.window.showInputBox({
      "placeHolder": 'Enter a short description for script ' + newScriptName
    }).then((newScripDesc) => {
...
    });
  });

Et enfin j’ai dû aller chercher le répertoire racine de mon workspace

      const workspaceFolder = vscode.workspace.workspaceFolders ? vscode.workspace.workspaceFolders[0] : undefined;

Paramétrage global du module et prise en compte.

Je l’ai dit j’ai besoin d’un paramètre qui me dit ou trouver mon arborescence gimctl, ce paramètre change une fois pour toute pour chaque workspace, je n’ai donc pas besoin de le saisir à chaque fois que je crée un nouveau script (que j’utilise donc la primitive que j’écris). C’est donc un paramétrage d’extension. Je modifie le fichier package.json et plus particulièrement la sexion contributes afin de positionner toutes les bonnes informations pour mon plugin.

  "contributes": {
    "commands": [{
      "command": "gimctl-code-generator.addGIMScript",
      "title": "Add GIM script"
    }],
    "configuration": [{
      "id": "cfgRootDir",
      "title": "Directory of gimctl in workspace",
      "properties": {
        "addGIMScript.gimctlRootDir": {
          "type":"string",
          "default": "gimctl"
        }
      }
    }]
  },

J’ai donc une section command qui décrit ma primitive et une section configuration qui décrit la variable workspace que je veux utiliser et qui par défaut vaudra gimctl. il faut maintenant que je me serve de ma variable globale dans mon code.

const gimctlRootDir = vscode.workspace.getConfiguration('addGIMScript').get('gimctlRootDir') ;

La primitive dans son ensemble

Au delà de récupérer des varaibles je veux créer des fichiers et écrire dedans. C’est assez simple et bien documenté, je laisse ici le code pour l’écriture de la varaible de description dans le fichier de description

const vscode = require('vscode');
const fs= require('fs');

function activate(context) {
  let disposable = vscode.commands.registerCommand('gimctl-code-generator.addGIMScript', () => {
	vscode.window.showInputBox({
		"placeHolder": 'Enter a for new script '
	  }).then((newScriptName) => {
    vscode.window.showInputBox({
      "placeHolder": 'Enter a short description for script ' + newScriptName
    }).then((newScripDesc) => {
      // Get the first workspace folder (if any)
      const workspaceFolder = vscode.workspace.workspaceFolders ? vscode.workspace.workspaceFolders[0] : undefined;
	  const gimctlRootDir = vscode.workspace.getConfiguration('addGIMScript').get('gimctlRootDir') ;
      // Create a file in the specified subdirectory of the workspace
      const fileRootPath = workspaceFolder ? workspaceFolder.uri.fsPath + '/' + gimctlRootDir: undefined;
      if (!fileRootPath) {
        vscode.window.showErrorMessage('Error: No workspace folder opened');
        return;
      }

	  let filePath=fileRootPath + '/bin/' + newScriptName + '.sh';

	  if ( fs.existsSync(filePath) ) {
        vscode.window.showErrorMessage('Error: script ' + newScriptName + ' allready existing');
        return;		
	  }

	  let fileDescPath=fileRootPath + '/doc/' + newScriptName + '.desc';
	  let fileDocPath=fileRootPath + '/doc/' + newScriptName + '.md';

      fs.writeFile(fileDescPath, newScripDesc, (err) => {
        if (err) {
          vscode.window.showErrorMessage('Error creating file: ' + err );
        } else {
          vscode.window.showInformationMessage('File created successfully: ' + fileDescPath);
        }
      });
      
	  let     mdDesc ='gimctl __' + newScriptName + '__' + MY_EOL ;

...

     fs.writeFile(fileDocPath, mdDesc, (err) => {
        if (err) {
          vscode.window.showErrorMessage('Error creating file: ' + err );
        } else {
          vscode.window.showInformationMessage('File created successfully: ' + fileDocPath);
        }
      });

	  let shContent = '# ' + newScriptName + MY_EOL ;

...

      fs.writeFile(filePath, shContent, (err) => {
        if (err) {
          vscode.window.showErrorMessage('Error creating file: ' + err );
        } else {
          vscode.window.showInformationMessage('File created successfully: ' + filePath);
        }
      });
    });
  });
  });
  context.subscriptions.push(disposable);
}

   

// This method is called when your extension is deactivated
function deactivate() {}

module.exports = {
	activate,
	deactivate
}

C’est certes assez moche mais ça fait ce que je veux

Publier le package sur la marketplace

Préparer le package

Il faut avoir un compte sur le marcketplace et créer un “publisher” dedans. rien de compliqué ici.

Il faut ensuite modifier le fichier package.json afin de donner les informations souhaitée au marcketplace et de pouvoir publier notre extetension, c’est ici le début du fichier qui est modifié.

{
  "name": "gimctl-code-generator",
  "displayName": "gimctl code generator",
  "publisher": "ojo-webdelphes",
  "description": "primitive de simplification d'écriture du code des scripts gimctl",
  "icon": "images/logo.png",
  "version": "0.0.3",

J’ai créé un fichier png logo.png que j’ai mis dans le répertoire images, pour que ma page de module ait un beau logo :), mais surtout j’ai préciser le publisher.

Il est aussi impossible de publier une option sans avoir mis à jour le fichier README.md, je l’ai donc fait. Le squelette guide, rien de compliqué, je ne détaille pas le contenu du fichier ici.

Crée le package

Un package vscode est un fichier .vsix il est créé par l’instruction vsce pckage lancée depuis le répertoire de travail.

ojoly%MAC-Pro gimctl-code-generator % vsce package                 
 WARNING  A 'repository' field is missing from the 'package.json' manifest file.
Do you want to continue? [y/N] y
 WARNING  LICENSE.md, LICENSE.txt or LICENSE not found
Do you want to continue? [y/N] y
 DONE  Packaged: /Users/ojoly/devs/gimctl-code-generator/gimctl-code-generator-0.0.3.vsix (7 files, 9.46KB)

Il ne reste plus qu’à crée votre extension dans votre espace marcketplace et d’y télécharger le fichier .vsix généré (ici gimctl-code-generator-0.0.3.vsix) et après quelques minutes votre option peut être téléchargée depuis vscode et a sa page dans le marcketplace.

Installation et paramétrage

Chercher gimctl dans les extensions et cliquer sur installer
Cliquer sur l’icone de configuration, puis sur Paramètres d’extenion
Le paramètre rootdir peut être positionné soit pour le workspace courant soit pour l’utilisateur.
Ne reste qu’à utiliser CTRL+Shift+P pour appeler la primitive. Elle lancera successivement les dialogues souhaités et exécutera ce qui est demandé.