# Tutoriel : construire un éditeur Turbo pour votre propre langage À la fin de ce tutoriel, vous disposerez d'un IDE de terminal en plein écran — barre de menus, fenêtres déplaçables, souris, thèmes, arborescence de projet, fenêtres de terminal et coloration syntaxique — pour le langage de votre choix. Nous en construirons un pour **Zig**, et il fera environ quatre-vingts lignes de Go. Aucune connaissance de turbo-core n'est requise. Il vous faut Go 1.26 ou plus récent, et un terminal. ## Prérequis - Go 1.26 ou plus récent dans votre PATH. - Une copie de turbo-core à côté de l'endroit où vous allez travailler. Si vous n'en avez pas : ```bash git clone ssh://git@rickub.com/turbo-editors/turbo-core.git ``` ## Étape 1 — Créer le module À côté de votre copie de turbo-core, tapez : ```bash mkdir turbo-zig && cd turbo-zig go mod init example.com/turbo-zig ``` Vous devriez voir : ``` go: creating new go.mod: module example.com/turbo-zig ``` Nous avons maintenant un module vide. Nous allons le brancher sur la bibliothèque. ## Étape 2 — Dépendre de turbo-core Tapez : ```bash go mod edit -require=rickub.com/turbo-editors/turbo-core@v0.1.0 go mod edit -replace=rickub.com/turbo-editors/turbo-core=../turbo-core ``` Rien ne s'affiche. Regardez `go.mod` : ```bash cat go.mod ``` Vous devriez voir une ligne `require` et une ligne `replace` nommant turbo-core. C'est le `replace` qui fait que la copie posée à côté de vous est celle qui sera utilisée. ## Étape 3 — Dire quel éditeur c'est Créez `profile.go` : ```go package main import "rickub.com/turbo-editors/turbo-core/profile" // zig est l'éditeur que nous construisons. func zig() profile.Profile { return profile.Profile{ Name: "Turbo Zig", Slug: "turbo-zig", Language: "Zig", ToolsMenu: "~Z~ig", RootMarkers: []string{"build.zig"}, Server: profile.Server{ Command: "zls", InstallHint: "voir https://github.com/zigtools/zls", }, Templates: profile.Templates{ Settings: settingsTemplate, Snippets: snippetsTemplate, Tools: toolsTemplate, }, } } ``` Voilà la totalité de ce qui distingue cet éditeur de Turbo Go. Nous n'avons pas encore écrit les modèles — c'est l'étape suivante. ## Étape 4 — Écrire les trois fichiers de départ Créez `templates.go` : ```go package main const settingsTemplate = `# turbo-zig project settings. [editor] theme = %q autosave = false autosave_delay = %q ` const snippetsTemplate = `# turbo-zig snippets. # # Un extrait sans groupe va dans %s. # Les vôtres vivent dans : # %s [[snippet]] name = "TODO" body = "TODO: " ` const toolsTemplate = `# turbo-zig tools. [[tool]] name = "~B~uild" command = "zig build" output = "popup" [[tool]] name = "~T~est" command = "zig build test" output = "popup" [[tool]] name = "~R~un" command = "zig build run" output = "terminal" ` ``` Les trous comptent : `settingsTemplate` prend le nom du thème puis le délai de sauvegarde automatique, `snippetsTemplate` prend le nom du groupe sans nom puis le chemin des extraits de l'utilisateur. La [référence Profile](../reference/profile.md) les donne exactement. ## Étape 5 — Écrire la commande Créez `main.go` : ```go package main import ( "context" "fmt" "os" "github.com/gdamore/tcell/v2" "rickub.com/turbo-editors/turbo-core/app" ) func main() { if err := run(); err != nil { fmt.Fprintln(os.Stderr, "turbo-zig:", err) os.Exit(1) } } func run() error { screen, err := tcell.NewScreen() if err != nil { return err } if err := screen.Init(); err != nil { return err } defer screen.Fini() screen.EnableMouse() screen.EnablePaste() p := zig() editor := app.New(screen, "turbo-classic", p) files := os.Args[1:] if len(files) == 0 { editor.NewFile() } for _, file := range files { editor.Open(file) } ctx, cancel := context.WithCancel(context.Background()) defer cancel() editor.StartLanguageServer(ctx, app.ProjectRoot(p, files)) defer editor.Language().Stop(context.Background()) return editor.Run() } ``` ## Étape 6 — Le lancer Tapez : ```bash go mod tidy go run . main.go ``` Votre terminal se remplit d'un bureau bleu. En haut, vous devriez lire : ``` File Edit Search Run Options Window Snippets Zig Help ``` et une fenêtre au milieu montrant `main.go`. Appuyez sur **F10** pour ouvrir la barre de menus, les flèches pour vous y déplacer, et **Alt-X** pour sortir. Nous avons un éditeur qui marche. Tout ce qui est sur cette barre fonctionne : **F3** ouvre un fichier, **F8** ouvre une fenêtre de terminal avec un vrai shell dedans, **F9** ouvre l'arborescence du projet, **Options ▸ Theme** propose onze thèmes de couleurs. ## Étape 7 — Colorer le langage Les fichiers Zig ne sont pas encore colorés, parce que rien n'a dit à la bibliothèque ce qu'est Zig. Créez `syntax.go` : ```go package main import "rickub.com/turbo-editors/turbo-core/syntax" // registerZig enseigne à la bibliothèque un tout petit peu de Zig : ses // mots-clés, ses commentaires et ses chaînes. func registerZig() { syntax.Register(syntax.Definition{ Language: "zig", Extensions: []string{".zig"}, Highlight: highlightZig, }) } var zigKeywords = map[string]bool{ "const": true, "var": true, "fn": true, "pub": true, "return": true, "if": true, "else": true, "while": true, "for": true, "struct": true, "try": true, "defer": true, "test": true, "comptime": true, } func highlightZig(src string) [][]syntax.Span { return syntax.ScanLines(src, func(line []rune, carry struct{}) ([]syntax.Span, struct{}) { s := syntax.NewLineScanner(line) for !s.AtEnd() { switch r := s.Peek(0); { case r == ' ' || r == '\t': s.SkipSpaces() case s.HasPrefix(0, "//"): s.TakeRest(syntax.ClassComment) case r == '"': syntax.TakeQuoted(s, '"', syntax.ClassString) case syntax.IsDigit(r): s.TakeWhile(syntax.ClassNumber, syntax.IsWordRune) case syntax.IsLetter(r) || r == '_': start := s.Pos() for !s.AtEnd() && syntax.IsWordRune(s.Peek(0)) { s.Advance(1) } class := syntax.ClassIdentifier if zigKeywords[string(line[start:s.Pos()])] { class = syntax.ClassKeyword } s.Emit(start, s.Pos(), class) default: s.Advance(1) } } return s.Spans(), carry }) } ``` Ajoutez une ligne à `run`, juste au-dessus de `p := zig()` : ```go registerZig() ``` ## Étape 8 — Le voir coloré Écrivez un fichier Zig et ouvrez-le : ```bash cat > hello.zig <<'EOF' // A greeting. const std = @import("std"); pub fn main() void { std.debug.print("hello {d}\n", .{42}); } EOF go run . hello.zig ``` `const`, `pub`, `fn` sont dessinés dans la couleur des mots-clés, `// A greeting.` dans celle des commentaires, `"hello {d}\n"` dans celle des chaînes et `42` dans celle des nombres. Appuyez sur **Alt-X** pour sortir. ## Et maintenant ? Vous avez un éditeur. Ce qui lui manque encore, c'est un vrai analyseur — celui ci-dessus connaît quatorze mots-clés et ignore tout des chaînes multilignes `\\` et des littéraux de caractère. - Pour en écrire un correct → [Ajouter un langage](../how-to/add-a-language.md) - Pour faire marcher la complétion → [Dialoguer avec un serveur de langage](../how-to/talk-to-a-language-server.md) - Pour voir tous les champs que vous avez sautés → [Référence Profile](../reference/profile.md) - Pour comprendre pourquoi la bibliothèque a cette forme → [Architecture](../explanation/architecture.md)