Filerange text_object_indentation(Text *txt, size_t pos) { char c; size_t bol = text_line_begin(txt, pos); size_t sol = text_line_start(txt, bol); size_t start = bol; size_t end = text_line_next(txt, bol); size_t line_indent = sol - bol; bool line_empty = text_byte_get(txt, bol, &c) && (c == '\r' || c == '\n'); char *buf = text_bytes_alloc0(txt, bol, line_indent); char *tmp = malloc(line_indent); if (!buf || !tmp) { free(buf); free(tmp); return text_range_empty(); } while ((bol = text_line_begin(txt, text_line_prev(txt, start))) != start) { sol = text_line_start(txt, bol); size_t indent = sol - bol; if (indent < line_indent) break; bool empty = text_byte_get(txt, bol, &c) && (c == '\r' || c == '\n'); if (line_empty && !empty) break; if (line_indent == 0 && empty) break; text_bytes_get(txt, bol, line_indent, tmp); if (memcmp(buf, tmp, line_indent)) break; start = bol; } do { bol = end; sol = text_line_start(txt, bol); size_t indent = sol - bol; if (indent < line_indent) break; bool empty = text_byte_get(txt, bol, &c) && (c == '\r' || c == '\n'); if (line_empty && !empty) break; if (line_indent == 0 && empty) break; text_bytes_get(txt, bol, line_indent, tmp); if (memcmp(buf, tmp, line_indent)) break; end = text_line_next(txt, bol); } while (bol != end); free(buf); free(tmp); return text_range_new(start, end); }
static size_t op_put(Vis *vis, Text *txt, OperatorContext *c) { size_t pos = c->pos; bool sel = text_range_size(&c->range) > 0; bool sel_linewise = sel && text_range_is_linewise(txt, &c->range); if (sel) { text_delete_range(txt, &c->range); pos = c->pos = c->range.start; } switch (c->arg->i) { case VIS_OP_PUT_AFTER: case VIS_OP_PUT_AFTER_END: if (c->reg->linewise && !sel_linewise) pos = text_line_next(txt, pos); else if (!sel) pos = text_char_next(txt, pos); break; case VIS_OP_PUT_BEFORE: case VIS_OP_PUT_BEFORE_END: if (c->reg->linewise) pos = text_line_begin(txt, pos); break; } size_t len; const char *data = register_get(vis, c->reg, &len); for (int i = 0; i < c->count; i++) { text_insert(txt, pos, data, len); pos += len; } if (c->reg->linewise) { switch (c->arg->i) { case VIS_OP_PUT_BEFORE_END: case VIS_OP_PUT_AFTER_END: pos = text_line_start(txt, pos); break; case VIS_OP_PUT_AFTER: pos = text_line_start(txt, text_line_next(txt, c->pos)); break; case VIS_OP_PUT_BEFORE: pos = text_line_start(txt, c->pos); break; } } else { switch (c->arg->i) { case VIS_OP_PUT_AFTER: case VIS_OP_PUT_BEFORE: pos = text_char_prev(txt, pos); break; } } return pos; }
Filerange text_range_linewise(Text *txt, Filerange *rin) { Filerange rout = *rin; rout.start = text_line_begin(txt, rin->start); if (rin->end != text_line_begin(txt, rin->end)) rout.end = text_line_next(txt, rin->end); return rout; }
static bool cmd_select(Vis *vis, Win *win, Command *cmd, const char *argv[], Cursor *cur, Filerange *range) { Filerange sel = text_range_empty(); if (!win) return sam_execute(vis, NULL, cmd->cmd, NULL, &sel); bool ret = true; View *view = win->view; Text *txt = win->file->text; bool multiple_cursors = view_cursors_multiple(view); Cursor *primary = view_cursors_primary_get(view); for (Cursor *c = view_cursors(view), *next; c && ret; c = next) { next = view_cursors_next(c); size_t pos = view_cursors_pos(c); if (vis->mode->visual) { sel = view_cursors_selection_get(c); } else if (cmd->cmd->address) { /* convert a single line range to a goto line motion */ if (!multiple_cursors && cmd->cmd->cmddef->func == cmd_print) { Address *addr = cmd->cmd->address; switch (addr->type) { case '+': case '-': addr = addr->right; /* fall through */ case 'l': if (addr && addr->type == 'l' && !addr->right) addr->type = 'g'; break; } } sel = text_range_new(pos, pos); } else if (cmd->cmd->cmddef->flags & CMD_ADDRESS_POS) { sel = text_range_new(pos, pos); } else if (cmd->cmd->cmddef->flags & CMD_ADDRESS_LINE) { sel = text_object_line(txt, pos); } else if (cmd->cmd->cmddef->flags & CMD_ADDRESS_AFTER) { size_t next_line = text_line_next(txt, pos); sel = text_range_new(next_line, next_line); } else if (multiple_cursors) { sel = text_object_line(txt, pos); } else { sel = text_range_new(0, text_size(txt)); } if (text_range_valid(&sel)) ret &= sam_execute(vis, win, cmd->cmd, c, &sel); if (cmd->cmd->cmddef->flags & CMD_ONCE) break; } if (vis->win && vis->win->view == view && primary != view_cursors_primary_get(view)) view_cursors_primary_set(view_cursors(view)); return ret; }
size_t text_function_start_prev(Text *txt, size_t pos) { char c; size_t apos = text_byte_get(txt, pos, &c) && c == '}' && pos > 0 ? pos - 1 : pos; size_t a = text_function_end_next(txt, apos); size_t b = text_function_end_prev(txt, pos); if (a != apos) { size_t match = text_bracket_match(txt, a); a = match != a ? text_line_next(txt, text_line_empty_prev(txt, match)) : pos; } if (b != pos) { size_t match = text_bracket_match(txt, b); b = match != b ? text_line_next(txt, text_line_empty_prev(txt, match)) : pos; } if (a >= pos && b >= pos) return pos; else if (a >= pos) return b; else if (b >= pos) return a; else return MAX(a, b); }
size_t text_function_start_next(Text *txt, size_t pos) { size_t a = text_function_end_next(txt, pos); size_t b = a; char c; if (a != pos) { Iterator it = text_iterator_get(txt, a); while (text_iterator_byte_next(&it, &c) && (c == '\r' || c == '\n')); a = it.pos; } if (b != pos) { size_t match = text_bracket_match(txt, b); b = match != b ? text_line_next(txt, text_line_empty_prev(txt, match)) : pos; } if (a <= pos && b <= pos) return pos; else if (a <= pos) return b; else if (b <= pos) return a; else return MIN(a, b); }
static Filerange address_line_evaluate(Address *addr, File *file, Filerange *range, int sign) { Text *txt = file->text; size_t offset = addr->number != 0 ? addr->number : 1; size_t start = range->start, end = range->end, line; if (sign > 0) { char c; if (end > 0 && text_byte_get(txt, end-1, &c) && c == '\n') end--; line = text_lineno_by_pos(txt, end); line = text_pos_by_lineno(txt, line + offset); } else if (sign < 0) { line = text_lineno_by_pos(txt, start); line = offset < line ? text_pos_by_lineno(txt, line - offset) : 0; } else { line = text_pos_by_lineno(txt, addr->number); } if (addr->type == 'g') return text_range_new(line, line); else return text_range_new(line, text_line_next(txt, line)); }
Filerange text_object_line(Text *txt, size_t pos) { Filerange r; r.start = text_line_begin(txt, pos); r.end = text_line_next(txt, pos); return r; }
size_t text_range_line_next(Text *txt, Filerange *r, size_t pos) { if (!text_range_contains(r, pos)) return EPOS; size_t newpos = text_line_next(txt, pos); return newpos != pos && newpos < r->end ? newpos : EPOS; }
size_t text_line_down(Text *txt, size_t pos) { int width = text_line_width_get(txt, pos); size_t next = text_line_next(txt, pos); return text_line_width_set(txt, next, width); }
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; }