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

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_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:

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.