Use coord instead of iterators for selections

This commit is contained in:
Maxime Coste 2013-06-03 18:58:09 +02:00
parent 02b33c7d8f
commit 4ef1bfa4db
13 changed files with 174 additions and 163 deletions

View File

@ -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())

View File

@ -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
}

View File

@ -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,12 +38,12 @@ 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,
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<String>& 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<String> Editor::selections_content() const
{
std::vector<String> 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<BufferCoord>(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);
}
}

View File

@ -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());
}
}
}

View File

@ -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<bool>();
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; });
}
}

View File

@ -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);
}
}

View File

@ -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)

View File

@ -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<std::vector<String>>();
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<String> 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<String> 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<SelectFlags::Extend | SelectFlags::Reverse> },
{ { Key::Modifiers::Alt, 'F' }, select_to_next_char<SelectFlags::Inclusive | SelectFlags::Extend | SelectFlags::Reverse> },
{ { Key::Modifiers::None, 'd' }, erase },
{ { Key::Modifiers::None, 'd' }, erase_selections },
{ { Key::Modifiers::None, 'c' }, change },
{ { Key::Modifiers::None, 'i' }, insert<InsertMode::Insert> },
{ { Key::Modifiers::None, 'I' }, insert<InsertMode::InsertAtLineBegin> },

View File

@ -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<bool assume_different_line, bool assume_greater_than_begin>
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<bool assume_different_line, bool assume_greater_than_begin>
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;
}
};

View File

@ -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<String>;
// 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)) {}

View File

@ -85,7 +85,7 @@ typedef boost::regex_iterator<BufferIterator> RegexIterator;
template<bool punctuation_is_word>
Selection select_to_next_word(const Buffer& buffer, const Selection& selection)
{
Utf8Iterator begin = selection.last();
Utf8Iterator begin = buffer.iterator_at(selection.last());
if (categorize<punctuation_is_word>(*begin) !=
categorize<punctuation_is_word>(*(begin+1)))
++begin;
@ -108,7 +108,7 @@ template Selection select_to_next_word<true>(const Buffer&, const Selection&);
template<bool punctuation_is_word>
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<punctuation_is_word>(*begin) !=
categorize<punctuation_is_word>(*(begin+1)))
++begin;
@ -130,7 +130,7 @@ template Selection select_to_next_word_end<true>(const Buffer&, const Selection&
template<bool punctuation_is_word>
Selection select_to_previous_word(const Buffer& buffer, const Selection& selection)
{
Utf8Iterator begin = selection.last();
Utf8Iterator begin = buffer.iterator_at(selection.last());
if (categorize<punctuation_is_word>(*begin) !=
categorize<punctuation_is_word>(*(begin-1)))
@ -159,7 +159,7 @@ template Selection select_to_previous_word<true>(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<Codepoint> matching_pairs = { '(', ')', '{', '}', '[', ']', '<', '>' };
Utf8Iterator it = selection.last();
Utf8Iterator it = buffer.iterator_at(selection.last());
std::vector<Codepoint>::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<bool punctuation_is_word>
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<punctuation_is_word>(*first))
{
@ -399,7 +399,7 @@ template Selection select_whole_word<true>(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<false>(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;

View File

@ -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()

View File

@ -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<LineCount>(options()["scrolloff"].get<int>(),
(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);