summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build.c156
-rw-r--r--command.c5
-rw-r--r--command.h2
-rw-r--r--ide-usages.c68
-rw-r--r--lsp.c1
-rw-r--r--main.c7
-rw-r--r--ted.cfg2
-rw-r--r--ted.h7
8 files changed, 172 insertions, 76 deletions
diff --git a/build.c b/build.c
index 9c5ee7a..04a9488 100644
--- a/build.c
+++ b/build.c
@@ -61,12 +61,16 @@ static bool build_run_next_command_in_queue(Ted *ted) {
}
}
-// make sure you set ted->build_dir before running this!
-static void build_queue_finish(Ted *ted) {
+void build_setup_buffer(Ted *ted) {
// new empty build output buffer
TextBuffer *build_buffer = &ted->build_buffer;
buffer_new_file(build_buffer, NULL);
build_buffer->store_undo_events = false; // don't need undo events for build output buffer
+}
+
+// make sure you set ted->build_dir before running this!
+static void build_queue_finish(Ted *ted) {
+ build_setup_buffer(ted);
build_run_next_command_in_queue(ted); // run the first command
}
@@ -160,7 +164,82 @@ static bool is_source_path(char32_t c) {
|| strchr(allowed_ascii_symbols_in_path, (char)c);
}
-static void build_frame(Ted *ted, float x1, float y1, float x2, float y2) {
+// make sure you set ted->build_dir before running this!
+void build_check_for_errors(Ted *ted) {
+ TextBuffer *buffer = &ted->build_buffer;
+ arr_clear(ted->build_errors);
+ for (u32 line_idx = 0; line_idx < buffer->nlines; ++line_idx) {
+ Line *line = &buffer->lines[line_idx];
+ if (line->len < 3) {
+ continue;
+ }
+ bool is_error = true;
+ char32_t *p = line->str, *end = p + line->len;
+
+ {
+ // rust errors look like:
+ // " --> file:line:column"
+ while (p != end && *p == ' ') {
+ ++p;
+ }
+ if (end - p >= 4 && p[0] == '-' && p[1] == '-' && p[2] == '>' && p[3] == ' ') {
+ p += 4;
+ }
+ }
+
+ // check if we have something like main.c:5 or main.c(5)
+
+ // get file name
+ char32_t *filename_start = p;
+ while (p != end) {
+ if ((*p == ':' || *p == '(')
+ && p != line->str + 1) // don't catch "C:\thing\whatever.c" as "filename: C, line number: \thing\whatever.c"
+ break;
+ if (!is_source_path(*p)) {
+ is_error = false;
+ break;
+ }
+ ++p;
+ }
+ if (p == end) is_error = false;
+ u32 filename_len = (u32)(p - filename_start);
+ if (filename_len == 0) is_error = false;
+
+ if (is_error) {
+ ++p; // move past : or (
+ int line_number = parse_nonnegative_integer(&p, end);
+ if (p != end && line_number > 0) {
+ // it's an error
+ line_number -= 1; // line numbers in output start from 1.
+ int column_number = 0;
+ // check if there's a column number
+ if (*p == ':') {
+ ++p; // move past :
+ int num = parse_nonnegative_integer(&p, end);
+ if (num > 0) {
+ column_number = num - 1; // column numbers in output start from 1
+ }
+ }
+ char *filename = str32_to_utf8_cstr(str32(filename_start, filename_len));
+ if (filename) {
+ char full_path[TED_PATH_MAX];
+ path_full(ted->build_dir, filename, full_path, sizeof full_path);
+ BuildError error = {
+ .filename = str_dup(full_path),
+ .pos = {.line = (u32)line_number, .index = (u32)column_number},
+ .build_output_line = line_idx
+ };
+ arr_add(ted->build_errors, error);
+ }
+ }
+ }
+ }
+ // go to the first error (if there is one)
+ ted->build_error = 0;
+ build_go_to_error(ted);
+}
+
+void build_frame(Ted *ted, float x1, float y1, float x2, float y2) {
TextBuffer *buffer = &ted->build_buffer;
Process *process = &ted->build_process;
assert(ted->build_shown);
@@ -230,76 +309,7 @@ static void build_frame(Ted *ted, float x1, float y1, float x2, float y2) {
if (!build_run_next_command_in_queue(ted)) {
ted->building = false;
// done command queue; check for errors
- for (u32 line_idx = 0; line_idx < buffer->nlines; ++line_idx) {
- Line *line = &buffer->lines[line_idx];
- if (line->len < 3) {
- continue;
- }
- bool is_error = true;
- char32_t *p = line->str, *end = p + line->len;
-
- {
- // rust errors look like:
- // " --> file:line:column"
- while (p != end && *p == ' ') {
- ++p;
- }
- if (end - p >= 4 && p[0] == '-' && p[1] == '-' && p[2] == '>' && p[3] == ' ') {
- p += 4;
- }
- }
-
- // check if we have something like main.c:5 or main.c(5)
-
- // get file name
- char32_t *filename_start = p;
- while (p != end) {
- if ((*p == ':' || *p == '(')
- && p != line->str + 1) // don't catch "C:\thing\whatever.c" as "filename: C, line number: \thing\whatever.c"
- break;
- if (!is_source_path(*p)) {
- is_error = false;
- break;
- }
- ++p;
- }
- if (p == end) is_error = false;
- u32 filename_len = (u32)(p - filename_start);
- if (filename_len == 0) is_error = false;
-
- if (is_error) {
- ++p; // move past : or (
- int line_number = parse_nonnegative_integer(&p, end);
- if (p != end && line_number > 0) {
- // it's an error
- line_number -= 1; // line numbers in output start from 1.
- int column_number = 0;
- // check if there's a column number
- if (*p == ':') {
- ++p; // move past :
- int num = parse_nonnegative_integer(&p, end);
- if (num > 0) {
- column_number = num - 1; // column numbers in output start from 1
- }
- }
- char *filename = str32_to_utf8_cstr(str32(filename_start, filename_len));
- if (filename) {
- char full_path[TED_PATH_MAX];
- path_full(ted->build_dir, filename, full_path, sizeof full_path);
- BuildError error = {
- .filename = str_dup(full_path),
- .pos = {.line = (u32)line_number, .index = (u32)column_number},
- .build_output_line = line_idx
- };
- arr_add(ted->build_errors, error);
- }
- }
- }
- }
-
- // go to the first error (if there is one)
- ted->build_error = 0;
- build_go_to_error(ted);
+ build_check_for_errors(ted);
}
}
buffer->view_only = true;
diff --git a/command.c b/command.c
index 4f2d643..d49f57a 100644
--- a/command.c
+++ b/command.c
@@ -293,7 +293,9 @@ void command_execute(Ted *ted, Command c, i64 argument) {
if (ted->autocomplete.open)
autocomplete_prev(ted);
break;
-
+ case CMD_FIND_USAGES:
+ usages_find(ted);
+ break;
case CMD_UNDO:
if (buffer) buffer_undo(buffer, argument);
break;
@@ -395,6 +397,7 @@ void command_execute(Ted *ted, Command c, i64 argument) {
case CMD_ESCAPE:
definition_cancel_lookup(ted);
+ usages_cancel_lookup(ted);
if (*ted->error_shown) {
// dismiss error box
*ted->error_shown = '\0';
diff --git a/command.h b/command.h
index b84f7ad..000b050 100644
--- a/command.h
+++ b/command.h
@@ -63,6 +63,7 @@ ENUM_U16 {
CMD_SET_LANGUAGE,
CMD_AUTOCOMPLETE,
CMD_AUTOCOMPLETE_BACK,
+ CMD_FIND_USAGES,
CMD_COPY,
CMD_CUT,
@@ -161,6 +162,7 @@ static CommandName const command_names[] = {
{"paste", CMD_PASTE},
{"autocomplete", CMD_AUTOCOMPLETE},
{"autocomplete-back", CMD_AUTOCOMPLETE_BACK},
+ {"find-usages", CMD_FIND_USAGES},
{"find", CMD_FIND},
{"find-replace", CMD_FIND_REPLACE},
{"tab-close", CMD_TAB_CLOSE},
diff --git a/ide-usages.c b/ide-usages.c
new file mode 100644
index 0000000..21999be
--- /dev/null
+++ b/ide-usages.c
@@ -0,0 +1,68 @@
+void usages_cancel_lookup(Ted *ted) {
+ Usages *usages = &ted->usages;
+ if (usages->last_request_id) {
+ LSP *lsp = ted_get_lsp_by_id(ted, usages->last_request_lsp);
+ lsp_cancel_request(lsp, usages->last_request_id);
+ usages->last_request_id = 0;
+ }
+}
+
+void usages_find(Ted *ted) {
+ Usages *usages = &ted->usages;
+ TextBuffer *buffer = ted->active_buffer;
+ if (!buffer) return;
+ LSP *lsp = buffer_lsp(buffer);
+ if (!lsp) return;
+
+ // send the request
+ LSPRequest request = {.type = LSP_REQUEST_REFERENCES};
+ LSPRequestReferences *refs = &request.data.references;
+ refs->include_declaration = true;
+ refs->position = buffer_cursor_pos_as_lsp_document_position(buffer);
+ usages_cancel_lookup(ted);
+ usages->last_request_lsp = lsp->id;
+ usages->last_request_id = lsp_send_request(lsp, &request);
+ usages->last_request_time = ted->frame_time;
+}
+
+
+void usages_process_lsp_response(Ted *ted, LSPResponse *response) {
+ Usages *usages = &ted->usages;
+ if (response->request.type != LSP_REQUEST_REFERENCES)
+ return; // not for us
+ if (response->request.id != usages->last_request_id)
+ return;
+ LSP *lsp = ted_get_lsp_by_id(ted, usages->last_request_lsp);
+ LSPResponseReferences *refs = &response->data.references;
+ if (lsp && arr_len(refs->locations)) {
+ TextBuffer *buffer = &ted->build_buffer;
+ build_setup_buffer(ted);
+ ted->build_shown = true;
+ arr_foreach_ptr(refs->locations, LSPLocation, location) {
+ char text[1024];
+ strbuf_printf(text, "%s:%u: \n",
+ lsp_document_path(lsp, location->document),
+ location->range.start.line + 1);
+ buffer_insert_utf8_at_cursor(buffer, text);
+ buffer_cursor_move_to_end_of_file(buffer);
+ }
+ buffer->view_only = true;
+
+ // the build_dir doesn't really matter since we're using absolute paths
+ // but might as well set it to something reasonable.
+ char *root = ted_get_root_dir(ted);
+ strbuf_cpy(ted->build_dir, root);
+ free(root);
+
+ build_check_for_errors(ted);
+ } else {
+ ted->cursor_error_time = time_get_seconds();
+ }
+ usages->last_request_id = 0;
+}
+
+void usages_frame(Ted *ted) {
+ Usages *usages = &ted->usages;
+ if (usages->last_request_id && timespec_sub(ted->frame_time, usages->last_request_time) > 0.2)
+ ted->cursor = ted->cursor_wait; // this request is takin a while
+}
diff --git a/lsp.c b/lsp.c
index 81cb4c9..16e0087 100644
--- a/lsp.c
+++ b/lsp.c
@@ -604,6 +604,7 @@ bool lsp_covers_path(LSP *lsp, const char *path) {
void lsp_cancel_request(LSP *lsp, LSPRequestID id) {
if (!id) return;
+ if (!lsp) return;
LSPRequest request = {.type = LSP_REQUEST_CANCEL};
request.data.cancel.id = id;
diff --git a/main.c b/main.c
index dca6fb5..38d4e4b 100644
--- a/main.c
+++ b/main.c
@@ -1,8 +1,8 @@
/*
@TODO:
-- find usages (textDocument/references)
-- different highlight colors
+- show line containing usage
- framerate-cap setting
+- change frame_time to a double
- highlight-enabled, and highlight-auto
- handle multiple symbols with same name in go-to-definition menu
- :go-to-cursor-definition
@@ -156,6 +156,7 @@ bool tag_goto(Ted *ted, char const *tag);
#include "ide-hover.c"
#include "ide-definitions.c"
#include "ide-highlights.c"
+#include "ide-usages.c"
#include "command.c"
#include "config.c"
#include "session.c"
@@ -920,6 +921,7 @@ int main(int argc, char **argv) {
hover_process_lsp_response(ted, r);
definitions_process_lsp_response(ted, lsp, r);
highlights_process_lsp_response(ted, r);
+ usages_process_lsp_response(ted, r);
} break;
}
lsp_message_free(&message);
@@ -1028,6 +1030,7 @@ int main(int argc, char **argv) {
hover_frame(ted, frame_dt);
definitions_frame(ted);
highlights_frame(ted);
+ usages_frame(ted);
} else {
autocomplete_close(ted);
text_utf8_anchored(font, "Press Ctrl+O to open a file or Ctrl+N to create a new one.",
diff --git a/ted.cfg b/ted.cfg
index f81c079..88204db 100644
--- a/ted.cfg
+++ b/ted.cfg
@@ -201,9 +201,11 @@ Ctrl+Shift+r = :reload-all
# 16 GLSL
# -1 Guess from the file extension (default)
+# IDE features
Ctrl+Space = :autocomplete
# go to previous completion
Ctrl+Shift+Space = :autocomplete-back
+Ctrl+u = :find-usages
Ctrl+z = :undo
Ctrl+Shift+z = :redo
diff --git a/ted.h b/ted.h
index 45a9531..41f7bbc 100644
--- a/ted.h
+++ b/ted.h
@@ -385,6 +385,12 @@ typedef struct {
} Autocomplete;
typedef struct {
+ LSPID last_request_lsp;
+ LSPRequestID last_request_id;
+ struct timespec last_request_time;
+} Usages;
+
+typedef struct {
// displayed normal
char *label_pre;
// displayed bold
@@ -502,6 +508,7 @@ typedef struct Ted {
Hover hover;
Definitions definitions;
Highlights highlights;
+ Usages usages;
FILE *log;