turbo-editors/turbo-corepublic Fork 0
v0.9.0
Commits
Clone
git clone https://git.rickub.com/turbo-editors/turbo-core.git
git clone ssh://git@rickub.com/turbo-editors/turbo-core.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

🛟 Updated. 28d5985 · on v0.9.0 · k33g · 11h ago
2026-09-18-first-launch-lsp.md · 34 lines · 3.2 KBmarkdown
Blame HistoryOpen raw

Handoff — 2026-09-18 — first launch: LSP dead until save + relaunch → fixed

State

The user reported that on turbo-go's first launch the LSP does not work, and that saving, quitting and relaunching fixes it. Diagnosed by reading the code, confirmed by the user (the window had started Untitled), and fixed in this session through a full /methodical-dev cycle. Not committed.

The defect: a buffer that gains its path through Save As was never announced to the language server. Language.DidOpen skips path "" (an Untitled window has nothing to announce), afterSave sent only didSave, and announceOpenDocuments runs once (a.announced) — and a server ignores didChange/didSave for a document it was never told is open, so every LSP feature stayed dead in that window until a restart reopened the file under its name.

The fix, all in app:

  • app/language.go — the documents map is keyed by absolute path (pathKey, the renamed diagnosticKey), so Knows does not depend on how a path is spelt. Without this, the next change would re-announce a document opened relative and saved absolute.
  • app/actions_file.goannounceSaved, called from afterSave so both save paths get it: didOpen for a path the server does not Knows, didSave otherwise. And save remembers the buffer's path before SaveAs rewrites it; renamed (absolute comparison) triggers DidClose of the old document on a genuine rename, so no ghost document stays open on the server.

Tests: 5 new (one in app/diagnostics_test.go, four in the new app/save_test.go), each verified by breaking the code it covers. Suite green, -race green on app. Quality run #30 PASS 0/0/0, complexity 2308. Docs EN + FR: reference/app.md (a Knows row, a paragraph on what saving sends), how-to/talk-to-a-language-server.md (a "file created inside the editor" variant), plus app/README.md.

In flight

Nothing half-written. The tree also still carries the uncommitted Windows terminal work of 2026-09-17 (disjoint files — see that day's handoff); this session's change sits on top of it on main.

Next steps

  1. Commit (the user does this, or asks for it): the fix, its tests, the docs, and .memory/.
  2. Drive it once against a real gopls: start turbo-go, type into an Untitled window, save as x.go, complete — the one verification the fake server cannot give.
  3. Tag and release turbo-core, then re-pin the six editors so they inherit the fix.

Open questions / blockers

  • The user's report ("la 1ère fois") may also have had a second ingredient: gopls's very first run on a machine populates its caches and answers empty completions while loading, even once LSP: ready shows. Unmeasured; worth remembering if a report comes back after this fix ships.

Watch out for

  • Do not move the announcement into Buffer.SaveAs: it would put the language server underneath the text type. Rejected on purpose.
  • Autosave never renames (it rewrites the buffer's own path), which is why DidClose lives in save alone. If autosave ever learns to rename, that assumption breaks.
  • waitForMethod in the app tests polls up to 2 s; a test asserting a notification was not sent must wait for a later one first (see TestSaveAsToTheSamePathClosesNothing), or it asserts on a race.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# Handoff — 2026-09-18 — first launch: LSP dead until save + relaunch → fixed

## State

The user reported that on turbo-go's first launch the LSP does not work, and that saving, quitting and relaunching fixes it. Diagnosed by reading the code, confirmed by the user (the window had started **Untitled**), and **fixed in this session** through a full `/methodical-dev` cycle. Not committed.

The defect: a buffer that gains its path through Save As was never announced to the language server. `Language.DidOpen` skips path `""` (an Untitled window has nothing to announce), `afterSave` sent only `didSave`, and `announceOpenDocuments` runs once (`a.announced`) — and a server ignores `didChange`/`didSave` for a document it was never told is open, so every LSP feature stayed dead in that window until a restart reopened the file under its name.

The fix, all in `app`:

- `app/language.go` — the `documents` map is keyed by absolute path (`pathKey`, the renamed `diagnosticKey`), so `Knows` does not depend on how a path is spelt. Without this, the next change would re-announce a document opened relative and saved absolute.
- `app/actions_file.go``announceSaved`, called from `afterSave` so both save paths get it: `didOpen` for a path the server does not `Knows`, `didSave` otherwise. And `save` remembers the buffer's path before `SaveAs` rewrites it; `renamed` (absolute comparison) triggers `DidClose` of the old document on a genuine rename, so no ghost document stays open on the server.

Tests: 5 new (one in `app/diagnostics_test.go`, four in the new `app/save_test.go`), each verified by breaking the code it covers. Suite green, `-race` green on `app`. Quality run #30 PASS 0/0/0, complexity 2308. Docs EN + FR: `reference/app.md` (a `Knows` row, a paragraph on what saving sends), `how-to/talk-to-a-language-server.md` (a "file created inside the editor" variant), plus `app/README.md`.

## In flight

Nothing half-written. The tree also still carries the **uncommitted Windows terminal work of 2026-09-17** (disjoint files — see that day's handoff); this session's change sits on top of it on `main`.

## Next steps

1. Commit (the user does this, or asks for it): the fix, its tests, the docs, and `.memory/`.
2. Drive it once against a real gopls: start turbo-go, type into an Untitled window, save as `x.go`, complete — the one verification the fake server cannot give.
3. Tag and release turbo-core, then re-pin the six editors so they inherit the fix.

## Open questions / blockers

- The user's report ("la 1ère fois") may also have had a second ingredient: gopls's very first run on a machine populates its caches and answers empty completions while loading, even once `LSP: ready` shows. Unmeasured; worth remembering if a report comes back after this fix ships.

## Watch out for

- Do not move the announcement into `Buffer.SaveAs`: it would put the language server underneath the text type. Rejected on purpose.
- Autosave never renames (it rewrites the buffer's own path), which is why `DidClose` lives in `save` alone. If autosave ever learns to rename, that assumption breaks.
- `waitForMethod` in the app tests polls up to 2 s; a test asserting a notification was *not* sent must wait for a later one first (see `TestSaveAsToTheSamePathClosesNothing`), or it asserts on a race.