summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt5
-rw-r--r--README.md2
-rw-r--r--buffer.c23
-rw-r--r--command.c5
-rw-r--r--command.h1
-rw-r--r--ide-document-link.c3
-rw-r--r--ide-rename-symbol.c91
-rw-r--r--lsp-write.c3
-rw-r--r--main.c4
-rw-r--r--menu.c68
-rw-r--r--ted.cfg3
-rw-r--r--ted.h15
12 files changed, 202 insertions, 21 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 32a8b50..3641e65 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -3,8 +3,9 @@ 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-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)
+ ide-usages.c ide-rename-symbol.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/README.md b/README.md
index bc5d9e2..0448170 100644
--- a/README.md
+++ b/README.md
@@ -246,7 +246,7 @@ chmod +x ~/.local/bin/rust-analyzer
## Tags (lightweight LSP alternative)
If an LSP is too much for you, you can also use [ctags](https://github.com/universal-ctags/ctags)
-for autocompletion and jump to definition. You can press Ctrl+T
+for autocompletion and jump to definition. You can run the `:generate-tags` command
at any time to generate or re-generate tags.
Ctrl+Click (go to definition), Ctrl+D (see all definitions), and autocomplete are all supported.
Autocomplete will just complete to stuff in the tags file, so it won't complete local
diff --git a/buffer.c b/buffer.c
index 05e1f43..fae8924 100644
--- a/buffer.c
+++ b/buffer.c
@@ -1386,24 +1386,33 @@ i64 buffer_cursor_move_right_words(TextBuffer *buffer, i64 nwords) {
return ret;
}
-String32 buffer_word_at_pos(TextBuffer *buffer, BufferPos pos) {
+void buffer_word_span_at_pos(TextBuffer *buffer, BufferPos pos, u32 *word_start, u32 *word_end) {
buffer_pos_validate(buffer, &pos);
Line *line = &buffer->lines[pos.line];
char32_t *str = line->str;
- i64 word_start, word_end;
- for (word_start = pos.index; word_start > 0; --word_start) {
- if (!is32_word(str[word_start - 1]))
+ i64 start, end;
+ for (start = pos.index; start > 0; --start) {
+ if (!is32_word(str[start - 1]))
break;
}
- for (word_end = pos.index; word_end < line->len; ++word_end) {
- if (!is32_word(str[word_end]))
+ for (end = pos.index; end < line->len; ++end) {
+ if (!is32_word(str[end]))
break;
}
+ *word_start = (u32)start;
+ *word_end = (u32)end;
+}
+
+String32 buffer_word_at_pos(TextBuffer *buffer, BufferPos pos) {
+ buffer_pos_validate(buffer, &pos);
+ Line *line = &buffer->lines[pos.line];
+ u32 word_start=0, word_end=0;
+ buffer_word_span_at_pos(buffer, pos, &word_start, &word_end);
u32 len = (u32)(word_end - word_start);
if (len == 0)
return str32(NULL, 0);
else
- return str32(str + word_start, len);
+ return str32(line->str + word_start, len);
}
String32 buffer_word_at_cursor(TextBuffer *buffer) {
diff --git a/command.c b/command.c
index 65d2072..cce923d 100644
--- a/command.c
+++ b/command.c
@@ -104,6 +104,7 @@ static CommandName command_names[] = {
{"macro-execute", CMD_MACRO_EXECUTE},
{"increment-number", CMD_INCREMENT_NUMBER},
{"decrement-number", CMD_DECREMENT_NUMBER},
+ {"rename-symbol", CMD_RENAME_SYMBOL},
};
static_assert_if_possible(arr_count(command_names) == CMD_COUNT)
@@ -679,5 +680,9 @@ void command_execute_ex(Ted *ted, Command c, CommandArgument full_argument, Comm
case CMD_MACRO_EXECUTE:
macro_execute(ted, (u32)argument);
break;
+ case CMD_RENAME_SYMBOL:
+ if (buffer && buffer_lsp(buffer))
+ menu_open(ted, MENU_RENAME_SYMBOL);
+ break;
}
}
diff --git a/command.h b/command.h
index 72dfb7c..e83f71e 100644
--- a/command.h
+++ b/command.h
@@ -104,6 +104,7 @@ typedef enum {
CMD_AUTOCOMPLETE,
CMD_AUTOCOMPLETE_BACK,
CMD_FIND_USAGES,
+ CMD_RENAME_SYMBOL,
/// "go to definition of..." menu
CMD_GOTO_DEFINITION,
CMD_GOTO_DEFINITION_AT_CURSOR,
diff --git a/ide-document-link.c b/ide-document-link.c
index 2328a6b..731089c 100644
--- a/ide-document-link.c
+++ b/ide-document-link.c
@@ -86,7 +86,8 @@ void document_link_frame(Ted *ted) {
void document_link_process_lsp_response(Ted *ted, const LSPResponse *response) {
DocumentLinks *dl = &ted->document_links;
- if (response->request.type != LSP_REQUEST_DOCUMENT_LINK)
+ if (response->request.type != LSP_REQUEST_DOCUMENT_LINK
+ || response->request.id != dl->last_request.id)
return;
if (!dl->last_request.id)
return; // request was cancelled
diff --git a/ide-rename-symbol.c b/ide-rename-symbol.c
new file mode 100644
index 0000000..ee2f6e1
--- /dev/null
+++ b/ide-rename-symbol.c
@@ -0,0 +1,91 @@
+#include "ted.h"
+
+void rename_symbol_clear(Ted *ted) {
+ RenameSymbol *rs = &ted->rename_symbol;
+ ted_cancel_lsp_request(ted, &rs->request_id);
+ free(rs->new_name);
+ rs->new_name = NULL;
+ if (ted->menu == MENU_RENAME_SYMBOL)
+ menu_close(ted);
+}
+
+void rename_symbol_frame(Ted *ted) {
+ RenameSymbol *rs = &ted->rename_symbol;
+ TextBuffer *buffer = ted->prev_active_buffer;
+ LSP *lsp = buffer ? buffer_lsp(buffer) : NULL;
+
+ if (ted->menu != MENU_RENAME_SYMBOL || !buffer || !lsp) {
+ rename_symbol_clear(ted);
+ return;
+ }
+
+ if (rs->new_name && !rs->request_id.id) {
+ // send the request
+ LSPRequest request = {.type = LSP_REQUEST_RENAME};
+ LSPRequestRename *data = &request.data.rename;
+ data->position = buffer_pos_to_lsp_document_position(buffer, buffer->cursor_pos);
+ data->new_name = str_dup(rs->new_name);
+ rs->request_id = lsp_send_request(lsp, &request);
+ }
+
+ // we're just waitin'
+ ted->cursor = ted->cursor_wait;
+}
+
+void rename_symbol_process_lsp_response(Ted *ted, LSPResponse *response) {
+ RenameSymbol *rs = &ted->rename_symbol;
+ if (response->request.type != LSP_REQUEST_RENAME
+ || response->request.id != rs->request_id.id) {
+ return;
+ }
+
+ LSPResponseRename *data = &response->data.rename;
+ LSP *lsp = ted_get_lsp_by_id(ted, rs->request_id.lsp);
+ if (!lsp) {
+ // LSP crashed or something
+ return;
+ }
+
+ assert(rs->new_name);
+
+ bool perform_changes = true;
+ arr_foreach_ptr(data->changes, LSPWorkspaceChange, change) {
+ if (change->type != LSP_CHANGE_EDIT) {
+ // TODO(eventually) : support these
+ ted_error(ted, "rename is too complicated for ted to perform.");
+ perform_changes = false;
+ }
+ }
+
+ if (perform_changes) {
+ arr_foreach_ptr(data->changes, LSPWorkspaceChange, change) {
+ printf("change type %u\n",change->type);
+ switch (change->type) {
+ case LSP_CHANGE_EDIT: {
+ LSPWorkspaceChangeEdit *edit = &change->data.edit;
+ const char *path = lsp_document_path(lsp, edit->document);
+ if (!ted_open_file(ted, path)) goto done;
+
+ TextBuffer *buffer = ted_get_buffer_with_file(ted, path);
+ if (!buffer) {
+ // this should never happen since we just
+ // successfully opened it
+ assert(0);
+ goto done;
+ }
+ printf("%s\n",path);
+
+ }
+ break;
+ case LSP_CHANGE_RENAME:
+ case LSP_CHANGE_DELETE:
+ case LSP_CHANGE_CREATE:
+ assert(0);
+ break;
+ }
+ }
+ done:;
+ }
+
+ rename_symbol_clear(ted);
+}
diff --git a/lsp-write.c b/lsp-write.c
index 85c5781..20a72ca 100644
--- a/lsp-write.c
+++ b/lsp-write.c
@@ -458,7 +458,8 @@ void write_request(LSP *lsp, LSPRequest *request) {
write_key_obj_start(o, "workspace");
write_key_bool(o, "workspaceFolders", true);
write_key_obj_start(o, "workspaceEdit");
- write_key_bool(o, "documentChanges", true);
+ // currently unsupported -- see ide-rename-symbol.c
+ // write_key_bool(o, "documentChanges", true);
write_key_arr_start(o, "resourceOperations");
write_arr_elem_string(o, "create");
write_arr_elem_string(o, "rename");
diff --git a/main.c b/main.c
index cbcf708..d72e217 100644
--- a/main.c
+++ b/main.c
@@ -74,6 +74,7 @@ FUTURE FEATURES:
#include "menu.c"
#include "ide-autocomplete.c"
#include "ide-signature-help.c"
+#include "ide-rename-symbol.c"
#include "ide-hover.c"
#include "ide-definitions.c"
#include "ide-highlights.c"
@@ -904,6 +905,7 @@ int main(int argc, char **argv) {
highlights_process_lsp_response(ted, r);
usages_process_lsp_response(ted, r);
document_link_process_lsp_response(ted, r);
+ rename_symbol_process_lsp_response(ted, r);
} break;
}
lsp_message_free(&message);
@@ -1012,6 +1014,7 @@ int main(int argc, char **argv) {
highlights_frame(ted);
usages_frame(ted);
document_link_frame(ted);
+ rename_symbol_frame(ted);
} else {
autocomplete_close(ted);
if (!ted->build_shown) {
@@ -1192,6 +1195,7 @@ int main(int argc, char **argv) {
autocomplete_close(ted);
highlights_close(ted);
session_write(ted);
+ rename_symbol_clear(ted);
document_link_clear(ted);
for (int i = 0; i < TED_LSP_MAX; ++i) {
diff --git a/menu.c b/menu.c
index 5a65ea4..026913d 100644
--- a/menu.c
+++ b/menu.c
@@ -41,6 +41,9 @@ static void menu_close_with_next(Ted *ted, Menu next) {
case MENU_SHELL:
buffer_clear(&ted->line_buffer);
break;
+ case MENU_RENAME_SYMBOL:
+ buffer_clear(&ted->line_buffer);
+ break;
}
ted->menu = MENU_NONE;
ted->selector_open = NULL;
@@ -97,6 +100,9 @@ void menu_open(Ted *ted, Menu menu) {
ted_switch_to_buffer(ted, &ted->line_buffer);
ted->shell_history_pos = arr_len(ted->shell_history);
break;
+ case MENU_RENAME_SYMBOL:
+ ted_switch_to_buffer(ted, &ted->line_buffer);
+ break;
}
}
@@ -320,6 +326,12 @@ void menu_update(Ted *ted) {
build_start_with_command(ted, command);
}
break;
+ case MENU_RENAME_SYMBOL: {
+ RenameSymbol *rs = &ted->rename_symbol;
+ if (line_buffer->line_buffer_submitted && !rs->new_name) {
+ rs->new_name = str32_to_utf8_cstr(buffer_get_line(line_buffer, 0));
+ }
+ } break;
}
}
@@ -436,20 +448,60 @@ void menu_render(Ted *ted) {
text_render(font_bold);
} break;
- case MENU_SHELL: {
+ case MENU_SHELL: {
bounds.size.y = line_buffer_height + 2 * padding;
- rect_coords(bounds, &x1, &y1, &x2, &y2);
-
gl_geometry_rect(bounds, colors[COLOR_MENU_BG]);
gl_geometry_rect_border(bounds, settings->border_thickness, colors[COLOR_BORDER]);
gl_geometry_draw();
+ bounds = rect_shrink(bounds, padding);
+ rect_coords(bounds, &x1, &y1, &x2, &y2);
+ const char *text = "Run";
+ text_utf8(font_bold, text, x1, y1, colors[COLOR_TEXT]);
+ x1 += text_get_size_vec2(font_bold, text).x + padding;
+ text_render(font_bold);
- x1 += padding;
- y1 += padding;
- x2 -= padding;
- y2 -= padding;
+ buffer_render(&ted->line_buffer, rect4(x1, y1, x2, y2));
+ } break;
+ case MENU_RENAME_SYMBOL: {
+ // highlight symbol
+ TextBuffer *buffer = ted->prev_active_buffer;
+ if (!buffer) {
+ menu_close(ted);
+ return;
+ }
+ RenameSymbol *rs = &ted->rename_symbol;
+ if (rs->new_name) {
+ // already entered a new name
+ return;
+ }
- const char *text = "Run";
+ u32 sym_start=0, sym_end=0;
+ BufferPos cursor_pos = buffer->cursor_pos;
+ buffer_word_span_at_pos(buffer, cursor_pos, &sym_start, &sym_end);
+ BufferPos bpos0 = {
+ .line = cursor_pos.line,
+ .index = sym_start
+ };
+ BufferPos bpos1 = {
+ .line = cursor_pos.line,
+ .index = sym_end
+ };
+ // symbol should span from pos0 to pos1
+ vec2 p0 = buffer_pos_to_pixels(buffer, bpos0);
+ vec2 p1 = buffer_pos_to_pixels(buffer, bpos1);
+ p1.y += text_font_char_height(buffer_font(buffer));
+ Rect highlight = rect_endpoints(p0, p1);
+ gl_geometry_rect_border(highlight, settings->border_thickness, colors[COLOR_BORDER]);
+ gl_geometry_rect(highlight, colors[COLOR_HOVER_HL]);
+
+ float height = line_buffer_height + 2 * padding;
+ bounds.size.y = height;
+ gl_geometry_rect(bounds, colors[COLOR_MENU_BG]);
+ gl_geometry_rect_border(bounds, settings->border_thickness, colors[COLOR_BORDER]);
+ gl_geometry_draw();
+ bounds = rect_shrink(bounds, padding);
+ rect_coords(bounds, &x1, &y1, &x2, &y2);
+ const char *text = "Rename symbol to...";
text_utf8(font_bold, text, x1, y1, colors[COLOR_TEXT]);
x1 += text_get_size_vec2(font_bold, text).x + padding;
text_render(font_bold);
diff --git a/ted.cfg b/ted.cfg
index 81cdd43..dd49f5d 100644
--- a/ted.cfg
+++ b/ted.cfg
@@ -318,6 +318,7 @@ Ctrl+Space = :autocomplete
# go to previous completion
Ctrl+Shift+Space = :autocomplete-back
Ctrl+u = :find-usages
+Ctrl+r = :rename-symbol
Ctrl+z = :undo
Ctrl+Shift+z = :redo
@@ -363,7 +364,7 @@ Ctrl+] = :build-next-error
Ctrl+Shift+1 = :shell
Ctrl+! = :shell
-Ctrl+t = :generate-tags
+# Ctrl+t = :generate-tags
Ctrl+d = :goto-definition
# alternative to ctrl+click
diff --git a/ted.h b/ted.h
index 4327173..6b9ee2d 100644
--- a/ted.h
+++ b/ted.h
@@ -464,6 +464,8 @@ typedef enum {
MENU_COMMAND_SELECTOR,
/// "Run a shell command"
MENU_SHELL,
+ /// "Rename symbol"
+ MENU_RENAME_SYMBOL,
} Menu;
/// an entry in a selector menu (e.g. the "open" menu)
@@ -681,6 +683,12 @@ typedef struct {
DocumentLink *links;
} DocumentLinks;
+/// information for symbol rename (LSP)
+typedef struct {
+ char *new_name;
+ LSPServerRequestID request_id;
+} RenameSymbol;
+
/// "hover" information from LSP server
typedef struct {
LSPServerRequestID last_request;
@@ -850,6 +858,7 @@ typedef struct Ted {
Definitions definitions;
Highlights highlights;
Usages usages;
+ RenameSymbol rename_symbol;
FILE *log;
@@ -1078,6 +1087,8 @@ i64 buffer_cursor_move_left_words(TextBuffer *buffer, i64 nwords);
i64 buffer_cursor_move_right_words(TextBuffer *buffer, i64 nwords);
/// move cursor to "previous" position (i.e. \ref CMD_PREVIOUS_POSITION)
void buffer_cursor_move_to_prev_pos(TextBuffer *buffer);
+/// Get the start and end index of the word at the given position.
+void buffer_word_span_at_pos(TextBuffer *buffer, BufferPos pos, u32 *word_start, u32 *word_end);
/// Returns a string of word characters (see is32_word) around the position,
/// or an empty string if neither of the characters to the left and right of the cursor are word characters.
/// NOTE: The string is invalidated when the buffer is changed!!!
@@ -1541,6 +1552,10 @@ void hover_close(Ted *ted);
void hover_process_lsp_response(Ted *ted, LSPResponse *response);
void hover_frame(Ted *ted, double dt);
+// === ide-rename-symbol.c ===
+void rename_symbol_clear(Ted *ted);
+void rename_symbol_frame(Ted *ted);
+
// === ide-signature-help.c ===
/// figure out new signature help
void signature_help_retrigger(Ted *ted);