size_t text_line_empty_prev(Text *txt, size_t pos) { char c; Iterator it = text_iterator_get(txt, pos); while (text_iterator_byte_prev(&it, &c)) { if (c == '\n' && text_iterator_byte_prev(&it, &c)) { if (c == '\r') text_iterator_byte_prev(&it, &c); if (c == '\n') return it.pos + 1; } } return it.pos; }
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_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_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; }
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; }
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); }
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_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; }
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; }
static size_t find_prev(Text *txt, size_t pos, const char *s, bool line) { if (!s) return pos; size_t len = strlen(s), matched = len - 1; Iterator it = text_iterator_get(txt, pos), sit; if (len == 0) return pos; for (char c; text_iterator_byte_prev(&it, &c); ) { if (c == s[matched]) { if (matched == 0) return it.pos; if (matched == len - 1) sit = it; matched--; } else if (matched < len - 1) { it = sit; matched = len - 1; } if (line && c == '\n') break; } return 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 */ }