Aller au contenu

Extension LSP (serveur de langage)

Intelligence du code propulsée par le Language Server Protocol — soulignements d'erreurs, navigation, survol, complétion, aide à la signature, formatage, recherche de références, renommage et aller au symbole — pour tout langage disposant d'un serveur de langage. L'éditeur parle au serveur via LSP ; l'extension est le client. Les serveurs disponibles sont pilotés par les données (un petit JSON par langage) : C++/clangd ci-dessous n'est qu'une des définitions livrées.

L'éditeur n'embarque ni ne télécharge de serveurs

Un serveur de langage est un programme séparé que vous installez une fois. MTE détecte un serveur installé et le pointe vers votre projet ; il n'en livre jamais. Voir Obtenir un serveur de langage.

Fonctionnalités actuelles

Fonctionnalité Comment
Diagnostics Les erreurs/avertissements du serveur apparaissent en soulignements ondulés (rouge = erreur, ambre = avertissement, bleu = info, gris = indice), mis à jour pendant la frappe.
Aller à la définition Curseur sur un symbole et F12, ou Recherche ▸ Navigation dans le code ▸ Aller à la définition. Saute à la définition, en ouvrant un autre fichier si nécessaire.
Rechercher les références Curseur sur un symbole → Shift+F12 (ou Recherche ▸ Navigation dans le code ▸ Rechercher les références). Les résultats s'ouvrent dans un panneau inférieur Références, groupés par fichier avec un aperçu de la ligne source ; double-clic pour sauter.
Survol Laissez le pointeur sur un symbole ; une infobulle montre son type/sa signature et sa documentation (rendue depuis le Markdown du serveur).
Complétion Ctrl+Space demande les complétions au serveur et les fond dans la fenêtre de complétion de l'éditeur (avant les suggestions intégrées de mots-clés/mots des tampons), filtrées pendant la frappe.
Aide à la signature Taper le ( d'un appel ou une , affiche une bulle avec la signature de la fonction et le paramètre actif en surbrillance ; ) la ferme.
Formater le document Alt+Shift+F (⌃⌥F sur macOS ; ou Édition ▸ Formater le document) reformate tout le document via le serveur, en une seule étape d'annulation. Le style relève de la configuration du serveur (pour clangd, un fichier .clang-format).
Rechercher les références (ci-dessus) —
Renommer le symbole Curseur sur un symbole → Shift+F6 (ou Édition ▸ Renommer le symbole). Saisissez un nouveau nom ; chaque usage est mis à jour. Quand le renommage couvre plusieurs fichiers, une confirmation est demandée d'abord — chaque fichier est ouvert, modifié et laissé modifié pour relecture et enregistrement.
Aller au symbole (dans le fichier) Ctrl+Shift+O ouvre la palette de commandes listant chaque symbole du fichier courant (fonctions, classes, méthodes…) ; tapez pour filtrer, Entrée saute. Chaque ligne montre le genre du symbole et son conteneur.
Aller au symbole dans l'espace de travail Ctrl+T cherche les symboles dans tout le projet pendant la frappe (le serveur fait la correspondance) ; Entrée ouvre le fichier au symbole.

Les sélecteurs « aller au symbole » sont les modes symboles de la palette de commandes — voir la palette de commandes.

Les commandes sont grisées tant qu'aucun serveur ne tourne

Aller à la définition, Rechercher les références, Formater le document et Renommer le symbole ne sont actives que lorsqu'un serveur de langage pour le langage du fichier courant est prêt — et que ce serveur a déclaré prendre en charge la requête lors de sa poignée de main. Sur un fichier sans serveur configuré, ou pendant qu'un serveur démarre, redémarre ou a abandonné, les entrées de menu sont grisées au lieu de ne rien faire une fois cliquées. L'indicateur d'état ci-dessous vous dit de quel cas il s'agit. Redémarrer les serveurs de langage n'est jamais grisée : c'est la porte de sortie d'un serveur qui a abandonné.

Références inter-fichiers et renommage exigent l'index du serveur

Rechercher les références et Renommer n'atteignent d'autres fichiers qu'une fois le projet indexé par le serveur. Pour C/C++, cela signifie un compile_commands.json (voir plus bas) et l'index d'arrière-plan de clangd ; sans lui, ils n'opèrent que sur le fichier ouvert.

Le renommage s'annule fichier par fichier pour l'instant

Un renommage multi-fichiers est appliqué fichier par fichier : chaque fichier est sa propre étape d'annulation (la boîte de confirmation le précise). Relisez les onglets modifiés avant d'enregistrer.

Dans le menu contextuel de l'éditeur

Faites un clic droit sur un symbole dans le texte : le menu propose, sous les commandes d'édition, Aller à la définition, Rechercher les références, Renommer le symbole et Formater le document. Chacune n'apparaît que si la même commande du menu principal fonctionnerait à ce moment-là — un serveur de langage est prêt pour le fichier et la prend en charge et, pour Renommer et Formater, le document n'est pas en lecture seule. Dans un fichier de texte brut, dans la marge à gauche du texte ou pendant le démarrage du serveur, aucune n'apparaît.

Les entrées agissent sur le symbole sur lequel vous avez fait un clic droit, et non sur celui qui se trouve sous le curseur ; le curseur et la sélection restent où ils étaient. Renommer propose la sélection comme ancien nom si vous avez cliqué à l'intérieur, et sinon le mot sous le pointeur. Formater le document met toujours en forme le document entier. Un menu ouvert au clavier agit au niveau du curseur.

Là où la plateforme affiche les raccourcis clavier dans les menus contextuels (Windows), chaque entrée affiche son raccourci — celui qui est en vigueur, y compris vos propres modifications dans le Mappeur de raccourcis. Sous macOS, les menus contextuels n'affichent pas de raccourcis.

Comment vos modifications parviennent au serveur

La frappe n'envoie pas un message par touche. Les modifications sont regroupées pendant une fraction de seconde puis envoyées comme une seule — et sous forme d'une seule plage modifiée plutôt que du fichier entier, dès lors que le serveur déclare savoir la traiter (la plupart le savent). Un gros fichier coûte donc quelques dizaines d'octets par salve de frappe au lieu d'une copie de lui-même.

Tout ce qui interroge le serveur sur le texte — infobulle, aller à la définition, rechercher les références, renommer, formater, complétion, aide à la signature — envoie d'abord la modification en attente, pour qu'une réponse ne porte jamais sur une version que vous avez déjà dépassée. L'enregistrement d'un fichier fait de même.

Démarrer un serveur ne bloque pas l'éditeur non plus : le processus est lancé en arrière-plan et le segment d'état affiche … jusqu'à la fin de la négociation. Un binaire de serveur qui se bloque au démarrage ne vous coûte que ce langage.

Un plafond au nombre de problèmes dessinés

Au plus 2000 ondulations sont dessinées par document. Au-delà, le fichier est de toute façon illisible, et chaque marqueur supplémentaire coûte du temps réel à l'éditeur — quand un fichier en produit davantage, les 2000 premiers sont affichés et la barre d'état indique Affichage des 2000 premiers problèmes sur N dans ce fichier, plutôt que de laisser le compte paraître discrètement plus petit qu'il n'est. C'est en général le signe que le serveur regarde le fichier de travers : une base de compilation manquante, ou un fichier généré qui n'est pas censé se compiler seul.

L'indicateur d'état du serveur

La barre d'état affiche un segment compact LSP: (à gauche des autres segments d'extension) résumant chaque serveur de langage pertinent pour vos fichiers ouverts :

LSP: cpp ✓ · no compile database  python ↻2  rust ✗
Glyphe Signification
… Démarrage — le serveur a été lancé et négocie.
✓ Prêt — répond aux requêtes.
↻n Redémarrage — le serveur s'est arrêté de façon inattendue ; la tentative automatique n° n est planifiée.
✗ Aucun serveur installé pour ce langage. Installez-en un (voir plus bas), puis utilisez Redémarrer les serveurs de langage.
⚠ Abandonné — le serveur n'a cessé de planter et les redémarrages automatiques se sont arrêtés. Seul un redémarrage manuel le relance.

Un langage peut porter une courte précision après le glyphe — le serveur tourne, et quelque chose dans votre projet limite quand même ce qu'il peut répondre. Une seule est affichée à la fois, la plus grave d'abord :

Précision Signification
· le serveur ne répond pas Plusieurs requêtes d'affilée sont restées sans réponse. Le serveur est vivant mais bloqué : Afficher le journal du serveur de langage, puis Redémarrer les serveurs de langage. La précision disparaît dès que le serveur répond à quoi que ce soit.
· aucune base de compilation Le serveur C/C++ tourne mais ne voit que le fichier ouvert — voir Les projets C/C++ ont besoin de compile_commands.json.
· pas de Cargo.toml, · pas de go.mod, · pas de tsconfig.json, … Le projet ne contient aucun des fichiers dont ce serveur a besoin pour le comprendre comme un projet. Il continue de travailler sur le fichier ouvert ; les réponses inter-fichiers resteront incomplètes tant que le fichier n'existe pas. Le nom cité est celui auquel le serveur tient le plus — voir Ce que chaque serveur attend du projet.

Le segment apparaît quand s'ouvre le premier document d'un langage configuré, ne suit que les langages dont vous avez réellement des documents ouverts — fermer le dernier fichier d'un langage retire son entrée (en rouvrir un la ramène aussitôt) — et disparaît entièrement quand plus aucun document de ce type ne reste.

Recherche ▸ Navigation dans le code ▸ Afficher le journal du serveur de langage montre les dernières sorties de chaque serveur en cours. C'est là qu'un serveur s'explique : une chaîne d'outils manquante, un fichier de configuration illisible, un projet qu'il a refusé de charger. La fenêtre est une fenêtre ordinaire, redimensionnable — chaque langage forme un en-tête nommant le serveur qui tourne pour lui, le texte est sélectionnable, Copier place tout le journal dans le presse-papiers, et la taille que vous lui donnez est mémorisée.

Si un serveur plante

Un serveur de langage qui se termine de façon inattendue est redémarré automatiquement avec un délai croissant (1 s, 2 s, 4 s… plafonné à 30 s). Pendant ce temps, l'indicateur montre ↻n et une notification de la barre d'état signale le premier plantage. Après un redémarrage réussi, vos documents ouverts sont réannoncés au serveur et les diagnostics reviennent d'eux-mêmes ; les soulignements périmés du serveur mort sont effacés immédiatement. L'édition n'est jamais bloquée.

Après 5 tentatives échouées d'affilée, l'extension abandonne : l'indicateur montre ⚠, une notification vous renvoie aux paramètres, et le langage reste éteint jusqu'à un redémarrage manuel. Un serveur qui a fonctionné correctement au moins une minute retrouve un quota de tentatives neuf lors d'un plantage ultérieur.

Redémarrage manuel — au choix :

  • Recherche ▸ Navigation dans le code ▸ Redémarrer les serveurs de langage (pas de raccourci par défaut ; aussi disponible depuis la palette de commandes), ou
  • le bouton Re-scanner / redémarrer les serveurs dans Préférences ▸ Serveur de langage.

Les deux arrêtent tous les serveurs, relancent la découverte avec les paramètres actuels et remettent à zéro le quota d'échecs — à utiliser après avoir installé un serveur manquant ou réparé un serveur cassé.

Pour voir pourquoi un serveur est mécontent avant de le redémarrer, utilisez Recherche ▸ Navigation dans le code ▸ Afficher le journal du serveur de langage.

Obtenir un serveur de langage

Installez le serveur de votre langage et vérifiez qu'il est dans le PATH (ou définissez un chemin explicite dans les paramètres). Si aucun n'est trouvé, la fonctionnalité ne fait simplement rien pour ce langage — l'indicateur d'état montre ✗ et le journal consigne un indice unique, mais rien ne vous interrompt.

MTE livre des définitions de serveurs (pas les serveurs) pour de nombreux langages : la plupart n'exigent que l'installation du serveur — aucune édition de JSON. Les définitions livrées couvrent C/C++, Objective-C, Python, Rust, Go, TypeScript, JavaScript, C#, Java, Kotlin, Swift, HTML, CSS/SCSS/Less, JSON, YAML et les scripts shell.

Langage Serveur Où l'obtenir
C/C++ clangd winget install LLVM.LLVM (Windows), le composant « C++ Clang tools » de l'installeur Visual Studio, brew install llvm (macOS), ou le paquet clang-tools-extra de votre distribution.
Python pyright ou pylsp npm i -g pyright (nécessite Node.js) ou pip install python-lsp-server (ne nécessite que Python).
TypeScript/JS typescript-language-server npm i -g typescript-language-server typescript.
Rust rust-analyzer rustup component add rust-analyzer.
Go gopls go install golang.org/x/tools/gopls@latest.
C# csharp-ls ou OmniSharp dotnet tool install -g csharp-ls (un simple outil dotnet, essayé en premier), ou une version d'OmniSharp téléchargée.
Java jdtls (Eclipse JDT Language Server) brew install jdtls (macOS), ou une archive décompressée dont le bin est dans le PATH.

Vérifiez dans un terminal, p. ex. clangd --version ou where clangd / which clangd. Pour un langage sans définition livrée, ajoutez-en une comme décrit dans Ajouter un langage.

Python : pyright ou pylsp

Python dispose de deux définitions livrées, et OctoEdit lance celle qui est réellement installée :

  1. pyright (pyright-langserver) est essayé en premier. C'est un paquet npm, il exige donc Node.js : npm i -g pyright.
  2. pylsp (python-lsp-server) est utilisé quand pyright est introuvable. C'est un paquet pip et il n'exige que Python : pip install python-lsp-server.

Si les deux sont installés, pyright l'emporte. Les paramètres ne comportent qu'une seule ligne pour Python (un indicateur d'activation, un chemin de remplacement), et elle s'applique au serveur effectivement utilisé — indiquer un chemin explicite vers un exécutable pylsp lance pylsp avec ses arguments de démarrage, pas ceux de pyright. Afficher le journal du serveur de langage nomme le serveur qui tourne vraiment.

Plusieurs serveurs pour un même langage

C'est un mécanisme général, pas un cas particulier de Python : un langage peut avoir autant de définitions livrées que nécessaire, ordonnées par un champ priority, et c'est la première dont l'exécutable est trouvé sur votre machine qui démarre. C# fonctionne de la même façon — csharp-ls d'abord, OmniSharp ensuite — parce que les deux prennent des arguments de démarrage différents et qu'un seul peut être correct.

Java : l'espace de travail propre au serveur

jdtls refuse de démarrer sans un répertoire de travail inscriptible qui lui appartient (-data). OctoEdit le fournit automatiquement, par projet, dans le dossier de données du plugin :

<user data>/plugins/org.mte.lsp/jdtls-workspaces/<project id>/

Vous n'avez jamais à le créer ni à le nommer. Le supprimer est sans danger — jdtls le reconstruit, au prix d'une réindexation.

Installation sous Windows

Une machine Windows neuve n'a en général aucun de clangd, cmake, ninja, node ou python dans son PATH. Cette section indique d'où vient chacun d'eux et comment le vérifier. Vous n'avez normalement pas à modifier le PATH vous-même — OctoEdit parcourt aussi les répertoires d'écosystèmes listés plus bas.

Ce que vous voulez Installation Où cela atterrit
clangd (C/C++) winget install LLVM.LLVM C:\Program Files\LLVM\bin\clangd.exe
cmake et ninja Le composant « C++ CMake tools for Windows » de l'installeur Visual Studio …\Microsoft Visual Studio\2022\<edition>\Common7\IDE\CommonExtensions\Microsoft\CMake\CMake\bin et …\CMake\Ninja — hors du PATH, mais accessibles via l'environnement de développement Visual Studio (voir plus bas). Un winget install Kitware.CMake / winget install Ninja-build.Ninja autonome les place au contraire dans le PATH.
pyright (Python) winget install OpenJS.NodeJS.LTS, puis npm i -g pyright %APPDATA%\npm\pyright-langserver.cmd
pylsp (Python, sans Node) Python depuis le Microsoft Store ou python.org, puis pip install python-lsp-server %LOCALAPPDATA%\Packages\PythonSoftwareFoundation.Python.3.*_*\LocalCache\local-packages\Python3*\Scripts\pylsp.exe (Store) ou %APPDATA%\Python\Python3*\Scripts\pylsp.exe

Vérifier ce qui est visible. Dans une invite de commandes ou un PowerShell ordinaire :

where clangd
where cmake
where ninja
where pyright-langserver
where pylsp

where ne parcourt que votre PATH. Un outil qu'il ne trouve pas peut tout de même être trouvé par OctoEdit, car la recherche couvre également les répertoires d'écosystèmes ci-dessus — les répertoires de scripts de Python, en particulier, ne sont jamais dans le PATH. Si un langage reste sur ✗ après l'installation de son serveur, utilisez Recherche ▸ Navigation dans le code ▸ Redémarrer les serveurs de langage, qui relance toute la recherche.

La génération de la base de compilation s'exécute dans l'environnement de développement Visual Studio. Quand OctoEdit exécute CMake pour vous (voir plus bas), il localise d'abord Visual Studio — via VSINSTALLDIR, sinon vswhere.exe — et enveloppe l'appel dans le VsDevCmd.bat -arch=x64 de cette installation. C'est ce qui met cl, le SDK Windows et les cmake/ninja propres à Visual Studio à disposition le temps de cette seule commande, si bien que l'éditeur n'en a pas besoin dans votre PATH. Sans Visual Studio (machine avec clang seul ou MinGW), l'appel est fait tel quel dans l'environnement de l'éditeur et le journal le dit ; il vous faut alors cmake et ninja accessibles vous-même, ainsi qu'un compilateur que CMake sait identifier.

Régénérer à la demande. Recherche ▸ Navigation dans le code ▸ Générer la base de compilation… lance la même génération à tout moment — y compris quand une base existe déjà (elle est reconfigurée sur place) et quand vous aviez répondu Ne plus demander pour ce projet. L'entrée est toujours disponible : lorsqu'elle ne peut rien générer, elle dit pourquoi dans la barre d'état (aucun document C/C++, aucun dossier de projet, aucun CMakeLists.txt à la racine, aucun cmake, ou aucun générateur capable d'exporter ici) au lieu d'être grisée pour une raison invisible.

Où est stockée la réponse « ne plus demander ». Ne plus demander pour ce projet est mémorisé par racine de projet dans %APPDATA%\7-bits\OctoEdit.ini, sous plugins/org.mte.lsp/noCompileDbPrompt/<hash> (le chemin du projet est haché, car un chemin n'est pas une clé de paramètres valide). Générer la base de compilation… l'efface pour ce projet.

Où OctoEdit cherche un serveur

Vous ne devriez rien avoir à configurer. Après avoir installé un serveur, il suffit d'ouvrir un dossier et un fichier source. L'éditeur essaie, dans l'ordre :

  1. un chemin explicite défini dans les paramètres — il fait autorité : s'il ne mène à rien, le langage reste éteint plutôt que de se rabattre en silence sur autre chose ;
  2. votre PATH, pour la commande du serveur et chaque alias connu (clangd-20, clangd-19, …) ;
  3. les emplacements d'installation de la définition du serveur pour votre système d'exploitation ;
  4. une liste intégrée de répertoires d'écosystèmes.
Répertoires d'écosystèmes parcourus
Windows le préfixe global npm et %APPDATA%\npm, %LOCALAPPDATA%\Microsoft\WinGet\Links, %USERPROFILE%\.cargo\bin, %USERPROFILE%\go\bin et %LOCALAPPDATA%\go\bin, %USERPROFILE%\.dotnet\tools, %ProgramFiles%\LLVM\bin, %ProgramFiles%\nodejs, ainsi que les répertoires de scripts utilisateur de Python : %APPDATA%\Python\Python3*\Scripts, %LOCALAPPDATA%\Packages\PythonSoftwareFoundation.Python.3.*_*\LocalCache\local-packages\Python3*\Scripts (Python du Microsoft Store) et %LOCALAPPDATA%\Programs\Python\Python3*\Scripts
macOS /opt/homebrew/bin et /opt/homebrew/opt/llvm/bin (le clangd de Homebrew se trouve dans le second — il n'est jamais lié dans le premier), /usr/local/bin, /usr/local/opt/llvm/bin, ~/.cargo/bin, ~/go/bin, ~/.local/bin, ~/.npm-global/bin, ~/.dotnet/tools, ~/Library/Python/3.*/bin
Linux /usr/local/bin, /usr/lib/llvm*/bin, Linuxbrew, ainsi que les mêmes ~/.cargo/bin, ~/go/bin, ~/.local/bin, ~/.npm-global/bin, ~/.dotnet/tools

Les entrées avec * sont de vrais jokers : le répertoire de version que Python crée (Python313, 3.13, ou le …Python.3.13_qbz5n2kfra8p0 du Store) est confronté à ce qui se trouve réellement sur le disque, du plus récent au plus ancien. C'est là que pip install python-lsp-server place pylsp — et c'est pourquoi son installation ne demande aucune modification du PATH.

macOS : lancement depuis le Finder ou depuis un terminal

Une application lancée depuis le Finder ou le Dock n'hérite pas du PATH de votre profil de shell — macOS lui en donne un minimal (/usr/bin:/bin:/usr/sbin:/sbin). Homebrew, Cargo, Go et npm seraient tous invisibles. OctoEdit demande donc à votre shell de connexion quel est son PATH, le parcourt également, et transmet l'environnement obtenu au serveur qu'il démarre — un serveur installé par l'un de ces outils est donc trouvé quelle que soit la façon dont vous lancez l'éditeur.

Le serveur est démarré une fois par langage et arrêté avec l'éditeur.

Les projets C/C++ ont besoin de compile_commands.json

clangd ne comprend votre code avec précision que s'il connaît les drapeaux de compilation, via une base de compilation (compile_commands.json). Sans elle, clangd devine et les fonctions inter-fichiers (références, renommage) restent limitées au fichier ouvert. clangd est démarré dans les deux cas.

Un projet C/C++ est considéré comme configuré s'il possède l'un de ceci :

  • un compile_commands.json à la racine du projet ou dans l'un des dossiers de build parcourus par OctoEdit (build/, Build/, out/, out/build/, cmake-build-debug/, cmake-build-release/, .mte-compile-commands/) ;
  • un compile_flags.txt à la racine du projet ;
  • un fichier de configuration .clangd à la racine du projet.

clangd lit lui-même les deux derniers ; rien n'est donc signalé à leur sujet.

Quand rien de tout cela n'est présent — pour tout projet C/C++, quel que soit son système de build — le segment d'état affiche · aucune base de compilation et un message de la barre d'état le dit une fois. Rien ne bloque ; aucune boîte de dialogue n'interrompt votre saisie.

Laisser OctoEdit la générer (projets CMake)

Si la racine du projet contient un CMakeLists.txt et qu'un CMake capable de produire réellement une base est disponible, cela vous est proposé une fois par projet :

  • Générer — exécute CMake en arrière-plan, en configuration seule : rien n'est compilé et vos propres dossiers de build restent intacts. Le résultat va dans <project>/.mte-compile-commands/. En cas de succès, clangd est redémarré avec la nouvelle base ; en cas d'échec, les dernières lignes du journal CMake sont affichées, accompagnées de la commande exacte qui a été tentée, afin que vous puissiez la reprendre vous-même.
  • Comment le faire manuellement — affiche cette même commande sans l'exécuter.
  • Ne plus demander pour ce projet — mémorisé par racine de projet.

La fenêtre « comment le faire manuellement » comme la fenêtre d'échec affichent la ligne de commande exacte dans un bloc de code sélectionnable, au-dessus d'un unique bouton Copier dont l'infobulle dit ce qu'il prend : la commande seule, ou la commande accompagnée de la fin du journal CMake lorsqu'il y en a un. La fenêtre d'échec est redimensionnable et mémorise la taille que vous lui donnez — chacune de ces fenêtres mémorise sa PROPRE taille, si bien qu'agrandir le journal du serveur ne fait pas s'ouvrir aussi grand une fenêtre d'instructions de deux lignes.

Vous n'êtes pas limité à cette unique proposition : Recherche ▸ Navigation dans le code ▸ Générer la base de compilation… lance la même génération quand vous le souhaitez — après avoir ajouté des fichiers, changé des options de build, ou après avoir répondu Ne plus demander pour ce projet (ce qui est alors effacé). Une base existante est reconfigurée sur place. L'entrée est toujours disponible ; lorsqu'elle ne peut rien générer, elle dit pourquoi dans la barre d'état.

Vos préréglages d'abord. Si le projet possède un CMakePresets.json (ou un CMakeUserPresets.json), OctoEdit configure avec l'un de vos préréglages plutôt que d'inventer une ligne de commande — cmake --preset <name> -B <project>/.mte-compile-commands -DCMAKE_EXPORT_COMPILE_COMMANDS=ON. Ainsi le générateur, le toolset et le bloc environment du préréglage (un QT6_DIR, un chemin de SDK) sont ceux dont le projet dépend déjà, et CMake les applique lui-même. Les préréglages masqués, exclus par une condition sur votre système, ou configurant avec un générateur incapable d'exporter sont ignorés ; un nom contenant debug est préféré. Votre propre dossier de build n'est jamais touché : l'éditeur redirige toujours la sortie avec -B.

Sous Windows, CMake est lancé dans un environnement de développement Visual Studio. Un éditeur démarré depuis l'Explorateur ou le menu Démarrer n'a pas de cl dans son PATH, et CMake se contenterait alors sans bruit de n'importe quel autre compilateur trouvé — y compris le clang.exe livré à l'intérieur d'une installation Visual Studio, qui échoue aussitôt sur des réglages propres à MSVC. OctoEdit localise donc Visual Studio avec vswhere et encapsule l'appel dans VsDevCmd.bat -arch=x64. Si aucun Visual Studio n'est installé (machine MinGW ou clang uniquement), l'appel se fait tel quel et le journal le signale.

Une tentative échouée précédente est nettoyée avant un nouvel essai : son CMakeCache.txt figerait sinon le compilateur qui a échoué. Un dossier contenant déjà un compile_commands.json fonctionnel est reconfiguré sur place, jamais supprimé.

Pourquoi la proposition n'apparaît pas toujours

CMAKE_EXPORT_COMPILE_COMMANDS n'est honoré que par les générateurs Ninja et Makefile ; le générateur Visual Studio l'ignore et n'écrit aucune base. Sous Windows, pour un projet sans préréglage utilisable, la proposition n'apparaît donc que si ninja est dans votre PATH. Lorsque les seuls préréglages applicables d'un projet sont ceux de Visual Studio et qu'il n'y a pas non plus de Ninja, OctoEdit indique quel préréglage a fait obstacle plutôt que de proposer d'exécuter quelque chose qui ne pourrait pas fonctionner.

La générer vous-même (CMake)

CMAKE_EXPORT_COMPILE_COMMANDS n'est honoré que par les générateurs Ninja et Makefile — le générateur Visual Studio l'ignore (il ne produit pas de compile_commands.json même avec le drapeau). Générez donc la base avec Ninja, dans un dossier dédié, séparé de celui où vous construisez d'habitude : CMake refuse de reconfigurer un dossier de build existant avec un autre générateur, un projet construit avec Visual Studio a donc besoin de son propre dossier Ninja.

C'est une étape de configuration seule (pas de build complet). Depuis la racine du projet :

cmake -S . -B .mte-compile-commands -G Ninja -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
  • .mte-compile-commands/ fait partie des dossiers scannés par MTE, le compile_commands.json produit est donc trouvé automatiquement. Tout dossier scanné convient (build/, out/, cmake-build-* ou la racine du projet) — gardez-le simplement séparé d'un dossier configuré avec un autre générateur.
  • Exécutez-la depuis un environnement où le projet se configure vraiment : le bon compilateur dans le PATH (p. ex. une x64 Native Tools Command Prompt sous Windows) et les variables dont votre projet a besoin (comme QT6_DIR pour un projet Qt). Si cmake ne trouve pas votre compilateur ou SDK, la base sort vide ou sans chemins d'inclusion et clangd indexera mal.
  • Rouvrez le dossier ensuite. Le journal ne devrait plus dire « no compile_commands.json » ; laissez à clangd un moment pour bâtir son index d'arrière-plan avant que références/renommage ne couvrent les fichiers.

Autres systèmes de build : Bear (bear -- make), compiledb, Meson, Bazel et d'autres savent aussi produire un compile_commands.json que l'extension trouvera.

Quel serveur sert quel fichier

C'est le nom du fichier qui décide. Chaque définition de serveur liste les extensions qu'elle sert, et c'est la première définition qui revendique l'extension d'un fichier qui démarre. La casse est ignorée, et seul le dernier point compte — Deploy.Prod.SH est un script shell.

C'est volontairement indépendant de la coloration syntaxique. Désactiver la coloration, ou ouvrir un fichier dépassant la limite de taille de la coloration, ne vous coûte plus aussi le serveur de langage.

L'identifiant du coloriseur reste un repli

Si aucune définition ne revendique l'extension, OctoEdit se rabat sur l'identifiant de langage attribué par le moteur de coloration. Une définition écrite à la main qui nomme un langage sans lister d'extension continue ainsi de fonctionner.

Le nom que le serveur reçoit est une seconde chose, distincte : LSP a son propre vocabulaire d'identifiants de langage, et plusieurs ne correspondent pas à ceux d'OctoEdit. Un fichier .sh est annoncé comme shellscript, un .scss comme scss, un .jsx comme javascriptreact, un .mm comme objective-cpp. Les serveurs dont le comportement dépend du langage — le serveur CSS, le serveur TypeScript — ont besoin que ce soit juste.

Extension Servie par Annoncée comme
.cpp .cxx .cc .h .hpp .hh .hxx .ipp .inl clangd cpp
.c clangd c
.m / .mm clangd objective-c / objective-cpp
.sh .bash .zsh .ksh bash-language-server shellscript
.css / .scss / .less vscode-css-language-server css / scss / less
.ts .mts .cts / .tsx typescript-language-server typescript / typescriptreact
.js .mjs .cjs / .jsx typescript-language-server javascript / javascriptreact
.json / .jsonc .json5 vscode-json-language-server json / jsonc

.h est revendiquée par la définition C/C++ plutôt que par celle d'Objective-C — les deux lancent clangd, qui décide lui-même de ce que contient un en-tête.

Ce que chaque serveur attend du projet

Plusieurs serveurs ne fonctionnent correctement que si un fichier particulier se trouve à la racine du projet. Quand il manque, l'indicateur d'état le dit au lieu de faire comme si tout allait bien :

Langage Attend Sans lui
Rust Cargo.toml rust-analyzer ne charge pas la crate du tout.
Go go.mod gopls ne peut pas résoudre le module.
TypeScript tsconfig.json (ou package.json) les réponses à l'échelle du projet sont indisponibles.
JavaScript jsconfig.json (ou package.json) comme ci-dessus.
Python (pyright) pyproject.toml / pyrightconfig.json / setup.py pyright fonctionne quand même ; il utilise seulement les valeurs par défaut pour les racines d'import et la rigueur.
Java pom.xml / build.gradle jdtls ne peut pas construire de chemin de classes.
C/C++ compile_commands.json voir la section ci-dessus.

Ajouter un langage

Les serveurs sont décrits par des fichiers JSON que l'extension charge au démarrage depuis <editor>/lsp-servers/*.json et depuis votre dossier. Chaque entrée associe un langage à sa commande plus des indices de découverte :

{
    "language":    "cpp",
    "languageId":  "cpp",
    "languageIds": { ".c": "c" },
    "command":     "clangd",
    "aliases":     ["clangd-20", "clangd-19"],
    "args":        ["--background-index", "--log=verbose"],
    "extensions":  [".cpp", ".h", ".hpp", ".c"],
    "projectMarkers": [],
    "searchDirs":      ["${LLVM}/bin", "${ProgramFiles}/LLVM/bin"],
    "searchDirsMac":   ["/opt/homebrew/opt/llvm/bin"],
    "searchDirsLinux": ["/usr/lib/llvm-20/bin"],
    "docsAnchor":  "cpp-clangd",
    "compileCommandsArg": "--compile-commands-dir=${dir}"
}
  • language est l'identifiant propre d'OctoEdit pour le langage — la clé autour de laquelle se groupent la page de paramètres, le segment d'état et la connexion. command/aliases sont les noms d'exécutables essayés dans le PATH.
  • extensions est l'association principale fichier → langage (voir ci-dessus). Avec le point.
  • args compte un élément de tableau par argument. ["--background-index", "--log=verbose"] passe deux arguments ; ["--background-index --log=verbose"] passe un seul argument contenant une espace, que tout serveur refuse — et comme il s'arrête aussitôt, cela ressemble à une boucle de plantage plutôt qu'à une faute de frappe. OctoEdit signale cette forme dans le journal de diagnostic au chargement de votre fichier.
  • languageId (facultatif) est l'identifiant envoyé au serveur dans didOpen ; il vaut language par défaut. Utilisez-le quand le nom de la spécification LSP diffère du nôtre (bash → shellscript, objc → objective-c).
  • languageIds (facultatif) remplace languageId par extension, pour un serveur qui sert plusieurs langages de la spécification.
  • projectMarkers (facultatif) sont les noms de fichiers que le serveur attend à la racine du projet. Quand aucun n'est présent, le segment d'état affiche pas de <first marker> ; cela n'arrête jamais le serveur.
  • searchDirs est la liste des emplacements d'installation pour Windows ; searchDirsMac et searchDirsLinux en sont les équivalents par plateforme. Les trois développent ${VAR}, et les listes POSIX développent aussi un ~ initial. Une entrée dont les variables ne sont pas définies est ignorée plutôt que transformée en chemin absurde.
  • args peut utiliser deux marqueurs : ${dataDir}, le dossier de données inscriptible du plugin, et ${rootHash}, un identifiant court et stable du projet courant. C'est ainsi que jdtls reçoit son -data <dir>. Un marqueur impossible à résoudre est laissé tel quel dans l'argument plutôt que remplacé par rien.
  • compileCommandsArg (facultatif) est ajouté, ${dir} étant remplacé par le dossier de la base localisée — c'est ainsi que clangd est pointé vers compile_commands.json.
  • priority (facultatif, 0 par défaut) ordonne plusieurs définitions d'un même langage : le nombre le plus petit est essayé en premier, et c'est la première définition dont l'exécutable est trouvé qui démarre. C'est ainsi que Python obtient pyright (priority: 0) avec pylsp (priority: 10) en solution de repli.

Vos propres définitions de serveurs

Les définitions livrées se trouvent dans le répertoire d'installation, qui ne vous appartient pas (Program Files sous Windows, un paquet d'application signé sous macOS). Placez les vôtres à côté : Préférences ▸ Serveur de langage ▸ Ouvrir le dossier des définitions de serveurs personnelles crée et ouvre

<user data>/plugins/org.mte.lsp/lsp-servers/

Chaque *.json qui s'y trouve est chargé après l'ensemble livré, et :

  • un fichier dont language et command correspondent à une définition livrée la remplace — c'est ainsi que vous changez les arguments de clangd ou ajoutez un dossier de recherche ;
  • tout le reste est ajouté — un deuxième serveur pour un langage que nous livrons, ou un langage pour lequel OctoEdit ne livre rien du tout.

Un fichier qui n'est pas du JSON valide, ou auquel il manque language ou command, est ignoré et signalé dans le journal de diagnostic plutôt que de vous coûter un langage en silence.

Les définitions sont lues une seule fois, au démarrage : redémarrez l'éditeur après les avoir modifiées (le bouton Rechercher de nouveau / redémarrer les serveurs relance la recherche, pas le chargement).

Une définition personnelle minimale : <user data>/…/lsp-servers/zig.json
{
    "language":   "zig",
    "command":    "zls",
    "extensions": [".zig", ".zon"],
    "projectMarkers": ["build.zig"],
    "searchDirs":      ["${LOCALAPPDATA}/zls"],
    "searchDirsMac":   ["/opt/homebrew/bin", "~/.local/bin"],
    "searchDirsLinux": ["~/.local/bin"]
}

Quel dossier reçoit le serveur

Un serveur de langage doit savoir où se trouve le projet : rust-analyzer ne chargera pas une crate sans cela, gopls a besoin de la racine du module, pyright y lit pyrightconfig.json, et clangd y cherche sa base.

  • Avec un dossier ouvert (Fichier ▸ Ouvrir un dossier), les fichiers qu'il contient lui appartiennent.
  • Pour un fichier hors du dossier ouvert, ou sans aucun dossier ouvert, OctoEdit remonte depuis le fichier en cherchant compile_commands.json, compile_flags.txt, .clangd, CMakeLists.txt, Cargo.toml, go.mod, pyrightconfig.json, pyproject.toml, setup.py, tsconfig.json, package.json, pom.xml, build.gradle, un .sln, .git, et enfin un .editorconfig déclarant root = true. Le dossier du fichier lui-même sert de repli, si bien qu'un serveur n'est jamais laissé sans projet.
  • Ouvrir le dossier après le fichier fonctionne. Les serveurs qui comprennent les changements de dossiers de travail en sont informés ; les autres sont redémarrés et vos documents ouverts leur sont réannoncés automatiquement. Aucun redémarrage manuel n'est nécessaire.

Dépannage

Symptôme Que vérifier
Le segment LSP: n'apparaît jamais Aucune définition de serveur ne revendique l'extension de ce fichier. Consultez quel serveur sert quel fichier, ou ajoutez une définition.
· le serveur ne répond pas Le serveur a cessé de répondre. Afficher le journal du serveur de langage, puis Redémarrer les serveurs de langage.
✗ pour un langage dont vous avez installé le serveur Vérifiez que l'exécutable est bien là : where clangd (Windows) / which clangd (macOS, Linux). Puis Recherche ▸ Navigation dans le code ▸ Redémarrer les serveurs de langage, qui relance toute la recherche.
⚠ Le serveur a démarré puis n'a cessé de planter. Afficher le journal du serveur de langage en donne généralement la raison ; corrigez-la, puis redémarrez les serveurs.
· aucune base de compilation sur un projet C/C++ Voir plus haut. Références et Renommer ne verront que le fichier ouvert d'ici là.
La complétion ne renvoie rien sur un fichier C++ Ce fichier manque probablement dans compile_commands.json (un en-tête qu'aucune unité de traduction n'inclut, ou un fichier ajouté depuis la génération de la base). Régénérez-la.
Tout fonctionne depuis un terminal, mais pas depuis le Finder Signalez-le — la lecture du PATH du shell de connexion doit rendre les deux cas identiques. Afficher le journal du serveur de langage et le journal de diagnostic consignent ce qui a été parcouru.
Un serveur exige un fichier de projet que vous n'avez pas typescript-language-server a besoin du paquet typescript, gopls d'un go.mod, rust-analyzer d'un Cargo.toml, pyright d'un pyrightconfig.json/pyproject.toml pour des réponses à l'échelle du projet.

Paramètres du serveur de langage

Préférences ▸ Serveur de langage liste chaque langage disposant d'une définition de serveur. Par langage, vous pouvez :

  • Activer / désactiver le serveur (un langage désactivé n'en démarre jamais).
  • Définir un chemin d'exécutable explicite (avec Parcourir…). Cette valeur l'emporte sur la recherche PATH/searchDirs ci-dessus — utile quand le serveur n'est pas dans le PATH ou que vous voulez un build précis.
  • Ajouter des Arguments supplémentaires, accolés après ceux de la définition — par exemple --log=verbose pour clangd. La ligne est découpée comme une ligne de commande : mettez donc entre guillemets une valeur contenant des espaces.

Le champ du chemin est un chemin, pas une ligne de commande

Saisir C:\tools\clangd.exe --log=verbose dans Chemin de l'exécutable demande à OctoEdit de trouver un fichier dont le nom se termine par .exe --log=verbose : il n'existe pas, et le langage signale alors l'absence totale de serveur. Mettez l'exécutable dans le champ du chemin et ses arguments dans Arguments supplémentaires. Si cela arrive quand même, le journal de diagnostic le dit explicitement.

Les changements prennent effet à OK / Appliquer : si un drapeau d'activation ou un chemin a réellement changé, les serveurs redémarrent automatiquement avec les nouveaux paramètres (une page non touchée ne perturbe jamais les serveurs en cours). Le bouton Re-scanner / redémarrer les serveurs force en plus un arrêt-et-redécouverte avec les paramètres actuels — utile après l'installation d'un serveur que l'éditeur ne trouvait pas ; il remet aussi à zéro le quota de redémarrages après plantage.

Ouvrir le dossier des définitions de serveurs personnelles crée et ouvre le dossier décrit dans Vos propres définitions de serveurs.

Limites

  • La coloration du code dans les infobulles de survol n'est pas appliquée (le texte est mis en forme — gras, titres, code à chasse fixe — mais pas colorisé syntaxiquement).
  • La précision dépend du serveur : pour C/C++, sans compile_commands.json, clangd retombe sur des heuristiques et les résultats peuvent être incomplets.
  • OctoEdit n'installe ni ne télécharge jamais de serveur. Il trouve celui que vous avez installé et le démarre ; s'il n'y en a aucun, la fonction reste éteinte et le segment d'état le dit.
  • Un processus serveur par langage, partagé par tous les projets ouverts de ce langage. Un deuxième projet est ajouté comme dossier de travail si le serveur le prend en charge ; sinon il est servi sous la racine du premier projet.
  • Kotlin, Swift et Objective-C bénéficient de l'intelligence de code mais pas de la coloration syntaxique — l'éditeur n'a pas encore de coloriseur pour eux. Les deux sont désormais indépendants : le serveur fonctionne malgré tout.
  • Les définitions de serveurs sont lues une seule fois au démarrage ; les modifier (les vôtres ou celles livrées) exige un redémarrage de l'éditeur.