# turbo-python — summary A snapshot of the present. Edited in place; the history is in `history.md`. ## What this is A Turbo C-style terminal IDE for Python, written in Go, built on **[turbo-core](https://rickub.com/turbo-editors/turbo-core)** — the library Turbo Go and Turbo Rust already share. This repository holds the command, the profile that says the editor is for Python, and the Python scanner. Everything else — the event loop, the windows, the dialogs, the themes, the LSP client, the terminal emulator, the project tree, the snippets and tools machinery — is the library's, and none of it is copied here. Module `rickub.com/turbo-editors/turbo-python`, `require`ing turbo-core **v0.4.1** from the module proxy with **no active `replace`**. The commented-out `replace` at the bottom of `go.mod` documents the escape hatch without being one; `01-release.tag.sh` refuses to tag a release whose `go.mod` carries a live one. ## Layout | | | | --- | --- | | `main.go` | flags, the terminal, `pythonlang.Register()`, the profile, the loop | | `internal/pythonlang/pythonlang.go` | `Name`, `Slug`, `Language`, `Profile()`, `Register()`, the four server directories | | `internal/pythonlang/scan.go` | the scanner's dispatcher, comments, decorators | | `internal/pythonlang/literals.go` | strings — all six prefixes, both triple quotes, line continuations | | `internal/pythonlang/words.go` | numbers, keywords, soft keywords, the name-shape rules | | `internal/pythonlang/*.toml.tmpl` | the three starter files, embedded by `templates.go` | | `diagram_test.go` | holds `docs/diagrams/packages.drawio` to `go list` | | `docs/{en,fr}/` | 33 pages each (README included), Diátaxis | ## How to build, test and measure ```bash make check # fmt, vet, then the whole suite — what a commit should pass make build # into bin/turbo-python, then check the binary reports its version make install # build, install onto PATH, report what it found go test ./... # 129 tests; the pylsp ones skip themselves without a server ``` Quality gate, separate from the tests: ```bash python3 ~/.claude/skills/quality/scripts/quality_report.py --workspace . ``` To build against a turbo-core you have changed but not released: ```bash go work init . ../turbo-core go list -f '{{.Dir}}' rickub.com/turbo-editors/turbo-core/app # must NOT be under pkg/mod ``` `go.work` and `go.work.sum` are gitignored. **Everything still builds and still passes** while testing the published library instead of your changes, so run that second line. ## Decisions in force - **The toolchain menu is `~P~ython`, not `uv`.** `P` is free — the fixed menus take F, E, S, R, C, O, W, N and H. Named after the language because the menu holds whatever the project put in its tools file, and a menu called `uv` holding `docker compose up` is a lie about what the menu is. - **The language server is `pylsp`, and the install hint says `pipx install "python-lsp-server[all]"`.** The `[all]` is load-bearing: installed bare, pylsp starts, completes and jumps, and publishes an **empty** diagnostics list for a file that does not parse, because the linters are extras. A gutter blank for want of a linter looks exactly like a gutter blank for want of a bug. - **pylsp answers seven of turbo-core's nine questions.** It advertises neither `implementation` nor `workspace/symbol`, so those two items report nothing found. That is documented rather than worked around, and a test asserts it so a future pylsp gaining them is noticed. - **`Profile()` is a function, not a variable**, because `Server.Dirs` reads the environment, and a variable would freeze whatever `VIRTUAL_ENV` said at link time — for a Python tool, the one value most likely to change between two runs in the same shell. - **Four places are searched for the server besides `PATH`**: the active virtual environment's `bin`, `~/.local/bin`, pyenv's shims, and every `~/Library/Python//bin` that exists. The last is read rather than guessed, and is on nobody's `PATH` on macOS. - **Root markers, in order: `pyproject.toml`, `setup.py`, `setup.cfg`.** - **A name wholly in capitals is a constant; any other capitalised name is a type.** Turbo Rust has only the second rule and documents `SCREAMING_SNAKE_CASE` as a known wrong answer; PEP 8 separates the two conventions well enough that the wrong answer was worth removing rather than inheriting. The cost is a class named `HTTP`. - **A capitalised name is a type even when it is called.** `ValueError("nope")` and `parse("nope")` are the same shape. This is the one rule Turbo Python and Turbo Rust order differently, on purpose. - **`self` and `cls` are coloured as builtins** although the language does not name them. - **An f-string is one string span**, `{…}` included. Finding where an expression ends inside a literal needs a parser; this is a scanner. - **The starter templates are embedded files, not Go constants**, with the `.tmpl` suffix because `settings.toml.tmpl` holds `theme = %q`, which is not valid TOML. - **`autosave = true` in the starter settings file, `false` in `settings.Default()`.** Two statements in two places on purpose: a project that created a settings file has said what it wants; a directory somebody merely started the editor in has not. - **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`. - **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. ## State as of 2026-09-03 - **Complete and green.** `go test ./...` passes; `gofmt -l` and `go vet` are clean. - **Quality gate: PASS.** 0 errors, 0 warnings, 0 smells; total complexity 91. - **Documentation**: 33 files × EN + FR (32 pages plus each language's README), a `README.md` at the root, and `docs/diagrams/packages.drawio` checked against `go list` by `diagram_test.go`. - **Verified in a real pty**, not only by tests: the Python menu under `Alt-P` with `Create tools file` available (`30;42`) and `Open tools file` greyed (`90;47`); `def` `97;44;1`, a called name `93;44;1`, `print` `96;44;1`, strings `92;44`, comments `38;2;143;143;143`; a triple-quoted f-string carried across a line break; the About box reading **"A Turbo C-style editor for Python, / written in Go."**; `Ctrl-Y` deleting a line; the theme dialog opening on the current theme; and — the one that matters — **a file that does not parse, opened by a relative path, showing `×` in the gutter and `⚠ invalid syntax` on the status bar**, which is the failure mode Turbo Go shipped with for months. - **The server is found outside `PATH`.** Verified by running the binary with `PATH` stripped to `/usr/bin:/bin`: it still said `LSP: ready`, from `~/.local/bin`. With `HOME` moved too it says `LSP: no pylsp — pipx install "python-lsp-server[all]"`. - **The editor is registered in the family.** turbo-core's `README.md`, both doc `README`s, `profile/profile.go`'s package comment, the release and workspace how-tos in both languages, both architecture explanations and `.memory/summary.md` now count three editors. turbo-core's `.go` strings were swept for hardcoded language names; every hit was a false positive, a doc-comment example, or a true statement about the implementation. - **Released as `v0.1.0`**, tagged and pushed at `bccbb0b` on `main`, working tree clean. turbo-core was tagged `v0.4.1` the same day — a docs-and-comment release, no behaviour change — and all three editors now pin it. ## Not yet established - **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. - **Never run on macOS or Windows.** Everything here was verified on Linux. The user's machine is macOS, so a binary built in this sandbox is an ELF they cannot run. - **No CI.** There is no pipeline configuration in the repository. - **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. - **Performance on a large file is unmeasured.** The scanner is a line at a time and carries three pieces of state, but nothing has been timed. - **Only pylsp has been tried.** pyright and ruff-lsp would both fit the profile; neither has been pointed at. ## State as of 2026-09-19 — moved to Rickub, released by a workflow - **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. - **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. - **`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`.