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); }
bool register_put(Register *reg, Text *txt, Filerange *range) { size_t len = range->end - range->start; if (!buffer_alloc((Buffer*)reg, len)) return false; reg->len = text_bytes_get(txt, range->start, len, reg->data); return true; }
static const char *cursors_select_next(Vis *vis, const char *keys, const Arg *arg) { Text *txt = vis_text(vis); View *view = vis_view(vis); Cursor *cursor = view_cursor(view); Filerange sel = view_cursors_selection_get(cursor); if (!text_range_valid(&sel)) return keys; size_t len = text_range_size(&sel); char *buf = malloc(len+1); if (!buf) return keys; len = text_bytes_get(txt, sel.start, len, buf); buf[len] = '\0'; Filerange word = text_object_word_find_next(txt, sel.end, buf); free(buf); if (text_range_valid(&word)) { cursor = view_cursors_new(view); if (!cursor) return keys; view_cursors_selection_set(cursor, &word); view_cursors_to(cursor, text_char_prev(txt, word.end)); } return keys; }
bool register_append(Register *reg, Text *txt, Filerange *range) { size_t rem = reg->size - reg->len; size_t len = range->end - range->start; if (len > rem && !buffer_alloc((Buffer*)reg, reg->size + len - rem)) return false; reg->len += text_bytes_get(txt, range->start, len, reg->data + reg->len); return true; }
static size_t text_function_end_direction(Text *txt, size_t pos, int direction) { size_t start = pos, match; if (direction < 0 && pos > 0) pos--; for (;;) { char c[3]; if (direction > 0) match = text_find_next(txt, pos, "\n}"); else match = text_find_prev(txt, pos, "\n}"); if (text_bytes_get(txt, match, sizeof c, c) != 3 || c[0] != '\n' || c[1] != '}') break; if (c[2] == '\r' || c[2] == '\n') return match+1; if (match == pos) match += direction; pos = match; } return start; }
static size_t op_case_change(Vis *vis, Text *txt, OperatorContext *c) { size_t len = text_range_size(&c->range); char *buf = malloc(len); if (!buf) return c->pos; len = text_bytes_get(txt, c->range.start, len, buf); size_t rem = len; for (char *cur = buf; rem > 0; cur++, rem--) { int ch = (unsigned char)*cur; if (isascii(ch)) { if (c->arg->i == VIS_OP_CASE_SWAP) *cur = islower(ch) ? toupper(ch) : tolower(ch); else if (c->arg->i == VIS_OP_CASE_UPPER) *cur = toupper(ch); else *cur = tolower(ch); } } text_delete(txt, c->range.start, len); text_insert(txt, c->range.start, buf, len); free(buf); return c->pos; }
size_t text_line_width_set(Text *txt, size_t pos, int width) { int cur_width = 0; mbstate_t ps = { 0 }; size_t bol = text_line_begin(txt, pos); Iterator it = text_iterator_get(txt, bol); for (;;) { char buf[MB_CUR_MAX]; size_t len = text_bytes_get(txt, it.pos, sizeof buf, buf); if (len == 0 || buf[0] == '\r' || buf[0] == '\n') break; wchar_t wc; size_t wclen = mbrtowc(&wc, buf, len, &ps); if (wclen == (size_t)-1 && errno == EILSEQ) { /* assume a replacement symbol will be displayed */ cur_width++; } else if (wclen == (size_t)-2) { /* do nothing, advance to next character */ } else if (wclen == 0) { /* assume NUL byte will be displayed as ^@ */ cur_width += 2; } else if (buf[0] == '\t') { cur_width++; } else { int w = wcwidth(wc); if (w == -1) w = 2; /* assume non-printable will be displayed as ^{char} */ cur_width += w; } if (cur_width >= width || !text_iterator_codepoint_next(&it, NULL)) break; } return it.pos; }
/* redraw the complete with data starting from view->start bytes into the file. * stop once the screen is full, update view->end, view->lastline */ void view_draw(View *view) { view_clear(view); /* current absolute file position */ size_t pos = view->start; /* number of bytes to read in one go */ size_t text_len = view->width * view->height; /* current buffer to work with */ char text[text_len+1]; /* remaining bytes to process in buffer*/ size_t rem = text_bytes_get(view->text, pos, text_len, text); /* NUL terminate because regex(3) function expect it */ text[rem] = '\0'; /* current position into buffer from which to interpret a character */ char *cur = text; /* current selection */ Filerange sel = view_selection_get(view); /* syntax definition to use */ Syntax *syntax = view->syntax; /* matched tokens for each syntax rule */ regmatch_t match[syntax ? LENGTH(syntax->rules) : 1][1], *matched = NULL; memset(match, 0, sizeof match); /* default and current curses attributes to use */ int default_attrs = COLOR_PAIR(0) | A_NORMAL, attrs = default_attrs; while (rem > 0) { /* current 'parsed' character' */ wchar_t wchar; Cell cell; memset(&cell, 0, sizeof cell); if (syntax) { if (matched && cur >= text + matched->rm_eo) { /* end of current match */ matched = NULL; attrs = default_attrs; for (int i = 0; i < LENGTH(syntax->rules); i++) { if (match[i][0].rm_so == -1) continue; /* no match on whole text */ /* reset matches which overlap with matched */ if (text + match[i][0].rm_so <= cur && cur < text + match[i][0].rm_eo) { match[i][0].rm_so = 0; match[i][0].rm_eo = 0; } } } if (!matched) { size_t off = cur - text; /* number of already processed bytes */ for (int i = 0; i < LENGTH(syntax->rules); i++) { SyntaxRule *rule = &syntax->rules[i]; if (!rule->rule) break; if (match[i][0].rm_so == -1) continue; /* no match on whole text */ if (off >= (size_t)match[i][0].rm_eo) { /* past match, continue search from current position */ if (regexec(&rule->regex, cur, 1, match[i], 0) || match[i][0].rm_so == match[i][0].rm_eo) { match[i][0].rm_so = -1; match[i][0].rm_eo = -1; continue; } match[i][0].rm_so += off; match[i][0].rm_eo += off; } if (text + match[i][0].rm_so <= cur && cur < text + match[i][0].rm_eo) { /* within matched expression */ matched = &match[i][0]; attrs = rule->color->attr; break; /* first match views */ } } } } size_t len = mbrtowc(&wchar, cur, rem, NULL); if (len == (size_t)-1 && errno == EILSEQ) { /* ok, we encountered an invalid multibyte sequence, * replace it with the Unicode Replacement Character * (FFFD) and skip until the start of the next utf8 char */ for (len = 1; rem > len && !ISUTF8(cur[len]); len++); cell = (Cell){ .data = "\xEF\xBF\xBD", .len = len, .width = 1, .istab = false }; } else if (len == (size_t)-2) { /* not enough bytes available to convert to a * wide character. advance file position and read * another junk into buffer. */ rem = text_bytes_get(view->text, pos, text_len, text); text[rem] = '\0'; cur = text; continue; } else if (len == 0) { /* NUL byte encountered, store it and continue */ cell = (Cell){ .data = "\x00", .len = 1, .width = 0, .istab = false }; } else { for (size_t i = 0; i < len; i++) cell.data[i] = cur[i]; cell.data[len] = '\0'; cell.istab = false; cell.len = len; cell.width = wcwidth(wchar); if (cell.width == -1) cell.width = 1; } if (cur[0] == '\r' && rem > 1 && cur[1] == '\n') { /* convert views style newline \r\n into a single char with len = 2 */ cell = (Cell){ .data = "\n", .len = 2, .width = 1, .istab = false }; } cell.attr = attrs; if (sel.start <= pos && pos < sel.end) cell.attr |= A_REVERSE; if (!view_addch(view, &cell)) break; rem -= cell.len; cur += cell.len; pos += cell.len; }