[{"data":1,"prerenderedAt":563},["ShallowReactive",2],{"seite-\u002Ffr\u002Fopen-source\u002Feditor-suche\u002F":3},{"id":4,"title":5,"body":6,"description":554,"extension":555,"meta":556,"navigation":557,"path":558,"robots":559,"seo":560,"stem":561,"__hash__":562},"seiten_fr\u002Fopen-source\u002Feditor-suche.md","Sept mois à la recherche d’un éditeur",{"type":7,"value":8,"toc":544},"minimark",[9,13,17,20,25,28,31,35,38,58,72,76,79,87,90,94,97,297,304,308,314,321,332,336,341,352,358,487,494,497,504,507,511,514,518,521,532,540],[10,11,5],"h1",{"id":12},"sept-mois-à-la-recherche-dun-éditeur",[14,15,16],"p",{},"VS Code a été mon premier et unique éditeur. Mais ses performances me\ngênaient : parfois, il consomme énormément de processeur et de mémoire. J’ai\ndonc commencé à chercher quelque chose de plus rapide.",[14,18,19],{},"Mes exigences étaient modestes : un aperçu pour les PDF et les images, un\nterminal, et Git dans l’éditeur.",[21,22,24],"h2",{"id":23},"les-éditeurs-en-terminal-sont-rapides-mais-aveugles","Les éditeurs en terminal sont rapides, mais aveugles",[14,26,27],{},"Le premier chemin menait au terminal. Là, la vitesse n’est pas un problème.\nL’aperçu, si : pas d’image, pas de PDF, pas de Markdown rendu.",[14,29,30],{},"J’ai donc essayé de l’ajouter après coup. Dans Fresh, les plugins sont trop\nlimités pour cela. Dans LazyVim, cela fonctionne, mais pas partout. Tous les\nterminaux ne suivent pas, et ce n’était pas stable non plus : LazyVim\nplantait lors des aperçus d’images dans l’explorateur. Par-dessus tout\nplanait le pressentiment que je finirais par buter contre un plafond inscrit\ndans le terminal lui-même.",[21,32,34],{"id":33},"changer-lenveloppe-plutôt-que-rafistoler-le-plugin","Changer l’enveloppe plutôt que rafistoler le plugin",[14,36,37],{},"C’est de là qu’est née ma première approche personnelle, et elle inverse le\nproblème. Si l’aperçu ne rentre pas dans le terminal, alors c’est l’éditeur\nen terminal qui doit entrer dans une fenêtre.",[14,39,40,41,45,46,49,50,53,54,57],{},"Deux tentatives sont allées dans ce sens. ",[42,43,44],"code",{},"neoview"," intègre Neovim via\n",[42,47,48],{},"nvim-rs"," dans une interface construite avec gpui, le framework avec lequel\nZed est aussi construit. L’interface communique avec Neovim via msgpack-rpc.\n",[42,51,52],{},"freshview"," fait la même chose avec l’éditeur Fresh, mais de manière plus\nétroite : Fresh tourne comme bibliothèque dans le même processus, egui\nfournit la fenêtre, ",[42,55,56],{},"egui_ratatui"," traduit entre les deux, et mupdf dessine\nles PDF et les images dans des fenêtres flottantes à côté.",[14,59,60,61,64,65,68,69,71],{},"Les tentatives suivantes, ",[42,62,63],{},"flexed"," et ",[42,66,67],{},"editor-framework",", ne sont plus des\nwrappers, mais des squelettes sur gpui dans lesquels rien n’est noyau et\ntout est plugin. Dans ",[42,70,67],{},", les plugins sont écrits en Lua et\nse rechargent sans recompilation.",[21,73,75],{"id":74},"pourquoi-rust-a-été-écarté","Pourquoi Rust a été écarté",[14,77,78],{},"Zed est un bon éditeur, mais sans aperçu PDF. Et Zed est écrit en Rust.",[14,80,81,82,64,84,86],{},"Rust était trop lent à compiler pour moi. ",[42,83,44],{},[42,85,52],{}," avaient\nleurs difficultés, un affichage lent ici, un plantage à la fermeture de\nl’aperçu PDF là. On aurait peut-être pu les maîtriser, peut-être pas. C’est\nautre chose qui a tranché : avec ces temps de compilation, je n’avançais pas\nassez vite. Et gpui, c’est du Rust, donc il est parti avec.",[14,88,89],{},"Je cherchais un autre langage rapide, avec lequel on puisse concurrencer Zed.\nGo paraissait poussiéreux, V trop peu connu, et Zig était justement en pleine\npercée comme concurrent de Rust. Donc Zig, et là, la compilation est pour moi\nagréablement plus rapide.",[21,91,93],{"id":92},"onze-tentatives-huit-interfaces","Onze tentatives, huit interfaces",[14,95,96],{},"Chaque ligne est un dépôt à part, dans l’ordre dans lequel ils ont été créés.\nCertains ont vécu un jour, d’autres une semaine. Les onze tiennent en cinq\nsemaines, entre début mars et début avril 2026.",[98,99,100,122],"table",{},[101,102,103],"thead",{},[104,105,106,110,113,116,119],"tr",{},[107,108,109],"th",{},"Début",[107,111,112],{},"Tentative",[107,114,115],{},"Langage",[107,117,118],{},"Interface",[107,120,121],{},"Raison de l’abandon",[123,124,125,142,155,168,182,197,214,231,248,265,281],"tbody",{},[104,126,127,131,133,136,139],{},[128,129,130],"td",{},"2 mars",[128,132,44],{},[128,134,135],{},"Rust",[128,137,138],{},"gpui",[128,140,141],{},"Temps de compilation",[104,143,144,146,148,150,153],{},[128,145,130],{},[128,147,52],{},[128,149,135],{},[128,151,152],{},"egui, egui_ratatui",[128,154,141],{},[104,156,157,160,162,164,166],{},[128,158,159],{},"8 mars",[128,161,63],{},[128,163,135],{},[128,165,138],{},[128,167,141],{},[104,169,170,173,175,177,180],{},[128,171,172],{},"9 mars",[128,174,67],{},[128,176,135],{},[128,178,179],{},"gpui, mlua",[128,181,141],{},[104,183,184,187,190,192,195],{},[128,185,186],{},"14 mars",[128,188,189],{},"slint-rust-editor",[128,191,135],{},[128,193,194],{},"Slint",[128,196,141],{},[104,198,199,202,205,208,211],{},[128,200,201],{},"15 mars",[128,203,204],{},"mojo-nuklear-editor",[128,206,207],{},"Mojo, pont C",[128,209,210],{},"Nuklear",[128,212,213],{},"Mojo inadapté pour écrire des éditeurs",[104,215,216,219,222,225,228],{},[128,217,218],{},"17 mars",[128,220,221],{},"slint-editor",[128,223,224],{},"Mojo",[128,226,227],{},"Slint via Python",[128,229,230],{},"Slint accessible uniquement via Python",[104,232,233,236,239,242,245],{},[128,234,235],{},"22 mars",[128,237,238],{},"v-gui-editor",[128,240,241],{},"V",[128,243,244],{},"Framework GUI de V",[128,246,247],{},"Le défilement a cassé après l’ajout de l’explorateur et des onglets",[104,249,250,253,256,259,262],{},[128,251,252],{},"25 mars",[128,254,255],{},"zed-clone",[128,257,258],{},"Zig",[128,260,261],{},"dvui",[128,263,264],{},"Plantage au défilement, gros fichiers lents",[104,266,267,270,273,275,278],{},[128,268,269],{},"26 mars",[128,271,272],{},"sokol-nanovg-zig-editor",[128,274,258],{},[128,276,277],{},"sokol, NanoVG",[128,279,280],{},"Police disparue après le passage à Vulkan, la boucle de jeu de sokol coûtait du CPU",[104,282,283,286,289,291,294],{},[128,284,285],{},"2 avril",[128,287,288],{},"qt-ziged",[128,290,258],{},[128,292,293],{},"Qt 6 via libqt6zig",[128,295,296],{},"Resté une démonstration technique",[14,298,299,300,303],{},"Le 3 avril a commencé ",[42,301,302],{},"zid",". Depuis, il n’y a plus eu de nouvelle tentative.",[21,305,307],{"id":306},"ce-qui-en-est-sorti","Ce qui en est sorti",[14,309,310,311,313],{},"L’éditeur s’appelle ",[42,312,302],{}," et est écrit en Zig. Il dessine via wgpu, avec le\nrenderer fixé sur le backend Vulkan. La mise en page est assurée par Clay.\nLe rendu du texte est maison : atlas de glyphes et renderer GPU basé sur\nFreeType.",[14,315,316,317,320],{},"L’éditeur lui-même est maison : saisie, commandes d’édition, recherche,\nretour à la ligne, numéros de ligne, raccourcis clavier. En dessous se trouve\ndu code tiers, et c’est voulu. Le stockage du texte est assuré par\n",[42,318,319],{},"flow-core",", la bibliothèque centrale de l’éditeur Flow Control, avec les\ntypes de tampon, de curseur et de sélection. La coloration syntaxique vient\nde la même maison via tree-sitter. Le terminal est l’émulation de Ghostty,\nle Markdown est rendu par zigdown, le PDF est dessiné par mupdf.",[14,322,323,324,64,326,328,329,331],{},"C’est un autre découpage que pour ",[42,325,44],{},[42,327,52],{},". Là, tout\nl’éditeur était tiers et seule l’enveloppe était maison. Avec ",[42,330,302],{},", c’est\nl’inverse : l’éditeur est maison, et ce qui est tiers, ce sont les briques en\ndessous, qui n’ont rien à voir avec l’édition.",[21,333,335],{"id":334},"les-règles-ont-leur-place-dans-le-code-pas-dans-le-prompt","Les règles ont leur place dans le code, pas dans le prompt",[14,337,338,340],{},[42,339,302],{}," a un agent intégré qui travaille avec un modèle de langage local,\nllama-server avec Qwen3-4B. Un agent dans l’éditeur a besoin d’outils, et les\noutils ont besoin de limites. La voie évidente consiste à écrire les limites\ndans le prompt système. Mais avec un modèle de cette taille, une ligne de\nprompt n’est qu’une prière, et elle coûte de la place à chaque requête dans\nun contexte de 8192 tokens. C’est pourquoi les limites sont dans le code. Là,\nen revanche, elles doivent être sans faille.",[14,342,343,344,347,348,351],{},"Un exemple. Qui veut écraser un fichier existant reçoit une boîte de dialogue\nde confirmation. Le modèle l’a contournée dès la première tentative : au lieu\nde ",[42,345,346],{},"write_file",", il a envoyé un ",[42,349,350],{},"replace_text"," dont le texte recherché était\nle fichier entier. Formellement pas un écrasement, en pratique exactement\ncela.",[14,353,354,355,357],{},"Depuis, ",[42,356,350],{}," compte aussi comme écrasement dès qu’il remplace la\nmoitié du fichier ou plus :",[359,360,365],"pre",{"className":361,"code":362,"language":363,"meta":364,"style":364},"language-zig shiki shiki-themes github-light github-dark","\u002F\u002F\u002F replace_text mit `old` = (fast) ganzer Datei ist ein verkapptes Überschreiben und\n\u002F\u002F\u002F braucht dieselbe Bestätigung wie write_file auf eine bestehende Datei.\npub fn replaceCountsAsRewrite(file_len: usize, old_len: usize) bool {\n    if (file_len == 0) return false;\n    return old_len * 2 >= file_len;\n}\n","zig","",[42,366,367,376,382,429,458,481],{"__ignoreMap":364},[368,369,372],"span",{"class":370,"line":371},"line",1,[368,373,375],{"class":374},"sJ8bj","\u002F\u002F\u002F replace_text mit `old` = (fast) ganzer Datei ist ein verkapptes Überschreiben und\n",[368,377,379],{"class":370,"line":378},2,[368,380,381],{"class":374},"\u002F\u002F\u002F braucht dieselbe Bestätigung wie write_file auf eine bestehende Datei.\n",[368,383,385,389,392,396,400,404,407,410,413,416,418,420,423,426],{"class":370,"line":384},3,[368,386,388],{"class":387},"szBVR","pub",[368,390,391],{"class":387}," fn",[368,393,395],{"class":394},"sScJk"," replaceCountsAsRewrite",[368,397,399],{"class":398},"sVt8B","(",[368,401,403],{"class":402},"s4XuR","file_len",[368,405,406],{"class":398},": ",[368,408,409],{"class":387},"usize",[368,411,412],{"class":398},", ",[368,414,415],{"class":402},"old_len",[368,417,406],{"class":398},[368,419,409],{"class":387},[368,421,422],{"class":398},") ",[368,424,425],{"class":387},"bool",[368,427,428],{"class":398}," {\n",[368,430,432,435,438,440,443,447,449,452,455],{"class":370,"line":431},4,[368,433,434],{"class":387},"    if",[368,436,437],{"class":398}," (",[368,439,403],{"class":402},[368,441,442],{"class":387}," ==",[368,444,446],{"class":445},"sj4cs"," 0",[368,448,422],{"class":398},[368,450,451],{"class":387},"return",[368,453,454],{"class":387}," false",[368,456,457],{"class":398},";\n",[368,459,461,464,467,470,473,476,479],{"class":370,"line":460},5,[368,462,463],{"class":387},"    return",[368,465,466],{"class":402}," old_len",[368,468,469],{"class":387}," *",[368,471,472],{"class":445}," 2",[368,474,475],{"class":387}," >=",[368,477,478],{"class":402}," file_len",[368,480,457],{"class":398},[368,482,484],{"class":370,"line":483},6,[368,485,486],{"class":398},"}\n",[14,488,489,490,493],{},"Il en va de même pour les chemins. Au lieu de dire au modèle de rester dans\nle projet, une fonction résout le chemin et ne renvoie rien s’il se trouve à\nl’extérieur. Un chemin qui sort du projet via ",[42,491,492],{},".."," ou en absolu ne peut alors\nproduire aucune opération sur un fichier.",[14,495,496],{},"De telles règles sont des fonctions pures, et les fonctions pures se testent.\nLes deux ont des tests unitaires. S’y ajoute un script de bout en bout qui\nfait tourner l’éditeur en headless contre le vrai modèle et vérifie le chemin\ndu refus : demander un écrasement, attendre la boîte de dialogue, refuser,\nfichier inchangé, et un chemin hors du projet est rejeté. Une ligne de prompt,\non ne peut que l’espérer.",[14,498,499,500,503],{},"À l’inverse, il en va de même pour les capacités. L’outil ",[42,501,502],{},"command"," reçoit\nses choix générés à partir de l’énumération des commandes de l’éditeur. Chaque\nmenu et chaque raccourci clavier est ainsi automatiquement accessible à\nl’agent, sans qu’une deuxième liste doive être tenue à jour quelque part.",[14,505,506],{},"La question de savoir dans quelle fenêtre apparaît un fichier ouvert par\nl’agent n’est pas non plus une instruction au modèle, mais une fonction avec\nun test unitaire. Le prompt système s’est réduit à trois phrases : le rôle,\nla convention selon laquelle les chemins s’entendent relativement au projet,\net la demande de répondre brièvement dans la langue de l’utilisateur après\nles résultats des outils. Tout le reste est dans le code, où c’est vérifié,\nau lieu d’être dans le prompt, où c’est demandé.",[21,508,510],{"id":509},"ce-qui-reste-ouvert","Ce qui reste ouvert",[14,512,513],{},"Il y a des limites qui restent en place volontairement. L’éditeur charge\nexactement une graisse de police, c’est pourquoi la vue Markdown affiche le\ngras et l’italique par des couleurs au lieu de vraies variantes de police. Ces\npoints figurent comme décisions dans la documentation du projet, pas comme\ntâches ouvertes.",[21,515,517],{"id":516},"où-en-est-le-projet-aujourdhui","Où en est le projet aujourd’hui",[14,519,520],{},"Sur les trois exigences de l’introduction, deux sont remplies : les PDF et\nles images s’ouvrent dans un onglet, le terminal est intégré. Git n’est là\nqu’à moitié. L’explorateur affiche le statut de chaque fichier, diff et blame\nmanquent encore. Se sont ajoutées des choses qui n’étaient pas sur la liste :\naperçu Markdown, diapositives à partir de Markdown avec export en PDF, saut à\nla définition via un serveur de langage, ouverture rapide et palette de\ncommandes, et l’agent. Il est compilé pour Linux et Windows.",[14,522,523,524,531],{},"Le code source est ouvert : ",[525,526,530],"a",{"href":527,"rel":528},"https:\u002F\u002Fgithub.com\u002Fgstrainovic\u002Fzid",[529],"nofollow","github.com\u002Fgstrainovic\u002Fzid",".\nLes onze tentatives précédentes sont également publiques, chacune sous son nom\ndans le tableau.",[14,533,534,535,539],{},"Comment l’agent pilote et vérifie l’éditeur sans fenêtre, c’est l’objet du\nprochain article : ",[525,536,538],{"href":537},"\u002Ffr\u002Fopen-source\u002Fki-testet-ohne-fenster\u002F","Un agent IA teste mon éditeur sans fenêtre",".",[541,542,543],"style",{},"html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":364,"searchDepth":378,"depth":378,"links":545},[546,547,548,549,550,551,552,553],{"id":23,"depth":378,"text":24},{"id":33,"depth":378,"text":34},{"id":74,"depth":378,"text":75},{"id":92,"depth":378,"text":93},{"id":306,"depth":378,"text":307},{"id":334,"depth":378,"text":335},{"id":509,"depth":378,"text":510},{"id":516,"depth":378,"text":517},"De VS Code aux éditeurs en terminal, puis de gpui à Zig. Onze tentatives et pourquoi les limites des outils d’un agent IA ont leur place dans le code.","md",{},true,"\u002Fopen-source\u002Feditor-suche",null,{"title":5,"description":554},"open-source\u002Feditor-suche","2-B1PVKqPlvKljB-qZzdc-0I9bvMg2tTEchra-Rl_6Y",1790517212992]