Browse Source

metaword: support Ctrl+E M in editor to evaluate metawords

Add shortcut configuration MagicWord.
Le Tan 8 years ago
parent
commit
92bc044cac

+ 2 - 0
src/resources/vnote.ini

@@ -247,3 +247,5 @@ MoveTabSplitRight=Shift+L
 VerticalSplit=V
 ; Remove current split
 RemoveSplit=R
+; Evaluate selected text or cursor word as magic words
+MagicWord=M

+ 54 - 0
src/utils/veditutils.cpp

@@ -735,3 +735,57 @@ void VEditUtils::insertTitleMark(QTextCursor &p_cursor,
     // Go to the end of this block.
     p_cursor.movePosition(QTextCursor::EndOfBlock);
 }
+
+void VEditUtils::findCurrentWord(QTextCursor p_cursor,
+                                 int &p_start,
+                                 int &p_end)
+{
+    QString text = p_cursor.block().text();
+    int pib = p_cursor.positionInBlock();
+
+    if (pib < text.size() && text[pib].isSpace()) {
+        p_start = p_end = p_cursor.position();
+        return;
+    }
+
+    p_cursor.movePosition(QTextCursor::StartOfWord);
+    p_start = p_cursor.position();
+    p_cursor.movePosition(QTextCursor::EndOfWord);
+    p_end = p_cursor.position();
+}
+
+void VEditUtils::findCurrentWORD(const QTextCursor &p_cursor,
+                                 int &p_start,
+                                 int &p_end)
+{
+    QTextBlock block = p_cursor.block();
+    QString text = block.text();
+    int pib = p_cursor.positionInBlock();
+
+    if (pib < text.size() && text[pib].isSpace()) {
+        p_start = p_end = p_cursor.position();
+        return;
+    }
+
+    // Find the start.
+    p_start = 0;
+    for (int i = pib - 1; i >= 0; --i) {
+        if (text[i].isSpace()) {
+            p_start = i + 1;
+            break;
+        }
+    }
+
+    // Find the end.
+    p_end = block.length() - 1;
+    for (int i = pib; i < text.size(); ++i) {
+        if (text[i].isSpace()) {
+            p_end = i;
+            break;
+        }
+    }
+
+    p_start += block.position();
+    p_end += block.position();
+}
+

+ 15 - 0
src/utils/veditutils.h

@@ -142,6 +142,21 @@ public:
                                 const QTextBlock &p_block,
                                 int p_level);
 
+    // Find the start and end of the word @p_cursor locates in (within a single block).
+    // @p_start and @p_end will be the global position of the start and end of the word.
+    // @p_start will equals to @p_end if @p_cursor is a space.
+    static void findCurrentWord(QTextCursor p_cursor,
+                                int &p_start,
+                                int &p_end);
+
+    // Find the start and end of the WORD @p_cursor locates in (within a single block).
+    // @p_start and @p_end will be the global position of the start and end of the WORD.
+    // @p_start will equals to @p_end if @p_cursor is a space.
+    // Attention: www|sss will select www, which is different from findCurrentWord().
+    static void findCurrentWORD(const QTextCursor &p_cursor,
+                                int &p_start,
+                                int &p_end);
+
 private:
     VEditUtils() {}
 };

+ 8 - 5
src/utils/vmetawordmanager.cpp

@@ -273,7 +273,9 @@ QString VMetaWordManager::evaluate(const QString &p_text) const
     VMetaWord metaWord(this,
                        MetaWordType::Compound,
                        tmpWord,
-                       p_text);
+                       p_text,
+                       nullptr,
+                       true);
     if (metaWord.isValid()) {
         return metaWord.evaluate();
     } else {
@@ -322,7 +324,8 @@ VMetaWord::VMetaWord(const VMetaWordManager *p_manager,
                      MetaWordType p_type,
                      const QString &p_word,
                      const QString &p_definition,
-                     MetaWordFunc p_function)
+                     MetaWordFunc p_function,
+                     bool p_allowAllSpaces)
     : m_manager(p_manager),
       m_type(p_type),
       m_word(p_word),
@@ -338,7 +341,7 @@ VMetaWord::VMetaWord(const VMetaWordManager *p_manager,
         Q_ASSERT(m_function);
     }
 
-    checkAndParseDefinition();
+    checkAndParseDefinition(p_allowAllSpaces);
 }
 
 bool VMetaWord::checkType(MetaWordType p_type)
@@ -346,7 +349,7 @@ bool VMetaWord::checkType(MetaWordType p_type)
     return m_type == p_type;
 }
 
-void VMetaWord::checkAndParseDefinition()
+void VMetaWord::checkAndParseDefinition(bool p_allowAllSpaces)
 {
     if (m_word.contains(VMetaWordManager::c_delimiter)) {
         m_valid = false;
@@ -358,7 +361,7 @@ void VMetaWord::checkAndParseDefinition()
 
     // We do not accept \n and \t in the definition.
     QRegExp defReg("[\\S ]*");
-    if (!defReg.exactMatch(m_definition)) {
+    if (!defReg.exactMatch(m_definition) && !p_allowAllSpaces) {
         m_valid = false;
         return;
     }

+ 5 - 2
src/utils/vmetawordmanager.h

@@ -50,7 +50,8 @@ public:
               MetaWordType p_type,
               const QString &p_word,
               const QString &p_definition,
-              MetaWordFunc p_function = nullptr);
+              MetaWordFunc p_function = nullptr,
+              bool p_allowAllSpaces = false);
 
     bool isValid() const;
 
@@ -116,7 +117,9 @@ private:
 
     // Parse children word from definition.
     // Children word MUST be defined in m_manager already.
-    void checkAndParseDefinition();
+    // @p_allowAllSpaces: if true then we allow all spaces including \n and \t
+    // to appear in the definition.
+    void checkAndParseDefinition(bool p_allowAllSpaces);
 
     // Parse @p_text to a list of tokens.
     static QVector<VMetaWord::Token> parseToTokens(const QString &p_text);

+ 9 - 65
src/utils/vvim.cpp

@@ -171,62 +171,6 @@ static void findCurrentSpace(const QTextCursor &p_cursor, int &p_start, int &p_e
     p_end += block.position();
 }
 
-// Find the start and end of the word @p_cursor locates in (within a single block).
-// @p_start and @p_end will be the global position of the start and end of the word.
-// @p_start will equals to @p_end if @p_cursor is a space.
-static void findCurrentWord(QTextCursor p_cursor, int &p_start, int &p_end)
-{
-    QString text = p_cursor.block().text();
-    int pib = p_cursor.positionInBlock();
-
-    if (pib < text.size() && text[pib].isSpace()) {
-        p_start = p_end = p_cursor.position();
-        return;
-    }
-
-    p_cursor.movePosition(QTextCursor::StartOfWord);
-    p_start = p_cursor.position();
-    p_cursor.movePosition(QTextCursor::EndOfWord);
-    p_end = p_cursor.position();
-}
-
-// Find the start and end of the WORD @p_cursor locates in (within a single block).
-// @p_start and @p_end will be the global position of the start and end of the WORD.
-// @p_start will equals to @p_end if @p_cursor is a space.
-// Attention: www|sss will select www, which is different from findCurrentWord().
-static void findCurrentWORD(const QTextCursor &p_cursor, int &p_start, int &p_end)
-{
-    QTextBlock block = p_cursor.block();
-    QString text = block.text();
-    int pib = p_cursor.positionInBlock();
-
-    if (pib < text.size() && text[pib].isSpace()) {
-        p_start = p_end = p_cursor.position();
-        return;
-    }
-
-    // Find the start.
-    p_start = 0;
-    for (int i = pib - 1; i >= 0; --i) {
-        if (text[i].isSpace()) {
-            p_start = i + 1;
-            break;
-        }
-    }
-
-    // Find the end.
-    p_end = block.length() - 1;
-    for (int i = pib; i < text.size(); ++i) {
-        if (text[i].isSpace()) {
-            p_end = i;
-            break;
-        }
-    }
-
-    p_start += block.position();
-    p_end += block.position();
-}
-
 // Move @p_cursor to skip spaces if current cursor is placed at a space
 // (may move across blocks). It will stop by the empty block on the way.
 // Forward: wwwwsssss|wwww
@@ -2729,7 +2673,7 @@ bool VVim::processMovement(QTextCursor &p_cursor,
         for (int i = 0; i < p_repeat; ++i) {
             int start, end;
             // [start, end] is current WORD.
-            findCurrentWORD(p_cursor, start, end);
+            VEditUtils::findCurrentWORD(p_cursor, start, end);
 
             // Move cursor to end of current WORD.
             p_cursor.setPosition(end, p_moveMode);
@@ -2779,7 +2723,7 @@ bool VVim::processMovement(QTextCursor &p_cursor,
 
             int start, end;
             // [start, end] is current WORD.
-            findCurrentWORD(p_cursor, start, end);
+            VEditUtils::findCurrentWORD(p_cursor, start, end);
 
             // Move cursor to the end of current WORD.
             p_cursor.setPosition(end, p_moveMode);
@@ -2825,7 +2769,7 @@ bool VVim::processMovement(QTextCursor &p_cursor,
 
             int start, end;
             // [start, end] is current WORD.
-            findCurrentWORD(p_cursor, start, end);
+            VEditUtils::findCurrentWORD(p_cursor, start, end);
 
             // Move cursor to the start of current WORD.
             p_cursor.setPosition(start, p_moveMode);
@@ -2862,7 +2806,7 @@ bool VVim::processMovement(QTextCursor &p_cursor,
 
         for (int i = 0; i < p_repeat; ++i) {
             int start, end;
-            findCurrentWORD(p_cursor, start, end);
+            VEditUtils::findCurrentWORD(p_cursor, start, end);
 
             p_cursor.setPosition(start, p_moveMode);
 
@@ -3051,7 +2995,7 @@ handle_target:
         // Different from Vim:
         // We do not recognize a word as strict as Vim.
         int start, end;
-        findCurrentWord(p_cursor, start, end);
+        VEditUtils::findCurrentWord(p_cursor, start, end);
         if (start == end) {
             // Spaces, find next word.
             QTextCursor cursor = p_cursor;
@@ -3062,7 +3006,7 @@ handle_target:
                 }
 
                 if (!doc->characterAt(cursor.position()).isSpace()) {
-                    findCurrentWord(cursor, start, end);
+                    VEditUtils::findCurrentWord(cursor, start, end);
                     Q_ASSERT(start != end);
                     break;
                 }
@@ -3187,7 +3131,7 @@ bool VVim::selectRange(QTextCursor &p_cursor, const QTextDocument *p_doc,
         Q_ASSERT(p_repeat == -1);
         bool spaces = false;
         int start, end;
-        findCurrentWord(p_cursor, start, end);
+        VEditUtils::findCurrentWord(p_cursor, start, end);
 
         if (start == end) {
             // Select the space between previous word and next word.
@@ -3227,7 +3171,7 @@ bool VVim::selectRange(QTextCursor &p_cursor, const QTextDocument *p_doc,
         findCurrentSpace(p_cursor, start, end);
 
         if (start == end) {
-            findCurrentWORD(p_cursor, start, end);
+            VEditUtils::findCurrentWORD(p_cursor, start, end);
         } else {
             // Select the space between previous WORD and next WORD.
             spaces = true;
@@ -3246,7 +3190,7 @@ bool VVim::selectRange(QTextCursor &p_cursor, const QTextDocument *p_doc,
                         moveCursorAcrossSpaces(p_cursor, moveMode, true);
 
                         // [start, end] is current WORD.
-                        findCurrentWORD(p_cursor, start, end);
+                        VEditUtils::findCurrentWORD(p_cursor, start, end);
 
                         // Move cursor to the end of current WORD.
                         p_cursor.setPosition(end, moveMode);

+ 38 - 0
src/vedit.cpp

@@ -7,12 +7,17 @@
 #include "vtableofcontent.h"
 #include "utils/vutils.h"
 #include "utils/veditutils.h"
+#include "utils/vmetawordmanager.h"
 #include "veditoperations.h"
 #include "vedittab.h"
 
+
 extern VConfigManager *g_config;
+
 extern VNote *g_vnote;
 
+extern VMetaWordManager *g_mwMgr;
+
 void VEditConfig::init(const QFontMetrics &p_metric,
                        bool p_enableHeadingSequence)
 {
@@ -1388,3 +1393,36 @@ void VEdit::updateBlockLineDistanceHeight(int p_pos,
         cursor.endEditBlock();
     }
 }
+
+void VEdit::evaluateMagicWords()
+{
+    QString text;
+    QTextCursor cursor = textCursor();
+    if (!cursor.hasSelection()) {
+        // Get the WORD in current cursor.
+        int start, end;
+        VEditUtils::findCurrentWORD(cursor, start, end);
+
+        if (start == end) {
+            return;
+        } else {
+            cursor.setPosition(start);
+            cursor.setPosition(end, QTextCursor::KeepAnchor);
+        }
+    }
+
+    text = VEditUtils::selectedText(cursor);
+    Q_ASSERT(!text.isEmpty());
+    QString evaText = g_mwMgr->evaluate(text);
+    if (text != evaText) {
+        qDebug() << "evaluateMagicWords" << text << evaText;
+
+        cursor.insertText(evaText);
+
+        if (m_editOps) {
+            m_editOps->setVimMode(VimMode::Insert);
+        }
+
+        setTextCursor(cursor);
+    }
+}

+ 3 - 0
src/vedit.h

@@ -141,6 +141,9 @@ public:
 
     bool isBlockVisible(const QTextBlock &p_block);
 
+    // Evaluate selected text or cursor word as magic words.
+    void evaluateMagicWords();
+
 signals:
     // Request VEditTab to save and exit edit mode.
     void saveAndRead();

+ 15 - 0
src/veditarea.cpp

@@ -839,6 +839,10 @@ void VEditArea::registerCaptainTargets()
                                    g_config->getCaptainShortcutKeySequence("RemoveSplit"),
                                    this,
                                    removeSplitByCaptain);
+    captain->registerCaptainTarget(tr("MagicWord"),
+                                   g_config->getCaptainShortcutKeySequence("MagicWord"),
+                                   this,
+                                   evaluateMagicWordsByCaptain);
 }
 
 void VEditArea::activateTabByCaptain(void *p_target, void *p_data, int p_idx)
@@ -932,3 +936,14 @@ void VEditArea::removeSplitByCaptain(void *p_target, void *p_data)
     VEditArea *obj = static_cast<VEditArea *>(p_target);
     obj->removeCurrentWindow();
 }
+
+void VEditArea::evaluateMagicWordsByCaptain(void *p_target, void *p_data)
+{
+    Q_UNUSED(p_data);
+    VEditArea *obj = static_cast<VEditArea *>(p_target);
+    VEditTab *tab = obj->getCurrentTab();
+    if (tab) {
+        tab->evaluateMagicWords();
+    }
+}
+

+ 3 - 0
src/veditarea.h

@@ -188,6 +188,9 @@ private:
 
     static void removeSplitByCaptain(void *p_target, void *p_data);
 
+    // Evaluate selected text or the word on cursor as magic words.
+    static void evaluateMagicWordsByCaptain(void *p_target, void *p_data);
+
     // End Captain mode functions.
 
     int curWindowIndex;

+ 10 - 0
src/veditoperations.h

@@ -33,6 +33,9 @@ public:
     // Insert decoration markers or decorate selected text.
     virtual void decorateText(TextDecoration p_decoration) {Q_UNUSED(p_decoration);};
 
+    // Set Vim mode if not NULL.
+    void setVimMode(VimMode p_mode);
+
 signals:
     // Want to display a template message in status bar.
     void statusMessage(const QString &p_msg);
@@ -60,4 +63,11 @@ protected:
     VVim *m_vim;
 };
 
+inline void VEditOperations::setVimMode(VimMode p_mode)
+{
+    if (m_vim) {
+        m_vim->setMode(p_mode);
+    }
+}
+
 #endif // VEDITOPERATIONS_H

+ 4 - 0
src/vedittab.cpp

@@ -110,3 +110,7 @@ void VEditTab::updateStatus()
 {
     emit statusUpdated(fetchTabInfo());
 }
+
+void VEditTab::evaluateMagicWords()
+{
+}

+ 3 - 0
src/vedittab.h

@@ -82,6 +82,9 @@ public:
     // Emit signal to update current status.
     virtual void updateStatus();
 
+    // Called by evaluateMagicWordsByCaptain() to evaluate the magic words.
+    virtual void evaluateMagicWords();
+
 public slots:
     // Enter edit mode
     virtual void editFile() = 0;

+ 7 - 0
src/vmdtab.cpp

@@ -685,3 +685,10 @@ bool VMdTab::isHeadingSequenceEnabled() const
 {
     return m_enableHeadingSequence;
 }
+
+void VMdTab::evaluateMagicWords()
+{
+    if (isEditMode() && m_file->isModifiable()) {
+        getEditor()->evaluateMagicWords();
+    }
+}

+ 3 - 0
src/vmdtab.h

@@ -70,6 +70,9 @@ public:
 
     bool isHeadingSequenceEnabled() const;
 
+    // Evaluate magic words.
+    void evaluateMagicWords() Q_DECL_OVERRIDE;
+
 public slots:
     // Enter edit mode.
     void editFile() Q_DECL_OVERRIDE;