Coloration syntaxique avec Eclipse RCP
Par Damien BROSSARD, samedi 8 décembre 2007 à 15:18 :: Eclipse :: #7 :: rss
Le framework Eclipse RCP propose de nombreuses fonctionnalités pour créer des applications clientes. Eclipse est un environnement de développement bâti autour de ce framework et peut être facilement étendu en lui ajoutant des plugins. Il est ainsi possible de créer des éditeurs de texte personnalisés présentant une coloration syntaxique pour tout type de document.
Introduction
Dans ce billet, nous allons créer pas à pas un éditeur de texte avec coloration syntaxique pour le langage de script lua (cf. LUA).
Objectifs
L'éditeur sera simple, en ce sens qu'il ne fera que de la coloration syntaxique. Nous prendrons en compte les éléments suivants :
- mots clés du langage (cf. Manuel LUA)
- commentaires
- autres éléments
Les deux premiers éléments recevront une mise en forme particulière.
Pré-requis
- Les notions de plugin Eclipse et d'extension (dans la terminologie Eclipse RCP) sont requises pour comprendre la mise en oeuvre de l'éditeur.
- L'environnement utilisé est Eclipse 3.3, les classes utilisées peuvent différer dans les versions précédentes.
Création de l'éditeur
Création d'un nouveau plugin
Nous allons commencer par créer un nouveau plugin vide. Pour cela, on utilise le menu Fichier>Nouveau...>Projet.... A l'ouverture de la boite de dialogue, il suffit de choisir l'entrée Projet de plugin puis choisir suivant. On renseigne le nom (damien.tutorial.eclipse.editor.lua), et on indique que le projet est conçu pour Eclipse 3.3, puis on choisi suivant. La deuxième page de la boite de dialogue permet de choisir certains réglages du plugin, on ne sélectionnera pas Générer une classe d'activation..., par contre on cochera Ce plugin apporte des contributions à l'interface. La rubrique Voulez vous créer une application RCP sera renseignée à non. Une fois ces réglages choisis, on clique sur Terminer et notre nouveau projet de plugin est prêt.

L'environnement de développement a automatiquement ouvert un éditeur des propriétés du plugin. Dans le cas qui nous intéresse, nous allons utiliser simplement deux onglets : Dépendances et Extensions
Ajout des dépendances
La première étape va être de s'assurer que le plugin liste bien les dépendances nécessaires :
org.eclipse.core.runtimeorg.eclipse.uiorg.eclipse.ui.editorsorg.eclipse.jface.text
Pour ajouter une dépendance, il suffit de cliquer sur le bouton Ajouter... puis de sélectionner la ou les dépendance(s) voulue(s). La zone d'édition en haut de la boite de dialogue permet de filtrer la liste des plugins (celle-ci peut être plutôt longue en fonction de ce qui est installé).

Codage de l'éditeur
La classe LuaEditorScanner
Cette classe va nous permettre de décomposer un fichier lua afin d'en extraire des éléments que nous classerons en trois catégories :
- mots-clés
- commentaires
- le reste
A chaque catégorie, on va associer une mise en forme particulière, ce qui au final permettra d'obtenir la coloration syntaxique désirée.
LuaEditorScanner dérive de la classe RuleBasedScanner et est configurée en créant des règles servant à déterminer la catégorie d'un élément. On crée trois Token pour représenter chaque catégorie :
Token keyword = new Token(new TextAttribute(COLOR_KEYWORD, null, SWT.BOLD)); Token comment = new Token(new TextAttribute(COLOR_COMMENT, null, SWT.ITALIC)); Token other = new Token(new TextAttribute(COLOR_OTHER));
Afin de détecter les mots clés, on crée une règle de type WordRule. Chaque mot clé est ajouté via la méthode addWord qui prend en argument le mot clé et le token correspondant, on pourrait de cette manière définir plusieurs catégories de mots clés simplement en utilisant cette classe. Le constructeur de WordRule permet d'indiquer :
- la catégorie par défaut si l'élément ne correspond pas à un mot de la liste spécifiée, on va indiquer other
- une classe permettant de déterminer si un caractère correspond à un début d'identifiant ou à un contenu d'identifiant, ici on utilise la classe Character et on se base sur les méthodes de reconnaissance d'identifiant java
- si la règle est sensible à la casse ou non, par défaut elle l'est
WordRule rule = new WordRule(new IWordDetector() {
@Override
public boolean isWordPart(char c) {
return Character.isJavaIdentifierPart(c);
}
@Override
public boolean isWordStart(char c) {
return Character.isJavaIdentifierStart(c);
}
}, other);
Il nous reste à configurer l'ensemble des règles utilisables par notre scanner. Cela se fait par la méthode setRules qui prend en paramètre un tableau contenant les règles à employer. On y crée la règle permettant d'identifier les commentaires, à cette fin on crée une instance de la classe SingleLineRule spécialisée dans la détection d'éléments sur une ligne :
new SingleLineRule("--", null, comment, (char) 0, true)
On indique, le délimiteur marquant le début de l'élément (ici : --), le délimiteur marquant la fin de l'élément, s'il n'y en a pas, on indique null, la catégorie de l'élément (comment), le caractère d'échappement, et un booléen indiquant si la fin du fichier termine une ligne.
Si on place tout ceci ensemble on obtient le code suivant :
package damien.tutorial.eclipse.editor.lua;
import org.eclipse.jface.text.TextAttribute;
import org.eclipse.jface.text.rules.IRule;
import org.eclipse.jface.text.rules.IWordDetector;
import org.eclipse.jface.text.rules.RuleBasedScanner;
import org.eclipse.jface.text.rules.SingleLineRule;
import org.eclipse.jface.text.rules.Token;
import org.eclipse.jface.text.rules.WordRule;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.PlatformUI;
/**
* Défini le scanner de source lua.
* @author damien
*/
public class LuaEditorScanner extends RuleBasedScanner {
private static final Color COLOR_KEYWORD;
private static final Color COLOR_COMMENT;
private static final Color COLOR_OTHER;
private static final String [] KEYWORDS = new String {
"and", "break", "do", "else", "elseif",
"end", "false", "for", "function", "if",
"in", "local", "nil", "not", "or",
"repeat", "return", "then", "true", "until", "while"
};
static {
Display d = PlatformUI.getWorkbench().getDisplay();
COLOR_KEYWORD = new Color(d, 0, 0, 128);
COLOR_COMMENT = new Color(d, 0, 128, 0);
COLOR_OTHER = new Color(d, 0, 0, 0);
}
/**
* Initialise le scanner.
*/
public LuaEditorScanner() {
Token keyword = new Token(new TextAttribute(COLOR_KEYWORD, null, SWT.BOLD));
Token comment = new Token(new TextAttribute(COLOR_COMMENT, null, SWT.ITALIC));
Token other = new Token(new TextAttribute(COLOR_OTHER));
WordRule rule = new WordRule(new IWordDetector() {
@Override
public boolean isWordPart(char c) {
return Character.isJavaIdentifierPart(c);
}
@Override
public boolean isWordStart(char c) {
return Character.isJavaIdentifierStart(c);
}
}, other);
for (String k : KEYWORDS) {
rule.addWord(k, keyword);
}
setRules(new IRule {
rule,
new SingleLineRule("--", null, comment, (char) 0, true)
});
}
}
La classe LuaEditorConfiguration
A présent que nous disposons d'un scanner de source lua, il nous faut le déclarer. On va pour cela créer une classe permettant de configurer l'éditeur de code source. La classe LuaEditorConfiguration va dériver de la classe TextSourceViewerConfiguration qui fournit un paramétrage par défaut pour configurer un éditeur de texte destiné à la manipulation de code source. Nous allons simplement redéfinir la méthode getRepresentationReconciler qui est appelée automatiquement et sert à fournir une classe permettant de mettre à jour la mise en forme du document au fur et à mesure de la saisie. Pour cela, on défini deux éléments clé : un damager et un repairer.
Le Damager (interface IPresentationDamager) est chargé de déterminer quelle portion du document doit être à nouveau mise en forme suite à un changement de celui-ci; Le Repairer (interface IPresentationRepairer) se charge de calculer les nouveaux styles à appliquer sur cette portion de document. Dans notre cas, on crée une instance de DefaultDamagerRepairer qui implémente les deux interfaces précédentes et utilise un scanner afin de déterminer les styles à appliquer. Nous allons simplement lui fournir une instance de notre LuaEditorScanner. On obtient alors la classe suivante :
package damien.tutorial.eclipse.editor.lua;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.presentation.IPresentationReconciler;
import org.eclipse.jface.text.presentation.PresentationReconciler;
import org.eclipse.jface.text.rules.DefaultDamagerRepairer;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.ui.editors.text.TextSourceViewerConfiguration;
/**
* Défini la configuration pour notre éditeur lua
* @author damien
*/
public class LuaEditorConfiguration extends TextSourceViewerConfiguration {
@Override
public IPresentationReconciler getPresentationReconciler(ISourceViewer sourceViewer) {
PresentationReconciler pr = new PresentationReconciler();
DefaultDamagerRepairer ddr = new DefaultDamagerRepairer(new LuaEditorScanner());
pr.setDamager(ddr, IDocument.DEFAULT_CONTENT_TYPE);
pr.setRepairer(ddr, IDocument.DEFAULT_CONTENT_TYPE);
return pr;
}
}
La classe LuaEditor
Il s'agit de la partie la plus simple de notre éditeur. Nous allons créer une nouvelle classe dérivant de TextEditor, cette classe se contentera "d'indiquer" sa configuration, c'est à dire de passer une nouvelle instance de LuaEditorConfiguration à la méthode setSourceViewerConfiguration. On obtient le code suivant :
package damien.tutorial.eclipse.editor.lua;
import org.eclipse.ui.editors.text.TextEditor;
/**
* Défini notre éditeur pour les fichiers script lua.
* @author damien
*/
public class LuaEditor extends TextEditor {
/**
* Crée et configure notre éditeur.
*/
public LuaEditor() {
setSourceViewerConfiguration(new LuaEditorConfiguration());
}
}
Ajout de l'extension
A présent que les classes composant notre éditeur sont réalisées, il ne nous reste plus qu'à créer une extension permettant d'utiliser l'éditeur en question. C'est cette extension qui fait qu'une fois notre plugin chargé par eclipse, l'éditeur LuaEditor sera disponible pour l'utilisateur.
Pour configurer notre extension, il faut retourner dans la page de configuration de notre plugin (ouvrir plugin.xml). On choisi alors l'onglet Extensions. Une fois dans cette page, on ajoute une nouvelle extension avec le bouton Ajouter.... Il faut choisir une extension de type org.eclipse.ui.editors. Un clic droit sur cette extension permet de définir un nouvel éditeur. La zone droite de la page va permettre de renseigner les éléments qui vont permettre de configurer l'éditeur. Cinq champs vont permettre de configurer notre éditeur :
- id : défini l'identifiant de notre éditeur, généralement on choisi le nom du plugin
- name : défini le nom de l'éditeur tel qu'il apparaît dans la liste des éditeurs dans Ouvrir avec...
- icon : associe une image (généralement 16x16) à notre éditeur, sans cette image l'éditeur n'apparaît pas dans la liste des éditeurs
- extensions : associe l'éditeur à une ou plusieurs extensions de fichier, ici on va indiquer "lua"
- class : indique la classe java correspondant au code de notre éditeur : damien.tutorial.eclipse.editor.lua.LuaEditor

Test de notre éditeur
Afin de tester notre éditeur, revenons sur la page d'accueil de la configuration du plugin, puis cliquons sur Lancer une application Eclipse. Un nouvel environnement comprenant notre plugin est chargé. Il ne nous reste plus qu'à ouvrir ou créer un nouveau fichier lua et à l'éditer pour voir les mots clé et les commentaires se colorer au fur et à mesure de la saisie !
La vue supérieure montre notre éditeur en action, les mots clés apparaissent en bleu et les commentaires en vert. Le même code est présenté dessous dans un simple éditeur de texte.
Pour utiliser notre éditeur dans notre environnement de développement, il suffit de l'exporter (soit en archive soit dans un répertoire) et de copier le résultat dans le répertoire plugins d'eclipse. Au prochain démarrage d'eclipse, notre éditeur sera disponible.
Et maintenant ?
Notre éditeur est loin d'offrir toutes les fonctionnalités d'un outil de développement, ce n'est qu'un "simple" éditeur de texte colorant certains mots. Il sera intéressant d'y ajouter :
- complétion automatique
- validation du code source lors de sa sauvegarde, détection des erreurs de compilation
- coloration syntaxique des chaînes de caractères, des commentaires multi-ligne
- ...
Commentaires
Aucun commentaire pour le moment.
Ajouter un commentaire
Les commentaires pour ce billet sont fermés.