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
; Remove current split
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.
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,
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() {}
};

View File

@ -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;
}

View File

@ -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);

View File

@ -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);

View File

@ -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);
}
}

View File

@ -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();

View File

@ -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();
}
}

View File

@ -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;

View File

@ -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

View File

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

View File

@ -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;

View File

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

View File

@ -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;