diff options
| author | Marc André Tanner <mat@brain-dump.org> | 2017-04-19 13:11:52 +0200 |
|---|---|---|
| committer | Marc André Tanner <mat@brain-dump.org> | 2017-04-19 13:42:25 +0200 |
| commit | 2c8fcaa8fbb17b99aa5b0f8bfbbe0451dfa509f3 (patch) | |
| tree | 1160dd96040c19f2a6da365e29240d978c6067ca | |
| parent | a2316394d35eb548545730cbf2b57b27e733214c (diff) | |
| download | vis-2c8fcaa8fbb17b99aa5b0f8bfbbe0451dfa509f3.tar.gz vis-2c8fcaa8fbb17b99aa5b0f8bfbbe0451dfa509f3.tar.xz | |
vis: restructure register handling
Decouple register content from cursors. Previously each cursor had
exactly one corresponding register. Now each register can save a
list of values whose lifetime is not tied to the cursor.
If multiple cursors exist and a put with a register holding only
a single value is performed, then this value is inserted at every
cursor location. If there are fewer values available than cursors,
then only the matching ones will be used. If a register holding
multiple values is inserted in a single cursor context, only the
first value will be used. Another option would be to join all
existing values. The details of this behavior might be changed
in the future.
<C-r> in insert mode has not yet been adapted and register handling
in general needs to be cleaned up further.
Fix #527
| -rw-r--r-- | main.c | 1 | ||||
| -rw-r--r-- | register.c | 138 | ||||
| -rw-r--r-- | register.h | 17 | ||||
| -rw-r--r-- | view.c | 6 | ||||
| -rw-r--r-- | view.h | 2 | ||||
| -rw-r--r-- | vis-core.h | 3 | ||||
| -rw-r--r-- | vis-operators.c | 10 | ||||
| -rw-r--r-- | vis.c | 31 |
8 files changed, 152 insertions, 56 deletions
@@ -18,6 +18,7 @@ #include "util.h" #include "libutf.h" #include "array.h" +#include "buffer.h" #define PAGE INT_MAX #define PAGE_HALF (INT_MAX-1) @@ -5,66 +5,143 @@ #include "text.h" #include "util.h" #include "register.h" +#include "buffer.h" + +static Buffer *register_buffer(Register *reg, size_t slot) { + Buffer *buf = array_get(®->values, slot); + if (buf) + return buf; + if (array_resize(®->values, slot) && (buf = array_get(®->values, slot))) + return buf; + Buffer new; + buffer_init(&new); + if (!array_add(®->values, &new)) + return NULL; + size_t capacity = array_capacity(®->values); + for (size_t i = array_length(®->values); i < capacity; i++) { + if (!array_add(®->values, &new)) + return NULL; + } + return array_get(®->values, slot); +} static ssize_t read_buffer(void *context, char *data, size_t len) { buffer_append(context, data, len); return len; } +bool register_init(Register *reg) { + Buffer buf; + buffer_init(&buf); + array_init_sized(®->values, sizeof(Buffer)); + return array_add(®->values, &buf); +} + void register_release(Register *reg) { - buffer_release(®->buf); + if (!reg) + return; + size_t n = array_capacity(®->values); + for (size_t i = 0; i < n; i++) + buffer_release(array_get(®->values, i)); + array_release(®->values); } -const char *register_get(Vis *vis, Register *reg, size_t *len) { +const char *register_slot_get(Vis *vis, Register *reg, size_t slot, size_t *len) { + if (len) + *len = 0; switch (reg->type) { case REGISTER_NORMAL: - if (reg->buf.len > 0 && reg->buf.data[reg->buf.len-1] != '\0') - buffer_append(®->buf, "\0", 1); + { + Buffer *buf = array_get(®->values, slot); + if (!buf) + return NULL; + buffer_terminate(buf); if (len) - *len = reg->buf.len > 0 ? reg->buf.len - 1 : 0; - return reg->buf.data; + *len = buffer_length0(buf); + return buffer_content0(buf); + } case REGISTER_CLIPBOARD: { Buffer buferr; buffer_init(&buferr); - buffer_clear(®->buf); + Buffer *buf = array_get(®->values, slot); + if (!buf) + return NULL; + buffer_clear(buf); int status = vis_pipe(vis, vis->win->file, &(Filerange){ .start = 0, .end = 0 }, (const char*[]){ VIS_CLIPBOARD, "--paste", NULL }, - ®->buf, read_buffer, &buferr, read_buffer); + buf, read_buffer, &buferr, read_buffer); if (status != 0) vis_info_show(vis, "Command failed %s", buffer_content0(&buferr)); - *len = reg->buf.len; - return reg->buf.data; + buffer_release(&buferr); + if (len) + *len = buffer_length0(buf); + return buffer_content0(buf); } case REGISTER_BLACKHOLE: default: - *len = 0; return NULL; } } +const char *register_get(Vis *vis, Register *reg, size_t *len) { + return register_slot_get(vis, reg, 0, len); +} + +bool register_slot_put(Vis *vis, Register *reg, size_t slot, const char *data, size_t len) { + if (reg->type != REGISTER_NORMAL) + return false; + Buffer *buf = register_buffer(reg, slot); + return buf && buffer_put(buf, data, len); +} + bool register_put(Vis *vis, Register *reg, const char *data, size_t len) { - return reg->type == REGISTER_NORMAL && buffer_put(®->buf, data, len); + return register_slot_put(vis, reg, 0, data, len) && + register_resize(reg, 1); } bool register_put0(Vis *vis, Register *reg, const char *data) { return register_put(vis, reg, data, strlen(data)+1); } -bool register_put_range(Vis *vis, Register *reg, Text *txt, Filerange *range) { +static bool register_slot_append_range(Register *reg, size_t slot, Text *txt, Filerange *range) { + switch (reg->type) { + case REGISTER_NORMAL: + { + Buffer *buf = register_buffer(reg, slot); + if (!buf) + return false; + size_t len = text_range_size(range); + if (len == SIZE_MAX || !buffer_grow(buf, len+1)) + return false; + if (buf->len > 0 && buf->data[buf->len-1] == '\0') + buf->len--; + buf->len += text_bytes_get(txt, range->start, len, buf->data + buf->len); + return buffer_append(buf, "\0", 1); + } + default: + return false; + } +} + +bool register_slot_put_range(Vis *vis, Register *reg, size_t slot, Text *txt, Filerange *range) { if (reg->append) - return register_append_range(reg, txt, range); + return register_slot_append_range(reg, slot, txt, range); + switch (reg->type) { case REGISTER_NORMAL: { + Buffer *buf = register_buffer(reg, slot); + if (!buf) + return false; size_t len = text_range_size(range); - if (len == SIZE_MAX || !buffer_reserve(®->buf, len+1)) + if (len == SIZE_MAX || !buffer_reserve(buf, len+1)) return false; - reg->buf.len = text_bytes_get(txt, range->start, len, reg->buf.data); - return buffer_append(®->buf, "\0", 1); + buf->len = text_bytes_get(txt, range->start, len, buf->data); + return buffer_append(buf, "\0", 1); } case REGISTER_CLIPBOARD: { @@ -77,6 +154,7 @@ bool register_put_range(Vis *vis, Register *reg, Text *txt, Filerange *range) { if (status != 0) vis_info_show(vis, "Command failed %s", buffer_content0(&buferr)); + buffer_release(&buferr); return status == 0; } case REGISTER_BLACKHOLE: @@ -86,19 +164,15 @@ bool register_put_range(Vis *vis, Register *reg, Text *txt, Filerange *range) { } } -bool register_append_range(Register *reg, Text *txt, Filerange *range) { - switch (reg->type) { - case REGISTER_NORMAL: - { - size_t len = text_range_size(range); - if (!buffer_grow(®->buf, len+1)) - return false; - if (reg->buf.len > 0 && reg->buf.data[reg->buf.len-1] == '\0') - reg->buf.len--; - reg->buf.len += text_bytes_get(txt, range->start, len, reg->buf.data + reg->buf.len); - return buffer_append(®->buf, "\0", 1); - } - default: - return false; - } +bool register_put_range(Vis *vis, Register *reg, Text *txt, Filerange *range) { + return register_slot_put_range(vis, reg, 0, txt, range) && + register_resize(reg, 1); +} + +size_t register_count(Register *reg) { + return array_length(®->values); +} + +bool register_resize(Register *reg, size_t count) { + return array_truncate(®->values, count); } @@ -4,11 +4,11 @@ #include <stddef.h> #include <stdbool.h> #include "vis.h" -#include "buffer.h" +#include "array.h" #include "text-util.h" typedef struct { - Buffer buf; + Array values; bool linewise; /* place register content on a new line when inserting? */ bool append; enum { @@ -18,11 +18,20 @@ typedef struct { } type; } Register; +bool register_init(Register*); void register_release(Register*); + const char *register_get(Vis*, Register*, size_t *len); -bool register_put(Vis*, Register*, const char *data, size_t len); +const char *register_slot_get(Vis*, Register*, size_t slot, size_t *len); + bool register_put0(Vis*, Register*, const char *data); +bool register_put(Vis*, Register*, const char *data, size_t len); +bool register_slot_put(Vis*, Register*, size_t slot, const char *data, size_t len); + bool register_put_range(Vis*, Register*, Text*, Filerange*); -bool register_append_range(Register*, Text*, Filerange*); +bool register_slot_put_range(Vis*, Register*, size_t slot, Text*, Filerange*); + +size_t register_count(Register*); +bool register_resize(Register*, size_t count); #endif @@ -55,7 +55,6 @@ struct Cursor { /* cursor position */ Selection *sel; /* selection (if any) which folows the cursor upon movement */ Mark lastsel_anchor;/* previously used selection data, */ Mark lastsel_cursor;/* used to restore it */ - Register reg; /* per cursor register to support yank/put operation */ int generation; /* used to filter out newly created cursors during iteration */ int number; /* how many cursors are located before this one */ View *view; /* associated view to which this cursor belongs */ @@ -1005,7 +1004,6 @@ bool view_cursors_multiple(View *view) { static void view_cursors_free(Cursor *c) { if (!c) return; - register_release(&c->reg); for (Cursor *after = c->next; after; after = after->next) after->number--; if (c->prev) @@ -1119,10 +1117,6 @@ int view_cursors_cell_set(Cursor *c, int cell) { return c->col; } -Register *view_cursors_register(Cursor *c) { - return &c->reg; -} - void view_cursors_scroll_to(Cursor *c, size_t pos) { View *view = c->view; if (view->cursor == c) { @@ -160,8 +160,6 @@ void view_cursors_to(Cursor*, size_t pos); void view_cursors_scroll_to(Cursor*, size_t pos); /* place cursor on given (line, column) pair, both values are 1-based */ void view_cursors_place(Cursor*, size_t line, size_t col); -/* get register associated with this register */ -Register *view_cursors_register(Cursor*); /* start selected area at current cursor position. further cursor movements * will affect the selected region. */ void view_cursors_selection_start(Cursor*); @@ -10,6 +10,7 @@ #include "map.h" #include "ring-buffer.h" #include "array.h" +#include "buffer.h" /* a mode contains a set of key bindings which are currently valid. * @@ -40,6 +41,7 @@ struct Mode { struct OperatorContext { int count; /* how many times should the command be executed? */ Register *reg; /* always non-NULL, set to a default register */ + size_t reg_slot; /* register slot to use */ Filerange range; /* which part of the file should be affected by the operator */ size_t pos; /* at which byte from the start of the file should the operation start? */ size_t newpos; /* new position after motion or EPOS if none given */ @@ -248,6 +250,7 @@ void action_reset(Action*); size_t vis_text_insert_nl(Vis*, Text*, size_t pos); void mode_set(Vis *vis, Mode *new_mode); +Macro *macro_get(Vis *vis, enum VisRegister); void window_selection_save(Win *win); Win *window_new_file(Vis*, File*, enum UiOption); diff --git a/vis-operators.c b/vis-operators.c index 1ef32c3..38ac7c8 100644 --- a/vis-operators.c +++ b/vis-operators.c @@ -8,7 +8,7 @@ static size_t op_delete(Vis *vis, Text *txt, OperatorContext *c) { c->reg->linewise = c->linewise; - register_put_range(vis, c->reg, txt, &c->range); + register_slot_put_range(vis, c->reg, c->reg_slot, txt, &c->range); text_delete_range(txt, &c->range); size_t pos = c->range.start; if (c->linewise && pos == text_size(txt)) @@ -26,10 +26,10 @@ static size_t op_change(Vis *vis, Text *txt, OperatorContext *c) { static size_t op_yank(Vis *vis, Text *txt, OperatorContext *c) { c->reg->linewise = c->linewise; - register_put_range(vis, c->reg, txt, &c->range); + register_slot_put_range(vis, c->reg, c->reg_slot, txt, &c->range); if (c->reg == &vis->registers[VIS_REG_DEFAULT]) { vis->registers[VIS_REG_ZERO].linewise = c->reg->linewise; - register_put_range(vis, &vis->registers[VIS_REG_ZERO], txt, &c->range); + register_slot_put_range(vis, &vis->registers[VIS_REG_ZERO], c->reg_slot, txt, &c->range); } return c->linewise ? c->pos : c->range.start; } @@ -59,7 +59,7 @@ static size_t op_put(Vis *vis, Text *txt, OperatorContext *c) { } size_t len; - const char *data = register_get(vis, c->reg, &len); + const char *data = register_slot_get(vis, c->reg, c->reg_slot, &len); for (int i = 0; i < c->count; i++) { char nl; @@ -298,7 +298,7 @@ bool vis_operator(Vis *vis, enum VisOperator id, ...) { break; case VIS_OP_REPLACE: { - Macro *macro = &vis->registers[VIS_REG_DOT].buf; + Macro *macro = macro_get(vis, VIS_REG_DOT); macro_reset(macro); macro_append(macro, va_arg(ap, char*)); vis->action.arg.s = macro->data; @@ -54,7 +54,6 @@ const RegisterDef vis_registers[] = { [VIS_REG_SHELL] = { '!', VIS_HELP("Last shell command given to either <, >, |, or !") }, }; -static Macro *macro_get(Vis *vis, enum VisRegister); static void macro_replay(Vis *vis, const Macro *macro); static void macro_replay_internal(Vis *vis, const Macro *macro); static void vis_keys_push(Vis *vis, const char *input, size_t pos, bool record); @@ -670,6 +669,8 @@ Vis *vis_new(Ui *ui, VisEvent *event) { vis->tabwidth = 8; vis->expandtab = false; vis->change_colors = true; + for (size_t i = 0; i < LENGTH(vis->registers); i++) + register_init(&vis->registers[i]); vis->registers[VIS_REG_BLACKHOLE].type = REGISTER_BLACKHOLE; vis->registers[VIS_REG_CLIPBOARD].type = REGISTER_CLIPBOARD; array_init(&vis->operators); @@ -834,6 +835,15 @@ void vis_do(Vis *vis) { a->type & LINEWISE || (a->movement && a->movement->type & LINEWISE) || vis->mode == &vis_modes[VIS_MODE_VISUAL_LINE]); + + Register *reg = a->reg; + size_t reg_slot = multiple_cursors ? EPOS : 0; + size_t last_reg_slot = reg_slot; + if (!reg) + reg = &vis->registers[file->internal ? VIS_REG_PROMPT : VIS_REG_DEFAULT]; + if (a->op == &vis_operators[VIS_OP_PUT_AFTER] && multiple_cursors && register_count(reg) == 1) + reg_slot = 0; + for (Cursor *cursor = view_cursors(view), *next; cursor; cursor = next) { if (vis->interrupted) break; @@ -847,21 +857,20 @@ void vis_do(Vis *vis) { continue; } - Register *reg = multiple_cursors ? view_cursors_register(cursor) : a->reg; - if (!reg) - reg = &vis->registers[file->internal ? VIS_REG_PROMPT : VIS_REG_DEFAULT]; - OperatorContext c = { .count = count, .pos = pos, .newpos = EPOS, .range = text_range_empty(), .reg = reg, + .reg_slot = reg_slot == EPOS ? (size_t)view_cursors_number(cursor) : reg_slot, .linewise = linewise, .arg = &a->arg, .context = a->op ? a->op->context : NULL, }; + last_reg_slot = c.reg_slot; + bool err = false; if (a->movement) { size_t start = pos; @@ -1000,6 +1009,14 @@ void vis_do(Vis *vis) { } if (a->op) { + + if (a->op == &vis_operators[VIS_OP_YANK] || + a->op == &vis_operators[VIS_OP_DELETE] || + a->op == &vis_operators[VIS_OP_CHANGE] || + a->op == &vis_operators[VIS_OP_REPLACE]) { + register_resize(reg, last_reg_slot+1); + } + /* we do not support visual repeat, still do something resonable */ if (vis->mode->visual && !a->movement && !a->textobj) a->movement = &vis_motions[VIS_MOVE_NOP]; @@ -1389,13 +1406,13 @@ int vis_run(Vis *vis, int argc, char *argv[]) { return vis->exit_status; } -static Macro *macro_get(Vis *vis, enum VisRegister id) { +Macro *macro_get(Vis *vis, enum VisRegister id) { if (id == VIS_MACRO_LAST_RECORDED) return vis->last_recording; if (VIS_REG_A <= id && id <= VIS_REG_Z) id -= VIS_REG_A; if (id < LENGTH(vis->registers)) - return &vis->registers[id].buf; + return array_get(&vis->registers[id].values, 0); return NULL; } |
