aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorRobin Haberkorn <rhaberkorn@fmsbw.de>2026-08-13 00:29:23 +0200
committerRobin Haberkorn <rhaberkorn@fmsbw.de>2026-08-13 01:04:41 +0200
commitb24c22dd15c0b02847d105b12e2fde72cb301d7d (patch)
treea20844581f59748124e6e474f549845ed0a8dcfe /src
parent259678833ad563f43d789c33e5304b2b7c6853cb (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.
Diffstat (limited to 'src')
-rw-r--r--src/lsp.c23
1 files changed, 16 insertions, 7 deletions
diff --git a/src/lsp.c b/src/lsp.c
index 72ce4d3..2c1f89b 100644
--- a/src/lsp.c
+++ b/src/lsp.c
@@ -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);