mirror of
https://gitee.com/vnotex/vnote.git
synced 2025-07-05 13:59:52 +08:00
metaword: support Ctrl+E M in editor to evaluate metawords
Add shortcut configuration MagicWord.
This commit is contained in:
parent
dcb6227d3b
commit
92bc044cac
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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() {}
|
||||
};
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -110,3 +110,7 @@ void VEditTab::updateStatus()
|
||||
{
|
||||
emit statusUpdated(fetchTabInfo());
|
||||
}
|
||||
|
||||
void VEditTab::evaluateMagicWords()
|
||||
{
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -685,3 +685,10 @@ bool VMdTab::isHeadingSequenceEnabled() const
|
||||
{
|
||||
return m_enableHeadingSequence;
|
||||
}
|
||||
|
||||
void VMdTab::evaluateMagicWords()
|
||||
{
|
||||
if (isEditMode() && m_file->isModifiable()) {
|
||||
getEditor()->evaluateMagicWords();
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user