diff options
-rw-r--r-- | CMakeLists.txt | 5 | ||||
-rw-r--r-- | README.md | 2 | ||||
-rw-r--r-- | buffer.c | 23 | ||||
-rw-r--r-- | command.c | 5 | ||||
-rw-r--r-- | command.h | 1 | ||||
-rw-r--r-- | ide-document-link.c | 3 | ||||
-rw-r--r-- | ide-rename-symbol.c | 91 | ||||
-rw-r--r-- | lsp-write.c | 3 | ||||
-rw-r--r-- | main.c | 4 | ||||
-rw-r--r-- | menu.c | 68 | ||||
-rw-r--r-- | ted.cfg | 3 | ||||
-rw-r--r-- | ted.h | 15 |
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() @@ -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 @@ -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) { @@ -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; } } @@ -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"); @@ -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) { @@ -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); @@ -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 @@ -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); |