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.
This commit is contained in:
97
patterns/autocomplete.md
Normal file
97
patterns/autocomplete.md
Normal file
@@ -0,0 +1,97 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user