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

Add shortcut configuration MagicWord.
This commit is contained in:
Le Tan 2017-10-19 19:41:57 +08:00
parent dcb6227d3b
commit 92bc044cac
15 changed files with 179 additions and 72 deletions

View File

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

View File

@ -735,3 +735,57 @@ void VEditUtils::insertTitleMark(QTextCursor &p_cursor,
// Go to the end of this block. // Go to the end of this block.
p_cursor.movePosition(QTextCursor::EndOfBlock); 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();
}

View File

@ -142,6 +142,21 @@ public:
const QTextBlock &p_block, const QTextBlock &p_block,
int p_level); 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: private:
VEditUtils() {} VEditUtils() {}
}; };

View File

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

View File

@ -50,7 +50,8 @@ public:
MetaWordType p_type, MetaWordType p_type,
const QString &p_word, const QString &p_word,
const QString &p_definition, const QString &p_definition,
MetaWordFunc p_function = nullptr); MetaWordFunc p_function = nullptr,
bool p_allowAllSpaces = false);
bool isValid() const; bool isValid() const;
@ -116,7 +117,9 @@ private:
// Parse children word from definition. // Parse children word from definition.
// Children word MUST be defined in m_manager already. // 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. // Parse @p_text to a list of tokens.
static QVector<VMetaWord::Token> parseToTokens(const QString &p_text); static QVector<VMetaWord::Token> parseToTokens(const QString &p_text);

View File

@ -171,62 +171,6 @@ static void findCurrentSpace(const QTextCursor &p_cursor, int &p_start, int &p_e
p_end += block.position(); 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 // 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. // (may move across blocks). It will stop by the empty block on the way.
// Forward: wwwwsssss|wwww // Forward: wwwwsssss|wwww
@ -2729,7 +2673,7 @@ bool VVim::processMovement(QTextCursor &p_cursor,
for (int i = 0; i < p_repeat; ++i) { for (int i = 0; i < p_repeat; ++i) {
int start, end; int start, end;
// [start, end] is current WORD. // [start, end] is current WORD.
findCurrentWORD(p_cursor, start, end); VEditUtils::findCurrentWORD(p_cursor, start, end);
// Move cursor to end of current WORD. // Move cursor to end of current WORD.
p_cursor.setPosition(end, p_moveMode); p_cursor.setPosition(end, p_moveMode);
@ -2779,7 +2723,7 @@ bool VVim::processMovement(QTextCursor &p_cursor,
int start, end; int start, end;
// [start, end] is current WORD. // [start, end] is current WORD.
findCurrentWORD(p_cursor, start, end); VEditUtils::findCurrentWORD(p_cursor, start, end);
// Move cursor to the end of current WORD. // Move cursor to the end of current WORD.
p_cursor.setPosition(end, p_moveMode); p_cursor.setPosition(end, p_moveMode);
@ -2825,7 +2769,7 @@ bool VVim::processMovement(QTextCursor &p_cursor,
int start, end; int start, end;
// [start, end] is current WORD. // [start, end] is current WORD.
findCurrentWORD(p_cursor, start, end); VEditUtils::findCurrentWORD(p_cursor, start, end);
// Move cursor to the start of current WORD. // Move cursor to the start of current WORD.
p_cursor.setPosition(start, p_moveMode); p_cursor.setPosition(start, p_moveMode);
@ -2862,7 +2806,7 @@ bool VVim::processMovement(QTextCursor &p_cursor,
for (int i = 0; i < p_repeat; ++i) { for (int i = 0; i < p_repeat; ++i) {
int start, end; int start, end;
findCurrentWORD(p_cursor, start, end); VEditUtils::findCurrentWORD(p_cursor, start, end);
p_cursor.setPosition(start, p_moveMode); p_cursor.setPosition(start, p_moveMode);
@ -3051,7 +2995,7 @@ handle_target:
// Different from Vim: // Different from Vim:
// We do not recognize a word as strict as Vim. // We do not recognize a word as strict as Vim.
int start, end; int start, end;
findCurrentWord(p_cursor, start, end); VEditUtils::findCurrentWord(p_cursor, start, end);
if (start == end) { if (start == end) {
// Spaces, find next word. // Spaces, find next word.
QTextCursor cursor = p_cursor; QTextCursor cursor = p_cursor;
@ -3062,7 +3006,7 @@ handle_target:
} }
if (!doc->characterAt(cursor.position()).isSpace()) { if (!doc->characterAt(cursor.position()).isSpace()) {
findCurrentWord(cursor, start, end); VEditUtils::findCurrentWord(cursor, start, end);
Q_ASSERT(start != end); Q_ASSERT(start != end);
break; break;
} }
@ -3187,7 +3131,7 @@ bool VVim::selectRange(QTextCursor &p_cursor, const QTextDocument *p_doc,
Q_ASSERT(p_repeat == -1); Q_ASSERT(p_repeat == -1);
bool spaces = false; bool spaces = false;
int start, end; int start, end;
findCurrentWord(p_cursor, start, end); VEditUtils::findCurrentWord(p_cursor, start, end);
if (start == end) { if (start == end) {
// Select the space between previous word and next word. // 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); findCurrentSpace(p_cursor, start, end);
if (start == end) { if (start == end) {
findCurrentWORD(p_cursor, start, end); VEditUtils::findCurrentWORD(p_cursor, start, end);
} else { } else {
// Select the space between previous WORD and next WORD. // Select the space between previous WORD and next WORD.
spaces = true; spaces = true;
@ -3246,7 +3190,7 @@ bool VVim::selectRange(QTextCursor &p_cursor, const QTextDocument *p_doc,
moveCursorAcrossSpaces(p_cursor, moveMode, true); moveCursorAcrossSpaces(p_cursor, moveMode, true);
// [start, end] is current WORD. // [start, end] is current WORD.
findCurrentWORD(p_cursor, start, end); VEditUtils::findCurrentWORD(p_cursor, start, end);
// Move cursor to the end of current WORD. // Move cursor to the end of current WORD.
p_cursor.setPosition(end, moveMode); p_cursor.setPosition(end, moveMode);

View File

@ -7,12 +7,17 @@
#include "vtableofcontent.h" #include "vtableofcontent.h"
#include "utils/vutils.h" #include "utils/vutils.h"
#include "utils/veditutils.h" #include "utils/veditutils.h"
#include "utils/vmetawordmanager.h"
#include "veditoperations.h" #include "veditoperations.h"
#include "vedittab.h" #include "vedittab.h"
extern VConfigManager *g_config; extern VConfigManager *g_config;
extern VNote *g_vnote; extern VNote *g_vnote;
extern VMetaWordManager *g_mwMgr;
void VEditConfig::init(const QFontMetrics &p_metric, void VEditConfig::init(const QFontMetrics &p_metric,
bool p_enableHeadingSequence) bool p_enableHeadingSequence)
{ {
@ -1388,3 +1393,36 @@ void VEdit::updateBlockLineDistanceHeight(int p_pos,
cursor.endEditBlock(); 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);
}
}

View File

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

View File

@ -839,6 +839,10 @@ void VEditArea::registerCaptainTargets()
g_config->getCaptainShortcutKeySequence("RemoveSplit"), g_config->getCaptainShortcutKeySequence("RemoveSplit"),
this, this,
removeSplitByCaptain); removeSplitByCaptain);
captain->registerCaptainTarget(tr("MagicWord"),
g_config->getCaptainShortcutKeySequence("MagicWord"),
this,
evaluateMagicWordsByCaptain);
} }
void VEditArea::activateTabByCaptain(void *p_target, void *p_data, int p_idx) 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); VEditArea *obj = static_cast<VEditArea *>(p_target);
obj->removeCurrentWindow(); 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();
}
}

View File

@ -188,6 +188,9 @@ private:
static void removeSplitByCaptain(void *p_target, void *p_data); 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. // End Captain mode functions.
int curWindowIndex; int curWindowIndex;

View File

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

View File

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

View File

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

View File

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

View File

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