Configuration
Les réglages de Textchum suivent un principe : l'interface graphique est le moyen confortable de les changer, et un simple fichier JSON est la sortie de secours toujours disponible. Il y a exactement un seul dépôt — le fichier — et la fenêtre Réglages le lit et l'écrit ; rien ne vit uniquement à l'intérieur de l'application.
La fenêtre Réglages
Textchum → Settings… (⌘,) édite les réglages reconnus :
- Apparence — suivre le système (en changeant en direct quand macOS le fait) ou forcer le clair ou le sombre.
- Thème — la palette de syntaxe ; voir Thèmes plus bas.
- Icônes de fichier — un pack d'icônes VS Code pour l'arborescence ; voir Icônes de fichier.
- Ouvrir les fichiers dans — des onglets de la fenêtre courante (le défaut) ou des fenêtres séparées. Avec des fenêtres séparées, le navigateur de chaque fenêtre ne liste que les documents de son propre groupe d'onglets.
- Police — toute famille à chasse fixe installée sur le système, ou la police à chasse fixe de la plateforme.
- Taille de police — de 6 à 72 points.
- Largeur de tabulation — de 1 à 16 colonnes.
- Afficher les numéros de ligne — la marge, aussi basculables par session avec View → Toggle Line Numbers (⇧⌘L).
Chaque changement s'applique immédiatement aux fenêtres d'édition ouvertes et s'écrit sur disque au même instant. Il n'y a aucun bouton Appliquer ou Enregistrer à oublier.
Le fichier
Les réglages vivent dans :
~/Library/Application Support/Textchum/config.json
Un fichier édité à la main pourrait ressembler à :
{
"appearance": "dark",
"editor": {
"font_family": "JetBrains Mono",
"font_size": 13,
"tab_width": 4,
"hover": false
}
}
appearance accepte "system", "light" ou "dark" ; en son absence
(le défaut), le système est suivi. editor.hover désactive la bulle de
documentation au survol (true, le défaut, la laisse active).
editor.new_files_in place les documents neufs dans un "tab" du
groupe de la fenêtre frontale (le défaut) ou dans une "window" à
eux. editor.mark_occurrences (true par défaut) marque les autres
endroits où apparaît le mot sélectionné ;
editor.occurrences_case_sensitive et editor.occurrences_whole_word
décident de ce qui en est un, tous deux true par défaut.
Tout est optionnel — fichier, section ou clé manquants signifient simplement la valeur par défaut. Les écritures sont atomiques (fichier temporaire puis renommage), comme toute écriture de Textchum.
Deux garanties rendent l'édition à la main sûre :
- Les clés inconnues survivent. La fenêtre de réglages ne réécrit que les clés qui lui appartiennent. Tout le reste du fichier — vos annotations, des clés d'une version plus récente — est préservé tel quel à chaque enregistrement.
- Les fichiers cassés ne sont jamais écrasés. Si le fichier ne peut pas
être analysé, Textchum démarre avec les réglages par défaut, le signale
une fois au lancement et laisse le fichier exactement tel que vous
l'aviez écrit, pour le réparer dans n'importe quel éditeur — y compris
Textchum lui-même. Si vous changez un réglage depuis l'interface pendant
que le fichier est cassé, l'original inanalysable est d'abord copié vers
config.json.bak, puis remplacé.
Les valeurs hors limites ou mal typées ne comptent pas comme une casse : un
font_size de 4000 est ramené dans la plage valide, un font_family de
42 est ignoré, et le reste du fichier fonctionne normalement.
Ailleurs, le temps d'une exécution
--data-dir <chemin> garde tout ce que Textchum possède sous un seul
répertoire pour cette exécution — la configuration, les thèmes, les
packs d'icônes, la session et le journal du serveur de langage — au
lieu des emplacements habituels :
Textchum --data-dir ~/profil-d-essai
C'est un profil entier fabriqué pour l'occasion et jeté ensuite, le
vrai n'étant jamais ouvert ; make playground s'en sert, et tout ce
qui ne doit pas toucher à vos réglages aussi. Sous Linux, une exécution
avec son propre profil est son propre processus : remettre les fichiers
à une instance déjà lancée les ouvrirait dans le profil de celle-ci.
--config <chemin> en est la version étroite : il désigne un fichier
de configuration, et la session le suit.
Thèmes
Le sélecteur Theme de l'onglet General choisit la palette de syntaxe. Sept sont fournis d'origine : Textchum (par défaut), Textchum High Contrast, Graphite (un thème feutré presque monochrome) et les classiques — Molokai, Solarized, Dracula et Gruvbox. Chaque thème porte une palette claire et une palette sombre dans un même fichier, si bien qu'un thème sert aux deux modes d'apparence (les classiques nés sombres associent leur palette canonique à une claire au contraste ajusté ; Solarized et Gruvbox utilisent leurs vraies palettes claires).
Les thèmes personnels sont des fichiers JSON dans :
~/Library/Application Support/Textchum/themes/
sélectionnés par nom de fichier (sans .json) ; un fichier portant le
nom d'un thème intégré le remplace. Textchum → Open Themes Folder
ouvre (et crée) ce dossier. Le plus rapide pour en commencer un
est de générer un point de départ complet — chaque nom de capture
stylée, rempli avec la palette par défaut — et de ne changer que les
couleurs :
Textchum --emit-theme ~/Library/Application\ Support/Textchum/themes/Mien.json
Les entrées associent des noms de capture tree-sitter à des styles :
{
"name": "Mien",
"styles": {
"keyword": {"light": "#AD3DA4", "dark": "#FC5FA3", "bold": true},
"comment": {"light": "#707F8C", "dark": "#7F8C98", "italic": true}
}
}
Les couleurs s'écrivent #RRGGBB ou #RRGGBBAA. Tout ce qui est omis
— une couleur, un drapeau, une capture entière — garde la valeur de la
palette par défaut : un thème n'a besoin de dire que ce qu'il change.
Les règles de secours sont celles de la configuration : un thème qui ne
se laisse pas analyser retombe sur le thème par défaut avec un seul
avertissement et n'est jamais écrasé, et les clés inconnues survivent.
Les fichiers de thème sont lus au lancement et au changement de
sélection.
En importer un depuis un autre éditeur
Textchum → Importer un thème récupère les couleurs de VS Code ou de
TextMate. Choisissez un fichier de thème, ou un dossier qui en contient
plusieurs — un répertoire d'extension VS Code (son package.json dit
ce qu'il apporte) ou un bundle TextMate (ses thèmes sont dans
Themes/). Tout ce qui s'y trouve est importé, et le premier est mis.
Les deux éditeurs décrivent la couleur par portée TextMate ;
importer revient donc à traduire les portées vers les noms de capture
de Textchum : entity.name.function devient function,
keyword.control.loop devient repeat. Les portées s'arrêtent là où
les captures continuent — aucun thème ne colore if autrement que
while — alors une capture que la source n'a jamais nommée prend la
couleur de celle dont elle est un cas particulier, dans les deux sens :
un thème qui dit keyword colore toutes les sortes de mot-clé, et un
qui ne dit que constant.numeric colore toute la famille des
constantes.
Deux choses à savoir avant de trouver les couleurs fausses :
- Un thème remplit une apparence. Les deux éditeurs écrivent un thème pour un fond clair ou pour un fond sombre ; ceux de Textchum portent les deux. L'import remplit le côté que la source déclare et laisse l'autre à la palette par défaut, en disant lequel il a rempli. Importer un thème sombre alors que l'éditeur est en apparence claire ne change rien de visible.
- Les portées sans destination sont nommées. Tout ce que la source a coloré et à quoi aucune capture ne répond est listé à la fin. Ces couleurs restent inutilisées.
Le résultat est un fichier de thème ordinaire dans le dossier des thèmes, modifiable comme un autre.
Icônes de fichier
L'arborescence dessine une icône par ligne. Sans pack, c'est celle que
le bureau propose pour le type du fichier, qui distingue Python de
Markdown et ne va guère plus loin — et n'a jamais entendu parler d'un
fichier nommé Dockerfile.
Réglages → Général → Icônes de fichier accepte un pack d'icônes VS Code. Les packs déjà vus sont sur la liste, séparés entre ceux importés ici et ceux ouverts là où ils sont ; System icons est le retour en arrière.
Import… copie le pack dans le dossier propre à Textchum —
~/Library/Application Support/Textchum/icons/ sur macOS,
~/.local/share/textchum/icons/ sur Linux — déplacer ou supprimer
l'original n'emporte donc pas les icônes. Open… pointe vers un pack
là où il est et s'en souvient, ce qui convient à un pack que vous
maintenez vous-même. Les deux acceptent le fichier JSON du thème
d'icônes ou le dossier d'extension qui le contient (son package.json
dit lequel). Delete supprime un pack importé ; un pack ouvert
ailleurs appartient à qui l'a mis là, il ne peut donc que sortir de la
liste.
Le choix est un chemin dans config.json, et les packs ouverts
ailleurs sont retenus à côté :
{
"icon_pack": "~/packs/material-icon-theme/dist/material-icons.json",
"icon_packs": ["~/packs/material-icon-theme/dist/material-icons.json"]
}
Un pack dont le dossier a disparu quitte la liste au lieu d'y rester pour échouer une fois choisi. Un pack illisible est signalé une fois et l'arborescence garde les icônes du système.
La recherche suit celle de VS Code, du plus précis au moins précis :
- Le nom entier du fichier (
Dockerfile,cargo.toml), en minuscules. - L'extension la plus longue qui corresponde :
component.test.tsessaietest.tsavantts. - Le langage que Textchum a retenu pour le fichier — c'est aussi par là qu'un langage fixé à la main dans Propriétés du fichier atteint l'icône.
- La valeur par défaut du pack lui-même.
La section light d'un pack remplace n'importe laquelle de ces
réponses sur fond clair, une recherche à la fois : un pack qui n'en
redessine qu'une poignée garde le reste.
Deux choses restent dehors. Les icônes de dossier : l'arborescence
dessine les siennes. Les icônes tracées avec une police — les
définitions fontCharacter qu'utilisent Seti et ses descendants —
demandent la police installée et un texte là où va une image ; un pack
qui n'a que cela est refusé avec cette raison, plutôt que chargé pour
ne rien dessiner.
Projets
L'onglet Projects décide où un projet commence et finit — la frontière selon laquelle le navigateur regroupe et sur laquelle le pool de serveurs de langage indexe ses instances. Chaque interrupteur existe en deux exemplaires : comme valeur par défaut pour tous les projets, et par racine de projet. Une ligne ajoutée avec le champ de chemin (qui complète les noms de répertoire pendant la frappe et porte un bouton Browse…) remplace les valeurs par défaut pour cette racine seulement.
- Manifest projects — normalement le dépôt le plus externe
l'emporte : ouvrir un fichier n'importe où dans un dépôt fait du dépôt
le projet, quel que soit le nombre de
Cargo.tomloupyproject.tomlentre les deux. L'activer redécoupe une racine aux manifestes de langage, si bien que les modules imbriqués redeviennent des projets à part entière. - Recursive config — fait que les réglages par projet d'une racine (ses commandes de serveur de langage et ces interrupteurs eux-mêmes) s'appliquent aux projets imbriqués qu'elle contient, l'ancêtre le plus proche d'abord. Utile pour les monorepos : une configuration en haut, beaucoup de projets en dessous.
- Ctags fallback — répond à Aller à la Définition depuis un index Universal Ctags quand aucun serveur de langage n'est disponible ; voir serveurs de langage.
Dans le fichier, tout cela vit dans une section workspace :
{
"workspace": {
"manifest_projects": false,
"recursive_config": false,
"ctags_fallback": false,
"projects": {
"/Users/you/code/monorepo": {
"manifest_projects": true,
"recursive_config": true
}
}
}
}
Préprocesseurs de sauvegarde
Les formateurs et correcteurs peuvent s'exécuter automatiquement avant
chaque sauvegarde, par langage — pour tous les projets ou pour une
racine précise, exactement comme les serveurs de langage. Chaque entrée
est une chaîne : une commande par ligne, dans l'ordre, où chaque
commande lit le document sur l'entrée standard et réécrit le document
entier sur la sortie standard (la convention - que suivent presque
tous les formateurs). Si un maillon échoue — code de sortie non nul,
sortie vide, ou plus de dix secondes sans répondre — rien n'est
appliqué, l'erreur (avec le stderr de l'outil) s'affiche, et la
sauvegarde demande s'il faut continuer sans traitement.
{
"preprocessors": {
"defaults": {
"python": ["ruff check --fix -", "black -"],
"go": ["gofmt"]
},
"projects": {
"/work/site": { "javascript": ["prettier --stdin-filepath {filename}"] }
}
}
}
{path} et {filename} n'importe où dans une commande se développent
en le chemin absolu du document et son nom — pour les outils qui lisent
stdin mais déduisent leur comportement du nom, comme le
--stdin-filepath de Prettier. Un document sans titre offre Untitled
plus l'extension de son langage.
Une entrée de projet remplace la chaîne par défaut pour ce langage,
elle ne s'y ajoute jamais. La fenêtre Réglages édite cette même section
sous Serveurs de langage, et Édition ▸ Lancer les préprocesseurs
(⌃⌥⌘F, nom d'action runPreprocessors) lance la chaîne à la demande
sans sauvegarder — formater avec vos outils plutôt qu'avec le
formateur du serveur. Le résultat arrive en une seule édition, donc ⌘Z
l'annule.
Correction orthographique
La prose passe par le correcteur du système — les dictionnaires que partagent toutes les apps du Mac — restreint à là où la prose vit vraiment : les commentaires dans le code, et le document entier en Markdown, dans les messages de commit git et le texte brut. Les identifiants et les littéraux de chaîne ne sont jamais signalés. Les fautes portent une teinte violette, distincte du rouge/orange/bleu des diagnostics.
Choisissez la langue dans Réglages ▸ Général ▸ « Spell check prose » —
Désactivé (le défaut), automatique selon le contenu, ou un dictionnaire
précis — ou réglez editor.spell à la main : "auto" ou un
identifiant comme "fr" ou "en_US". Les dictionnaires disponibles
sont ceux activés dans Réglages Système ▸ Clavier ▸ Saisie de texte.
{ "editor": { "spell": "auto" } }
Plusieurs dictionnaires peuvent s'appliquer à la fois : nommez-les séparés par des virgules. Un mot que l'un d'eux connaît est bien orthographié, ce dont a besoin un texte qui change de langue au milieu d'un paragraphe :
{ "editor": { "spell": "en_US, fr_FR" } }
editor.spell_words est votre propre liste : noms de projet, sigles, et
tout ce qu'aucun dictionnaire ne fournit. Un clic droit sur un mot
signalé propose les suggestions, Ajouter au dictionnaire, qui écrit
le mot ici, et Ignorer, qui l'accepte jusqu'à la fermeture de
l'éditeur. La liste s'édite aussi dans les préférences.
{ "editor": { "spell_words": ["SBX", "Textchum"] } }
Sous Linux les mêmes réglages passent par hunspell : installez
hunspell plus un paquet de dictionnaire (hunspell-fr,
hunspell-en-us, …) et les marques apparaissent ; "auto" suit
$LANG, et les dictionnaires que hunspell trouve sont listés à côté du
champ dans les préférences.
Enregistrement automatique
Désactivé par défaut. editor.autosave est un nombre de secondes ; le
compte repart à chaque frappe, donc l'enregistrement a lieu une fois la
saisie terminée et non au milieu d'une phrase.
{ "editor": { "autosave": 30 } }
Deux choses qu'il ne fait délibérément pas. Il n'enregistre jamais un document sans nom : il n'y a nulle part où le mettre, et lui en inventer un n'appartient pas à l'éditeur. Et il n'exécute pas les préprocesseurs d'enregistrement : un formateur qui reflue la ligne que vous êtes en train d'écrire ne rend pas service, cela reste donc l'affaire des enregistrements explicites.
Raccourcis clavier
Réglages ▸ Clavier les tient : un profil, et chaque commande avec le raccourci auquel elle répond, modifiable sur place.
Profils. On arrive d'un autre éditeur avec ses raccourcis dans les
doigts : les trois pour lesquels ces éditeurs sont connus sont donc
fournis avec la compilation — Visual Studio Code, Sublime Text et
IntelliJ IDEA. Un profil nomme les commandes qu'il déplace et laisse le
reste tranquille : en choisir un change ce pour quoi cet éditeur est
connu, et rien d'autre. keys_profile garde le choix ; vide, ce sont
les raccourcis propres à Textchum.
Changer un raccourci par-dessus un profil garde le profil : le
changement est un remplacement, et Reset changes les abandonne
tous. Save as profile transforme ce qui est en vigueur en un profil
à vous — la façon de modifier un préréglage, puisque ceux-là sont
fournis avec la compilation. Les profils enregistrés vivent dans
key_profiles, et un profil qui reprend un nom fourni le remplace.
Le fichier écrit les remplacements dans une section keys : un objet
de noms d'action vers des spécifications modificateurs+touche,
appliqué par-dessus le profil.
{
"keys": {
"openQuickly": "cmd+p",
"goToBlockEnd": "ctrl+alt+down",
"findInProject": "cmd+shift+g"
}
}
Modificateurs : cmd, shift, alt, ctrl — cmd est Command sur
macOS et Ctrl sur Linux, un profil veut donc dire la même chose des
deux côtés. Touches : un caractère, de f1 à f20, ou
up/down/left/right/return/escape/space/tab/delete.
Parmi les actions : new, open, openQuickly, save, saveAs,
close, undo, redo, find, findAndReplace, findNext,
findPrevious, useSelectionForFind, findInProject,
jumpToDefinition, findReferences, codeActions, renameSymbol, formatDocument,
runPreprocessors,
documentOutline, goBack, goForward,
blameLine, goToLine,
goToBlockStart, goToBlockEnd,
toggleNavigator, togglePreview, toggleLineNumbers,
toggleHover, showHover, serverStatus, newWithFormat, revealInTree, reopenClosed,
togglePathDisplay, redraw, commandPalette, settings —
un nom inconnu est journalisé avec la liste complète. Aller au début/à
la fin du bloc (⌃⌥↑/⌃⌥↓ par défaut) saute par-dessus le bloc syntaxique
multiligne le plus interne autour du curseur, grâce à l'arbre qui
alimente déjà la coloration. Et quand un raccourci échappe tout à fait
à la mémoire, la palette de commandes (⇧⌘P) cherche floue n'importe
quelle action de menu par son nom et exécute la sélection.
Rechargement à chaud
Le fichier est surveillé pendant que Textchum tourne : éditez
config.json ailleurs et le changement s'applique dès qu'il atterrit —
apparence, thème, polices, raccourcis, table des serveurs, tout, y
compris la fenêtre Réglages si elle est ouverte. Les sauvegardes de
l'app elle-même sont reconnues et ignorées, et un fichier qui échoue
momentanément à parser retombe sur les défauts sans être écrasé,
exactement comme au lancement.
Réglages d'éditeur par projet
Une racine de projet peut remplacer la police, sa taille et la largeur
de tabulation pour toutes les fenêtres qu'elle contient — les lignes de
l'onglet Projets portent les trois champs (vide signifie « hériter de
la valeur générale »), et le fichier l'écrit comme un objet editor
sur l'entrée workspace :
{
"workspace": {
"projects": {
"/work/legacy": { "editor": { "tab_width": 8, "font_size": 12 } }
}
}
}
Ajouter un projet
L'onglet Projets liste les racines des documents ouverts : un projet s'ajoute donc en le choisissant, pas en tapant son chemin. Copier les réglages de part d'un projet déjà configuré — ses serveurs, ses commandes d'enregistrement, ses indicateurs et ses réglages d'éditeur, le tout — ce dont a besoin un second service de même disposition. Le même choix figure sur la ligne de chaque projet, pour copier vers un qui existe déjà.
Un champ de remplacement vide montre ce qu'il hérite : une case blanche dit donc ce qui s'applique au lieu de vous laisser aller voir.
Une racine configurée dont le répertoire a disparu est marquée missing, et Remove missing les oublie toutes : plus rien ne correspondra jamais à ces entrées.
Les langages que la compilation ignore
La coloration de Textchum vient de grammaires tree-sitter compilées
dans le binaire. Un langage qu'il ne porte pas se nomme dans
languages, avec la grammaire comme bibliothèque compilée et sa
requête de coloration comme fichier :
{
"languages": {
"dockerfile": {
"grammar": "~/.local/share/textchum/grammars/libtree-sitter-dockerfile.dylib",
"highlights": "~/.local/share/textchum/grammars/dockerfile/highlights.scm",
"extensions": ["dockerfile"],
"filenames": ["Dockerfile", "Containerfile"]
}
}
}
aliases, filenames et injections sont facultatifs, symbol
aussi : le constructeur est tree_sitter_<nom> sauf mention contraire,
tirets et points devenant des soulignés. Une grammaire faite pour un
autre tree-sitter est refusée d'après son numéro d'ABI plutôt que
suivie jusqu'au plantage, et un nom que la compilation connaît déjà est
remplacé par celui de la configuration — de quoi corriger une grammaire
dépassée sans attendre une version.
Pour en construire une, depuis le dépôt de la grammaire :
cc -O2 -fPIC -shared -I src -o libtree-sitter-NOM.dylib src/parser.c src/scanner.c
(.so sous Linux, et sans src/scanner.c quand la grammaire n'en a
pas.) Une entrée qui ne charge pas coûte ce langage et rien d'autre :
l'éditeur dit ce qui s'est passé et continue.
La langue de l'interface
L'interface parle anglais, espagnol ou français ; Interface language dans les Préférences en choisit une, et System suit celle de la machine. Le changement s'applique au démarrage suivant.
Les catalogues sont ceux de gettext et vivent dans le cœur : les deux
shells et le cœur disent donc les mêmes choses avec les mêmes mots. La
source est core/textchum-core/i18n/<langue>.po — le format que
parlent les traducteurs et leurs outils — et la compilation en tire le
.mo que lit l'éditeur. Une phrase sans traduction se lit telle
qu'elle est écrite en anglais plutôt que comme une clé manquante.
Un catalogue à soi se met dans le profil, et est lu à la place de celui de la compilation :
msgfmt -o ~/.config/textchum/translations/fr.mo mon-fr.po
~/Library/Application Support/Textchum/translations/<langue>.mo~/.config/textchum/translations/<langue>.mo
Un fichier nommé d'après une langue que la compilation ne porte pas se lit de la même façon : une quatrième langue est un catalogue, pas une version.
Pour travailler les traductions du dépôt, scripts/i18n.sh extrait les
chaînes et les fusionne dans chaque catalogue comme gettext l'entend :
xgettext les trouve, msgmerge reporte l'existant et marque comme
douteux ce dont l'anglais a changé, et msgfmt vérifie le résultat.
make check lance scripts/i18n.sh --check : une chaîne ajoutée sans
traduction casse la compilation au lieu de sortir en anglais.
Registres de projet
Un fichier se souvient de la façon dont il est divisé, de l'endroit que
chaque vue regardait, de ce qui est replié et de ce qu'on lui a dit
qu'il était quand son nom ne le dit pas. Ce sont des données du
fichier : elles vivent avec le projet plutôt que dans config.json, un
registre par racine de projet, en JSON comme le reste.
{
"version": 1,
"root": "/work/engine",
"files": {
"src/parser.rs": {
"views": 2,
"dividers": [0.45],
"folds": [[12, 48]],
"language": "rust",
"places": [{"caret": 812, "scroll": 240.0}]
}
}
}
Les registres sont gardés dans le profil, à côté de la session et des
thèmes : une exécution pointée sur un profil d'essai écrit les siens.
Keep each project's state with the checkout place le registre dans
<root>/.tchum, pour une disposition qui voyage avec le clone ; le
choix est global, car une réponse par projet devrait de toute façon
être notée quelque part de central pour être retrouvée.
Le balayage se fait au démarrage, sur un fil à lui : il oublie les registres des projets qui ne sont plus là et ceux qui n'ont pas été écrits depuis plus longtemps que la fenêtre (90 jours par défaut ; zéro les garde jusqu'à ce qu'on les efface). Forget records at launch l'arrête, et Manage… à côté du dossier des registres liste ce qui existe, ce dont chacun parle et quand il a été écrit, pour les oublier un par un ou d'un coup.
Pas encore là
- Rien pour le moment — notez la prochaine gêne quand elle se présentera.