Use coord instead of iterators for selections
This commit is contained in:
parent
02b33c7d8f
commit
4ef1bfa4db
|
@ -187,11 +187,11 @@ static DisplayLine generate_status_line(Client& client)
|
||||||
{
|
{
|
||||||
auto& context = client.context();
|
auto& context = client.context();
|
||||||
auto pos = context.editor().main_selection().last();
|
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;
|
std::ostringstream oss;
|
||||||
oss << context.buffer().display_name()
|
oss << context.buffer().display_name()
|
||||||
<< " " << (int)pos.line()+1 << ":" << (int)col+1;
|
<< " " << (int)pos.line+1 << ":" << (int)col+1;
|
||||||
if (context.buffer().is_modified())
|
if (context.buffer().is_modified())
|
||||||
oss << " [+]";
|
oss << " [+]";
|
||||||
if (context.input_handler().is_recording())
|
if (context.input_handler().is_recording())
|
||||||
|
|
|
@ -30,8 +30,8 @@ void DynamicSelectionList::check_invariant() const
|
||||||
kak_assert(buffer.is_valid(sel.last()));
|
kak_assert(buffer.is_valid(sel.last()));
|
||||||
kak_assert(not buffer.is_end(sel.first()));
|
kak_assert(not buffer.is_end(sel.first()));
|
||||||
kak_assert(not buffer.is_end(sel.last()));
|
kak_assert(not buffer.is_end(sel.last()));
|
||||||
kak_assert(utf8::is_character_start(sel.first()));
|
kak_assert(utf8::is_character_start(buffer.iterator_at(sel.first())));
|
||||||
kak_assert(utf8::is_character_start(sel.last()));
|
kak_assert(utf8::is_character_start(buffer.iterator_at(sel.last())));
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
119
src/editor.cc
119
src/editor.cc
|
@ -20,17 +20,17 @@ Editor::Editor(Buffer& buffer)
|
||||||
m_main_sel = 0;
|
m_main_sel = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void avoid_eol(BufferIterator& it)
|
static void avoid_eol(const Buffer& buffer, BufferCoord& coord)
|
||||||
{
|
{
|
||||||
const auto column = it.column();
|
const auto column = coord.column;
|
||||||
if (column != 0 and column == it.buffer().line_length(it.line()) - 1)
|
if (column != 0 and column == buffer.line_length(coord.line) - 1)
|
||||||
it = utf8::previous(it);
|
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(buffer, sel.first());
|
||||||
avoid_eol(sel.last());
|
avoid_eol(buffer, sel.last());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Editor::erase()
|
void Editor::erase()
|
||||||
|
@ -38,13 +38,13 @@ void Editor::erase()
|
||||||
scoped_edition edition(*this);
|
scoped_edition edition(*this);
|
||||||
for (auto& sel : m_selections)
|
for (auto& sel : m_selections)
|
||||||
{
|
{
|
||||||
m_buffer->erase(sel.min(), utf8::next(sel.max()));
|
Kakoune::erase(*m_buffer, sel);
|
||||||
avoid_eol(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)
|
InsertMode mode)
|
||||||
{
|
{
|
||||||
switch (mode)
|
switch (mode)
|
||||||
{
|
{
|
||||||
|
@ -52,37 +52,37 @@ static BufferIterator prepare_insert(Buffer& buffer, const Selection& sel,
|
||||||
return sel.min();
|
return sel.min();
|
||||||
case InsertMode::Replace:
|
case InsertMode::Replace:
|
||||||
{
|
{
|
||||||
BufferIterator pos = sel.min();
|
BufferCoord pos = sel.min();
|
||||||
buffer.erase(sel.min(), utf8::next(sel.max()));
|
Kakoune::erase(buffer, sel);
|
||||||
return pos;
|
return pos;
|
||||||
}
|
}
|
||||||
case InsertMode::Append:
|
case InsertMode::Append:
|
||||||
{
|
{
|
||||||
// special case for end of lines, append to current line instead
|
// special case for end of lines, append to current line instead
|
||||||
auto pos = std::max(sel.first(), sel.last());
|
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;
|
return pos;
|
||||||
else
|
else
|
||||||
return utf8::next(pos);
|
return buffer.char_next(pos);
|
||||||
}
|
}
|
||||||
case InsertMode::InsertAtLineBegin:
|
case InsertMode::InsertAtLineBegin:
|
||||||
return buffer.iterator_at_line_begin(sel.min());
|
return sel.min().line;
|
||||||
case InsertMode::AppendAtLineEnd:
|
case InsertMode::AppendAtLineEnd:
|
||||||
return buffer.iterator_at_line_end(sel.max())-1;
|
return buffer.char_prev(sel.max().line+1);
|
||||||
case InsertMode::InsertAtNextLineBegin:
|
case InsertMode::InsertAtNextLineBegin:
|
||||||
return buffer.iterator_at_line_end(sel.max());
|
return sel.max().line+1;
|
||||||
case InsertMode::OpenLineBelow:
|
case InsertMode::OpenLineBelow:
|
||||||
case InsertMode::OpenLineAbove:
|
case InsertMode::OpenLineAbove:
|
||||||
{
|
{
|
||||||
auto line = mode == 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");
|
buffer.insert(line, "\n");
|
||||||
return {buffer, line};
|
return line;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
kak_assert(false);
|
kak_assert(false);
|
||||||
return BufferIterator{};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
void Editor::insert(const String& str, InsertMode mode)
|
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)
|
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);
|
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.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();
|
check_invariant();
|
||||||
}
|
}
|
||||||
|
@ -112,15 +113,16 @@ void Editor::insert(const memoryview<String>& strings, InsertMode mode)
|
||||||
for (size_t i = 0; i < selections().size(); ++i)
|
for (size_t i = 0; i < selections().size(); ++i)
|
||||||
{
|
{
|
||||||
auto& sel = m_selections[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)];
|
const String& str = strings[std::min(i, strings.size()-1)];
|
||||||
m_buffer->insert(pos, str);
|
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.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();
|
check_invariant();
|
||||||
}
|
}
|
||||||
|
@ -129,7 +131,7 @@ std::vector<String> Editor::selections_content() const
|
||||||
{
|
{
|
||||||
std::vector<String> contents;
|
std::vector<String> contents;
|
||||||
for (auto& sel : m_selections)
|
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;
|
return contents;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -183,12 +185,11 @@ void Editor::move_selections(CharCount offset, SelectMode mode)
|
||||||
for (auto& sel : m_selections)
|
for (auto& sel : m_selections)
|
||||||
{
|
{
|
||||||
auto last = sel.last();
|
auto last = sel.last();
|
||||||
auto limit = offset < 0 ? buffer().iterator_at_line_begin(last)
|
last = clamp<BufferCoord>(m_buffer->char_advance(last, offset),
|
||||||
: utf8::previous(buffer().iterator_at_line_end(last));
|
last.line, m_buffer->char_prev(last.line+1));
|
||||||
last = utf8::advance(last, limit, offset);
|
|
||||||
sel.first() = mode == SelectMode::Extend ? sel.first() : last;
|
sel.first() = mode == SelectMode::Extend ? sel.first() : last;
|
||||||
sel.last() = last;
|
sel.last() = last;
|
||||||
avoid_eol(sel);
|
avoid_eol(*m_buffer, sel);
|
||||||
}
|
}
|
||||||
sort_and_merge_overlapping(m_selections, m_main_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);
|
kak_assert(mode == SelectMode::Replace or mode == SelectMode::Extend);
|
||||||
for (auto& sel : m_selections)
|
for (auto& sel : m_selections)
|
||||||
{
|
{
|
||||||
BufferCoord pos = sel.last().coord();
|
auto pos = sel.last();
|
||||||
CharCount column = utf8::distance(m_buffer->iterator_at_line_begin(pos.line), sel.last());
|
CharCount column = m_buffer->char_distance(pos.line, pos);
|
||||||
pos.line += offset;
|
pos.line += offset;
|
||||||
BufferIterator last = utf8::advance(m_buffer->iterator_at_line_begin(pos.line),
|
auto last = std::min(m_buffer->char_advance(pos.line, column),
|
||||||
m_buffer->iterator_at_line_end(pos.line)-1, column);
|
m_buffer->char_prev(pos.line+1));
|
||||||
sel.first() = mode == SelectMode::Extend ? sel.first() : last;
|
sel.first() = mode == SelectMode::Extend ? sel.first() : last;
|
||||||
sel.last() = last;
|
sel.last() = last;
|
||||||
avoid_eol(sel);
|
avoid_eol(*m_buffer, sel);
|
||||||
}
|
}
|
||||||
sort_and_merge_overlapping(m_selections, m_main_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& sel = m_selections[m_main_sel];
|
||||||
auto& pos = sel.last();
|
auto& pos = sel.last();
|
||||||
|
|
||||||
if (*pos == '\n' and not pos.is_begin() and *utf8::previous(pos) != '\n')
|
if (pos.column != 0 and pos.column == m_buffer->line_length(pos.line) - 1)
|
||||||
pos = utf8::previous(pos);
|
pos = m_buffer->char_prev(pos);
|
||||||
sel.first() = pos;
|
sel.first() = pos;
|
||||||
|
|
||||||
m_selections.erase(m_selections.begin(), m_selections.begin() + m_main_sel);
|
m_selections.erase(m_selections.begin(), m_selections.begin() + m_main_sel);
|
||||||
|
@ -392,21 +393,22 @@ private:
|
||||||
SelectionList m_ranges;
|
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()
|
return lhs.min() <= rhs.min() ? buffer.char_next(lhs.max()) >= rhs.min()
|
||||||
: lhs.min() <= utf8::next(rhs.max());
|
: lhs.min() <= buffer.char_next(rhs.max());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Editor::undo()
|
bool Editor::undo()
|
||||||
{
|
{
|
||||||
|
using namespace std::placeholders;
|
||||||
ModifiedRangesListener listener(buffer());
|
ModifiedRangesListener listener(buffer());
|
||||||
bool res = m_buffer->undo();
|
bool res = m_buffer->undo();
|
||||||
if (res and not listener.ranges().empty())
|
if (res and not listener.ranges().empty())
|
||||||
{
|
{
|
||||||
m_selections = std::move(listener.ranges());
|
m_selections = std::move(listener.ranges());
|
||||||
m_main_sel = m_selections.size() - 1;
|
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();
|
check_invariant();
|
||||||
return res;
|
return res;
|
||||||
|
@ -414,13 +416,14 @@ bool Editor::undo()
|
||||||
|
|
||||||
bool Editor::redo()
|
bool Editor::redo()
|
||||||
{
|
{
|
||||||
|
using namespace std::placeholders;
|
||||||
ModifiedRangesListener listener(buffer());
|
ModifiedRangesListener listener(buffer());
|
||||||
bool res = m_buffer->redo();
|
bool res = m_buffer->redo();
|
||||||
if (res and not listener.ranges().empty())
|
if (res and not listener.ranges().empty())
|
||||||
{
|
{
|
||||||
m_selections = std::move(listener.ranges());
|
m_selections = std::move(listener.ranges());
|
||||||
m_main_sel = m_selections.size() - 1;
|
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();
|
check_invariant();
|
||||||
return res;
|
return res;
|
||||||
|
@ -463,17 +466,17 @@ IncrementalInserter::IncrementalInserter(Editor& editor, InsertMode mode)
|
||||||
utf8_it first, last;
|
utf8_it first, last;
|
||||||
switch (mode)
|
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:
|
case InsertMode::Replace:
|
||||||
{
|
{
|
||||||
buffer.erase(sel.min(), utf8::next(sel.max()));
|
Kakoune::erase(buffer, sel);
|
||||||
first = last = sel.min();
|
first = last = buffer.iterator_at(sel.min());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case InsertMode::Append:
|
case InsertMode::Append:
|
||||||
{
|
{
|
||||||
first = sel.min();
|
first = buffer.iterator_at(sel.min());
|
||||||
last = std::max(sel.first(), sel.last());
|
last = buffer.iterator_at(sel.max());
|
||||||
// special case for end of lines, append to current line instead
|
// special case for end of lines, append to current line instead
|
||||||
auto coord = last.underlying_iterator().coord();
|
auto coord = last.underlying_iterator().coord();
|
||||||
if (coord.column != buffer.line_length(coord.line) - 1)
|
if (coord.column != buffer.line_length(coord.line) - 1)
|
||||||
|
@ -483,13 +486,13 @@ IncrementalInserter::IncrementalInserter(Editor& editor, InsertMode mode)
|
||||||
|
|
||||||
case InsertMode::OpenLineBelow:
|
case InsertMode::OpenLineBelow:
|
||||||
case InsertMode::AppendAtLineEnd:
|
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;
|
last = first;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case InsertMode::OpenLineAbove:
|
case InsertMode::OpenLineAbove:
|
||||||
case InsertMode::InsertAtLineBegin:
|
case InsertMode::InsertAtLineBegin:
|
||||||
first = buffer.iterator_at_line_begin(sel.min());
|
first = buffer.iterator_at(sel.min().line);
|
||||||
if (mode == InsertMode::OpenLineAbove)
|
if (mode == InsertMode::OpenLineAbove)
|
||||||
--first;
|
--first;
|
||||||
else
|
else
|
||||||
|
@ -534,9 +537,9 @@ IncrementalInserter::~IncrementalInserter()
|
||||||
{
|
{
|
||||||
for (auto& sel : m_editor.m_selections)
|
for (auto& sel : m_editor.m_selections)
|
||||||
{
|
{
|
||||||
if (m_mode == InsertMode::Append and sel.last().column() > 0)
|
if (m_mode == InsertMode::Append and sel.last().column > 0)
|
||||||
sel.last() = utf8::previous(sel.last());
|
sel.last() = m_editor.buffer().char_prev(sel.last());
|
||||||
avoid_eol(sel);
|
avoid_eol(m_editor.buffer(), sel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -564,8 +567,8 @@ void IncrementalInserter::erase()
|
||||||
{
|
{
|
||||||
for (auto& sel : m_editor.m_selections)
|
for (auto& sel : m_editor.m_selections)
|
||||||
{
|
{
|
||||||
BufferIterator pos = sel.last();
|
BufferCoord pos = sel.last();
|
||||||
m_editor.buffer().erase(utf8::previous(pos), pos);
|
m_editor.buffer().erase(m_editor.buffer().char_prev(pos), pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ void preserve_indent(Buffer& buffer, Selection& selection, String& content)
|
||||||
{
|
{
|
||||||
if (content == "\n")
|
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);
|
auto first_non_white = buffer.iterator_at(line_begin);
|
||||||
while ((*first_non_white == '\t' or *first_non_white == ' ') and
|
while ((*first_non_white == '\t' or *first_non_white == ' ') and
|
||||||
not first_non_white.is_end())
|
not first_non_white.is_end())
|
||||||
|
@ -82,12 +82,12 @@ struct RegexFilter
|
||||||
String suffix(it+1, content.end());
|
String suffix(it+1, content.end());
|
||||||
content = String(content.begin(), it-1);
|
content = String(content.begin(), it-1);
|
||||||
|
|
||||||
auto first = selection.first();
|
auto& first = selection.first();
|
||||||
auto last = selection.last();
|
auto& last = selection.last();
|
||||||
buffer.insert(position, suffix);
|
buffer.insert(position, suffix);
|
||||||
if (selection.first() == selection.last())
|
if (first == last)
|
||||||
selection.first() -= suffix.length();
|
first = buffer.advance(first, -suffix.length());
|
||||||
selection.last() -= suffix.length();
|
last = buffer.advance(last, -suffix.length());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -280,22 +280,23 @@ void show_line_numbers(const Window& window, DisplayBuffer& display_buffer)
|
||||||
void highlight_selections(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 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)
|
for (size_t i = 0; i < window.selections().size(); ++i)
|
||||||
{
|
{
|
||||||
auto& sel = window.selections()[i];
|
auto& sel = window.selections()[i];
|
||||||
const bool forward = sel.first() <= sel.last();
|
const bool forward = sel.first() <= sel.last();
|
||||||
BufferIterator begin = forward ? sel.first() : utf8::next(sel.last());
|
BufferCoord begin = forward ? sel.first() : buffer.char_next(sel.last());
|
||||||
BufferIterator end = forward ? sel.last() : utf8::next(sel.first());
|
BufferCoord end = forward ? sel.last() : buffer.char_next(sel.first());
|
||||||
|
|
||||||
const bool primary = (i == window.main_selection_index());
|
const bool primary = (i == window.main_selection_index());
|
||||||
if (not only_cursor)
|
if (not only_cursor)
|
||||||
{
|
{
|
||||||
ColorPair sel_colors = get_color(primary ? "PrimarySelection" : "SecondarySelection");
|
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; });
|
[&](DisplayAtom& atom) { atom.colors = sel_colors; });
|
||||||
}
|
}
|
||||||
ColorPair cur_colors = get_color(primary ? "PrimaryCursor" : "SecondaryCursor");
|
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; });
|
[&](DisplayAtom& atom) { atom.colors = cur_colors; });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -610,11 +610,13 @@ public:
|
||||||
for (auto& sel : m_context.editor().selections())
|
for (auto& sel : m_context.editor().selections())
|
||||||
{
|
{
|
||||||
auto offset = buffer.offset(sel.last());
|
auto offset = buffer.offset(sel.last());
|
||||||
|
auto pos = buffer.iterator_at(sel.last());
|
||||||
if (offset >= beg_offset and offset + end_offset < buffer_len and
|
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);
|
auto beg = pos - beg_offset;
|
||||||
buffer.insert(sel.last(), candidate);
|
buffer.erase(beg, pos + end_offset);
|
||||||
|
buffer.insert(beg, candidate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -68,7 +68,7 @@ void register_env_vars()
|
||||||
shell_manager.register_env_var("selection",
|
shell_manager.register_env_var("selection",
|
||||||
[](const String& name, const Context& context)
|
[](const String& name, const Context& context)
|
||||||
{ const Range& sel = context.editor().main_selection();
|
{ 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",
|
shell_manager.register_env_var("selections",
|
||||||
[](const String& name, const Context& context)
|
[](const String& name, const Context& context)
|
||||||
{ auto sels = context.editor().selections_content();
|
{ auto sels = context.editor().selections_content();
|
||||||
|
@ -91,15 +91,15 @@ void register_env_vars()
|
||||||
{ return ClientManager::instance().get_client(context).name(); });
|
{ return ClientManager::instance().get_client(context).name(); });
|
||||||
shell_manager.register_env_var("cursor_line",
|
shell_manager.register_env_var("cursor_line",
|
||||||
[](const String& name, const Context& context)
|
[](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",
|
shell_manager.register_env_var("cursor_column",
|
||||||
[](const String& name, const Context& context)
|
[](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",
|
shell_manager.register_env_var("selection_desc",
|
||||||
[](const String& name, const Context& context)
|
[](const String& name, const Context& context)
|
||||||
{ auto& sel = context.editor().main_selection();
|
{ auto& sel = context.editor().main_selection();
|
||||||
auto beg = sel.min();
|
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); });
|
to_string((int)context.buffer().distance(beg, sel.max())+1); });
|
||||||
shell_manager.register_env_var("window_width",
|
shell_manager.register_env_var("window_width",
|
||||||
[](const String& name, const Context& context)
|
[](const String& name, const Context& context)
|
||||||
|
|
|
@ -136,14 +136,15 @@ void goto_commands(Context& context)
|
||||||
case 'f':
|
case 'f':
|
||||||
{
|
{
|
||||||
const Range& sel = context.editor().main_selection();
|
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' };
|
static constexpr char forbidden[] = { '\'', '\\', '\0' };
|
||||||
for (auto c : forbidden)
|
for (auto c : forbidden)
|
||||||
if (contains(filename, c))
|
if (contains(filename, c))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto paths = context.options()["path"].get<std::vector<String>>();
|
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), '/');
|
auto it = find(reversed(buffer_name), '/');
|
||||||
if (it != buffer_name.rend())
|
if (it != buffer_name.rend())
|
||||||
paths.insert(paths.begin(), String{buffer_name.begin(), it.base()});
|
paths.insert(paths.begin(), String{buffer_name.begin(), it.base()});
|
||||||
|
@ -248,7 +249,7 @@ void pipe(Context& context)
|
||||||
Editor& editor = context.editor();
|
Editor& editor = context.editor();
|
||||||
std::vector<String> strings;
|
std::vector<String> strings;
|
||||||
for (auto& sel : context.editor().selections())
|
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, {},
|
cmdline, context, {},
|
||||||
EnvVarMap{}));
|
EnvVarMap{}));
|
||||||
editor.insert(strings, InsertMode::Replace);
|
editor.insert(strings, InsertMode::Replace);
|
||||||
|
@ -332,20 +333,21 @@ void use_selection_as_search_pattern(Context& context)
|
||||||
{
|
{
|
||||||
std::vector<String> patterns;
|
std::vector<String> patterns;
|
||||||
auto& sels = context.editor().selections();
|
auto& sels = context.editor().selections();
|
||||||
|
const auto& buffer = context.buffer();
|
||||||
for (auto& sel : sels)
|
for (auto& sel : sels)
|
||||||
{
|
{
|
||||||
auto begin = sel.min();
|
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";
|
auto content = "\\Q" + context.buffer().string(begin, end) + "\\E";
|
||||||
if (smart)
|
if (smart)
|
||||||
{
|
{
|
||||||
if (begin.is_begin() or
|
if (begin == BufferCoord{0,0} or
|
||||||
(is_word(utf8::codepoint(begin)) and not
|
(is_word(buffer.char_at(begin)) and not
|
||||||
is_word(utf8::codepoint(utf8::previous(begin)))))
|
is_word(buffer.char_at(buffer.char_prev(begin)))))
|
||||||
content = "\\b" + content;
|
content = "\\b" + content;
|
||||||
if (end.is_end() or
|
if (buffer.is_end(end) or
|
||||||
(is_word(utf8::codepoint(utf8::previous(end))) and not
|
(is_word(buffer.char_at(buffer.char_prev(end))) and not
|
||||||
is_word(utf8::codepoint(end))))
|
is_word(buffer.char_at(end))))
|
||||||
content = content + "\\b";
|
content = content + "\\b";
|
||||||
}
|
}
|
||||||
patterns.push_back(std::move(content));
|
patterns.push_back(std::move(content));
|
||||||
|
@ -371,7 +373,7 @@ void cat_yank(Context& context)
|
||||||
to_string(sels.size()) + " selections", get_color("Information") });
|
to_string(sels.size()) + " selections", get_color("Information") });
|
||||||
}
|
}
|
||||||
|
|
||||||
void erase(Context& context)
|
void erase_selections(Context& context)
|
||||||
{
|
{
|
||||||
RegisterManager::instance()['"'] = context.editor().selections_content();
|
RegisterManager::instance()['"'] = context.editor().selections_content();
|
||||||
context.editor().erase();
|
context.editor().erase();
|
||||||
|
@ -478,7 +480,7 @@ void join_select_spaces(Context& context)
|
||||||
kak_assert(std::is_sorted(res.begin(), res.end(),
|
kak_assert(std::is_sorted(res.begin(), res.end(),
|
||||||
[](const Selection& lhs, const Selection& rhs)
|
[](const Selection& lhs, const Selection& rhs)
|
||||||
{ return lhs.min() < rhs.min(); }));
|
{ 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();
|
res.pop_back();
|
||||||
return res;
|
return res;
|
||||||
});
|
});
|
||||||
|
@ -499,11 +501,13 @@ void keep(Context& context)
|
||||||
constexpr const char* prompt = matching ? "keep matching:" : "keep not matching:";
|
constexpr const char* prompt = matching ? "keep matching:" : "keep not matching:";
|
||||||
regex_prompt(context, prompt, [](const Regex& ex, Context& context) {
|
regex_prompt(context, prompt, [](const Regex& ex, Context& context) {
|
||||||
Editor& editor = context.editor();
|
Editor& editor = context.editor();
|
||||||
|
const Buffer& buffer = context.buffer();
|
||||||
SelectionList sels = editor.selections();
|
SelectionList sels = editor.selections();
|
||||||
SelectionList keep;
|
SelectionList keep;
|
||||||
for (auto& sel : sels)
|
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);
|
keep.push_back(sel);
|
||||||
}
|
}
|
||||||
if (keep.empty())
|
if (keep.empty())
|
||||||
|
@ -703,8 +707,8 @@ void align(Context& context)
|
||||||
{
|
{
|
||||||
auto& selections = context.editor().selections();
|
auto& selections = context.editor().selections();
|
||||||
auto& buffer = context.buffer();
|
auto& buffer = context.buffer();
|
||||||
auto get_column = [&buffer](const BufferIterator& it)
|
auto get_column = [&buffer](const BufferCoord& coord)
|
||||||
{ return utf8::distance(buffer.iterator_at_line_begin(it), it); };
|
{ return buffer.char_distance({coord.line, 0}, coord); };
|
||||||
|
|
||||||
CharCount max_col = 0;
|
CharCount max_col = 0;
|
||||||
for (auto& sel : selections)
|
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, '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::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, 'c' }, change },
|
||||||
{ { Key::Modifiers::None, 'i' }, insert<InsertMode::Insert> },
|
{ { Key::Modifiers::None, 'i' }, insert<InsertMode::Insert> },
|
||||||
{ { Key::Modifiers::None, 'I' }, insert<InsertMode::InsertAtLineBegin> },
|
{ { Key::Modifiers::None, 'I' }, insert<InsertMode::InsertAtLineBegin> },
|
||||||
|
|
|
@ -22,9 +22,9 @@ void on_buffer_change(const Buffer& buffer, SelectionList& sels,
|
||||||
const BufferCoord& begin, const BufferCoord& end, LineCount end_line)
|
const BufferCoord& begin, const BufferCoord& end, LineCount end_line)
|
||||||
{
|
{
|
||||||
auto update_beg = std::lower_bound(sels.begin(), sels.end(), begin,
|
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,
|
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)
|
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>
|
template<bool assume_different_line, bool assume_greater_than_begin>
|
||||||
struct UpdateInsert
|
struct UpdateInsert
|
||||||
{
|
{
|
||||||
void operator()(const Buffer& buffer, BufferIterator& it,
|
void operator()(const Buffer& buffer, BufferCoord& coord,
|
||||||
const BufferCoord& begin, const BufferCoord& end) const
|
const BufferCoord& begin, const BufferCoord& end) const
|
||||||
{
|
{
|
||||||
auto coord = it.coord();
|
|
||||||
if (assume_different_line)
|
if (assume_different_line)
|
||||||
kak_assert(begin.line < coord.line);
|
kak_assert(begin.line < coord.line);
|
||||||
if (not assume_greater_than_begin and coord < begin)
|
if (not assume_greater_than_begin and coord < begin)
|
||||||
|
@ -62,17 +61,15 @@ struct UpdateInsert
|
||||||
coord.column = end.column + coord.column - begin.column;
|
coord.column = end.column + coord.column - begin.column;
|
||||||
|
|
||||||
coord.line += end.line - begin.line;
|
coord.line += end.line - begin.line;
|
||||||
it = coord;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template<bool assume_different_line, bool assume_greater_than_begin>
|
template<bool assume_different_line, bool assume_greater_than_begin>
|
||||||
struct UpdateErase
|
struct UpdateErase
|
||||||
{
|
{
|
||||||
void operator()(const Buffer& buffer, BufferIterator& it,
|
void operator()(const Buffer& buffer, BufferCoord& coord,
|
||||||
const BufferCoord& begin, const BufferCoord& end) const
|
const BufferCoord& begin, const BufferCoord& end) const
|
||||||
{
|
{
|
||||||
auto coord = it.coord();
|
|
||||||
if (not assume_greater_than_begin and coord < begin)
|
if (not assume_greater_than_begin and coord < begin)
|
||||||
return;
|
return;
|
||||||
if (assume_different_line)
|
if (assume_different_line)
|
||||||
|
@ -89,7 +86,6 @@ struct UpdateErase
|
||||||
else
|
else
|
||||||
coord.line -= end.line - begin.line;
|
coord.line -= end.line - begin.line;
|
||||||
}
|
}
|
||||||
it = coord;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -10,28 +10,28 @@ namespace Kakoune
|
||||||
struct Range
|
struct Range
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Range(const BufferIterator& first, const BufferIterator& last)
|
Range(const BufferCoord& first, const BufferCoord& last)
|
||||||
: m_first{first}, m_last{last} {}
|
: m_first{first}, m_last{last} {}
|
||||||
|
|
||||||
void merge_with(const Range& range);
|
void merge_with(const Range& range);
|
||||||
|
|
||||||
BufferIterator& first() { return m_first; }
|
BufferCoord& first() { return m_first; }
|
||||||
BufferIterator& last() { return m_last; }
|
BufferCoord& last() { return m_last; }
|
||||||
|
|
||||||
const BufferIterator& first() const { return m_first; }
|
const BufferCoord& first() const { return m_first; }
|
||||||
const BufferIterator& last() const { return m_last; }
|
const BufferCoord& last() const { return m_last; }
|
||||||
|
|
||||||
bool operator== (const Range& other) const
|
bool operator== (const Range& other) const
|
||||||
{
|
{
|
||||||
return m_first == other.m_first and m_last == other.m_last;
|
return m_first == other.m_first and m_last == other.m_last;
|
||||||
}
|
}
|
||||||
|
|
||||||
const BufferIterator& min() const { return std::min(m_first, m_last); }
|
const BufferCoord& min() const { return std::min(m_first, m_last); }
|
||||||
const BufferIterator& max() const { return std::max(m_first, m_last); }
|
const BufferCoord& max() const { return std::max(m_first, m_last); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
BufferIterator m_first;
|
BufferCoord m_first;
|
||||||
BufferIterator m_last;
|
BufferCoord m_last;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline bool overlaps(const Range& lhs, const Range& rhs)
|
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();
|
: 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>;
|
using CaptureList = std::vector<String>;
|
||||||
|
|
||||||
// A selection is a Range, associated with a CaptureList
|
// A selection is a Range, associated with a CaptureList
|
||||||
struct Selection : public Range
|
struct Selection : public Range
|
||||||
{
|
{
|
||||||
Selection(const BufferIterator& first, const BufferIterator& last,
|
Selection(const BufferCoord& first, const BufferCoord& last,
|
||||||
CaptureList captures = {})
|
CaptureList captures = {})
|
||||||
: Range(first, last), m_captures(std::move(captures)) {}
|
: Range(first, last), m_captures(std::move(captures)) {}
|
||||||
|
|
||||||
|
|
|
@ -85,7 +85,7 @@ typedef boost::regex_iterator<BufferIterator> RegexIterator;
|
||||||
template<bool punctuation_is_word>
|
template<bool punctuation_is_word>
|
||||||
Selection select_to_next_word(const Buffer& buffer, const Selection& selection)
|
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) !=
|
if (categorize<punctuation_is_word>(*begin) !=
|
||||||
categorize<punctuation_is_word>(*(begin+1)))
|
categorize<punctuation_is_word>(*(begin+1)))
|
||||||
++begin;
|
++begin;
|
||||||
|
@ -108,7 +108,7 @@ template Selection select_to_next_word<true>(const Buffer&, const Selection&);
|
||||||
template<bool punctuation_is_word>
|
template<bool punctuation_is_word>
|
||||||
Selection select_to_next_word_end(const Buffer& buffer, const Selection& selection)
|
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) !=
|
if (categorize<punctuation_is_word>(*begin) !=
|
||||||
categorize<punctuation_is_word>(*(begin+1)))
|
categorize<punctuation_is_word>(*(begin+1)))
|
||||||
++begin;
|
++begin;
|
||||||
|
@ -130,7 +130,7 @@ template Selection select_to_next_word_end<true>(const Buffer&, const Selection&
|
||||||
template<bool punctuation_is_word>
|
template<bool punctuation_is_word>
|
||||||
Selection select_to_previous_word(const Buffer& buffer, const Selection& selection)
|
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) !=
|
if (categorize<punctuation_is_word>(*begin) !=
|
||||||
categorize<punctuation_is_word>(*(begin-1)))
|
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)
|
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))
|
if (*first == '\n' and not is_end(first + 1))
|
||||||
++first;
|
++first;
|
||||||
|
|
||||||
|
@ -175,7 +175,7 @@ Selection select_line(const Buffer& buffer, const Selection& selection)
|
||||||
Selection select_matching(const Buffer& buffer, const Selection& selection)
|
Selection select_matching(const Buffer& buffer, const Selection& selection)
|
||||||
{
|
{
|
||||||
std::vector<Codepoint> matching_pairs = { '(', ')', '{', '}', '[', ']', '<', '>' };
|
std::vector<Codepoint> matching_pairs = { '(', ')', '{', '}', '[', ']', '<', '>' };
|
||||||
Utf8Iterator it = selection.last();
|
Utf8Iterator it = buffer.iterator_at(selection.last());
|
||||||
std::vector<Codepoint>::iterator match = matching_pairs.end();
|
std::vector<Codepoint>::iterator match = matching_pairs.end();
|
||||||
while (not is_eol(*it))
|
while (not is_eol(*it))
|
||||||
{
|
{
|
||||||
|
@ -289,15 +289,15 @@ Selection select_surrounding(const Buffer& buffer, const Selection& selection,
|
||||||
const CodepointPair& matching,
|
const CodepointPair& matching,
|
||||||
ObjectFlags flags)
|
ObjectFlags flags)
|
||||||
{
|
{
|
||||||
auto res = find_surrounding(selection.last(), matching, flags);
|
auto res = find_surrounding(buffer.iterator_at(selection.last()), matching, flags);
|
||||||
if (not res)
|
if (not res)
|
||||||
return selection;
|
return selection;
|
||||||
|
|
||||||
if (flags == (ObjectFlags::ToBegin | ObjectFlags::ToEnd) and
|
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 == 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 ? Selection{*res} : selection;
|
||||||
}
|
}
|
||||||
return *res;
|
return *res;
|
||||||
|
@ -306,7 +306,7 @@ Selection select_surrounding(const Buffer& buffer, const Selection& selection,
|
||||||
Selection select_to(const Buffer& buffer, const Selection& selection,
|
Selection select_to(const Buffer& buffer, const Selection& selection,
|
||||||
Codepoint c, int count, bool inclusive)
|
Codepoint c, int count, bool inclusive)
|
||||||
{
|
{
|
||||||
Utf8Iterator begin = selection.last();
|
Utf8Iterator begin = buffer.iterator_at(selection.last());
|
||||||
Utf8Iterator end = begin;
|
Utf8Iterator end = begin;
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
|
@ -323,7 +323,7 @@ Selection select_to(const Buffer& buffer, const Selection& selection,
|
||||||
Selection select_to_reverse(const Buffer& buffer, const Selection& selection,
|
Selection select_to_reverse(const Buffer& buffer, const Selection& selection,
|
||||||
Codepoint c, int count, bool inclusive)
|
Codepoint c, int count, bool inclusive)
|
||||||
{
|
{
|
||||||
Utf8Iterator begin = selection.last();
|
Utf8Iterator begin = buffer.iterator_at(selection.last());
|
||||||
Utf8Iterator end = begin;
|
Utf8Iterator end = begin;
|
||||||
do
|
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)
|
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;
|
Utf8Iterator end = begin + 1;
|
||||||
skip_while(end, [](Codepoint cur) { return not is_eol(cur); });
|
skip_while(end, [](Codepoint cur) { return not is_eol(cur); });
|
||||||
return utf8_range(begin, end-1);
|
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)
|
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;
|
Utf8Iterator end = begin - 1;
|
||||||
skip_while_reverse(end, [](Codepoint cur) { return not is_eol(cur); });
|
skip_while_reverse(end, [](Codepoint cur) { return not is_eol(cur); });
|
||||||
return utf8_range(begin, is_begin(end) ? end : end+1);
|
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>
|
template<bool punctuation_is_word>
|
||||||
Selection select_whole_word(const Buffer& buffer, const Selection& selection, ObjectFlags flags)
|
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;
|
Utf8Iterator last = first;
|
||||||
if (is_word<punctuation_is_word>(*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)
|
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;
|
BufferIterator last = first;
|
||||||
|
|
||||||
if (flags & ObjectFlags::ToBegin)
|
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)
|
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;
|
BufferIterator last = first;
|
||||||
|
|
||||||
if (flags & ObjectFlags::ToBegin and not is_begin(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)
|
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
|
// no need to be utf8 aware for is_eol as we only use \n as line seperator
|
||||||
BufferIterator first = selection.first();
|
BufferIterator first = buffer.iterator_at(selection.first());
|
||||||
BufferIterator last = selection.last();
|
BufferIterator last = buffer.iterator_at(selection.last());
|
||||||
BufferIterator& to_line_start = first <= last ? first : last;
|
BufferIterator& to_line_start = first <= last ? first : last;
|
||||||
BufferIterator& to_line_end = first <= last ? last : first;
|
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)
|
Selection trim_partial_lines(const Buffer& buffer, const Selection& selection)
|
||||||
{
|
{
|
||||||
// same as select_whole_lines
|
// same as select_whole_lines
|
||||||
BufferIterator first = selection.first();
|
BufferIterator first = buffer.iterator_at(selection.first());
|
||||||
BufferIterator last = selection.last();
|
BufferIterator last = buffer.iterator_at(selection.last());
|
||||||
BufferIterator& to_line_start = first <= last ? first : last;
|
BufferIterator& to_line_start = first <= last ? first : last;
|
||||||
BufferIterator& to_line_end = first <= last ? last : first;
|
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
|
// regex matching do not use Utf8Iterator as boost::regex handle utf8
|
||||||
// decoding itself
|
// decoding itself
|
||||||
BufferIterator begin = selection.last();
|
BufferIterator begin = buffer.iterator_at(selection.last());
|
||||||
BufferIterator end = begin;
|
BufferIterator end = begin;
|
||||||
CaptureList captures;
|
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)
|
SelectionList select_all_matches(const Buffer& buffer, const Selection& selection, const Regex& regex)
|
||||||
{
|
{
|
||||||
auto sel_end = utf8::next(selection.max());
|
auto sel_end = utf8::next(buffer.iterator_at(selection.max()));
|
||||||
RegexIterator re_it(selection.min(), sel_end, regex);
|
RegexIterator re_it(buffer.iterator_at(selection.min()), sel_end, regex);
|
||||||
RegexIterator re_end;
|
RegexIterator re_end;
|
||||||
|
|
||||||
SelectionList result;
|
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,
|
SelectionList split_selection(const Buffer& buffer, const Selection& selection,
|
||||||
const Regex& regex)
|
const Regex& regex)
|
||||||
{
|
{
|
||||||
auto sel_end = utf8::next(selection.max());
|
auto begin = buffer.iterator_at(selection.min());
|
||||||
RegexIterator re_it(selection.min(), sel_end, regex,
|
auto sel_end = utf8::next(buffer.iterator_at(selection.max()));
|
||||||
boost::regex_constants::match_nosubs);
|
RegexIterator re_it(begin, sel_end, regex, boost::regex_constants::match_nosubs);
|
||||||
RegexIterator re_end;
|
RegexIterator re_end;
|
||||||
|
|
||||||
SelectionList result;
|
SelectionList result;
|
||||||
BufferIterator begin = selection.min();
|
|
||||||
for (; re_it != re_end; ++re_it)
|
for (; re_it != re_end; ++re_it)
|
||||||
{
|
{
|
||||||
BufferIterator end = (*re_it)[0].first;
|
BufferIterator end = (*re_it)[0].first;
|
||||||
|
|
|
@ -62,8 +62,8 @@ void test_editor()
|
||||||
editor.multi_select(std::bind(select_all_matches, _1, _2, Regex{"\\n\\h*"}));
|
editor.multi_select(std::bind(select_all_matches, _1, _2, Regex{"\\n\\h*"}));
|
||||||
for (auto& sel : editor.selections())
|
for (auto& sel : editor.selections())
|
||||||
{
|
{
|
||||||
kak_assert(*sel.min() == '\n');
|
kak_assert(buffer.byte_at(sel.min()) == '\n');
|
||||||
editor.buffer().erase(sel.min(), utf8::next(sel.max()));
|
erase(buffer, sel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
editor.undo();
|
editor.undo();
|
||||||
|
@ -71,7 +71,7 @@ void test_editor()
|
||||||
Selection sel{ buffer.iterator_at_line_begin(2_line), buffer.end()-1 };
|
Selection sel{ buffer.iterator_at_line_begin(2_line), buffer.end()-1 };
|
||||||
editor.select(sel, SelectMode::Replace);
|
editor.select(sel, SelectMode::Replace);
|
||||||
editor.insert("",InsertMode::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()
|
void test_incremental_inserter()
|
||||||
|
|
|
@ -42,7 +42,7 @@ void Window::display_selection_at(LineCount line)
|
||||||
{
|
{
|
||||||
if (line >= 0 or line < m_dimensions.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);
|
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;
|
LineCount buffer_line = m_position.line + line;
|
||||||
if (buffer_line >= buffer().line_count())
|
if (buffer_line >= buffer().line_count())
|
||||||
break;
|
break;
|
||||||
BufferIterator line_begin = buffer().iterator_at_line_begin(buffer_line);
|
BufferCoord limit{buffer_line+1, 0};
|
||||||
BufferIterator line_end = buffer().iterator_at_line_end(buffer_line);
|
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);
|
||||||
BufferIterator begin = utf8::advance(line_begin, line_end, (int)m_position.column);
|
|
||||||
BufferIterator end = utf8::advance(begin, line_end, (int)m_dimensions.column);
|
|
||||||
|
|
||||||
lines.push_back(DisplayLine(buffer_line));
|
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();
|
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()
|
void Window::scroll_to_keep_cursor_visible_ifn()
|
||||||
{
|
{
|
||||||
const BufferIterator first = main_selection().first();
|
const auto& first = main_selection().first();
|
||||||
const BufferIterator last = main_selection().last();
|
const auto& last = main_selection().last();
|
||||||
|
|
||||||
const LineCount offset = std::min<LineCount>(options()["scrolloff"].get<int>(),
|
const LineCount offset = std::min<LineCount>(options()["scrolloff"].get<int>(),
|
||||||
(m_dimensions.line - 1) / 2);
|
(m_dimensions.line - 1) / 2);
|
||||||
|
|
||||||
// scroll lines if needed, try to get as much of the selection visible as possible
|
// 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_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());
|
m_dimensions.line, buffer().line_count());
|
||||||
|
|
||||||
// highlight only the line containing the cursor
|
// highlight only the line containing the cursor
|
||||||
DisplayBuffer display_buffer;
|
DisplayBuffer display_buffer;
|
||||||
DisplayBuffer::LineList& lines = display_buffer.lines();
|
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);
|
lines.back().push_back(DisplayAtom(AtomContent(buffer(), last.line, last.line+1)));
|
||||||
BufferIterator line_end = buffer().iterator_at_line_end(last);
|
|
||||||
lines.back().push_back(DisplayAtom(AtomContent(buffer(), line_begin.coord(), line_end.coord())));
|
|
||||||
|
|
||||||
display_buffer.compute_range();
|
display_buffer.compute_range();
|
||||||
m_highlighters(*this, display_buffer);
|
m_highlighters(*this, display_buffer);
|
||||||
|
@ -145,21 +141,21 @@ void Window::scroll_to_keep_cursor_visible_ifn()
|
||||||
for (auto& atom : lines.back())
|
for (auto& atom : lines.back())
|
||||||
{
|
{
|
||||||
if (atom.content.has_buffer_range() and
|
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)
|
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
|
else
|
||||||
column += atom.content.content().char_length();
|
column += atom.content.content().char_length();
|
||||||
|
|
||||||
CharCount first_col = first.line() == last.line() ?
|
CharCount first_col = first.line == last.line ?
|
||||||
utf8::distance(line_begin, first) : 0_char;
|
buffer().char_distance(last.line, first) : 0_char;
|
||||||
if (first_col < m_position.column)
|
if (first_col < m_position.column)
|
||||||
m_position.column = first_col;
|
m_position.column = first_col;
|
||||||
else if (column >= m_position.column + m_dimensions.column)
|
else if (column >= m_position.column + m_dimensions.column)
|
||||||
m_position.column = column - (m_dimensions.column - 1);
|
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)
|
if (last_col < m_position.column)
|
||||||
m_position.column = last_col;
|
m_position.column = last_col;
|
||||||
else if (column >= m_position.column + m_dimensions.column)
|
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();
|
column += atom.content.content().char_length();
|
||||||
}
|
}
|
||||||
if (last != buffer().end())
|
if (not buffer().is_end(last))
|
||||||
{
|
{
|
||||||
// the cursor should always be visible.
|
// the cursor should always be visible.
|
||||||
kak_assert(false);
|
kak_assert(false);
|
||||||
|
|
Loading…
Reference in New Issue
Block a user