turbo-editors/turbo-pythonpublic Fork 0
v1.0.2
Commits
Clone
git clone https://git.rickub.com/turbo-editors/turbo-python.git
git clone ssh://git@rickub.com/turbo-editors/turbo-python.git

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

📦 Turbo Python 6fc62ea · on v1.0.2 · k33g · 10h ago
project-settings.md · 68 lines · 6.8 KBmarkdown
Blame HistoryOpen raw

Project settings — explanation

What is this about?

A project can keep a .turbo-python/settings.toml beside its code, saying which theme to use and whether to save files automatically. This page is about the decisions inside that sentence: why the file is only ever looked for in one place, why creating it is a menu item rather than something that happens by itself, and why automatic saving works the way it does.

Why the directory is not searched for upwards

pyproject.toml is found by walking up from the file you opened until one turns up, and the language server uses exactly that. The settings file deliberately does not.

The walk is right for pyproject.toml because a module has a real boundary: the file is either above you or it is not, and being inside a module is a fact about the code. "The project" is not a fact about the code. It is where you decided to start working, and the same directory tree can be several projects depending on what you are doing in it — a monorepo's services/api is a project when you are working on the API and part of a larger one when you are not.

A walk would also make the setting act at a distance. You open a file, and the editor's colours change because of a file three directories up that you did not know existed. Every explanation of that behaviour has to start with "well, it searches upwards", and the rule you would rather be able to state is the one that is now true: the project is the directory you started the editor in.

The cost is real and worth naming. Start the editor from internal/app and the project's theme does not apply. The answer is to start from the project root, which is where you would run go build and git anyway.

Why creating the file is a menu item

The alternative was tempting: the first time you pick a theme, write .turbo-python/settings.toml so the choice sticks. Every editor that stores workspace state does something like it.

It was rejected because it puts a directory into someone's repository as a side effect of trying a colour. The user is one git status away from a change they did not make, in a project that may not be theirs, possibly in a review. A theme picked to look at for ten seconds should not leave anything behind.

So the file is created only by Options ▸ Create project settings, and its existence means something: this project has settings on purpose. That is also what makes the write-back rule simple to state — the theme is written to the file when the file exists, and not otherwise — with no flag anywhere for "do you want to remember this?".

Why the theme is written in place rather than re-encoded

Once the file exists, picking a theme rewrites it. Marshalling the Settings struct back to TOML would be four lines and would delete every comment in the file.

That matters more here than it usually would, because this file is meant to be edited by hand. It is the reason TOML colouring exists in the editor at all; the created file is mostly comments explaining the keys; a team will add comments of their own saying why they chose what they chose. Losing all of it the first time someone tries a different theme would be a silent, surprising deletion of somebody's writing.

So the rewrite finds the theme line inside the [editor] table and changes the value between the = and any trailing comment. Everything else in the file comes back byte for byte. It is about forty lines rather than four, and it is the difference between a file you can keep things in and a file that eats them.

Why automatic saving waits for a pause

Three triggers were considered.

On a fixed interval is the simplest and is wrong: it writes in the middle of an edit. Half a renamed identifier reaches disk, a file watcher rebuilds, and a test suite fails on code that never existed as anyone's intention.

On leaving the window never writes while you work, which sounds safe and means the thing on disk can be an hour behind the thing on screen — precisely when it matters, because the reason to want autosave is usually a tool watching the file.

After a pause in typing is what both other editors and this one settled on. Two seconds is long enough that a pause for thought is not a write, short enough that a rebuild follows a change closely. A run of typing is one write, not one per keystroke.

There is one deadline for the whole editor rather than one per window, because "you stopped typing" is one event. A per-window deadline would save the file you have moved away from at a different moment from the one in front of you, which nobody could observe and which is more state to keep right.

Why the deadline is checked, and only nudged by a timer

This is the same trap the language-server announcement and the terminal redraws both hit, and it is worth stating once more because it will come up again.

The editor blocks in PollEvent. To notice a deadline while nothing is happening, something has to wake it, and the only way to wake it from a timer is PostEvent — which drops events when its queue is full.

So the timer is not what decides. The deadline is state, checked at the top of every turn of the event loop, exactly as announceOpenDocuments checks whether the language server is ready. The timer's only job is to make sure a turn happens. A nudge that gets dropped costs a save that is late until the next keystroke or click; a design where the timer did the saving would lose it altogether.

Why a failed automatic save does not open a dialog

An autosave nobody asked for should not interrupt with a modal, and a modal that reappears every two seconds because a file is read-only is worse than the problem it reports. It goes on the status bar instead, and the deadline is cleared before the write is attempted, so a file that cannot be written is tried once per edit rather than forever.

Why TOML colouring reuses the code classes

Adding syntax.tomlkey and friends would have meant every theme — including the ones users have written — silently failing to colour TOML until it was updated.

The classes already there fit: a table header names a structure, so it reads as a type; a key names a thing, so it reads as an identifier; true and false are constants because that is what they are. The result is that every theme that ever worked colours TOML correctly, with no change and no new keys. The scanner is hand-written for the same reason the terminal emulator is — TOML is a small, fully specified language, and it is one file against a third dependency.

How it relates to the rest

 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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# Project settings — explanation

## What is this about?

A project can keep a `.turbo-python/settings.toml` beside its code, saying which theme to use and whether to save files automatically. This page is about the decisions inside that sentence: why the file is only ever looked for in one place, why creating it is a menu item rather than something that happens by itself, and why automatic saving works the way it does.

## Why the directory is not searched for upwards

`pyproject.toml` is found by walking up from the file you opened until one turns up, and the language server uses exactly that. The settings file deliberately does not.

The walk is right for `pyproject.toml` because a module has a real boundary: the file is either above you or it is not, and being inside a module is a fact about the code. "The project" is not a fact about the code. It is where you decided to start working, and the same directory tree can be several projects depending on what you are doing in it — a monorepo's `services/api` is a project when you are working on the API and part of a larger one when you are not.

A walk would also make the setting act at a distance. You open a file, and the editor's colours change because of a file three directories up that you did not know existed. Every explanation of that behaviour has to start with "well, it searches upwards", and the rule you would rather be able to state is the one that is now true: **the project is the directory you started the editor in.**

The cost is real and worth naming. Start the editor from `internal/app` and the project's theme does not apply. The answer is to start from the project root, which is where you would run `go build` and `git` anyway.

## Why creating the file is a menu item

The alternative was tempting: the first time you pick a theme, write `.turbo-python/settings.toml` so the choice sticks. Every editor that stores workspace state does something like it.

It was rejected because it puts a directory into someone's repository as a side effect of trying a colour. The user is one `git status` away from a change they did not make, in a project that may not be theirs, possibly in a review. A theme picked to look at for ten seconds should not leave anything behind.

So the file is created only by **Options ▸ Create project settings**, and its existence means something: this project has settings on purpose. That is also what makes the write-back rule simple to state — **the theme is written to the file when the file exists, and not otherwise** — with no flag anywhere for "do you want to remember this?".

## Why the theme is written in place rather than re-encoded

Once the file exists, picking a theme rewrites it. Marshalling the `Settings` struct back to TOML would be four lines and would delete every comment in the file.

That matters more here than it usually would, because this file is *meant* to be edited by hand. It is the reason TOML colouring exists in the editor at all; the created file is mostly comments explaining the keys; a team will add comments of their own saying why they chose what they chose. Losing all of it the first time someone tries a different theme would be a silent, surprising deletion of somebody's writing.

So the rewrite finds the `theme` line inside the `[editor]` table and changes the value between the `=` and any trailing comment. Everything else in the file comes back byte for byte. It is about forty lines rather than four, and it is the difference between a file you can keep things in and a file that eats them.

## Why automatic saving waits for a pause

Three triggers were considered.

**On a fixed interval** is the simplest and is wrong: it writes in the middle of an edit. Half a renamed identifier reaches disk, a file watcher rebuilds, and a test suite fails on code that never existed as anyone's intention.

**On leaving the window** never writes while you work, which sounds safe and means the thing on disk can be an hour behind the thing on screen — precisely when it matters, because the reason to want autosave is usually a tool watching the file.

**After a pause in typing** is what both other editors and this one settled on. Two seconds is long enough that a pause for thought is not a write, short enough that a rebuild follows a change closely. A run of typing is one write, not one per keystroke.

There is one deadline for the whole editor rather than one per window, because "you stopped typing" is one event. A per-window deadline would save the file you have moved away from at a different moment from the one in front of you, which nobody could observe and which is more state to keep right.

## Why the deadline is checked, and only nudged by a timer

This is the same trap the language-server announcement and the terminal redraws both hit, and it is worth stating once more because it will come up again.

The editor blocks in `PollEvent`. To notice a deadline while nothing is happening, something has to wake it, and the only way to wake it from a timer is `PostEvent` — which **drops** events when its queue is full.

So the timer is not what decides. The deadline is state, checked at the top of every turn of the event loop, exactly as `announceOpenDocuments` checks whether the language server is ready. The timer's only job is to make sure a turn happens. A nudge that gets dropped costs a save that is late until the next keystroke or click; a design where the timer did the saving would lose it altogether.

## Why a failed automatic save does not open a dialog

An autosave nobody asked for should not interrupt with a modal, and a modal that reappears every two seconds because a file is read-only is worse than the problem it reports. It goes on the status bar instead, and the deadline is cleared *before* the write is attempted, so a file that cannot be written is tried once per edit rather than forever.

## Why TOML colouring reuses the code classes

Adding `syntax.tomlkey` and friends would have meant every theme — including the ones users have written — silently failing to colour TOML until it was updated.

The classes already there fit: a table header names a structure, so it reads as a type; a key names a thing, so it reads as an identifier; `true` and `false` are constants because that is what they are. The result is that every theme that ever worked colours TOML correctly, with no change and no new keys. The scanner is hand-written for the same reason the terminal emulator is — TOML is a small, fully specified language, and it is one file against a third dependency.

## How it relates to the rest

- The exact keys and their defaults: [Project settings reference](../reference/project-settings.md)
- Setting one up: [How to give a project its own settings](../how-to/configure-a-project.md)
- The other TOML file the editor reads: [Theme file format](../reference/themes.md)
- Where `settings` sits among the packages: [Architecture](architecture.md)