summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--command.h1
-rw-r--r--ide-rename.c0
-rw-r--r--lsp-json.c14
-rw-r--r--lsp-parse.c64
-rw-r--r--lsp.c3
-rw-r--r--lsp.h54
-rw-r--r--main.c8
7 files changed, 106 insertions, 38 deletions
diff --git a/command.h b/command.h
index 734a104..b84f7ad 100644
--- a/command.h
+++ b/command.h
@@ -59,6 +59,7 @@ ENUM_U16 {
CMD_RELOAD_ALL, // reload all buffers from file
CMD_QUIT,
+ // IDE features
CMD_SET_LANGUAGE,
CMD_AUTOCOMPLETE,
CMD_AUTOCOMPLETE_BACK,
diff --git a/ide-rename.c b/ide-rename.c
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/ide-rename.c
diff --git a/lsp-json.c b/lsp-json.c
index 2f6f0f1..6d6765d 100644
--- a/lsp-json.c
+++ b/lsp-json.c
@@ -465,6 +465,20 @@ JSONValue json_array_get(const JSON *json, JSONArray array, u64 i) {
return (JSONValue){0};
}
+// returns the `i`th key in `object`.
+JSONValue json_object_key(const JSON *json, JSONObject object, u64 i) {
+ if (i < object.len)
+ return json->values[object.items + i];
+ return (JSONValue){0};
+}
+
+// returns the `i`th value in `object`.
+JSONValue json_object_value(const JSON *json, JSONObject object, u64 i) {
+ if (i < object.len)
+ return json->values[object.items + object.len + i];
+ return (JSONValue){0};
+}
+
// returns NaN if `x` is not a number (ha ha).
double json_force_number(JSONValue x) {
if (x.type == JSON_NUMBER) {
diff --git a/lsp-parse.c b/lsp-parse.c
index 7eb8460..1d84408 100644
--- a/lsp-parse.c
+++ b/lsp-parse.c
@@ -190,6 +190,19 @@ static JSONString get_markup_content(const JSON *json, JSONValue markup_value) {
}
}
+static bool parse_text_edit(LSP *lsp, LSPResponse *response, const JSON *json, JSONValue value, LSPTextEdit *edit) {
+ JSONObject object = json_force_object(value);
+ JSONValue range = json_object_get(json, object, "range");
+ if (!parse_range(lsp, json, range, &edit->range))
+ return false;
+ JSONValue new_text_value = json_object_get(json, object, "newText");
+ if (!lsp_expect_string(lsp, new_text_value, "completion newText"))
+ return false;
+ edit->new_text = lsp_response_add_json_string(response,
+ json, new_text_value.val.string);
+ return true;
+}
+
static bool parse_completion(LSP *lsp, const JSON *json, LSPResponse *response) {
// deal with textDocument/completion response.
// result: CompletionItem[] | CompletionList | null
@@ -239,9 +252,9 @@ static bool parse_completion(LSP *lsp, const JSON *json, LSPResponse *response)
// defaults
item->sort_text = item->label;
item->filter_text = item->label;
+ item->edit_type = LSP_COMPLETION_EDIT_PLAIN;
+ item->at_cursor = true;
item->text_edit = (LSPTextEdit) {
- .type = LSP_TEXT_EDIT_PLAIN,
- .at_cursor = true,
.range = {{0}, {0}},
.new_text = item->label
};
@@ -278,13 +291,13 @@ static bool parse_completion(LSP *lsp, const JSON *json, LSPResponse *response)
double edit_type = json_object_get_number(json, item_object, "insertTextFormat");
if (!isnan(edit_type)) {
- if (edit_type != LSP_TEXT_EDIT_PLAIN && edit_type != LSP_TEXT_EDIT_SNIPPET) {
+ if (edit_type != LSP_COMPLETION_EDIT_PLAIN && edit_type != LSP_COMPLETION_EDIT_SNIPPET) {
// maybe in the future more edit types will be added.
// probably they'll have associated capabilities, but I think it's best to just ignore unrecognized types
debug_println("Bad InsertTextFormat: %g", edit_type);
- edit_type = LSP_TEXT_EDIT_PLAIN;
+ edit_type = LSP_COMPLETION_EDIT_PLAIN;
}
- item->text_edit.type = (LSPTextEditType)edit_type;
+ item->edit_type = (LSPCompletionEditType)edit_type;
}
JSONValue documentation_value = json_object_get(json, item_object, "documentation");
@@ -312,18 +325,9 @@ static bool parse_completion(LSP *lsp, const JSON *json, LSPResponse *response)
// what should happen when this completion is selected?
JSONValue text_edit_value = json_object_get(json, item_object, "textEdit");
if (text_edit_value.type == JSON_OBJECT) {
- JSONObject text_edit = text_edit_value.val.object;
- item->text_edit.at_cursor = false;
-
- JSONValue range = json_object_get(json, text_edit, "range");
- if (!parse_range(lsp, json, range, &item->text_edit.range))
+ item->at_cursor = false;
+ if (!parse_text_edit(lsp, response, json, text_edit_value, &item->text_edit))
return false;
-
- JSONValue new_text_value = json_object_get(json, text_edit, "newText");
- if (!lsp_expect_string(lsp, new_text_value, "completion newText"))
- return false;
- item->text_edit.new_text = lsp_response_add_json_string(response,
- json, new_text_value.val.string);
} else {
// not using textEdit. check insertText.
JSONValue insert_text_value = json_object_get(json, item_object, "insertText");
@@ -676,6 +680,31 @@ static bool parse_server2client_request(LSP *lsp, JSON *json, LSPRequest *reques
return false;
}
+static bool parse_workspace_edit(LSP *lsp, LSPResponse *response, const JSON *json, JSONObject object, LSPWorkspaceEdit *edit) {
+ JSONObject changes = json_object_get_object(json, object, "changes");
+ for (u32 c = 0; c < changes.len; ++c) {
+ JSONValue uri = json_object_key(json, changes, c);
+ JSONArray edits = json_force_array(json_object_value(json, changes, c));
+ LSPDocumentID document = 0;
+ if (!parse_document_uri(lsp, json, uri, &document))
+ return false;
+ for (u32 e = 0; e < edits.len; ++e) {
+ LSPWorkspaceChange *change = arr_addp(edit->changes);
+ change->type = LSP_CHANGE_EDIT;
+ change->data.edit.document = document;
+ JSONValue text_edit = json_array_get(json, edits, e);
+ if (!parse_text_edit(lsp, response, json, text_edit, &change->data.edit.edit))
+ return false;
+ }
+ }
+ todo : the other thing
+ return true;
+}
+
+static bool parse_rename(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 void process_message(LSP *lsp, JSON *json) {
@@ -733,6 +762,9 @@ static void process_message(LSP *lsp, JSON *json) {
case LSP_REQUEST_WORKSPACE_SYMBOLS:
add_to_messages = parse_workspace_symbols(lsp, json, &response);
break;
+ case LSP_REQUEST_RENAME:
+ add_to_messages = parse_rename(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.c b/lsp.c
index 18b150e..253d465 100644
--- a/lsp.c
+++ b/lsp.c
@@ -100,6 +100,9 @@ static void lsp_response_free(LSPResponse *r) {
case LSP_REQUEST_WORKSPACE_SYMBOLS:
arr_free(r->data.workspace_symbols.symbols);
break;
+ case LSP_REQUEST_RENAME:
+ arr_free(r->data.rename.changes);
+ break;
default:
break;
}
diff --git a/lsp.h b/lsp.h
index 57c81b5..fa89792 100644
--- a/lsp.h
+++ b/lsp.h
@@ -249,29 +249,19 @@ typedef enum {
#define LSP_COMPLETION_KIND_MAX 25
} LSPCompletionKind;
+typedef struct {
+ LSPRange range;
+ LSPString new_text;
+} LSPTextEdit;
// see InsertTextFormat in the LSP spec.
typedef enum {
// plain text
- LSP_TEXT_EDIT_PLAIN = 1,
+ LSP_COMPLETION_EDIT_PLAIN = 1,
// snippet e.g. "some_method($1, $2)$0"
- LSP_TEXT_EDIT_SNIPPET = 2
-} LSPTextEditType;
-
-typedef struct {
- LSPTextEditType type;
+ LSP_COMPLETION_EDIT_SNIPPET = 2
+} LSPCompletionEditType;
- // if set to true, `range` should be ignored
- // -- this is a completion which uses insertText.
- // how to handle this:
- // "VS Code when code complete is requested in this example
- // `con<cursor position>` and a completion item with an `insertText` of
- // `console` is provided it will only insert `sole`"
- bool at_cursor;
-
- LSPRange range;
- LSPString new_text;
-} LSPTextEdit;
typedef struct {
// display text for this completion
@@ -284,6 +274,15 @@ typedef struct {
LSPString documentation;
// the edit to be applied when this completion is selected.
LSPTextEdit text_edit;
+ // type for text_edit
+ LSPCompletionEditType edit_type;
+ // if set to true, `text_edit.range` should be ignored
+ // -- this is a completion which uses insertText.
+ // how to handle this:
+ // "VS Code when code complete is requested in this example
+ // `con<cursor position>` and a completion item with an `insertText` of
+ // `console` is provided it will only insert `sole`"
+ bool at_cursor;
// note: the items are sorted here in this file,
// so you probably don't need to access this.
LSPString sort_text;
@@ -346,7 +345,25 @@ typedef struct {
LSPSymbolInformation *symbols;
} LSPResponseWorkspaceSymbols;
-typedef LSPRequestType LSPResponseType;
+typedef enum {
+ LSP_CHANGE_EDIT = 1
+} LSPWorkspaceChangeType;
+
+typedef struct {
+ LSPWorkspaceChangeType type;
+ union {
+ struct {
+ LSPDocumentID document;
+ LSPTextEdit edit;
+ } edit;
+ } data;
+} LSPWorkspaceChange;
+
+typedef struct {
+ LSPWorkspaceChange *changes;
+} LSPWorkspaceEdit;
+typedef LSPWorkspaceEdit LSPResponseRename;
+
typedef struct {
LSPRequest request; // the request which this is a response to
char *error; // if not NULL, the data field will just be zeroed
@@ -361,6 +378,7 @@ typedef struct {
LSPResponseHover hover;
LSPResponseDefinition definition;
LSPResponseWorkspaceSymbols workspace_symbols;
+ LSPResponseRename rename;
} data;
} LSPResponse;
diff --git a/main.c b/main.c
index 63a07a1..6613f2e 100644
--- a/main.c
+++ b/main.c
@@ -1,13 +1,12 @@
/*
@TODO:
- more LSP stuff:
- - find usages
- - rename
- - check for others
+ - document highlight (textDocument/documentHighlight)
+ - find usages (textDocument/references)
+ - rename (textDocument/rename)
- handle multiple symbols with same name in go-to-definition menu
- :go-to-cursor-definition
- test full unicode position handling
-- 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.
- add last_request_id checking to autocomplete_process_lsp_response
@@ -39,6 +38,7 @@ FUTURE FEATURES:
- robust find (results shouldn't move around when you type things)
- multiple files with command line arguments
- :set-build-command
+- document links using LSP textDocument/documentLink request
- add numlock as a key modifier? (but make sure "Ctrl+S" handles both "No NumLock+Ctrl+S" and "NumLock+Ctrl+S")
- better undo chaining (dechain on backspace?)
- allow multiple fonts (fonts directory?)