From 69d96c90da543fee83cd6f9fbac6d3348d28d446 Mon Sep 17 00:00:00 2001 From: Maxime Coste Date: Tue, 31 Jan 2012 19:12:06 +0000 Subject: [PATCH] extract an Editor class from Window and refactor --- src/editor.cc | 340 ++++++++++++++++++++++++++++++++++ src/editor.hh | 115 ++++++++++++ src/main.cc | 24 +-- src/selection.cc | 120 ++++++++++++ src/selection.hh | 56 ++++++ src/window.cc | 464 ++--------------------------------------------- src/window.hh | 143 +-------------- 7 files changed, 671 insertions(+), 591 deletions(-) create mode 100644 src/editor.cc create mode 100644 src/editor.hh create mode 100644 src/selection.cc create mode 100644 src/selection.hh 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(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(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 Selector; + typedef std::function 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 m_selections; + idvaluemap 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> 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(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(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)), 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(m_first.buffer()).register_modification_listener(this); +} + +void Selection::unregister_with_buffer() +{ + const_cast(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 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 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(m_first.buffer()).register_modification_listener(this); -} - -void Selection::unregister_with_buffer() -{ - const_cast(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 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) -{ - selections().clear(); - selections().push_back(Selection(iterator, iterator)); - - scroll_to_keep_cursor_visible_ifn(); -} - void Window::update_display_buffer() { + scroll_to_keep_cursor_visible_ifn(); + 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) +void Window::on_end_incremental_insert() { - if (m_filters.contains(filter.first)) - throw id_not_unique(filter.first); - m_filters.append(std::forward(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(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) -{ - 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 -#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 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 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 Selector; - typedef std::function 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 on_end_incremental_insert(); + 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(); } - - friend class IncrementalInserter; - IncrementalInserter* m_current_inserter; - - Buffer& m_buffer; BufferCoord m_position; DisplayCoord m_dimensions; - std::vector m_selections; DisplayBuffer m_display_buffer; HighlighterGroup m_highlighters; - idvaluemap 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