Files
cproof-context/patterns/autocomplete.md
jabber.developer2 22977846a3 docs: split context into layered, agent-oriented files
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.
2026-04-30 20:52:06 +03:00

98 lines
3.3 KiB
Markdown

# 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`:
```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`):
```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
```c
char* roster_contact_autocomplete(const char* const search_str,
gboolean previous, void* context);
```
- Stateless across invocations — no module-level `_last_match` globals.
- Returns a freshly-allocated `char*` (caller frees), or `NULL` when no more
matches.
- `previous` cycles backward (Shift+Tab).
- `context` is whatever was passed in `autocomplete_param_with_func`'s last
arg (often `NULL`).
`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`:
```c
static char* _account_autocomplete(ProfWin* window, const char* const input, gboolean previous);
```
Registered in `cmd_ac_init`:
```c
g_hash_table_insert(ac_funcs, "/account", _account_autocomplete);
```
The dispatcher inspects the input prefix and picks the right
`autocomplete_param_with_*` call.