ADR 0088: Config keys document themselves¶
Status¶
Accepted
Context¶
ase_config_write_default_if_missing() writes a commented starter file
on first run and never touches it again
(ADR 0008, decision 5). That starter
file was the only user-facing description of what config keys exist.
Which means it is documentation with an expiry date. A user who first
ran the editor in September has a file describing September's keys, and
every key added since is invisible to them — there is no other list. The
three changes before this one added lang.<id>.lsp, filetype.<suffix>
and .ase.conf, none of which appear in an already-written file.
Asked "where is the config file, and how do I find out what I can put in it", the honest answer was that you read the source. For a pillar named Minimal that is the wrong kind of minimal.
There was a second, quieter version of the same gap: animations and
line_numbers had no entry in ase_config_create_default(). Their
defaults lived in the GUI code that read them, as fallbacks for a NULL
lookup. Nothing was wrong with the behaviour, but "what is the default"
had two possible places to look and no single answer.
Decision¶
One table, and everything derives from it¶
AseConfigKeyDoc — key, default, one-line summary, whether a project
file may set it — is now the single list, and ase_config_key_docs()
hands it out. From it come:
- the shipped defaults, which
ase_config_create_default()loops over rather than listing by hand; - the editor's own reference, built at runtime;
- the tests below.
The point is not reuse. It is that a key cannot be added without a summary, because the struct has a field for one and the test fails when it is empty. Documentation that is a required argument does not drift.
This is the second time this codebase has reached for the same fix:
helpSections() is data rather than an HTML literal because the literal
"drifted badly out of date". Same disease, same treatment, one layer
down.
The reference is in the editor, not in a file on disk¶
F1 gains a Configuration section listing every key with its summary, generated from the table, with the config file's own path as the section note. It is correct by construction for the binary you are running, which a written-once file cannot be.
Search now narrows a section to its matching rows instead of showing the whole section. That was tolerable for a six-row keybinding block and is not for a nineteen-row key list: a list you can only locate is not the same as a list you can search.
Generated rows are HTML-escaped. The rows are rich text, and the first
two generated keys were lang.<id>.lsp and filetype.<suffix> — both
of which silently lose their angle brackets to the rich-text parser
otherwise. The hand-written rows keep writing their own entities, which
is why the escaping belongs at the point a row is generated rather than
where it is rendered.
:config opens the config file¶
The "where is it" question deserves an answer shorter than a path. It writes the starter file first if it has gone missing, so the command always lands on something editable.
Two tests, guarding the two ways this rots¶
- every key in the table has a non-empty summary, and its
projectflag agrees withase_config_key_allowed_in_project()— otherwise the panel would describe a scope the parser does not enforce; - the generated starter file mentions every key in the table, so a new user's file is complete even though the in-app reference is now the authority.
Consequences¶
An existing config file is still never rewritten, so it stays stale. That is deliberate — silently editing a file the user owns to insert comments is worse than the problem. The fix is that the stale file no longer matters: F1 is the reference, and a user who wants the current commented template can delete theirs and reopen the editor. The starter file's job narrows from "the documentation" to "a working example to start from".
Moving animations and line_numbers into the table gives them real
defaults where they previously had none. Both callers were checked: a
NULL lookup meant false and "absolute", and the table now supplies
exactly those strings, so behaviour is unchanged. Anything added to the
table later must be checked the same way — a default that appears where
code expected NULL is a behaviour change wearing a documentation
costume.
The summaries have to fit one line in a panel, so they say what a key does and not why. The why stays in the ADRs, and a key whose reasoning matters should point at one.