diff options
Diffstat (limited to 'Libraries')
-rw-r--r-- | Libraries/LibGUI/TextDocument.cpp | 105 | ||||
-rw-r--r-- | Libraries/LibGUI/TextDocument.h | 3 | ||||
-rw-r--r-- | Libraries/LibGUI/TextEditor.h | 1 |
3 files changed, 65 insertions, 44 deletions
diff --git a/Libraries/LibGUI/TextDocument.cpp b/Libraries/LibGUI/TextDocument.cpp index e3fa5d804e..0c2003b891 100644 --- a/Libraries/LibGUI/TextDocument.cpp +++ b/Libraries/LibGUI/TextDocument.cpp @@ -112,6 +112,17 @@ bool TextDocumentLine::ends_in_whitespace() const return isspace(code_points()[length() - 1]); } +size_t TextDocumentLine::leading_spaces() const +{ + size_t count = 0; + for (; count < m_text.size(); ++count) { + if (m_text[count] != ' ') { + break; + } + } + return count; +} + String TextDocumentLine::to_utf8() const { StringBuilder builder; @@ -661,6 +672,52 @@ InsertTextCommand::InsertTextCommand(TextDocument& document, const String& text, { } +void InsertTextCommand::perform_formatting(const TextDocument::Client& client) +{ + const size_t tab_width = client.soft_tab_width(); + const auto& dest_line = m_document.line(m_range.start().line()); + const bool should_auto_indent = client.is_automatic_indentation_enabled(); + + StringBuilder builder; + size_t column = dest_line.length(); + size_t line_indentation = dest_line.leading_spaces(); + bool at_start_of_line = line_indentation == column; + + for (auto input_char : m_text) { + if (input_char == '\n') { + builder.append('\n'); + column = 0; + if (should_auto_indent) { + for (; column < line_indentation; ++column) { + builder.append(' '); + } + } + at_start_of_line = true; + } else if (input_char == '\t') { + size_t next_soft_tab_stop = ((column + tab_width) / tab_width) * tab_width; + size_t spaces_to_insert = next_soft_tab_stop - column; + for (size_t i = 0; i < spaces_to_insert; ++i) { + builder.append(' '); + } + column = next_soft_tab_stop; + if (at_start_of_line) { + line_indentation = column; + } + } else { + if (input_char == ' ') { + if (at_start_of_line) { + ++line_indentation; + } + } else { + at_start_of_line = false; + } + builder.append(input_char); + ++column; + } + } + m_text = builder.build(); +} + void InsertTextCommand::redo() { auto new_cursor = m_document.insert_at(m_range.start(), m_text, m_client); @@ -709,60 +766,20 @@ TextPosition TextDocument::insert_at(const TextPosition& position, const StringV return cursor; } -TextPosition TextDocument::insert_at(const TextPosition& position, u32 code_point, const Client* client) +TextPosition TextDocument::insert_at(const TextPosition& position, u32 code_point, const Client*) { - bool automatic_indentation_enabled = client ? client->is_automatic_indentation_enabled() : false; - size_t m_soft_tab_width = client ? client->soft_tab_width() : 4; - - bool at_head = position.column() == 0; - bool at_tail = position.column() == line(position.line()).length(); if (code_point == '\n') { - if (at_tail || at_head) { - String new_line_contents; - if (automatic_indentation_enabled && at_tail) { - size_t leading_spaces = 0; - auto& old_line = lines()[position.line()]; - for (size_t i = 0; i < old_line.length(); ++i) { - if (old_line.code_points()[i] == ' ') - ++leading_spaces; - else - break; - } - if (leading_spaces) - new_line_contents = String::repeated(' ', leading_spaces); - } - - size_t row = position.line(); - Vector<u32> line_content; - for (size_t i = position.column(); i < line(row).length(); i++) - line_content.append(line(row).code_points()[i]); - insert_line(position.line() + (at_tail ? 1 : 0), make<TextDocumentLine>(*this, new_line_contents)); - notify_did_change(); - return { position.line() + 1, line(position.line() + 1).length() }; - } auto new_line = make<TextDocumentLine>(*this); new_line->append(*this, line(position.line()).code_points() + position.column(), line(position.line()).length() - position.column()); - - Vector<u32> line_content; - for (size_t i = 0; i < new_line->length(); i++) - line_content.append(new_line->code_points()[i]); line(position.line()).truncate(*this, position.column()); insert_line(position.line() + 1, move(new_line)); notify_did_change(); return { position.line() + 1, 0 }; - } - if (code_point == '\t') { - size_t next_soft_tab_stop = ((position.column() + m_soft_tab_width) / m_soft_tab_width) * m_soft_tab_width; - size_t spaces_to_insert = next_soft_tab_stop - position.column(); - for (size_t i = 0; i < spaces_to_insert; ++i) { - line(position.line()).insert(*this, position.column(), ' '); - } + } else { + line(position.line()).insert(*this, position.column(), code_point); notify_did_change(); - return { position.line(), next_soft_tab_stop }; + return { position.line(), position.column() + 1 }; } - line(position.line()).insert(*this, position.column(), code_point); - notify_did_change(); - return { position.line(), position.column() + 1 }; } void TextDocument::remove(const TextRange& unnormalized_range) diff --git a/Libraries/LibGUI/TextDocument.h b/Libraries/LibGUI/TextDocument.h index d20ef059da..8b94667187 100644 --- a/Libraries/LibGUI/TextDocument.h +++ b/Libraries/LibGUI/TextDocument.h @@ -194,6 +194,7 @@ public: Optional<size_t> last_non_whitespace_column() const; bool ends_in_whitespace() const; bool is_empty() const { return length() == 0; } + size_t leading_spaces() const; private: // NOTE: This vector is null terminated. @@ -204,6 +205,7 @@ class TextDocumentUndoCommand : public Command { public: TextDocumentUndoCommand(TextDocument&); virtual ~TextDocumentUndoCommand(); + virtual void perform_formatting(const TextDocument::Client&) { } void execute_from(const TextDocument::Client& client) { @@ -220,6 +222,7 @@ protected: class InsertTextCommand : public TextDocumentUndoCommand { public: InsertTextCommand(TextDocument&, const String&, const TextPosition&); + virtual void perform_formatting(const TextDocument::Client&) override; virtual void undo() override; virtual void redo() override; virtual bool is_insert_text() const override { return true; } diff --git a/Libraries/LibGUI/TextEditor.h b/Libraries/LibGUI/TextEditor.h index 693cb18241..6f55d39a44 100644 --- a/Libraries/LibGUI/TextEditor.h +++ b/Libraries/LibGUI/TextEditor.h @@ -260,6 +260,7 @@ private: inline void execute(Args&&... args) { auto command = make<T>(*m_document, forward<Args>(args)...); + command->perform_formatting(*this); on_edit_action(*command); command->execute_from(*this); m_document->add_to_undo_stack(move(command)); |