Ceci est une ancienne révision du document !
Table des matières
Prérequis
La documentation est Extension API de Visual Studio Code. Archive du 11/12/2020 le 01/02/2021
Node.js
Il faut installer Node.js qui possède une application pour créer un squelette de module.
Installer les programmes yo
et generator-code
par :
npm install -g yo generator-code
Création du module
Générer le module de base pour la coloration syntaxique :
yo code
Résultat de la console :
? ========================================================================== We're constantly looking for ways to make yo better! May we anonymously report usage statistics to improve the tool over time? More info: https://github.com/yeoman/insight & http://yeoman.io ========================================================================== Yes _-----_ ╭──────────────────────────╮ | | │ Welcome to the Visual │ |--(o)--| │ Studio Code Extension │ `---------´ │ generator! │ ( _´U`_ ) ╰──────────────────────────╯ /___A___\ / | ~ | __'.___.'__ ´ ` |° ´ Y ` ? What type of extension do you want to create? New Language Support Enter the URL (http, https) or the file path of the tmLanguage grammar or press ENTER to start with a new grammar. ? URL or file to import, or none for new: ? What's the name of your extension? CELLman Lang ? What's the identifier of your extension? cellman-lang ? What's the description of your extension? Syntax highlighting for CELLman lang Enter the id of the language. The id is an identifier and is single, lower-case name such as 'php', 'javascript' ? Language id: cellman Enter the name of the language. The name will be shown in the VS Code editor mode selector. ? Language name: CELLman Enter the file extensions of the language. Use commas to separate multiple entries (e.g. .ruby, .rb) ? File extensions: .cmc, .TRAN Enter the root scope name of the grammar (e.g. source.ruby) ? Scope names: source.cmc ? Initialize a git repository? Yes create cellman-lang\syntaxes\cellman.tmLanguage.json create cellman-lang\.vscode\launch.json create cellman-lang\package.json create cellman-lang\README.md create cellman-lang\CHANGELOG.md create cellman-lang\vsc-extension-quickstart.md create cellman-lang\language-configuration.json create cellman-lang\.vscodeignore create cellman-lang\.gitignore create cellman-lang\.gitattributes Your extension cellman-lang has been created! To start editing with Visual Studio Code, use the following commands: cd cellman-lang code . Open vsc-extension-quickstart.md inside the new extension for further instructions on how to modify, test and publish your extension. For more information, also visit http://code.visualstudio.com and follow us @code.
Syntaxe des fichiers
package.json
Dans ce fichier, il faut uniquement modifier la partie contributes
(documentation Extension points).
languages
{ "contributes": { "languages": [{ "id": "cellman_cmc", "aliases": ["CELLman", "cellman"], "extensions": [".cmc", ".TRAN"], "configuration": "./language-configuration.json", "filenames": [], "firstLine": "..." }], "grammars": ... } }
languages
, on définit un tableau de langages identifié par unid
.aliases
: le premier alias servira de label.extensions
: filtreront les fichiers où s'appliquera la coloration syntaxique. Chaque extension unique ne doit être que dans un seul langage. Si une extension est présente dans deux langages, l'un des deux sera simplement ignoré.filenames
: c'est un filtre qui prend en compte le nom du fichier et pas uniquement son extension. C'est utile pour les fichiers sans extension.firstLine
: on applique une expression régulière sur le contenu de la première ligne du fichier.
grammars
{ "contributes": { "languages": ..., "grammars": [{ "language": "cellman_cmc", "scopeName": "source.common", "path": "./syntaxes/cellman.tmLanguage.common.json" }, { "scopeName": "source.TRAN", "path": "./syntaxes/cellman.tmLanguage.TRAN.json", "injectTo": [ "source.common" ] }] } }
Il y a deux façons : via l'id du langage ou via une extension du langage.
On peut utiliser le champ language
et lui attribuer le champ id
précédemment définit via languages.language
. Les fichiers concernés seront ceux qui respecteront extensions
, filenames
ou firstLine
. Le champ scopeName
devra correspondre au champ scopeName
dans le fichier définit par l'attribut path
.
Il est aussi possible d'étendre un langage existant. Pour cela, il faut connaître le nom de la coloration syntaxique attribué au texte. Cette information peut s'afficher en saisissant la commande Developer: Inspect Editor Tokens and Scopes
via CTRL+SHIFT+P
. On note alors ce nom dans le tableau injectTo
. La couleur pourra alors être surchargée par les règles définies dans le fichier path
.
xxx.tmLanguage.json
Voir Language Grammars pour la syntaxe.
Arborescence de base
{ "$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json", "name": "CELLman", "patterns": [ { "include": "#comments" } ], "repository": { "comments": ... }, "scopeName": "source.common" }
avec 3 champs à personnaliser :
patterns
: ce tableau ne contient que les noms qui font référence aux noms contenu dans le champrepository
. En pratique il peut contenir d'autres données mais l'utilisation exclusive duinclude
est une bonne pratique du générateur automatique.
repository
: contient la liste des patterns dont le nom est défini précédemment.
scopeName
: doit être le même que celui définit dans le fichierpackage.json
à la rubriquegrammars
.
pattern
Chaque repository
contient un dictionnaire de pattern.
Il est possible de ne définir que le pattern avec son type de contenu. Ici, on repère 4 mots clés (if
, while
, for
, return
).
Le nom doit impérativement respecter la syntaxe de TextMate (§12.4 Naming Conventions
). Ici, keyword.control
signifie « mainly related to flow control ». Ensuite, on rajoute un dernier .
avec l'abréviation du langage.
"keywords": { "patterns": [{ "name": "keyword.control.cell", "match": "\\b(if|while|for|return)\\b" }] },
Dans l'autre exemple ci-dessous, le symbole "
et "
désigne le début (begin
) et la fin (end
) d'un texte. Ce texte aura comme coloration syntaxique string.quoted.double
.
Ensuite, à l'intérieur de ce texte, on cherche le pattern \\.
(caractère d'échappement) et on lui applique la coloration syntaxique constant.character.escape
. Et on peut faire autant de sous-niveau que nécessaire.
"strings": { "name": "string.quoted.double.cell", "begin": "\"", "end": "\"", "patterns": [ { "name": "constant.character.escape.cell", "match": "\\\\." } ] }
Il est aussi possible de colorer en fonction des groupes de l'expression régulière via captures
:
"strings": { "name": "string.quoted.double.cell", "match": "\\\"(.*)\\\"", "captures": { "1": { "name": "string.inside.cell" } } }
Il est aussi possible d'utiliser beginCaptures
et endCaptures
sur les textes correspondant aux expressions régulières de begin
et end
.
Bonnes pratiques
Principe
L'idéal est d'avoir la sémantique du langage et de le convertir en expression régulière dans un format compatible.
Il est donc préférable d'identifier chaque type de ligne et de construire les expressions régulières correspondantes.
Exemple
- package.json
{ "name": "cell-lang", "displayName": "Cell Lang", "description": "Syntax highlight for cell lang", "version": "0.0.1", "engines": { "vscode": "^1.52.0" }, "categories": [ "Programming Languages" ], "contributes": { "languages": [ { "id": "lang", "aliases": [ "lang", "lang" ], "extensions": [ "lang" ], "configuration": "./language-configuration.json" } ], "grammars": [ { "language": "lang", "scopeName": "source.lang", "path": "./syntaxes/lang.tmLanguage.json" } ] } }
- lang.tmLanguage.json
{ "$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json", "name": "cell", "patterns": [ { "include": "#function" }, { "include": "#code" }, { "match": ".*", "name": "invalid.source.lang" } ], "repository": { "float": { "name": "constant.numeric.float.lang", "match": "[0-9]+\\.[0-9]*(f|d)" }, "integer": { "name": "constant.numeric.integer.lang", "match": "[0-9]+" }, "hex": { "name": "constant.numeric.hex.lang", "match": "0(x|X)[0-9a-fA-F]+" }, "number": { "patterns": [ { "include": "#float" }, { "include": "#hex" }, { "include": "#integer" } ] }, "expression": { "patterns": [ { "match": "(.*)\\b\\s*(\\+|\\/|\\*|-)\\b\\s*(.*)\\b", "name": "string.regexp.expr.lang", "captures": { "1": { "patterns": [ { "include": "#number" } ] }, "2": { "name": "keyword.operator.expr.lang" }, "3": { "patterns": [ { "include": "#number" } ] } } }, { "include": "#number" } ] }, "primary_type": { "patterns": [ { "match": "(int|short|char|double|float)", "name": "storage.type.lang" }, { "match": ".*", "name": "invalid.primary_type.lang" } ] }, "init_variable": { "patterns": [ { "name": "meta.expr.init_variable.lang", "match": "\\s*([^\\s]+)\\b\\s*(.+)\\b\\s*=\\s*(.+);", "captures": { "1": { "patterns": [ { "include": "#primary_type" } ] }, "2": { "name": "variable.name.lang" }, "3": { "patterns": [ { "include": "#expression" } ] } } } ] }, "code": { "patterns": [ { "include": "#if" }, { "include": "#init_variable" }, { "include": "#expression" }, { "include": "#number" } ] }, "function": { "begin": "^\\s*(function)\\s+([^\\s]*)\\s*$", "beginCaptures": { "1": { "name": "storage.type.function.lang" }, "2": { "name": "entity.name.method.lang" } }, "end": "\\s*(end_function)\\s*", "endCaptures": { "1": { "name": "keyword.other.lang" } }, "name": "meta.body.function.definition.lang", "patterns": [ { "include": "#code" } ] }, "if": { "begin": "^(\\s*)(if)(.*)(then)\\s*", "beginCaptures": { "2": { "name": "keyword.control.lang" }, "3": { "patterns": [ { "include": "#expression" } ] }, "4": { "name": "keyword.control.lang" } }, "end": "^(\\1)(fi)\\s*", "endCaptures": { "2": { "name": "keyword.control.lang" } }, "name": "meta.body.if.definition.lang", "patterns": [ { "include": "#code" } ] } }, "scopeName": "source.lang" }
| Version colorée : ![]() |