summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--autocomplete.c2
-rw-r--r--hover.c79
-rw-r--r--lsp-parse.c57
-rw-r--r--lsp-write.c9
-rw-r--r--lsp.c2
-rw-r--r--lsp.h8
-rw-r--r--main.c20
-rw-r--r--ted.h12
8 files changed, 184 insertions, 5 deletions
diff --git a/autocomplete.c b/autocomplete.c
index 9166ea1..1271fba 100644
--- a/autocomplete.c
+++ b/autocomplete.c
@@ -288,6 +288,8 @@ static char symbol_kind_icon(SymbolKind k) {
static void autocomplete_frame(Ted *ted) {
Autocomplete *ac = &ted->autocomplete;
+ if (!ac->open) return;
+
TextBuffer *buffer = ted->active_buffer;
Font *font = ted->font;
float char_height = text_font_char_height(font);
diff --git a/hover.c b/hover.c
new file mode 100644
index 0000000..8b4794a
--- /dev/null
+++ b/hover.c
@@ -0,0 +1,79 @@
+// LSP hover information (textDocument/hover request)
+
+void hover_close(Ted *ted) {
+ Hover *hover = &ted->hover;
+ hover->time = 0.0;
+ hover->open = false;
+ free(hover->text);
+ hover->text = NULL;
+}
+
+void hover_send_request(Ted *ted) {
+ // find the buffer where the mouse is
+ for (int i = 0; i < TED_MAX_BUFFERS; ++i) {
+ TextBuffer *buffer = &ted->buffers[i];
+ if (!buffer->filename) continue;
+ LSP *lsp = buffer_lsp(buffer);
+ if (!lsp) continue;
+ BufferPos mouse_pos = {0};
+ if (buffer_pixels_to_pos(buffer, ted->mouse_pos, &mouse_pos)) {
+ // send the request
+ LSPRequest request = {.type = LSP_REQUEST_HOVER};
+ LSPRequestHover *h = &request.data.hover;
+ h->position = buffer_pos_to_lsp_document_position(buffer, mouse_pos);
+ lsp_send_request(lsp, &request);
+ break;
+ }
+ }
+}
+
+void hover_process_lsp_response(Ted *ted, LSPResponse *response) {
+ if (!response) return;
+ if (response->request.type != LSP_REQUEST_HOVER) return;
+
+ Hover *hover = &ted->hover;
+ LSPResponseHover *hover_response = &response->data.hover;
+ free(hover->text);
+ hover->text = NULL;
+
+ const char *contents = lsp_response_string(response, hover_response->contents);
+ if (*contents) {
+ hover->text = str_dup(contents);
+ char *p = hover->text + strlen(hover->text) - 1;
+ // remove trailing whitespace
+ // (rust-analyzer gives us trailing newlines for local variables)
+ for (; p > hover->text && isspace(*p); --p)
+ *p = '\0';
+ }
+}
+
+void hover_frame(Ted *ted, double dt) {
+ Hover *hover = &ted->hover;
+
+ if (ted->autocomplete.open) {
+ hover_close(ted);
+ }
+
+ if (!hover->open) {
+ hover->time += dt;
+ if (hover->time > 1.0) {
+ hover_send_request(ted);
+ hover->open = true;
+ }
+ return;
+ }
+
+ if (!hover->text)
+ return;
+
+ const char *text = hover->text;
+ u16 lines = 0; // number of lines of text
+ for (int i = 0; text[i]; ++i)
+ if (text[i] == '\n')
+ ++lines;
+
+ //Font *font = ted->font;
+ //float width = 200, height = lines * font->char_height;
+
+
+}
diff --git a/lsp-parse.c b/lsp-parse.c
index 555a62a..7141485 100644
--- a/lsp-parse.c
+++ b/lsp-parse.c
@@ -73,8 +73,11 @@ static bool parse_range(LSP *lsp, const JSON *json, JSONValue range_value, LSPRa
JSONObject range_object = range_value.val.object;
JSONValue start = json_object_get(json, range_object, "start");
JSONValue end = json_object_get(json, range_object, "end");
- return parse_position(lsp, json, start, &range->start)
+ bool success = parse_position(lsp, json, start, &range->start)
&& parse_position(lsp, json, end, &range->end);
+ if (!success)
+ memset(range, 0, sizeof *range);
+ return success;
}
@@ -403,6 +406,55 @@ static bool parse_signature_help(LSP *lsp, const JSON *json, LSPResponse *respon
return true;
}
+static bool parse_hover(LSP *lsp, const JSON *json, LSPResponse *response) {
+ LSPResponseHover *hover = &response->data.hover;
+ JSONObject result = json_force_object(json_get(json, "result"));
+
+ JSONValue range = json_object_get(json, result, "range");
+ parse_range(lsp, json, range, &hover->range);
+
+ JSONValue contents = json_object_get(json, result, "contents");
+
+ switch (contents.type) {
+ case JSON_OBJECT:
+ case JSON_STRING:
+ // all good
+ break;
+ case JSON_ARRAY: {
+ JSONArray contents_array = contents.val.array;
+ if (contents_array.len == 0) {
+ // the server probably should have just returned result: null.
+ // but the spec doesn't seem to forbid this, so we'll handle it.
+ return true;
+ }
+ // it's giving us multiple strings, but we'll just show the first one
+ contents = json_array_get(json, contents_array, 0);
+ } break;
+ default:
+ lsp_set_error(lsp, "Bad contents field on textDocument/hover response.");
+ return false;
+ }
+
+ // contents should either be a MarkupContent or a MarkedString
+ // i.e. it is either a string or has a member `value: string`
+ JSONString contents_string = {0};
+ if (contents.type == JSON_STRING) {
+ contents_string = contents.val.string;
+ } else {
+ JSONObject contents_object = json_force_object(contents);
+ JSONValue value = json_object_get(json, contents_object, "value");
+ if (value.type == JSON_STRING) {
+ contents_string = value.val.string;
+ } else {
+ lsp_set_error(lsp, "Bad contents field on textDocument/hover response.");
+ return false;
+ }
+ }
+
+ hover->contents = lsp_response_add_json_string(response, json, contents_string);
+ return true;
+}
+
// fills request->id/id_string appropriately given the request's json
// returns true on success
static WarnUnusedResult bool parse_id(JSON *json, LSPRequest *request) {
@@ -539,6 +591,9 @@ static void process_message(LSP *lsp, JSON *json) {
case LSP_REQUEST_SIGNATURE_HELP:
add_to_messages = parse_signature_help(lsp, json, &response);
break;
+ case LSP_REQUEST_HOVER:
+ add_to_messages = parse_hover(lsp, json, &response);
+ break;
case LSP_REQUEST_INITIALIZE: {
// it's the response to our initialize request!
if (result.type == JSON_OBJECT) {
diff --git a/lsp-write.c b/lsp-write.c
index dce4c22..7db0f66 100644
--- a/lsp-write.c
+++ b/lsp-write.c
@@ -408,6 +408,8 @@ static void write_request(LSP *lsp, LSPRequest *request) {
write_obj_end(o);
write_key_bool(o, "contextSupport", true);
write_obj_end(o);
+
+ // signature help capabilities
write_key_obj_start(o, "signatureHelp");
write_key_obj_start(o, "signatureInformation");
write_key_obj_start(o, "parameterInformation");
@@ -418,6 +420,13 @@ static void write_request(LSP *lsp, LSPRequest *request) {
// we don't have context support because sending the activeSignatureHelp member is annoying
//write_key_bool(o, "contextSupport", true);
write_obj_end(o);
+
+ // hover capabilities
+ write_key_obj_start(o, "hover");
+ write_key_arr_start(o, "contentFormat");
+ write_arr_elem_string(o, "plaintext");
+ write_arr_end(o);
+ 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 73a7045..bc14c60 100644
--- a/lsp.c
+++ b/lsp.c
@@ -1,5 +1,5 @@
// print server-to-client communication
-#define LSP_SHOW_S2C 1
+#define LSP_SHOW_S2C 0
// print client-to-server communication
#define LSP_SHOW_C2S 0
diff --git a/lsp.h b/lsp.h
index 9bbdd77..0b25091 100644
--- a/lsp.h
+++ b/lsp.h
@@ -272,6 +272,13 @@ typedef struct {
LSPSignatureInformation *signatures;
} LSPResponseSignatureHelp;
+typedef struct {
+ // the range of text to highlight
+ LSPRange range;
+ // little tool tip to show
+ LSPString contents;
+} LSPResponseHover;
+
typedef LSPRequestType LSPResponseType;
typedef struct {
@@ -284,6 +291,7 @@ typedef struct {
union {
LSPResponseCompletion completion;
LSPResponseSignatureHelp signature_help;
+ LSPResponseHover hover;
} data;
} LSPResponse;
diff --git a/main.c b/main.c
index b59079e..ba759f8 100644
--- a/main.c
+++ b/main.c
@@ -4,6 +4,9 @@
- hover
- go to definition using LSP
- find usages
+- highlight hover range
+- check mouse position in hover_process_lsp_response
+- hover-enabled, hover-time settings
- check if there are any other non-optional/nice-to-have-support-for server-to-client requests
- better non-error window/showMessage(Request)
- document lsp.h and lsp.c.
@@ -140,6 +143,7 @@ bool tag_goto(Ted *ted, char const *tag);
#include "menu.c"
#include "autocomplete.c"
#include "signature-help.c"
+#include "hover.c"
#include "command.c"
#include "config.c"
#include "session.c"
@@ -656,6 +660,8 @@ int main(int argc, char **argv) {
switch (event.type) {
case SDL_QUIT:
+ hover_close(ted);
+
command_execute(ted, CMD_QUIT, 1);
break;
case SDL_MOUSEWHEEL: {
@@ -670,6 +676,8 @@ int main(int argc, char **argv) {
}
} break;
case SDL_MOUSEBUTTONDOWN: {
+ hover_close(ted);
+
Uint32 button = event.button.button;
u8 times = event.button.clicks; // number of clicks
float x = (float)event.button.x, y = (float)event.button.y;
@@ -736,6 +744,8 @@ int main(int argc, char **argv) {
}
} break;
case SDL_MOUSEMOTION: {
+ hover_close(ted);
+
float x = (float)event.motion.x, y = (float)event.motion.y;
if (ted->drag_buffer != ted->active_buffer)
ted->drag_buffer = NULL;
@@ -748,11 +758,15 @@ int main(int argc, char **argv) {
}
} break;
case SDL_KEYDOWN: {
+ hover_close(ted);
+
SDL_Scancode scancode = event.key.keysym.scancode;
SDL_Keymod modifier = event.key.keysym.mod;
ted_press_key(ted, scancode, modifier);
} break;
case SDL_TEXTINPUT: {
+ hover_close(ted);
+
char *text = event.text.text;
if (buffer
// unfortunately, some key combinations like ctrl+minus still register as a "-" text input event
@@ -887,6 +901,7 @@ int main(int argc, char **argv) {
LSPResponse *r = &message.u.response;
autocomplete_process_lsp_response(ted, r);
signature_help_process_lsp_response(ted, r);
+ hover_process_lsp_response(ted, r);
} break;
}
lsp_message_free(&message);
@@ -991,9 +1006,9 @@ int main(int argc, char **argv) {
if (ted->nodes_used[0]) {
float y1 = padding;
node_frame(ted, node, rect4(x1, y1, x2, y));
- if (ted->autocomplete.open)
- autocomplete_frame(ted);
+ autocomplete_frame(ted);
signature_help_frame(ted);
+ hover_frame(ted, frame_dt);
} else {
autocomplete_close(ted);
text_utf8_anchored(font, "Press Ctrl+O to open a file or Ctrl+N to create a new one.",
@@ -1126,6 +1141,7 @@ int main(int argc, char **argv) {
build_stop(ted);
if (ted->menu)
menu_close(ted);
+ hover_close(ted);
autocomplete_close(ted);
session_write(ted);
diff --git a/ted.h b/ted.h
index 53d9697..f1748b0 100644
--- a/ted.h
+++ b/ted.h
@@ -447,6 +447,15 @@ typedef struct {
Signature signatures[SIGNATURE_HELP_MAX];
} SignatureHelp;
+typedef struct {
+ // is some hover info being displayed?
+ bool open;
+ // amount of time user has been hovering cursor for
+ double time;
+ // text to display
+ char *text;
+} Hover;
+
typedef struct Ted {
struct LSP *lsps[TED_LSP_MAX + 1];
@@ -493,7 +502,6 @@ 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;
@@ -503,6 +511,8 @@ typedef struct Ted {
bool build_shown; // are we showing the build output?
bool building; // is the build process running?
Autocomplete autocomplete;
+ SignatureHelp signature_help;
+ Hover hover;
FILE *log;