diff --git a/src/client_manager.cc b/src/client_manager.cc index 10bbbc69..c4dbda6f 100644 --- a/src/client_manager.cc +++ b/src/client_manager.cc @@ -187,11 +187,11 @@ static DisplayLine generate_status_line(Client& client) { auto& context = client.context(); auto pos = context.editor().main_selection().last(); - auto col = utf8::distance(context.buffer().iterator_at_line_begin(pos), pos); + auto col = context.buffer().char_distance({pos.line, 0}, pos); std::ostringstream oss; oss << context.buffer().display_name() - << " " << (int)pos.line()+1 << ":" << (int)col+1; + << " " << (int)pos.line+1 << ":" << (int)col+1; if (context.buffer().is_modified()) oss << " [+]"; if (context.input_handler().is_recording()) diff --git a/src/dynamic_selection_list.cc b/src/dynamic_selection_list.cc index 3de8c80c..dfc78436 100644 --- a/src/dynamic_selection_list.cc +++ b/src/dynamic_selection_list.cc @@ -30,8 +30,8 @@ void DynamicSelectionList::check_invariant() const kak_assert(buffer.is_valid(sel.last())); kak_assert(not buffer.is_end(sel.first())); kak_assert(not buffer.is_end(sel.last())); - kak_assert(utf8::is_character_start(sel.first())); - kak_assert(utf8::is_character_start(sel.last())); + kak_assert(utf8::is_character_start(buffer.iterator_at(sel.first()))); + kak_assert(utf8::is_character_start(buffer.iterator_at(sel.last()))); } #endif } diff --git a/src/editor.cc b/src/editor.cc index 0de58d5e..fa569d00 100644 --- a/src/editor.cc +++ b/src/editor.cc @@ -20,17 +20,17 @@ Editor::Editor(Buffer& buffer) m_main_sel = 0; } -static void avoid_eol(BufferIterator& it) +static void avoid_eol(const Buffer& buffer, BufferCoord& coord) { - const auto column = it.column(); - if (column != 0 and column == it.buffer().line_length(it.line()) - 1) - it = utf8::previous(it); + const auto column = coord.column; + if (column != 0 and column == buffer.line_length(coord.line) - 1) + coord = buffer.char_prev(coord); } -static void avoid_eol(Selection& sel) +static void avoid_eol(const Buffer& buffer, Range& sel) { - avoid_eol(sel.first()); - avoid_eol(sel.last()); + avoid_eol(buffer, sel.first()); + avoid_eol(buffer, sel.last()); } void Editor::erase() @@ -38,13 +38,13 @@ void Editor::erase() scoped_edition edition(*this); for (auto& sel : m_selections) { - m_buffer->erase(sel.min(), utf8::next(sel.max())); - avoid_eol(sel); + Kakoune::erase(*m_buffer, sel); + avoid_eol(*m_buffer, sel); } } -static BufferIterator prepare_insert(Buffer& buffer, const Selection& sel, - InsertMode mode) +static BufferCoord prepare_insert(Buffer& buffer, const Selection& sel, + InsertMode mode) { switch (mode) { @@ -52,37 +52,37 @@ static BufferIterator prepare_insert(Buffer& buffer, const Selection& sel, return sel.min(); case InsertMode::Replace: { - BufferIterator pos = sel.min(); - buffer.erase(sel.min(), utf8::next(sel.max())); + BufferCoord pos = sel.min(); + Kakoune::erase(buffer, sel); return pos; } case InsertMode::Append: { // special case for end of lines, append to current line instead auto pos = std::max(sel.first(), sel.last()); - if (pos.column() == buffer.line_length(pos.line()) - 1) + if (pos.column == buffer.line_length(pos.line) - 1) return pos; else - return utf8::next(pos); + return buffer.char_next(pos); } case InsertMode::InsertAtLineBegin: - return buffer.iterator_at_line_begin(sel.min()); + return sel.min().line; case InsertMode::AppendAtLineEnd: - return buffer.iterator_at_line_end(sel.max())-1; + return buffer.char_prev(sel.max().line+1); case InsertMode::InsertAtNextLineBegin: - return buffer.iterator_at_line_end(sel.max()); + return sel.max().line+1; case InsertMode::OpenLineBelow: case InsertMode::OpenLineAbove: { auto line = mode == InsertMode::OpenLineAbove ? - sel.min().line() : sel.max().line() + 1; + sel.min().line : sel.max().line + 1; buffer.insert(line, "\n"); - return {buffer, line}; + return line; } } kak_assert(false); - return BufferIterator{}; + return {}; } void Editor::insert(const String& str, InsertMode mode) @@ -91,14 +91,15 @@ void Editor::insert(const String& str, InsertMode mode) for (auto& sel : m_selections) { - BufferIterator pos = prepare_insert(*m_buffer, sel, mode); + BufferCoord pos = prepare_insert(*m_buffer, sel, mode); m_buffer->insert(pos, str); - if (mode == InsertMode::Replace and not pos.is_end()) + if (mode == InsertMode::Replace and not m_buffer->is_end(pos)) { sel.first() = pos; - sel.last() = str.empty() ? pos : utf8::character_start(pos + str.length() - 1); + sel.last() = str.empty() ? + pos : m_buffer->char_advance(pos, str.char_length() - 1); } - avoid_eol(sel); + avoid_eol(*m_buffer, sel); } check_invariant(); } @@ -112,15 +113,16 @@ void Editor::insert(const memoryview& strings, InsertMode mode) for (size_t i = 0; i < selections().size(); ++i) { auto& sel = m_selections[i]; - BufferIterator pos = prepare_insert(*m_buffer, sel, mode); + BufferCoord pos = prepare_insert(*m_buffer, sel, mode); const String& str = strings[std::min(i, strings.size()-1)]; m_buffer->insert(pos, str); - if (mode == InsertMode::Replace and not pos.is_end()) + if (mode == InsertMode::Replace and not m_buffer->is_end(pos)) { sel.first() = pos; - sel.last() = str.empty() ? pos : utf8::character_start(pos + str.length() - 1); + sel.last() = str.empty() ? + pos : m_buffer->char_advance(pos, str.char_length() - 1); } - avoid_eol(sel); + avoid_eol(*m_buffer, sel); } check_invariant(); } @@ -129,7 +131,7 @@ std::vector Editor::selections_content() const { std::vector contents; for (auto& sel : m_selections) - contents.push_back(m_buffer->string(sel.min(), utf8::next(sel.max()))); + contents.push_back(m_buffer->string(sel.min(), m_buffer->char_next(sel.max()))); return contents; } @@ -183,12 +185,11 @@ void Editor::move_selections(CharCount offset, SelectMode mode) for (auto& sel : m_selections) { auto last = sel.last(); - auto limit = offset < 0 ? buffer().iterator_at_line_begin(last) - : utf8::previous(buffer().iterator_at_line_end(last)); - last = utf8::advance(last, limit, offset); + last = clamp(m_buffer->char_advance(last, offset), + last.line, m_buffer->char_prev(last.line+1)); sel.first() = mode == SelectMode::Extend ? sel.first() : last; sel.last() = last; - avoid_eol(sel); + avoid_eol(*m_buffer, sel); } sort_and_merge_overlapping(m_selections, m_main_sel); } @@ -198,14 +199,14 @@ void Editor::move_selections(LineCount offset, SelectMode mode) kak_assert(mode == SelectMode::Replace or mode == SelectMode::Extend); for (auto& sel : m_selections) { - BufferCoord pos = sel.last().coord(); - CharCount column = utf8::distance(m_buffer->iterator_at_line_begin(pos.line), sel.last()); + auto pos = sel.last(); + CharCount column = m_buffer->char_distance(pos.line, pos); pos.line += offset; - BufferIterator last = utf8::advance(m_buffer->iterator_at_line_begin(pos.line), - m_buffer->iterator_at_line_end(pos.line)-1, column); + auto last = std::min(m_buffer->char_advance(pos.line, column), + m_buffer->char_prev(pos.line+1)); sel.first() = mode == SelectMode::Extend ? sel.first() : last; sel.last() = last; - avoid_eol(sel); + avoid_eol(*m_buffer, sel); } sort_and_merge_overlapping(m_selections, m_main_sel); } @@ -215,8 +216,8 @@ void Editor::clear_selections() auto& sel = m_selections[m_main_sel]; auto& pos = sel.last(); - if (*pos == '\n' and not pos.is_begin() and *utf8::previous(pos) != '\n') - pos = utf8::previous(pos); + if (pos.column != 0 and pos.column == m_buffer->line_length(pos.line) - 1) + pos = m_buffer->char_prev(pos); sel.first() = pos; m_selections.erase(m_selections.begin(), m_selections.begin() + m_main_sel); @@ -392,21 +393,22 @@ private: SelectionList m_ranges; }; -inline bool touches(const Range& lhs, const Range& rhs) +inline bool touches(const Buffer& buffer, const Range& lhs, const Range& rhs) { - return lhs.min() <= rhs.min() ? utf8::next(lhs.max()) >= rhs.min() - : lhs.min() <= utf8::next(rhs.max()); + return lhs.min() <= rhs.min() ? buffer.char_next(lhs.max()) >= rhs.min() + : lhs.min() <= buffer.char_next(rhs.max()); } bool Editor::undo() { + using namespace std::placeholders; ModifiedRangesListener listener(buffer()); bool res = m_buffer->undo(); if (res and not listener.ranges().empty()) { m_selections = std::move(listener.ranges()); m_main_sel = m_selections.size() - 1; - merge_overlapping(m_selections, m_main_sel, touches); + merge_overlapping(m_selections, m_main_sel, std::bind(touches, std::ref(buffer()), _1, _2)); } check_invariant(); return res; @@ -414,13 +416,14 @@ bool Editor::undo() bool Editor::redo() { + using namespace std::placeholders; ModifiedRangesListener listener(buffer()); bool res = m_buffer->redo(); if (res and not listener.ranges().empty()) { m_selections = std::move(listener.ranges()); m_main_sel = m_selections.size() - 1; - merge_overlapping(m_selections, m_main_sel, touches); + merge_overlapping(m_selections, m_main_sel, std::bind(touches, std::ref(buffer()), _1, _2)); } check_invariant(); return res; @@ -463,17 +466,17 @@ IncrementalInserter::IncrementalInserter(Editor& editor, InsertMode mode) utf8_it first, last; switch (mode) { - case InsertMode::Insert: first = sel.max(); last = sel.min(); break; + case InsertMode::Insert: first = buffer.iterator_at(sel.max()); last = buffer.iterator_at(sel.min()); break; case InsertMode::Replace: { - buffer.erase(sel.min(), utf8::next(sel.max())); - first = last = sel.min(); + Kakoune::erase(buffer, sel); + first = last = buffer.iterator_at(sel.min()); break; } case InsertMode::Append: { - first = sel.min(); - last = std::max(sel.first(), sel.last()); + first = buffer.iterator_at(sel.min()); + last = buffer.iterator_at(sel.max()); // special case for end of lines, append to current line instead auto coord = last.underlying_iterator().coord(); if (coord.column != buffer.line_length(coord.line) - 1) @@ -483,13 +486,13 @@ IncrementalInserter::IncrementalInserter(Editor& editor, InsertMode mode) case InsertMode::OpenLineBelow: case InsertMode::AppendAtLineEnd: - first = utf8_it(buffer.iterator_at_line_end(sel.max())) - 1; + first = utf8_it(buffer.iterator_at(sel.max().line+1)) - 1; last = first; break; case InsertMode::OpenLineAbove: case InsertMode::InsertAtLineBegin: - first = buffer.iterator_at_line_begin(sel.min()); + first = buffer.iterator_at(sel.min().line); if (mode == InsertMode::OpenLineAbove) --first; else @@ -534,9 +537,9 @@ IncrementalInserter::~IncrementalInserter() { for (auto& sel : m_editor.m_selections) { - if (m_mode == InsertMode::Append and sel.last().column() > 0) - sel.last() = utf8::previous(sel.last()); - avoid_eol(sel); + if (m_mode == InsertMode::Append and sel.last().column > 0) + sel.last() = m_editor.buffer().char_prev(sel.last()); + avoid_eol(m_editor.buffer(), sel); } } @@ -564,8 +567,8 @@ void IncrementalInserter::erase() { for (auto& sel : m_editor.m_selections) { - BufferIterator pos = sel.last(); - m_editor.buffer().erase(utf8::previous(pos), pos); + BufferCoord pos = sel.last(); + m_editor.buffer().erase(m_editor.buffer().char_prev(pos), pos); } } diff --git a/src/filters.cc b/src/filters.cc index ae6aa760..53f6576d 100644 --- a/src/filters.cc +++ b/src/filters.cc @@ -10,7 +10,7 @@ void preserve_indent(Buffer& buffer, Selection& selection, String& content) { if (content == "\n") { - BufferCoord line_begin{selection.last().line(), 0}; + BufferCoord line_begin{selection.last().line, 0}; auto first_non_white = buffer.iterator_at(line_begin); while ((*first_non_white == '\t' or *first_non_white == ' ') and not first_non_white.is_end()) @@ -82,12 +82,12 @@ struct RegexFilter String suffix(it+1, content.end()); content = String(content.begin(), it-1); - auto first = selection.first(); - auto last = selection.last(); + auto& first = selection.first(); + auto& last = selection.last(); buffer.insert(position, suffix); - if (selection.first() == selection.last()) - selection.first() -= suffix.length(); - selection.last() -= suffix.length(); + if (first == last) + first = buffer.advance(first, -suffix.length()); + last = buffer.advance(last, -suffix.length()); } } } diff --git a/src/highlighters.cc b/src/highlighters.cc index edd3d7d3..064c1660 100644 --- a/src/highlighters.cc +++ b/src/highlighters.cc @@ -280,22 +280,23 @@ void show_line_numbers(const Window& window, DisplayBuffer& display_buffer) void highlight_selections(const Window& window, DisplayBuffer& display_buffer) { const bool only_cursor = window.is_editing() and window.options()["insert_hide_sel"].get(); + const auto& buffer = window.buffer(); for (size_t i = 0; i < window.selections().size(); ++i) { auto& sel = window.selections()[i]; const bool forward = sel.first() <= sel.last(); - BufferIterator begin = forward ? sel.first() : utf8::next(sel.last()); - BufferIterator end = forward ? sel.last() : utf8::next(sel.first()); + BufferCoord begin = forward ? sel.first() : buffer.char_next(sel.last()); + BufferCoord end = forward ? sel.last() : buffer.char_next(sel.first()); const bool primary = (i == window.main_selection_index()); if (not only_cursor) { ColorPair sel_colors = get_color(primary ? "PrimarySelection" : "SecondarySelection"); - highlight_range(display_buffer, begin.coord(), end.coord(), false, + highlight_range(display_buffer, begin, end, false, [&](DisplayAtom& atom) { atom.colors = sel_colors; }); } ColorPair cur_colors = get_color(primary ? "PrimaryCursor" : "SecondaryCursor"); - highlight_range(display_buffer, sel.last().coord(), utf8::next(sel.last()).coord(), false, + highlight_range(display_buffer, sel.last(), buffer.char_next(sel.last()), false, [&](DisplayAtom& atom) { atom.colors = cur_colors; }); } } diff --git a/src/input_handler.cc b/src/input_handler.cc index d76d3b14..fd2be75c 100644 --- a/src/input_handler.cc +++ b/src/input_handler.cc @@ -610,11 +610,13 @@ public: for (auto& sel : m_context.editor().selections()) { auto offset = buffer.offset(sel.last()); + auto pos = buffer.iterator_at(sel.last()); if (offset >= beg_offset and offset + end_offset < buffer_len and - std::equal(sel.last() - beg_offset, sel.last(), begin)) + std::equal(pos - beg_offset, pos, begin)) { - buffer.erase(sel.last() - beg_offset, sel.last() + end_offset); - buffer.insert(sel.last(), candidate); + auto beg = pos - beg_offset; + buffer.erase(beg, pos + end_offset); + buffer.insert(beg, candidate); } } diff --git a/src/main.cc b/src/main.cc index fc196564..980438a5 100644 --- a/src/main.cc +++ b/src/main.cc @@ -68,7 +68,7 @@ void register_env_vars() shell_manager.register_env_var("selection", [](const String& name, const Context& context) { const Range& sel = context.editor().main_selection(); - return context.buffer().string(sel.min(), utf8::next(sel.max())); }); + return content(context.buffer(), sel); }); shell_manager.register_env_var("selections", [](const String& name, const Context& context) { auto sels = context.editor().selections_content(); @@ -91,15 +91,15 @@ void register_env_vars() { return ClientManager::instance().get_client(context).name(); }); shell_manager.register_env_var("cursor_line", [](const String& name, const Context& context) - { return to_string(context.editor().main_selection().last().line() + 1); }); + { return to_string(context.editor().main_selection().last().line + 1); }); shell_manager.register_env_var("cursor_column", [](const String& name, const Context& context) - { return to_string(context.editor().main_selection().last().column() + 1); }); + { return to_string(context.editor().main_selection().last().column + 1); }); shell_manager.register_env_var("selection_desc", [](const String& name, const Context& context) { auto& sel = context.editor().main_selection(); auto beg = sel.min(); - return to_string(beg.line() + 1) + ':' + to_string(beg.column() + 1) + '+' + + return to_string(beg.line + 1) + ':' + to_string(beg.column + 1) + '+' + to_string((int)context.buffer().distance(beg, sel.max())+1); }); shell_manager.register_env_var("window_width", [](const String& name, const Context& context) diff --git a/src/normal.cc b/src/normal.cc index 0204dc3a..65347c48 100644 --- a/src/normal.cc +++ b/src/normal.cc @@ -136,14 +136,15 @@ void goto_commands(Context& context) case 'f': { const Range& sel = context.editor().main_selection(); - String filename = context.buffer().string(sel.min(), utf8::next(sel.max())); + const Buffer& buffer = context.buffer(); + String filename = content(buffer, sel); static constexpr char forbidden[] = { '\'', '\\', '\0' }; for (auto c : forbidden) if (contains(filename, c)) return; auto paths = context.options()["path"].get>(); - const String& buffer_name = context.buffer().name(); + const String& buffer_name = buffer.name(); auto it = find(reversed(buffer_name), '/'); if (it != buffer_name.rend()) paths.insert(paths.begin(), String{buffer_name.begin(), it.base()}); @@ -248,7 +249,7 @@ void pipe(Context& context) Editor& editor = context.editor(); std::vector strings; for (auto& sel : context.editor().selections()) - strings.push_back(ShellManager::instance().pipe({sel.min(), utf8::next(sel.max())}, + strings.push_back(ShellManager::instance().pipe(content(context.buffer(), sel), cmdline, context, {}, EnvVarMap{})); editor.insert(strings, InsertMode::Replace); @@ -332,20 +333,21 @@ void use_selection_as_search_pattern(Context& context) { std::vector patterns; auto& sels = context.editor().selections(); + const auto& buffer = context.buffer(); for (auto& sel : sels) { auto begin = sel.min(); - auto end = utf8::next(sel.max()); + auto end = buffer.char_next(sel.max()); auto content = "\\Q" + context.buffer().string(begin, end) + "\\E"; if (smart) { - if (begin.is_begin() or - (is_word(utf8::codepoint(begin)) and not - is_word(utf8::codepoint(utf8::previous(begin))))) + if (begin == BufferCoord{0,0} or + (is_word(buffer.char_at(begin)) and not + is_word(buffer.char_at(buffer.char_prev(begin))))) content = "\\b" + content; - if (end.is_end() or - (is_word(utf8::codepoint(utf8::previous(end))) and not - is_word(utf8::codepoint(end)))) + if (buffer.is_end(end) or + (is_word(buffer.char_at(buffer.char_prev(end))) and not + is_word(buffer.char_at(end)))) content = content + "\\b"; } patterns.push_back(std::move(content)); @@ -371,7 +373,7 @@ void cat_yank(Context& context) to_string(sels.size()) + " selections", get_color("Information") }); } -void erase(Context& context) +void erase_selections(Context& context) { RegisterManager::instance()['"'] = context.editor().selections_content(); context.editor().erase(); @@ -478,7 +480,7 @@ void join_select_spaces(Context& context) kak_assert(std::is_sorted(res.begin(), res.end(), [](const Selection& lhs, const Selection& rhs) { return lhs.min() < rhs.min(); })); - if (not res.empty() and utf8::next(res.back().max()).is_end()) + if (not res.empty() and buffer.is_end(buffer.char_next(res.back().max()))) res.pop_back(); return res; }); @@ -499,11 +501,13 @@ void keep(Context& context) constexpr const char* prompt = matching ? "keep matching:" : "keep not matching:"; regex_prompt(context, prompt, [](const Regex& ex, Context& context) { Editor& editor = context.editor(); + const Buffer& buffer = context.buffer(); SelectionList sels = editor.selections(); SelectionList keep; for (auto& sel : sels) { - if (boost::regex_search(sel.min(), utf8::next(sel.max()), ex) == matching) + if (boost::regex_search(buffer.iterator_at(sel.min()), + utf8::next(buffer.iterator_at(sel.max())), ex) == matching) keep.push_back(sel); } if (keep.empty()) @@ -703,8 +707,8 @@ void align(Context& context) { auto& selections = context.editor().selections(); auto& buffer = context.buffer(); - auto get_column = [&buffer](const BufferIterator& it) - { return utf8::distance(buffer.iterator_at_line_begin(it), it); }; + auto get_column = [&buffer](const BufferCoord& coord) + { return buffer.char_distance({coord.line, 0}, coord); }; CharCount max_col = 0; for (auto& sel : selections) @@ -774,7 +778,7 @@ KeyMap keymap = { { Key::Modifiers::Alt, 'T' }, select_to_next_char }, { { Key::Modifiers::Alt, 'F' }, select_to_next_char }, - { { Key::Modifiers::None, 'd' }, erase }, + { { Key::Modifiers::None, 'd' }, erase_selections }, { { Key::Modifiers::None, 'c' }, change }, { { Key::Modifiers::None, 'i' }, insert }, { { Key::Modifiers::None, 'I' }, insert }, diff --git a/src/selection.cc b/src/selection.cc index 0a4c4e32..f7360e7f 100644 --- a/src/selection.cc +++ b/src/selection.cc @@ -22,9 +22,9 @@ void on_buffer_change(const Buffer& buffer, SelectionList& sels, const BufferCoord& begin, const BufferCoord& end, LineCount end_line) { auto update_beg = std::lower_bound(sels.begin(), sels.end(), begin, - [](const Selection& s, const BufferCoord& c) { return std::max(s.first().coord(), s.last().coord()) < c; }); + [](const Selection& s, const BufferCoord& c) { return std::max(s.first(), s.last()) < c; }); auto update_only_line_beg = std::upper_bound(sels.begin(), sels.end(), end_line, - [](LineCount l, const Selection& s) { return l < std::min(s.first().coord(), s.last().coord()).line; }); + [](LineCount l, const Selection& s) { return l < std::min(s.first(), s.last()).line; }); if (update_beg != update_only_line_beg) { @@ -50,10 +50,9 @@ void on_buffer_change(const Buffer& buffer, SelectionList& sels, template struct UpdateInsert { - void operator()(const Buffer& buffer, BufferIterator& it, + void operator()(const Buffer& buffer, BufferCoord& coord, const BufferCoord& begin, const BufferCoord& end) const { - auto coord = it.coord(); if (assume_different_line) kak_assert(begin.line < coord.line); if (not assume_greater_than_begin and coord < begin) @@ -62,17 +61,15 @@ struct UpdateInsert coord.column = end.column + coord.column - begin.column; coord.line += end.line - begin.line; - it = coord; } }; template struct UpdateErase { - void operator()(const Buffer& buffer, BufferIterator& it, + void operator()(const Buffer& buffer, BufferCoord& coord, const BufferCoord& begin, const BufferCoord& end) const { - auto coord = it.coord(); if (not assume_greater_than_begin and coord < begin) return; if (assume_different_line) @@ -89,7 +86,6 @@ struct UpdateErase else coord.line -= end.line - begin.line; } - it = coord; } }; diff --git a/src/selection.hh b/src/selection.hh index e8754df5..c7e5e2c3 100644 --- a/src/selection.hh +++ b/src/selection.hh @@ -10,28 +10,28 @@ namespace Kakoune struct Range { public: - Range(const BufferIterator& first, const BufferIterator& last) + Range(const BufferCoord& first, const BufferCoord& last) : m_first{first}, m_last{last} {} void merge_with(const Range& range); - BufferIterator& first() { return m_first; } - BufferIterator& last() { return m_last; } + BufferCoord& first() { return m_first; } + BufferCoord& last() { return m_last; } - const BufferIterator& first() const { return m_first; } - const BufferIterator& last() const { return m_last; } + const BufferCoord& first() const { return m_first; } + const BufferCoord& last() const { return m_last; } bool operator== (const Range& other) const { return m_first == other.m_first and m_last == other.m_last; } - const BufferIterator& min() const { return std::min(m_first, m_last); } - const BufferIterator& max() const { return std::max(m_first, m_last); } + const BufferCoord& min() const { return std::min(m_first, m_last); } + const BufferCoord& max() const { return std::max(m_first, m_last); } private: - BufferIterator m_first; - BufferIterator m_last; + BufferCoord m_first; + BufferCoord m_last; }; inline bool overlaps(const Range& lhs, const Range& rhs) @@ -40,12 +40,22 @@ inline bool overlaps(const Range& lhs, const Range& rhs) : lhs.min() <= rhs.max(); } +inline String content(const Buffer& buffer, const Range& range) +{ + return buffer.string(range.min(), buffer.char_next(range.max())); +} + +inline void erase(Buffer& buffer, const Range& range) +{ + return buffer.erase(range.min(), buffer.char_next(range.max())); +} + using CaptureList = std::vector; // A selection is a Range, associated with a CaptureList struct Selection : public Range { - Selection(const BufferIterator& first, const BufferIterator& last, + Selection(const BufferCoord& first, const BufferCoord& last, CaptureList captures = {}) : Range(first, last), m_captures(std::move(captures)) {} diff --git a/src/selectors.cc b/src/selectors.cc index ae9f7397..c2e7e62f 100644 --- a/src/selectors.cc +++ b/src/selectors.cc @@ -85,7 +85,7 @@ typedef boost::regex_iterator RegexIterator; template Selection select_to_next_word(const Buffer& buffer, const Selection& selection) { - Utf8Iterator begin = selection.last(); + Utf8Iterator begin = buffer.iterator_at(selection.last()); if (categorize(*begin) != categorize(*(begin+1))) ++begin; @@ -108,7 +108,7 @@ template Selection select_to_next_word(const Buffer&, const Selection&); template Selection select_to_next_word_end(const Buffer& buffer, const Selection& selection) { - Utf8Iterator begin = selection.last(); + Utf8Iterator begin = buffer.iterator_at(selection.last()); if (categorize(*begin) != categorize(*(begin+1))) ++begin; @@ -130,7 +130,7 @@ template Selection select_to_next_word_end(const Buffer&, const Selection& template Selection select_to_previous_word(const Buffer& buffer, const Selection& selection) { - Utf8Iterator begin = selection.last(); + Utf8Iterator begin = buffer.iterator_at(selection.last()); if (categorize(*begin) != categorize(*(begin-1))) @@ -159,7 +159,7 @@ template Selection select_to_previous_word(const Buffer&, const Selection& Selection select_line(const Buffer& buffer, const Selection& selection) { - Utf8Iterator first = selection.last(); + Utf8Iterator first = buffer.iterator_at(selection.last()); if (*first == '\n' and not is_end(first + 1)) ++first; @@ -175,7 +175,7 @@ Selection select_line(const Buffer& buffer, const Selection& selection) Selection select_matching(const Buffer& buffer, const Selection& selection) { std::vector matching_pairs = { '(', ')', '{', '}', '[', ']', '<', '>' }; - Utf8Iterator it = selection.last(); + Utf8Iterator it = buffer.iterator_at(selection.last()); std::vector::iterator match = matching_pairs.end(); while (not is_eol(*it)) { @@ -289,15 +289,15 @@ Selection select_surrounding(const Buffer& buffer, const Selection& selection, const CodepointPair& matching, ObjectFlags flags) { - auto res = find_surrounding(selection.last(), matching, flags); + auto res = find_surrounding(buffer.iterator_at(selection.last()), matching, flags); if (not res) return selection; if (flags == (ObjectFlags::ToBegin | ObjectFlags::ToEnd) and - matching.first != matching.second and not res->last().is_end() and + matching.first != matching.second and not buffer.is_end(res->last()) and (*res == selection or Range{res->last(), res->first()} == selection)) { - res = find_surrounding(res->last() + 1, matching, flags); + res = find_surrounding(buffer.iterator_at(res->last()) + 1, matching, flags); return res ? Selection{*res} : selection; } return *res; @@ -306,7 +306,7 @@ Selection select_surrounding(const Buffer& buffer, const Selection& selection, Selection select_to(const Buffer& buffer, const Selection& selection, Codepoint c, int count, bool inclusive) { - Utf8Iterator begin = selection.last(); + Utf8Iterator begin = buffer.iterator_at(selection.last()); Utf8Iterator end = begin; do { @@ -323,7 +323,7 @@ Selection select_to(const Buffer& buffer, const Selection& selection, Selection select_to_reverse(const Buffer& buffer, const Selection& selection, Codepoint c, int count, bool inclusive) { - Utf8Iterator begin = selection.last(); + Utf8Iterator begin = buffer.iterator_at(selection.last()); Utf8Iterator end = begin; do { @@ -339,7 +339,7 @@ Selection select_to_reverse(const Buffer& buffer, const Selection& selection, Selection select_to_eol(const Buffer& buffer, const Selection& selection) { - Utf8Iterator begin = selection.last(); + Utf8Iterator begin = buffer.iterator_at(selection.last()); Utf8Iterator end = begin + 1; skip_while(end, [](Codepoint cur) { return not is_eol(cur); }); return utf8_range(begin, end-1); @@ -347,7 +347,7 @@ Selection select_to_eol(const Buffer& buffer, const Selection& selection) Selection select_to_eol_reverse(const Buffer& buffer, const Selection& selection) { - Utf8Iterator begin = selection.last(); + Utf8Iterator begin = buffer.iterator_at(selection.last()); Utf8Iterator end = begin - 1; skip_while_reverse(end, [](Codepoint cur) { return not is_eol(cur); }); return utf8_range(begin, is_begin(end) ? end : end+1); @@ -356,7 +356,7 @@ Selection select_to_eol_reverse(const Buffer& buffer, const Selection& selection template Selection select_whole_word(const Buffer& buffer, const Selection& selection, ObjectFlags flags) { - Utf8Iterator first = selection.last(); + Utf8Iterator first = buffer.iterator_at(selection.last()); Utf8Iterator last = first; if (is_word(*first)) { @@ -399,7 +399,7 @@ template Selection select_whole_word(const Buffer&, const Selection&, Obje Selection select_whole_sentence(const Buffer& buffer, const Selection& selection, ObjectFlags flags) { - BufferIterator first = selection.last(); + BufferIterator first = buffer.iterator_at(selection.last()); BufferIterator last = first; if (flags & ObjectFlags::ToBegin) @@ -450,7 +450,7 @@ Selection select_whole_sentence(const Buffer& buffer, const Selection& selection Selection select_whole_paragraph(const Buffer& buffer, const Selection& selection, ObjectFlags flags) { - BufferIterator first = selection.last(); + BufferIterator first = buffer.iterator_at(selection.last()); BufferIterator last = first; if (flags & ObjectFlags::ToBegin and not is_begin(first)) @@ -493,8 +493,8 @@ Selection select_whole_paragraph(const Buffer& buffer, const Selection& selectio Selection select_whole_lines(const Buffer& buffer, const Selection& selection) { // no need to be utf8 aware for is_eol as we only use \n as line seperator - BufferIterator first = selection.first(); - BufferIterator last = selection.last(); + BufferIterator first = buffer.iterator_at(selection.first()); + BufferIterator last = buffer.iterator_at(selection.last()); BufferIterator& to_line_start = first <= last ? first : last; BufferIterator& to_line_end = first <= last ? last : first; @@ -513,8 +513,8 @@ Selection select_whole_lines(const Buffer& buffer, const Selection& selection) Selection trim_partial_lines(const Buffer& buffer, const Selection& selection) { // same as select_whole_lines - BufferIterator first = selection.first(); - BufferIterator last = selection.last(); + BufferIterator first = buffer.iterator_at(selection.first()); + BufferIterator last = buffer.iterator_at(selection.last()); BufferIterator& to_line_start = first <= last ? first : last; BufferIterator& to_line_end = first <= last ? last : first; @@ -565,7 +565,7 @@ Selection select_next_match(const Buffer& buffer, const Selection& selection, co { // regex matching do not use Utf8Iterator as boost::regex handle utf8 // decoding itself - BufferIterator begin = selection.last(); + BufferIterator begin = buffer.iterator_at(selection.last()); BufferIterator end = begin; CaptureList captures; @@ -594,8 +594,8 @@ template Selection select_next_match(const Buffer&, const Selection&, con SelectionList select_all_matches(const Buffer& buffer, const Selection& selection, const Regex& regex) { - auto sel_end = utf8::next(selection.max()); - RegexIterator re_it(selection.min(), sel_end, regex); + auto sel_end = utf8::next(buffer.iterator_at(selection.max())); + RegexIterator re_it(buffer.iterator_at(selection.min()), sel_end, regex); RegexIterator re_end; SelectionList result; @@ -620,13 +620,12 @@ SelectionList select_all_matches(const Buffer& buffer, const Selection& selectio SelectionList split_selection(const Buffer& buffer, const Selection& selection, const Regex& regex) { - auto sel_end = utf8::next(selection.max()); - RegexIterator re_it(selection.min(), sel_end, regex, - boost::regex_constants::match_nosubs); + auto begin = buffer.iterator_at(selection.min()); + auto sel_end = utf8::next(buffer.iterator_at(selection.max())); + RegexIterator re_it(begin, sel_end, regex, boost::regex_constants::match_nosubs); RegexIterator re_end; SelectionList result; - BufferIterator begin = selection.min(); for (; re_it != re_end; ++re_it) { BufferIterator end = (*re_it)[0].first; diff --git a/src/unit_tests.cc b/src/unit_tests.cc index 77e081d6..789e9c79 100644 --- a/src/unit_tests.cc +++ b/src/unit_tests.cc @@ -62,8 +62,8 @@ void test_editor() editor.multi_select(std::bind(select_all_matches, _1, _2, Regex{"\\n\\h*"})); for (auto& sel : editor.selections()) { - kak_assert(*sel.min() == '\n'); - editor.buffer().erase(sel.min(), utf8::next(sel.max())); + kak_assert(buffer.byte_at(sel.min()) == '\n'); + erase(buffer, sel); } } editor.undo(); @@ -71,7 +71,7 @@ void test_editor() Selection sel{ buffer.iterator_at_line_begin(2_line), buffer.end()-1 }; editor.select(sel, SelectMode::Replace); editor.insert("",InsertMode::Replace); - kak_assert(not editor.main_selection().first().is_end()); + kak_assert(not buffer.is_end(editor.main_selection().first())); } void test_incremental_inserter() diff --git a/src/window.cc b/src/window.cc index 6dea573e..dba0a302 100644 --- a/src/window.cc +++ b/src/window.cc @@ -42,7 +42,7 @@ void Window::display_selection_at(LineCount line) { if (line >= 0 or line < m_dimensions.line) { - auto cursor_line = main_selection().last().line(); + auto cursor_line = main_selection().last().line; m_position.line = std::max(0_line, cursor_line - line); } } @@ -69,14 +69,12 @@ void Window::update_display_buffer() LineCount buffer_line = m_position.line + line; if (buffer_line >= buffer().line_count()) break; - BufferIterator line_begin = buffer().iterator_at_line_begin(buffer_line); - BufferIterator line_end = buffer().iterator_at_line_end(buffer_line); - - BufferIterator begin = utf8::advance(line_begin, line_end, (int)m_position.column); - BufferIterator end = utf8::advance(begin, line_end, (int)m_dimensions.column); + BufferCoord limit{buffer_line+1, 0}; + auto begin = std::min(buffer().char_advance(buffer_line, m_position.column), limit); + auto end = std::min(buffer().char_advance(begin, m_dimensions.column), limit); lines.push_back(DisplayLine(buffer_line)); - lines.back().push_back(DisplayAtom(AtomContent(buffer(), begin.coord(), end.coord()))); + lines.back().push_back(DisplayAtom(AtomContent(buffer(), begin, end))); } m_display_buffer.compute_range(); @@ -112,26 +110,24 @@ static LineCount adapt_view_pos(LineCount line, LineCount offset, void Window::scroll_to_keep_cursor_visible_ifn() { - const BufferIterator first = main_selection().first(); - const BufferIterator last = main_selection().last(); + const auto& first = main_selection().first(); + const auto& last = main_selection().last(); const LineCount offset = std::min(options()["scrolloff"].get(), (m_dimensions.line - 1) / 2); // scroll lines if needed, try to get as much of the selection visible as possible - m_position.line = adapt_view_pos(first.line(), offset, m_position.line, + m_position.line = adapt_view_pos(first.line, offset, m_position.line, m_dimensions.line, buffer().line_count()); - m_position.line = adapt_view_pos(last.line(), offset, m_position.line, + m_position.line = adapt_view_pos(last.line, offset, m_position.line, m_dimensions.line, buffer().line_count()); // highlight only the line containing the cursor DisplayBuffer display_buffer; DisplayBuffer::LineList& lines = display_buffer.lines(); - lines.push_back(DisplayLine(last.line())); + lines.push_back(DisplayLine(last.line)); - BufferIterator line_begin = buffer().iterator_at_line_begin(last); - BufferIterator line_end = buffer().iterator_at_line_end(last); - lines.back().push_back(DisplayAtom(AtomContent(buffer(), line_begin.coord(), line_end.coord()))); + lines.back().push_back(DisplayAtom(AtomContent(buffer(), last.line, last.line+1))); display_buffer.compute_range(); m_highlighters(*this, display_buffer); @@ -145,21 +141,21 @@ void Window::scroll_to_keep_cursor_visible_ifn() for (auto& atom : lines.back()) { if (atom.content.has_buffer_range() and - atom.content.begin() <= last.coord() and atom.content.end() > last.coord()) + atom.content.begin() <= last and atom.content.end() > last) { if (atom.content.type() == AtomContent::BufferRange) - column += utf8::distance(buffer().iterator_at(atom.content.begin()), last); + column += buffer().char_distance(atom.content.begin(), last); else column += atom.content.content().char_length(); - CharCount first_col = first.line() == last.line() ? - utf8::distance(line_begin, first) : 0_char; + CharCount first_col = first.line == last.line ? + buffer().char_distance(last.line, first) : 0_char; if (first_col < m_position.column) m_position.column = first_col; else if (column >= m_position.column + m_dimensions.column) m_position.column = column - (m_dimensions.column - 1); - CharCount last_col = utf8::distance(line_begin, last); + CharCount last_col = buffer().char_distance(last.line, last); if (last_col < m_position.column) m_position.column = last_col; else if (column >= m_position.column + m_dimensions.column) @@ -169,7 +165,7 @@ void Window::scroll_to_keep_cursor_visible_ifn() } column += atom.content.content().char_length(); } - if (last != buffer().end()) + if (not buffer().is_end(last)) { // the cursor should always be visible. kak_assert(false);