LibGUI: Indent selected text on tab press

If selected text is less than a whole line, usual delete/replace takes
place. Otherwise, if the selected text is a whole line or spans
multiple lines, the selection will be indented.
This commit is contained in:
huttongrabiel 2022-06-18 12:02:51 -07:00
parent e98f8f20df
commit 00f51d42d2
4 changed files with 80 additions and 2 deletions

View file

@ -932,6 +932,33 @@ String ReplaceAllTextCommand::action_text() const
return m_action_text;
}
IndentSelection::IndentSelection(TextDocument& document, size_t tab_width, TextRange const& range)
: TextDocumentUndoCommand(document)
, m_tab_width(tab_width)
, m_range(range)
{
}
void IndentSelection::redo()
{
auto const tab = String::repeated(' ', m_tab_width);
for (size_t i = m_range.start().line(); i <= m_range.end().line(); i++) {
m_document.insert_at({ i, 0 }, tab, m_client);
}
m_document.set_all_cursors(m_range.start());
}
void IndentSelection::undo()
{
for (size_t i = m_range.start().line(); i <= m_range.end().line(); i++) {
m_document.remove({ { i, 0 }, { i, m_tab_width } });
}
m_document.set_all_cursors(m_range.start());
}
TextPosition TextDocument::insert_at(TextPosition const& position, StringView text, Client const* client)
{
TextPosition cursor = position;

View file

@ -259,4 +259,16 @@ private:
String m_action_text;
};
class IndentSelection : public TextDocumentUndoCommand {
public:
IndentSelection(TextDocument&, size_t tab_width, TextRange const&);
virtual void undo() override;
virtual void redo() override;
TextRange const& range() const { return m_range; }
private:
size_t m_tab_width { 0 };
TextRange m_range;
};
}

View file

@ -867,6 +867,15 @@ void TextEditor::keydown_event(KeyEvent& event)
return;
}
if (event.key() == KeyCode::Key_Tab) {
if (has_selection()) {
if (is_indenting_selection()) {
indent_selection();
return;
}
}
}
if (event.key() == KeyCode::Key_Delete) {
if (!is_editable())
return;
@ -952,6 +961,33 @@ void TextEditor::keydown_event(KeyEvent& event)
event.ignore();
}
bool TextEditor::is_indenting_selection()
{
auto const selection_start = m_selection.start() > m_selection.end() ? m_selection.end() : m_selection.start();
auto const selection_end = m_selection.end() > m_selection.start() ? m_selection.end() : m_selection.start();
auto const whole_line_selected = selection_end.column() - selection_start.column() >= current_line().length() - current_line().first_non_whitespace_column();
auto const on_same_line = selection_start.line() == selection_end.line();
if (has_selection() && (whole_line_selected || !on_same_line)) {
return true;
}
return false;
}
void TextEditor::indent_selection()
{
auto const selection_start = m_selection.start() > m_selection.end() ? m_selection.end() : m_selection.start();
auto const selection_end = m_selection.end() > m_selection.start() ? m_selection.end() : m_selection.start();
if (is_indenting_selection()) {
execute<IndentSelection>(m_soft_tab_width, TextRange(selection_start, selection_end));
m_selection.set_start({ selection_start.line(), selection_start.column() + m_soft_tab_width });
m_selection.set_end({ selection_end.line(), selection_end.column() + m_soft_tab_width });
set_cursor({ m_cursor.line(), m_cursor.column() + m_soft_tab_width });
}
}
void TextEditor::delete_previous_word()
{
TextRange to_erase(document().first_word_before(m_cursor, true), m_cursor);
@ -1444,7 +1480,7 @@ void TextEditor::insert_at_cursor_or_replace_selection(StringView text)
{
ReflowDeferrer defer(*this);
VERIFY(is_editable());
if (has_selection())
if (has_selection() && !is_indenting_selection())
delete_selection();
// Check if adding a newline leaves the previous line as just whitespace.
@ -1453,7 +1489,8 @@ void TextEditor::insert_at_cursor_or_replace_selection(StringView text)
&& clear_length > 0
&& current_line().leading_spaces() == clear_length;
execute<InsertTextCommand>(text, m_cursor);
if (!is_indenting_selection())
execute<InsertTextCommand>(text, m_cursor);
if (should_clear_last_line) { // If it does leave just whitespace, clear it.
auto const original_cursor_position = cursor();

View file

@ -151,6 +151,8 @@ public:
void select_current_line();
virtual void undo();
virtual void redo();
bool is_indenting_selection();
void indent_selection();
Function<void()> on_change;
Function<void(bool modified)> on_modified_change;