From 2c8fcaa8fbb17b99aa5b0f8bfbbe0451dfa509f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Andr=C3=A9=20Tanner?= Date: Wed, 19 Apr 2017 13:11:52 +0200 Subject: 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. in insert mode has not yet been adapted and register handling in general needs to be cleaned up further. Fix #527 --- main.c | 1 + register.c | 138 +++++++++++++++++++++++++++++++++++++++++++------------- register.h | 17 +++++-- view.c | 6 --- view.h | 2 - vis-core.h | 3 ++ vis-operators.c | 10 ++-- vis.c | 31 ++++++++++--- 8 files changed, 152 insertions(+), 56 deletions(-) diff --git a/main.c b/main.c index 9e7e9a3..8091c13 100644 --- a/main.c +++ b/main.c @@ -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) diff --git a/register.c b/register.c index 69409c1..b55a059 100644 --- a/register.c +++ b/register.c @@ -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); } diff --git a/register.h b/register.h index a815a90..379da87 100644 --- a/register.h +++ b/register.h @@ -4,11 +4,11 @@ #include #include #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 diff --git a/view.c b/view.c index 7e893ff..76410aa 100644 --- a/view.c +++ b/view.c @@ -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) { diff --git a/view.h b/view.h index c382505..262c581 100644 --- a/view.h +++ b/view.h @@ -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*); diff --git a/vis-core.h b/vis-core.h index 654085d..b0caee1 100644 --- a/vis-core.h +++ b/vis-core.h @@ -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; diff --git a/vis.c b/vis.c index 4cfa3fe..a6a48da 100644 --- a/vis.c +++ b/vis.c @@ -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; } -- cgit v1.2.3