Browse Source

vim-mode: support Copy/Paste/Change/Indent/UnIndent/ToLower/ToUpper action

Le Tan 8 years ago
parent
commit
5047e19b24
5 changed files with 1654 additions and 114 deletions
  1. 78 1
      src/utils/veditutils.cpp
  2. 27 0
      src/utils/veditutils.h
  3. 1461 59
      src/utils/vvim.cpp
  4. 84 6
      src/utils/vvim.h
  5. 4 48
      src/vmdeditoperations.cpp

+ 78 - 1
src/utils/veditutils.cpp

@@ -19,7 +19,7 @@ void VEditUtils::removeBlock(QTextCursor &p_cursor, QString *p_text)
 
     p_cursor.select(QTextCursor::BlockUnderCursor);
     if (p_text) {
-        *p_text = p_cursor.selectedText() + "\n";
+        *p_text = selectedText(p_cursor) + "\n";
     }
 
     p_cursor.deleteChar();
@@ -133,3 +133,80 @@ void VEditUtils::moveCursorFirstNonSpaceCharacter(QTextCursor &p_cursor,
     p_cursor.setPosition(block.position() + idx, p_mode);
 }
 
+void VEditUtils::removeObjectReplacementCharacter(QString &p_text)
+{
+    QRegExp orcBlockExp(QString("[\\n|^][ |\\t]*\\xfffc[ |\\t]*(?=\\n)"));
+    p_text.remove(orcBlockExp);
+    p_text.remove(QChar::ObjectReplacementCharacter);
+}
+
+QString VEditUtils::selectedText(const QTextCursor &p_cursor)
+{
+    QString text = p_cursor.selectedText();
+    text.replace(QChar::ParagraphSeparator, '\n');
+    return text;
+}
+
+// Use another QTextCursor to remain the selection.
+void VEditUtils::indentSelectedBlocks(const QTextDocument *p_doc,
+                                      const QTextCursor &p_cursor,
+                                      const QString &p_indentationText,
+                                      bool p_isIndent)
+{
+    int nrBlocks = 1;
+    int start = p_cursor.selectionStart();
+    int end = p_cursor.selectionEnd();
+
+    QTextBlock sBlock = p_doc->findBlock(start);
+    if (start != end) {
+        QTextBlock eBlock = p_doc->findBlock(end);
+        nrBlocks = eBlock.blockNumber() - sBlock.blockNumber() + 1;
+    }
+
+    QTextCursor bCursor(sBlock);
+    bCursor.beginEditBlock();
+    for (int i = 0; i < nrBlocks; ++i) {
+        if (p_isIndent) {
+            indentBlock(bCursor, p_indentationText);
+        } else {
+            unindentBlock(bCursor, p_indentationText);
+        }
+
+        bCursor.movePosition(QTextCursor::NextBlock);
+    }
+    bCursor.endEditBlock();
+}
+
+void VEditUtils::indentBlock(QTextCursor &p_cursor,
+                             const QString &p_indentationText)
+{
+    QTextBlock block = p_cursor.block();
+    if (block.length() > 1) {
+        p_cursor.movePosition(QTextCursor::StartOfBlock);
+        p_cursor.insertText(p_indentationText);
+    }
+}
+
+void VEditUtils::unindentBlock(QTextCursor &p_cursor,
+                               const QString &p_indentationText)
+{
+    QTextBlock block = p_cursor.block();
+    QString text = block.text();
+    if (text.isEmpty()) {
+        return;
+    }
+
+    p_cursor.movePosition(QTextCursor::StartOfBlock);
+    if (text[0] == '\t') {
+        p_cursor.deleteChar();
+    } else if (text[0].isSpace()) {
+        int width = p_indentationText.size();
+        for (int i = 0; i < width; ++i) {
+            if (text[i] == ' ') {
+                p_cursor.deleteChar();
+            } else {
+                break;
+            }
+        }
+    }
+}

+ 27 - 0
src/utils/veditutils.h

@@ -4,6 +4,8 @@
 #include <QTextBlock>
 #include <QTextCursor>
 
+class QTextDocument;
+
 // Utils for text edit.
 class VEditUtils
 {
@@ -36,6 +38,31 @@ public:
     // Need to call setTextCursor() to make it take effect.
     static bool insertListMarkAsPreviousBlock(QTextCursor &p_cursor);
 
+    // Remove ObjectReplaceCharacter in p_text.
+    // If the ObjectReplaceCharacter is in a block with only other spaces, remove the
+    // whole block.
+    static void removeObjectReplacementCharacter(QString &p_text);
+
+    // p_cursor.selectedText() will use U+2029 (QChar::ParagraphSeparator)
+    // instead of \n for a new line.
+    // This function will translate it to \n.
+    static QString selectedText(const QTextCursor &p_cursor);
+
+    // Indent selected blocks. If no selection, indent current block.
+    // @p_isIndent: whether it is indentation or unindentation.
+    static void indentSelectedBlocks(const QTextDocument *p_doc,
+                                     const QTextCursor &p_cursor,
+                                     const QString &p_indentationText,
+                                     bool p_isIndent);
+
+    // Indent current block.
+    // Skip empty block.
+    static void indentBlock(QTextCursor &p_cursor,
+                            const QString &p_indentationText);
+
+    static void unindentBlock(QTextCursor &p_cursor,
+                              const QString &p_indentationText);
+
 private:
     VEditUtils() {}
 };

File diff suppressed because it is too large
+ 1461 - 59
src/utils/vvim.cpp


+ 84 - 6
src/utils/vvim.h

@@ -5,6 +5,7 @@
 #include <QString>
 #include <QTextCursor>
 #include <QHash>
+#include <QDebug>
 #include "vutils.h"
 
 class VEdit;
@@ -101,6 +102,7 @@ private:
         Delete,
         Copy,
         Paste,
+        PasteBefore,
         Change,
         Indent,
         UnIndent,
@@ -143,7 +145,22 @@ private:
     enum class Range
     {
         Line = 0,
-        Word,
+        WordInner,
+        WordAround,
+        WORDInner,
+        WORDAround,
+        QuoteInner,
+        QuoteAround,
+        DoubleQuoteInner,
+        DoubleQuoteAround,
+        ParenthesisInner,
+        ParenthesisAround,
+        BracketInner,
+        BracketAround,
+        AngleBracketInner,
+        AngleBracketAround,
+        BraceInner,
+        BraceAround,
         Invalid
     };
 
@@ -257,6 +274,26 @@ private:
             return m_name == c_blackHoleRegister;
         }
 
+        bool isSelectionRegister() const
+        {
+            return m_name == c_selectionRegister;
+        }
+
+        bool isBlock() const
+        {
+            return m_value.endsWith('\n');
+        }
+
+        // @p_value is the content to update.
+        // If @p_value ends with \n, then it is a block.
+        // When @p_value is a block, we need to add \n at the end if necessary.
+        // If @m_append is true and @p_value is a block, we need to add \n between
+        // them if necessary.
+        void update(const QString &p_value);
+
+        // Read the value of this register.
+        const QString &read();
+
         QChar m_name;
         QString m_value;
 
@@ -287,6 +324,21 @@ private:
     // @p_tokens is the arguments of the Action::Delete action.
     void processDeleteAction(QList<Token> &p_tokens);
 
+    // @p_tokens is the arguments of the Action::Copy action.
+    void processCopyAction(QList<Token> &p_tokens);
+
+    // @p_tokens is the arguments of the Action::Paste and Action::PasteBefore action.
+    void processPasteAction(QList<Token> &p_tokens, bool p_pasteBefore);
+
+    // @p_tokens is the arguments of the Action::Change action.
+    void processChangeAction(QList<Token> &p_tokens);
+
+    // @p_tokens is the arguments of the Action::Indent and Action::UnIndent action.
+    void processIndentAction(QList<Token> &p_tokens, bool p_isIndent);
+
+    // @p_tokens is the arguments of the Action::ToLower and Action::ToUpper action.
+    void processToLowerAction(QList<Token> &p_tokens, bool p_toLower);
+
     // Clear selection if there is any.
     // Returns true if there is selection.
     bool clearSelection();
@@ -331,19 +383,44 @@ private:
 
     // Delete selected text if there is any.
     // @p_clearEmptyBlock: whether to remove the empty block after deletion.
-    void deleteSelectedText(bool p_clearEmptyBlock);
-
     void deleteSelectedText(QTextCursor &p_cursor, bool p_clearEmptyBlock);
 
-    // Save @p_text to the Register pointed by m_register.
+    // Copy selected text if there is any.
+    // Will clear selection.
+    // @p_addNewLine: whether to add a new line \n to the selection.
+    void copySelectedText(bool p_addNewLine);
+
+    void copySelectedText(QTextCursor &p_cursor, bool p_addNewLine);
+
+    // Convert the case of selected text if there is any.
+    // Will clear selection.
+    // @p_toLower: to lower or upper.
+    void convertCaseOfSelectedText(QTextCursor &p_cursor, bool p_toLower);
+
+    // Save @p_text to the Register pointed by m_regName.
+    // Remove QChar::ObjectReplacementCharacter before saving.
     void saveToRegister(const QString &p_text);
 
     // Move @p_cursor according to @p_moveMode and @p_movement.
     // Return true if it has moved @p_cursor.
     bool processMovement(QTextCursor &p_cursor, const QTextDocument *p_doc,
-                         QTextCursor::MoveMode &p_moveMode,
+                         QTextCursor::MoveMode p_moveMode,
                          Movement p_movement, int p_repeat);
 
+    // Move @p_cursor according to @p_moveMode and @p_range.
+    // Return true if it has moved @p_cursor.
+    bool selectRange(QTextCursor &p_cursor, const QTextDocument *p_doc,
+                     Range p_range, int p_repeat);
+
+    // Check if there is an Action token with Delete/Copy/Change action.
+    bool hasActionTokenValidForTextObject() const;
+
+    // Check if m_keys only contains @p_key.
+    bool checkPendingKey(const Key &p_key) const;
+
+    // Check if m_tokens only contains action token @p_action.
+    bool checkActionToken(Action p_action) const;
+
     VEdit *m_editor;
     const VEditConfig *m_editConfig;
     VimMode m_mode;
@@ -351,6 +428,7 @@ private:
     // A valid command token should follow the rule:
     // Action, Repeat, Movement.
     // Action, Repeat, Range.
+    // Action, Repeat.
     QList<Key> m_keys;
     QList<Token> m_tokens;
 
@@ -360,7 +438,7 @@ private:
     QHash<QChar, Register> m_registers;
 
     // Currently used register.
-    QChar m_register;
+    QChar m_regName;
 
     static const QChar c_unnamedRegister;
     static const QChar c_blackHoleRegister;

+ 4 - 48
src/vmdeditoperations.cpp

@@ -363,23 +363,9 @@ bool VMdEditOperations::handleKeyTab(QKeyEvent *p_event)
             m_autoIndentPos = -1;
             cursor.beginEditBlock();
             // Indent each selected line.
-            QTextBlock block = doc->findBlock(cursor.selectionStart());
-            QTextBlock endBlock = doc->findBlock(cursor.selectionEnd());
-            int endBlockNum = endBlock.blockNumber();
-            while (true) {
-                Q_ASSERT(block.isValid());
-                if (!block.text().isEmpty()) {
-                    QTextCursor blockCursor(block);
-                    blockCursor.insertText(text);
-                }
-
-                if (block.blockNumber() == endBlockNum) {
-                    break;
-                }
-
-                block = block.next();
-            }
+            VEditUtils::indentSelectedBlocks(doc, cursor, text, true);
             cursor.endEditBlock();
+            m_editor->setTextCursor(cursor);
         } else {
             // If it is a Tab key following auto list, increase the indent level.
             QTextBlock block = cursor.block();
@@ -417,49 +403,19 @@ bool VMdEditOperations::handleKeyBackTab(QKeyEvent *p_event)
     QTextDocument *doc = m_editor->document();
     QTextCursor cursor = m_editor->textCursor();
     QTextBlock block = doc->findBlock(cursor.selectionStart());
-    QTextBlock endBlock = doc->findBlock(cursor.selectionEnd());
-
     bool continueAutoIndent = false;
     int seq = -1;
     if (cursor.position() == m_autoIndentPos && isListBlock(block, &seq) &&
         !cursor.hasSelection()) {
         continueAutoIndent = true;
     }
-    int endBlockNum = endBlock.blockNumber();
+
     cursor.beginEditBlock();
     if (continueAutoIndent && seq != -1) {
         changeListBlockSeqNumber(block, 1);
     }
 
-    for (; block.isValid() && block.blockNumber() <= endBlockNum;
-         block = block.next()) {
-        QTextCursor blockCursor(block);
-        QString text = block.text();
-        if (text.isEmpty()) {
-            continue;
-        } else if (text[0] == '\t') {
-            blockCursor.deleteChar();
-            continue;
-        } else if (text[0] != ' ') {
-            continue;
-        } else {
-            // Spaces.
-            if (m_editConfig->m_expandTab) {
-                int width = m_editConfig->m_tabSpaces.size();
-                for (int i = 0; i < width; ++i) {
-                    if (text[i] == ' ') {
-                        blockCursor.deleteChar();
-                    } else {
-                        break;
-                    }
-                }
-                continue;
-            } else {
-                blockCursor.deleteChar();
-                continue;
-            }
-        }
-    }
+    VEditUtils::indentSelectedBlocks(doc, cursor, m_editConfig->m_tabSpaces, false);
     cursor.endEditBlock();
 
     if (continueAutoIndent) {

Some files were not shown because too many files changed in this diff