| 📦 Turbo Python 6fc62ea k33g 10h ago | 1 | # turbo-python — summary |
| 2 | |
| 3 | A snapshot of the present. Edited in place; the history is in `history.md`. |
| 4 | |
| 5 | ## What this is |
| 6 | |
| 7 | A Turbo C-style terminal IDE for Python, written in Go, built on |
| 8 | **[turbo-core](https://rickub.com/turbo-editors/turbo-core)** — the library Turbo Go and |
| 9 | Turbo Rust already share. This repository holds the command, the profile that says the |
| 10 | editor is for Python, and the Python scanner. Everything else — the event loop, the |
| 11 | windows, the dialogs, the themes, the LSP client, the terminal emulator, the project tree, |
| 12 | the snippets and tools machinery — is the library's, and none of it is copied here. |
| 13 | |
| 14 | Module `rickub.com/turbo-editors/turbo-python`, `require`ing turbo-core **v0.4.1** from |
| 15 | the module proxy with **no active `replace`**. The commented-out `replace` at the bottom of |
| 16 | `go.mod` documents the escape hatch without being one; `01-release.tag.sh` refuses to tag a |
| 17 | release whose `go.mod` carries a live one. |
| 18 | |
| 19 | ## Layout |
| 20 | |
| 21 | | | | |
| 22 | | --- | --- | |
| 23 | | `main.go` | flags, the terminal, `pythonlang.Register()`, the profile, the loop | |
| 24 | | `internal/pythonlang/pythonlang.go` | `Name`, `Slug`, `Language`, `Profile()`, `Register()`, the four server directories | |
| 25 | | `internal/pythonlang/scan.go` | the scanner's dispatcher, comments, decorators | |
| 26 | | `internal/pythonlang/literals.go` | strings — all six prefixes, both triple quotes, line continuations | |
| 27 | | `internal/pythonlang/words.go` | numbers, keywords, soft keywords, the name-shape rules | |
| 28 | | `internal/pythonlang/*.toml.tmpl` | the three starter files, embedded by `templates.go` | |
| 29 | | `diagram_test.go` | holds `docs/diagrams/packages.drawio` to `go list` | |
| 30 | | `docs/{en,fr}/` | 33 pages each (README included), Diátaxis | |
| 31 | |
| 32 | ## How to build, test and measure |
| 33 | |
| 34 | ```bash |
| 35 | make check # fmt, vet, then the whole suite — what a commit should pass |
| 36 | make build # into bin/turbo-python, then check the binary reports its version |
| 37 | make install # build, install onto PATH, report what it found |
| 38 | go test ./... # 129 tests; the pylsp ones skip themselves without a server |
| 39 | ``` |
| 40 | |
| 41 | Quality gate, separate from the tests: |
| 42 | |
| 43 | ```bash |
| 44 | python3 ~/.claude/skills/quality/scripts/quality_report.py --workspace . |
| 45 | ``` |
| 46 | |
| 47 | To build against a turbo-core you have changed but not released: |
| 48 | |
| 49 | ```bash |
| 50 | go work init . ../turbo-core |
| 51 | go list -f '{{.Dir}}' rickub.com/turbo-editors/turbo-core/app # must NOT be under pkg/mod |
| 52 | ``` |
| 53 | |
| 54 | `go.work` and `go.work.sum` are gitignored. **Everything still builds and still passes** |
| 55 | while testing the published library instead of your changes, so run that second line. |
| 56 | |
| 57 | ## Decisions in force |
| 58 | |
| 59 | - **The toolchain menu is `~P~ython`, not `uv`.** `P` is free — the fixed menus take F, E, |
| 60 | S, R, C, O, W, N and H. Named after the language because the menu holds whatever the |
| 61 | project put in its tools file, and a menu called `uv` holding `docker compose up` is a lie |
| 62 | about what the menu is. |
| 63 | - **The language server is `pylsp`, and the install hint says `pipx install |
| 64 | "python-lsp-server[all]"`.** The `[all]` is load-bearing: installed bare, pylsp starts, |
| 65 | completes and jumps, and publishes an **empty** diagnostics list for a file that does not |
| 66 | parse, because the linters are extras. A gutter blank for want of a linter looks exactly |
| 67 | like a gutter blank for want of a bug. |
| 68 | - **pylsp answers seven of turbo-core's nine questions.** It advertises neither |
| 69 | `implementation` nor `workspace/symbol`, so those two items report nothing found. That is |
| 70 | documented rather than worked around, and a test asserts it so a future pylsp gaining them |
| 71 | is noticed. |
| 72 | - **`Profile()` is a function, not a variable**, because `Server.Dirs` reads the |
| 73 | environment, and a variable would freeze whatever `VIRTUAL_ENV` said at link time — for a |
| 74 | Python tool, the one value most likely to change between two runs in the same shell. |
| 75 | - **Four places are searched for the server besides `PATH`**: the active virtual |
| 76 | environment's `bin`, `~/.local/bin`, pyenv's shims, and every |
| 77 | `~/Library/Python/<version>/bin` that exists. The last is read rather than guessed, and is |
| 78 | on nobody's `PATH` on macOS. |
| 79 | - **Root markers, in order: `pyproject.toml`, `setup.py`, `setup.cfg`.** |
| 80 | - **A name wholly in capitals is a constant; any other capitalised name is a type.** Turbo |
| 81 | Rust has only the second rule and documents `SCREAMING_SNAKE_CASE` as a known wrong |
| 82 | answer; PEP 8 separates the two conventions well enough that the wrong answer was worth |
| 83 | removing rather than inheriting. The cost is a class named `HTTP`. |
| 84 | - **A capitalised name is a type even when it is called.** `ValueError("nope")` and |
| 85 | `parse("nope")` are the same shape. This is the one rule Turbo Python and Turbo Rust order |
| 86 | differently, on purpose. |
| 87 | - **`self` and `cls` are coloured as builtins** although the language does not name them. |
| 88 | - **An f-string is one string span**, `{…}` included. Finding where an expression ends |
| 89 | inside a literal needs a parser; this is a scanner. |
| 90 | - **The starter templates are embedded files, not Go constants**, with the `.tmpl` suffix |
| 91 | because `settings.toml.tmpl` holds `theme = %q`, which is not valid TOML. |
| 92 | - **`autosave = true` in the starter settings file, `false` in `settings.Default()`.** Two |
| 93 | statements in two places on purpose: a project that created a settings file has said what |
| 94 | it wants; a directory somebody merely started the editor in has not. |
| 95 | - **Agent windows are turbo-core's, and what belongs here is the starter file.** `acp.toml.tmpl` is the fourth embedded template, and `profile.Templates.Agents` is the whole of Turbo Python's contribution to the feature. The example agent is `docker agent serve acp .turbo-python/agent.yaml`; the only other thing about this editor in it is the sentence saying a ```python fence is coloured by the scanner this editor colours its own files with. Every other editor got agent windows the same way — one file, one line. The reasoning, and why it could not have been built here, is in `docs/*/explanation/agent-windows.md`. |
| 96 | - **The starter agents file teaches the window's keyboard as well as the format.** `Enter`, `Alt-Enter`, `Tab`, `Esc`, `Ctrl-W` and the copying keys are all in its comments, because a file the editor hands you is the one document a user is guaranteed to see. |
| 97 | |
| 98 | ## State as of 2026-09-03 |
| 99 | |
| 100 | - **Complete and green.** `go test ./...` passes; `gofmt -l` and `go vet` are clean. |
| 101 | - **Quality gate: PASS.** 0 errors, 0 warnings, 0 smells; total complexity 91. |
| 102 | - **Documentation**: 33 files × EN + FR (32 pages plus each language's README), a `README.md` at the root, and |
| 103 | `docs/diagrams/packages.drawio` checked against `go list` by `diagram_test.go`. |
| 104 | - **Verified in a real pty**, not only by tests: the Python menu under `Alt-P` with |
| 105 | `Create tools file` available (`30;42`) and `Open tools file` greyed (`90;47`); `def` |
| 106 | `97;44;1`, a called name `93;44;1`, `print` `96;44;1`, strings `92;44`, comments |
| 107 | `38;2;143;143;143`; a triple-quoted f-string carried across a line break; the About box |
| 108 | reading **"A Turbo C-style editor for Python, / written in Go."**; `Ctrl-Y` deleting a |
| 109 | line; the theme dialog opening on the current theme; and — the one that matters — |
| 110 | **a file that does not parse, opened by a relative path, showing `×` in the gutter and |
| 111 | `⚠ invalid syntax` on the status bar**, which is the failure mode Turbo Go shipped with |
| 112 | for months. |
| 113 | - **The server is found outside `PATH`.** Verified by running the binary with `PATH` |
| 114 | stripped to `/usr/bin:/bin`: it still said `LSP: ready`, from `~/.local/bin`. With `HOME` |
| 115 | moved too it says `LSP: no pylsp — pipx install "python-lsp-server[all]"`. |
| 116 | - **The editor is registered in the family.** turbo-core's `README.md`, both doc `README`s, |
| 117 | `profile/profile.go`'s package comment, the release and workspace how-tos in both |
| 118 | languages, both architecture explanations and `.memory/summary.md` now count three |
| 119 | editors. turbo-core's `.go` strings were swept for hardcoded language names; every hit was |
| 120 | a false positive, a doc-comment example, or a true statement about the implementation. |
| 121 | - **Released as `v0.1.0`**, tagged and pushed at `bccbb0b` on `main`, working tree clean. |
| 122 | turbo-core was tagged `v0.4.1` the same day — a docs-and-comment release, no behaviour |
| 123 | change — and all three editors now pin it. |
| 124 | |
| 125 | ## Not yet established |
| 126 | |
| 127 | - **Agent windows have never been opened in this editor.** The feature is turbo-core's and was driven end to end from turbo-go against a real `docker agent` and a real llama.cpp; what is here is the starter file, covered by tests that create it, load it back and check it names this editor's own language. Nobody has run `turbo-python`, pressed `Alt-A` and talked to an agent from it. |
| 128 | |
| 129 | |
| 130 | - **Never run on macOS or Windows.** Everything here was verified on Linux. The user's |
| 131 | machine is macOS, so a binary built in this sandbox is an ELF they cannot run. |
| 132 | - **No CI.** There is no pipeline configuration in the repository. |
| 133 | - **The release scripts have never been run from this sandbox.** `v0.1.0` exists, tagged by the user; `01`–`04` have only ever been read here. |
| 134 | - **Performance on a large file is unmeasured.** The scanner is a line at a time and carries |
| 135 | three pieces of state, but nothing has been timed. |
| 136 | - **Only pylsp has been tried.** pyright and ruff-lsp would both fit the profile; neither |
| 137 | has been pointed at. |
| 138 | |
| 139 | ## State as of 2026-09-19 — moved to Rickub, released by a workflow |
| 140 | |
| 141 | - **Module path `rickub.com/turbo-editors/turbo-python`**, depending on `rickub.com/turbo-editors/turbo-core v1.0.0` — the first turbo-core version published under that path (`v0.9.0` on the proxy still declares the Codeberg path and cannot be required as `rickub.com/…`). Every import, the Makefile's `VERSION_PKG`, `scripts/install.sh`, the README and the docs say `rickub.com`. `GOWORK=off make check` green. The repository on this side is a fresh `git init` with `origin` at `ssh://git@rickub.com/turbo-editors/turbo-python.git` and **no commit yet**; `01-release.tag.sh` makes the first one. |
| 142 | - **Releases are one script and one workflow**, modelled on turbo-core's and identical to turbo-go's. `01-release.tag.sh` runs `make check` under `TURBO_PYTHON_RELEASING=1`, refuses a tag taken locally or on origin (bump, never move), refuses a `replace` in `go.mod`, commits, pushes the current branch, then tags and pushes the tag. That push starts `.github/workflows/release.yml`: `go test` with `TURBO_PYTHON_RELEASING=1`, `./02-build-releases.sh "${GITHUB_REF_NAME}"`, release notes from the tag message, a run artifact, then `softprops/action-gh-release@v2` attaching `turbo-python-*`, `SHA256SUMS` and `README.md` with the job's own `GITHUB_TOKEN` — the only credential Rickub's release API accepts. **`02-release.publish.sh` and `04-release.upload-binaries.sh` are gone**; the build script is now `02-build-releases.sh`, takes the tag as `$1` (CI has no `release.env`), validates it, refuses a `replace`, and starts from an empty `release/${TAG}/`. `release.env` holds only `TAG` and `ABOUT`; `turbo-python.token.env` is read by nothing. |
| 143 | - **`release_test.go`** runs `01` for real against a throwaway bare remote (publishes, then refuses the same tag), runs `02` alone to see it refuse `v0.o.0`, and asserts the workflow's trigger, `contents: write`, `./02-build-releases.sh`, `fail_on_unmatched_files`, docs linked at the tag, no `secrets.`, and `TURBO_PYTHON_RELEASING`. The copy of the module for the clone leaves out `.git`, `bin`, `release`, `kits`, `demo`, `demos`, `*.env` and `go.work*`; children run with `GOWORK=off`. |