static const char *cursors_select_skip(Vis *vis, const char *keys, const Arg *arg) { View *view = vis_view(vis); Cursor *cursor = view_cursor(view); keys = cursors_select_next(vis, keys, arg); if (cursor != view_cursor(view)) view_cursors_dispose(cursor); return keys; }
static bool cmd_guard(Vis *vis, Win *win, Command *cmd, const char *argv[], Cursor *cur, Filerange *range) { if (!win) return false; bool match = !text_search_range_forward(win->file->text, range->start, text_range_size(range), cmd->regex, 0, NULL, 0); if (match ^ (argv[0][0] == 'v')) return sam_execute(vis, win, cmd->cmd, cur, range); view_cursors_dispose(cur); return true; }
void action_do(Vis *vis, Action *a) { Win *win = vis->win; Text *txt = win->file->text; View *view = win->view; if (a->op == &vis_operators[VIS_OP_FILTER] && !vis->mode->visual) vis_mode_switch(vis, VIS_MODE_VISUAL_LINE); int count = MAX(a->count, 1); bool repeatable = a->op && !vis->macro_operator; bool multiple_cursors = view_cursors_multiple(view); bool linewise = !(a->type & CHARWISE) && ( a->type & LINEWISE || (a->movement && a->movement->type & LINEWISE) || vis->mode == &vis_modes[VIS_MODE_VISUAL_LINE]); for (Cursor *cursor = view_cursors(view), *next; cursor; cursor = next) { next = view_cursors_next(cursor); size_t pos = view_cursors_pos(cursor); Register *reg = multiple_cursors ? view_cursors_register(cursor) : a->reg; if (!reg) reg = &vis->registers[win->file->internal ? VIS_REG_PROMPT : VIS_REG_DEFAULT]; OperatorContext c = { .count = count, .pos = pos, .newpos = EPOS, .range = text_range_empty(), .reg = reg, .linewise = linewise, .arg = &a->arg, }; if (a->movement) { size_t start = pos; for (int i = 0; i < count; i++) { size_t pos_prev = pos; if (a->movement->txt) pos = a->movement->txt(txt, pos); else if (a->movement->cur) pos = a->movement->cur(cursor); else if (a->movement->file) pos = a->movement->file(vis, vis->win->file, pos); else if (a->movement->vis) pos = a->movement->vis(vis, txt, pos); else if (a->movement->view) pos = a->movement->view(vis, view); else if (a->movement->win) pos = a->movement->win(vis, win, pos); else if (a->movement->user) pos = a->movement->user(vis, win, a->movement->data, pos); if (pos == EPOS || a->movement->type & IDEMPOTENT || pos == pos_prev) break; } if (pos == EPOS) { c.range.start = start; c.range.end = start; pos = start; } else { c.range = text_range_new(start, pos); c.newpos = pos; } if (!a->op) { if (a->movement->type & CHARWISE) view_cursors_scroll_to(cursor, pos); else view_cursors_to(cursor, pos); if (vis->mode->visual) c.range = view_cursors_selection_get(cursor); if (a->movement->type & JUMP) window_jumplist_add(win, pos); else window_jumplist_invalidate(win); } else if (a->movement->type & INCLUSIVE || (linewise && a->movement->type & LINEWISE_INCLUSIVE)) { c.range.end = text_char_next(txt, c.range.end); } } else if (a->textobj) { if (vis->mode->visual) c.range = view_cursors_selection_get(cursor); else c.range.start = c.range.end = pos; for (int i = 0; i < count; i++) { Filerange r = text_range_empty(); if (a->textobj->txt) r = a->textobj->txt(txt, pos); else if (a->textobj->vis) r = a->textobj->vis(vis, txt, pos); else if (a->textobj->user) r = a->textobj->user(vis, win, a->textobj->data, pos); if (!text_range_valid(&r)) break; if (a->textobj->type & OUTER) { r.start--; r.end++; } if (a->textobj->type & SPLIT) c.range = r; else c.range = text_range_union(&c.range, &r); if (i < count - 1) pos = c.range.end + 1; } } else if (vis->mode->visual) { c.range = view_cursors_selection_get(cursor); if (!text_range_valid(&c.range)) c.range.start = c.range.end = pos; } if (linewise && vis->mode != &vis_modes[VIS_MODE_VISUAL]) c.range = text_range_linewise(txt, &c.range); if (vis->mode->visual) { view_cursors_selection_set(cursor, &c.range); if (vis->mode == &vis_modes[VIS_MODE_VISUAL] || a->textobj) view_cursors_selection_sync(cursor); } if (a->op) { size_t pos = a->op->func(vis, txt, &c); if (pos == EPOS) { view_cursors_dispose(cursor); } else if (pos <= text_size(txt)) { /* moving the cursor will affect the selection. * because we want to be able to later restore * the old selection we update it again before * leaving visual mode. */ Filerange sel = view_cursors_selection_get(cursor); view_cursors_to(cursor, pos); if (vis->mode->visual) { if (sel.start == EPOS && sel.end == EPOS) sel = c.range; else if (sel.start == EPOS) sel = text_range_new(c.range.start, sel.end); else if (sel.end == EPOS) sel = text_range_new(c.range.start, sel.start); if (vis->mode == &vis_modes[VIS_MODE_VISUAL_LINE]) sel = text_range_linewise(txt, &sel); view_cursors_selection_set(cursor, &sel); } } } } if (a->op) { /* 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]; /* operator implementations must not change the mode, * they might get called multiple times (once for every cursor) */ if (a->op == &vis_operators[VIS_OP_INSERT] || a->op == &vis_operators[VIS_OP_CHANGE]) { vis_mode_switch(vis, VIS_MODE_INSERT); } else if (a->op == &vis_operators[VIS_OP_REPLACE]) { vis_mode_switch(vis, VIS_MODE_REPLACE); } else if (a->op == &vis_operators[VIS_OP_FILTER]) { if (a->arg.s) vis_cmd(vis, a->arg.s); else vis_prompt_show(vis, ":|"); } else if (vis->mode == &vis_modes[VIS_MODE_OPERATOR_PENDING]) { mode_set(vis, vis->mode_prev); } else if (vis->mode->visual) { vis_mode_switch(vis, VIS_MODE_NORMAL); } text_snapshot(txt); vis_draw(vis); } if (a != &vis->action_prev) { if (repeatable) { if (!a->macro) a->macro = vis->macro_operator; vis->action_prev = *a; } action_reset(a); } } void action_reset(Action *a) { memset(a, 0, sizeof(*a)); a->count = VIS_COUNT_UNKNOWN; }
static const char *cursors_remove(Vis *vis, const char *keys, const Arg *arg) { View *view = vis_view(vis); view_cursors_dispose(view_cursor(view)); view_cursor_to(view, view_cursor_get(view)); return keys; }
static bool cmd_extract(Vis *vis, Win *win, Command *cmd, const char *argv[], Cursor *cur, Filerange *range) { if (!win) return false; bool ret = true; Text *txt = win->file->text; if (cmd->regex) { size_t start = range->start, end = range->end, last_start = EPOS; RegexMatch match[1]; while (start < end) { bool found = text_search_range_forward(txt, start, end - start, cmd->regex, 1, match, start > range->start ? REG_NOTBOL : 0) == 0; Filerange r = text_range_empty(); if (found) { if (argv[0][0] == 'x') r = text_range_new(match[0].start, match[0].end); else r = text_range_new(start, match[0].start); if (match[0].start == match[0].end) { if (last_start == match[0].start) { start++; continue; } /* in Plan 9's regexp library ^ matches the beginning * of a line, however in POSIX with REG_NEWLINE ^ * matches the zero-length string immediately after a * newline. Try filtering out the last such match at EOF. */ if (end == match[0].start && start > range->start) break; } start = match[0].end; } else { if (argv[0][0] == 'y') r = text_range_new(start, end); start = end; } if (text_range_valid(&r)) { Mark mark_start = text_mark_set(txt, start); Mark mark_end = text_mark_set(txt, end); ret &= sam_execute(vis, win, cmd->cmd, NULL, &r); last_start = start = text_mark_get(txt, mark_start); if (start == EPOS && last_start != r.end) last_start = start = r.end; end = text_mark_get(txt, mark_end); if (start == EPOS || end == EPOS) { ret = false; break; } } } } else { size_t start = range->start, end = range->end; while (start < end) { size_t next = text_line_next(txt, start); if (next > end) next = end; Filerange r = text_range_new(start, next); if (start == next || !text_range_valid(&r)) break; start = next; Mark mark_start = text_mark_set(txt, start); Mark mark_end = text_mark_set(txt, end); ret &= sam_execute(vis, win, cmd->cmd, NULL, &r); start = text_mark_get(txt, mark_start); if (start == EPOS) start = r.end; end = text_mark_get(txt, mark_end); if (end == EPOS) { ret = false; break; } } } view_cursors_dispose(cur); return ret; }