summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorpommicket <pommicket@gmail.com>2022-12-22 18:55:15 -0500
committerpommicket <pommicket@gmail.com>2022-12-22 18:56:41 -0500
commit91ff61cc22c08e2c247b6b689561e6d18cf276e7 (patch)
treef8025b141122ba219fbe45a1a9c2770e49d76905
parent2667c71e71d77ecade1142c133ed7181ce38c664 (diff)
detail text
-rw-r--r--autocomplete.c37
-rw-r--r--json.c43
-rw-r--r--lsp-parse.c31
-rw-r--r--lsp.h2
-rw-r--r--main.c2
-rw-r--r--ted.h1
-rw-r--r--unicode.h3
7 files changed, 100 insertions, 19 deletions
diff --git a/autocomplete.c b/autocomplete.c
index 1421201..eb5993f 100644
--- a/autocomplete.c
+++ b/autocomplete.c
@@ -142,6 +142,8 @@ static void autocomplete_process_lsp_response(Ted *ted, const LSPResponse *respo
ted_completion->label = str_dup(lsp_response_string(response, lsp_completion->label));
ted_completion->filter = str_dup(lsp_response_string(response, lsp_completion->filter_text));
ted_completion->text = str_dup(lsp_response_string(response, lsp_completion->text_edit.new_text));
+ const char *detail = lsp_response_string(response, lsp_completion->detail);
+ ted_completion->detail = *detail ? str_dup(detail) : NULL;
}
}
autocomplete_update_suggested(ted);
@@ -201,11 +203,11 @@ static void autocomplete_frame(Ted *ted) {
autocomplete_find_completions(ted);
- char *completions[AUTOCOMPLETE_NCOMPLETIONS_VISIBLE] = {0};
+ Autocompletion completions[AUTOCOMPLETE_NCOMPLETIONS_VISIBLE] = {0};
size_t ncompletions = 0;
arr_foreach_ptr(ac->suggested, u32, suggestion) {
Autocompletion *completion = &ac->completions[*suggestion];
- completions[ncompletions++] = completion->label;
+ completions[ncompletions++] = *completion;
if (ncompletions == AUTOCOMPLETE_NCOMPLETIONS_VISIBLE)
break;
}
@@ -280,7 +282,36 @@ static void autocomplete_frame(Ted *ted) {
} else {
for (size_t i = 0; i < ncompletions; ++i) {
state.x = x + padding; state.y = y;
- text_utf8_with_state(font, &state, completions[i]);
+ text_utf8_with_state(font, &state, completions[i].label);
+ const char *detail = completions[i].detail;
+ if (detail) {
+ double label_end_x = state.x;
+
+ char show_text[128] = {0};
+
+ int amount_detail = 0;
+ for (; ; ++amount_detail) {
+ if (unicode_is_continuation_byte((u8)detail[amount_detail]))
+ continue; // don't cut off text in the middle of a code point.
+
+ char text[128] = {0};
+ strbuf_printf(text, "%.*s%s", amount_detail, detail,
+ (size_t)amount_detail == strlen(detail) ? "" : "...");
+ double width = text_get_size_v2(font, text).x;
+ if (label_end_x + width + 2 * padding < state.max_x) {
+ strbuf_cpy(show_text, text);
+ }
+ // don't break if not, since we want to use "blabla"
+ // even if "blabl..." is too long
+
+ if (!detail[amount_detail]) break;
+ }
+ if (amount_detail >= 3) {
+ //rgba_u32_to_floats(colors[COLOR_COMMENT], state.color);
+ text_utf8_anchored(font, show_text, state.max_x, state.y,
+ colors[COLOR_COMMENT], ANCHOR_TOP_RIGHT);
+ }
+ }
y += char_height;
}
}
diff --git a/json.c b/json.c
index c11d907..e07b0ec 100644
--- a/json.c
+++ b/json.c
@@ -444,6 +444,49 @@ JSONValue json_object_get(const JSON *json, JSONObject object, const char *name)
return (JSONValue){0};
}
+// returns (JSONString){0} (which is interpreted as an empty string) if `name` does
+// not exist or is not a string.
+JSONString json_object_get_string(const JSON *json, JSONObject object, const char *name) {
+ JSONValue value = json_object_get(json, object, name);
+ if (value.type == JSON_STRING) {
+ return value.val.string;
+ } else {
+ return (JSONString){0};
+ }
+}
+
+// returns (JSONObject){0} (which is interpreted as an empty object) if `name` does
+// not exist or is not an object.
+JSONObject json_object_get_object(const JSON *json, JSONObject object, const char *name) {
+ JSONValue value = json_object_get(json, object, name);
+ if (value.type == JSON_OBJECT) {
+ return value.val.object;
+ } else {
+ return (JSONObject){0};
+ }
+}
+
+// returns (JSONArray){0} (which is interpreted as an empty array) if `name` does
+// not exist or is not an array.
+JSONArray json_object_get_array(const JSON *json, JSONObject object, const char *name) {
+ JSONValue value = json_object_get(json, object, name);
+ if (value.type == JSON_ARRAY) {
+ return value.val.array;
+ } else {
+ return (JSONArray){0};
+ }
+}
+
+// returns NaN if `name` does not exist or is not a number.
+double json_object_get_number(const JSON *json, JSONObject object, const char *name) {
+ JSONValue value = json_object_get(json, object, name);
+ if (value.type == JSON_NUMBER) {
+ return value.val.number;
+ } else {
+ return NAN;
+ }
+}
+
JSONValue json_array_get(const JSON *json, JSONArray array, u64 i) {
if (i < array.len) {
return json->values[array.elements + i];
diff --git a/lsp-parse.c b/lsp-parse.c
index 6167cc8..8afca07 100644
--- a/lsp-parse.c
+++ b/lsp-parse.c
@@ -130,31 +130,32 @@ static bool parse_completion(LSP *lsp, const JSON *json, LSPResponse *response)
.new_text = item->label
};
- JSONValue sort_text_value = json_object_get(json, item_object, "sortText");
- if (sort_text_value.type == JSON_STRING) {
+ JSONString sort_text = json_object_get_string(json, item_object, "sortText");
+ if (sort_text.pos) {
// LSP allows using a different string for sorting.
- item->sort_text = lsp_response_add_json_string(response,
- json, sort_text_value.val.string);
+ item->sort_text = lsp_response_add_json_string(response, json, sort_text);
}
- JSONValue filter_text_value = json_object_get(json, item_object, "filterText");
- if (filter_text_value.type == JSON_STRING) {
+ JSONString filter_text = json_object_get_string(json, item_object, "filterText");
+ if (filter_text.pos) {
// LSP allows using a different string for filtering.
- item->filter_text = lsp_response_add_json_string(response,
- json, filter_text_value.val.string);
+ item->filter_text = lsp_response_add_json_string(response, json, filter_text);
}
- JSONValue text_type_value = json_object_get(json, item_object, "insertTextFormat");
- if (text_type_value.type == JSON_NUMBER) {
- double type = text_type_value.val.number;
- if (type != LSP_TEXT_EDIT_PLAIN && type != LSP_TEXT_EDIT_SNIPPET) {
- lsp_set_error(lsp, "Bad InsertTextFormat: %g", type);
+ 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) {
+ lsp_set_error(lsp, "Bad InsertTextFormat: %g", edit_type);
return false;
}
- item->text_edit.type = (LSPTextEditType)type;
+ item->text_edit.type = (LSPTextEditType)edit_type;
}
- // @TODO: detail
+
+ JSONString detail_text = json_object_get_string(json, item_object, "detail");
+ if (detail_text.pos) {
+ item->detail = lsp_response_add_json_string(response, json, detail_text);
+ }
// @TODO(eventually): additionalTextEdits
// (try to find a case where this comes up)
diff --git a/lsp.h b/lsp.h
index d8d8029..d965cf6 100644
--- a/lsp.h
+++ b/lsp.h
@@ -136,6 +136,8 @@ typedef struct {
LSPString label;
// text used to filter completions
LSPString filter_text;
+ // more detail for this item, e.g. the signature of a function
+ LSPString detail;
// the edit to be applied when this completion is selected.
LSPTextEdit text_edit;
// note: the items are sorted here in this file,
diff --git a/main.c b/main.c
index 5bd0a47..364e7e3 100644
--- a/main.c
+++ b/main.c
@@ -1,6 +1,6 @@
/*
@TODO:
-- show detail and type
+- only show "Loading..." if it's taking some time (prevent flash)
- LSP setting
- scroll through completions
- figure out under which circumstances backspace should close completions
diff --git a/ted.h b/ted.h
index a2c0c98..d56f3ab 100644
--- a/ted.h
+++ b/ted.h
@@ -356,6 +356,7 @@ typedef struct {
char *label;
char *filter;
char *text;
+ char *detail; // this can be NULL!
} Autocompletion;
typedef struct {
diff --git a/unicode.h b/unicode.h
index fb9810a..fdd5669 100644
--- a/unicode.h
+++ b/unicode.h
@@ -8,6 +8,9 @@ static bool unicode_is_start_of_code_point(u8 byte) {
// continuation bytes are of the form 10xxxxxx
return (byte & 0xC0) != 0x80;
}
+static bool unicode_is_continuation_byte(u8 byte) {
+ return (byte & 0xC0) == 0x80;
+}
// A lot like mbrtoc32. Doesn't depend on the locale though, for one thing.
// *c will be filled with the next UTF-8 code point in `str`. `bytes` refers to the maximum