Cette page décrit chaque partie de Kaivalya Editor telle qu’elle est. Pour le compiler une première fois, voir la Mise en route ; pour une tâche précise — ajouter un réglage, une commande, une mesure —, voir les Guides pratiques ; pour comprendre pourquoi il est construit ainsi, voir Comprendre l’architecture.
Code écrit par une IA. L’essentiel du code source a été écrit par un assistant IA (Claude), sous la direction de l’auteur humain, qui a relu, validé et décidé chaque modification. C’est précisé ici par transparence.
1. L’espace de travail
Un espace de travail Cargo de treize crates, en Rust édition 2024, sous licence
AGPL-3.0-or-later. Seule kve-ui importe le framework graphique ; chaque
crate marquée sans interface se teste sans fenêtre.
| Crate | Sans interface | Ce dont elle s’occupe |
|---|---|---|
kve-core |
oui | Le tampon de texte (une rope), les positions en graphèmes, les modifications et l’annulation ; la lecture des couleurs ; le lancement des formateurs externes |
kve-config |
oui | Les réglages et leur fichier auto-documenté, le registre des commandes et ses raccourcis, le catalogue de la palette /, les sessions, les emplacements récents, l’historique local, les deux catalogues de traduction |
kve-markdown |
oui | Le markdown : le HTML des aperçus et des exports, le plan, la table des matières, les tableaux, les listes, les liens et les rétroliens, la recherche des TODO, le todo.md du tableau des tâches, le formateur intégré |
kve-format |
oui | Les autres formateurs intégrés : JavaScript et TypeScript, JSON, CSS, HTML, XML, Vue, YAML, TOML, PHP |
kve-fs |
oui | L’arborescence du projet, les règles d’exclusion, la surveillance des fichiers, la création, le renommage et la suppression de fichiers |
kve-search |
oui | L’index des fichiers et sa recherche approximative ; la recherche et le remplacement dans tout un projet |
kve-git |
oui | Git via gitoxide : état, différences, indexation, commit, historique, blame ; git lui-même pour la récupération, l’envoi, le rebasage et le changement de branche |
kve-canvas |
oui | Le JSON Canvas d’Obsidian : lu, modifié et réécrit champ par champ ; la géométrie du tableau ; son export SVG |
kve-ui |
non | La fenêtre : chaque vue, panneau et boîte de dialogue, avec gpui et gpui-component |
kve-preview |
non | L’aperçu complet : un second programme, une fenêtre qui dessine des pages HTML entières avec Blitz |
kaivalya-editor |
non | L’exécutable : les arguments, puis la fenêtre |
kve-bench |
oui | Le banc de mesure et les budgets qu’il fait respecter |
kve-drive |
— | Un outil de développement : un pointeur et un clavier pour une fenêtre lancée sous cage |
Comment les crates dépendent les unes des autres
Les flèches vont d’une crate vers ce qu’elle utilise. Tout ce qui se trouve
sous kve-ui est sans interface ; kve-ui est la seule à savoir qu’une
fenêtre existe, et seul l’exécutable en dépend. kve-bench utilise directement
les crates sans interface, pour les mesurer sans fenêtre.
Les fichiers principaux
| Crate | Où regarder d’abord |
|---|---|
kve-ui |
window.rs — la fenêtre de l’éditeur : onglets, panneaux, menus, le traitement de chaque commande ; canvasview.rs le tableau blanc, boardview.rs le tableau des tâches, logview.rs, gitviews.rs (historique, graphe, blame), diffview.rs, conflictview.rs, grid.rs la fenêtre de tableau, formula.rs, heavy.rs le côté émetteur de l’aperçu complet, panewindow.rs un panneau dans sa propre fenêtre, theme.rs les jetons de couleur, syntax.rs la coloration, menu.rs les menus |
kve-config |
settings.rs les réglages et leur fichier commenté, command.rs le registre des commandes, palette.rs le catalogue de la palette /, session.rs, places.rs, history.rs l’historique local, characters.rs, rules.rs le fichier du projet ; locales/ les deux catalogues |
kve-markdown |
html.rs le HTML des aperçus et des exports, outline.rs, toc.rs, table.rs, list.rs, board.rs le todo.md du tableau des tâches, mentions.rs les rétroliens, wikilink.rs, transclude.rs les inclusions, format.rs le formateur, spelling.rs, maths.rs, mermaid.rs, stylesheet.rs |
kve-core |
document.rs le tampon, edit.rs, position.rs les graphèmes, colour.rs, external.rs le lancement d’un formateur |
kve-fs |
tree.rs l’arborescence du projet, rules.rs les règles d’exclusion, watch.rs la surveillance, make.rs créer, renommer, mettre à la corbeille |
kve-search |
index.rs et matcher.rs le sélecteur de fichiers, content.rs la recherche et le remplacement dans le projet, glob.rs |
kve-canvas |
lib.rs le modèle, lu et écrit, geometry.rs le repérage et les courbes, edit.rs les modifications, svg.rs l’export |
kve-git |
lib.rs : état, différences, commit, historique, graphe, blame, et les appels à git |
kve-preview |
main.rs la fenêtre du second programme, lib.rs ce qu’on lui envoie |
spike/ contient des mesures faites avant une décision, gardées hors de
l’espace de travail pour que chacune puisse fixer ses propres versions.
crates/kve-ui/src/window.rs est la fenêtre de l’éditeur — de loin le plus gros
fichier —, et chaque autre vue a son propre fichier : canvasview.rs (le
tableau blanc), boardview.rs (le tableau des tâches), logview.rs,
gitviews.rs, grid.rs (la fenêtre de tableau), formula.rs, heavy.rs (le
côté émetteur de l’aperçu complet), panewindow.rs (un panneau dans sa propre
fenêtre).
2. Chaîne de compilation et dépendances
- Rust 1.98.0, fixé par
rust-toolchain.toml, avecrustfmtetclippy. Le changer est un acte délibéré : relancer les mesures et les consigner. - Toutes les dépendances viennent de crates.io, framework graphique compris :
gpuiest le paquetgpui-pre,gpui-componentet sa famille sont en 0.6. Il n’y a aucune dépendance git. Cargo.lockest versionné, et c’est lui qui fixe les versions. Chaque compilation et chaque tâche de CI passe--locked. On ne lance jamais uncargo updatesans argument : il résoudrait tout vers les versions les plus récentes et changerait le framework sous une mesure. Le retrait d’une crate de l’espace de travail s’enregistre aveccargo update --workspace --offline, qui ne touche qu’aux entrées de l’espace de travail lui-même..cargo/config.tomlutilise l’éditeur de liensmoldviaclangquand ils sont installés.
3. Compiler
make |
Ce que fait la cible |
|---|---|
build |
L’éditeur avec l’aperçu complet, et kve-preview, en mode release |
build-light |
L’éditeur seul, sans l’aperçu complet — quelques secondes au lieu de plusieurs minutes |
check |
Les cinq vérifications de la CI, dans son ordre : fmt, clippy, doc, test, bench |
fmt |
cargo fmt --all --check |
clippy |
cargo clippy --workspace --all-targets --locked -- -D warnings |
doc |
cargo doc sur toutes les crates, éléments privés compris, avertissements traités comme des erreurs |
test |
cargo test --workspace --locked |
bench |
kve-bench en mode release : renvoie 1 si un budget est dépassé |
site |
Les pages de référence générées, puis le site web avec Hugo |
walkthrough |
Les vérifications qui demandent une vraie fenêtre, pilotées sous cage, une capture chacune |
deb |
Un paquet Ubuntu/Debian de la version compilée, target/deb/kaivalya-editor_<version>_<arch>.deb — tools/deb.sh : make install dans un dossier intermédiaire sous /usr, exécutables allégés de leurs symboles, dépendances calculées par dpkg-shlibdeps plus Vulkan et Wayland (ouverts à l’exécution, non liés). Il s’installe sur la version d’Ubuntu où il a été construit et les suivantes |
sync |
Recopie project.toml dans les fichiers qui ne savent pas le lire : le repository de Cargo.toml, le développeur, la page d’accueil et le dépôt du fichier metainfo. Un test de kve-config échoue tant qu’il n’a pas été lancé |
install / uninstall |
Les deux programmes, l’entrée de bureau, les métadonnées AppStream et les icônes, sous PREFIX (~/.local par défaut) |
La fonctionnalité Cargo heavy-preview de kaivalya-editor décide si une
version compilée contient le moteur de l’aperçu complet ; le réglage
heavy_preview décide si une version qui le contient le propose.
4. Les fichiers que l’éditeur lit et écrit
| Emplacement | Contenu |
|---|---|
~/.config/kaivalya-editor/settings.toml |
Vos réglages, écrits avec chaque option commentée s’il n’en existe pas |
~/.config/kaivalya-editor/themes/ |
Vos thèmes, au format JSON de gpui-component |
~/.config/kaivalya-editor/styles/ |
Vos feuilles de style pour les aperçus et les exports |
~/.config/kaivalya-editor/templates/ |
Vos modèles de notes |
~/.local/share/kaivalya-editor/sessions/<empreinte>.toml |
Ce qui était ouvert dans un projet, identifié par une empreinte FNV-1a de son chemin |
~/.local/share/kaivalya-editor/places.toml |
Les fichiers et dossiers récents et favoris |
~/.local/share/kaivalya-editor/history/ |
L’historique local : une version d’un fichier à chaque enregistrement |
<projet>/.kaivalya/settings.toml |
Les réglages propres à un projet : seulement ce qui diffère des vôtres |
<projet>/.kaivalya/project.toml |
Les règles de dossier d’un projet, comme la feuille de style des notes d’un dossier |
<projet>/.kaivalya/templates/ |
Les modèles propres à un projet |
<projet>/todo.md |
Le tableau des tâches et Mes tâches |
Les variables XDG_CONFIG_HOME et XDG_DATA_HOME déplacent les deux premiers
dossiers ; la visite guidée automatisée et le script de captures d’écran s’en
servent pour travailler sur une configuration jetable. Rien n’est écrit dans un
projet sans que vous le demandiez : les sessions et l’historique sont rangés
dans votre dossier de données.
5. Les réglages
- Les valeurs par défaut sont un fichier,
resources/settings/defaults.toml, intégré à l’exécutable. Aucune valeur par défaut n’est écrite en Rust : un champ deSettingssans valeur dans ce fichier fait échouer les tests. Settings::parseest la seule porte d’entrée. Un fichier partiel est fusionné par-dessus la table des valeurs par défaut : un fichier de réglages n’a besoin de mentionner que ce qu’il change. Une clé que l’éditeur ne connaît plus est ignorée.- Le fichier se documente lui-même. Chaque option y est écrite avec son explication, tirée du catalogue de la langue du lecteur. Un test refuse un réglage qui n’est pas expliqué dans chaque catalogue.
- Les tests ne lisent jamais
defaults.toml. Ils utilisentresources/settings/for-tests.tomlviaSettings::for_tests()(la fonctionnalitétest-support), dont les valeurs ne bougent pas ; un test vérifie que les deux fichiers ont les mêmes clés. Un test peut vérifier une propriété des vraies valeurs par défaut, jamais une valeur. - Le
.kaivalya/settings.tomld’un projet est fusionné par-dessus les vôtres de la même façon.
6. Commandes, menus et raccourcis
- Un seul registre,
crates/kve-config/src/command.rs: chaque commande a unid(file.save), une portée, une clé de catalogue pour son libellé (command.file.save), un raccourci par défaut et éventuellement un groupe (un sous-menu). crates/kve-ui/src/menu.rsassocie chaque identifiant à son actiongpui, etbindings()danswindow.rsinstalle les raccourcis. Les menus, la page de référence des raccourcis et les raccourcis eux-mêmes lisent la même table : un raccourci ne peut pas être affiché à un endroit et associé à un autre.- Les raccourcis d’édition du texte (
Ctrl+Z,Ctrl+C…) appartiennent au composant d’édition, dans son contexteInput; une vue qui n’est pas du texte — le tableau blanc, la vue journal — gère elle-même ses touches.
7. Les traductions
rust-i18n, deux catalogues :crates/kve-config/locales/en-US.ymletfr-FR.yml. Tout texte visible par l’utilisateur passe par eux —t!(), oukve_config::tr()et les fonctions voisines pour les textes avec paramètres.- Un test échoue si les deux catalogues n’ont pas exactement les mêmes clés, ou si l’un d’eux déclare une clé deux fois.
- La langue est donnée par le réglage
locale, ou à défaut par le$LANGdu bureau.
8. L’aperçu complet
kve-preview est un programme séparé, que l’éditeur lance à la demande et
cherche à côté de lui. L’éditeur lui envoie des pages HTML entières via un
tube — le document tel que l’écrit kve-markdown, avec la feuille de style et
les formules déjà dessinées — ; l’aperçu ne lit jamais de fichier et ne demande
jamais rien à l’éditeur. Son moteur de rendu est Blitz (Stylo, Taffy, Vello),
fixé à des versions exactes. Il n’a pas accès au réseau.
9. Le tableau blanc
Un fichier .canvas est lu par kve_canvas::parse dans un Canvas — des
nœuds, des liens, et chaque champ que le modèle ne connaît pas, conservé pour
être réécrit. Le tableau (canvasview.rs) dessine directement ce modèle : les
cartes sous forme d’éléments, les liens et la grille peints en dessous. Chaque
modification est écrite immédiatement, dans la mise en forme d’Obsidian — un
nœud ou un lien par ligne —, et seulement si le texte change. Une écriture qui
échoue marque l’onglet comme non enregistré. Le tableau relit son fichier quand
celui-ci change sur le disque.
10. Git
kve-git fait ce que feraient status, diff, add et commit, directement
sur le dépôt, avec gitoxide — rien ne dépend donc de la configuration du git
installé. Là où gitoxide n’a pas de réponse, il lance git : récupération,
envoi, rebasage, changement et création de branche.
11. Les tests
- Des tests unitaires dans chaque crate, à côté du code.
- Des tests de fenêtre dans
kve-ui, grâce autest-supportdegpui: une vraie fenêtre, sans compositeur.#[gpui::test],a_window_with(…)pour en ouvrir une,VisualTestContextpour la dessiner, etdebug_bounds("sélecteur")pour savoir où un élément a été dessiné (les sélecteurs n’existent qu’en compilation de débogage). Les boîtes de dialogue de fichiers se répondent avecsimulate_path_prompt_responseetsimulate_new_path_selection. Une reconstruction différée demande d’avancer l’horloge :cx.executor().advance_clock(…), puisrun_until_parked(). - Une vraie fenêtre :
tools/drive.sh start | run | shot | stoplance l’éditeur souscage, sans affichage, et le pilote aveckve-drive— déplacements, clics, glissements, raccourcis, saisie —, tandis quegrimen fait des captures.make walkthroughenchaîne toutes les vérifications qui demandent une fenêtre. - La définition de « terminé » exige des tests des cas qui échouent, pas seulement de ceux qui fonctionnent.
12. Les performances
kve-benchmesure chaque opération pour laquelle un budget est fixé — ouvrir un fichier, le travail d’une frappe, une recherche, un formatage, l’écriture d’un tableau blanc — et échoue quand un 99e centile dépasse son budget. Les budgets viennent du §4 de la feuille de route : 16 ms pour tout ce qui se fait dans une image, 4 ms pour le traitement d’une saisie, davantage pour ce qui n’est demandé qu’une fois.KVE_TIMINGS=1fait écrire à l’éditeur une ligne par étape sur la sortie d’erreur : chaque étape de l’ouverture d’un projet, ainsi que la lecture, l’écriture et chaque image du tableau blanc.- Les chiffres sont conservés dans
docs/perf/, une page par phase et une pour les séries de retours, avec ce qui a été mesuré, sur quoi, et ce qui a changé.
13. L’intégration continue
.github/workflows/ci.yml installe les bibliothèques système, puis lance le
formatage, clippy, la documentation et les tests ; les tests propres à l’aperçu
complet et une vérification de kve-preview avec son moteur ; puis les mesures
de performance — les commandes de make check. Les mesures qui demandent une
vraie fenêtre sont lancées à la main sur la machine de référence, pas en CI.
14. Le site web
Hugo, dans website/, en anglais et en français côte à côte (page.en.md,
page.fr.md). La documentation utilisateur, dans content/docs/, compte une
page par partie Diátaxis, comme ces pages pour les développeurs : Premiers
pas (tutoriel), Guides pratiques, le Guide d’utilisation (référence —
chaque fonction, une section chacune) et Comprendre l’éditeur (explication) ;
le diataxis: de chaque page dit laquelle, et leurs poids les ordonnent.
Trois autres pages de référence sont générées à partir des tables de
l’éditeur lui-même par make site — tous les réglages, tous les raccourcis, le
catalogue de la palette / — : elles ne peuvent pas diverger du code ;
marquées annex: true, elles ne sont pas listées comme des pages à part :
le chapitre Annexes du Guide d’utilisation y mène. chapter_depth: 2 donne
à la carte du Guide d’utilisation les sections de chaque chapitre, sur sa
ligne. Les captures d’écran viennent de tools/doc-shots.py, qui
pilote l’éditeur sur un projet de démonstration, dans chaque langue. Une page
marquée draft: true décrit la prochaine version et est publiée avec elle.
15. Publier une version
tools/release.sh check | prepare | finish <version> : check ne modifie rien ;
prepare fait toutes les modifications d’une version et laisse les notes de
version sous forme de squelette à rédiger ; finish lance les vérifications,
commite, pose l’étiquette, construit l’archive, le .deb et le site. Il ne pousse, ne
téléverse et ne déploie jamais rien — ces étapes sont affichées pour qu’une
personne les fasse.
project.toml, à la racine, dit une seule fois ce que plusieurs fichiers
diraient sinon chacun de leur côté : le nom, l’identifiant de l’application,
le site, le dépôt, le site de l’auteur, le nom du développeur et le
responsable du paquet. Le site le monte comme site.Data.project — le pied de
page le lit, et une page écrit {{< project repository >}} ou
{{< project raw "LICENSE.md" >}} plutôt qu’une adresse. release.sh et
deb.sh le lisent par tools/project.sh <clé>. Cargo.toml et le fichier
metainfo ne savent rien lire : make sync le leur recopie (prepare le
lance), et un test échoue quand une copie diverge ou qu’une page réécrit
l’adresse du dépôt.
16. Les fonctions, une par une
Chaque fonction ci-dessous est décrite de la même façon : le chemin, de
la touche ou du menu jusqu’à ce qui change, les fichiers et les fonctions
en jeu, comment elle marche, et ses tests et ses mesures. Les
fonctions sont nommées comme dans le code ; window.rs désigne
crates/kve-ui/src/window.rs, où la fenêtre de l’éditeur — EditorWindow —
traite chaque commande.
16.0 Le chemin de toute commande
Une commande est déclarée une seule fois, dans kve_config::command::COMMANDS :
son identifiant, sa portée (Fichier, Projet, Éditeur), la clé de
traduction de son libellé, sa touche par défaut et son sous-menu. Les menus
(menu.rs) et le clavier (window.rs, bindings) sont tous deux construits
depuis cette table : une commande ne peut pas avoir de touche sans entrée de
menu, ni de libellé sans traduction. Tous deux émettent une action gpui,
qu’EditorWindow reçoit dans un gestionnaire enregistré par on_action dans
son render.
La règle que suivent les fonctions : le calcul — ce que devient le
document — est une fonction pure dans un crate sans fenêtre
(kve-markdown, kve-config, kve-core), testée sans compositeur ; le
gestionnaire lit le texte et la sélection, l’appelle, et réécrit le résultat
par replace_text, qui fait du changement une seule étape d’annulation. Ce
qui grandit avec le document tourne sur l’exécuteur d’arrière-plan
(cx.background_spawn) et est abandonné si le texte a changé entre-temps.
16.1 Gras, italique, barré
- Chemin :
Bold,Italic,Strikethrough→bold/italic/strikethrough→emphasise(Emphasis, …)→kve_markdown::toggle_emphasis→replace_text, puisset_selected_rangesur les mots. - Fichiers :
crates/kve-markdown/src/emphasis.rs,window.rs. - Comment :
toggle_emphasis(text, selection, what)rend le nouveau texte et la plage à sélectionner — toujours les mots, jamais les marques, ce qui permet à un second appui de défaire le premier. Trois cas : rien de sélectionné (marques écrites, curseur entre elles) ; marques dans la sélection (**mot**sélectionné en entier — retirées) ; marques juste autour (retirées aussi, sinon un second appui donnerait****mot****). Que*soit le début de**est pris en compte : l’italique dans un mot en gras s’ajoute au lieu de retirer. Une plage hors du texte ou qui coupe un caractère rendNoneplutôt que de paniquer. - Tests :
emphasis::tests— huit, dontpressing_it_twice_puts_the_document_backetitalic_inside_a_bold_word_adds_rather_than_removes.
16.2 Des listes qui se poursuivent seules
- Chemin :
Entréedans le contexteInputde l’éditeur →enter_in_list(un gestionnaire de touche sur l’élément de l’éditeur, avant son propre retour à la ligne) →kve_markdown::enter_in_list(text, caret)→replace_textet le curseur déplacé. - Fichiers :
crates/kve-markdown/src/list.rs,window.rs. - Comment : des lignes, pas une analyse — savoir dans quel élément est le
curseur ne demande que sa ligne et celles du dessus.
enter_in_listrendNonehors d’un élément (l’éditeur coupe alors la ligne comme d’habitude), l’élément suivant pour un élément avec du texte (même marque, numéro suivant avec son délimiteur,[ ]dans une liste de tâches, la fin de ligne reportée), l’indentation et la marque du parent pour un élément vide imbriqué, et une ligne vide pour un élément vide au premier niveau. Les délimiteurs de code au-dessus du curseur sont comptés, pour qu’une ligne dans un bloc de code ne soit jamais prise pour un élément.Maj+Entréen’est pas intercepté. - Tests :
list::tests(huit) ; dans la fenêtre,enter_goes_on_with_a_list_then_up_and_out,shift_enter_in_a_list_only_breaks_the_line. - Mesuré :
Entréeà la fin d’un document de 480 Ko, 1,2 ms p50, 1,5 ms p99, budget 4 ms (kve-bench).
16.3 Formater un fichier
- Chemin :
Format→format→formatter_for(index)choisit → markdown :reformat→kve_markdown::formatsur le fil principal ; tout le reste :format_off_thread→kve_format::format(intégré) oukve_core::external(un programme) sur l’exécuteur d’arrière-plan → le résultat réécrit en une seule modification, le curseur gardé. - Fichiers :
crates/kve-markdown/src/format.rs(markdown, le greffondprintplus la garde des indices),crates/kve-format/src/lib.rs(niveau 1 : swc pardprint-plugin-typescript,dprint-plugin-json,malva,markup_fmt,pretty_yaml,taplo,mago-formatter),crates/kve-core/src/external.rs(niveaux 2 et 3 :rustfmt,gofmt,shfmt,ruff,dprint).
- Comment : chaque formateur répond
Ok(None)quand le texte est déjà formaté, si bien qu’un formatage qui ne change rien ne fait ni modification, ni étape d’annulation, ni écriture. Un texte qui ne s’analyse pas est une erreur, signalée et non écrite.tab_size,insert_spacesetline_widthatteignent chaque formateur parFormatOptions/kve_format::Layout. En markdown, un~indice~est caché àdprintpuis remis ; s’il ne peut pas l’être, le formatage est abandonné (ProtectionLost, F101).format_on_savepasse parformats_on_save, le même choix par langage. - Tests :
formatting_lines_up_a_ragged_table,formatting_keeps_the_cursor_where_it_was,format_on_save_writes_the_formatted_text_once,formatting_rust_runs_rustfmt,json_is_formatted_by_the_formatter_built_in,a_language_with_no_formatter_says_so; ceux dekve-format, par langage. - Mesuré : 100 Ko de TypeScript, intégré, 42,7 ms p50 ; de CSS,
26,4 ms p50 (budget 120 ms, hors du fil principal) ;
rustfmtsur 600 lignes par les tubes, 40 ms.
16.4 Vérifier l’orthographe
- Chemin :
CheckSpelling→check_spelling(basculeOpenDocument::spelling_on) →schedule_spelling(delay, say)→ sur l’exécuteur d’arrière-plan :dictionary_for→prose_words→unknown_words(lanceenchant-2) →misspelt→ de retour sur le fil principal :underline_misspelt→ les décorations de l’éditeur. - Fichiers :
crates/kve-markdown/src/spelling.rs,window.rs.
- Comment :
prose_wordsparcourt les événements de pulldown-cmark et garde les mots desTexthors des blocs de code, des blocs HTML et des métadonnées, puis écarte ceux d’un wikilien ou de l’en-tête (une liste triée et une recherche dichotomique), un#mot-clé, et les mots contenant un chiffre ou un_.unknown_wordsdonne àenchant-2 -l -d <dict>chaque mot distinct une seule fois, écrit depuis un autre fil pour qu’aucun tube ne se remplisse. Un programme absent estSpellError::NoChecker, un dictionnaire absentNoDictionary; l’un comme l’autre arrête la vérification et le dit. Après chaque pause,schedule_rebuild(le même délai de 120 ms que l’aperçu) appelleschedule_spellingen silence ; un résultat pour un texte qui a changé depuis est abandonné. Les soulignements sont une collection deTextDecorationsur l’éditeur, ondulés, dans la couleurdangerdu thème. - Tests :
spelling::tests—only_the_prose_is_offered,the_dictionary_is_the_documents_language,enchant_is_asked_when_there_is_one; dans la fenêtre,check_spelling_underlines_and_clears,check_spelling_without_the_dictionary_says_so. - Mesuré : les mots d’un texte de 480 Ko, 11,7 ms p50 (budget 50 ms) ;
enchant-2sur ses 4 860 mots distincts, 30 ms.KVE_TIMINGS=1affiche une lignespellingpar vérification.
16.5 La palette /
- Chemin : chaque frappe dans un document markdown →
kve_config::palette::typed(text, caret)(le mot sous le curseur est-il un/…?) →matching(query)→ le menu dessiné au curseur →Entrée,Tabou un clic →choose→replace_text. - Fichiers :
crates/kve-config/src/palette.rs(le catalogue, la recherche, ce qu’écrit une ligne),crates/kve-config/src/characters.rs(/symbol,/emoji,/entity), les modèles deresources/templates/et du projet ; le menu danswindow.rs(le champslash,slash_act,palette_key). - Comment : la frappe ne valide jamais —
typedrépondNonedès que le mot ne commence plus par/, si bien qu’une espace, un chemin ou une date ne l’ouvrent jamais ; une recherche vide est un menu fermé ; seulchooseécrit. Deux niveaux pour parcourir (sections, puis lignes), une seule liste pour chercher. Le corps d’une ligne porte une marque de curseur, là où le curseur arrive. Voir La palette / pour le catalogue. - Tests :
palette::tests, donta_path_and_a_date_never_open_it,every_body_marks_one_caret; dans la fenêtre,a_query_nothing_matches_closes_it_and_keeps_every_character. - Mesuré : le déclencheur sur un document de 1 Mo, moins de 0,001 ms ;
une recherche avec caractères (
/arr), 0,22 ms p50 (budget 4 ms).
16.6 Tableaux
- Chemin : Insérer un tableau →
insert_table→new_table_in→kve_markdown::insert_table(text, at, 3, 2), le curseur dans la première cellule d’en-tête (la ligne/tablede la palette appelle la même) ; Ajouter une ligne / une colonne, Aligner →table_row,table_column,align_table→kve_markdown::table_attrouve le tableau sous le curseur, qui est modifié et réécrit aligné ; Modifier en grille →table_grid→grid.rsouvre une fenêtreWindowKind::Dialog→ Appliquer réécrit sur la plage mémorisée. - Fichiers :
crates/kve-markdown/src/table.rs,crates/kve-ui/src/grid.rs,window.rs. - Comment : le tableau analysé est la vérité et les champs de la grille
une vue de lui : chaque changement qui n’est pas une frappe (une ligne, une
colonne, un alignement, un tri) va au tableau et les champs sont
reconstruits ; la frappe va dans l’autre sens. La grille est modale, donc la
plage d’octets mémorisée à l’ouverture est encore celle du tableau à
Appliquer — vérifiée quand même. Un
|échappé reste dans sa cellule ; un paragraphe avec des|n’est pas un tableau. - Tests :
table::tests(dontan_escaped_pipe_stays_inside_its_cell) ;a_table_is_inserted_grown_and_lined_up,a_table_command_with_no_table_under_the_cursor_says_so.
16.7 La fenêtre des formules
- Chemin :
Formula→formula→kve_markdown::maths_at(text, caret)trouve le$…$/$$…$$autour du curseur (ou en ouvre un vide au curseur) →formula.rsouvre une fenêtre de dialogue avec leMathEditorderatex-gpui→ Appliquer → le TeX réécrit exactement sur cette plage, sous la forme où il a été trouvé. - Fichiers :
crates/kve-ui/src/formula.rs,crates/kve-ui/src/maths.rs,crates/kve-markdown/src/maths.rs. - Comment : le document ne contient jamais que du TeX ; la fenêtre tient un arbre tant qu’elle est ouverte. Appliquer sur une formule non touchée n’écrit rien, et une formule dont l’aller-retour par l’éditeur structurel n’est pas fidèle le dit avant toute frappe. La même touche, fenêtre ouverte, la ferme. Le même moteur RaTeX dessine les maths dans l’aperçu complet : ce qui est construit est ce qui sera montré.
16.8 Couleurs
- Chemin :
ToggleColourPane→toggle_colour_pane→ en fenêtre (colour_as = "window",panewindow.rs) ou en panneau → Insérer →insert_colour→write_at_the_cursor(hex_of(colour)), oucolour_boardquand un tableau blanc est au premier plan →remember_colour. À part, à chaque pause,schedule_swatches→kve_core::colours(text)→ une pastille de fond sur chaque#. - Fichiers :
crates/kve-core/src/colour.rs(trouver les couleurs),window.rs(colour_sliders,colour_slider_row,insert_colour,paint_the_colours),crates/kve-ui/src/panewindow.rs. - Comment : une couleur est
#rgb,#rgba,#rrggbbou#rrggbbaa, trouvée par une recherche d’octet sur#; seul ce qui suit est lu ; est refusé (un#après&est une référence de caractère). Seul le#est peint, en fond : un premier plan se battrait avec la coloration syntaxique, et le vainqueur dépend de l’ordre d’un hachage en amont (F137). Les pastilles sont dessinées en CSS, JSON, markdown, XML (donc HTML et SVG) et Vue. La fenêtre écrit là où était le curseur dans la fenêtre d’éditeur à laquelle elle appartient ; l’opacité n’est écrite que si elle n’est pas pleine. - Tests :
the_colour_pane_writes_its_colour_at_the_cursor,the_colour_key_opens_and_shuts_its_window,a_stylesheet_opens_with_a_chip_on_every_colour,the_colour_window_colours_the_selection_on_a_board;colour::tests. - Mesuré : trouver les couleurs de 100 Ko, 0,023 ms p50 (budget 4 ms).
16.9 Caractères et émojis
- Chemin :
ToggleCharactersPane→toggle_characters_pane→open_characters(une fenêtre,characters_as) → un clic → le caractère, ou son entité, écrit au curseur →kve_config::characters::remember. - Fichiers :
crates/kve-config/src/characters.rs(le catalogue etsearch),crates/kve-ui/src/emoji.rs(la police d’émojis en couleur fournie),window.rs. - Comment : un catalogue, deux portes — la fenêtre et les
/symbol,/emoji,/entityde la palette appellent touscharacters::search(query, kind, limit). Symboles et entités forment une table nommée en anglais et en français ; les émojis viennent du crateemojisavec leurs noms CLDR, en anglais seulement. La liste des derniers utilisés est gardée dans la session. Les émojis sont dessinés en couleur par la policeNotoColorEmoji.ttffournie, ajoutée aux polices de secours de chaque fenêtre. - Tests :
characters::tests,characters_from_the_palette_and_the_pane,the_characters_as_a_window. - Mesuré : une recherche de la palette avec caractères, 0,22 ms p50.
16.10 Coller depuis l’historique
- Chemin :
Ctrl+C/Ctrl+Xen route vers l’éditeur →copying→remember_clip;PasteFromHistory→paste_from_history→ la liste au curseur (clips) →clips_key→take_clip→ collé, et remis en tête. - Fichiers :
window.rsseulement. - Comment : un
Vec<String>sur la fenêtre, le plus récent en premier, sans doublon, coupé àclipboard_history; le texte actuel du presse-papiers système est mis en tête quand il vient d’un autre programme. Jamais écrit sur le disque. - Tests :
paste_from_history_by_the_keys,clipboard_history_zero_keeps_nothing.
16.11 Historique local
- Chemin : chaque enregistrement →
kve_config::history::record(etrecord_diskavant le premier enregistrement, et quand un autre programme a modifié le fichier) ; ouverture d’un projet →prune_all;LocalHistory→show_local_history→ un ongletlocalhistory.rs→ une version choisie → la vue des différences → Restaurer →restore_selected, par la fenêtre. - Fichiers :
crates/kve-config/src/history.rs,crates/kve-ui/src/localhistory.rs,crates/kve-ui/src/diffview.rs. - Comment :
~/.local/share/kaivalya-editor/history/<hash>/, un dossier par fichier contenant le chemin du fichier et un<millisecondes>.snappar version — un dossier lisible aveclsetcat. Une version égale à la plus récente n’est pas gardée. Bornée parKeep { count, days }, appliqué quand un fichier est enregistré et à tous les fichiers à l’ouverture d’un projet. La restauration passe par la fenêtre, qui la refuse s’il y a des modifications non enregistrées. - Tests :
history::tests(dontthe_newest_by_count_and_by_age_are_kept,two_paths_that_share_a_hash_do_not_share_a_folder) ;the_local_history_keeps_every_save_and_puts_one_back. - Mesuré : garder une version de 100 Ko à l’enregistrement, 4,3 ms p50 (budget 16 ms).
16.12 Coller une capture d’écran
- Chemin :
PasteImage→paste_image→clipboard_image→write_image(danspaste_image_folderà côté du document, sous un nom libre) → un lienau curseur. Des fichiers déposés →drop_files→ les images, en markdown, sont liées parlink_dropped(copiées à côté du document, sauf si elles sont déjà dessous) ; le reste est ouvert. - Fichiers :
window.rs. - Comment : trois refus, chacun avec un message : pas d’image dans le presse-papiers, un document jamais enregistré (pas d’« à côté »), un dossier où l’on ne peut pas écrire. L’extension suit le format de l’image du presse-papiers.
- Tests :
pasting_with_nothing_on_the_clipboard_says_so,dropping_pictures_links_them_and_opens_the_rest.
16.13 Liens vers d’autres fichiers
- Chemin : une frappe dans
[[ou](→kve_markdown::complete_path(base, typed, kind)→ la liste au curseur (paths) →Entrée/Tab→ un dossier y entre, un fichier termine le lien. - Fichiers :
crates/kve-markdown/src/wikilink.rs(complete_path,wikilinks),window.rs. - Comment : ce qui est tapé jusqu’au dernier
/désigne un dossier, parcouru depuis celui du document ; le reste est le début d’un nom dedans. Unread_dirpar frappe, aucun index du projet. Les dossiers d’abord ; un fichier caché seulement après un point ; huit lignes à la fois. - Tests :
a_path_is_written_from_the_folder_the_document_is_in,escape_shuts_the_completion_and_keeps_the_characters,a_rust_file_is_not_offered_completions. - Mesuré : 0,69 ms p99 pour un dossier de mille entrées.
16.14 Ouvrir un projet
- Chemin : Ouvrir un dossier →
open_folder→open_project(root)→ l’ancien projet rangé (session écrite, autocommit vidé, recherche, état TODO et git abandonnés, l’index des fichiers abandonné) →kve_fs::Tree::open(root, rules)→watch(root, rules)→ la session relue →apply_project_settings→ les panneaux TODO et git relus s’ils sont à l’écran. - Fichiers :
window.rs(open_project,watch,take_changes,mend_index),crates/kve-fs/src/tree.rs,crates/kve-fs/src/rules.rs,crates/kve-fs/src/watch.rs.
- Comment : l’arbre lit un dossier la première fois qu’on l’ouvre et
jamais avant : un projet de 100 000 fichiers coûte son dossier racine. Les
Rules(show_hidden,use_gitignore,.git/toujours exclu) décident de ce qui est listé ; un changement de règles rejuge les noms déjà lus — sans disque —, relance l’observateur et abandonne l’index. L’observateur estnotifyderrière unPipelinequi regroupe les événements ; la fenêtre recueille ce qui est stabilisé avec une minuterie plutôt qu’une attente bloquante, et applique chaque changement à l’arbre, à l’index des fichiers (FileIndex::apply) et à un document ouvert, qui est relu (read_again_from_disk, F237) sauf s’il a des modifications non enregistrées. Un flux d’événements perdu jette l’index plutôt que de s’y fier. Un projet qu’on ne peut pas observer (plus de surveillances inotify disponibles) s’ouvre quand même. - Tests :
a_project_that_cannot_be_watched_still_opens,a_lost_event_stream_throws_the_index_away_rather_than_trusting_it,an_ignored_folder_is_watched_while_the_explorer_has_it_open,a_tab_with_unsaved_edits_keeps_them_when_the_disk_changes. - Mesuré : ouvrir un projet de 10 000 fichiers, 28,6 ms p50 (budget 60 ms) ; reconstruire 10 000 lignes d’explorateur, 1,4 ms. L’enregistrement de l’observateur prenait 0,6 à 1,1 s sur le fil principal avant d’en être sorti ; ce que la fenêtre attend maintenant à l’ouverture d’un projet est d’environ 6 ms.
16.15 L’explorateur, son filtre, et créer des fichiers
- Chemin : un clic sur un dossier →
Tree::expand(unread_dir) →rowsreconstruites ; Afficher les fichiers cachés / ignorés →toggle_hidden_files,toggle_ignored_files→Tree::set_rules; la case de filtre →filter_by→ l’index des fichiers et la recherche floue →refilter→ seulement les dossiers menant à une correspondance ; le menu d’une ligne →kve_fs::make::{file, folder, rename, trash}. - Fichiers :
crates/kve-fs/src/tree.rs,crates/kve-fs/src/make.rs,crates/kve-search/src/index.rs,crates/kve-search/src/matcher.rs,window.rs. - Comment : le filtre cherche dans l’index des fichiers du projet, pas
dans les lignes à l’écran, donc il trouve des fichiers dans des dossiers
jamais ouverts ; un motif dans la case est lu comme un motif. La création
refuse un chemin qui sort du projet (
make::inside), n’écrase jamais (write_if_new), et la suppression va dans la corbeille du bureau (make::trash), qui sert d’annulation. - Tests :
a_filtered_tree_is_the_folders_on_the_way_to_a_match,the_filter_reaches_a_file_the_tree_has_never_read,a_glob_in_the_filter_box_is_a_glob,the_keyboard_walks_the_filtered_rows_rather_than_the_tree,a_path_that_leaves_the_project_is_refused.
16.16 Trouver un fichier par son nom
- Chemin :
FindFile→find_file→open_the_picker→build_indexs’il n’y en a pas (sur l’exécuteur d’arrière-plan) → chaque frappe →Matcher::best(index, query, limit)→Entrée→ le fichier ouvert. - Fichiers :
crates/kve-search/src/index.rs(FileIndex::build,apply),crates/kve-search/src/matcher.rs(le score denucleo-matcher),window.rs. - Comment : l’index est la liste des chemins relatifs du projet, sous les
règles de l’explorateur, construit la première fois qu’on le demande — le
sélecteur, le filtre de l’explorateur, les wikiliens — et réparé ensuite
par l’observateur ; il part avec le projet (F143). Un projet trop grand
pour être listé le dit (
is_truncated). Le matcher garde les tampons de travail de nucleo d’une frappe à l’autre ; la casse intelligente vient deCaseMatching::Smart. - Tests :
the_picker_reads_the_project_once_and_finds_by_name,choosing_from_the_picker_opens_the_file_and_shuts_it,the_picker_is_a_toggle_and_leaves_the_keyboard_where_it_found_it. - Mesuré : indexer 10 000 fichiers, 15 ms p50 (budget 200 ms) ; une recherche floue dessus, 0,31 ms p50 (budget 4 ms).
16.17 Rechercher et remplacer dans le projet
- Chemin : une frappe dans la case →
schedule_search(avec un délai ; le drapeau d’abandon de la recherche précédente levé) → sur l’exécuteur d’arrière-plankve_search::search(root, rules, query, filters, limits, cancel)→Results→ le panneau. Tout remplacer → une confirmation →replace_all→kve_search::replaceavec exactement les fichiers listés → les onglets ouverts relus → la recherche relancée. - Fichiers :
crates/kve-search/src/content.rs,crates/kve-search/src/glob.rs,window.rs(schedule_search,replace_all,open_match).
- Comment : pas de crates
grep— le parcours d’ignore,memchrpour les lignes, un test deNULpour les binaires. Les règles propres à la recherche excluent toujours ce que.gitignoreexclut, sauf si ignorés est coché ; les motifs d’inclusion et d’exclusion élaguent le parcours au lieu de filtrer les résultats. Les résultats sont coupés à mille fichiers, cent correspondances par fichier et quatre cents caractères par ligne. Un remplacement recherche de nouveau chaque fichier en l’écrivant, saute un fichier ouvert avec des modifications non enregistrées ou modifié depuis, écrit dans un fichier temporaire et le renomme par-dessus l’original, en gardant ses permissions. - Tests :
replacing_changes_what_was_listed_and_says_what_it_did,a_file_with_unsaved_changes_is_left_alone_by_a_replace,a_replace_that_was_cancelled_changes_nothing,the_filter_boxes_narrow_the_search; ceux dekve-search. - Mesuré : une recherche sur 50 000 fichiers, 99 ms p50 (budget 1 000 ms) ; un projet avec sa sortie de compilation dedans, 14 ms.
16.18 Le plan et la table des matières
- Chemin : à chaque pause (
schedule_rebuild, 120 ms) → markdown :kve_markdown::outline(text); code :symbols::outline(language, text)(une requête tree-sitter par langage) → le panneau du plan. Générer ou mettre à jour la table des matières →table_of_contents→kve_markdown::update_table_of_contents→replace_text. - Fichiers :
crates/kve-markdown/src/outline.rs,crates/kve-markdown/src/toc.rs,crates/kve-ui/src/symbols.rs. - Comment : le panneau du plan et la table des matières sont une seule
lecture des titres, ils ne peuvent donc pas diverger. La table des matières
vit entre
<!-- toc -->et<!-- /toc -->: écrite au curseur la première fois, remplacée là où elle est ensuite, jusqu’àtoc_max_level. Le plan du code utilise une requête@definition/@namepar grammaire, à la façon destags.scmde tree-sitter ; un test compile chaque requête contre sa grammaire. - Tests :
opening_the_outline_lists_the_headings;outline::tests,toc::tests,symbols::tests.
16.19 Rétroliens
- Chemin : la section Rétroliens du panneau markdown ouverte, un
changement de document ou un enregistrement → sur l’exécuteur
d’arrière-plan, un parcours des fichiers markdown du projet →
kve_markdown::mentions(source, from, target)par fichier → la section. Générer ou mettre à jour les rétroliens →update_mentions→kve_markdown::update_mentions→replace_text. - Fichiers :
crates/kve-markdown/src/mentions.rs,window.rs. - Comment : une mention est un wikilien (inclusions comprises) ou un lien
ou une image markdown, résolu comme un clic le résout : un lien depuis le
dossier du fichier qui le contient ou la racine du projet, un wikilien à
côté du fichier, depuis la racine, ou par son seul nom — si bien que
[[Notes]]compte pour chaqueNotes.md. Un chemin entre accents graves n’est pas un lien. Le bloc écrit se tient entre<!-- mentions -->et<!-- /mentions -->, chaque phrase citée dans un bloc de code en ligne pour que ses liens ne soient pas comptés la fois suivante. - Tests :
the_mentions_are_listed_and_written;mentions::tests. - Mesuré : les rétroliens d’une note sur 2 000 fichiers markdown, 204 ms p50 (budget 1 000 ms), hors du fil principal.
16.20 Le panneau TODO
- Chemin : le panneau à l’écran, ou toute écriture dans le projet →
look_for_todos→ sur l’exécuteur d’arrière-plankve_search::search(Query::todos(tags), …)— le parcours même de la recherche →todo_groups(tags, results)→ le panneau ;todo.mdlu et analysé parkve_markdown::boardpour Mes tâches. Une case →tick_item; la flèche →move_to_my_tasks. - Fichiers :
crates/kve-search/src/content.rs(Query::tags,Query::todos,is_checklist_item),crates/kve-markdown/src/board.rs,crates/kve-markdown/src/inline.rs(le markdown d’une carte sur une ligne),window.rs(look_for_todos,todo_groups,my_tasks_rows,tick_item,move_to_my_tasks). - Comment : un mot-clé est un mot entier tel qu’écrit —
TODO:etTODO(ana)oui,todoetMASTODONnon — et une ligne à deux mots-clés n’est listée qu’une fois, sous le premier. Les listes de tâches sont une recherche littérale de[ ], puisis_checklist_itemne garde que les éléments en début de ligne dans du markdown. Le parcours ne tourne que tant que le panneau est à l’écran, et un nouveau parcours abandonne l’ancien entre deux fichiers. Cocher relit d’abord la ligne et refuse une ligne qui a changé ; c’est écrit dans l’onglet ouvert s’il y en a un, en une étape d’annulation. Déplacer une ligne marquée écrit la carte danstodo.mdet retire le commentaire, ou toute la ligne, de son fichier. - Tests :
the_todo_pane_lists_the_tagged_lines_of_the_project,my_tasks_are_the_projects_todo_file,my_tasks_draws_a_cards_markdown,a_task_is_moved_out_of_the_code_into_my_tasks,a_checklist_item_is_ticked_from_the_pane. - Mesuré : les mots-clés sur 50 000 fichiers, 105 ms p50 (budget 1 000 ms) ; le markdown d’une carte, 1,5 µs par ligne.
16.21 Le tableau des tâches
- Chemin : Le tableau des tâches →
show_board→ un ongletboardview::BoardViewqui lit letodo.mdanalysé par la fenêtre à chaque dessin → un glisser →move_card→kve_markdown::move_card→ écrit dans l’onglet detodo.mds’il est ouvert, enregistré aussitôt ; + →add_card→kve_markdown::add_card; la case d’une sous-tâche →tick_item. - Fichiers :
crates/kve-markdown/src/board.rs(board,Card,Subtask,move_card,add_card),crates/kve-ui/src/boardview.rs. - Comment : chaque
##est une colonne, chaque élément de premier niveau une carte ; une tâche indentée sous une carte est sa sous-tâche (un niveau ; plus profond, c’est la même liste ; une ligne vide la termine). Un déplacement emporte les lignes indentées de la carte. Le tableau ne garde aucune copie du fichier et ne lit rien lui-même ; il suit letodo.mdouvert à chaque modification. Le texte d’une carte ne passe par la vue markdown degpui-baseque s’il contient un caractère markdown ; les sous-tâches sont repliées derrière leur compte, parce qu’une ligne dessinée coûte autant qu’une carte à chaque image. - Tests :
board::tests(donttasks_indented_under_a_card_are_its_subtasks),the_board_shows_todo_and_a_dragged_card_moves,a_card_shows_markdown_and_its_subtasks_tick. - Mesuré : déplacer ou ajouter une carte dans un
todo.mdde 500 cartes, 0,024 ms ; une image d’un tableau de 50 cartes à trois sous-tâches, 6 ms en texte simple et 12 ms tout en markdown (3,4 ms avant F238).
16.22 La note du jour et les modèles
- Chemin :
TodaysNote→todays_note→<daily_note_folder>/<AAAA-MM-JJ>.md→ s’il manque, le modèledaily_note_templaterempli et écrit parkve_fs::make::write_if_new→ ouvert. - Fichiers :
window.rs,crates/kve-config/src/palette.rs(les modèles comme lignes de la palette, les marqueurs{{…}}),resources/templates/(fournis, copiés dans le dossier de configuration seulement s’ils n’y sont pas). - Comment : jamais d’écrasement — une note existante est ouverte. Les
modèles sont cherchés dans le
.kaivalya/templates/du projet, puis dans~/.config/kaivalya-editor/templates/; un nom sans réponse est signalé, et la note commence vide.
16.23 Les réglages d’un projet, et le fichier du projet
- Chemin : Réglages du projet →
open_project_settings→ le fichier ouvert (créé s’il manque) ; à l’ouverture du projet et à l’enregistrement de ce fichier →apply_project_settings(root)→ la table du projet fusionnée par-dessus la vôtre, par-dessus les valeurs par défaut. Écrire le fichier du projet →write_rule_file→kve_config::rules::rule_template()→.kaivalya/project.toml. - Fichiers :
window.rs,crates/kve-config/src/settings.rs(Settings::parse, la fusion),crates/kve-config/src/rules.rs. - Comment : chaque option est cherchée dans le fichier du projet, puis le
vôtre, puis
defaults.toml: une nouvelle option n’oblige jamais à mettre à jour le fichier du projet. Une clé inconnue est nommée et le reste s’applique ; un fichier qui ne s’analyse pas laisse vos réglages intacts. Le fichier de règles est produit à partir des mêmes clés de traduction que l’aide du panneau markdown, chaque clé commentée : en écrire un dans un arbre ne change rien tant qu’aucune ligne n’est décommentée.
16.24 Sessions, lieux, et plusieurs fenêtres
- Chemin : tout changement à retenir (un onglet, un curseur, un repli) →
remember→session()→kve_config::session::Session::save_in→ un fichier par racine de projet dans le dossier de données ; à l’ouverture du projet →load_in→surviving()(les fichiers supprimés écartés). Récents et favoris →kve_config::places. Nouvelle fenêtre →new_window. - Fichiers :
crates/kve-config/src/session.rs,crates/kve-config/src/places.rs,window.rs. - Comment : rien n’est écrit dans le projet. Les sessions de projets qui
n’existent plus sont élaguées (
prune_in). Chaque fenêtre a son propre projet ; ouvrir un projet tenu par une autre fenêtre amène celle-ci au premier plan, parce que deux fenêtres écriraient chacune la session par-dessus l’autre. Quitter interroge chaque fenêtre tour à tour et s’arrête au premier Annuler. - Tests :
the_lists_are_written_down_and_read_back,opening_a_file_puts_it_at_the_front_of_the_recent_list;session::tests,places::tests.
16.25 Atteindre un panneau sans la souris
- Chemin :
F6/Maj+F6→focus_pane/focus_previous_pane→ le panneau suivant affiché par un bord, dans l’ordre du rail, puis le texte → le gestionnaire de touches du panneau (flèches,Début,Fin,←→,Entrée). - Fichiers :
window.rs. - Comment : il ne s’arrête que sur un panneau à l’écran, jamais sur une
icône du rail. Un panneau commence là où vous êtes — le plan sur le titre
au-dessus du curseur, l’explorateur et les fichiers ouverts sur le fichier
au premier plan.
Entréeouvre et rend le clavier au texte. - Tests :
the_keyboard_goes_round_the_panes_that_are_drawn,the_files_pane_is_walked_from_the_keyboard.
16.26 Git : état, différences et commit
- Chemin : toute écriture dans le projet, ou le panneau ouvert →
read_git_if_stale→read_git→ sur l’exécuteur d’arrière-plankve_git::open→status,branch,upstream,remotes→ le panneau (git_pane). Un double-clic →show_diff→kve_git::diff/hunks/side_by_side→ un ongletdiffview.rs. Committer →commit_git→kve_git::commit(repo, paths, message). - Fichiers :
crates/kve-git/src/lib.rs,crates/kve-ui/src/diffview.rs,window.rs(git_pane,read_git,commit_git,show_diff).
- Comment : tout ce qui lit ou committe passe par gitoxide, donc le
gitinstallé et sa configuration n’importent pas ; rien danskve-gitn’est assez rapide pour une image, si bien que la fenêtre l’appelle toujours hors du fil principal.commitindexe les chemins cochés (un fichier supprimé est retiré), prend l’auteur dans la configuration de git, et refuse un message vide ou un commit qui ne changerait rien. Les différences sont calculées contreHEADavec trois lignes de contexte ; des changements proches partagent un bloc, et un remplacement est placé à côté de ce qu’il remplace. - Tests : le
tests.rsdekve-git— dontonly_the_files_chosen_are_committed,a_commit_that_cannot_be_made_says_why,the_diff_is_against_head_with_context; dans la fenêtre,the_git_pane_lists_and_commits_what_is_ticked,enter_in_the_message_commits. - Mesuré :
git statusde ce dépôt, 2,3 ms p50 (budget 120 ms).
16.27 Git : branches et réseau
- Chemin : le nom de la branche →
branch_menu→ une branche →switch_branch(les documents non enregistrés demandés d’abord) →kve_git::switch→ les documents ouverts relus ou fermés. Nouvelle branche… →start_naming_a_branch→create_named_branch→kve_git::create_branch. Récupérer, Pousser, Rebaser (dessinés pargit_actions) →run_network(Network::…)→kve_git::fetch,push,rebase→ les documents réécrits relus (follow_the_rebase). - Fichiers :
crates/kve-git/src/lib.rs,window.rs. - Comment : ces cinq-là lancent le
gitinstallé, donc les clés ssh, les gestionnaires d’identifiants et les proxys marchent comme dans un terminal ; les invites sont coupées (GIT_TERMINAL_PROMPT=0et compagnie), si bien qu’un dépôt qui veut un mot de passe échoue avec la phrase de git au lieu de rester bloqué.switchrefuse d’écraser une modification et nomme les fichiers ;pushdéfinit la branche suivie la première fois ;rebaserefuse tant qu’autre chose est en cours, et un conflit lancegit rebase --abort— il n’y a pas de bouton Continuer derrière. Une commande en cours est dessinée dans la couleur d’accent, et un second appui ne fait rien. - Tests :
switching_writes_the_branch_and_back,switching_refuses_to_overwrite_and_carries_the_rest,push_and_fetch_through_git,a_rebase_that_conflicts_is_undone,a_rebase_refuses_when_it_should; dans la fenêtre,switching_branch_rewrites_the_open_document,fetch_and_push_from_the_pane,rebase_from_the_pane.
16.28 Git : historique, graphe, blame et conflits
- Chemin : Historique →
show_history→kve_git::log(repo, 500)→ un ongletgitviews::HistoryView→ un commit →kve_git::details→ un fichier →open_commit_diff→kve_git::diff_in_commit. Graphe →show_graph→kve_git::graph→gitviews. Historique des lignes →blame_file→kve_git::blame. Un fichier U double-cliqué →conflictview.rs→kve_git::conflicts→ Garder notre version / leur version / les deux →kve_git::resolve→ Marquer comme résolu →kve_git::mark_resolved. - Fichiers :
crates/kve-git/src/lib.rs,crates/kve-ui/src/gitviews.rs,crates/kve-ui/src/conflictview.rs. - Comment : le graphe trie les commits topologiquement, les enfants
d’abord même quand deux partagent la même seconde, et donne à chaque lignée
une voie et une couleur. Le blame décrit la version du dernier commit, et
ne dessine que les lignes à l’écran (F207), comme la vue des journaux. Un
bloc en conflit est lu depuis les marqueurs du fichier, qui reste la
vérité : un bloc corrigé à la main quitte simplement l’onglet de fusion. Un
message vide sur un commit de fusion prend
merge_message, celui que git a préparé. - Tests :
the_log_is_newest_first_and_blame_names_each_line,the_graph_draws_a_branch_and_its_merge,the_topological_sort_puts_children_first,conflict_markers_are_read_and_resolved,a_conflicted_merge_is_resolved_and_committed; dans la fenêtre,history_and_blame_open_windows. - Mesuré : le journal, 500 commits, 20 ms p50 ; le graphe de toutes les branches, 21 ms ; le blame d’un fichier de 22 000 lignes, 220 ms (budget 1 000 ms), tout hors du fil principal.
16.29 Committer tout seul
- Chemin : un enregistrement →
saved_for_autocommit(la règle lue parautocommit_ruledans.kaivalya/project.toml; demandée une fois parask_about_autocommit) → une minuterie, relancée à chaque enregistrement (autocommit_wait) →take_autocommit→ sur l’exécuteur d’arrière-plankve_git::autocommit(repo, paths)puiskve_git::push→commit_and_push. Quitter le projet, fermer ou quitter l’éditeur →autocommit_now. - Fichiers :
crates/kve-git/src/lib.rs(autocommit),window.rs,crates/kve-config/src/session.rs(AutocommitAnswer: la réponse et la règle à laquelle elle a été donnée). - Comment :
autocommitne prend que les chemins enregistrés qui diffèrent deHEAD, leurs noms comme message, et rendNonependant une fusion, un rebasage ou un cherry-pick ; un fichier en conflit n’est jamais pris. La réponse à la question est gardée avec la règle à laquelle elle répondait : une règle changée redemande. À la fermeture de la fenêtre, le commit et l’envoi se font sur-le-champ et la fenêtre les attend ; à l’ouverture d’un autre projet, ils tournent hors du fil principal. - Tests :
autocommit_takes_the_saved_files_that_changed;an_autocommit_pushes_and_leaving_the_project_does_not_wait,closing_the_window_commits_and_pushes_what_was_waiting. - Mesuré : committer et pousser à la fermeture, un fichier, un dépôt nu sur le même disque, 24 à 31 ms (budget 100 ms) ; un dépôt de l’autre côté d’un réseau ajoute son propre aller-retour.
16.30 L’aperçu en direct
- Chemin :
TogglePreview/ToggleSplit→toggle_preview/toggle_split→ le mode du document (edit,preview,split) → à chaque pauseschedule_rebuild→ la source, ses retours à la ligne simples joints (kve_markdown::join_soft_breaks), donnée auTextViewState::markdownde l’aperçu → la vue markdown degpui-base. En vue partagée, un curseur qui change de section →preview_follows_the_caret. - Fichiers :
window.rs,crates/kve-markdown/src/preview.rs,crates/kve-markdown/src/outline.rs. - Comment : l’aperçu en direct est la vue markdown de la boîte à outils
elle-même : il ne coûte rien de plus et se redessine à chaque pause ; il
suit le thème de l’éditeur et montre tel qu’écrit ce qu’il ne connaît pas
(
$…$,[!TIP], mermaid). Le curseur, le défilement et un clic sur un titre restent accordés par la correspondance titres-blocs du plan, celle qu’utilise un clic dans le plan. Rien n’est construit pour un aperçu qui n’est pas à l’écran. - Tests :
nothing_is_built_for_a_preview_that_is_not_open,the_preview_follows_the_caret_from_heading_to_heading.
16.31 L’aperçu complet
- Chemin :
OpenHeavyPreview→open_heavy_preview→heavy::Previewlancekve-preview→ à chaque pauseschedule_heavy_preview→heavy_page→kve_markdown::page(source, PageOptions)(le HTML, la feuille de style choisie incluse, maths et diagrammes dessinés en SVG) → écrit sur l’entrée standard du processus →kve-previewdessine la dernière page avec Blitz.
- Fichiers :
crates/kve-ui/src/heavy.rs(ce qui est envoyé, et le processus),crates/kve-preview/src/main.rs(le moteur de rendu),crates/kve-markdown/src/page.rs,crates/kve-markdown/src/html.rs,crates/kve-ui/src/maths.rs. - Comment : l’aperçu est nourri d’une page ; il ne lit jamais de
fichier et ne demande rien à l’éditeur, si bien qu’en faire un panneau plus
tard est un changement de transport. Deux verrous : la fonctionnalité cargo
heavy-previewdécide si la compilation contient un moteur de rendu et de diagrammes,heavy_previewsi une compilation qui les a les propose. Un tube qui refuse une page veut dire que le processus est parti : la poignée est abandonnée et la barre d’état le dit. La page est celle de l’export, à une ligne<base>près, qui permet à la fenêtre de trouver vos images sur le disque. - Tests :
the_heavy_preview_draws_markdown_and_html_and_nothing_else,the_heavy_preview_says_it_is_off_rather_than_doing_nothing;page::tests. - Mesuré : la page de 100 Ko de markdown, 17 ms p50 (budget 120 ms) ; vingt formules en SVG, 6 ms ; un organigramme mermaid de six nœuds, 56 ms (551 ms la première fois). Le processus de l’aperçu occupe environ 117 Mo tant qu’il est ouvert.
16.32 Avec quelle feuille de style un document est dessiné
- Chemin : un changement de document, d’en-tête ou de règle →
refresh_markdown_rules→kve_markdown::stylesheet_for(document, source, chosen, stop)→ la pastille du pied de fenêtre (stylesheet_chip) et la page. La pastille →switch_stylesheet→ le choix gardé dans la session. - Fichiers :
crates/kve-markdown/src/stylesheet.rs(stylesheet_for,folder_rule),crates/kve-ui/src/styles.rs(le dossier des styles, les feuilles fournies écrites),window.rs. - Comment : quatre réponses dans l’ordre — votre choix pour ce document,
le
css:de l’en-tête, la règle[[css]]la plus longue qui correspond dans.kaivalya/project.toml(la remontée s’arrête à la racine du projet), la feuille GitHub fournie. Un fichier nommé qui n’existe pas reste la réponse, pour qu’une faute de frappe soit signalée plutôt que cachée. Les réponses sont mises en cache : le pied de fenêtre les lit à chaque image, et aucun appel système n’a sa place dans le dessin. La lecture de l’en-tête est bornée ; la partie qui touche aux fichiers ne tourne que quand cette lecture change. - Tests :
stylesheet::tests—choosing_the_bundled_one_beats_the_front_matter,the_longest_path_wins_and_the_projects_styles_come_first; dans la fenêtre,the_switcher_offers_the_stylesheets_in_the_project,only_a_rendered_file_has_a_stylesheet_to_argue_about.
16.33 Exporter en HTML
- Chemin : Exporter en HTML… →
export_html→ la boîte de dialogue (images, dessin par le navigateur, boutons de retour, feuille de style) →export_html_page→heavy_page_for_export(scripts, back_button)→kve_markdown::with_images(images en URI de données, ou copiées à côté) → écrit. Exporter le projet… →export_project_asked→ un dossier →export_project_into→ la même chose par fichier markdown, les liens entre pages réécrits en.html. - Fichiers :
window.rs,crates/kve-markdown/src/export.rs,crates/kve-markdown/src/page.rs. - Comment : l’export est la page de l’aperçu complet sans sa ligne
<base>. Dessinée ici, la page ne nomme aucun script ; laissée au navigateur, elle garde le TeX et la source mermaid et nomme MathJax et mermaid.js parexport_mathjax_url/export_mermaid_url, entre guillemets pour qu’une adresse ne puisse pas sortir de son attribut. Deux images de même nom ne deviennent pas une. L’export du projet parcourt sous les règles de git et laisse de côté le dossier où il écrit ; un lien vers un fichier qui n’est pas une page reste tel qu’écrit, et il est compté. - Tests :
the_export_asks_before_it_writes,an_export_is_one_file_with_its_pictures_inside_it,an_export_can_put_its_pictures_beside_it_instead,the_second_export_hands_the_maths_and_the_diagram_to_the_browser,a_project_is_exported_page_by_page,an_export_takes_the_stylesheet_chosen_for_it;export::tests,page::tests.
16.34 Comment le markdown est lu
- Chemin : chaque lecture d’un document — l’aperçu, la page, le plan,
l’orthographe — passe par
kve_markdown::reading::options(), l’unique jeu d’options de pulldown-cmark ;to_html_with(source, HtmlOptions)produit le HTML. - Fichiers :
crates/kve-markdown/src/reading.rs,html.rs,callout.rs(les encadrés),wikilink.rs,transclude.rs(les inclusions, avec une garde contre une note qui s’inclut elle-même),maths.rs,mermaid.rs,tag.rs,front_matter.rs. - Comment : une seule lecture, si bien que le plan, la table des
matières, l’aperçu et l’export ne peuvent pas diverger sur ce que contient
un document. Les titres portent les ancres que distribue le plan, deux
titres identiques recevant deux ancres distinctes. Les notes de bas de page
sont rassemblées où qu’elles soient écrites. Les maths sont trouvées par
maths()selon les règles du document de référence ; un bloc de code n’est jamais des maths. - Tests :
html::tests(dontheadings_carry_the_anchors_the_outline_handed_out),callout::tests,transclude::tests,maths::tests; les tests du document de référence danscrates/kve-markdown/tests/reference_document.rs. - Mesuré : 100 Ko de markdown en HTML, 19 ms p50 (budget 120 ms) ; y trouver les maths, 0,85 ms.
16.35 La vue journal
- Chemin : un fichier
.logouvert, ou Afficher comme journal →view_as_log→ un ongletlogview.rs→kve_core::log::LineIndex::of(path)(où commence chaque ligne) → les lignes à l’écran lues dans le fichier au moment de les dessiner. Un filtre, un niveau ou un intervalle de temps → un passage sur le fichier sur l’exécuteur d’arrière-plan → les lignes gardées. Aller à l’heure… →kve_core::log::first_at. Une minuterie chaque seconde →LineIndex::extend→ ce qui a été ajouté est indexé et filtré à part. - Fichiers :
crates/kve-core/src/log.rs(level_of,stamp_end,moment_of,bound,first_at,LineIndex),crates/kve-ui/src/logview.rs. - Comment : le fichier n’est jamais chargé — huit octets par ligne pour
l’index, un petit cache des lignes dessinées. Un niveau est un mot près du
début de la ligne, sous l’une de ses graphies habituelles ; une ligne qui
n’en a pas (une trace de pile) prend celui de la précédente. Un moment est
lu comme un nombre qui se trie (
aaaammjjhhmmssmmm), si bien qu’un intervalle est le passage même du filtre, et Aller à l’heure coupe l’index en deux, en lisant quelques lignes à chacune d’une vingtaine d’étapes — d’où l’hypothèse d’un fichier dans l’ordre du temps. Un fichier devenu plus court est relu depuis le début. - Tests :
a_log_is_filtered_followed_and_opened_as_text,a_log_is_searched_by_dates;log::tests. - Mesuré : indexer un journal de 50 Mo et 500 000 lignes, 7,6 ms p50 (budget 500 ms) ; le filtrer sur un mot, 158 ms (budget 1 000 ms, hors du fil principal) ; aller à une heure, 0,65 ms.
16.36 Tableaux blancs
- Chemin : un
.canvasouvert →kve_canvas::parse→ un ongletcanvasview.rsqui dessine leCanvas→ chaque geste décidé d’après la géométrie du tableau (kve_canvas::geometry) → le modèle modifié (kve_canvas::edit) →kve_canvas::to_jsonécrit aussitôt s’il diffère. Exporter en SVG… →kve_canvas::svg::to_svg. Nouveau tableau blanc →new_whiteboard; Afficher comme tableau blanc →show_whiteboard. - Fichiers :
crates/kve-canvas/src/lib.rs(le modèle, lu et écrit),geometry.rs(ce qui est sous le pointeur, les côtés, les courbes),edit.rs,svg.rs;crates/kve-ui/src/canvasview.rs.
- Comment : le modèle garde chaque champ qu’il ne connaît pas et le réécrit tel quel, dans la disposition d’Obsidian d’un nœud ou d’une arête par ligne, si bien qu’un changement de carte est le changement d’une ligne dans git. Les cartes hors de l’écran ne sont pas construites ; en dessous d’une taille où ses mots feraient quelques pixels, une carte ne dessine que son cadre, et elle n’offre ses connecteurs et sa poignée qu’à partir de 48 × 32 pixels à l’écran. Une écriture qui échoue garde le changement sur le tableau et marque l’onglet non enregistré. Le tableau relit son fichier quand il change sur le disque.
- Tests :
a_canvas_opens_on_the_board_and_what_is_dropped_is_written,a_window_started_on_a_canvas_shows_the_board,the_board_exports_as_one_svg,the_text_colour_setting_reaches_the_board_and_its_picture; ceux dekve-canvas. - Mesuré : écrire un tableau de 2 000 cartes, 2,4 ms p50 (budget
16 ms) ; le lire, 4,6 ms ; ce qui est sous un appui, 0,2 ms ; l’export SVG,
23 ms.
KVE_TIMINGS=1affiche la lecture, les écritures et les images du tableau.
16.37 Les panneaux, le rail et les panneaux en fenêtre
- Chemin : un clic sur une icône du rail →
reveal(pane)ou le bord replié ; un survol →peek(pane)(sipeek_on_hover) ; un panneau montré en fenêtre (colour_as,characters_as) →toggle_pane_window→panewindow.rs. - Fichiers :
window.rs(Pane,reveal,peek,side_of,open_on),crates/kve-ui/src/panewindow.rs,crates/kve-ui/src/icon.rs. - Comment : chaque bord montre un panneau ; le bord d’un panneau est son
réglage
<panneau>_side, et les icônes suiventrail_order(une liste pour les deux bords ; noms inconnus ignorés, répétitions comptées une fois, panneaux non listés à la suite). Un survol dessine le panneau par-dessus le texte et masque ce qui est dessous ; un glisser qui y commence le garde ouvert jusqu’au relâchement. Une fenêtre de panneau dessine le contenu même du panneau de la fenêtre d’éditeur — pas de seconde copie de l’état — et rend ce qu’elle écrit à la fenêtre qui tient le document. - Tests :
the_rail_keeps_every_icon_and_pushes_the_open_one_in,the_rail_follows_the_order_it_is_given,hovering_an_icon_peeks_the_pane_over_the_editor,a_rail_told_not_to_peek_does_not,a_drag_out_of_a_peek_keeps_it_open_until_the_release,an_edge_folds_to_its_rail_and_comes_back.
16.38 Langages et coloration
- Chemin : un fichier ouvert → son langage d’après son nom → la grammaire
tree-sitter de la boîte à outils → les captures associées aux jetons du
thème par
syntax.rs. Le langage du pied de fenêtre, ou Langage… →choose_language_action→choose_language→ les couleurs, le plan, le panneau markdown et le formateur suivent. - Fichiers :
crates/kve-ui/src/syntax.rs,crates/kve-ui/src/code.rs,window.rs. - Comment : rien ne regarde dans un fichier pour deviner ; le nom décide, et un choix dans le pied de fenêtre le corrige pour ce fichier tant qu’il est ouvert (un Enregistrer sous l’efface). Une capture que le thème ne nomme pas est dessinée dans la couleur ordinaire, si bien qu’un manque ressemble à une grammaire qui ne colore pas. Le HTML est coloré avec la grammaire XML : la grammaire HTML fournie n’analyse pas le HTML (F119).
- Tests :
a_language_chosen_overrides_the_name;syntax::tests,code::tests.
16.39 Thèmes et polices
- Chemin : au démarrage et à un changement de réglages →
theme::install(setting)→apply_configured("light","dark", ouapply_named) →set_fonts→settled: les polices de l’interface et du code vérifiées contre ce qui est installé (available_fonts,installed,has_bold). Chaque couleur que dessine l’éditeur vient detheme::tokens(cx). - Fichiers :
crates/kve-ui/src/theme.rs,resources/themes/*.json(écrits dans~/.config/kaivalya-editor/themes/au démarrage),crates/kve-ui/src/emoji.rs. - Comment : un thème est un fichier JSON au format de la boîte à outils, avec un schéma à côté. Une police absente est remplacée ici, avant que le framework ne le fasse, pour que la chaîne de secours de la fenêtre (la police d’émojis en couleur) survive (F171) ; le remplacement prend la première police de secours qui a son propre gras, parce qu’une police variable est dessinée à une seule graisse (F242). Une police nommée dans les réglages est gardée telle quelle ; une à laquelle rien ne répond est signalée dans la barre d’état.
- Tests :
theme::tests— donta_missing_family_becomes_the_one_the_framework_would_draw,a_fallback_with_a_bold_is_taken_first;every_theme_round_trips_through_the_file. - Mesuré : au plus vingt recherches de police, une fois, au démarrage.
16.40 Le fichier de réglages
- Chemin :
Ctrl+,→open_settings→ le fichier annoté écrit dans votre langue s’il manque, mis à jour avec toute nouvelle option en gardant vos valeurs (l’ancien ensettings.toml.bak) → ouvert. Au démarrage →Settings::parse(votre fichier)fusionné par-dessusresources/settings/defaults.toml. Réinitialiser →reset_settings, après avoir demandé. - Fichiers :
crates/kve-config/src/settings.rs,resources/settings/defaults.toml,resources/settings/for-tests.toml,crates/kve-config/locales/*.yml(le commentaire de chaque option,settings.*). - Comment :
Settings::parseest la seule porte : un fichier partiel est fusionné par-dessus la table des valeurs par défaut, si bien qu’une option ajoutée plus tard n’oblige jamais à mettre votre fichier à jour. Une clé inconnue est signalée et le reste s’applique ; un fichier cassé est une erreur à la lecture mais n’arrête jamais l’éditeur. Les tests tournent surfor-tests.toml, dont les valeurs ne bougent pas, et n’affirment jamais ce que ditdefaults.toml(F139). Voir Guides pratiques pour ajouter une option. - Tests :
every_setting_is_explained_in_every_shipped_locale,the_two_settings_files_name_the_same_keys,a_partial_file_fills_the_rest_from_the_defaults,an_unknown_key_is_reported_rather_than_silently_ignored,a_file_written_before_an_option_existed_gains_it_and_keeps_what_it_had,the_settings_example_in_the_readme_actually_parses; dans la fenêtre,the_settings_command_writes_the_annotated_file_and_opens_it,resetting_the_settings_asks_first_and_keeps_the_old_file.