diff options
| author | Robin Haberkorn <rhaberkorn@fmsbw.de> | 2026-08-13 00:29:23 +0200 |
|---|---|---|
| committer | Robin Haberkorn <rhaberkorn@fmsbw.de> | 2026-08-13 01:04:41 +0200 |
| commit | b24c22dd15c0b02847d105b12e2fde72cb301d7d (patch) | |
| tree | a20844581f59748124e6e474f549845ed0a8dcfe | |
| parent | 259678833ad563f43d789c33e5304b2b7c6853cb (diff) | |
LSP: support ctags-lsp
* It sends `null` instead of empty lists, even though this is violating the LSP specs.
* Filter out duplicates from the auto-completion lists.
This may benefit other servers as well.
* ctags-lsp does not fuzzy search for workspace/symbol, so auto-completions
aren't really a thing.
The only special case is sending the empty string, which results in
all symbols being returned. This can be slow, though.
* ctags-lsp does not support textDocument/references, so n:FT$ won't be supported.
| -rw-r--r-- | src/lsp.c | 23 |
1 files changed, 16 insertions, 7 deletions
@@ -918,7 +918,7 @@ teco_lsp_lookup_symbol(teco_string_t str, gboolean match_exact, GError **error) while (sj_iter_object(&reader, obj, &key, &val) && !teco_json_eq(key, "result")); sj_Value arr = val; - if (arr.type != SJ_ARRAY) { + if (arr.type != SJ_NULL && arr.type != SJ_ARRAY) { g_set_error(error, TECO_ERROR, TECO_ERROR_FAILED, "Expected array in response (at byte %zu)", arr.start - reader.data); @@ -962,7 +962,7 @@ teco_lsp_lookup_symbol(teco_string_t str, gboolean match_exact, GError **error) results++; } if (results > 1) - teco_interface_msg(TECO_MSG_INFO, "Found %u references", results); + teco_interface_msg(TECO_MSG_INFO, "Found %u symbols", results); teco_undo_ptr(teco_lsp.current) = (teco_lsp_result_t *)teco_lsp.list.first; return TRUE; @@ -1013,6 +1013,7 @@ teco_lsp_symbol_auto_complete(const gchar *symbol, teco_string_t *insert) !teco_json_eq(key, "result")); sj_Value arr = val; if (arr.type != SJ_ARRAY) + /* can also be `null` in some LSPs */ return FALSE; GSList *list = NULL; @@ -1057,9 +1058,13 @@ teco_lsp_symbol_auto_complete(const gchar *symbol, teco_string_t *insert) } else if (list_len > 1) { list = g_slist_sort(list, (GCompareFunc)strcmp); - for (GSList *entry = list; entry != NULL; entry = g_slist_next(entry)) - teco_interface_popup_add(TECO_POPUP_PLAIN, entry->data, - strlen(entry->data), FALSE); + for (GSList *entry = list; entry != NULL; entry = g_slist_next(entry)) { + const gchar *name = entry->data; + if (g_slist_next(entry) && !strcmp(name, g_slist_next(entry)->data)) + /* filter out duplicates */ + continue; + teco_interface_popup_add(TECO_POPUP_PLAIN, name, strlen(name), FALSE); + } teco_interface_popup_show(symbol_len); } @@ -1111,12 +1116,16 @@ teco_lsp_lookup_definition(teco_buffer_t *buffer, teco_int_t pos, GError **error sj_Value key, val = {0}; while (sj_iter_object(&reader, obj, &key, &val) && !teco_json_eq(key, "result")); + /* + * Some servers can return `null` for an empty list + * or an object for a single result. + */ if (val.type == SJ_OBJECT) { teco_lsp_result_t *result = teco_lsp_parse_location(reader, val, error); if (!result) return FALSE; teco_stailq_insert_tail(&teco_lsp.list, &result->entry); - } else if (val.type != SJ_ARRAY) { + } else if (val.type != SJ_NULL && val.type != SJ_ARRAY) { g_set_error(error, TECO_ERROR, TECO_ERROR_FAILED, "Expected array in response (at byte %zu)", val.start - reader.data); @@ -1186,7 +1195,7 @@ teco_lsp_lookup_references(teco_buffer_t *buffer, teco_int_t pos, GError **error while (sj_iter_object(&reader, obj, &key, &val) && !teco_json_eq(key, "result")); sj_Value arr = val; - if (arr.type != SJ_ARRAY) { + if (arr.type != SJ_NULL && arr.type != SJ_ARRAY) { g_set_error(error, TECO_ERROR, TECO_ERROR_FAILED, "Expected array in response (at byte %zu)", arr.start - reader.data); |
