Created
January 30, 2021 05:10
-
-
Save adsr/51101740a8b974ffec1505851dbfdf8c to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| diff --git a/buffer.c b/buffer.c | |
| index 10f81ed..b6eaa87 100644 | |
| --- a/buffer.c | |
| +++ b/buffer.c | |
| @@ -697,6 +697,126 @@ int buffer_get_bline(buffer_t *self, bint_t line_index, bline_t **ret_bline) { | |
| return buffer_get_bline_w_hint(self, line_index, self->first_line, ret_bline); | |
| } | |
| +// Replace nchars in buffer starting at ma, optionally constrained by opt_mb. | |
| +// This is used for cursors with is_block==1. This operation can only add | |
| +// chars, never delete. (When replacing wide chars with ascii, ending up with | |
| +// less bytes is possible.) Instead of deleting, it adds whitespace. If data | |
| +// contains newlines, the edit operation moves to the next existing bline. It | |
| +// does not add a new bline unless the operation is at the end of the buffer. | |
| +// When opt_mb is specified, the operation is further constrained to the | |
| +// rectange between the two marks. The larger of nchars and data_len is used | |
| +// for determining the size of the replace operation. If nchars is greater | |
| +// than data_len, whitespace is used to make up the difference. data can be | |
| +// NULL in which case whitespace is used exclusively. | |
| +int buffer_block_replace(buffer_t *self, mark_t *a, mark_t *b, char *repl, bint_t repl_len) { | |
| + str_t replacement = {0}, ws = {0}, line = {0}; | |
| + bline_t *bline; | |
| + bint_t col, w, h, ndel, nlines, nchars; | |
| + char *newline; | |
| + int constrain_to_rect; | |
| + | |
| + if (repl) { | |
| + str_set_len(&replacement, repl, (size_t)repl_len); | |
| + } else { | |
| + str_append_nchars(&replacement, ' ', (size_t)repl_len); | |
| + } | |
| + | |
| + if (b) { | |
| + constrain_to_rect = 1; | |
| + mark_cmp(a, b, &a, &b); | |
| + bline = a->bline; | |
| + col = MLBUF_MIN(a->col, b->col); | |
| + w = MLBUF_MAX(a->col, b->col) - col + 1; | |
| + h = MLBUF_MAX(a->bline->line_index, b->bline->line_index) - MLBUF_MIN(a->bline->line_index, b->bline->line_index) + 1; | |
| + } else { | |
| + constrain_to_rect = 0; | |
| + bline = a->bline; | |
| + col = a->col; | |
| + w = 0; | |
| + h = 0; | |
| + } | |
| + | |
| + nlines = 0; | |
| + while (1) { | |
| + // Get next line in replacement | |
| + newline = memchr(replacement.data, '\n', replacement.len); | |
| + line.data = replacement.data; | |
| + line.len = newline ? (size_t)(newline - replacement.data) : replacement.len; | |
| + | |
| + // Overwrite bline:col with line | |
| + /* | |
| + line_len == 0 line_len > 0 | |
| + unconstrained constrained | |
| + short long | |
| + | |
| + 000 do nothing | |
| + 001 rightpad after col | |
| + 010 do nothing | |
| + 011 rightpad after col | |
| + | |
| + 100 leftpad then insert str | |
| + 101 replace at col with str | |
| + 110 leftpad then insert str constrained | |
| + 111 replace at col with str and then rightpad | |
| + */ | |
| + | |
| + // TODO wide chars | |
| + MLBUF_BLINE_ENSURE_CHARS(bline); | |
| + str_clear(&ws); | |
| + if (line.len < 1) { | |
| + if (col <= bline->char_count) { // long | |
| + if (constrain_to_rect) { | |
| + str_append_nchars(&ws, ' ', w); | |
| + buffer_replace_w_bline(self, bline, col, bline->char_count - col, ws.data, ws.len); | |
| + } else { | |
| + // buffer_delete_w_bline(self, bline, col, bline->char_count - col); | |
| + } | |
| + } | |
| + } else { | |
| + if (col > bline->char_count) { | |
| + str_append_nchars(&ws, ' ', (size_t)(col - bline->char_count)); | |
| + bline_insert(bline, bline->char_count, ws.data, ws.len, NULL); | |
| + MLBUF_BLINE_ENSURE_CHARS(bline); | |
| + nchars = constrain_to_rect ? MLBUF_MIN((size_t)w, line.len) : line.len; | |
| + bline_insert(bline, bline->char_count, line.data, nchars, NULL); | |
| + } else { | |
| + ndel = MLBUF_MIN((size_t)bline->char_count - col, line.len); | |
| + nchars = constrain_to_rect ? MLBUF_MIN((size_t)w, line.len) : line.len; | |
| + buffer_replace_w_bline(self, bline, col, ndel, line.data, nchars); | |
| + MLBUF_BLINE_ENSURE_CHARS(bline); | |
| + if (constrain_to_rect && nchars < w && ) { // RESUME | |
| + str_append_nchars(&ws, ' ', (size_t)(w - nchars)); | |
| + bline_insert(bline, bline->char_count, ws.data, ws.len, NULL); | |
| + } | |
| + } | |
| + } | |
| + | |
| + // Done with this line | |
| + nlines += 1; | |
| + | |
| + // Check stop conditions | |
| + if (constrain_to_rect && nlines >= h) break; // Apply height constraint | |
| + if (!newline) break; // No lines left | |
| + str_set_len( | |
| + &replacement, | |
| + newline + 1, | |
| + (size_t)(replacement.len - (line.len + 1)) | |
| + ); | |
| + if (replacement.len < 1) break; // Nothing left | |
| + | |
| + // Advance to next line | |
| + if (!bline->next) { | |
| + bline_insert(bline, bline->char_count, "\n", 1, NULL); | |
| + } | |
| + bline = bline->next; | |
| + } | |
| + | |
| + str_free(&ws); | |
| + str_free(&replacement); | |
| + | |
| + return MLBUF_OK; | |
| +} | |
| + | |
| // Return a line given a line_index, starting at hint and iterating outward | |
| int buffer_get_bline_w_hint(buffer_t *self, bint_t line_index, bline_t *opt_hint, bline_t **ret_bline) { | |
| bline_t *fwd, *rev, *found; | |
| diff --git a/bview.c b/bview.c | |
| index 9bb6061..bad3993 100644 | |
| --- a/bview.c | |
| +++ b/bview.c | |
| @@ -838,6 +838,19 @@ static void _bview_draw_status(bview_t *self) { | |
| anchor_nlines = 0; | |
| } | |
| + // Block indicator | |
| + int i_block_fg, i_block_bg; | |
| + char *i_block; | |
| + if (active_edit->active_cursor->is_block) { | |
| + i_block_fg = TB_WHITE | TB_BOLD; | |
| + i_block_bg = TB_BLACK; | |
| + i_block = "b"; | |
| + } else { | |
| + i_block_fg = 0; | |
| + i_block_bg = 0; | |
| + i_block = "."; | |
| + } | |
| + | |
| // Async indicator | |
| int i_async_fg, i_async_bg; | |
| char *i_async; | |
| @@ -879,16 +892,17 @@ static void _bview_draw_status(bview_t *self) { | |
| MLBUF_BLINE_ENSURE_CHARS(mark->bline); | |
| tb_printf(editor->rect_status, 0, 0, 0, 0, "%*.*s", editor->rect_status.w, editor->rect_status.w, " "); | |
| tb_printf_attr(editor->rect_status, 0, 0, | |
| - "@%d,%d;%s@%d,%d;" // mle_normal mode | |
| - "[@%d,%d;%s@%d,%d;%s@%d,%d;%s@%d,%d;%s@%d,%d;] " // [....] need_input,anchor,macro,async | |
| - "buf:@%d,%d;%d@%d,%d;/@%d,%d;%d@%d,%d; " // buf:1/2 bview num | |
| - "<@%d,%d;%s@%d,%d;> " // <php> syntax | |
| - "line:@%d,%d;%llu@%d,%d;/@%d,%d;%llu@%d,%d; " // line:1/100 line | |
| - "col:@%d,%d;%llu@%d,%d;/@%d,%d;%llu@%d,%d; " // col:0/80 col | |
| - "%s@%d,%d;%lld@%d,%d;,@%d,%d;%llu@%d,%d; ", // sel:10,1 sel len, nlines | |
| + "@%d,%d;%s@%d,%d;" // mle_normal mode | |
| + "[@%d,%d;%s@%d,%d;%s@%d,%d;%s@%d,%d;%s@%d,%d;%s@%d,%d;] " // [.....] need_input,anchor,block,macro,async | |
| + "buf:@%d,%d;%d@%d,%d;/@%d,%d;%d@%d,%d; " // buf:1/2 bview num | |
| + "<@%d,%d;%s@%d,%d;> " // <php> syntax | |
| + "line:@%d,%d;%llu@%d,%d;/@%d,%d;%llu@%d,%d; " // line:1/100 line | |
| + "col:@%d,%d;%llu@%d,%d;/@%d,%d;%llu@%d,%d; " // col:0/80 col | |
| + "%s@%d,%d;%lld@%d,%d;,@%d,%d;%llu@%d,%d; ", // sel:10,1 sel len, nlines | |
| TB_MAGENTA | TB_BOLD, 0, active->kmap_tail->kmap->name, 0, 0, | |
| i_needinput_fg, i_needinput_bg, i_needinput, | |
| i_anchor_fg, i_anchor_bg, i_anchor, | |
| + i_block_fg, i_block_bg, i_block, | |
| i_macro_fg, i_macro_bg, i_macro, | |
| i_async_fg, i_async_bg, i_async, 0, 0, | |
| TB_BLUE | TB_BOLD, 0, bview_num, 0, 0, TB_BLUE, 0, bview_count, 0, 0, | |
| @@ -1181,6 +1195,7 @@ static int _bview_is_in_range(bline_t *bline, bint_t col, srule_t **ret_srule) { | |
| mark.bline = bline; | |
| mark.col = col; | |
| DL_FOREACH(bline->buffer->range_srules, node) { | |
| + mark.is_block = node->srule->range_a->is_block; | |
| if (mark_is_between(&mark, node->srule->range_a, node->srule->range_b)) { | |
| *ret_srule = node->srule; | |
| return 1; | |
| diff --git a/cmd.c b/cmd.c | |
| index 5c19969..0a32a36 100644 | |
| --- a/cmd.c | |
| +++ b/cmd.c | |
| @@ -139,9 +139,9 @@ int cmd_insert_newline_below(cmd_context_t *ctx) { | |
| MLE_MULTI_CURSOR_CODE(ctx->cursor, | |
| mark_clone(cursor->mark, &mark); | |
| mark_move_eol(mark); | |
| - cursor->mark->lefty += 1; // Prevent cursor from moving if at eol | |
| + cursor->mark->is_lefty += 1; // Prevent cursor from moving if at eol | |
| mark_insert_after(mark, "\n", 1); | |
| - cursor->mark->lefty -= 1; | |
| + cursor->mark->is_lefty -= 1; | |
| mark_destroy(mark); | |
| ); | |
| return MLE_OK; | |
| @@ -350,6 +350,14 @@ int cmd_toggle_anchor(cmd_context_t *ctx) { | |
| return MLE_OK; | |
| } | |
| +// Toggle is_block on cursors | |
| +int cmd_toggle_block(cmd_context_t *ctx) { | |
| + MLE_MULTI_CURSOR_CODE(ctx->cursor, | |
| + cursor_toggle_block(cursor); | |
| + ); | |
| + return MLE_OK; | |
| +} | |
| + | |
| // Drop an is_asleep=1 cursor | |
| int cmd_drop_sleeping_cursor(cmd_context_t *ctx) { | |
| return bview_add_cursor_asleep(ctx->bview, ctx->cursor->mark->bline, ctx->cursor->mark->col, NULL); | |
| diff --git a/cursor.c b/cursor.c | |
| index 9386153..b9f1945 100644 | |
| --- a/cursor.c | |
| +++ b/cursor.c | |
| @@ -10,6 +10,9 @@ int cursor_clone(cursor_t *cursor, int use_srules, cursor_t **ret_clone) { | |
| cursor_toggle_anchor(clone, use_srules); | |
| mark_join(clone->anchor, cursor->anchor); | |
| } | |
| + if (cursor->is_block) { | |
| + cursor_toggle_block(clone); | |
| + } | |
| *ret_clone = clone; | |
| return MLE_OK; | |
| } | |
| @@ -65,6 +68,14 @@ int cursor_lift_anchor(cursor_t *cursor) { | |
| return cursor_toggle_anchor(cursor, cursor->sel_rule ? 1 : 0); | |
| } | |
| +// Toggle is_block mode | |
| +int cursor_toggle_block(cursor_t *cursor) { | |
| + cursor->is_block = cursor->is_block ? 0 : 1; | |
| + if (cursor->mark) cursor->mark->is_block = cursor->is_block; | |
| + if (cursor->anchor) cursor->anchor->is_block = cursor->is_block; | |
| + return MLE_OK; | |
| +} | |
| + | |
| // Get lo and hi marks in a is_anchored=1 cursor | |
| int cursor_get_lo_hi(cursor_t *cursor, mark_t **ret_lo, mark_t **ret_hi) { | |
| if (!cursor->is_anchored) { | |
| @@ -305,8 +316,8 @@ int cursor_replace(cursor_t *cursor, int interactive, char *opt_regex, char *opt | |
| mark_join(search_mark, cursor->mark); | |
| mark_join(orig_mark, cursor->mark); | |
| orig_viewport_y = cursor->bview->viewport_y; | |
| - orig_mark->lefty = 1; // lefty==1 avoids moving when text is inserted at mark | |
| - lo_mark->lefty = 1; | |
| + orig_mark->is_lefty = 1; // is_lefty==1 avoids moving when text is inserted at mark | |
| + lo_mark->is_lefty = 1; | |
| if (cursor->is_anchored) { | |
| anchored_before = mark_is_gt(cursor->mark, cursor->anchor); | |
| mark_join(lo_mark, !anchored_before ? cursor->mark : cursor->anchor); | |
| diff --git a/editor.c b/editor.c | |
| index d8f2c08..1111fe9 100644 | |
| --- a/editor.c | |
| +++ b/editor.c | |
| @@ -1485,6 +1485,7 @@ static void _editor_register_cmds(editor_t *editor) { | |
| _editor_register_cmd_fn(editor, "cmd_split_vertical", cmd_split_vertical); | |
| _editor_register_cmd_fn(editor, "cmd_suspend", cmd_suspend); | |
| _editor_register_cmd_fn(editor, "cmd_toggle_anchor", cmd_toggle_anchor); | |
| + _editor_register_cmd_fn(editor, "cmd_toggle_block", cmd_toggle_block); | |
| _editor_register_cmd_fn(editor, "cmd_uncut", cmd_uncut); | |
| _editor_register_cmd_fn(editor, "cmd_undo", cmd_undo); | |
| _editor_register_cmd_fn(editor, "cmd_viewport_bot", cmd_viewport_bot); | |
| @@ -1583,6 +1584,7 @@ static void _editor_init_kmaps(editor_t *editor) { | |
| MLE_KBINDING_DEF("cmd_delete_word_before", "C-w"), | |
| MLE_KBINDING_DEF("cmd_delete_word_after", "M-d"), | |
| MLE_KBINDING_DEF("cmd_toggle_anchor", "M-a"), | |
| + MLE_KBINDING_DEF("cmd_toggle_block", "F6"), | |
| MLE_KBINDING_DEF("cmd_drop_sleeping_cursor", "C-/ ."), | |
| MLE_KBINDING_DEF("cmd_wake_sleeping_cursors", "C-/ a"), | |
| MLE_KBINDING_DEF("cmd_remove_extra_cursors", "C-/ /"), | |
| diff --git a/mark.c b/mark.c | |
| index c5afc11..42107e3 100644 | |
| --- a/mark.c | |
| +++ b/mark.c | |
| @@ -36,6 +36,9 @@ int mark_clone_w_letter(mark_t *self, char letter, mark_t **ret_mark) { | |
| // Insert data before mark | |
| int mark_insert_before(mark_t *self, char *data, bint_t data_len) { | |
| + if (self->is_block && !(data_len == 1 && *data == '\n')) { | |
| + return buffer_block_replace(self->bline->buffer, self, NULL, data, data_len); | |
| + } | |
| return bline_insert(self->bline, self->col, data, data_len, NULL); | |
| } | |
| @@ -238,16 +241,21 @@ int mark_is_lte(mark_t *self, mark_t *other) { | |
| // Return 1 if self is between a and b | |
| int mark_is_between(mark_t *self, mark_t *ma, mark_t *mb) { | |
| mark_t *a, *b; | |
| - if (mark_is_gt(mb, ma)) { | |
| - a = ma; | |
| - b = mb; | |
| - } else { | |
| - a = mb; | |
| - b = ma; | |
| - } | |
| + if (self->is_block) return mark_is_between_rect(self, ma, mb); | |
| + mark_cmp(ma, mb, &a, &b); | |
| return mark_is_gte(self, a) && mark_is_lt(self, b) ? 1 : 0; | |
| } | |
| +// Return 1 if self is between the rectangle defined by a and b | |
| +int mark_is_between_rect(mark_t *self, mark_t *ma, mark_t *mb) { | |
| + bint_t left, right, top, bottom; | |
| + mark_get_block_dim(ma, mb, &left, &right, &top, &bottom, NULL, NULL); | |
| + return self->bline->line_index >= top | |
| + && self->bline->line_index <= bottom | |
| + && self->col >= left | |
| + && self->col <= right ? 1 : 0; | |
| +} | |
| + | |
| // Find top-level bracket to the left examining no more than max_chars | |
| int mark_find_bracket_top(mark_t *self, bint_t max_chars, bline_t **ret_line, bint_t *ret_col, bint_t *ret_brkt) { | |
| bline_t *cur_line; | |
| @@ -369,6 +377,7 @@ int mark_find_bracket_pair(mark_t *self, bint_t max_chars, bline_t **ret_line, b | |
| int mark_delete_between_mark(mark_t *self, mark_t *other) { | |
| bint_t offset_a; | |
| bint_t offset_b; | |
| + if (self->is_block) return mark_delete_between_mark_rect(self, other); | |
| buffer_get_offset(self->bline->buffer, self->bline, self->col, &offset_a); | |
| buffer_get_offset(other->bline->buffer, other->bline, other->col, &offset_b); | |
| if (offset_a == offset_b) { | |
| @@ -379,26 +388,92 @@ int mark_delete_between_mark(mark_t *self, mark_t *other) { | |
| return buffer_delete(self->bline->buffer, offset_a, offset_b - offset_a); | |
| } | |
| +// Block erase data between self and other | |
| +int mark_delete_between_mark_rect(mark_t *self, mark_t *other) { | |
| + str_t ws = {0}; | |
| + bline_t *bline; | |
| + bint_t col, width; | |
| + mark_t *lo, *hi; | |
| + | |
| + mark_cmp(self, other, &lo, &hi); | |
| + mark_get_block_dim(lo, hi, &col, NULL, NULL, NULL, &width, NULL); | |
| + | |
| + for (bline = lo->bline; bline != hi->bline->next; bline = bline->next) { | |
| + MLBUF_BLINE_ENSURE_CHARS(bline); | |
| + if (col < bline->char_count) { | |
| + str_append_nchars(&ws, ' ', (size_t)(MLBUF_MIN(bline->char_count - col, width))); | |
| + } | |
| + if (bline != hi->bline) str_append_char(&ws, '\n'); | |
| + } | |
| + | |
| + buffer_block_replace(self->bline->buffer, self, other, ws.data, ws.len); | |
| + str_free(&ws); | |
| + | |
| + return MLBUF_OK; | |
| +} | |
| + | |
| // Return data between self and other | |
| int mark_get_between_mark(mark_t *self, mark_t *other, char **ret_str, bint_t *ret_str_len) { | |
| bint_t ig; | |
| - if (mark_is_gt(self, other)) { | |
| - return buffer_substr( | |
| - self->bline->buffer, | |
| - other->bline, other->col, | |
| - self->bline, self->col, | |
| - ret_str, ret_str_len, &ig | |
| - ); | |
| - } else if (mark_is_gt(other, self)) { | |
| - return buffer_substr( | |
| - self->bline->buffer, | |
| - self->bline, self->col, | |
| - other->bline, other->col, | |
| - ret_str, ret_str_len, &ig | |
| - ); | |
| - } | |
| - *ret_str = strdup(""); | |
| - *ret_str_len = 0; | |
| + mark_t *lo, *hi; | |
| + if (self->is_block) { | |
| + return mark_get_between_mark_rect(self, other, ret_str, ret_str_len); | |
| + } | |
| + if (mark_cmp(self, other, &lo, &hi) == 0) { | |
| + *ret_str = strdup(""); | |
| + *ret_str_len = 0; | |
| + return MLBUF_OK; | |
| + } | |
| + return buffer_substr( | |
| + lo->bline->buffer, | |
| + lo->bline, lo->col, | |
| + hi->bline, hi->col, | |
| + ret_str, ret_str_len, &ig | |
| + ); | |
| +} | |
| + | |
| +// Return data between self and other | |
| +int mark_get_between_mark_rect(mark_t *self, mark_t *other, char **ret_str, bint_t *ret_str_len) { | |
| + str_t ret = {0}; | |
| + bline_t *bline; | |
| + bint_t col, width, data_len, nchars; | |
| + mark_t *lo, *hi; | |
| + char *data; | |
| + | |
| + mark_cmp(self, other, &lo, &hi); | |
| + mark_get_block_dim(lo, hi, &col, NULL, NULL, NULL, &width, NULL); | |
| + | |
| + for (bline = lo->bline; bline != hi->bline->next; bline = bline->next) { | |
| + MLBUF_BLINE_ENSURE_CHARS(bline); | |
| + if (col < bline->char_count) { | |
| + // TODO avoid alloc | |
| + buffer_substr(lo->bline->buffer, bline, col, bline, MLBUF_MIN(bline->char_count - col, width), &data, &data_len, &nchars); | |
| + str_append_len(&ret, data, data_len); | |
| + free(data); | |
| + } | |
| + if (bline != hi->bline) str_append_char(&ret, '\n'); | |
| + } | |
| + *ret_str = ret.data; | |
| + *ret_str_len = (bint_t)ret.len; | |
| + return MLBUF_OK; | |
| +} | |
| + | |
| +// Return dimensions of rectangle defined by a and b | |
| +int mark_get_block_dim(mark_t *a, mark_t *b, bint_t *optret_l, bint_t *optret_r, bint_t *optret_t, bint_t *optret_b, bint_t *optret_w, bint_t *optret_h) { | |
| + bint_t left, right, top, bottom, width, height; | |
| + mark_cmp(a, b, &a, &b); | |
| + left = MLBUF_MIN(a->col, b->col); | |
| + right = MLBUF_MAX(a->col, b->col); | |
| + top = MLBUF_MIN(a->bline->line_index, b->bline->line_index); | |
| + bottom = MLBUF_MAX(a->bline->line_index, b->bline->line_index); | |
| + width = (right - left) + 1; | |
| + height = (bottom - top) + 1; | |
| + if (optret_l) *optret_l = left; | |
| + if (optret_r) *optret_r = right; | |
| + if (optret_t) *optret_t = top; | |
| + if (optret_b) *optret_b = bottom; | |
| + if (optret_w) *optret_w = width; | |
| + if (optret_h) *optret_h = height; | |
| return MLBUF_OK; | |
| } | |
| @@ -407,13 +482,7 @@ int mark_get_nchars_between(mark_t *self, mark_t *other, bint_t *ret_nchars) { | |
| mark_t *a, *b; | |
| bline_t *bline; | |
| bint_t col, nchars; | |
| - if (mark_is_gt(self, other)) { | |
| - a = other; | |
| - b = self; | |
| - } else if (mark_is_gt(other, self)) { | |
| - a = self; | |
| - b = other; | |
| - } else { | |
| + if (mark_cmp(self, other, &a, &b) == 0) { | |
| *ret_nchars = 0; | |
| return MLBUF_OK; | |
| } | |
| @@ -652,11 +721,29 @@ int mark_get_char_before(mark_t *self, uint32_t *ret_char) { | |
| return MLBUF_OK; | |
| } | |
| + | |
| +// Output mark a and b sorted as ret_lo and ret_hi | |
| +// Return -1 if a comes before b, 1 if b comes after a, or 0 if equal | |
| +int mark_cmp(mark_t *a, mark_t *b, mark_t **ret_lo, mark_t **ret_hi) { | |
| + if (mark_is_gt(b, a)) { | |
| + *ret_lo = a; | |
| + *ret_hi = b; | |
| + return -1; | |
| + } else if (mark_is_gt(a, b)) { | |
| + *ret_lo = b; | |
| + *ret_hi = a; | |
| + return 1; | |
| + } | |
| + *ret_lo = a; | |
| + *ret_hi = b; | |
| + return 0; | |
| +} | |
| + | |
| // Return 1 if mark is after col, else 0. Lefty marks are considered 'after' col | |
| // if `mark->col > col`. Righty marks (the default) are considered 'after' col | |
| // if `mark->col >= col`. | |
| int mark_is_after_col_minus_lefties(mark_t *self, bint_t col) { | |
| - if (self->lefty) { | |
| + if (self->is_lefty) { | |
| return self->col > col ? 1 : 0; | |
| } | |
| return self->col >= col ? 1 : 0; | |
| diff --git a/mlbuf.h b/mlbuf.h | |
| index adc09b4..8487573 100644 | |
| --- a/mlbuf.h | |
| +++ b/mlbuf.h | |
| @@ -131,7 +131,8 @@ struct mark_s { | |
| char letter; | |
| mark_t *next; | |
| mark_t *prev; | |
| - int lefty; | |
| + int is_lefty; | |
| + int is_block; | |
| }; | |
| // srule_t | |
| @@ -175,6 +176,7 @@ int buffer_substr(buffer_t *self, bline_t *start_line, bint_t start_col, bline_t | |
| int buffer_insert(buffer_t *self, bint_t offset, char *data, bint_t data_len, bint_t *optret_num_chars); | |
| int buffer_delete(buffer_t *self, bint_t offset, bint_t num_chars); | |
| int buffer_replace(buffer_t *self, bint_t offset, bint_t num_chars, char *data, bint_t data_len); | |
| +int buffer_block_replace(buffer_t *self, mark_t *a, mark_t *b, char *repl, bint_t repl_len); | |
| int buffer_insert_w_bline(buffer_t *self, bline_t *start_line, bint_t start_col, char *data, bint_t data_len, bint_t *optret_num_chars); | |
| int buffer_delete_w_bline(buffer_t *self, bline_t *start_line, bint_t start_col, bint_t num_chars); | |
| int buffer_replace_w_bline(buffer_t *self, bline_t *start_line, bint_t start_col, bint_t num_chars, char *data, bint_t data_len); | |
| @@ -208,9 +210,11 @@ int bline_count_chars(bline_t *bline); | |
| // mark functions | |
| int mark_clone(mark_t *self, mark_t **ret_mark); | |
| int mark_clone_w_letter(mark_t *self, char letter, mark_t **ret_mark); | |
| +int mark_cmp(mark_t *a, mark_t *b, mark_t **ret_lo, mark_t **ret_hi); | |
| int mark_delete_after(mark_t *self, bint_t num_chars); | |
| int mark_delete_before(mark_t *self, bint_t num_chars); | |
| int mark_delete_between_mark(mark_t *self, mark_t *other); | |
| +int mark_delete_between_mark_rect(mark_t *self, mark_t *other); | |
| int mark_destroy(mark_t *self); | |
| int mark_find_bracket_pair(mark_t *self, bint_t max_chars, bline_t **ret_line, bint_t *ret_col, bint_t *ret_brkt); | |
| int mark_find_bracket_top(mark_t *self, bint_t max_chars, bline_t **ret_line, bint_t *ret_col, bint_t *ret_brkt); | |
| @@ -221,6 +225,8 @@ int mark_find_prev_cre(mark_t *self, pcre *cre, bline_t **ret_line, bint_t *ret_ | |
| int mark_find_prev_re(mark_t *self, char *re, bint_t re_len, bline_t **ret_line, bint_t *ret_col, bint_t *ret_num_chars); | |
| int mark_find_prev_str(mark_t *self, char *str, bint_t str_len, bline_t **ret_line, bint_t *ret_col, bint_t *ret_num_chars); | |
| int mark_get_between_mark(mark_t *self, mark_t *other, char **ret_str, bint_t *ret_str_len); | |
| +int mark_get_between_mark_rect(mark_t *self, mark_t *other, char **ret_str, bint_t *ret_str_len); | |
| +int mark_get_block_dim(mark_t *a, mark_t *b, bint_t *optret_l, bint_t *optret_r, bint_t *optret_t, bint_t *optret_b, bint_t *optret_w, bint_t *optret_h); | |
| int mark_get_char_after(mark_t *self, uint32_t *ret_char); | |
| int mark_get_char_before(mark_t *self, uint32_t *ret_char); | |
| int mark_get_nchars_between(mark_t *self, mark_t *other, bint_t *ret_nchars); | |
| @@ -231,12 +237,13 @@ int mark_is_after_col_minus_lefties(mark_t *self, bint_t col); | |
| int mark_is_at_bol(mark_t *self); | |
| int mark_is_at_eol(mark_t *self); | |
| int mark_is_at_word_bound(mark_t *self, int side); | |
| +int mark_is_between(mark_t *self, mark_t *ma, mark_t *mb); | |
| +int mark_is_between_rect(mark_t *self, mark_t *ma, mark_t *mb); | |
| int mark_is_eq(mark_t *self, mark_t *other); | |
| int mark_is_gte(mark_t *self, mark_t *other); | |
| int mark_is_gt(mark_t *self, mark_t *other); | |
| int mark_is_lte(mark_t *self, mark_t *other); | |
| int mark_is_lt(mark_t *self, mark_t *other); | |
| -int mark_is_between(mark_t *self, mark_t *ma, mark_t *mb); | |
| int mark_join(mark_t *self, mark_t *other); | |
| int mark_move_beginning(mark_t *self); | |
| int mark_move_bol(mark_t *self); | |
| @@ -280,6 +287,7 @@ int srule_destroy(srule_t *srule); | |
| // utf8 functions | |
| int utf8_char_length(char c); | |
| +int utf8_str_length(const char *d, size_t dlen, size_t max_slen, size_t *out_slen, size_t *out_dlen); | |
| int utf8_char_to_unicode(uint32_t *out, const char *c, const char *stop); | |
| int utf8_unicode_to_char(char *out, uint32_t c); | |
| @@ -301,6 +309,7 @@ void str_clear(str_t *str); | |
| void str_free(str_t *str); | |
| void str_sprintf(str_t *str, const char *fmt, ...); | |
| void str_append_replace_with_backrefs(str_t *str, char *subj, char *repl, int pcre_rc, int *pcre_ovector, int pcre_ovecsize); | |
| +void str_append_nchars(str_t *str, char c, size_t n); | |
| // Macros | |
| #define MLBUF_DEBUG 1 | |
| diff --git a/mle.h b/mle.h | |
| index c51fb3b..cbeadbe 100644 | |
| --- a/mle.h | |
| +++ b/mle.h | |
| @@ -225,6 +225,7 @@ struct cursor_s { | |
| mark_t *anchor; | |
| int is_anchored; | |
| int is_asleep; | |
| + int is_block; | |
| srule_t *sel_rule; | |
| char *cut_buffer; | |
| cursor_t *next; | |
| @@ -465,6 +466,7 @@ int cursor_select_by_word_back(cursor_t *cursor, int use_srules); | |
| int cursor_select_by_word(cursor_t *cursor, int use_srules); | |
| int cursor_select_by_word_forward(cursor_t *cursor, int use_srules); | |
| int cursor_toggle_anchor(cursor_t *cursor, int use_srules); | |
| +int cursor_toggle_block(cursor_t *cursor); | |
| int cursor_uncut(cursor_t *cursor); | |
| // cmd functions | |
| @@ -546,6 +548,7 @@ int cmd_split_horizontal(cmd_context_t *ctx); | |
| int cmd_split_vertical(cmd_context_t *ctx); | |
| int cmd_suspend(cmd_context_t *ctx); | |
| int cmd_toggle_anchor(cmd_context_t *ctx); | |
| +int cmd_toggle_block(cmd_context_t *ctx); | |
| int cmd_uncut(cmd_context_t *ctx); | |
| int cmd_undo(cmd_context_t *ctx); | |
| int cmd_viewport_bot(cmd_context_t *ctx); | |
| @@ -675,44 +678,45 @@ extern editor_t _editor; | |
| #define MLE_RE_WORD_BACK "((?<=\\W)\\w|^)" | |
| /* | |
| -TODO PRIORITY | |
| +TODO | |
| +[ ] add block select/move | |
| +[ ] replace MLBUF_BLINE_ENSURE_CHARS with function | |
| +[ ] add option to undo bactions in same loop# as a group | |
| [ ] catch ENOSPC | |
| -[ ] cleanup lua api, e.g., multi-retval | |
| [ ] reduce compiler warnings | |
| -[ ] add cmd_tabulate | |
| -[ ] add `_free_` hint to (opt)ret vars that need to be freed | |
| -[ ] finish uscript callbacks | |
| -[ ] add some sort of syntax checking via hooks | |
| [ ] add ## param to page_up/down (by half/third etc) | |
| [ ] fix alt/ctrl-enter in prompt inserts newline | |
| -[ ] replace mark_set_pcre_capture with mark local | |
| [ ] fix invalid cli switch should exit(1) | |
| -[ ] use editor prompt history when bview prompt history is empty | |
| -TODO BACKLOG | |
| -[ ] improve isearch kmap (next/prev history) | |
| -[ ] add option to undo bactions in same loop# as a group | |
| +BACKLOG | |
| [ ] add buffer_repeat | |
| -[ ] add block select/move | |
| -[ ] investigate crash when M-e cat'ing huge files | |
| -[ ] add mark stack (push, move around, pop to go back) | |
| +[ ] add cmd_tabulate | |
| +[ ] add `_free_` hint to (opt)ret vars that need to be freed | |
| [ ] add last cmd status indicator | |
| +[ ] add mark stack (push, move around, pop to go back) | |
| +[ ] add some sort of syntax checking via hooks | |
| [ ] check if buffer exists by inode instead of path | |
| +[ ] cleanup lua api, e.g., multi-retval | |
| +[ ] finish uscript callbacks | |
| +[ ] improve isearch kmap (next/prev history) | |
| +[ ] investigate crash when M-e cat'ing huge files | |
| +[ ] move single-use macros out of mle.h | |
| +[ ] replace mark_set_pcre_capture with mark local | |
| [ ] review default key bindings | |
| -[ ] review use of multi_cursor_code | |
| [ ] review use of MLE_RETURN_ERR | |
| -[ ] move single-use macros out of mle.h | |
| -TODO REWRITE | |
| +[ ] review use of multi_cursor_code | |
| +[ ] use editor prompt history when bview prompt history is empty | |
| +REWRITE | |
| [ ] rewrite kmap, trie code is hard to read, ** and ## is not elegant, do not use kinput as hash key | |
| -[ ] rewrite hili code (use_srules sucks; overlapping multi rules bug; test_buffer_srule_overlap.c.todo) | |
| +[ ] rewrite hili code (use_srules sucks; test_buffer_srule_overlap.c.todo) | |
| [ ] rewrite aproc and menu code | |
| [ ] rewrite buffer_set_mmapped to avoid huge mallocs | |
| -TODO MAYBE | |
| -[ ] ?allow uscripts to preempt control, use shared uscriptfd | |
| -[ ] ?add vim emulation mode | |
| -[ ] ?add refcounting to prevent uscripts from segfaulting | |
| -[ ] ?make colors, status line, layout configurable | |
| -[ ] ?add multi-level undo/redo | |
| -[ ] ?add simple mouse support | |
| +MAYBE | |
| +[ ] add multi-level undo/redo | |
| +[ ] add refcounting to prevent uscripts from segfaulting | |
| +[ ] add simple mouse support | |
| +[ ] add vim emulation mode | |
| +[ ] allow uscripts to preempt control, use shared uscriptfd | |
| +[ ] make colors, status line, layout configurable | |
| */ | |
| #endif | |
| diff --git a/utf8.c b/utf8.c | |
| index 12802b2..6be8e04 100644 | |
| --- a/utf8.c | |
| +++ b/utf8.c | |
| @@ -28,6 +28,22 @@ int utf8_char_length(char c) { | |
| return utf8_length[(unsigned char)c]; | |
| } | |
| +int utf8_str_length(const char *d, size_t dlen, size_t max_slen, size_t *out_slen, size_t *out_dlen) { | |
| + size_t slen, clen, vlen, i; | |
| + slen = 0; | |
| + clen = 0; | |
| + vlen = 0; | |
| + for (i = 0; i < dlen; i += clen) { | |
| + clen = (size_t)utf8_char_length(d[i]); | |
| + slen += 1; | |
| + vlen += clen; | |
| + if (max_slen > 0 && slen >= max_slen) break; | |
| + } | |
| + if (out_dlen) *out_dlen = vlen; | |
| + if (out_slen) *out_slen = slen; | |
| + return 0; | |
| +} | |
| + | |
| int utf8_char_to_unicode(uint32_t *out, const char *c, const char *stop) { | |
| if (*c == 0) | |
| return -1; | |
| diff --git a/util.c b/util.c | |
| index c4851ba..ce8bd5e 100644 | |
| --- a/util.c | |
| +++ b/util.c | |
| @@ -478,6 +478,16 @@ void str_append_char(str_t *str, char c) { | |
| str_put_len(str, &c, 1, 0); | |
| } | |
| +// Append c to str n times | |
| +void str_append_nchars(str_t *str, char c, size_t n) { | |
| + size_t req_cap; | |
| + req_cap = str->len + n + 1; | |
| + str_ensure_cap(str, req_cap); | |
| + memset(str->data + str->len, c, n); | |
| + str->len += n; | |
| + *(str->data + str->len) = '\0'; | |
| +} | |
| + | |
| // Prepend from data up until data_stop to str | |
| void str_prepend_stop(str_t *str, char *data, char *data_stop) { | |
| size_t data_len; | |
| @@ -503,7 +513,7 @@ void str_set(str_t *str, char *data) { | |
| // Set str to data for data_len bytes | |
| void str_set_len(str_t *str, char *data, size_t data_len) { | |
| str_ensure_cap(str, data_len+1); | |
| - memcpy(str->data, data, data_len); | |
| + memmove(str->data, data, data_len); | |
| str->len = data_len; | |
| *(str->data + str->len) = '\0'; | |
| } | |
| @@ -530,9 +540,7 @@ void str_sprintf(str_t *str, const char *fmt, ...) { | |
| void str_put_len(str_t *str, char *data, size_t data_len, int is_prepend) { | |
| size_t req_cap; | |
| req_cap = str->len + data_len + 1; | |
| - if (req_cap > str->cap) { | |
| - str_ensure_cap(str, req_cap); | |
| - } | |
| + str_ensure_cap(str, req_cap); | |
| if (is_prepend) { | |
| memmove(str->data + data_len, str->data, str->len); | |
| memcpy(str->data, data, data_len); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment