Replace the single file-structure.md with a stratified layout designed
for AI/agent skill consumption: tables and concrete identifiers over
prose, files loaded on demand, content separated by churn rate.
Layers:
- architecture/ stable structural reference (overview, source-map,
test-map, data-flow)
- patterns/ memory, commands, autocomplete, events, xmpp,
encryption, ui, plugins
- testing/ unit-tests, stubs, functional-tests, bench
- build/ local, docker, ci
- playbooks/ add-command, add-test, add-autocomplete,
add-event-handler, add-encryption
- gotchas.md append-only dated entries (seven seed entries)
- wip/ branch-specific notes; deleted on merge to master
Stable layers describe cproof on master only. In-flight feature
branches (currently feat/ai) get a single file under wip/.
INDEX.md is the entry map with churn labels; SKILL.md is the
always-loaded skill hint pointing to it.
3.3 KiB
Autocomplete
The Autocomplete type
src/tools/autocomplete.h defines an opaque Autocomplete (GList-backed
sorted set with a stateful "current iterator" for cycling through matches).
| Function | Purpose |
|---|---|
autocomplete_new() |
Create empty AC. |
autocomplete_clear(ac) |
Drop all entries (keep object). |
autocomplete_free(ac) |
Destroy. |
autocomplete_add(ac, item) |
Insert sorted. |
autocomplete_add_unsorted(ac, item, reversed) |
Append, no sort. |
autocomplete_add_all(ac, items) |
Bulk add from char**. |
autocomplete_update(ac, items) |
Clear + add_all. |
autocomplete_remove(ac, item) |
Drop one. |
autocomplete_remove_all(ac, items) |
Drop many. |
autocomplete_complete(ac, search, quote, previous) |
Cycle to next/previous match. |
autocomplete_reset(ac) |
Reset cycle position. |
autocomplete_contains(ac, value) |
Membership. |
Two completion flavours used in commands
In src/command/cmd_ac.c:
autocomplete_param_with_ac(input, "/cmd sub", static_ac, quote, previous);
autocomplete_param_with_func(input, "/cmd sub", callback, previous, ctx);
| When to use | Choose |
|---|---|
| Fixed token set known at startup. | autocomplete_param_with_ac + a static Autocomplete (allocated in cmd_ac_init, freed in cmd_ac_uninit). |
| Token set is dynamic / queried from state. | autocomplete_param_with_func + a callback char* fn(const char* search, gboolean previous, void* ctx). |
Static Autocompletes
Pattern (from cmd_ac.c):
// File-scope:
static Autocomplete account_ac;
// In cmd_ac_init():
account_ac = autocomplete_new();
autocomplete_add(account_ac, "list");
autocomplete_add(account_ac, "show");
autocomplete_add(account_ac, "add");
// ...
// Registered with:
g_hash_table_insert(ac_funcs, "/account", _account_autocomplete);
Static Autocomplete instances are added to a file-scope free-list array
near the top of cmd_ac.c so cmd_ac_uninit can call autocomplete_free
on each at shutdown. Add new static ACs to that free-list.
Function-callback completers
char* roster_contact_autocomplete(const char* const search_str,
gboolean previous, void* context);
- Stateless across invocations — no module-level
_last_matchglobals. - Returns a freshly-allocated
char*(caller frees), orNULLwhen no more matches. previouscycles backward (Shift+Tab).contextis whatever was passed inautocomplete_param_with_func's last arg (oftenNULL).
roster_contact_autocomplete (src/xmpp/roster_list.c) is a clean
canonical example: it delegates straight to autocomplete_complete against
a roster-owned Autocomplete object — no callback-local state.
Avoid the legacy "store previous match in a static" pattern. Some older completers do it; do not copy them. Keeping callbacks stateless makes shift-tab cycling and concurrent completers safe.
Per-command dispatcher
Every command's autocomplete is a single static function in cmd_ac.c:
static char* _account_autocomplete(ProfWin* window, const char* const input, gboolean previous);
Registered in cmd_ac_init:
g_hash_table_insert(ac_funcs, "/account", _account_autocomplete);
The dispatcher inspects the input prefix and picks the right
autocomplete_param_with_* call.