summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorpommicket <pommicket@gmail.com>2023-08-04 19:35:48 -0400
committerpommicket <pommicket@gmail.com>2023-08-04 19:35:48 -0400
commit45d69107725a02cf6d912a571b771da3ef5abfbf (patch)
tree6b5d44104f57074dc00f1bab8eed986ca707b8a2
parentf278b832dc6e085d05b243bbae2af82e78e36d6d (diff)
textDocument/documentLink parsing
-rw-r--r--CMakeLists.txt6
-rw-r--r--ide-document-link.c49
-rw-r--r--lsp-json.c1
-rw-r--r--lsp-parse.c66
-rw-r--r--lsp-write.c15
-rw-r--r--lsp.c7
-rw-r--r--lsp.h18
-rw-r--r--main.c5
-rw-r--r--ted.h12
-rw-r--r--test/lsp/go/main.go1
-rw-r--r--test/lsp/rust/.gitignore1
-rw-r--r--test/lsp/rust/Cargo.lock7
-rw-r--r--test/lsp/rust/Cargo.toml8
-rw-r--r--test/lsp/rust/src/main.rs3
-rw-r--r--test/test.html6
15 files changed, 184 insertions, 21 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index e808041..32a8b50 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -2,9 +2,9 @@ cmake_minimum_required(VERSION 3.1)
project(ted)
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
set(SOURCES buffer.c build.c colors.c command.c config.c find.c gl.c ide-autocomplete.c
- ide-definitions.c ide-highlights.c ide-hover.c ide-signature-help.c ide-usages.c
- lsp.c lsp-json.c lsp-parse.c lsp-write.c main.c menu.c node.c os.c session.c
- stb_image.c stb_truetype.c syntax.c tags.c ted.c text.c ui.c util.c macro.c)
+ ide-document-link.c ide-definitions.c ide-highlights.c ide-hover.c ide-signature-help.c
+ ide-usages.c lsp.c lsp-json.c lsp-parse.c lsp-write.c main.c menu.c node.c os.c
+ session.c stb_image.c stb_truetype.c syntax.c tags.c ted.c text.c ui.c util.c macro.c)
else()
set(SOURCES main.c)
endif()
diff --git a/ide-document-link.c b/ide-document-link.c
new file mode 100644
index 0000000..d0e1ef8
--- /dev/null
+++ b/ide-document-link.c
@@ -0,0 +1,49 @@
+#include "ted.h"
+
+void document_link_frame(Ted *ted) {
+ DocumentLink *document_link = &ted->document_link;
+
+ bool key_down = ted_is_ctrl_down(ted);
+ if (!key_down) {
+ document_link->key_press_time = 0.0;
+ } else if (document_link->key_press_time == 0.0) {
+ document_link->key_press_time = ted->frame_time;
+ }
+
+ bool show_links = document_link->key_press_time != 0.0
+ && ted->frame_time - document_link->key_press_time > 1.0;
+
+ if (!show_links) {
+ ted_cancel_lsp_request(ted, &document_link->last_request);
+ return;
+ }
+
+ TextBuffer *buffer = ted->active_buffer;
+ if (!buffer)
+ return;
+
+ LSP *lsp = buffer_lsp(buffer);
+ if (!lsp)
+ return;
+
+ if (!document_link->last_request.id) {
+ // send the request
+ LSPRequest request = {.type = LSP_REQUEST_DOCUMENT_LINK};
+ LSPRequestDocumentLink *lnk = &request.data.document_link;
+ lnk->document = buffer_lsp_document_id(buffer);
+ document_link->last_request = lsp_send_request(lsp, &request);
+ }
+}
+
+void document_link_process_lsp_response(Ted *ted, const LSPResponse *response) {
+ if (response->request.type != LSP_REQUEST_DOCUMENT_LINK)
+ return;
+
+ (void)ted;//TODO
+ const LSPResponseDocumentLink *document_link = &response->data.document_link;
+
+ arr_foreach_ptr(document_link->links, const LSPDocumentLink, link) {
+ printf("target: %s\n", lsp_response_string(response, link->target));
+
+ }
+}
diff --git a/lsp-json.c b/lsp-json.c
index 08f926a..4884c8c 100644
--- a/lsp-json.c
+++ b/lsp-json.c
@@ -663,6 +663,7 @@ void json_debug_print(const JSON *json) {
printf("%u values (capacity %u, text length %zu)\n",
arr_len(json->values), arr_cap(json->values), strlen(json->text));
json_debug_print_value(json, json->values[0]);
+ printf("\n");
}
// e.g. converts "Hello\nworld" to "Hello\\nworld"
diff --git a/lsp-parse.c b/lsp-parse.c
index 6c5ccec..c07679a 100644
--- a/lsp-parse.c
+++ b/lsp-parse.c
@@ -236,6 +236,12 @@ static void parse_capabilities(LSP *lsp, const JSON *json, JSONObject capabiliti
cap->rename_support = true;
}
+ // check for textDocument/documentLink support
+ JSONValue document_link_value = json_object_get(json, capabilities, "documentLinkProvider");
+ if (document_link_value.type == JSON_OBJECT) {
+ cap->document_link_support = true;
+ }
+
// check for workspace/symbol support
JSONValue workspace_symbol_value = json_object_get(json, capabilities, "workspaceSymbolProvider");
if (workspace_symbol_value.type != JSON_UNDEFINED && workspace_symbol_value.type != JSON_FALSE) {
@@ -275,7 +281,7 @@ static bool parse_text_edit(LSP *lsp, LSPResponse *response, const JSON *json, J
return true;
}
-static bool parse_completion(LSP *lsp, const JSON *json, LSPResponse *response) {
+static bool parse_completion_response(LSP *lsp, const JSON *json, LSPResponse *response) {
// deal with textDocument/completion response.
// result: CompletionItem[] | CompletionList | null
LSPResponseCompletion *completion = &response->data.completion;
@@ -418,7 +424,7 @@ static bool parse_completion(LSP *lsp, const JSON *json, LSPResponse *response)
return true;
}
-static bool parse_signature_help(LSP *lsp, const JSON *json, LSPResponse *response) {
+static bool parse_signature_help_response(LSP *lsp, const JSON *json, LSPResponse *response) {
JSONObject result = json_force_object(json_get(json, "result"));
//json_debug_print_object(json, result);printf("\n");
LSPResponseSignatureHelp *help = &response->data.signature_help;
@@ -516,7 +522,7 @@ 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) {
+static bool parse_hover_response(LSP *lsp, const JSON *json, LSPResponse *response) {
LSPResponseHover *hover = &response->data.hover;
JSONValue result_value = json_get(json, "result");
if (result_value.type == JSON_NULL)
@@ -593,7 +599,7 @@ static bool parse_location(LSP *lsp, const JSON *json, JSONValue value, LSPLocat
return true;
}
-static bool parse_definition(LSP *lsp, const JSON *json, LSPResponse *response) {
+static bool parse_definition_response(LSP *lsp, const JSON *json, LSPResponse *response) {
JSONValue result = json_get(json, "result");
if (result.type == JSON_NULL) {
// no location
@@ -660,7 +666,7 @@ static bool parse_symbol_information(LSP *lsp, const JSON *json, JSONValue value
return true;
}
-static bool parse_workspace_symbols(LSP *lsp, const JSON *json, LSPResponse *response) {
+static bool parse_workspace_symbols_response(LSP *lsp, const JSON *json, LSPResponse *response) {
LSPResponseWorkspaceSymbols *syms = &response->data.workspace_symbols;
JSONArray result = json_force_array(json_get(json, "result"));
arr_set_len(syms->symbols, result.len);
@@ -845,12 +851,12 @@ static bool parse_workspace_edit(LSP *lsp, LSPResponse *response, const JSON *js
return true;
}
-static bool parse_rename(LSP *lsp, const JSON *json, LSPResponse *response) {
+static bool parse_rename_response(LSP *lsp, const JSON *json, LSPResponse *response) {
JSONObject result = json_force_object(json_get(json, "result"));
return parse_workspace_edit(lsp, response, json, result, &response->data.rename);
}
-static bool parse_highlight(LSP *lsp, const JSON *json, LSPResponse *response) {
+static bool parse_highlight_response(LSP *lsp, const JSON *json, LSPResponse *response) {
LSPResponseHighlight *hl = &response->data.highlight;
JSONArray result = json_force_array(json_get(json, "result"));
for (u32 h = 0; h < result.len; ++h) {
@@ -906,7 +912,7 @@ static int references_location_cmp(void *context, const void *av, const void *bv
return 0;
}
-static bool parse_references(LSP *lsp, const JSON *json, LSPResponse *response) {
+static bool parse_references_response(LSP *lsp, const JSON *json, LSPResponse *response) {
LSPResponseReferences *refs = &response->data.references;
JSONArray result = json_force_array(json_get(json, "result"));
for (u32 r = 0; r < result.len; ++r) {
@@ -919,6 +925,31 @@ static bool parse_references(LSP *lsp, const JSON *json, LSPResponse *response)
return true;
}
+static bool parse_document_link_response(LSP *lsp, const JSON *json, LSPResponse *response) {
+ LSPResponseDocumentLink *data = &response->data.document_link;
+ JSONArray result = json_force_array(json_get(json, "result"));
+
+ for (u32 i = 0; i < result.len; ++i) {
+ JSONObject link_object = json_array_get_object(json, result, i);
+ JSONString target = json_object_get_string(json, link_object, "target");
+ JSONValue range = json_object_get(json, link_object, "range");
+ JSONString tooltip = json_object_get_string(json, link_object, "tooltip");
+ if (!target.len) {
+ // technically this can be omitted and force us to send
+ // a resolve request, but idk if any servers out there
+ // actually do that
+ continue;
+ }
+
+ LSPDocumentLink *link = arr_addp(data->links);
+ if (!parse_range(lsp, json, range, &link->range))
+ return false;
+ link->target = lsp_response_add_json_string(response, json, target);
+ link->tooltip = lsp_response_add_json_string(response, json, tooltip);
+ }
+ return true;
+}
+
void process_message(LSP *lsp, JSON *json) {
#if 0
@@ -969,31 +1000,34 @@ void process_message(LSP *lsp, JSON *json) {
add_to_messages = true;
} else switch (response.request.type) {
case LSP_REQUEST_COMPLETION:
- add_to_messages = parse_completion(lsp, json, &response);
+ add_to_messages = parse_completion_response(lsp, json, &response);
break;
case LSP_REQUEST_SIGNATURE_HELP:
- add_to_messages = parse_signature_help(lsp, json, &response);
+ add_to_messages = parse_signature_help_response(lsp, json, &response);
break;
case LSP_REQUEST_HOVER:
- add_to_messages = parse_hover(lsp, json, &response);
+ add_to_messages = parse_hover_response(lsp, json, &response);
break;
case LSP_REQUEST_DEFINITION:
case LSP_REQUEST_DECLARATION:
case LSP_REQUEST_TYPE_DEFINITION:
case LSP_REQUEST_IMPLEMENTATION:
- add_to_messages = parse_definition(lsp, json, &response);
+ add_to_messages = parse_definition_response(lsp, json, &response);
break;
case LSP_REQUEST_HIGHLIGHT:
- add_to_messages = parse_highlight(lsp, json, &response);
+ add_to_messages = parse_highlight_response(lsp, json, &response);
break;
case LSP_REQUEST_REFERENCES:
- add_to_messages = parse_references(lsp, json, &response);
+ add_to_messages = parse_references_response(lsp, json, &response);
break;
case LSP_REQUEST_WORKSPACE_SYMBOLS:
- add_to_messages = parse_workspace_symbols(lsp, json, &response);
+ add_to_messages = parse_workspace_symbols_response(lsp, json, &response);
break;
case LSP_REQUEST_RENAME:
- add_to_messages = parse_rename(lsp, json, &response);
+ add_to_messages = parse_rename_response(lsp, json, &response);
+ break;
+ case LSP_REQUEST_DOCUMENT_LINK:
+ add_to_messages = parse_document_link_response(lsp, json, &response);
break;
case LSP_REQUEST_INITIALIZE:
if (!lsp->initialized) {
diff --git a/lsp-write.c b/lsp-write.c
index df695a6..85c5781 100644
--- a/lsp-write.c
+++ b/lsp-write.c
@@ -288,6 +288,8 @@ static const char *lsp_request_method(LSPRequest *request) {
return "textDocument/implementation";
case LSP_REQUEST_HIGHLIGHT:
return "textDocument/documentHighlight";
+ case LSP_REQUEST_DOCUMENT_LINK:
+ return "textDocument/documentLink";
case LSP_REQUEST_RENAME:
return "textDocument/rename";
case LSP_REQUEST_WORKSPACE_FOLDERS:
@@ -447,6 +449,11 @@ void write_request(LSP *lsp, LSPRequest *request) {
write_key_obj_start(o, "definition");
// NOTE: LocationLink support doesn't seem useful to us right now.
write_obj_end(o);
+
+ // document link capabilities
+ write_key_obj_start(o, "documentLink");
+ write_key_bool(o, "tooltipSupport", true);
+ write_obj_end(o);
write_obj_end(o);
write_key_obj_start(o, "workspace");
write_key_bool(o, "workspaceFolders", true);
@@ -579,6 +586,14 @@ void write_request(LSP *lsp, LSPRequest *request) {
write_obj_end(o);
write_obj_end(o);
} break;
+ case LSP_REQUEST_DOCUMENT_LINK: {
+ const LSPRequestDocumentLink *lnk = &request->data.document_link;
+ write_key_obj_start(o, "params");
+ write_key_obj_start(o, "textDocument");
+ write_key_file_uri(o, "uri", lnk->document);
+ write_obj_end(o);
+ write_obj_end(o);
+ } break;
case LSP_REQUEST_RENAME: {
const LSPRequestRename *rename = &request->data.rename;
write_key_obj_start(o, "params");
diff --git a/lsp.c b/lsp.c
index cac2bea..77e1454 100644
--- a/lsp.c
+++ b/lsp.c
@@ -54,6 +54,7 @@ void lsp_request_free(LSPRequest *r) {
case LSP_REQUEST_HIGHLIGHT:
case LSP_REQUEST_DID_CLOSE:
case LSP_REQUEST_WORKSPACE_FOLDERS:
+ case LSP_REQUEST_DOCUMENT_LINK:
break;
case LSP_REQUEST_CONFIGURATION: {
LSPRequestConfiguration *config = &r->data.configuration;
@@ -112,6 +113,9 @@ void lsp_response_free(LSPResponse *r) {
case LSP_REQUEST_REFERENCES:
arr_free(r->data.references.locations);
break;
+ case LSP_REQUEST_DOCUMENT_LINK:
+ arr_free(r->data.document_link.links);
+ break;
default:
break;
}
@@ -193,6 +197,8 @@ static bool lsp_supports_request(LSP *lsp, const LSPRequest *request) {
return cap->highlight_support;
case LSP_REQUEST_REFERENCES:
return cap->references_support;
+ case LSP_REQUEST_DOCUMENT_LINK:
+ return cap->document_link_support;
}
assert(0);
return false;
@@ -232,6 +238,7 @@ static bool request_type_is_notification(LSPRequestType type) {
case LSP_REQUEST_RENAME:
case LSP_REQUEST_WORKSPACE_SYMBOLS:
case LSP_REQUEST_WORKSPACE_FOLDERS:
+ case LSP_REQUEST_DOCUMENT_LINK:
return false;
}
assert(0);
diff --git a/lsp.h b/lsp.h
index fe74703..4e5cf03 100644
--- a/lsp.h
+++ b/lsp.h
@@ -85,6 +85,7 @@ typedef enum {
LSP_REQUEST_HIGHLIGHT, //< textDocument/documentHighlight
LSP_REQUEST_REFERENCES, //< textDocument/references
LSP_REQUEST_RENAME, //< textDocument/rename
+ LSP_REQUEST_DOCUMENT_LINK, //< textDocument/documentLink
LSP_REQUEST_WORKSPACE_SYMBOLS, //< workspace/symbol
LSP_REQUEST_DID_CHANGE_WORKSPACE_FOLDERS, //< workspace/didChangeWorkspaceFolders
@@ -191,6 +192,10 @@ typedef struct {
} LSPRequestReferences;
typedef struct {
+ LSPDocumentID document;
+} LSPRequestDocumentLink;
+
+typedef struct {
// string to filter the symbols by
char *query;
} LSPRequestWorkspaceSymbols;
@@ -233,6 +238,7 @@ typedef struct {
LSPRequestMessage message;
LSPRequestDidChangeWorkspaceFolders change_workspace_folders;
LSPRequestRename rename;
+ LSPRequestDocumentLink document_link;
} data;
} LSPRequest;
@@ -475,6 +481,16 @@ typedef struct {
typedef LSPWorkspaceEdit LSPResponseRename;
typedef struct {
+ LSPRange range;
+ LSPString target;
+ LSPString tooltip;
+} LSPDocumentLink;
+
+typedef struct {
+ LSPDocumentLink *links;
+} LSPResponseDocumentLink;
+
+typedef struct {
LSPRequest request; // the request which this is a response to
char *error; // if not NULL, the data field will just be zeroed
// LSP responses tend to have a lot of strings.
@@ -492,6 +508,7 @@ typedef struct {
LSPResponseRename rename;
LSPResponseHighlight highlight;
LSPResponseReferences references;
+ LSPResponseDocumentLink document_link;
} data;
} LSPResponse;
@@ -524,6 +541,7 @@ typedef struct {
bool workspace_folders_support;
bool rename_support;
bool references_support;
+ bool document_link_support;
} LSPCapabilities;
typedef struct LSP {
diff --git a/main.c b/main.c
index 0045e98..8109df8 100644
--- a/main.c
+++ b/main.c
@@ -1,8 +1,6 @@
/*
FUTURE FEATURES:
- autodetect indentation (tabs vs spaces)
-- font setting & support for multiple fonts to cover more characters
-- support for variable-width fonts
- robust find (results shouldn't move around when you type things)
- document links using LSP textDocument/documentLink request
- rename using LSP (textDocument/rename)
@@ -81,6 +79,7 @@ FUTURE FEATURES:
#include "ide-definitions.c"
#include "ide-highlights.c"
#include "ide-usages.c"
+#include "ide-d.c"
#include "command.c"
#include "macro.c"
#include "config.c"
@@ -900,6 +899,7 @@ int main(int argc, char **argv) {
definitions_process_lsp_response(ted, lsp, r);
highlights_process_lsp_response(ted, r);
usages_process_lsp_response(ted, r);
+ document_link_process_lsp_response(ted, r);
} break;
}
lsp_message_free(&message);
@@ -1009,6 +1009,7 @@ int main(int argc, char **argv) {
definitions_frame(ted);
highlights_frame(ted);
usages_frame(ted);
+ document_link_frame(ted);
} else {
autocomplete_close(ted);
if (!ted->build_shown) {
diff --git a/ted.h b/ted.h
index 2cbf305..ea2fd68 100644
--- a/ted.h
+++ b/ted.h
@@ -665,6 +665,13 @@ typedef struct {
Signature signatures[SIGNATURE_HELP_MAX];
} SignatureHelp;
+/// "document link" information (LSP)
+typedef struct {
+ LSPServerRequestID last_request;
+ // time when activation key was pressed
+ double key_press_time;
+} DocumentLink;
+
/// "hover" information from LSP server
typedef struct {
LSPServerRequestID last_request;
@@ -825,6 +832,7 @@ typedef struct Ted {
bool building;
Autocomplete autocomplete;
SignatureHelp signature_help;
+ DocumentLink document_link;
Hover hover;
Definitions definitions;
Highlights highlights;
@@ -1498,6 +1506,10 @@ void definitions_selector_render(Ted *ted, Rect bounds);
void definitions_selector_close(Ted *ted);
void definitions_frame(Ted *ted);
+// === ide-document-link.c ===
+void document_link_frame(Ted *ted);
+void document_link_process_lsp_response(Ted *ted, const LSPResponse *response);
+
// === ide-highlights.c ===
void highlights_close(Ted *ted);
void highlights_process_lsp_response(Ted *ted, LSPResponse *response);
diff --git a/test/lsp/go/main.go b/test/lsp/go/main.go
index d60bcd1..1cf4073 100644
--- a/test/lsp/go/main.go
+++ b/test/lsp/go/main.go
@@ -7,6 +7,7 @@ type X struct {
y int
}
+/// https://example.com
func main() {
_, err := fmt.Println("hello world");
if err != nil {
diff --git a/test/lsp/rust/.gitignore b/test/lsp/rust/.gitignore
new file mode 100644
index 0000000..eb5a316
--- /dev/null
+++ b/test/lsp/rust/.gitignore
@@ -0,0 +1 @@
+target
diff --git a/test/lsp/rust/Cargo.lock b/test/lsp/rust/Cargo.lock
new file mode 100644
index 0000000..b21cc6a
--- /dev/null
+++ b/test/lsp/rust/Cargo.lock
@@ -0,0 +1,7 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "rust"
+version = "0.1.0"
diff --git a/test/lsp/rust/Cargo.toml b/test/lsp/rust/Cargo.toml
new file mode 100644
index 0000000..1ec6963
--- /dev/null
+++ b/test/lsp/rust/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "rust"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
diff --git a/test/lsp/rust/src/main.rs b/test/lsp/rust/src/main.rs
new file mode 100644
index 0000000..e7a11a9
--- /dev/null
+++ b/test/lsp/rust/src/main.rs
@@ -0,0 +1,3 @@
+fn main() {
+ println!("Hello, world!");
+}
diff --git a/test/test.html b/test/test.html
new file mode 100644
index 0000000..7677c8f
--- /dev/null
+++ b/test/test.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html>
+<body>
+<a href="https://example.com">example</a>
+</body>
+</html>