瀏覽代碼

LibGUI: Make autoformatting performed on InsertTextCommand visible to on_edit_action

Sahan Fernando 4 年之前
父節點
當前提交
8dc0d9b7b7
共有 3 個文件被更改,包括 65 次插入44 次删除
  1. 61 44
      Libraries/LibGUI/TextDocument.cpp
  2. 3 0
      Libraries/LibGUI/TextDocument.h
  3. 1 0
      Libraries/LibGUI/TextEditor.h

+ 61 - 44
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)

+ 3 - 0
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; }

+ 1 - 0
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));