summaryrefslogtreecommitdiff
path: root/src
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
parentd23a175533ebc04fa5c8a9712118cc5bf509adf0 (diff)
extract an Editor class from Window and refactor
Diffstat (limited to 'src')
-rw-r--r--src/editor.cc340
-rw-r--r--src/editor.hh115
-rw-r--r--src/main.cc26
-rw-r--r--src/selection.cc120
-rw-r--r--src/selection.hh56
-rw-r--r--src/window.cc462
-rw-r--r--src/window.hh143
7 files changed, 671 insertions, 591 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);
+ }
+}
+
+}
diff --git a/src/editor.hh b/src/editor.hh
new file mode 100644
index 00000000..988564ce
--- /dev/null
+++ b/src/editor.hh
@@ -0,0 +1,115 @@
+#ifndef editor_hh_INCLUDED
+#define editor_hh_INCLUDED
+
+#include "buffer.hh"
+#include "selection.hh"
+#include "filter.hh"
+#include "idvaluemap.hh"
+#include "hooks_manager.hh"
+
+namespace Kakoune
+{
+
+class IncrementalInserter;
+
+// An Editor is a buffer mutator
+//
+// The Editor class provides methods to manipulate a set of selections
+// and to use these selections to mutate it's buffer.
+class Editor
+{
+public:
+ typedef BufferString String;
+ typedef std::function<Selection (const BufferIterator&)> Selector;
+ typedef std::function<SelectionList (const Selection&)> MultiSelector;
+
+ Editor(Buffer& buffer);
+ virtual ~Editor() {}
+
+ Buffer& buffer() const { return m_buffer; }
+
+ void erase();
+ void insert(const String& string);
+ void append(const String& string);
+ void replace(const String& string);
+
+ void push_selections();
+ void pop_selections();
+
+ void move_selections(const BufferCoord& offset, bool append = false);
+ void clear_selections();
+ void keep_selection(int index);
+ void select(const BufferIterator& iterator);
+ void select(const Selector& selector, bool append = false);
+ void multi_select(const MultiSelector& selector);
+
+ BufferString selection_content() const;
+ const SelectionList& selections() const { return m_selections.back(); }
+
+ bool undo();
+ bool redo();
+
+ void add_filter(FilterAndId&& filter);
+ void remove_filter(const std::string& id);
+
+ CandidateList complete_filterid(const std::string& prefix,
+ size_t cursor_pos = std::string::npos);
+
+ bool is_inserting() const { return m_current_inserter != nullptr; }
+
+private:
+ void erase_noundo();
+ void insert_noundo(const String& string);
+ void append_noundo(const String& string);
+
+ SelectionList& selections() { return m_selections.back(); }
+
+ void check_invariant() const;
+
+ friend class IncrementalInserter;
+ IncrementalInserter* m_current_inserter;
+
+ void begin_incremental_insert(IncrementalInserter* inserter);
+ void end_incremental_insert(IncrementalInserter* inserter);
+ virtual void on_begin_incremental_insert() {}
+ virtual void on_end_incremental_insert() {}
+
+
+ Buffer& m_buffer;
+ std::vector<SelectionList> m_selections;
+ idvaluemap<std::string, FilterFunc> m_filters;
+};
+
+// An IncrementalInserter manage insert mode
+class IncrementalInserter
+{
+public:
+ enum class Mode
+ {
+ Insert,
+ Append,
+ Change,
+ InsertAtLineBegin,
+ AppendAtLineEnd,
+ OpenLineBelow,
+ OpenLineAbove
+ };
+
+ IncrementalInserter(Editor& editor, Mode mode = Mode::Insert);
+ ~IncrementalInserter();
+
+ void insert(const Editor::String& string);
+ void insert_capture(size_t index);
+ void erase();
+ void move_cursors(const BufferCoord& offset);
+
+private:
+ void apply(Modification&& modification) const;
+
+ Editor& m_editor;
+};
+
+}
+
+#endif // editor_hh_INCLUDED
+
diff --git a/src/main.cc b/src/main.cc
index 686dec64..45f38b6a 100644
--- a/src/main.cc
+++ b/src/main.cc
@@ -440,7 +440,7 @@ void do_insert(Window& window, IncrementalInserter::Mode mode)
Key key = get_key();
if (not insert_char(window, inserter, key))
- return;
+ break;
last_insert_sequence.keys.push_back(key);
draw_window(window);
@@ -466,7 +466,7 @@ void do_go(Window& window, int count)
BufferIterator target =
window.buffer().iterator_at(BufferCoord(count-1, 0));
- window.move_cursor_to(target);
+ window.select(target);
}
else
{
@@ -481,7 +481,7 @@ void do_go(Window& window, int count)
{
BufferIterator target =
window.buffer().iterator_at(BufferCoord(0,0));
- window.move_cursor_to(target);
+ window.select(target);
break;
}
case 'l':
@@ -496,7 +496,7 @@ void do_go(Window& window, int count)
{
BufferIterator target = window.buffer().iterator_at(
BufferCoord(window.buffer().line_count() - 1, 0));
- window.move_cursor_to(target);
+ window.select(target);
break;
}
}
@@ -926,15 +926,15 @@ void do_select_surrounding(Window& window, int count)
std::unordered_map<Key, std::function<void (Window& window, int count)>> keymap =
{
- { { Key::Modifiers::None, 'h' }, [](Window& window, int count) { window.move_selections(DisplayCoord(0, -std::max(count,1))); } },
- { { Key::Modifiers::None, 'j' }, [](Window& window, int count) { window.move_selections(DisplayCoord( std::max(count,1), 0)); } },
- { { Key::Modifiers::None, 'k' }, [](Window& window, int count) { window.move_selections(DisplayCoord(-std::max(count,1), 0)); } },
- { { Key::Modifiers::None, 'l' }, [](Window& window, int count) { window.move_selections(DisplayCoord(0, std::max(count,1))); } },
-
- { { Key::Modifiers::None, 'H' }, [](Window& window, int count) { window.move_selections(DisplayCoord(0, -std::max(count,1)), true); } },
- { { Key::Modifiers::None, 'J' }, [](Window& window, int count) { window.move_selections(DisplayCoord( std::max(count,1), 0), true); } },
- { { Key::Modifiers::None, 'K' }, [](Window& window, int count) { window.move_selections(DisplayCoord(-std::max(count,1), 0), true); } },
- { { Key::Modifiers::None, 'L' }, [](Window& window, int count) { window.move_selections(DisplayCoord(0, std::max(count,1)), true); } },
+ { { Key::Modifiers::None, 'h' }, [](Window& window, int count) { window.move_selections(BufferCoord(0, -std::max(count,1))); } },
+ { { Key::Modifiers::None, 'j' }, [](Window& window, int count) { window.move_selections(BufferCoord( std::max(count,1), 0)); } },
+ { { Key::Modifiers::None, 'k' }, [](Window& window, int count) { window.move_selections(BufferCoord(-std::max(count,1), 0)); } },
+ { { Key::Modifiers::None, 'l' }, [](Window& window, int count) { window.move_selections(BufferCoord(0, std::max(count,1))); } },
+
+ { { Key::Modifiers::None, 'H' }, [](Window& window, int count) { window.move_selections(BufferCoord(0, -std::max(count,1)), true); } },
+ { { Key::Modifiers::None, 'J' }, [](Window& window, int count) { window.move_selections(BufferCoord( std::max(count,1), 0), true); } },
+ { { Key::Modifiers::None, 'K' }, [](Window& window, int count) { window.move_selections(BufferCoord(-std::max(count,1), 0), true); } },
+ { { Key::Modifiers::None, 'L' }, [](Window& window, int count) { window.move_selections(BufferCoord(0, std::max(count,1)), true); } },
{ { Key::Modifiers::None, 't' }, [](Window& window, int count) { window.select(std::bind(select_to, _1, getch(), count, false)); } },
{ { Key::Modifiers::None, 'f' }, [](Window& window, int count) { window.select(std::bind(select_to, _1, getch(), count, true)); } },
diff --git a/src/selection.cc b/src/selection.cc
new file mode 100644
index 00000000..e772179c
--- /dev/null
+++ b/src/selection.cc
@@ -0,0 +1,120 @@
+#include "selection.hh"
+
+namespace Kakoune
+{
+
+Selection::Selection(const BufferIterator& first, const BufferIterator& last,
+ const CaptureList& captures)
+ : m_first(first), m_last(last), m_captures(captures)
+{
+ register_with_buffer();
+}
+
+Selection::Selection(const BufferIterator& first, const BufferIterator& last,
+ CaptureList&& captures)
+ : m_first(first), m_last(last), m_captures(captures)
+{
+ register_with_buffer();
+}
+
+Selection::Selection(const Selection& other)
+ : m_first(other.m_first), m_last(other.m_last),
+ m_captures(other.m_captures)
+{
+ register_with_buffer();
+}
+
+Selection::Selection(Selection&& other)
+ : m_first(other.m_first), m_last(other.m_last),
+ m_captures(other.m_captures)
+{
+ register_with_buffer();
+}
+
+Selection::~Selection()
+{
+ unregister_with_buffer();
+}
+
+Selection& Selection::operator=(const Selection& other)
+{
+ const bool new_buffer = &m_first.buffer() != &other.m_first.buffer();
+ if (new_buffer)
+ unregister_with_buffer();
+
+ m_first = other.m_first;
+ m_last = other.m_last;
+ m_captures = other.m_captures;
+
+ if (new_buffer)
+ register_with_buffer();
+
+ return *this;
+}
+
+BufferIterator Selection::begin() const
+{
+ return std::min(m_first, m_last);
+}
+
+BufferIterator Selection::end() const
+{
+ return std::max(m_first, m_last) + 1;
+}
+
+void Selection::merge_with(const Selection& selection)
+{
+ if (m_first <= m_last)
+ m_first = std::min(m_first, selection.m_first);
+ else
+ m_first = std::max(m_first, selection.m_first);
+ m_last = selection.m_last;
+}
+
+BufferString Selection::capture(size_t index) const
+{
+ if (index < m_captures.size())
+ return m_captures[index];
+ return "";
+}
+
+static void update_iterator(const Modification& modification,
+ BufferIterator& iterator)
+{
+ if (iterator < modification.position)
+ return;
+
+ size_t length = modification.content.length();
+ if (modification.type == Modification::Erase)
+ {
+ // do not move length on the other side of the inequality,
+ // as modification.position + length may be after buffer end
+ if (iterator - length <= modification.position)
+ iterator = modification.position;
+ else
+ iterator -= length;
+ }
+ else
+ {
+ assert(modification.type == Modification::Insert);
+ iterator += length;
+ }
+}
+
+void Selection::on_modification(const Modification& modification)
+{
+ update_iterator(modification, m_first);
+ update_iterator(modification, m_last);
+}
+
+void Selection::register_with_buffer()
+{
+ const_cast<Buffer&>(m_first.buffer()).register_modification_listener(this);
+}
+
+void Selection::unregister_with_buffer()
+{
+ const_cast<Buffer&>(m_first.buffer()).unregister_modification_listener(this);
+}
+
+}
diff --git a/src/selection.hh b/src/selection.hh
new file mode 100644
index 00000000..fc695f9c
--- /dev/null
+++ b/src/selection.hh
@@ -0,0 +1,56 @@
+#ifndef selection_hh_INCLUDED
+#define selection_hh_INCLUDED
+
+#include "buffer.hh"
+
+namespace Kakoune
+{
+
+struct Selection : public ModificationListener
+{
+ typedef std::vector<BufferString> CaptureList;
+
+ Selection(const BufferIterator& first, const BufferIterator& last,
+ const CaptureList& captures = CaptureList());
+
+ Selection(const BufferIterator& first, const BufferIterator& last,
+ CaptureList&& captures);
+
+ Selection(const Selection& other);
+ Selection(Selection&& other);
+
+ ~Selection();
+
+ Selection& operator=(const Selection& other);
+
+ BufferIterator begin() const;
+ BufferIterator end() const;
+
+ const BufferIterator& first() const { return m_first; }
+ const BufferIterator& last() const { return m_last; }
+
+ void merge_with(const Selection& selection);
+
+ BufferString capture(size_t index) const;
+ const CaptureList& captures() const { return m_captures; }
+
+private:
+ BufferIterator m_first;
+ BufferIterator m_last;
+
+ CaptureList m_captures;
+
+ void on_modification(const Modification& modification);
+
+ void register_with_buffer();
+ void unregister_with_buffer();
+
+ void check_invariant();
+};
+
+typedef std::vector<Selection> SelectionList;
+
+}
+
+#endif // selection_hh_INCLUDED
+
diff --git a/src/window.cc b/src/window.cc
index 52edf331..72ab0578 100644
--- a/src/window.cc
+++ b/src/window.cc
@@ -11,139 +11,11 @@
namespace Kakoune
{
-Selection::Selection(const BufferIterator& first, const BufferIterator& last,
- const CaptureList& captures)
- : m_first(first), m_last(last), m_captures(captures)
-{
- register_with_buffer();
-}
-
-Selection::Selection(const BufferIterator& first, const BufferIterator& last,
- CaptureList&& captures)
- : m_first(first), m_last(last), m_captures(captures)
-{
- register_with_buffer();
-}
-
-Selection::Selection(const Selection& other)
- : m_first(other.m_first), m_last(other.m_last),
- m_captures(other.m_captures)
-{
- register_with_buffer();
-}
-
-Selection::Selection(Selection&& other)
- : m_first(other.m_first), m_last(other.m_last),
- m_captures(other.m_captures)
-{
- register_with_buffer();
-}
-
-Selection::~Selection()
-{
- unregister_with_buffer();
-}
-
-Selection& Selection::operator=(const Selection& other)
-{
- const bool new_buffer = &m_first.buffer() != &other.m_first.buffer();
- if (new_buffer)
- unregister_with_buffer();
-
- m_first = other.m_first;
- m_last = other.m_last;
- m_captures = other.m_captures;
-
- if (new_buffer)
- register_with_buffer();
-
- return *this;
-}
-
-BufferIterator Selection::begin() const
-{
- return std::min(m_first, m_last);
-}
-
-BufferIterator Selection::end() const
-{
- return std::max(m_first, m_last) + 1;
-}
-
-void Selection::merge_with(const Selection& selection)
-{
- if (m_first <= m_last)
- m_first = std::min(m_first, selection.m_first);
- else
- m_first = std::max(m_first, selection.m_first);
- m_last = selection.m_last;
-}
-
-BufferString Selection::capture(size_t index) const
-{
- if (index < m_captures.size())
- return m_captures[index];
- return "";
-}
-
-static void update_iterator(const Modification& modification,
- BufferIterator& iterator)
-{
- if (iterator < modification.position)
- return;
-
- size_t length = modification.content.length();
- if (modification.type == Modification::Erase)
- {
- // do not move length on the other side of the inequality,
- // as modification.position + length may be after buffer end
- if (iterator - length <= modification.position)
- iterator = modification.position;
- else
- iterator -= length;
- }
- else
- {
- assert(modification.type == Modification::Insert);
- iterator += length;
- }
-}
-
-void Selection::on_modification(const Modification& modification)
-{
- update_iterator(modification, m_first);
- update_iterator(modification, m_last);
-}
-
-void Selection::register_with_buffer()
-{
- const_cast<Buffer&>(m_first.buffer()).register_modification_listener(this);
-}
-
-void Selection::unregister_with_buffer()
-{
- const_cast<Buffer&>(m_first.buffer()).unregister_modification_listener(this);
-}
-
-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;
-};
-
Window::Window(Buffer& buffer)
- : m_buffer(buffer),
+ : Editor(buffer),
m_position(0, 0),
- m_dimensions(0, 0),
- m_current_inserter(nullptr)
+ m_dimensions(0, 0)
{
- m_selections.push_back(SelectionList());
- selections().push_back(Selection(buffer.begin(), buffer.begin()));
-
HighlighterRegistry& registry = HighlighterRegistry::instance();
GlobalHooksManager::instance().run_hook("WinCreate", buffer.name(),
@@ -153,42 +25,16 @@ Window::Window(Buffer& buffer)
registry.add_highlighter_to_window(*this, "highlight_selections", HighlighterParameters());
}
-void Window::check_invariant() const
-{
- assert(not selections().empty());
-}
-
DisplayCoord Window::cursor_position() const
{
- check_invariant();
return line_and_column_at(cursor_iterator());
}
BufferIterator Window::cursor_iterator() const
{
- check_invariant();
return selections().back().last();
}
-void Window::erase()
-{
- if (m_current_inserter == nullptr)
- {
- scoped_undo_group undo_group(m_buffer);
- erase_noundo();
- }
- else
- erase_noundo();
-}
-
-void Window::erase_noundo()
-{
- check_invariant();
- for (auto& sel : selections())
- m_buffer.modify(Modification::make_erase(sel.begin(), sel.end()));
- scroll_to_keep_cursor_visible_ifn();
-}
-
template<typename Iterator>
static DisplayCoord measure_string(Iterator begin, Iterator end)
{
@@ -207,76 +53,15 @@ static DisplayCoord measure_string(Iterator begin, Iterator end)
return result;
}
-static DisplayCoord measure_string(const Window::String& string)
+static DisplayCoord measure_string(const Editor::String& string)
{
return measure_string(string.begin(), string.end());
}
-void Window::insert(const String& string)
-{
- if (m_current_inserter == nullptr)
- {
- scoped_undo_group undo_group(m_buffer);
- insert_noundo(string);
- }
- else
- insert_noundo(string);
-}
-
-void Window::insert_noundo(const String& string)
-{
- for (auto& sel : selections())
- m_buffer.modify(Modification::make_insert(sel.begin(), string));
- scroll_to_keep_cursor_visible_ifn();
-}
-
-void Window::append(const String& string)
-{
- if (m_current_inserter == nullptr)
- {
- scoped_undo_group undo_group(m_buffer);
- append_noundo(string);
- }
- else
- append_noundo(string);
-}
-
-void Window::append_noundo(const String& string)
-{
- for (auto& sel : selections())
- m_buffer.modify(Modification::make_insert(sel.end(), string));
- scroll_to_keep_cursor_visible_ifn();
-}
-
-void Window::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);
- }
-}
-
-bool Window::undo()
-{
- return m_buffer.undo();
-}
-
-bool Window::redo()
-{
- return m_buffer.redo();
-}
-
BufferIterator Window::iterator_at(const DisplayCoord& window_pos) const
{
if (m_display_buffer.begin() == m_display_buffer.end())
- return m_buffer.begin();
+ return buffer().begin();
if (DisplayCoord(0,0) <= window_pos)
{
@@ -290,7 +75,7 @@ BufferIterator Window::iterator_at(const DisplayCoord& window_pos) const
}
}
- return m_buffer.iterator_at(m_position + BufferCoord(window_pos));
+ return buffer().iterator_at(m_position + BufferCoord(window_pos));
}
DisplayCoord Window::line_and_column_at(const BufferIterator& iterator) const
@@ -310,109 +95,19 @@ DisplayCoord Window::line_and_column_at(const BufferIterator& iterator) const
}
}
}
- BufferCoord coord = m_buffer.line_and_column_at(iterator);
+ BufferCoord coord = buffer().line_and_column_at(iterator);
return DisplayCoord(coord.line - m_position.line,
coord.column - m_position.column);
}
-void Window::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 Window::keep_selection(int index)
-{
- check_invariant();
-
- if (index < selections().size())
- {
- Selection sel = selections()[index];
- selections().clear();
- selections().push_back(std::move(sel));
- }
-}
-
-void Window::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()));
- }
- scroll_to_keep_cursor_visible_ifn();
-}
-
-struct nothing_selected : public runtime_error
-{
- nothing_selected() : runtime_error("nothing was selected") {}
-};
-
-void Window::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);
- scroll_to_keep_cursor_visible_ifn();
-}
-
-BufferString Window::selection_content() const
-{
- check_invariant();
-
- return m_buffer.string(selections().back().begin(),
- selections().back().end());
-}
-
-void Window::move_selections(const DisplayCoord& 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);
- }
- scroll_to_keep_cursor_visible_ifn();
-}
-
-void Window::move_cursor_to(const BufferIterator& iterator)
+void Window::update_display_buffer()
{
- selections().clear();
- selections().push_back(Selection(iterator, iterator));
-
scroll_to_keep_cursor_visible_ifn();
-}
-void Window::update_display_buffer()
-{
m_display_buffer.clear();
- BufferIterator begin = m_buffer.iterator_at(m_position);
- BufferIterator end = m_buffer.iterator_at(m_position +
+ BufferIterator begin = buffer().iterator_at(m_position);
+ BufferIterator end = buffer().iterator_at(m_position +
BufferCoord(m_dimensions.line, m_dimensions.column))+1;
if (begin == end)
return;
@@ -430,8 +125,6 @@ void Window::set_dimensions(const DisplayCoord& dimensions)
void Window::scroll_to_keep_cursor_visible_ifn()
{
- check_invariant();
-
DisplayCoord cursor = line_and_column_at(selections().back().last());
if (cursor.line < 0)
{
@@ -454,144 +147,23 @@ void Window::scroll_to_keep_cursor_visible_ifn()
std::string Window::status_line() const
{
- BufferCoord cursor = m_buffer.line_and_column_at(selections().back().last());
+ BufferCoord cursor = buffer().line_and_column_at(selections().back().last());
std::ostringstream oss;
- oss << m_buffer.name();
- if (m_buffer.is_modified())
+ oss << buffer().name();
+ if (buffer().is_modified())
oss << " [+]";
oss << " -- " << cursor.line+1 << "," << cursor.column+1
<< " -- " << selections().size() << " sel -- ";
- if (m_current_inserter)
+ if (is_inserting())
oss << "[Insert]";
return oss.str();
}
-void Window::add_filter(FilterAndId&& filter)
-{
- if (m_filters.contains(filter.first))
- throw id_not_unique(filter.first);
- m_filters.append(std::forward<FilterAndId>(filter));
-}
-
-void Window::remove_filter(const std::string& id)
-{
- m_filters.remove(id);
-}
-
-CandidateList Window::complete_filterid(const std::string& prefix,
- size_t cursor_pos)
-{
- return m_filters.complete_id<str_to_str>(prefix, cursor_pos);
-}
-
-void Window::push_selections()
-{
- SelectionList current_selections = selections();
- m_selections.push_back(std::move(current_selections));
-}
-
-void Window::pop_selections()
-{
- if (m_selections.size() > 1)
- m_selections.pop_back();
- else
- throw runtime_error("no more selections on stack");
-}
-
-IncrementalInserter::IncrementalInserter(Window& window, Mode mode)
- : m_window(window)
+void Window::on_end_incremental_insert()
{
- assert(not m_window.m_current_inserter);
- m_window.m_current_inserter = this;
- m_window.check_invariant();
-
- m_window.m_buffer.begin_undo_group();
-
- if (mode == Mode::Change)
- window.erase_noundo();
-
- for (auto& sel : m_window.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_window.m_buffer.iterator_at_line_end(sel.end() - 1) - 1;
- break;
-
- case Mode::OpenLineAbove:
- case Mode::InsertAtLineBegin:
- pos = m_window.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(DisplayCoord(0, -1));
-
- m_window.push_selections();
- m_window.hooks_manager().run_hook("InsertEnd", "", Context(m_window));
- m_window.pop_selections();
-
- assert(m_window.m_current_inserter == this);
- m_window.m_current_inserter = nullptr;
- m_window.m_buffer.end_undo_group();
-}
-
-void IncrementalInserter::apply(Modification&& modification) const
-{
- for (auto filter : m_window.m_filters)
- filter.second(m_window.buffer(), modification);
- m_window.buffer().modify(std::move(modification));
-}
-
-
-void IncrementalInserter::insert(const Window::String& string)
-{
- for (auto& sel : m_window.selections())
- apply(Modification::make_insert(sel.begin(), string));
-}
-
-void IncrementalInserter::insert_capture(size_t index)
-{
- for (auto& sel : m_window.selections())
- m_window.m_buffer.modify(Modification::make_insert(sel.begin(),
- sel.capture(index)));
- m_window.scroll_to_keep_cursor_visible_ifn();
-}
-
-void IncrementalInserter::erase()
-{
- for (auto& sel : m_window.selections())
- {
- sel = Selection(sel.first() - 1, sel.last() - 1);
- apply(Modification::make_erase(sel.begin(), sel.end()));
- }
-
- m_window.scroll_to_keep_cursor_visible_ifn();
-}
-
-void IncrementalInserter::move_cursors(const DisplayCoord& offset)
-{
- for (auto& sel : m_window.selections())
- {
- DisplayCoord pos = m_window.line_and_column_at(sel.last());
- BufferIterator it = m_window.iterator_at(pos + offset);
- sel = Selection(it, it);
- }
+ push_selections();
+ hooks_manager().run_hook("InsertEnd", "", Context(*this));
+ pop_selections();
}
}
diff --git a/src/window.hh b/src/window.hh
index e1591d40..ad755388 100644
--- a/src/window.hh
+++ b/src/window.hh
@@ -3,129 +3,45 @@
#include <functional>
-#include "buffer.hh"
+#include "editor.hh"
#include "display_buffer.hh"
#include "completion.hh"
#include "highlighter.hh"
#include "highlighter_group.hh"
-#include "filter.hh"
-#include "idvaluemap.hh"
-#include "hooks_manager.hh"
namespace Kakoune
{
-
-struct Selection : public ModificationListener
-{
- typedef std::vector<BufferString> CaptureList;
-
- Selection(const BufferIterator& first, const BufferIterator& last,
- const CaptureList& captures = CaptureList());
-
- Selection(const BufferIterator& first, const BufferIterator& last,
- CaptureList&& captures);
-
- Selection(const Selection& other);
- Selection(Selection&& other);
-
- ~Selection();
-
- Selection& operator=(const Selection& other);
-
- BufferIterator begin() const;
- BufferIterator end() const;
-
- const BufferIterator& first() const { return m_first; }
- const BufferIterator& last() const { return m_last; }
-
- void merge_with(const Selection& selection);
-
- BufferString capture(size_t index) const;
- const CaptureList& captures() const { return m_captures; }
-
-private:
- BufferIterator m_first;
- BufferIterator m_last;
-
- CaptureList m_captures;
-
- void on_modification(const Modification& modification);
-
- void register_with_buffer();
- void unregister_with_buffer();
-
- void check_invariant();
-};
-
-typedef std::vector<Selection> SelectionList;
-
-class IncrementalInserter;
class HighlighterGroup;
// A Window is an editing view onto a Buffer
//
-// The Window class manage a set of selections and provides means to modify
-// both the selections and the buffer. It also handle the display of the
-// buffer with it's highlighters.
-class Window
+// The Window class is an interactive Editor adding display functionalities
+// to the editing ones already provided by the Editor class.
+// Display can be customized through the use of highlighters handled by
+// the window's HighlighterGroup
+class Window : public Editor
{
public:
- typedef BufferString String;
- typedef std::function<Selection (const BufferIterator&)> Selector;
- typedef std::function<SelectionList (const Selection&)> MultiSelector;
-
- void erase();
- void insert(const String& string);
- void append(const String& string);
- void replace(const String& string);
-
const BufferCoord& position() const { return m_position; }
+
DisplayCoord cursor_position() const;
BufferIterator cursor_iterator() const;
- Buffer& buffer() const { return m_buffer; }
-
BufferIterator iterator_at(const DisplayCoord& window_pos) const;
DisplayCoord line_and_column_at(const BufferIterator& iterator) const;
- void move_selections(const DisplayCoord& offset, bool append = false);
- void move_cursor_to(const BufferIterator& iterator);
-
- void clear_selections();
- void keep_selection(int index);
- void select(const Selector& selector, bool append = false);
- void multi_select(const MultiSelector& selector);
- BufferString selection_content() const;
- const SelectionList& selections() const { return m_selections.back(); }
-
void set_dimensions(const DisplayCoord& dimensions);
const DisplayBuffer& display_buffer() const { return m_display_buffer; }
void update_display_buffer();
- bool undo();
- bool redo();
+ const SelectionList& selections() const { return Editor::selections(); }
std::string status_line() const;
- struct id_not_unique : public runtime_error
- {
- id_not_unique(const std::string& id)
- : runtime_error("id not unique: " + id) {}
- };
-
HighlighterGroup& highlighters() { return m_highlighters; }
- void add_filter(FilterAndId&& filter);
- void remove_filter(const std::string& id);
-
- CandidateList complete_filterid(const std::string& prefix,
- size_t cursor_pos = std::string::npos);
-
- void push_selections();
- void pop_selections();
-
HooksManager& hooks_manager() { return m_hooks_manager; }
private:
@@ -134,58 +50,19 @@ private:
Window(Buffer& buffer);
Window(const Window&) = delete;
- void check_invariant() const;
- void scroll_to_keep_cursor_visible_ifn();
-
- void erase_noundo();
- void insert_noundo(const String& string);
- void append_noundo(const String& string);
-
- SelectionList& selections() { return m_selections.back(); }
+ void on_end_incremental_insert();
- friend class IncrementalInserter;
- IncrementalInserter* m_current_inserter;
+ void scroll_to_keep_cursor_visible_ifn();
- Buffer& m_buffer;
BufferCoord m_position;
DisplayCoord m_dimensions;
- std::vector<SelectionList> m_selections;
DisplayBuffer m_display_buffer;
HighlighterGroup m_highlighters;
- idvaluemap<std::string, FilterFunc> m_filters;
HooksManager m_hooks_manager;
};
-class IncrementalInserter
-{
-public:
- enum class Mode
- {
- Insert,
- Append,
- Change,
- InsertAtLineBegin,
- AppendAtLineEnd,
- OpenLineBelow,
- OpenLineAbove
- };
-
- IncrementalInserter(Window& window, Mode mode = Mode::Insert);
- ~IncrementalInserter();
-
- void insert(const Window::String& string);
- void insert_capture(size_t index);
- void erase();
- void move_cursors(const DisplayCoord& offset);
-
-private:
- void apply(Modification&& modification) const;
-
- Window& m_window;
-};
-
}
#endif // window_hh_INCLUDED