#include "editor/decl.h" #include "io/knot.h" #include "io/sysio.h" #include "utils/utils.h" struct GraphemeIterator { char *line{nullptr}; uint32_t len{0}; GraphemeIterator(char *line, uint32_t len) : line(line), len(len) {} char *next(uint32_t *o_len, uint32_t *o_width) { if (!line || len == 0) return nullptr; char *cur = line; uint32_t g = grapheme_next_character_break_utf8(cur, len); if (o_width) *o_width = display_width(cur, g); if (o_len) *o_len = g; line += g; len -= g; return cur; } }; struct VisualIterator { LineIterator *it{nullptr}; uint32_t line_index{UINT32_MAX}; uint32_t offset{0}; uint32_t len{0}; bool first{true}; char *line{nullptr}; uint32_t render_width{io::cols}; std::stack> prev_stack; VisualIterator(Knot *root, Coord pos, uint32_t width) : it(begin_l_iter(root, pos.row)), line_index(pos.row - 1), offset(pos.col), render_width(width) {} std::pair prev() { if (!it) return {{UINT32_MAX, UINT32_MAX}, {UINT32_MAX, UINT32_MAX}}; if (prev_stack.empty()) return _prev(); auto ret = prev_stack.top(); offset = ret.first.col; prev_stack.pop(); return ret; } std::pair _prev() { if (!it) return {{UINT32_MAX, UINT32_MAX}, {UINT32_MAX, UINT32_MAX}}; line_index--; line = prev_line(it, &len); if (!line) return {{UINT32_MAX, UINT32_MAX}, {UINT32_MAX, UINT32_MAX}}; if (len > 0 && line[len - 1] == '\n') len--; offset = first ? offset : 0; first = false; GraphemeIterator g(line + offset, len - offset); uint32_t o_len, o_width, rendered = 0, advance = 0; while (g.next(&o_len, &o_width)) { if (rendered + o_width > render_width) { prev_stack.push({{line_index, offset}, {line_index, offset + advance}}); offset += advance; rendered = 0; advance = 0; } advance += o_len; rendered += o_width; } return {{line_index, offset}, {line_index, offset + advance}}; } std::pair next() { if (!it) return {{UINT32_MAX, UINT32_MAX}, {UINT32_MAX, UINT32_MAX}}; if (!line) { line_index++; line = next_line(it, &len); if (!line) return {{UINT32_MAX, UINT32_MAX}, {UINT32_MAX, UINT32_MAX}}; if (len > 0 && line[len - 1] == '\n') len--; offset = first ? offset : 0; first = false; } GraphemeIterator g(line + offset, len - offset); uint32_t o_len, o_width, rendered = 0, advance = 0; while (g.next(&o_len, &o_width)) { if (rendered + o_width > render_width) { offset += advance; return {{line_index, offset - advance}, {line_index, offset}}; } advance += o_len; rendered += o_width; } offset += advance; if (offset >= len) line = nullptr; return {{line_index, offset}, {line_index, offset + advance}}; } ~VisualIterator() { if (!it) return; free(it->buffer); free(it); } };