summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorpommicket <pommicket@gmail.com>2022-12-27 18:28:56 -0500
committerpommicket <pommicket@gmail.com>2022-12-27 18:28:56 -0500
commite112a90ff73f7f407ed2251f905565713c237bc1 (patch)
treefd2cca33161c807cbbf8b29d9a49e778bee5aca8
parent1eaef3694d54d3d92f0b43304c72f5148b4e5db9 (diff)
start signature help
-rw-r--r--autocomplete.c2
-rw-r--r--buffer.c27
-rw-r--r--lsp-parse.c51
-rw-r--r--lsp-write.c7
-rw-r--r--lsp.c4
-rw-r--r--lsp.h28
-rw-r--r--main.c25
-rw-r--r--signature-help.c12
-rw-r--r--ted.h7
9 files changed, 132 insertions, 31 deletions
diff --git a/autocomplete.c b/autocomplete.c
index 2955183..5045a3d 100644
--- a/autocomplete.c
+++ b/autocomplete.c
@@ -121,7 +121,7 @@ static void autocomplete_send_completion_request(Ted *ted, TextBuffer *buffer, B
request.data.completion = (LSPRequestCompletion) {
.position = {
.document = lsp_document_id(lsp, buffer->filename),
- .pos = buffer_pos_to_lsp(buffer, pos)
+ .pos = buffer_pos_to_lsp_position(buffer, pos)
},
.context = {
.trigger_kind = lsp_trigger,
diff --git a/buffer.c b/buffer.c
index 7b569bd..9796eb5 100644
--- a/buffer.c
+++ b/buffer.c
@@ -1419,8 +1419,13 @@ static Status buffer_insert_lines(TextBuffer *buffer, u32 where, u32 number) {
return false;
}
+LSPDocumentID buffer_lsp_document_id(TextBuffer *buffer) {
+ LSP *lsp = buffer_lsp(buffer);
+ return lsp_document_id(lsp, buffer->filename);
+}
+
// LSP uses UTF-16 indices because Microsoft fucking loves UTF-16 and won't let it die
-LSPPosition buffer_pos_to_lsp(TextBuffer *buffer, BufferPos pos) {
+LSPPosition buffer_pos_to_lsp_position(TextBuffer *buffer, BufferPos pos) {
LSPPosition lsp_pos = {
.line = pos.line
};
@@ -1436,6 +1441,22 @@ LSPPosition buffer_pos_to_lsp(TextBuffer *buffer, BufferPos pos) {
return lsp_pos;
}
+LSPDocumentPosition buffer_pos_to_lsp_document_position(TextBuffer *buffer, BufferPos pos) {
+ LSPDocumentPosition docpos = {
+ .document = buffer_lsp_document_id(buffer),
+ .pos = buffer_pos_to_lsp_position(buffer, pos)
+ };
+ return docpos;
+}
+
+LSPPosition buffer_cursor_pos_as_lsp_position(TextBuffer *buffer) {
+ return buffer_pos_to_lsp_position(buffer, buffer->cursor_pos);
+}
+
+LSPDocumentPosition buffer_cursor_pos_as_lsp_document_position(TextBuffer *buffer) {
+ return buffer_pos_to_lsp_document_position(buffer, buffer->cursor_pos);
+}
+
static void buffer_send_lsp_did_change(LSP *lsp, TextBuffer *buffer, BufferPos pos,
u32 nchars_deleted, String32 new_text) {
if (!buffer_is_named_file(buffer))
@@ -1443,9 +1464,9 @@ static void buffer_send_lsp_did_change(LSP *lsp, TextBuffer *buffer, BufferPos p
LSPDocumentChangeEvent event = {0};
if (new_text.len > 0)
event.text = str32_to_utf8_cstr(new_text);
- event.range.start = buffer_pos_to_lsp(buffer, pos);
+ event.range.start = buffer_pos_to_lsp_position(buffer, pos);
BufferPos pos_end = buffer_pos_advance(buffer, pos, nchars_deleted);
- event.range.end = buffer_pos_to_lsp(buffer, pos_end);
+ event.range.end = buffer_pos_to_lsp_position(buffer, pos_end);
lsp_document_changed(lsp, buffer->filename, event);
}
diff --git a/lsp-parse.c b/lsp-parse.c
index a6e932e..339c490 100644
--- a/lsp-parse.c
+++ b/lsp-parse.c
@@ -78,6 +78,28 @@ static bool parse_range(LSP *lsp, const JSON *json, JSONValue range_value, LSPRa
}
+static uint32_t *parse_trigger_characters(const JSON *json, JSONArray trigger_chars) {
+ uint32_t *array = NULL;
+ arr_reserve(array, trigger_chars.len);
+ for (u32 i = 0; i < trigger_chars.len; ++i) {
+ char character[8] = {0};
+ json_string_get(json,
+ json_array_get_string(json, trigger_chars, i),
+ character,
+ sizeof character);
+ if (*character) {
+ // the fact that they're called "trigger characters" makes
+ // me think multi-character triggers aren't allowed
+ // even though that would be nice in some languages,
+ // e.g. "::"
+ char32_t c = 0;
+ unicode_utf8_to_utf32(&c, character, strlen(character));
+ if (c) arr_add(array, c);
+ }
+ }
+ return array;
+}
+
static void parse_capabilities(LSP *lsp, const JSON *json, JSONObject capabilities) {
LSPCapabilities *cap = &lsp->capabilities;
@@ -88,26 +110,19 @@ static void parse_capabilities(LSP *lsp, const JSON *json, JSONObject capabiliti
JSONObject completion = completion_value.val.object;
JSONArray trigger_chars = json_object_get_array(json, completion, "triggerCharacters");
- for (u32 i = 0; i < trigger_chars.len; ++i) {
- char character[8] = {0};
- json_string_get(json,
- json_array_get_string(json, trigger_chars, i),
- character,
- sizeof character);
- if (*character) {
- char32_t c = 0;
- unicode_utf8_to_utf32(&c, character, strlen(character));
- // the fact that they're called "trigger characters" makes
- // me think multi-character triggers aren't allowed
- // even though that would be nice in some languages,
- // e.g. "::"
- if (c) {
- arr_add(lsp->trigger_chars, c);
- }
- }
- }
+ lsp->completion_trigger_chars = parse_trigger_characters(json, trigger_chars);
}
+ // check SignatureHelpOptions
+ JSONValue signature_help_value = json_object_get(json, capabilities, "signatureHelpProvider");
+ if (signature_help_value.type == JSON_OBJECT) {
+ cap->signature_help_support = true;
+ JSONObject signature_help = signature_help_value.val.object;
+ JSONArray trigger_chars = json_object_get_array(json, signature_help, "triggerCharacters");
+ lsp->signature_help_trigger_chars = parse_trigger_characters(json, trigger_chars);
+ JSONArray retrigger_chars = json_object_get_array(json, signature_help, "retriggerCharacters");
+ lsp->signature_help_retrigger_chars = parse_trigger_characters(json, retrigger_chars);
+ }
JSONObject workspace = json_object_get_object(json, capabilities, "workspace");
// check WorkspaceFoldersServerCapabilities
diff --git a/lsp-write.c b/lsp-write.c
index ffa5337..835cfce 100644
--- a/lsp-write.c
+++ b/lsp-write.c
@@ -251,6 +251,8 @@ static const char *lsp_request_method(LSPRequest *request) {
return "textDocument/didChange";
case LSP_REQUEST_COMPLETION:
return "textDocument/completion";
+ case LSP_REQUEST_SIGNATURE_HELP:
+ return "textDocument/signatureHelp";
case LSP_REQUEST_WORKSPACE_FOLDERS:
return "workspace/workspaceFolders";
case LSP_REQUEST_DID_CHANGE_WORKSPACE_FOLDERS:
@@ -275,6 +277,7 @@ static bool request_type_is_notification(LSPRequestType type) {
case LSP_REQUEST_SHOW_MESSAGE:
case LSP_REQUEST_LOG_MESSAGE:
case LSP_REQUEST_COMPLETION:
+ case LSP_REQUEST_SIGNATURE_HELP:
case LSP_REQUEST_WORKSPACE_FOLDERS:
return false;
}
@@ -386,6 +389,10 @@ static void write_request(LSP *lsp, LSPRequest *request) {
write_obj_end(o);
write_key_bool(o, "contextSupport", true);
write_obj_end(o);
+ write_key_obj_start(o, "signatureHelp");
+ // we don't have context support because sending the activeSignatureHelp member is annoying
+ //write_key_bool(o, "contextSupport", true);
+ write_obj_end(o);
write_obj_end(o);
write_key_obj_start(o, "workspace");
write_key_bool(o, "workspaceFolders", true);
diff --git a/lsp.c b/lsp.c
index a490aad..9ec7ad7 100644
--- a/lsp.c
+++ b/lsp.c
@@ -447,7 +447,9 @@ void lsp_free(LSP *lsp) {
arr_free(lsp->workspace_folders);
- arr_free(lsp->trigger_chars);
+ arr_free(lsp->completion_trigger_chars);
+ arr_free(lsp->signature_help_trigger_chars);
+ arr_free(lsp->signature_help_retrigger_chars);
memset(lsp, 0, sizeof *lsp);
free(lsp);
}
diff --git a/lsp.h b/lsp.h
index 5452dc2..1c75861 100644
--- a/lsp.h
+++ b/lsp.h
@@ -33,6 +33,7 @@ typedef enum {
LSP_REQUEST_DID_CLOSE, // textDocument/didClose
LSP_REQUEST_DID_CHANGE, // textDocument/didChange
LSP_REQUEST_COMPLETION, // textDocument/completion
+ LSP_REQUEST_SIGNATURE_HELP, // textDocument/signatureHelp
LSP_REQUEST_DID_CHANGE_WORKSPACE_FOLDERS, // workspace/didChangeWorkspaceFolders
// server-to-client
@@ -82,12 +83,15 @@ typedef struct {
LSPPosition pos;
} LSPDocumentPosition;
-typedef enum {
- LSP_TRIGGER_NONE = 0, // not actually defined in LSP spec
- LSP_TRIGGER_INVOKED = 1,
- LSP_TRIGGER_CHARACTER = 2,
- LSP_TRIGGER_INCOMPLETE = 3
-} LSPCompletionTriggerKind;
+
+// these triggers are used for completion context and signature help context.
+#define LSP_TRIGGER_NONE 0 // not actually defined in LSP spec
+#define LSP_TRIGGER_INVOKED 1
+#define LSP_TRIGGER_CHARACTER 2
+#define LSP_TRIGGER_INCOMPLETE 3
+#define LSP_TRIGGER_CONTENT_CHANGE 3
+typedef u8 LSPCompletionTriggerKind;
+typedef u8 LSPSignatureHelpTriggerKind;
typedef struct {
LSPCompletionTriggerKind trigger_kind;
@@ -100,6 +104,10 @@ typedef struct {
} LSPRequestCompletion;
typedef struct {
+ LSPDocumentPosition position;
+} LSPRequestSignatureHelp;
+
+typedef struct {
LSPDocumentID *removed; // dynamic array
LSPDocumentID *added; // dynamic array
} LSPRequestDidChangeWorkspaceFolders;
@@ -115,6 +123,7 @@ typedef struct {
LSPRequestDidClose close;
LSPRequestDidChange change;
LSPRequestCompletion completion;
+ LSPRequestSignatureHelp signature_help;
// LSP_REQUEST_SHOW_MESSAGE or LSP_REQUEST_LOG_MESSAGE
LSPRequestMessage message;
LSPRequestDidChangeWorkspaceFolders change_workspace_folders;
@@ -263,6 +272,7 @@ typedef struct {
} LSPDocumentData;
typedef struct {
+ bool signature_help_support;
bool completion_support;
// support for multiple root folders
// sadly, as of me writing this, clangd and rust-analyzer don't support this
@@ -316,7 +326,11 @@ typedef struct LSP {
// never accessed in main thread before `initialized = true`.
LSPCapabilities capabilities;
// thread-safety: same as `capabilities`
- char32_t *trigger_chars; // dynamic array of "trigger characters"
+ char32_t *completion_trigger_chars; // dynamic array
+ // thread-safety: same as `capabilities`
+ char32_t *signature_help_trigger_chars; // dynamic array
+ // thread-safety: same as `capabilities`
+ char32_t *signature_help_retrigger_chars; // dynamic array
SDL_mutex *workspace_folders_mutex;
LSPDocumentID *workspace_folders; // dynamic array of root directories of LSP workspace folders
SDL_mutex *error_mutex;
diff --git a/main.c b/main.c
index e10d6c3..3f028f6 100644
--- a/main.c
+++ b/main.c
@@ -5,6 +5,8 @@
- hover
- go to definition using LSP
- find usages
+- go through signature help capabilities
+- separate signature-help setting (dont use trigger-characters)
- check if there are any other non-optional/nice-to-have-support-for server-to-client requests
- do something with lsp->error
- document lsp.h and lsp.c.
@@ -138,6 +140,7 @@ bool tag_goto(Ted *ted, char const *tag);
#include "tags.c"
#include "menu.c"
#include "autocomplete.c"
+#include "signature-help.c"
#include "command.c"
#include "config.c"
#include "session.c"
@@ -768,13 +771,33 @@ int main(int argc, char **argv) {
char32_t last_char = 0;
unicode_utf8_to_utf32(&last_char, &text[last_code_point],
strlen(text) - last_code_point);
- arr_foreach_ptr(lsp->trigger_chars, char32_t, c) {
+ arr_foreach_ptr(lsp->completion_trigger_chars, char32_t, c) {
if (*c == last_char) {
autocomplete_open(ted, last_char);
break;
}
}
+ bool signature_help = false;
+ arr_foreach_ptr(lsp->signature_help_trigger_chars, char32_t, c) {
+ if (*c == last_char) {
+ signature_help = true;
+ break;
+ }
+ }
+
+ if (ted->signature_help.open) {
+ arr_foreach_ptr(lsp->signature_help_retrigger_chars, char32_t, c) {
+ if (*c == last_char) {
+ signature_help = true;
+ break;
+ }
+ }
+ }
+
+ if (signature_help)
+ signature_help_open(ted, last_char);
+
if (settings->identifier_trigger_characters && is_word(last_char)
&& !is_digit(last_char))
autocomplete_open(ted, last_char);
diff --git a/signature-help.c b/signature-help.c
new file mode 100644
index 0000000..df8c564
--- /dev/null
+++ b/signature-help.c
@@ -0,0 +1,12 @@
+// deals with textDocument/signatureHelp LSP requests
+
+void signature_help_open(Ted *ted, char32_t trigger) {
+ (void)trigger; // for now we don't send context
+ TextBuffer *buffer = ted->active_buffer;
+ if (!buffer) return;
+ LSP *lsp = buffer_lsp(buffer);
+ LSPRequest request = {.type = LSP_REQUEST_SIGNATURE_HELP};
+ LSPRequestSignatureHelp *s = &request.data.signature_help;
+ s->position = buffer_cursor_pos_as_lsp_document_position(buffer);
+ lsp_send_request(lsp, &request);
+}
diff --git a/ted.h b/ted.h
index 8d5d40c..7cfdbd4 100644
--- a/ted.h
+++ b/ted.h
@@ -417,6 +417,11 @@ typedef struct {
Rect rect; // rectangle where the autocomplete menu is (needed to avoid interpreting autocomplete clicks as other clicks)
} Autocomplete;
+// "signature help" (LSP) is thing that shows the current parameter, etc.
+typedef struct {
+ bool open;
+} SignatureHelp;
+
typedef struct Ted {
struct LSP *lsps[TED_LSP_MAX + 1];
@@ -463,6 +468,7 @@ typedef struct Ted {
bool find; // is the find or find+replace menu open?
bool replace; // is the find+replace menu open?
bool find_regex, find_case_sensitive; // find options
+ SignatureHelp signature_help;
u32 find_flags; // flags used last time search term was compiled
pcre2_code *find_code;
pcre2_match_data *find_match_data;
@@ -542,6 +548,7 @@ Settings *ted_active_settings(Ted *ted);
Settings *ted_get_settings(Ted *ted, const char *path, Language lang);
void ted_load_configs(Ted *ted, bool reloading);
struct LSP *ted_get_lsp(Ted *ted, const char *path, Language lang);
+struct LSP *ted_active_lsp(Ted *ted);
struct LSP *ted_get_lsp_by_id(Ted *ted, u32 id);
static TextBuffer *find_search_buffer(Ted *ted);
// first, we read all config files, then we parse them.