refactor(ai): use stateful autocomplete for provider name matching
Some checks failed
CI Code / Check spelling (pull_request) Successful in 57s
CI Code / Check coding style (pull_request) Successful in 1m4s
CI Code / Code Coverage (pull_request) Failing after 1m7s
CI Code / Linux (debian) (pull_request) Failing after 2m58s
CI Code / Linux (ubuntu) (pull_request) Failing after 3m9s
CI Code / Linux (arch) (pull_request) Failing after 3m47s

Replace the manual GHashTable iteration in ai_providers_find() with the
existing stateful autocomplete system. Provider names are now kept in
sync via autocomplete_add/remove during ai_add_provider/ai_remove_provider,
and ai_providers_find delegates to autocomplete_complete() for deterministic
tab-completion cycling through sorted results.

This eliminates manual GList allocation, iteration, and cleanup, while
providing consistent forward/backward cycling behavior shared by all
autocomplete callers.
This commit is contained in:
2026-04-30 23:16:12 +00:00
parent 002a6ed15b
commit 461c0c32dd

View File

@@ -15,6 +15,7 @@
#include "log.h"
#include "config/preferences.h"
#include "profanity.h"
#include "tools/autocomplete.h"
#include "ui/ui.h"
#include <curl/curl.h>
@@ -28,6 +29,7 @@
/* Global state */
static GHashTable* providers = NULL;
static GHashTable* provider_keys = NULL;
static Autocomplete providers_ac = NULL;
/* ========================================================================
* Curl helpers
@@ -245,6 +247,9 @@ ai_client_init(void)
providers = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)ai_provider_unref);
provider_keys = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
/* Create autocomplete for provider names */
providers_ac = autocomplete_new();
/* Add default providers */
ai_add_provider("openai", DEFAULT_OPENAI_URL, NULL);
ai_add_provider("perplexity", DEFAULT_PERPLEXITY_URL, NULL);
@@ -266,6 +271,11 @@ ai_client_shutdown(void)
providers = NULL;
provider_keys = NULL;
if (providers_ac) {
autocomplete_free(providers_ac);
providers_ac = NULL;
}
curl_global_cleanup();
log_info("AI client shutdown");
}
@@ -304,6 +314,9 @@ ai_add_provider(const gchar* name, const gchar* api_url, const gchar* org_id)
AIProvider* provider = ai_provider_new(name, api_url, org_id);
g_hash_table_insert(providers, g_strdup(name), provider);
/* Sync autocomplete */
autocomplete_add(providers_ac, name);
log_info("Added provider: %s (URL: %s)", name, api_url);
return provider; /* Caller gets non-owning pointer; hash table owns ref */
}
@@ -313,6 +326,10 @@ ai_remove_provider(const gchar* name)
{
if (!name || !providers)
return FALSE;
/* Sync autocomplete before removing */
autocomplete_remove(providers_ac, name);
return g_hash_table_remove(providers, name);
}
@@ -334,40 +351,23 @@ ai_list_providers(void)
return result;
}
/* ========================================================================
* Provider autocomplete state
* ======================================================================== */
/* Stateful provider name finder with case-sensitive matching.
* Maintains position in sorted list for deterministic tab-completion cycling.
* The autocomplete is kept in sync via ai_add_provider/ai_remove_provider. */
char*
ai_providers_find(const char* const search_str, gboolean previous, void* context)
{
if (!providers || !search_str || strlen(search_str) == 0) {
return NULL;
/* Initialize autocomplete on first use */
if (!providers_ac) {
providers_ac = autocomplete_new();
}
GHashTableIter iter;
gpointer key;
gpointer value;
/* Collect all matching providers */
GList* matches = NULL;
g_hash_table_iter_init(&iter, providers);
while (g_hash_table_iter_next(&iter, &key, &value)) {
if (g_str_has_prefix((gchar*)key, search_str)) {
matches = g_list_append(matches, g_strdup((gchar*)key));
}
}
if (matches == NULL) {
return NULL;
}
/* Return first or last match based on previous flag */
gchar* result;
if (previous) {
result = g_strdup((gchar*)matches->data);
} else {
result = g_strdup((gchar*)g_list_last(matches)->data);
}
g_list_free_full(matches, g_free);
return result;
/* Use stateful autocomplete */
return autocomplete_complete(providers_ac, search_str, FALSE, previous);
}
gchar*