2012-01-31 20:12:06 +01:00
|
|
|
#include "editor.hh"
|
|
|
|
|
|
|
|
#include "exception.hh"
|
|
|
|
#include "utils.hh"
|
2012-02-10 00:47:55 +01:00
|
|
|
#include "register.hh"
|
|
|
|
#include "register_manager.hh"
|
2012-01-31 20:12:06 +01:00
|
|
|
|
2012-02-10 14:58:29 +01:00
|
|
|
#include <array>
|
|
|
|
|
2012-01-31 20:12:06 +01:00
|
|
|
namespace Kakoune
|
|
|
|
{
|
|
|
|
|
|
|
|
Editor::Editor(Buffer& buffer)
|
|
|
|
: m_buffer(buffer),
|
2012-02-08 00:41:10 +01:00
|
|
|
m_edition_level(0)
|
2012-01-31 20:12:06 +01:00
|
|
|
{
|
|
|
|
m_selections.push_back(SelectionList());
|
2012-02-10 00:47:55 +01:00
|
|
|
m_selections.back().push_back(Selection(buffer.begin(), buffer.begin()));
|
2012-01-31 20:12:06 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void Editor::erase()
|
|
|
|
{
|
2012-02-08 00:41:10 +01:00
|
|
|
scoped_edition edition(*this);
|
2012-01-31 20:12:06 +01:00
|
|
|
for (auto& sel : selections())
|
|
|
|
m_buffer.modify(Modification::make_erase(sel.begin(), sel.end()));
|
|
|
|
}
|
|
|
|
|
2012-02-10 00:47:55 +01:00
|
|
|
template<bool append>
|
2012-03-08 22:23:29 +01:00
|
|
|
static void do_insert(Editor& editor, const String& string)
|
2012-02-10 00:47:55 +01:00
|
|
|
{
|
|
|
|
scoped_edition edition(editor);
|
|
|
|
for (auto& sel : editor.selections())
|
|
|
|
{
|
|
|
|
BufferIterator pos = append ? sel.end() : sel.begin();
|
|
|
|
editor.buffer().modify(Modification::make_insert(pos, string));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template<bool append>
|
2012-03-08 22:23:29 +01:00
|
|
|
static void do_insert(Editor& editor, const memoryview<String>& strings)
|
2012-02-10 00:47:55 +01:00
|
|
|
{
|
|
|
|
if (strings.empty())
|
|
|
|
return;
|
|
|
|
|
|
|
|
scoped_edition edition(editor);
|
|
|
|
for (size_t i = 0; i < editor.selections().size(); ++i)
|
|
|
|
{
|
|
|
|
BufferIterator pos = append ? editor.selections()[i].end()
|
|
|
|
: editor.selections()[i].begin();
|
|
|
|
size_t index = std::min(i, strings.size()-1);
|
|
|
|
editor.buffer().modify(Modification::make_insert(pos, strings[index]));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-01-31 20:12:06 +01:00
|
|
|
void Editor::insert(const String& string)
|
|
|
|
{
|
2012-02-10 00:47:55 +01:00
|
|
|
do_insert<false>(*this, string);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Editor::insert(const memoryview<String>& strings)
|
|
|
|
{
|
|
|
|
do_insert<false>(*this, strings);
|
2012-01-31 20:12:06 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void Editor::append(const String& string)
|
|
|
|
{
|
2012-02-10 00:47:55 +01:00
|
|
|
do_insert<true>(*this, string);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Editor::append(const memoryview<String>& strings)
|
|
|
|
{
|
|
|
|
do_insert<true>(*this, strings);
|
2012-01-31 20:12:06 +01:00
|
|
|
}
|
|
|
|
|
2012-04-14 03:17:09 +02:00
|
|
|
void Editor::replace(const String& string)
|
2012-01-31 20:12:06 +01:00
|
|
|
{
|
2012-02-08 00:41:10 +01:00
|
|
|
scoped_edition edition(*this);
|
|
|
|
erase();
|
|
|
|
insert(string);
|
2012-01-31 20:12:06 +01:00
|
|
|
}
|
|
|
|
|
2012-03-08 22:23:29 +01:00
|
|
|
std::vector<String> Editor::selections_content() const
|
2012-02-10 00:47:55 +01:00
|
|
|
{
|
|
|
|
std::vector<String> contents;
|
|
|
|
for (auto& sel : selections())
|
|
|
|
contents.push_back(m_buffer.string(sel.begin(), sel.end()));
|
|
|
|
return contents;
|
|
|
|
}
|
|
|
|
|
2012-01-31 20:12:06 +01:00
|
|
|
void Editor::push_selections()
|
|
|
|
{
|
2012-02-10 00:47:55 +01:00
|
|
|
SelectionList current_selections = m_selections.back();
|
2012-01-31 20:12:06 +01:00
|
|
|
m_selections.push_back(std::move(current_selections));
|
|
|
|
}
|
|
|
|
|
|
|
|
void Editor::pop_selections()
|
|
|
|
{
|
|
|
|
if (m_selections.size() > 1)
|
|
|
|
m_selections.pop_back();
|
|
|
|
else
|
|
|
|
throw runtime_error("no more selections on stack");
|
|
|
|
}
|
|
|
|
|
|
|
|
void Editor::move_selections(const BufferCoord& offset, bool append)
|
|
|
|
{
|
2012-02-10 00:47:55 +01:00
|
|
|
for (auto& sel : m_selections.back())
|
2012-01-31 20:12:06 +01:00
|
|
|
{
|
|
|
|
BufferCoord pos = m_buffer.line_and_column_at(sel.last());
|
|
|
|
BufferIterator last = m_buffer.iterator_at(pos + BufferCoord(offset));
|
|
|
|
sel = Selection(append ? sel.first() : last, last);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Editor::clear_selections()
|
|
|
|
{
|
|
|
|
check_invariant();
|
|
|
|
BufferIterator pos = selections().back().last();
|
|
|
|
|
|
|
|
if (*pos == '\n' and not pos.is_begin() and *(pos-1) != '\n')
|
|
|
|
--pos;
|
|
|
|
|
|
|
|
Selection sel = Selection(pos, pos);
|
2012-02-10 00:47:55 +01:00
|
|
|
m_selections.back().clear();
|
|
|
|
m_selections.back().push_back(std::move(sel));
|
2012-01-31 20:12:06 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void Editor::keep_selection(int index)
|
|
|
|
{
|
|
|
|
check_invariant();
|
|
|
|
|
|
|
|
if (index < selections().size())
|
|
|
|
{
|
|
|
|
Selection sel = selections()[index];
|
2012-02-10 00:47:55 +01:00
|
|
|
m_selections.back().clear();
|
|
|
|
m_selections.back().push_back(std::move(sel));
|
2012-01-31 20:12:06 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Editor::select(const BufferIterator& iterator)
|
|
|
|
{
|
2012-02-10 00:47:55 +01:00
|
|
|
m_selections.back().clear();
|
|
|
|
m_selections.back().push_back(Selection(iterator, iterator));
|
2012-01-31 20:12:06 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void Editor::select(const Selector& selector, bool append)
|
|
|
|
{
|
|
|
|
check_invariant();
|
|
|
|
|
2012-02-10 14:58:29 +01:00
|
|
|
std::array<std::vector<String>, 10> captures;
|
2012-02-10 00:47:55 +01:00
|
|
|
|
2012-02-10 14:58:29 +01:00
|
|
|
size_t capture_count = -1;
|
2012-02-10 00:47:55 +01:00
|
|
|
for (auto& sel : m_selections.back())
|
2012-01-31 20:12:06 +01:00
|
|
|
{
|
2012-02-10 00:47:55 +01:00
|
|
|
SelectionAndCaptures res = selector(sel);
|
|
|
|
if (append)
|
|
|
|
sel.merge_with(res.selection);
|
|
|
|
else
|
|
|
|
sel = std::move(res.selection);
|
|
|
|
|
2012-02-10 14:58:29 +01:00
|
|
|
assert(capture_count == -1 or capture_count == res.captures.size());
|
|
|
|
capture_count = res.captures.size();
|
2012-02-10 00:47:55 +01:00
|
|
|
|
|
|
|
for (size_t i = 0; i < res.captures.size(); ++i)
|
|
|
|
captures[i].push_back(res.captures[i]);
|
2012-01-31 20:12:06 +01:00
|
|
|
}
|
2012-02-10 00:47:55 +01:00
|
|
|
|
2012-02-10 14:58:29 +01:00
|
|
|
if (not captures[0].empty())
|
2012-01-31 20:12:06 +01:00
|
|
|
{
|
2012-02-10 00:47:55 +01:00
|
|
|
for (size_t i = 0; i < captures.size(); ++i)
|
|
|
|
RegisterManager::instance()['0' + i] = std::move(captures[i]);
|
2012-01-31 20:12:06 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct nothing_selected : public runtime_error
|
|
|
|
{
|
|
|
|
nothing_selected() : runtime_error("nothing was selected") {}
|
|
|
|
};
|
|
|
|
|
|
|
|
void Editor::multi_select(const MultiSelector& selector)
|
|
|
|
{
|
|
|
|
check_invariant();
|
|
|
|
|
2012-02-10 14:58:29 +01:00
|
|
|
std::array<std::vector<String>, 10> captures;
|
2012-02-10 00:47:55 +01:00
|
|
|
|
2012-02-10 14:58:29 +01:00
|
|
|
size_t capture_count = -1;
|
2012-01-31 20:12:06 +01:00
|
|
|
SelectionList new_selections;
|
2012-02-10 00:47:55 +01:00
|
|
|
for (auto& sel : m_selections.back())
|
2012-01-31 20:12:06 +01:00
|
|
|
{
|
2012-02-10 00:47:55 +01:00
|
|
|
SelectionAndCapturesList res = selector(sel);
|
|
|
|
for (auto& sel_and_cap : res)
|
|
|
|
{
|
|
|
|
new_selections.push_back(sel_and_cap.selection);
|
|
|
|
|
2012-02-10 14:58:29 +01:00
|
|
|
assert(capture_count == -1 or
|
|
|
|
capture_count == sel_and_cap.captures.size());
|
|
|
|
capture_count = sel_and_cap.captures.size();
|
2012-02-10 00:47:55 +01:00
|
|
|
|
|
|
|
for (size_t i = 0; i < sel_and_cap.captures.size(); ++i)
|
|
|
|
captures[i].push_back(sel_and_cap.captures[i]);
|
|
|
|
}
|
2012-01-31 20:12:06 +01:00
|
|
|
}
|
|
|
|
if (new_selections.empty())
|
|
|
|
throw nothing_selected();
|
|
|
|
|
2012-02-10 00:47:55 +01:00
|
|
|
m_selections.back() = std::move(new_selections);
|
2012-01-31 20:12:06 +01:00
|
|
|
|
2012-02-10 14:58:29 +01:00
|
|
|
if (not captures[0].empty())
|
2012-02-10 00:47:55 +01:00
|
|
|
{
|
|
|
|
for (size_t i = 0; i < captures.size(); ++i)
|
|
|
|
RegisterManager::instance()['0' + i] = std::move(captures[i]);
|
|
|
|
}
|
2012-01-31 20:12:06 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
bool Editor::undo()
|
|
|
|
{
|
|
|
|
return m_buffer.undo();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Editor::redo()
|
|
|
|
{
|
|
|
|
return m_buffer.redo();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Editor::check_invariant() const
|
|
|
|
{
|
|
|
|
assert(not selections().empty());
|
|
|
|
}
|
|
|
|
|
|
|
|
struct id_not_unique : public runtime_error
|
|
|
|
{
|
2012-04-14 03:17:09 +02:00
|
|
|
id_not_unique(const String& id)
|
2012-01-31 20:12:06 +01:00
|
|
|
: runtime_error("id not unique: " + id) {}
|
|
|
|
};
|
|
|
|
|
|
|
|
void Editor::add_filter(FilterAndId&& filter)
|
|
|
|
{
|
|
|
|
if (m_filters.contains(filter.first))
|
|
|
|
throw id_not_unique(filter.first);
|
|
|
|
m_filters.append(std::forward<FilterAndId>(filter));
|
|
|
|
}
|
|
|
|
|
2012-04-14 03:17:09 +02:00
|
|
|
void Editor::remove_filter(const String& id)
|
2012-01-31 20:12:06 +01:00
|
|
|
{
|
|
|
|
m_filters.remove(id);
|
|
|
|
}
|
|
|
|
|
2012-04-14 03:17:09 +02:00
|
|
|
CandidateList Editor::complete_filterid(const String& prefix,
|
2012-01-31 20:12:06 +01:00
|
|
|
size_t cursor_pos)
|
|
|
|
{
|
|
|
|
return m_filters.complete_id<str_to_str>(prefix, cursor_pos);
|
|
|
|
}
|
|
|
|
|
2012-02-08 00:41:10 +01:00
|
|
|
void Editor::begin_edition()
|
2012-01-31 20:12:06 +01:00
|
|
|
{
|
2012-02-08 00:41:10 +01:00
|
|
|
++m_edition_level;
|
2012-01-31 20:12:06 +01:00
|
|
|
|
2012-02-08 00:41:10 +01:00
|
|
|
if (m_edition_level == 1)
|
2012-02-03 14:55:22 +01:00
|
|
|
m_buffer.begin_undo_group();
|
2012-01-31 20:12:06 +01:00
|
|
|
}
|
|
|
|
|
2012-02-08 00:41:10 +01:00
|
|
|
void Editor::end_edition()
|
2012-01-31 20:12:06 +01:00
|
|
|
{
|
2012-02-08 00:41:10 +01:00
|
|
|
assert(m_edition_level > 0);
|
|
|
|
if (m_edition_level == 1)
|
2012-02-03 14:55:22 +01:00
|
|
|
m_buffer.end_undo_group();
|
2012-02-08 00:41:10 +01:00
|
|
|
|
|
|
|
--m_edition_level;
|
2012-01-31 20:12:06 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
IncrementalInserter::IncrementalInserter(Editor& editor, Mode mode)
|
2012-02-08 00:41:10 +01:00
|
|
|
: m_editor(editor), m_edition(editor)
|
2012-01-31 20:12:06 +01:00
|
|
|
{
|
2012-02-08 00:41:10 +01:00
|
|
|
m_editor.on_incremental_insertion_begin();
|
2012-01-31 20:12:06 +01:00
|
|
|
|
|
|
|
if (mode == Mode::Change)
|
2012-02-08 00:41:10 +01:00
|
|
|
editor.erase();
|
2012-01-31 20:12:06 +01:00
|
|
|
|
2012-02-10 00:47:55 +01:00
|
|
|
for (auto& sel : m_editor.m_selections.back())
|
2012-01-31 20:12:06 +01:00
|
|
|
{
|
|
|
|
BufferIterator pos;
|
|
|
|
switch (mode)
|
|
|
|
{
|
|
|
|
case Mode::Insert: pos = sel.begin(); break;
|
|
|
|
case Mode::Append: pos = sel.end(); break;
|
|
|
|
case Mode::Change: pos = sel.begin(); break;
|
|
|
|
|
|
|
|
case Mode::OpenLineBelow:
|
|
|
|
case Mode::AppendAtLineEnd:
|
|
|
|
pos = m_editor.m_buffer.iterator_at_line_end(sel.end() - 1) - 1;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Mode::OpenLineAbove:
|
|
|
|
case Mode::InsertAtLineBegin:
|
|
|
|
pos = m_editor.m_buffer.iterator_at_line_begin(sel.begin());
|
|
|
|
if (mode == Mode::OpenLineAbove)
|
|
|
|
--pos;
|
2012-03-07 20:19:33 +01:00
|
|
|
else
|
|
|
|
{
|
|
|
|
auto first_non_blank = pos;
|
|
|
|
while (*first_non_blank == ' ' or *first_non_blank == '\t')
|
|
|
|
++first_non_blank;
|
|
|
|
if (*first_non_blank != '\n')
|
|
|
|
pos = first_non_blank;
|
|
|
|
}
|
2012-01-31 20:12:06 +01:00
|
|
|
break;
|
|
|
|
}
|
2012-02-10 00:47:55 +01:00
|
|
|
sel = Selection(pos, pos);
|
2012-01-31 20:12:06 +01:00
|
|
|
|
|
|
|
if (mode == Mode::OpenLineBelow or mode == Mode::OpenLineAbove)
|
|
|
|
apply(Modification::make_insert(pos, "\n"));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
IncrementalInserter::~IncrementalInserter()
|
|
|
|
{
|
|
|
|
move_cursors(BufferCoord(0, -1));
|
2012-02-08 00:41:10 +01:00
|
|
|
m_editor.on_incremental_insertion_end();
|
2012-01-31 20:12:06 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void IncrementalInserter::apply(Modification&& modification) const
|
|
|
|
{
|
|
|
|
for (auto filter : m_editor.m_filters)
|
|
|
|
filter.second(m_editor.buffer(), modification);
|
|
|
|
m_editor.buffer().modify(std::move(modification));
|
|
|
|
}
|
|
|
|
|
2012-03-08 22:23:29 +01:00
|
|
|
void IncrementalInserter::insert(const String& string)
|
2012-01-31 20:12:06 +01:00
|
|
|
{
|
|
|
|
for (auto& sel : m_editor.selections())
|
|
|
|
apply(Modification::make_insert(sel.begin(), string));
|
|
|
|
}
|
|
|
|
|
2012-02-10 00:47:55 +01:00
|
|
|
void IncrementalInserter::insert(const Register& reg)
|
2012-01-31 20:12:06 +01:00
|
|
|
{
|
2012-02-10 00:47:55 +01:00
|
|
|
m_editor.insert(reg);
|
2012-01-31 20:12:06 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void IncrementalInserter::erase()
|
|
|
|
{
|
2012-02-10 00:47:55 +01:00
|
|
|
for (auto& sel : m_editor.m_selections.back())
|
2012-01-31 20:12:06 +01:00
|
|
|
{
|
|
|
|
sel = Selection(sel.first() - 1, sel.last() - 1);
|
|
|
|
apply(Modification::make_erase(sel.begin(), sel.end()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void IncrementalInserter::move_cursors(const BufferCoord& offset)
|
|
|
|
{
|
2012-02-10 00:47:55 +01:00
|
|
|
for (auto& sel : m_editor.m_selections.back())
|
2012-01-31 20:12:06 +01:00
|
|
|
{
|
|
|
|
BufferCoord pos = m_editor.m_buffer.line_and_column_at(sel.last());
|
|
|
|
BufferIterator it = m_editor.m_buffer.iterator_at(pos + offset);
|
|
|
|
sel = Selection(it, it);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|