Accueil Nos publications Blog VS Code Extension – Partie 2 : Créer une cheat-sheet dynamique

VS Code Extension – Partie 2 : Créer une cheat-sheet dynamique

Dans notre dernier article, je vous présentais les extensions VS Code vous permettant de "doper votre IDE".
Nous y abordions les concepts ainsi que la mise en oeuvre de la création d’une extension et son debug.
De manière un peu frustrante nous nous sommes arrêtés à ce "HelloWorld" un peu simple.
Dans cette suite, je vous propose d’aller plus loin et de créer une véritable extension "utile".

Une cheat-sheet dynamique ?

Pour ma part, ma mémoire est assez sélective et ne garde pas en tête les commandes des CLI (Command Line Interface) type git, dotnet, etc.
Je sais très bien que je peux retrouver mes petits en utilsant git –help mais c’est assez laborieux et il faut bien souvent connaître la commande basique pour pouvoir demander les paramètres d’usage un peu spécifiques.
Vous pourriez me répondre que je peux également aller chercher la documentation en ligne. Ou encore que je peux imprimer une "cheat-sheet" ou "pense-bête". Mais là encore nous faisons face à des restrictions : nécessité d’avoir une connexion internet ou de devoir transporter et retrouver cette fameuse cheat-sheet quand on en a besoin.
Pour ces raisons, j’ai choisi de créer des cheat-sheet dynamiques.
L’objectif : m’aider à retrouver les commandes spécifiques de manière intuitive et sans sortir de mon IDE.
Bien évidemment le template d’extension que nous allons créer ensemble n’est pas limité aux CLI et vous pourrez l’adapter à vos usages spécifiques ou à ceux de votre équipe par exemple.

Environnement de travail

Comme précisé dans mon dernier article, les éléments nécessaires pour générer notre extension sont :

Génération de l’extension

Là aussi je vous invite à regarder l’article précédent pour plus de détails mais, pour faire simple, on lance la commande suivante et on choisit une extension en TypeScript.

yo code

Voilà nous avons notre base de travail.
Rentrons dans le vif.

Ajout des commandes

Pour pouvoir proposer un pense-bête à notre utilisateur, il faut bien évidemment créer les entrées de ce pense-bête.

Pour cela, nous allons créer une classe que nous nommerons Choice. Cette classe contiendra des propriétés, un intitulé label à afficher permettant de retrouver l’entrée, ainsi que la valeur de l’entrée (la commande) value en elle-même.

interface Choice {
  label: string = '';
  value: string = '';
}

Maintenant que nous avons notre objet de base, nous allons pouvoir créer la liste des entrées.
Prenons par exemple les commandes nécessaires pour créer, packager et publier une extension, alors nous nous retrouverons avec un array que nous nommerons choices :

const choices: Choice[] = [
  { label: 'Create an extension', value: 'yo code' },
  { label: 'Package an extension', value: 'vsce package'  },
  { label: 'Publish an extension', value: 'vsce publish' }
];

Modification de la commande

Lors de la création d’une extension un fichier package.json a été créé à la racine. Ce fichier est le contrat de votre extension. Nous y déclarons par exemple son nom, sa description, mais surtout les commandes qui l’activent.
Nous allons donc modifier la partie commands de ce fichier pour que notre extension soit plus facile à trouver :

"contributes": {
    "commands": [
    {
        "command": "myfirstextension.findcommand",
        "title": "Find VSCE Command"
    }
    ]
},

Une fois cette mofification effectuée, il va bien évidemment falloir mettre à jour la commande elle-même, à commencer par son id (l’oubli bête dont je vous parlais la dernière fois).
Trouvez donc le fichier extension.ts dans le répertoire src et modifiez la ligne suivante avec le bon id de commande :

let disposable = vscode.commands.registerCommand('myfirstextension.findcommand', async () => {

Voilà, notre extension est donc accessible en cherchant le libellé Find VSCE Command.

Création d’une liste déroulante

Attaquons-nous maintenant à la recherche.
Quand on choisit Find VSCE Command, on veut voir la liste des libellés. Pour cela, il nous faut une liste déroulante.

Je vous propose donc de créer une function qui a pour but d’afficher la liste des choix. Pour cela la function va extraire les libellés, fournir cet array de string à l’API vscode.window.showQuickPick, récupérer l’élément selectionné par l’utilisateur et retrouver le Choice associé.

async function SelectQuickPick():Promise<Choice> {

    // extract labels from choices
    let labels = choices.map(choice => ({ label: choice     label}));

    // show quickPick and get result
    let result = await vscode.window.showQuickPick(labels);

    // extract choice from 'label' result
    return choices.find(c => c.label === result?.label) as Choice;
}

Notre méthode de liste déroulante créée, nous pouvons l’utiliser dans le code de la commande elle-même.

let disposable = vscode.commands.registerCommand('myfirstextension.helloWorld', async () => {

    let choice = await SelectQuickPick() as Choice;
    ...

Bien sûr, si l’utilisateur n’a selectionné aucun élément mais a quitté la liste déroulante, l’objet choice sera undefined. Vous devrez donc penser à créer du code défensif vérifiant ce cas pour quitter votre commande.

Affichage du résultat

Une fois le choix réalisé par l’utilisateur, il est important de ne pas continuer à lui afficher ‘Hello World’ mais bien la valeur de la commande qu’il cherche.
Modifiez donc le contenu de la logique de commande par le code suivant :

// Display a message box to the user
vscode.window.showInformationMessage(`Usage: "${choice.value}"`

Copier dans le presse-papier

Notre utilisateur peut désormais effectuer une recherche parmi une liste de choix que nous avons définie et la valeur associée lui sera affichée dans une notification.
Malheureusement il n’est pas aisé de sélectionner et copier le texte de ces notifications. Nous allons donc lui faciliter la tâche en faisant appel à nouveau aux API des extensions.
En effet, il est possible de rajouter des boutons dans les notifications et d’exécuter du code sur le clic d’un de ces boutons.
Nous allons donc ajouter un bouton Copy qui, s’il est selectionné, ajoutera la valeur du résultat dans le presse-papier et confirmera par une autre notification cette copie.
Pour cela, nous allons ajouter un paramètre à notre méthode showInformationMessage.

// Display a message box to the user
    vscode.window.showInformationMessage(`Usage: "${choice.value}"`, ...['Copy']).then(selection => {
        vscode.env.clipboard.writeText(choice.value as string);
        vscode.window.showInformationMessage('The command was copied to the clipboard');
    });

Le code complet de notre commande est donc le suivant :

let disposable = vscode.commands.registerCommand('myfirstextension.helloWorld', async () => {
    // The code you place here will be executed every time your command is executed

    let choice = await SelectQuickPick() as Choice;

    // Display a message box to the user
    vscode.window.showInformationMessage(`Usage: "${choice.value}"`, ...['Copy']).then(selection => {
        vscode.env.clipboard.writeText(choice.value as string);
        vscode.window.showInformationMessage('The command was copied to the clipboard');
    });
});

Conclusion

Nous avons créé en quelques lignes une cheat-sheet dynamique nous permettant de proposer à nos utilisateurs une liste déroulante de choix guidés leur permettant de retrouver les commandes importantes.

Pour créer notre premère extension utile, nous avons manipulé quelques API de bases :

  • vscode.commands.registerCommand
  • vscode.window.showQuickPick
  • vscode.window.showInformationMessage
  • vscode.env.clipboard.writeText

À vous désormais de vous approprier ce code et toutes les API disponibles pour créer votre cheat-sheet ou toute autre extension plus poussée que vous auriez en tête.

Pour aller plus loin

Si vous souhaitez allez un peu plus loin dans la création de cheat-sheet dynamique, je vous invite à parcourir le code de mes extensions GIT CLI Explorer et DotNet CLI Explorer. Ces extensions sont disponibles dans le marketplace et ajoutent quelques fonctionnalités comme les listes imbriquées et l’affichage de détails des commandes dans une webview interne, toujours sans quitter votre IDE.

Le code étant open-source et mes compétences en typescript assez limitées, n’hésitez pas à ouvrir des issues dans Github demandant des fix, des nouvelles fonctionnalités voire proposer vous-même des améliorations via des Pull-Request.

© SOAT
Toute reproduction interdite sans autorisation de la société SOAT