| 📦 Turbo Go 3d7798b k33g 10h ago | 1 | # Architecture — explanation |
| 2 | |
| 3 | ## What is this about? |
| 4 | |
| 5 | Turbo Go is a command, a profile and a scanner. Everything else — the editing widget, the windows, the menus, the dialogs, the themes, the terminal emulator, the file tree, the LSP client — is [turbo-core](https://rickub.com/turbo-editors/turbo-core), the library every Turbo editor is built on. |
| 6 | |
| 7 | That was not always true. Turbo Go was a single program of about eleven and a half thousand lines in fourteen packages until a second editor was wanted, at which point the fourteen moved into a library and one stayed here. This page is about the split that resulted. |
| 8 | |
| 9 | ## What is in this repository |
| 10 | |
| 11 | ``` |
| 12 | main.go flags, the terminal, and the wiring |
| 13 | internal/golang the whole of what makes this Turbo Go |
| 14 | golang.go the profile: name, menu, server, root marker |
| 15 | scan.go the Go scanner, on top of go/scanner |
| 16 | templates.go three //go:embed declarations |
| 17 | *.toml.tmpl the three starter files a project gets, embedded |
| 18 | ``` |
| 19 | |
| 20 | About four hundred lines. There is no `internal/app`, no `internal/ui`, no `internal/buffer` — those exist once, in the library, and every editor built on it uses them unchanged. |
| 21 | |
| 22 | ## What `main` does |
| 23 | |
| 24 | Six things, in this order: |
| 25 | |
| 26 | 1. Parses the flags. |
| 27 | 2. Calls `golang.Register()`, which teaches the library to colour `.go` files. |
| 28 | 3. Builds `golang.Profile()` — the value that says this editor is Turbo Go. |
| 29 | 4. Reads `.turbo-go/settings.toml` from the working directory, if there is one. |
| 30 | 5. Opens the terminal and hands the screen, the theme name and the profile to `app.New`. |
| 31 | 6. Starts gopls in the module root, and runs the event loop. |
| 32 | |
| 33 | That is the whole command. Every decision it makes — which theme wins, which files to open, whether to start a language server — is about *this run*, not about Go. |
| 34 | |
| 35 | ## The profile is the seam |
| 36 | |
| 37 | ```go |
| 38 | profile.Profile{ |
| 39 | Name: "Turbo Go", |
| 40 | Slug: "turbo-go", |
| 41 | Language: "Go", |
| 42 | ToolsMenu: "~G~o", |
| 43 | RootMarkers: []string{"go.mod"}, |
| 44 | Server: profile.Server{Command: "gopls", Args: []string{"serve"}, …}, |
| 45 | Templates: profile.Templates{Settings: …, Snippets: …, Tools: …}, |
| 46 | } |
| 47 | ``` |
| 48 | |
| 49 | Everything that used to be a hardcoded `"turbo-go"`, `"gopls"` or `"go.mod"` somewhere in eleven thousand lines is one field here. The library reads them; nothing in the library knows what any of them mean. |
| 50 | |
| 51 | `Slug` carries more than it looks. The binary is `turbo-go`, the project directory is `.turbo-go`, the user's own configuration lives in `~/.config/turbo-go`, and the environment variables that override it are `TURBO_GO_THEME_DIR` and `TURBO_GO_SNIPPET_DIR` — all derived from that one word. Those names are unchanged by the refactoring, deliberately: somebody who set `TURBO_GO_THEME_DIR` did so against a released binary. |
| 52 | |
| 53 | ## Why the Go scanner is here and not in the library |
| 54 | |
| 55 | turbo-core colours eight languages itself: TOML, YAML, Markdown, JavaScript, HTML, XML, Dockerfiles and shell. Those are the ones every editor meets whatever it is for — a project's configuration is TOML or YAML, its documentation is Markdown, its scripts are shell, its image build a Dockerfile. |
| 56 | |
| 57 | Go is not one of them. The language that *defines* an editor is registered by that editor, which is why a `.rs` file opens as plain text here and a `.go` file opens as plain text in Turbo Rust. |
| 58 | |
| 59 | The Go scanner is also the one that is *least* like the others. Every language in the library is scanned a line at a time with `syntax.LineScanner`; Go goes through `go/scanner`, the lexer the Go toolchain itself uses, and converts its byte offsets with `syntax.LineIndex`. That the library supports both shapes is because of this scanner. |
| 60 | |
| 61 | ## What moved, and what did not |
| 62 | |
| 63 | | Was | Is | |
| 64 | | --- | --- | |
| 65 | | `internal/buffer`, `internal/ui`, `internal/editor`, … | `turbo-core/buffer`, `turbo-core/ui`, `turbo-core/editor`, … | |
| 66 | | `internal/syntax` — six languages | `turbo-core/syntax` — eight, plus a registry; Go lives here | |
| 67 | | `internal/app` with a `Name` constant | `turbo-core/app` taking a `profile.Profile` | |
| 68 | | `internal/lsp` hardcoding gopls | `turbo-core/lsp` taking a `profile.Server` | |
| 69 | | `moduleRoot` in `main.go` | `app.ProjectRoot(p, files)`, with `go.mod` in the profile | |
| 70 | | `settings.DirName = ".turbo-go"` | `p.ProjectDir()` | |
| 71 | |
| 72 | **Nothing about the editor's behaviour changed.** The menus, the keys, the themes, the file formats and the environment variables are what they were. What changed is where the code lives. |
| 73 | |
| 74 | ## Why a library rather than a fork |
| 75 | |
| 76 | The alternative to extracting turbo-core was copying Turbo Go and changing the Go bits. It was rejected before it was started: two copies of eleven thousand lines drift within a month, and every fix has to be made twice by somebody who remembers there are two. |
| 77 | |
| 78 | The cost, accepted: a change to a menu now affects every editor at once, and Turbo Go can no longer make a decision that suits only Go without either putting it in the profile or arguing for it in the library. That is a real constraint, and it is the one that keeps the editors the same editor. |
| 79 | |
| 80 | ## How it relates to the rest |
| 81 | |
| 82 | - What each of the library's packages does: [turbo-core's package reference](https://rickub.com/turbo-editors/turbo-core/blob/main/docs/en/reference/packages.md) |
| 83 | - How the colouring works here: [Colouring and completion](colouring-and-completion.md) |
| 84 | - Why the tools menu is data: [Go tools](go-tools.md) |
| 85 | - The decisions that outlived the refactoring: [Design decisions](design-decisions.md) |