Autocompléter du JavaScript avec Tern

Quel que soit le langage avec lequel on travaille, le besoin d'une bonne autocomplétion se fait vite sentir dès lors que l'on est amené à travailler sur des programmes de plus de quelques lignes. Le problème, c'est que tous les langages de programmation ne sont pas égaux face à l'autocomplétion : les langages fortement typés, tels que le Java ou le C# sont simples à traiter pour un moteur d'autocomplétion et disposent ainsi de très bons outils d'édition.

Pour les langages à typage faible, comme le JavaScript, ce n'est pas aussi simple : il n'est pas toujours possible de déterminer le type d'une variable ou le résultat d'une opération avec une simple analyse statique du code, ce qui rend la création d'outils d'édition avancés très compliquée pour ces langages.

Tern est un moteur d'analyse avancé de code JavaScript : il analyse en temps réel le code source en cours d'édition et détermine le type des différentes variables et les résultats des opérations afin de fournir une autocomplétion solide pour l'édition de programmes en JavaScript.

Tern est indépendant de tout éditeur de texte ce qui lui permet d'être intégré à de nombreux éditeurs via des plugins spécifiques. Il est par exemple disponible pour VIM, Emacs, Sublime Text, et plusieurs autres éditeurs.

Installation de Tern

Étant donné que l'installation de Tern est dépendante de l'éditeur que vous utilisez, je ne vais pas détailler cette partie et je vous laisse vous référer à la documentation spécifique à chaque éditeur :

Amélioration de l'autocompletion d'un projet

Une fois le plugin Tern installé pour votre éditeur préféré, l'autocomplétion des sources JavaScript devrait être fonctionnelle. Cette autocomplétion peut être suffisante si vous écrivez des programmes JavaScript basiques qui tiennent en un seul fichier, sinon, il faut créer un fichier de configuration à la racine de votre projet pour indiquer à Tern quels sont les fichiers à analyser, et si vous travaillez dans des contextes ou avec des bibliothèques spécifiques (Node.js, RequireJS, Angular…).

Le fichier de configuration est au format JSON et doit être nommé .tern-project. Voici un exemple de configuration :

{
  "ecmaVersion": 5,

  "loadEagerly": [
    "./src/**/*.js",
    "./libs/*.js"
  ],

  "libs": [
    "browser",
    "underscore"
  ],

  "plugins": {
    "angular": {},
    "node": {}
  }
}

ecmaVersion

La version du standard ECMAScript utilisé dans votre code (5 ou 6, défini par défaut à 6).

loadEagerly

Permet de lister des fichiers à charger et à analyser dès que possible (l'utilisation de *glob patterns* est autorisée). Cette option n'est généralement pas nécessaire lorsque l'on utilise un plugin permettant le chargement de module (node, requirejs…).

libs

La section libs sert à charger des fichiers JSON contenant des définitions statiques qui permettent d'autocompléter une bibliothèque ou un environnement particulier.

Tern en fournit un certain nombre de base comme par exemple browser, jquery, underscore,... la liste complète peut être trouvée sur la page Github du projet.

Il est également possible de charger vos propres fichiers de définition en fournissant leurs chemins, relatifs à la racine de votre projet :

{
  "libs": [
    "./defs/mylib.json",
    "./defs/foobar.json"
  ]
}

Si vous souhaitez écrire vos propres fichiers de définition, vous pouvez lire la documentation de Tern sur le sujet.

plugins

Pour les cas les plus complexes, où les définitions statiques sont insuffisantes (pour gérer des systèmes de module par exemple), il est possible de faire appel à des plugins.

Tern en embarque plusieurs en standard. Parmi les plus utiles on peut noter node (et les plugins qu'il charge automatiquement : commonjs et node_resolve), requirejs, es_modules, webpack et angular. La liste complète est disponible dans la section plugins de la documentation de Tern.

Note

NOTE : Contrairement à la section libs, la section plugins n'est pas une liste mais un objet ; il est en effet possible de configurer certains plugins.

Comme pour les fichiers de définitions statiques, il est possible de charger des plugins tiers. D'après la documentation il est possible de charger un fichier JavaScript depuis le dossier du projet, mais en pratique cela ne fonctionne pas. Pour utiliser des plugins tiers, il faut se rendre dans le dossier où est installé le plugin Tern pour votre éditeur de texte et y installer le plugin Tern désiré (oui ça fait beaucoup de plugins en une seule phrase).

Par exemple, si je veux installer le plugin tern-abitbol pour mon éditeur VIM, ça donne :

cd ~/.vim/plugins/plugged/tern_for_vim/
npm install tern-abitbol

Et pour l'utiliser dans les projets où j'en ai besoin, il me suffit d'ajouter une ligne à mon fichier .tern-project :

{
  "plugins": {
    "abitbol": {},
  }
}

Il existe de nombreux plugins pour de nombreuses bibliothèques et framework, une simple recherche sur NPM vous en donnera la liste.

Le mot de la fin

Je me sers de Tern depuis quelques mois maintenant, et bien que le résultat ne soit pas toujours parfait (JavaScript est vraiment un langage très compliqué à analyser), il s'en sort généralement bien.

Si vous êtes curieux de savoir comment ça marche à l'intérieur (comment Tern fait pour analyser le code et deviner les types), je vous invite à lire cet article écrit par l'auteur du logiciel, où il explique sa démarche et le fonctionnement général de Tern.