summaryrefslogtreecommitdiff
path: root/src/editor.cc
diff options
context:
space:
mode:
authorMaxime Coste <frrrwww@gmail.com>2012-01-31 19:12:06 +0000
committerMaxime Coste <frrrwww@gmail.com>2012-01-31 19:12:06 +0000
commit69d96c90da543fee83cd6f9fbac6d3348d28d446 (patch)
tree49a3a90f68a2dd504630db0171468fda082c5266 /src/editor.cc
parentd23a175533ebc04fa5c8a9712118cc5bf509adf0 (diff)
extract an Editor class from Window and refactor
Diffstat (limited to 'src/editor.cc')
-rw-r--r--src/editor.cc340
1 files changed, 340 insertions, 0 deletions
diff --git a/src/editor.cc b/src/editor.cc
new file mode 100644
index 00000000..afce02db
--- /dev/null
+++ b/src/editor.cc
@@ -0,0 +1,340 @@
+#include "editor.hh"
+
+#include "exception.hh"
+#include "utils.hh"
+
+namespace Kakoune
+{
+
+namespace
+{
+
+struct scoped_undo_group
+{
+ scoped_undo_group(Buffer& buffer)
+ : m_buffer(buffer) { m_buffer.begin_undo_group(); }
+
+ ~scoped_undo_group() { m_buffer.end_undo_group(); }
+private:
+ Buffer& m_buffer;
+};
+
+}
+
+Editor::Editor(Buffer& buffer)
+ : m_buffer(buffer),
+ m_current_inserter(nullptr)
+{
+ m_selections.push_back(SelectionList());
+ selections().push_back(Selection(buffer.begin(), buffer.begin()));
+}
+
+void Editor::erase()
+{
+ if (m_current_inserter == nullptr)
+ {
+ scoped_undo_group undo_group(m_buffer);
+ erase_noundo();
+ }
+ else
+ erase_noundo();
+}
+
+void Editor::erase_noundo()
+{
+ check_invariant();
+ for (auto& sel : selections())
+ m_buffer.modify(Modification::make_erase(sel.begin(), sel.end()));
+}
+
+void Editor::insert(const String& string)
+{
+ if (m_current_inserter == nullptr)
+ {
+ scoped_undo_group undo_group(m_buffer);
+ insert_noundo(string);
+ }
+ else
+ insert_noundo(string);
+}
+
+void Editor::insert_noundo(const String& string)
+{
+ for (auto& sel : selections())
+ m_buffer.modify(Modification::make_insert(sel.begin(), string));
+}
+
+void Editor::append(const String& string)
+{
+ if (m_current_inserter == nullptr)
+ {
+ scoped_undo_group undo_group(m_buffer);
+ append_noundo(string);
+ }
+ else
+ append_noundo(string);
+}
+
+void Editor::append_noundo(const String& string)
+{
+ for (auto& sel : selections())
+ m_buffer.modify(Modification::make_insert(sel.end(), string));
+}
+
+void Editor::replace(const std::string& string)
+{
+ if (m_current_inserter == nullptr)
+ {
+ scoped_undo_group undo_group(m_buffer);
+ erase_noundo();
+ insert_noundo(string);
+ }
+ else
+ {
+ erase_noundo();
+ insert_noundo(string);
+ }
+}
+
+void Editor::push_selections()
+{
+ SelectionList current_selections = selections();
+ 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)
+{
+ for (auto& sel : selections())
+ {
+ 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);
+ selections().clear();
+ selections().push_back(std::move(sel));
+}
+
+void Editor::keep_selection(int index)
+{
+ check_invariant();
+
+ if (index < selections().size())
+ {
+ Selection sel = selections()[index];
+ selections().clear();
+ selections().push_back(std::move(sel));
+ }
+}
+
+void Editor::select(const BufferIterator& iterator)
+{
+ selections().clear();
+ selections().push_back(Selection(iterator, iterator));
+
+}
+
+void Editor::select(const Selector& selector, bool append)
+{
+ check_invariant();
+
+ if (not append)
+ {
+ for (auto& sel : selections())
+ sel = selector(sel.last());
+ }
+ else
+ {
+ for (auto& sel : selections())
+ sel.merge_with(selector(sel.last()));
+ }
+}
+
+struct nothing_selected : public runtime_error
+{
+ nothing_selected() : runtime_error("nothing was selected") {}
+};
+
+void Editor::multi_select(const MultiSelector& selector)
+{
+ check_invariant();
+
+ SelectionList new_selections;
+ for (auto& sel : selections())
+ {
+ SelectionList selections = selector(sel);
+ std::copy(selections.begin(), selections.end(),
+ std::back_inserter(new_selections));
+ }
+ if (new_selections.empty())
+ throw nothing_selected();
+
+ selections() = std::move(new_selections);
+}
+
+BufferString Editor::selection_content() const
+{
+ check_invariant();
+
+ return m_buffer.string(selections().back().begin(),
+ selections().back().end());
+}
+
+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
+{
+ id_not_unique(const std::string& id)
+ : 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));
+}
+
+void Editor::remove_filter(const std::string& id)
+{
+ m_filters.remove(id);
+}
+
+CandidateList Editor::complete_filterid(const std::string& prefix,
+ size_t cursor_pos)
+{
+ return m_filters.complete_id<str_to_str>(prefix, cursor_pos);
+}
+
+void Editor::begin_incremental_insert(IncrementalInserter* inserter)
+{
+ assert(not m_current_inserter);
+ m_current_inserter = inserter;
+ m_buffer.begin_undo_group();
+
+ on_begin_incremental_insert();
+}
+
+void Editor::end_incremental_insert(IncrementalInserter* inserter)
+{
+ on_end_incremental_insert();
+
+ assert(m_current_inserter and m_current_inserter == inserter);
+ m_current_inserter = nullptr;
+ m_buffer.end_undo_group();
+}
+
+IncrementalInserter::IncrementalInserter(Editor& editor, Mode mode)
+ : m_editor(editor)
+{
+ m_editor.begin_incremental_insert(this);
+
+ if (mode == Mode::Change)
+ editor.erase_noundo();
+
+ for (auto& sel : m_editor.selections())
+ {
+ 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;
+ break;
+ }
+ sel = Selection(pos, pos, sel.captures());
+
+ if (mode == Mode::OpenLineBelow or mode == Mode::OpenLineAbove)
+ apply(Modification::make_insert(pos, "\n"));
+ }
+}
+
+IncrementalInserter::~IncrementalInserter()
+{
+ move_cursors(BufferCoord(0, -1));
+ m_editor.end_incremental_insert(this);
+}
+
+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));
+}
+
+
+void IncrementalInserter::insert(const Editor::String& string)
+{
+ for (auto& sel : m_editor.selections())
+ apply(Modification::make_insert(sel.begin(), string));
+}
+
+void IncrementalInserter::insert_capture(size_t index)
+{
+ for (auto& sel : m_editor.selections())
+ m_editor.m_buffer.modify(Modification::make_insert(sel.begin(),
+ sel.capture(index)));
+}
+
+void IncrementalInserter::erase()
+{
+ for (auto& sel : m_editor.selections())
+ {
+ sel = Selection(sel.first() - 1, sel.last() - 1);
+ apply(Modification::make_erase(sel.begin(), sel.end()));
+ }
+}
+
+void IncrementalInserter::move_cursors(const BufferCoord& offset)
+{
+ for (auto& sel : m_editor.selections())
+ {
+ 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);
+ }
+}
+
+}