Skip to content

Instantly share code, notes, and snippets.

@adsr
Created January 30, 2021 05:10
Show Gist options
  • Select an option

  • Save adsr/51101740a8b974ffec1505851dbfdf8c to your computer and use it in GitHub Desktop.

Select an option

Save adsr/51101740a8b974ffec1505851dbfdf8c to your computer and use it in GitHub Desktop.
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