diff --git a/Userland/Libraries/LibGUI/TextDocument.cpp b/Userland/Libraries/LibGUI/TextDocument.cpp index e116677f74..5c2b9c16c6 100644 --- a/Userland/Libraries/LibGUI/TextDocument.cpp +++ b/Userland/Libraries/LibGUI/TextDocument.cpp @@ -674,6 +674,39 @@ TextPosition TextDocument::first_word_break_after(const TextPosition& position) return target; } +TextPosition TextDocument::first_word_before(const TextPosition& position, bool start_at_column_before) const +{ + if (position.column() == 0) { + if (position.line() == 0) { + return TextPosition(0, 0); + } + auto previous_line = this->line(position.line() - 1); + return TextPosition(position.line() - 1, previous_line.length()); + } + + auto target = position; + auto line = this->line(target.line()); + if (target.column() == line.length()) + start_at_column_before = 1; + + auto nonblank_passed = !is_ascii_blank(line.code_points()[target.column() - start_at_column_before]); + while (target.column() > 0) { + auto prev_code_point = line.code_points()[target.column() - 1]; + nonblank_passed |= !is_ascii_blank(prev_code_point); + + if (nonblank_passed && is_ascii_blank(prev_code_point)) { + break; + } else if (is_ascii_punctuation(prev_code_point)) { + target.set_column(target.column() - 1); + break; + } + + target.set_column(target.column() - 1); + } + + return target; +} + void TextDocument::undo() { if (!can_undo()) diff --git a/Userland/Libraries/LibGUI/TextDocument.h b/Userland/Libraries/LibGUI/TextDocument.h index dfd3daf719..e1617694b5 100644 --- a/Userland/Libraries/LibGUI/TextDocument.h +++ b/Userland/Libraries/LibGUI/TextDocument.h @@ -106,6 +106,8 @@ public: TextPosition first_word_break_before(const TextPosition&, bool start_at_column_before) const; TextPosition first_word_break_after(const TextPosition&) const; + TextPosition first_word_before(const TextPosition&, bool start_at_column_before) const; + void add_to_undo_stack(NonnullOwnPtr); bool can_undo() const { return m_undo_stack.can_undo(); } diff --git a/Userland/Libraries/LibGUI/TextEditor.cpp b/Userland/Libraries/LibGUI/TextEditor.cpp index 0202ed163f..7fcdec582e 100644 --- a/Userland/Libraries/LibGUI/TextEditor.cpp +++ b/Userland/Libraries/LibGUI/TextEditor.cpp @@ -883,6 +883,12 @@ void TextEditor::keydown_event(KeyEvent& event) event.ignore(); } +void TextEditor::delete_previous_word() +{ + TextRange to_erase(document().first_word_before(m_cursor, true), m_cursor); + execute(document().text_in_range(to_erase), to_erase); +} + void TextEditor::delete_current_line() { if (has_selection()) diff --git a/Userland/Libraries/LibGUI/TextEditor.h b/Userland/Libraries/LibGUI/TextEditor.h index b2640ce837..1a7830397a 100644 --- a/Userland/Libraries/LibGUI/TextEditor.h +++ b/Userland/Libraries/LibGUI/TextEditor.h @@ -135,6 +135,7 @@ public: void paste(); void do_delete(); void delete_current_line(); + void delete_previous_word(); void select_all(); virtual void undo(); virtual void redo(); diff --git a/Userland/Libraries/LibGUI/VimEditingEngine.cpp b/Userland/Libraries/LibGUI/VimEditingEngine.cpp index 3cf3354147..a3530f6d32 100644 --- a/Userland/Libraries/LibGUI/VimEditingEngine.cpp +++ b/Userland/Libraries/LibGUI/VimEditingEngine.cpp @@ -794,6 +794,16 @@ bool VimEditingEngine::on_key_in_insert_mode(const KeyEvent& event) if (EditingEngine::on_key(event)) return true; + if (event.ctrl()) { + switch (event.key()) { + case KeyCode::Key_W: + m_editor->delete_previous_word(); + return true; + default: + break; + } + } + if (event.key() == KeyCode::Key_Escape || (event.ctrl() && event.key() == KeyCode::Key_LeftBracket) || (event.ctrl() && event.key() == KeyCode::Key_C)) { if (m_editor->cursor().column() > 0) move_one_left();