From 03d8ee1082d11fafae58f6697edb7e7a04ec6b82 Mon Sep 17 00:00:00 2001 From: Paul Berg Date: Mon, 26 Apr 2021 23:37:56 +0200 Subject: [PATCH] VimEditingEngine: allow selection of the endline character This patch fixes the visual selection of endline characters in the VimEditingEngine. When the visual mode is disabled and the cursor is located on the endline character, it is shifted back to the last character of the line. --- .../Libraries/LibGUI/VimEditingEngine.cpp | 40 ++++++++++++++----- Userland/Libraries/LibGUI/VimEditingEngine.h | 4 +- 2 files changed, 34 insertions(+), 10 deletions(-) diff --git a/Userland/Libraries/LibGUI/VimEditingEngine.cpp b/Userland/Libraries/LibGUI/VimEditingEngine.cpp index fd7eed398a..da687b614c 100644 --- a/Userland/Libraries/LibGUI/VimEditingEngine.cpp +++ b/Userland/Libraries/LibGUI/VimEditingEngine.cpp @@ -665,7 +665,7 @@ void VimMotion::calculate_find_range(VimCursor& cursor, int amount) m_find_mode = FindMode::None; } -Optional VimMotion::get_position(VimEditingEngine& engine) +Optional VimMotion::get_position(VimEditingEngine& engine, bool in_visual_mode) { auto range_optional = get_range(engine, true); if (!range_optional.has_value()) @@ -716,11 +716,13 @@ Optional VimMotion::get_position(VimEditingEngine& engine) // above in get_range normalizes some values which shouldn't be // end-exclusive during normal operations. bool is_at_start = range.end().column() == 0; - size_t column = is_at_start ? 0 : range.end().column() - 1; - // Need to not go beyond the last character, as standard in vim. auto& line = editor.line(range.end().line()); - return { TextPosition { range.end().line(), min(column, line.length() - 1) } }; + size_t column = is_at_start ? 0 : range.end().column() - 1; + column = min(column, line.length() - (in_visual_mode ? 0 : 1)); + // Need to not go beyond the last character, as standard in vim. + + return { TextPosition { range.end().line(), column } }; } } } @@ -1012,7 +1014,7 @@ bool VimEditingEngine::on_key_in_visual_mode(const KeyEvent& event) m_motion.add_key_code(event.key(), event.ctrl(), event.shift(), event.alt()); if (m_motion.is_complete()) { if (!m_motion.is_cancelled()) { - auto maybe_new_position = m_motion.get_position(*this); + auto maybe_new_position = m_motion.get_position(*this, true); if (maybe_new_position.has_value()) { auto new_position = maybe_new_position.value(); m_editor->set_cursor(new_position); @@ -1114,7 +1116,7 @@ bool VimEditingEngine::on_key_in_visual_mode(const KeyEvent& event) m_motion.add_key_code(event.key(), event.ctrl(), event.shift(), event.alt()); if (m_motion.is_complete()) { if (!m_motion.is_cancelled()) { - auto maybe_new_position = m_motion.get_position(*this); + auto maybe_new_position = m_motion.get_position(*this, true); if (maybe_new_position.has_value()) { auto new_position = maybe_new_position.value(); m_editor->set_cursor(new_position); @@ -1151,6 +1153,7 @@ void VimEditingEngine::switch_to_visual_mode() m_vim_mode = VimMode::Visual; m_editor->reset_cursor_blink(); m_previous_key = {}; + m_selection_start_position = m_editor->cursor(); m_editor->selection()->set(m_editor->cursor(), { m_editor->cursor().line(), m_editor->cursor().column() + 1 }); m_editor->did_update_selection(); m_motion.reset(); @@ -1159,18 +1162,37 @@ void VimEditingEngine::switch_to_visual_mode() void VimEditingEngine::update_selection_on_cursor_move() { auto cursor = m_editor->cursor(); - auto& line = m_editor->current_line(); - cursor.set_column(min(cursor.column() + 1, line.length())); - m_editor->selection()->set_end(cursor); + auto start = m_selection_start_position < cursor ? m_selection_start_position : cursor; + auto end = m_selection_start_position < cursor ? cursor : m_selection_start_position; + + if (end.column() >= m_editor->current_line().length()) { + if (end.line() != m_editor->line_count() - 1) + end = { end.line() + 1, 0 }; + } else { + end.set_column(end.column() + 1); + } + + m_editor->selection()->set(start, end); m_editor->did_update_selection(); } +void VimEditingEngine::clamp_cursor_position() +{ + auto cursor = m_editor->cursor(); + if (cursor.column() >= m_editor->current_line().length()) { + cursor.set_column(m_editor->current_line().length() - 1); + m_editor->set_cursor(cursor); + } +} + void VimEditingEngine::clear_visual_mode_data() { if (m_editor->has_selection()) { m_editor->selection()->clear(); m_editor->did_update_selection(); + clamp_cursor_position(); } + m_selection_start_position = {}; } void VimEditingEngine::move_half_page_up() diff --git a/Userland/Libraries/LibGUI/VimEditingEngine.h b/Userland/Libraries/LibGUI/VimEditingEngine.h index 26b470af9c..74ebb705ac 100644 --- a/Userland/Libraries/LibGUI/VimEditingEngine.h +++ b/Userland/Libraries/LibGUI/VimEditingEngine.h @@ -101,7 +101,7 @@ public: void add_key_code(KeyCode key, bool ctrl, bool shift, bool alt); Optional get_range(class VimEditingEngine& engine, bool normalize_for_position = false); - Optional get_position(VimEditingEngine& engine); + Optional get_position(VimEditingEngine& engine, bool in_visual_mode = false); void reset(); /// Returns whether the motion should consume the next character no matter what. @@ -168,7 +168,9 @@ private: void yank(TextRange); void put(); + TextPosition m_selection_start_position = {}; void update_selection_on_cursor_move(); + void clamp_cursor_position(); void clear_visual_mode_data(); KeyCode m_previous_key {};