static size_t op_shift_left(Vis *vis, Text *txt, OperatorContext *c) { size_t pos = text_line_begin(txt, c->range.end), prev_pos; size_t tabwidth = vis->tabwidth, tablen; size_t deleted = 0; /* if range ends at the begin of a line, skip line break */ if (pos == c->range.end) pos = text_line_prev(txt, pos); do { char c; size_t len = 0; prev_pos = pos = text_line_begin(txt, pos); Iterator it = text_iterator_get(txt, pos); if (text_iterator_byte_get(&it, &c) && c == '\t') { len = 1; } else { for (len = 0; text_iterator_byte_get(&it, &c) && c == ' '; len++) text_iterator_byte_next(&it, NULL); } tablen = MIN(len, tabwidth); text_delete(txt, pos, tablen); pos = text_line_prev(txt, pos); deleted += tablen; } while (pos >= c->range.start && pos != prev_pos); return c->pos - deleted; }
static Filerange text_object_bracket(Text *txt, size_t pos, char type) { char c, open, close; int opened = 1, closed = 1; Filerange r = text_range_empty(); switch (type) { case '(': case ')': open = '('; close = ')'; break; case '{': case '}': open = '{'; close = '}'; break; case '[': case ']': open = '['; close = ']'; break; case '<': case '>': open = '<'; close = '>'; break; case '"': open = '"'; close = '"'; break; case '`': open = '`'; close = '`'; break; case '\'': open = '\''; close = '\''; break; default: return r; } Iterator it = text_iterator_get(txt, pos); if (open == close && text_iterator_byte_get(&it, &c) && (c == '"' || c == '`' || c == '\'')) { size_t match = text_bracket_match(txt, pos); r.start = MIN(pos, match) + 1; r.end = MAX(pos, match); return r; } while (text_iterator_byte_get(&it, &c)) { if (c == open && --opened == 0) { r.start = it.pos + 1; break; } else if (c == close && it.pos != pos) { opened++; } text_iterator_byte_prev(&it, NULL); } it = text_iterator_get(txt, pos); while (text_iterator_byte_get(&it, &c)) { if (c == close && --closed == 0) { r.end = it.pos; break; } else if (c == open && it.pos != pos) { closed++; } text_iterator_byte_next(&it, NULL); } if (!text_range_valid(&r)) return text_range_empty(); return r; }
size_t text_line_prev(Text *txt, size_t pos) { char c; Iterator it = text_iterator_get(txt, pos); if (!text_iterator_byte_get(&it, &c)) return pos; if (c == '\n') text_iterator_byte_prev(&it, &c); if (c == '\r') text_iterator_byte_prev(&it, &c); while (text_iterator_byte_get(&it, &c) && c != '\n') text_iterator_byte_prev(&it, NULL); if (text_iterator_byte_prev(&it, &c) && c != '\r') text_iterator_byte_next(&it, &c); return it.pos; }
Filerange text_object_longword_outer(Text *txt, size_t pos) { Filerange r; char c, prev = '0', next = '0'; Iterator it = text_iterator_get(txt, pos); if (!text_iterator_byte_get(&it, &c)) return text_range_empty(); if (text_iterator_byte_prev(&it, &prev)) text_iterator_byte_next(&it, NULL); text_iterator_byte_next(&it, &next); if (isspace(c)) { /* middle of two words, include leading white space */ r.start = text_char_next(txt, text_longword_end_prev(txt, pos)); r.end = text_char_next(txt, text_longword_end_next(txt, pos)); } else if (isspace(prev) && isspace(next)) { /* on a single character */ r.start = pos; r.end = text_longword_start_next(txt, pos); } else if (isspace(prev)) { /* at start of a word */ r.start = pos; r.end = text_longword_start_next(txt, text_longword_end_next(txt, pos)); } else if (isspace(next)) { /* at end of a word */ r.start = text_longword_start_prev(txt, pos); r.end = text_longword_start_next(txt, pos); } else { /* in the middle of a word */ r.start = text_longword_start_prev(txt, pos); r.end = text_longword_start_next(txt, text_longword_end_next(txt, pos)); } return r; }
size_t text_line_end(Text *txt, size_t pos) { char c; Iterator it = text_iterator_get(txt, pos); while (text_iterator_byte_get(&it, &c) && c != '\r' && c != '\n') text_iterator_byte_next(&it, NULL); return it.pos; }
size_t text_line_finish(Text *txt, size_t pos) { char c; Iterator it = text_iterator_get(txt, text_line_end(txt, pos)); do text_iterator_char_prev(&it, NULL); while (text_iterator_byte_get(&it, &c) && c != '\n' && space(c)); return it.pos; }
size_t text_line_start(Text *txt, size_t pos) { char c; Iterator it = text_iterator_get(txt, text_line_begin(txt, pos)); while (text_iterator_byte_get(&it, &c) && c != '\n' && space(c)) text_iterator_byte_next(&it, NULL); return it.pos; }
size_t text_paragraph_next(Text *txt, size_t pos) { char c; Iterator it = text_iterator_get(txt, pos); while (text_iterator_byte_get(&it, &c) && (c == '\n' || c == '\r')) text_iterator_byte_next(&it, NULL); return text_line_empty_next(txt, it.pos); }
size_t text_line_char_set(Text *txt, size_t pos, int count) { char c; size_t bol = text_line_begin(txt, pos); Iterator it = text_iterator_get(txt, bol); while (count-- > 0 && text_iterator_byte_get(&it, &c) && c != '\r' && c != '\n') text_iterator_char_next(&it, NULL); return it.pos; }
size_t text_line_offset(Text *txt, size_t pos, size_t off) { char c; size_t bol = text_line_begin(txt, pos); Iterator it = text_iterator_get(txt, bol); while (off-- > 0 && text_iterator_byte_get(&it, &c) && c != '\r' && c != '\n') text_iterator_byte_next(&it, NULL); return it.pos; }
size_t text_line_char_next(Text *txt, size_t pos) { char c; Iterator it = text_iterator_get(txt, pos); if (!text_iterator_byte_get(&it, &c) || c == '\r' || c == '\n') return pos; if (!text_iterator_char_next(&it, &c) || c == '\r' || c == '\n') return pos; return it.pos; }
size_t text_line_begin(Text *txt, size_t pos) { char c; Iterator it = text_iterator_get(txt, pos); if (!text_iterator_byte_get(&it, &c)) return pos; if (c == '\n') text_iterator_byte_prev(&it, &c); if (c == '\r') text_iterator_byte_prev(&it, &c); while (text_iterator_byte_get(&it, &c)) { if (c == '\n') { it.pos++; break; } text_iterator_byte_prev(&it, NULL); } return it.pos; }
size_t text_paragraph_prev(Text *txt, size_t pos) { char c; Iterator it = text_iterator_get(txt, pos); /* c == \0 catches starting the search at EOF */ while (text_iterator_byte_get(&it, &c) && (c == '\n' || c == '\r' || c == '\0')) text_iterator_byte_prev(&it, NULL); return text_line_empty_prev(txt, it.pos); }
size_t text_sentence_prev(Text *txt, size_t pos) { char c, prev = 'X'; bool content = false; Iterator it = text_iterator_get(txt, pos); while (it.pos != 0 && text_iterator_byte_prev(&it, &c)) { if (content && space(prev) && (c == '.' || c == '?' || c == '!')) { do text_iterator_byte_next(&it, NULL); while (text_iterator_byte_get(&it, &c) && space(c)); return it.pos; } content |= !space(c); prev = c; } /* The loop only ends on hitting BOF or error */ if (content) /* starting pos was after first sentence in file => find that sentences start */ while (text_iterator_byte_get(&it, &c) && space(c)) text_iterator_byte_next(&it, NULL); return it.pos; }
void vis_replace(Vis *vis, size_t pos, const char *data, size_t len) { Text *txt = vis->win->file->text; Iterator it = text_iterator_get(txt, pos); int chars = text_char_count(data, len); for (char c; chars-- > 0 && text_iterator_byte_get(&it, &c) && c != '\r' && c != '\n'; ) text_iterator_char_next(&it, NULL); text_delete(txt, pos, it.pos - pos); vis_insert(vis, pos, data, len); }
int text_line_char_get(Text *txt, size_t pos) { char c; int count = 0; size_t bol = text_line_begin(txt, pos); Iterator it = text_iterator_get(txt, bol); while (text_iterator_byte_get(&it, &c) && it.pos < pos && c != '\r' && c != '\n') { text_iterator_char_next(&it, NULL); count++; } return count; }
Filerange text_range_inner(Text *txt, Filerange *rin) { char c; Filerange r = *rin; Iterator it = text_iterator_get(txt, rin->start); while (text_iterator_byte_get(&it, &c) && isspace((unsigned char)c)) text_iterator_byte_next(&it, NULL); r.start = it.pos; it = text_iterator_get(txt, rin->end); do r.end = it.pos; while (text_iterator_byte_prev(&it, &c) && isspace((unsigned char)c)); return r; }
size_t text_customword_end_prev(Text *txt, size_t pos, int (*isboundary)(int)) { char c; Iterator it = text_iterator_get(txt, pos); if (!text_iterator_byte_get(&it, &c)) return pos; if (boundary(c)) while (boundary(c) && !space(c) && text_iterator_char_prev(&it, &c)); else while (!boundary(c) && text_iterator_char_prev(&it, &c)); while (space(c) && text_iterator_char_prev(&it, &c)); return it.pos; }
Filerange text_object_entire_inner(Text *txt, size_t pos) { char c; Filerange r = text_object_entire(txt, pos); Iterator it = text_iterator_get(txt, r.start); while (text_iterator_byte_get(&it, &c) && (c == '\r' || c == '\n')) text_iterator_byte_next(&it, NULL); r.start = it.pos; it = text_iterator_get(txt, r.end); while (text_iterator_byte_prev(&it, &c) && (c == '\r' || c == '\n')); r.end = it.pos; return text_range_linewise(txt, &r); }
size_t text_sentence_next(Text *txt, size_t pos) { char c, prev = 'X'; Iterator it = text_iterator_get(txt, pos), rev = text_iterator_get(txt, pos); if (!text_iterator_byte_get(&it, &c)) return pos; while (text_iterator_byte_get(&rev, &prev) && space(prev)) text_iterator_byte_prev(&rev, NULL); prev = rev.pos == 0 ? '.' : prev; /* simulate punctuation at BOF */ do { if ((prev == '.' || prev == '?' || prev == '!') && space(c)) { do text_iterator_byte_next(&it, NULL); while (text_iterator_byte_get(&it, &c) && space(c)); return it.pos; } prev = c; } while (text_iterator_byte_next(&it, &c)); return it.pos; }
Filerange text_object_range(Text *txt, size_t pos, int (*isboundary)(int)) { char c; size_t start; Iterator it = text_iterator_get(txt, pos), rit = it; if (!text_iterator_byte_get(&rit, &c) || boundary(c)) return text_range_empty(); char tmp = c; do start = rit.pos; while (text_iterator_char_prev(&rit, &c) && !boundary(c)); for (c = tmp; !boundary(c) && text_iterator_byte_next(&it, &c);); return text_range_new(start, it.pos); }
size_t text_line_empty_next(Text *txt, size_t pos) { char c; Iterator it = text_iterator_get(txt, pos); while (text_iterator_byte_get(&it, &c)) { if (c == '\n' && text_iterator_byte_next(&it, &c)) { size_t match = it.pos; if (c == '\r') text_iterator_byte_next(&it, &c); if (c == '\n') return match; } text_iterator_byte_next(&it, NULL); } return it.pos; }
static size_t find_next(Text *txt, size_t pos, const char *s, bool line) { if (!s) return pos; size_t len = strlen(s), matched = 0; Iterator it = text_iterator_get(txt, pos), sit; for (char c; matched < len && text_iterator_byte_get(&it, &c); ) { if (c == s[matched]) { if (matched == 0) sit = it; matched++; } else if (matched > 0) { it = sit; matched = 0; } text_iterator_byte_next(&it, NULL); if (line && c == '\n') break; } return matched == len ? it.pos - len : pos; }
static Filerange text_object_customword(Text *txt, size_t pos, int (*isboundary)(int)) { Filerange r; char c, prev = '0', next = '0'; Iterator it = text_iterator_get(txt, pos); if (!text_iterator_byte_get(&it, &c)) return text_range_empty(); if (text_iterator_byte_prev(&it, &prev)) text_iterator_byte_next(&it, NULL); text_iterator_byte_next(&it, &next); if (space(c)) { r.start = text_char_next(txt, text_customword_end_prev(txt, pos, isboundary)); r.end = text_customword_start_next(txt, pos, isboundary); } else if (boundary(prev) && boundary(next)) { if (boundary(c)) { r.start = text_char_next(txt, text_customword_end_prev(txt, pos, isboundary)); r.end = text_char_next(txt, text_customword_end_next(txt, pos, isboundary)); } else { /* on a single character */ r.start = pos; r.end = text_char_next(txt, pos); } } else if (boundary(prev)) { /* at start of a word */ r.start = pos; r.end = text_char_next(txt, text_customword_end_next(txt, pos, isboundary)); } else if (boundary(next)) { /* at end of a word */ r.start = text_customword_start_prev(txt, pos, isboundary); r.end = text_char_next(txt, pos); } else { /* in the middle of a word */ r.start = text_customword_start_prev(txt, pos, isboundary); r.end = text_char_next(txt, text_customword_end_next(txt, pos, isboundary)); } return r; }
size_t text_bracket_match_symbol(Text *txt, size_t pos, const char *symbols) { int direction, count = 1; char search, current, c; bool instring = false; Iterator it = text_iterator_get(txt, pos); if (!text_iterator_byte_get(&it, ¤t)) return pos; if (symbols && !memchr(symbols, current, strlen(symbols))) return pos; switch (current) { case '(': search = ')'; direction = 1; break; case ')': search = '('; direction = -1; break; case '{': search = '}'; direction = 1; break; case '}': search = '{'; direction = -1; break; case '[': search = ']'; direction = 1; break; case ']': search = '['; direction = -1; break; case '<': search = '>'; direction = 1; break; case '>': search = '<'; direction = -1; break; case '"': case '`': case '\'': { char special[] = " \n)}]>.,:;"; search = current; direction = 1; if (text_iterator_byte_next(&it, &c)) { /* if a single or double quote is followed by * a special character, search backwards */ if (memchr(special, c, sizeof(special))) direction = -1; text_iterator_byte_prev(&it, NULL); } break; } default: return pos; } if (direction >= 0) { /* forward search */ while (text_iterator_byte_next(&it, &c)) { if (c != current && c == '"') instring = !instring; if (!instring) { if (c == search && --count == 0) return it.pos; else if (c == current) count++; } } } else { /* backwards */ while (text_iterator_byte_prev(&it, &c)) { if (c != current && c == '"') instring = !instring; if (!instring) { if (c == search && --count == 0) return it.pos; else if (c == current) count++; } } } return pos; /* no match found */ }