| 📦 Turbo Go 3d7798b k33g 12h ago | 1 | # Handoff — 2026-08-31 — snippets, and submenus in the menu bar |
| 2 | |
| 3 | ## State |
| 4 | |
| 5 | **Ticket 0006 is done and green**, on branch `feature/snippets`, **uncommitted**. |
| 6 | |
| 7 | `Alt-N` opens a **Snippets** menu built from `.turbo-go/snippets.toml` and the user's own file, grouped into submenus and filtered by the language of the front window. Choosing one inserts it at the cursor, re-indented to the line it landed on, as one undo step. **Snippets ▸ Create snippets file** writes a commented starter file and opens it. |
| 8 | |
| 9 | ``` |
| 10 | File Edit Search Run Options Window Snippets Help |
| 11 | ┌───────────────┐┌──────────────────────┐ |
| 12 | │ if err != nil ││ Go ▶ │ |
| 13 | │ table test ││ General ▶ │ |
| 14 | └───────────────┘├──────────────────────┤ |
| 15 | │ Create snippets file │ |
| 16 | └──────────────────────┘ |
| 17 | ``` |
| 18 | |
| 19 | - `ui` gained **one level of submenus** (`MenuItem.Items`) and `Menu.OnOpen`. `menu.go` was split four ways: `menu.go` (types and state), `menu_draw.go`, `menu_events.go`, `submenu.go`. |
| 20 | - New `internal/snippets` (83.8 %), `editor.InsertSnippet` (editor 96.2 %), `internal/app/snippets.go`. |
| 21 | - Whole suite green under `-race`. Quality gate **PASS**: 0/0/0, complexity 1480. |
| 22 | - Docs complete in EN and FR — three new pages each, five existing pages updated each. `docs/diagrams/packages.drawio` gained `snippets` and three edges, re-checked against `go list` (34 edges each side). |
| 23 | - **Verified in a real terminal** through the project's own VT emulator: the menu, the submenu (which flipped left for want of room), the file creation, and a four-line snippet inserted with correct indentation. |
| 24 | |
| 25 | Earlier the same day, PR #5 merged the Markdown/JavaScript/HTML/shell colouring. |
| 26 | |
| 27 | ## In flight |
| 28 | |
| 29 | Nothing. Finished through Phase 8. |
| 30 | |
| 31 | ## Next steps |
| 32 | |
| 33 | 1. **Commit and open the PR.** `feature/snippets` is the branch. |
| 34 | 2. **Try it with thirty snippets.** The submenu geometry was verified with two groups of two. A group with twenty items is taller than the terminal, and nothing scrolls a menu panel — see the blockers below. |
| 35 | 3. **Tickets.** 0002, 0003, 0006, 0007, 0009, 0013 and 0014 are all implemented and all still `state: open`. |
| 36 | |
| 37 | ## Open questions / blockers |
| 38 | |
| 39 | - **A menu panel does not scroll.** `dropdownBounds` and `submenuBounds` are `len(items)+2` tall with no cap, so a group of thirty snippets draws a panel taller than the terminal and the bottom is simply clipped by the painter. This existed before — the Window menu has never been long enough to hit it — and snippets are the first thing that can. It wants either a scrolling panel or a "…" overflow item, and it is a `ui` feature to decide on rather than something to slip in. |
| 40 | - **Placeholders and tab stops are absent** (`${1:name}`, moving between them). Deliberate: a second feature with state to maintain across edits. The `body` is inserted verbatim apart from indentation. |
| 41 | - **Nothing rebuilds the menu bar itself.** `OnOpen` refills a menu's items, but the *set of menus* is fixed at start-up. That is fine today; a feature wanting to add or remove a whole menu at runtime would need more. |
| 42 | |
| 43 | ## Watch out for |
| 44 | |
| 45 | - **Two menus sharing a hot key silently make one unreachable.** `handleClosedKey` returns on the first match, so `~S~nippets` beside `~S~earch` meant `Alt-S` never reached Snippets — and every test passed. It was found by driving the real binary. `TestNoTwoMenusShareAHotKey` covers it now; keep it, and check it when adding a menu. |
| 46 | - **Flipping a submenu left does not fit a panel wider than the terminal.** The width is capped to the screen as well, and long labels are clipped by the painter. `TestASubmenuFlipsLeftWhenThereIsNoRoomOnTheRight` failed exactly on this before the cap. |
| 47 | - **`MenuItem.enabled()` must treat a submenu as choosable.** It used to return false for an item with no `Action`, which made every branch disabled and every submenu unreachable. `TestAnItemWithNoActionButASubmenuCanStillBeChosen` pins it. |
| 48 | - **`SetText` marks a buffer modified**, so `Modified()` cannot show that an editor operation did nothing. Use `Revision()` — `TestInsertingAnEmptySnippetChangesNothing` was wrong until it did. |
| 49 | - **The snippets tests must set `TURBO_GO_SNIPPET_DIR`.** Without it they read the snippets of whoever is running them, and pass or fail by accident. Every test in `internal/snippets` and `internal/app` that touches snippets sets it to an empty temp directory. |
| 50 | - **The template's bodies contain the two characters `\` and `t`, not a tab.** TOML interprets the escape on read. `TestTheCreatedFilesTabsSurviveTOML` checks the tab comes back out; a "helpful" replacement with a real tab would leave the template at the mercy of whatever a reader's editor does with tabs. |