diff --git a/src/resources/vnote.ini b/src/resources/vnote.ini index f3b7b6c6..d6e42680 100644 --- a/src/resources/vnote.ini +++ b/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 diff --git a/src/utils/veditutils.cpp b/src/utils/veditutils.cpp index ace042df..3dd81fab 100644 --- a/src/utils/veditutils.cpp +++ b/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(); +} + diff --git a/src/utils/veditutils.h b/src/utils/veditutils.h index 62d9f930..1272d44f 100644 --- a/src/utils/veditutils.h +++ b/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() {} }; diff --git a/src/utils/vmetawordmanager.cpp b/src/utils/vmetawordmanager.cpp index d32efb2f..66cd4a67 100644 --- a/src/utils/vmetawordmanager.cpp +++ b/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; } diff --git a/src/utils/vmetawordmanager.h b/src/utils/vmetawordmanager.h index abfb642a..cfba310b 100644 --- a/src/utils/vmetawordmanager.h +++ b/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 parseToTokens(const QString &p_text); diff --git a/src/utils/vvim.cpp b/src/utils/vvim.cpp index befb19c2..51c6d941 100644 --- a/src/utils/vvim.cpp +++ b/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); diff --git a/src/vedit.cpp b/src/vedit.cpp index 9e425b55..46b3d8ec 100644 --- a/src/vedit.cpp +++ b/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); + } +} diff --git a/src/vedit.h b/src/vedit.h index 12b4e6c9..bb9a9961 100644 --- a/src/vedit.h +++ b/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(); diff --git a/src/veditarea.cpp b/src/veditarea.cpp index 31474e19..69fd5be9 100644 --- a/src/veditarea.cpp +++ b/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(p_target); obj->removeCurrentWindow(); } + +void VEditArea::evaluateMagicWordsByCaptain(void *p_target, void *p_data) +{ + Q_UNUSED(p_data); + VEditArea *obj = static_cast(p_target); + VEditTab *tab = obj->getCurrentTab(); + if (tab) { + tab->evaluateMagicWords(); + } +} + diff --git a/src/veditarea.h b/src/veditarea.h index 75c0687f..0172f8a0 100644 --- a/src/veditarea.h +++ b/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; diff --git a/src/veditoperations.h b/src/veditoperations.h index cc6b2371..6eed4299 100644 --- a/src/veditoperations.h +++ b/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 diff --git a/src/vedittab.cpp b/src/vedittab.cpp index 52b6aefa..0e487490 100644 --- a/src/vedittab.cpp +++ b/src/vedittab.cpp @@ -110,3 +110,7 @@ void VEditTab::updateStatus() { emit statusUpdated(fetchTabInfo()); } + +void VEditTab::evaluateMagicWords() +{ +} diff --git a/src/vedittab.h b/src/vedittab.h index 9e6eba22..e1f30460 100644 --- a/src/vedittab.h +++ b/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; diff --git a/src/vmdtab.cpp b/src/vmdtab.cpp index f3cd018b..67943180 100644 --- a/src/vmdtab.cpp +++ b/src/vmdtab.cpp @@ -685,3 +685,10 @@ bool VMdTab::isHeadingSequenceEnabled() const { return m_enableHeadingSequence; } + +void VMdTab::evaluateMagicWords() +{ + if (isEditMode() && m_file->isModifiable()) { + getEditor()->evaluateMagicWords(); + } +} diff --git a/src/vmdtab.h b/src/vmdtab.h index 15417129..ec06b2ac 100644 --- a/src/vmdtab.h +++ b/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;