diff options
-rw-r--r-- | buffer.c | 114 | ||||
-rw-r--r-- | build.c | 15 | ||||
-rw-r--r-- | command.c | 20 | ||||
-rw-r--r-- | find.c | 120 | ||||
-rw-r--r-- | ide-autocomplete.c | 28 | ||||
-rw-r--r-- | ide-rename-symbol.c | 12 | ||||
-rw-r--r-- | ide-signature-help.c | 11 | ||||
-rw-r--r-- | ide-usages.c | 4 | ||||
-rw-r--r-- | main.c | 3 | ||||
-rw-r--r-- | menu.c | 46 | ||||
-rw-r--r-- | node.c | 2 | ||||
-rw-r--r-- | session.c | 42 | ||||
-rw-r--r-- | tags.c | 6 | ||||
-rw-r--r-- | ted-internal.h | 99 | ||||
-rw-r--r-- | ted.c | 19 | ||||
-rw-r--r-- | ted.h | 20 | ||||
-rw-r--r-- | ui.c | 4 |
17 files changed, 301 insertions, 264 deletions
@@ -8,6 +8,12 @@ #define BUFFER_UNTITLED "Untitled" // what to call untitled buffers +/// A single line in a buffer +typedef struct Line Line; + +/// A single undoable edit to a buffer +typedef struct BufferEdit BufferEdit; + struct Line { SyntaxState syntax; u32 len; @@ -24,6 +30,87 @@ struct BufferEdit { double time; // time at start of edit (i.e. the time just before the edit), in seconds since epoch }; +struct TextBuffer { + /// NULL if this buffer is untitled or doesn't correspond to a file (e.g. line buffers) + char *path; + /// we keep a back-pointer to the ted instance so we don't have to pass it in to every buffer function + Ted *ted; + /// number of characters scrolled in the x direction (multiply by space width to get pixels) + double scroll_x; + /// number of characters scrolled in the y direction + double scroll_y; + /// last write time to \ref path + double last_write_time; + /// the language the buffer has been manually set to, or \ref LANG_NONE if it hasn't been set to anything + i64 manual_language; + /// position of cursor + BufferPos cursor_pos; + /// if \ref selection is true, the text between \ref selection_pos and \ref cursor_pos is selected. + BufferPos selection_pos; + /// "previous" position of cursor, for \ref CMD_PREVIOUS_POSITION + BufferPos prev_cursor_pos; + /// "line buffers" are buffers which can only have one line of text (used for inputs) + bool is_line_buffer; + /// is anything selected? + bool selection; + /// set to false to disable undo events + bool store_undo_events; + /// will the next undo event be chained with the ones after? + bool will_chain_edits; + /// will the next undo event be chained with the previous one? + bool chaining_edits; + /// view-only mode + bool view_only; + /// (line buffers only) set to true when submitted. you have to reset it to false. + bool line_buffer_submitted; + /// If set to true, buffer will be scrolled to the cursor position next frame. + /// This is to fix the problem that \ref x1, \ref y1, \ref x2, \ref y2 are not updated until the buffer is rendered. + bool center_cursor_next_frame; + /// x coordinate of left side of buffer + float x1; + /// y coordinate of top side of buffer + float y1; + /// x coordinate of right side of buffer + float x2; + /// y coordinate of bottom side of buffer + float y2; + /// number of lines in buffer + u32 nlines; + /// capacity of \ref lines + u32 lines_capacity; + + /// cached settings index (into ted->all_settings), or -1 if has not been computed yet + i32 settings_idx; + + /// which LSP this document is open in + LSPID lsp_opened_in; + /// determining which LSP to use for a buffer takes some work, + /// so we don't want to do it every single frame. + /// this keeps track of the last time we actually checked what the correct LSP is. + double last_lsp_check; + + /// where in the undo history was the last write? used by \ref buffer_unsaved_changes + u32 undo_history_write_pos; + /// which lines are on screen? updated when \ref buffer_render is called. + u32 first_line_on_screen, last_line_on_screen; + + /// to cache syntax highlighting properly, it is important to keep track of the + /// first and last line modified since last frame. + u32 frame_earliest_line_modified; + /// see \ref frame_earliest_line_modified. + u32 frame_latest_line_modified; + + /// lines + Line *lines; + /// last error + char error[256]; + /// dynamic array of undo history + BufferEdit *undo_history; + /// dynamic array of redo history + BufferEdit *redo_history; +}; + + // this is a macro so we get -Wformat warnings #define buffer_error(buffer, ...) \ snprintf(buffer->error, sizeof buffer->error - 1, __VA_ARGS__) @@ -82,6 +169,17 @@ bool buffer_is_named_file(TextBuffer *buffer) { return buffer->path != NULL; } +bool buffer_is_line_buffer(TextBuffer *buffer) { + return buffer->is_line_buffer; +} + +bool line_buffer_is_submitted(TextBuffer *buffer) { + return buffer->is_line_buffer && buffer->line_buffer_submitted; +} + +void line_buffer_clear_submitted(TextBuffer *buffer) { + buffer->line_buffer_submitted = false; +} bool buffer_is_view_only(TextBuffer *buffer) { return buffer->view_only; @@ -107,6 +205,10 @@ BufferPos buffer_cursor_pos(TextBuffer *buffer) { return buffer->cursor_pos; } +bool buffer_has_selection(TextBuffer *buffer) { + return buffer->selection; +} + bool buffer_selection_pos(TextBuffer *buffer, BufferPos *pos) { if (buffer->selection) { if (pos) *pos = buffer->selection_pos; @@ -1107,7 +1209,10 @@ void buffer_scroll_to_cursor(TextBuffer *buffer) { buffer_scroll_to_pos(buffer, buffer->cursor_pos); } -// scroll so that the cursor is in the center of the screen +void buffer_set_manual_language(TextBuffer *buffer, u32 language) { + buffer->manual_language = language; +} + void buffer_center_cursor(TextBuffer *buffer) { double cursor_line = buffer->cursor_pos.line; double cursor_col = buffer_index_to_xoff(buffer, (u32)cursor_line, buffer->cursor_pos.index) @@ -1121,6 +1226,10 @@ void buffer_center_cursor(TextBuffer *buffer) { buffer_correct_scroll(buffer); } +void buffer_center_cursor_next_frame(TextBuffer *buffer) { + buffer->center_cursor_next_frame = true; +} + // move left (if `by` is negative) or right (if `by` is positive) by the specified amount. // returns the signed number of characters successfully moved (it could be less in magnitude than `by` if the beginning of the file is reached) static i64 buffer_pos_move_horizontally(TextBuffer *buffer, BufferPos *p, i64 by) { @@ -1549,7 +1658,6 @@ void buffer_cursor_move_to_end_of_file(TextBuffer *buffer) { static void buffer_lines_modified(TextBuffer *buffer, u32 first_line, u32 last_line) { assert(last_line >= first_line); - buffer->modified = true; if (first_line < buffer->frame_earliest_line_modified) buffer->frame_earliest_line_modified = first_line; if (last_line > buffer->frame_latest_line_modified) @@ -2606,8 +2714,6 @@ void buffer_redo(TextBuffer *buffer, i64 ntimes) { // buffer_end_edit_chain(buffer) // pressing ctrl+z will undo both the insertion of text1 and text2. void buffer_start_edit_chain(TextBuffer *buffer) { - assert(!buffer->chaining_edits); - assert(!buffer->will_chain_edits); buffer->will_chain_edits = true; } @@ -52,7 +52,7 @@ static bool build_run_next_command_in_queue(Ted *ted) { buffer_insert_text_at_cursor(build_buffer, str32(text, 2)); buffer_insert_utf8_at_cursor(build_buffer, command); buffer_insert_char_at_cursor(build_buffer, '\n'); - build_buffer->view_only = true; + buffer_set_view_only(build_buffer, true); free(command); return true; } else { @@ -70,7 +70,7 @@ 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 + buffer_set_undo_enabled(build_buffer, false); // don't need undo events for build output buffer } void build_queue_finish(Ted *ted) { @@ -147,7 +147,7 @@ static void build_go_to_error(Ted *ted) { // move cursor to error buffer_cursor_move_to_pos(buffer, pos); - buffer->center_cursor_next_frame = true; + buffer_center_cursor(buffer); // move cursor to error in build output TextBuffer *build_buffer = ted->build_buffer; @@ -203,7 +203,7 @@ 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) { + for (u32 line_idx = 0; line_idx < buffer_line_count(buffer); ++line_idx) { String32 line = buffer_get_line(buffer, line_idx); if (line.len < 3) { continue; @@ -313,7 +313,7 @@ void build_frame(Ted *ted, float x1, float y1, float x2, float y2) { assert(ted->build_shown); char buf[256]; if (ted->building) { - buffer->view_only = false; // disable view only temporarily so we can edit it + buffer_set_view_only(buffer, false); // disable view only temporarily so we can edit it bool any_text_inserted = false; while (1) { char incomplete[4]; @@ -363,8 +363,7 @@ void build_frame(Ted *ted, float x1, float y1, float x2, float y2) { if (any_text_inserted) { // show bottom of output (only relevant if there are no build errors) - buffer->cursor_pos = buffer_pos_end_of_file(buffer); - buffer_scroll_to_cursor(buffer); + buffer_cursor_move_to_end_of_file(buffer); } ProcessExitInfo info = {0}; @@ -380,7 +379,7 @@ void build_frame(Ted *ted, float x1, float y1, float x2, float y2) { build_check_for_errors(ted); } } - buffer->view_only = true; + buffer_set_view_only(buffer, true); } buffer_render(buffer, rect4(x1, y1, x2, y2)); } @@ -318,8 +318,8 @@ void command_execute_ex(Ted *ted, Command c, const CommandArgument *full_argumen } break; case CMD_COPY_PATH: - if (buffer) { - SDL_SetClipboardText(buffer->path); + if (buffer && buffer_is_named_file(buffer)) { + SDL_SetClipboardText(buffer_get_path(buffer)); } else { SDL_SetClipboardText(ted->cwd); } @@ -335,7 +335,7 @@ void command_execute_ex(Ted *ted, Command c, const CommandArgument *full_argumen } else if (autocomplete_is_open(ted) || autocomplete_has_phantom(ted)) { autocomplete_select_completion(ted); } else if (buffer) { - if (buffer->selection) + if (buffer_has_selection(buffer)) buffer_indent_selection(buffer); else buffer_insert_tab_at_cursor(buffer); @@ -350,7 +350,7 @@ void command_execute_ex(Ted *ted, Command c, const CommandArgument *full_argumen ted_switch_to_buffer(ted, buffer); buffer_select_all(buffer); } else if (buffer) { - if (buffer->selection) + if (buffer_has_selection(buffer)) buffer_dedent_selection(buffer); else buffer_dedent_cursor_line(buffer); @@ -417,7 +417,7 @@ void command_execute_ex(Ted *ted, Command c, const CommandArgument *full_argumen case CMD_SAVE: ted->last_save_time = ted->frame_time; if (buffer) { - if (!buffer->path) { + if (!buffer_is_named_file(buffer)) { command_execute(ted, CMD_SAVE_AS, 1); return; } @@ -426,7 +426,7 @@ void command_execute_ex(Ted *ted, Command c, const CommandArgument *full_argumen break; case CMD_SAVE_AS: ted->last_save_time = ted->frame_time; - if (buffer && !buffer->is_line_buffer) { + if (buffer && !buffer_is_line_buffer(buffer)) { menu_open(ted, MENU_SAVE_AS); } break; @@ -465,11 +465,11 @@ void command_execute_ex(Ted *ted, Command c, const CommandArgument *full_argumen break; case CMD_SET_LANGUAGE: - if (buffer && !buffer->is_line_buffer) { + if (buffer && !buffer_is_line_buffer(buffer)) { if (argument <= 0 || argument > LANG_USER_MAX || !language_is_valid((Language)argument)) - buffer->manual_language = 0; + buffer_set_manual_language(buffer, 0); else - buffer->manual_language = argument; + buffer_set_manual_language(buffer, (u32)argument); } break; case CMD_AUTOCOMPLETE: @@ -556,7 +556,7 @@ void command_execute_ex(Ted *ted, Command c, const CommandArgument *full_argumen break; case CMD_VIEW_ONLY: - if (buffer) buffer->view_only = !buffer->view_only; + if (buffer) buffer_set_view_only(buffer, !buffer_is_view_only(buffer)); break; case CMD_TAB_CLOSE: { @@ -122,8 +122,8 @@ static WarnUnusedResult bool find_match(Ted *ted, BufferPos *pos, u32 *match_sta pos->index = (u32)groups[1]; return true; } else { - pos->line += (u32)((i64)buffer->nlines + direction); - pos->line %= buffer->nlines; + pos->line += (u32)((i64)buffer_line_count(buffer) + direction); + pos->line %= buffer_line_count(buffer); if (direction == +1) pos->index = 0; else @@ -143,14 +143,8 @@ static void find_search_line(Ted *ted, u32 line, FindResult **results) { } } -// check if the search term needs to be recompiled -void find_update(Ted *ted, bool force) { - TextBuffer *find_buffer = ted->find_buffer; +void find_redo_search(Ted *ted) { u32 flags = find_compilation_flags(ted); - if (!force - && !find_buffer->modified // check if pattern has been modified, - && ted->find_flags == flags) // or checkboxes have been (un)checked - return; ted->find_flags = flags; TextBuffer *buffer = find_search_buffer(ted); if (!buffer) return; @@ -159,10 +153,10 @@ void find_update(Ted *ted, bool force) { if (find_compile_pattern(ted)) { BufferPos best_scroll_candidate = {U32_MAX, U32_MAX}; - BufferPos cursor_pos = buffer->cursor_pos; + BufferPos cursor_pos = buffer_cursor_pos(buffer); // find all matches arr_clear(ted->find_results); - for (u32 line = 0; line < buffer->nlines; ++line) { + for (u32 line = 0, count = buffer_line_count(buffer); line < count; ++line) { find_search_line(ted, line, &ted->find_results); } @@ -172,24 +166,24 @@ void find_update(Ted *ted, bool force) { best_scroll_candidate = res->start; } - find_buffer->modified = false; if (best_scroll_candidate.line != U32_MAX) // scroll to first match (if there is one) buffer_scroll_to_pos(buffer, best_scroll_candidate); - } else if (find_buffer->modified) { - buffer_scroll_to_cursor(buffer); } } // returns the index of the match we are "on", or U32_MAX for none. static u32 find_match_idx(Ted *ted) { - find_update(ted, false); TextBuffer *buffer = find_search_buffer(ted); if (!buffer) return U32_MAX; arr_foreach_ptr(ted->find_results, FindResult, result) { - if ((buffer_pos_eq(result->start, buffer->selection_pos) - && buffer_pos_eq(result->end, buffer->cursor_pos)) - || buffer_pos_eq(result->start, buffer->cursor_pos)) - return (u32)(result - ted->find_results); + u32 index = (u32)(result - ted->find_results); + BufferPos cur_pos = buffer_cursor_pos(buffer), sel_pos = {0}; + if (buffer_pos_eq(result->start, cur_pos)) + return index; + if (buffer_selection_pos(buffer, &sel_pos) + && buffer_pos_eq(result->start, sel_pos) + && buffer_pos_eq(result->end, cur_pos)) + return index; } return U32_MAX; } @@ -199,15 +193,20 @@ static void find_next_in_direction(Ted *ted, int direction) { TextBuffer *buffer = find_search_buffer(ted); if (!buffer) return; - BufferPos pos = direction == +1 || !buffer->selection ? buffer->cursor_pos : buffer->selection_pos; - u32 nlines = buffer->nlines; + BufferPos cursor_pos = buffer_cursor_pos(buffer); + BufferPos pos = cursor_pos; + if (direction == -1) { + // start from selection pos if there is one + buffer_selection_pos(buffer, &pos); + } + u32 nlines = buffer_line_count(buffer); // we need to search the starting line twice, because we might start at a non-zero index for (size_t nsearches = 0; nsearches < nlines + 1; ++nsearches) { u32 match_start, match_end; if (find_match(ted, &pos, &match_start, &match_end, direction)) { - if (nsearches == 0 && match_start == buffer->cursor_pos.index) { + if (nsearches == 0 && match_start == cursor_pos.index) { // if you click "next" and your cursor is on a match, it should go to the next // one, not the one you're on } else { @@ -222,10 +221,9 @@ static void find_next_in_direction(Ted *ted, int direction) { } // returns true if successful. -// this function zeroes but keeps around the old find result! make sure you call find_update(ted, true) after calling this function +// this function zeroes but keeps around the old find result! make sure you call find_redo_search after calling this function // one or more times! static bool find_replace_match(Ted *ted, u32 match_idx) { - find_update(ted, false); if (!ted->find_code) return false; bool success = false; @@ -256,7 +254,7 @@ static bool find_replace_match(Ted *ted, u32 match_idx) { flags, ted->find_match_data, NULL, replacement.str, replacement.len, output_buffer, &output_size); if (ret > 0) { - buffer->selection = false; // stop selecting match + buffer_deselect(buffer); // stop selecting match buffer_delete_chars_at_pos(buffer, match.start, len); if (output_buffer) buffer_insert_text_at_pos(buffer, match.start, str32(output_buffer, output_size)); @@ -288,7 +286,7 @@ void find_replace(Ted *ted) { if (match_idx != U32_MAX) { buffer_cursor_move_to_pos(buffer, ted->find_results[match_idx].start); // move to start of match find_replace_match(ted, match_idx); - find_update(ted, true); + find_redo_search(ted); } } @@ -324,7 +322,7 @@ void find_replace_all(Ted *ted) { break; } buffer_end_edit_chain(buffer); - find_update(ted, true); + find_redo_search(ted); } } } @@ -395,7 +393,8 @@ void find_menu_frame(Ted *ted, Rect menu_bounds) { find_replace_all(ted); } - find_update(ted, false); + if (ted->find_flags != find_compilation_flags(ted)) + find_redo_search(ted); arr_foreach_ptr(ted->find_results, FindResult, result) { // highlight matches BufferPos p1 = result->start, p2 = result->end; @@ -479,7 +478,7 @@ void find_open(Ted *ted, bool replace) { buffer_select_all(ted->active_buffer); ted->find = true; ted->replace = replace; - find_update(ted, true); + find_redo_search(ted); } void find_close(Ted *ted) { @@ -531,49 +530,50 @@ static void find_research_lines(Ted *ted, u32 line0, u32 line1) { } static void find_edit_notify(void *context, TextBuffer *buffer, const EditInfo *info) { - (void)context; - Ted *ted = buffer->ted; + Ted *ted = context; if (!ted->find) { return; } - if (buffer != find_search_buffer(ted)) - return; - - const u32 line = info->pos.line; - - if (info->chars_inserted) { - const u32 newlines_inserted = info->newlines_inserted; + if (buffer == find_search_buffer(ted)) { + const u32 line = info->pos.line; - if (newlines_inserted) { - // update line numbers for find results after insertion. - arr_foreach_ptr(ted->find_results, FindResult, res) { - if (res->start.line > line) { - res->start.line += newlines_inserted; - res->end.line += newlines_inserted; + if (info->chars_inserted) { + const u32 newlines_inserted = info->newlines_inserted; + + if (newlines_inserted) { + // update line numbers for find results after insertion. + arr_foreach_ptr(ted->find_results, FindResult, res) { + if (res->start.line > line) { + res->start.line += newlines_inserted; + res->end.line += newlines_inserted; + } } } - } - - find_research_lines(ted, line, line + newlines_inserted); - - } else if (info->chars_deleted) { - const u32 newlines_deleted = info->newlines_deleted; - - if (newlines_deleted) { - // update line numbers for find results after deletion. - arr_foreach_ptr(ted->find_results, FindResult, res) { - if (res->start.line >= line + newlines_deleted) { - res->start.line -= newlines_deleted; - res->end.line -= newlines_deleted; + + find_research_lines(ted, line, line + newlines_inserted); + + } else if (info->chars_deleted) { + const u32 newlines_deleted = info->newlines_deleted; + + if (newlines_deleted) { + // update line numbers for find results after deletion. + arr_foreach_ptr(ted->find_results, FindResult, res) { + if (res->start.line >= line + newlines_deleted) { + res->start.line -= newlines_deleted; + res->end.line -= newlines_deleted; + } } + } + find_research_lines(ted, line, line); } - - find_research_lines(ted, line, line); + } else if (buffer == ted->line_buffer) { + find_redo_search(ted); + buffer_scroll_to_cursor(buffer); } } void find_init(Ted *ted) { - ted_add_edit_notify(ted, find_edit_notify, NULL); + ted_add_edit_notify(ted, find_edit_notify, ted); } diff --git a/ide-autocomplete.c b/ide-autocomplete.c index 42183ff..6f67506 100644 --- a/ide-autocomplete.c +++ b/ide-autocomplete.c @@ -94,8 +94,8 @@ static bool autocomplete_should_display_phantom(Ted *ted) { TextBuffer *buffer = ted->active_buffer; bool show = !ac->open && buffer - && !buffer->view_only - && !buffer->is_line_buffer + && !buffer_is_view_only(buffer) + && !buffer_is_line_buffer(buffer) && buffer_settings(buffer)->phantom_completions && is32_word(buffer_char_before_cursor(buffer)) && !is32_word(buffer_char_at_cursor(buffer)); @@ -198,7 +198,7 @@ static void autocomplete_no_suggestions(Ted *ted) { } static void autocomplete_send_completion_request(Ted *ted, TextBuffer *buffer, BufferPos pos, uint32_t trigger, bool phantom) { - if (!buffer->path) + if (!buffer_is_named_file(buffer)) return; // no can do LSP *lsp = buffer_lsp(buffer); @@ -218,7 +218,7 @@ static void autocomplete_send_completion_request(Ted *ted, TextBuffer *buffer, B request.data.completion = (LSPRequestCompletion) { .position = { - .document = lsp_document_id(lsp, buffer->path), + .document = buffer_lsp_document_id(buffer), .pos = buffer_pos_to_lsp_position(buffer, pos) }, .context = { @@ -245,7 +245,7 @@ static void autocomplete_find_completions(Ted *ted, uint32_t trigger, bool phant TextBuffer *buffer = ted->active_buffer; if (!buffer) return; - BufferPos pos = buffer->cursor_pos; + BufferPos pos = buffer_cursor_pos(buffer); if (buffer_pos_eq(pos, ac->last_pos)) return; // no need to update completions. ac->trigger = trigger; @@ -451,11 +451,11 @@ void autocomplete_process_lsp_response(Ted *ted, const LSPResponse *response) { void autocomplete_open(Ted *ted, uint32_t trigger) { Autocomplete *ac = ted->autocomplete; - if (ac->open) return; TextBuffer *buffer = ted->active_buffer; + if (ac->open) return; if (!buffer) return; - if (!buffer->path) return; - if (buffer->view_only) return; + if (!buffer_is_named_file(buffer)) return; + if (buffer_is_view_only(buffer)) return; autocomplete_clear_phantom(ac); const Settings *settings = buffer_settings(buffer); bool regenerated = false; @@ -542,7 +542,7 @@ void autocomplete_frame(Ted *ted) { if (*word_at_cursor && str_has_prefix(ac->phantom, word_at_cursor)) { const char *completion = ac->phantom + strlen(word_at_cursor); if (*completion) { - vec2 pos = buffer_pos_to_pixels(buffer, buffer->cursor_pos); + vec2 pos = buffer_pos_to_pixels(buffer, buffer_cursor_pos(buffer)); #if 0 vec2 size = text_get_size_vec2(font, completion); // this makes the text below the phantom less visible. @@ -600,9 +600,9 @@ void autocomplete_frame(Ted *ted) { menu_height = 200.f; } - vec2 cursor_pos = buffer_pos_to_pixels(buffer, buffer->cursor_pos); - bool open_up = cursor_pos.y > 0.5f * (buffer->y1 + buffer->y2); // should the completion menu open upwards? - bool open_left = cursor_pos.x > 0.5f * (buffer->x1 + buffer->x2); + vec2 cursor_pos = buffer_pos_to_pixels(buffer, buffer_cursor_pos(buffer)); + bool open_up = cursor_pos.y > rect_ymid(buffer_rect(buffer)); // should the completion menu open upwards? + bool open_left = cursor_pos.x > rect_xmid(buffer_rect(buffer)); float x = cursor_pos.x, start_y = cursor_pos.y; if (open_left) x -= menu_width; if (open_up) @@ -646,9 +646,9 @@ void autocomplete_frame(Ted *ted) { // we've got some wacky calculations to figure out the // bounding rect for the documentation float doc_width = open_left ? ac->rect.pos.x - 2*padding - : buffer->x2 - (ac->rect.pos.x + ac->rect.size.x + 2*padding); + : rect_x2(buffer_rect(buffer)) - (ac->rect.pos.x + ac->rect.size.x + 2*padding); if (doc_width > 800) doc_width = 800; - float doc_height = buffer->y2 - (ac->rect.pos.y + 2*padding); + float doc_height = rect_y2(buffer_rect(buffer)) - (ac->rect.pos.y + 2*padding); if (doc_height > char_height * 20) doc_height = char_height * 20; // if the rect is too small, there's no point in showing it diff --git a/ide-rename-symbol.c b/ide-rename-symbol.c index 344f401..cc8f176 100644 --- a/ide-rename-symbol.c +++ b/ide-rename-symbol.c @@ -25,7 +25,7 @@ void rename_symbol_at_cursor(Ted *ted, TextBuffer *buffer, const char *new_name) // 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->position = buffer_cursor_pos_as_lsp_document_position(buffer); data->new_name = str_dup(new_name); rs->request_id = lsp_send_request(lsp, &request); } @@ -46,7 +46,7 @@ static void rename_symbol_menu_open(Ted *ted) { static void rename_symbol_menu_update(Ted *ted) { TextBuffer *line_buffer = ted->line_buffer; - if (line_buffer->line_buffer_submitted) { + if (line_buffer_is_submitted(line_buffer)) { char *new_name = str32_to_utf8_cstr(buffer_get_line(line_buffer, 0)); rename_symbol_at_cursor(ted, ted->prev_active_buffer, new_name); free(new_name); @@ -71,7 +71,7 @@ static void rename_symbol_menu_render(Ted *ted) { const float line_buffer_height = ted_line_buffer_height(ted); u32 sym_start=0, sym_end=0; - BufferPos cursor_pos = buffer->cursor_pos; + BufferPos cursor_pos = buffer_cursor_pos(buffer); buffer_word_span_at_pos(buffer, cursor_pos, &sym_start, &sym_end); BufferPos bpos0 = { .line = cursor_pos.line, @@ -149,10 +149,8 @@ void rename_symbol_process_lsp_response(Ted *ted, const LSPResponse *response) { if (!ted_open_file(ted, path)) goto done; TextBuffer *buffer = ted_get_buffer_with_file(ted, path); - if (!buffer->will_chain_edits) { - // chain all edits together so they can be undone with one ctrl+z - buffer_start_edit_chain(buffer); - } + // chain all edits together so they can be undone with one ctrl+z + buffer_start_edit_chain(buffer); if (!buffer) { // this should never happen since we just diff --git a/ide-signature-help.c b/ide-signature-help.c index ba6f216..5611c19 100644 --- a/ide-signature-help.c +++ b/ide-signature-help.c @@ -148,18 +148,19 @@ void signature_help_frame(Ted *ted) { u32 *colors = settings->colors; float border = settings->border_thickness; - float width = buffer->x2 - buffer->x1; + Rect buf_rect = buffer_rect(buffer); + float width = buf_rect.size.x; float height = FLT_MAX; const float char_height = text_font_char_height(font); // make sure signature help doesn't take up too much space while (1) { height = char_height * signature_count; - if (height < (buffer->y2 - buffer->y1) * 0.25f) + if (height < buffer_rect(buffer).size.y * 0.25f) break; --signature_count; if (signature_count == 0) return; } - float x = buffer->x1, y = buffer->y2 - height; + float x = buf_rect.pos.x, y = rect_y2(buf_rect) - height; gl_geometry_rect(rect_xywh(x, y - border, width, border), colors[COLOR_AUTOCOMPLETE_BORDER]); gl_geometry_rect(rect_xywh(x, y, width, height), @@ -173,8 +174,8 @@ void signature_help_frame(Ted *ted) { state.y = y; state.min_x = x; state.min_y = y; - state.max_x = buffer->x2; - state.max_y = buffer->y2; + state.max_x = rect_x2(buf_rect); + state.max_y = rect_y2(buf_rect); rgba_u32_to_floats(colors[COLOR_TEXT], state.color); text_utf8_with_state(font, &state, signature->label_pre); diff --git a/ide-usages.c b/ide-usages.c index 435b7a2..4caa644 100644 --- a/ide-usages.c +++ b/ide-usages.c @@ -77,7 +77,7 @@ void usages_process_lsp_response(Ted *ted, const LSPResponse *response) { char *line_text = NULL; if (last_buffer) { // read the line from the buffer - if (line < last_buffer->nlines) { + if (line < buffer_line_count(last_buffer)) { line_text = buffer_get_line_utf8(last_buffer, line); } } else if (last_file) { @@ -122,7 +122,7 @@ void usages_process_lsp_response(Ted *ted, const LSPResponse *response) { } if (last_file) fclose(last_file); - buffer->view_only = true; + buffer_set_view_only(buffer, true); // the build_dir doesn't really matter since we're using absolute paths // but might as well set it to something reasonable. @@ -1,5 +1,6 @@ /* TODO: +- fix find - public Node API - public Selector/FileSelector API @@ -798,7 +799,7 @@ int main(int argc, char **argv) { TextBuffer *buffer = ted->active_buffer; if (buffer) { if (buffer_is_named_file(buffer)) { - const char *buffer_path = buffer->path; + const char *buffer_path = buffer_get_path(buffer); assert(*buffer_path); char *last_sep = strrchr(buffer_path, PATH_SEPARATOR); if (last_sep) { @@ -26,12 +26,7 @@ void menu_close(Ted *ted) { } ted_switch_to_buffer(ted, ted->prev_active_buffer); - TextBuffer *buffer = ted->active_buffer; ted->prev_active_buffer = NULL; - if (buffer) { - buffer->scroll_x = ted->prev_active_buffer_scroll.x; - buffer->scroll_y = ted->prev_active_buffer_scroll.y; - } ted->menu_open_idx = 0; ted->menu_context = NULL; ted->selector_open = NULL; @@ -62,9 +57,7 @@ void menu_open_with_context(Ted *ted, const char *menu_name, void *context) { const MenuInfo *info = &ted->all_menus[menu_idx]; ted->menu_open_idx = menu_idx; ted->menu_context = context; - TextBuffer *prev_buf = ted->prev_active_buffer = ted->active_buffer; - if (prev_buf) - ted->prev_active_buffer_scroll = (dvec2) {prev_buf->scroll_x, prev_buf->scroll_y}; + ted->prev_active_buffer = ted->active_buffer; ted_switch_to_buffer(ted, NULL); *ted->warn_overwrite = 0; // clear warn_overwrite @@ -135,12 +128,18 @@ void menu_render(Ted *ted) { info->render(ted); } +static void menu_edit_notify(void *context, TextBuffer *buffer, const EditInfo *info) { + (void)info; + + Ted *ted = context; + if (buffer == ted->line_buffer && menu_is_open(ted, MENU_SHELL)) { + ted->shell_command_modified = true; + } + +} + void menu_shell_move(Ted *ted, int direction) { TextBuffer *line_buffer = ted->line_buffer; - if (line_buffer->modified) { - // don't do it if the command has been edited - return; - } i64 pos = ted->shell_history_pos; pos += direction; if (pos >= 0 && pos <= arr_len(ted->shell_history)) { @@ -149,13 +148,13 @@ void menu_shell_move(Ted *ted, int direction) { if (pos == arr_len(ted->shell_history)) { // bottom of history; just clear line buffer } else { - line_buffer->store_undo_events = false; + buffer_set_undo_enabled(line_buffer, false); buffer_insert_utf8_at_cursor(line_buffer, ted->shell_history[pos]); - line_buffer->store_undo_events = true; - line_buffer->modified = false; + buffer_set_undo_enabled(line_buffer, true); + ted->shell_command_modified = true; } // line_buffer->x/y1/2 are wrong (all 0), because of buffer_clear - line_buffer->center_cursor_next_frame = true; + buffer_center_cursor_next_frame(line_buffer); } } @@ -330,8 +329,6 @@ static void ask_reload_menu_update(Ted *ted) { break; case POPUP_NO: menu_close(ted); - if (buffer) - buffer->last_write_time = timespec_to_seconds(time_last_modified(buffer->path)); break; case POPUP_CANCEL: assert(0); break; } @@ -452,10 +449,10 @@ static void goto_line_menu_update(Ted *ted) { TextBuffer *buffer = ted->prev_active_buffer; if (*contents != '\0' && *end == '\0') { if (line_number < 1) line_number = 1; - if (line_number > (long)buffer->nlines) line_number = (long)buffer->nlines; + if (line_number > (long)buffer_line_count(buffer)) line_number = (long)buffer_line_count(buffer); BufferPos pos = {(u32)line_number - 1, 0}; - if (line_buffer->line_buffer_submitted) { + if (line_buffer_is_submitted(line_buffer)) { // let's go there! menu_close(ted); buffer_cursor_move_to_pos(buffer, pos); @@ -465,7 +462,7 @@ static void goto_line_menu_update(Ted *ted) { buffer_scroll_center_pos(buffer, pos); } } - line_buffer->line_buffer_submitted = false; + line_buffer_clear_submitted(line_buffer); free(contents); } @@ -506,13 +503,14 @@ static bool goto_line_menu_close(Ted *ted) { static void shell_menu_open(Ted *ted) { ted_switch_to_buffer(ted, ted->line_buffer); ted->shell_history_pos = arr_len(ted->shell_history); + ted->shell_command_modified = false; } static void shell_menu_update(Ted *ted) { TextBuffer *line_buffer = ted->line_buffer; - if (line_buffer->line_buffer_submitted) { + if (line_buffer_is_submitted(line_buffer)) { char *command = str32_to_utf8_cstr(buffer_get_line(line_buffer, 0)); - if (ted->shell_history_pos == arr_len(ted->shell_history) || line_buffer->modified) { + if (ted->shell_history_pos == arr_len(ted->shell_history) || ted->shell_command_modified) { arr_add(ted->shell_history, command); } menu_close(ted); @@ -562,6 +560,8 @@ void menu_init(Ted *ted) { // dummy 0 entry so that nothing has index 0. arr_add(ted->all_menus, (MenuInfo){0}); + ted_add_edit_notify(ted, menu_edit_notify, ted); + MenuInfo save_as_menu = { .open = save_as_menu_open, .update = save_as_menu_update, @@ -310,7 +310,7 @@ void node_frame(Ted *ted, Node *node, Rect r) { { if (buffer_unsaved_changes(buffer)) strbuf_printf(tab_title, "*%s*", filename); - else if (buffer->view_only) + else if (buffer_is_view_only(buffer)) strbuf_printf(tab_title, "VIEW %s", filename); else strbuf_printf(tab_title, "%s", filename); @@ -204,16 +204,18 @@ static Status session_read_node(Ted *ted, FILE *fp, Node *node) { static void session_write_buffer(FILE *fp, TextBuffer *buffer) { // some info about the buffer that should be restored if (buffer_is_named_file(buffer)) - write_cstr(fp, buffer->path); + write_cstr(fp, buffer_get_path(buffer)); else write_char(fp, 0); - write_double(fp, buffer->scroll_x); - write_double(fp, buffer->scroll_y); - write_bool(fp, buffer->view_only); - buffer_pos_write(buffer->cursor_pos, fp); - write_bool(fp, buffer->selection); - if (buffer->selection) - buffer_pos_write(buffer->selection_pos, fp); + write_double(fp, buffer_get_scroll_columns(buffer)); + write_double(fp, buffer_get_scroll_lines(buffer)); + write_bool(fp, buffer_is_view_only(buffer)); + buffer_pos_write(buffer_cursor_pos(buffer), fp); + BufferPos sel_pos = {0}; + bool has_selection = buffer_selection_pos(buffer, &sel_pos); + write_bool(fp, has_selection); + if (has_selection) + buffer_pos_write(sel_pos, fp); } static bool session_read_buffer(Ted *ted, FILE *fp) { @@ -227,19 +229,19 @@ static bool session_read_buffer(Ted *ted, FILE *fp) { } else { buffer_new_file(buffer, NULL); } - buffer->scroll_x = read_double(fp); - buffer->scroll_y = read_double(fp); - buffer->view_only = read_bool(fp); - buffer->cursor_pos = buffer_pos_read(buffer, fp); - buffer->selection = read_bool(fp); - if (buffer->selection) - buffer->selection_pos = buffer_pos_read(buffer, fp); - buffer_pos_validate(buffer, &buffer->cursor_pos); - buffer_pos_validate(buffer, &buffer->selection_pos); - if (buffer->selection && buffer_pos_eq(buffer->cursor_pos, buffer->selection_pos)) { - // this could happen if the file was changed on disk - buffer->selection = false; + double scroll_x = read_double(fp); + double scroll_y = read_double(fp); + buffer_set_view_only(buffer, read_bool(fp)); + BufferPos cursor_pos = buffer_pos_read(buffer, fp); + bool has_selection = read_bool(fp); + if (has_selection) { + buffer_cursor_move_to_pos(buffer, buffer_pos_read(buffer, fp)); + buffer_select_to_pos(buffer, cursor_pos); + } else { + buffer_cursor_move_to_pos(buffer, cursor_pos); } + buffer_scroll_to_pos(buffer, buffer_pos_start_of_file(buffer)); + buffer_scroll(buffer, scroll_x, scroll_y); } return true; } @@ -281,7 +281,7 @@ top:; // the tags file gives us a (1-indexed) line number BufferPos pos = {.line = (u32)line_number - 1, .index = 0}; buffer_cursor_move_to_pos(buffer, pos); - buffer->center_cursor_next_frame = true; + buffer_center_cursor_next_frame(buffer); success = true; } else if (address[0] == '/') { // the tags file gives us a pattern to look for @@ -324,7 +324,7 @@ top:; if (code) { pcre2_match_data *match_data = pcre2_match_data_create(10, NULL); if (match_data) { - for (u32 line_idx = 0; line_idx < buffer->nlines; ++line_idx) { + for (u32 line_idx = 0, line_count = buffer_line_count(buffer); line_idx < line_count; ++line_idx) { String32 line = buffer_get_line(buffer, line_idx); int n = pcre2_match(code, line.str, line.len, 0, PCRE2_NOTEMPTY, match_data, NULL); @@ -334,7 +334,7 @@ top:; PCRE2_SIZE index = ovector[0]; BufferPos pos = {line_idx, (u32)index}; buffer_cursor_move_to_pos(buffer, pos); - buffer->center_cursor_next_frame = true; + buffer_center_cursor_next_frame(buffer); success = true; break; } diff --git a/ted-internal.h b/ted-internal.h index c90499d..c986759 100644 --- a/ted-internal.h +++ b/ted-internal.h @@ -161,105 +161,16 @@ struct Settings { KeyAction *key_actions; }; -/// A single line in a buffer -typedef struct Line Line; - /// This structure is used temporarily when loading settings /// It's needed because we want more specific contexts to be dealt with last. typedef struct ConfigPart ConfigPart; -/// A single undoable edit to a buffer -typedef struct BufferEdit BufferEdit; - typedef struct EditNotifyInfo { EditNotify fn; void *context; EditNotifyID id; } EditNotifyInfo; -struct TextBuffer { - /// NULL if this buffer is untitled or doesn't correspond to a file (e.g. line buffers) - char *path; - /// we keep a back-pointer to the ted instance so we don't have to pass it in to every buffer function - Ted *ted; - /// number of characters scrolled in the x direction (multiply by space width to get pixels) - double scroll_x; - /// number of characters scrolled in the y direction - double scroll_y; - /// last write time to \ref path - double last_write_time; - /// the language the buffer has been manually set to, or \ref LANG_NONE if it hasn't been set to anything - i64 manual_language; - /// position of cursor - BufferPos cursor_pos; - /// if \ref selection is true, the text between \ref selection_pos and \ref cursor_pos is selected. - BufferPos selection_pos; - /// "previous" position of cursor, for \ref CMD_PREVIOUS_POSITION - BufferPos prev_cursor_pos; - /// "line buffers" are buffers which can only have one line of text (used for inputs) - bool is_line_buffer; - /// is anything selected? - bool selection; - /// set to false to disable undo events - bool store_undo_events; - /// This is set to true whenever a change is made to the buffer, and never set to false by buffer_ functions. - /// (Distinct from \ref buffer_unsaved_changes) - bool modified; - /// will the next undo event be chained with the ones after? - bool will_chain_edits; - /// will the next undo event be chained with the previous one? - bool chaining_edits; - /// view-only mode - bool view_only; - /// (line buffers only) set to true when submitted. you have to reset it to false. - bool line_buffer_submitted; - /// If set to true, buffer will be scrolled to the cursor position next frame. - /// This is to fix the problem that \ref x1, \ref y1, \ref x2, \ref y2 are not updated until the buffer is rendered. - bool center_cursor_next_frame; - /// x coordinate of left side of buffer - float x1; - /// y coordinate of top side of buffer - float y1; - /// x coordinate of right side of buffer - float x2; - /// y coordinate of bottom side of buffer - float y2; - /// number of lines in buffer - u32 nlines; - /// capacity of \ref lines - u32 lines_capacity; - - /// cached settings index (into ted->all_settings), or -1 if has not been computed yet - i32 settings_idx; - - /// which LSP this document is open in - LSPID lsp_opened_in; - /// determining which LSP to use for a buffer takes some work, - /// so we don't want to do it every single frame. - /// this keeps track of the last time we actually checked what the correct LSP is. - double last_lsp_check; - - /// where in the undo history was the last write? used by \ref buffer_unsaved_changes - u32 undo_history_write_pos; - /// which lines are on screen? updated when \ref buffer_render is called. - u32 first_line_on_screen, last_line_on_screen; - - /// to cache syntax highlighting properly, it is important to keep track of the - /// first and last line modified since last frame. - u32 frame_earliest_line_modified; - /// see \ref frame_earliest_line_modified. - u32 frame_latest_line_modified; - - /// lines - Line *lines; - /// last error - char error[256]; - /// dynamic array of undo history - BufferEdit *undo_history; - /// dynamic array of redo history - BufferEdit *redo_history; -}; - /// an entry in a selector menu (e.g. the "open" menu) typedef struct { /// label @@ -516,9 +427,6 @@ struct Ted { /// build error we are currently "on" u32 build_error; - /// used by menus to keep track of the scroll position so we can return to it. - dvec2 prev_active_buffer_scroll; - SDL_Cursor *cursor_arrow, *cursor_ibeam, *cursor_wait, *cursor_resize_h, *cursor_resize_v, *cursor_hand, *cursor_move; /// which cursor to use this frame @@ -539,6 +447,8 @@ struct Ted { char **shell_history; /// for keeping track of where we are in the shell history. u32 shell_history_pos; + /// has the shell command been modified (if so, we block up/down) + bool shell_command_modified; // points to a selector if any is open, otherwise NULL. Selector *selector_open; @@ -628,6 +538,11 @@ void buffer_highlight_lsp_range(TextBuffer *buffer, LSPRange range, ColorSetting /// process a mouse click. /// returns true if the event was consumed. bool buffer_handle_click(Ted *ted, TextBuffer *buffer, vec2 click, u8 times); +/// next frame, scroll so that the cursor is in the center of the buffer's rectangle. +/// +/// currently needed to avoid bad positioning when a buffer is created +/// and buffer_center_cursor is called immediately after +void buffer_center_cursor_next_frame(TextBuffer *buffer); // === build.c === void build_frame(Ted *ted, float x1, float y1, float x2, float y2); @@ -147,8 +147,9 @@ void ted_log(Ted *ted, const char *fmt, ...) { void ted_error_from_buffer(Ted *ted, TextBuffer *buffer) { - if (*buffer->error) - ted_error(ted, "%s", buffer->error); + const char *err = buffer_get_error(buffer); + if (err) + ted_error(ted, "%s", err); } void ted_out_of_mem(Ted *ted) { @@ -183,7 +184,7 @@ char *ted_get_root_dir_of(Ted *ted, const char *path) { char *ted_get_root_dir(Ted *ted) { TextBuffer *buffer = ted->active_buffer; if (buffer && buffer_is_named_file(buffer)) { - return ted_get_root_dir_of(ted, buffer->path); + return ted_get_root_dir_of(ted, buffer_get_path(buffer)); } else { return ted_get_root_dir_of(ted, ted->cwd); } @@ -448,7 +449,7 @@ void ted_switch_to_buffer(Ted *ted, TextBuffer *buffer) { autocomplete_close(ted); if (buffer != search_buffer) { if (ted->find) - find_update(ted, true); // make sure find results are for this file + find_redo_search(ted); // make sure find results are for this file } if (ted_is_regular_buffer(ted, buffer)) { @@ -568,7 +569,8 @@ TextBuffer *ted_get_buffer_with_file(Ted *ted, const char *path) { arr_foreach_ptr(ted->buffers, TextBufferPtr, pbuffer) { TextBuffer *buffer = *pbuffer; - if (buffer->path && paths_eq(path, buffer->path)) { + const char *buf_path = buffer_get_path(buffer); + if (buf_path && paths_eq(path, buf_path)) { return buffer; } } @@ -594,7 +596,8 @@ bool ted_open_file(Ted *ted, const char *filename) { // not open; we need to load it u16 tab_idx; TextBuffer *buffer = NULL; - if (ted->active_buffer && !ted->active_buffer->path + if (ted->active_buffer + && !buffer_is_named_file(ted->active_buffer) && ted_is_regular_buffer(ted, ted->active_buffer) && buffer_empty(ted->active_buffer)) { // the active buffer is just an empty untitled buffer. open it here. @@ -643,7 +646,7 @@ bool ted_save_all(Ted *ted) { arr_foreach_ptr(ted->buffers, TextBufferPtr, pbuffer) { TextBuffer *buffer = *pbuffer; if (buffer_unsaved_changes(buffer)) { - if (!buffer->path) { + if (!buffer_is_named_file(buffer)) { ted_switch_to_buffer(ted, buffer); menu_open(ted, MENU_SAVE_AS); success = false; // we haven't saved this buffer yet; we've just opened the "save as" menu. @@ -764,7 +767,7 @@ void ted_go_to_lsp_document_position(Ted *ted, LSP *lsp, LSPDocumentPosition pos TextBuffer *buffer = ted->active_buffer; BufferPos pos = buffer_pos_from_lsp(buffer, position.pos); buffer_cursor_move_to_pos(buffer, pos); - buffer->center_cursor_next_frame = true; + buffer_center_cursor_next_frame(buffer); } else { ted_flash_error_cursor(ted); } @@ -334,6 +334,8 @@ double buffer_get_scroll_lines(TextBuffer *buffer); double buffer_last_write_time(TextBuffer *buffer); /// get position of the cursor BufferPos buffer_cursor_pos(TextBuffer *buffer); +/// returns true if anything is selected +bool buffer_has_selection(TextBuffer *buffer); /// get position of non-cursor end of selection. /// /// `pos` is allowed to be NULL. @@ -347,6 +349,10 @@ const char *buffer_get_path(TextBuffer *buffer); void buffer_clear_undo_redo(TextBuffer *buffer); /// set whether undo history should be kept void buffer_set_undo_enabled(TextBuffer *buffer, bool enabled); +/// set manual language override for buffer. +/// +/// passing `language = 0` goes back to automatic language detection. +void buffer_set_manual_language(TextBuffer *buffer, u32 language); /// first line which will appear on screen u32 buffer_first_line_on_screen(TextBuffer *buffer); /// last line which will appear on screen @@ -361,6 +367,14 @@ const char *buffer_display_filename(TextBuffer *buffer); bool buffer_is_named_file(TextBuffer *buffer); /// does this buffer have unsaved changes? bool buffer_unsaved_changes(TextBuffer *buffer); +/// is this a line buffer? +bool buffer_is_line_buffer(TextBuffer *buffer); +/// has this line buffer been submitted? +/// +/// returns false if `buffer` is not a line buffer. +bool line_buffer_is_submitted(TextBuffer *buffer); +/// clear submission status of line buffer. +void line_buffer_clear_submitted(TextBuffer *buffer); /// returns the character after position pos, or 0 if pos is invalid char32_t buffer_char_at_pos(TextBuffer *buffer, BufferPos pos); /// returns the character after the cursor @@ -744,10 +758,8 @@ char *settings_get_root_dir(Settings *settings, const char *path); // === find.c === /// which buffer will be searched? TextBuffer *find_search_buffer(Ted *ted); -/// update find results. -/// -/// if `force` is true, the results will be updated even if the pattern & flags have not been changed. -void find_update(Ted *ted, bool force); +/// discard find results and perform search again. +void find_redo_search(Ted *ted); /// replace the match we are currently highlighting, or do nothing if there is no highlighted match void find_replace(Ted *ted); /// go to next find result @@ -90,8 +90,8 @@ char *selector_update(Ted *ted, Selector *s) { } } - if (line_buffer->line_buffer_submitted) { - line_buffer->line_buffer_submitted = false; + if (line_buffer_is_submitted(line_buffer)) { + line_buffer_clear_submitted(line_buffer); if (!ret) { if (s->enable_cursor) { // select this option |