mirror of
https://gitee.com/vnotex/vnote.git
synced 2025-07-05 13:59:52 +08:00
support normal Vim mode
1. Support Insert/Normal/Visual/VisualLine modes: - `V`, `v`, `I`, `i`, `A`, `a`, `s`, `Esc`, `Ctrl+[`, `o`, `O`; 2. Support movement commands (with Repeat support): - `h`, `j`, `k`, `l`, `gj`, `gk`; - `gg`, `G`; - `^`, `0`, `$`; - `Ctrl+U`, `Ctrl+D`, `PageUp`, `PageDown`, `Ctrl+B`;
This commit is contained in:
parent
224a7253ce
commit
a8c76d6742
@ -14,7 +14,10 @@ background: 005fff
|
|||||||
|
|
||||||
editor-current-line
|
editor-current-line
|
||||||
background: c5cae9
|
background: c5cae9
|
||||||
vim-background: a5d6a7
|
vim-insert-background: c5cae9
|
||||||
|
vim-normal-background: a5d6a7
|
||||||
|
vim-visual-background: a5d6a7
|
||||||
|
vim-replace-background: a5d6a7
|
||||||
|
|
||||||
H1
|
H1
|
||||||
foreground: 111111
|
foreground: 111111
|
||||||
|
@ -45,6 +45,9 @@ image_folder=_v_images
|
|||||||
; Enable trailing space highlight
|
; Enable trailing space highlight
|
||||||
enable_trailing_space_highlight=true
|
enable_trailing_space_highlight=true
|
||||||
|
|
||||||
|
; Enable Vim mode in edit mode
|
||||||
|
enable_vim_mode=false
|
||||||
|
|
||||||
[session]
|
[session]
|
||||||
tools_dock_checked=true
|
tools_dock_checked=true
|
||||||
|
|
||||||
|
@ -64,7 +64,8 @@ SOURCES += main.cpp\
|
|||||||
vimagepreviewer.cpp \
|
vimagepreviewer.cpp \
|
||||||
vexporter.cpp \
|
vexporter.cpp \
|
||||||
vmdtab.cpp \
|
vmdtab.cpp \
|
||||||
vhtmltab.cpp
|
vhtmltab.cpp \
|
||||||
|
utils/vvim.cpp
|
||||||
|
|
||||||
HEADERS += vmainwindow.h \
|
HEADERS += vmainwindow.h \
|
||||||
vdirectorytree.h \
|
vdirectorytree.h \
|
||||||
@ -115,7 +116,8 @@ HEADERS += vmainwindow.h \
|
|||||||
vimagepreviewer.h \
|
vimagepreviewer.h \
|
||||||
vexporter.h \
|
vexporter.h \
|
||||||
vmdtab.h \
|
vmdtab.h \
|
||||||
vhtmltab.h
|
vhtmltab.h \
|
||||||
|
utils/vvim.h
|
||||||
|
|
||||||
RESOURCES += \
|
RESOURCES += \
|
||||||
vnote.qrc \
|
vnote.qrc \
|
||||||
|
1010
src/utils/vvim.cpp
Normal file
1010
src/utils/vvim.cpp
Normal file
File diff suppressed because it is too large
Load Diff
243
src/utils/vvim.h
Normal file
243
src/utils/vvim.h
Normal file
@ -0,0 +1,243 @@
|
|||||||
|
#ifndef VVIM_H
|
||||||
|
#define VVIM_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QString>
|
||||||
|
#include <QTextCursor>
|
||||||
|
#include "vutils.h"
|
||||||
|
|
||||||
|
class VEdit;
|
||||||
|
class QKeyEvent;
|
||||||
|
class VEditConfig;
|
||||||
|
class QKeyEvent;
|
||||||
|
|
||||||
|
enum class VimMode {
|
||||||
|
Normal = 0,
|
||||||
|
Insert,
|
||||||
|
Visual,
|
||||||
|
VisualLine,
|
||||||
|
Replace,
|
||||||
|
Invalid
|
||||||
|
};
|
||||||
|
|
||||||
|
class VVim : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit VVim(VEdit *p_editor);
|
||||||
|
|
||||||
|
// Handle key press event.
|
||||||
|
// Returns true if the event is consumed and need no more handling.
|
||||||
|
bool handleKeyPressEvent(QKeyEvent *p_event);
|
||||||
|
|
||||||
|
// Return current mode.
|
||||||
|
VimMode getMode() const;
|
||||||
|
|
||||||
|
// Set current mode.
|
||||||
|
void setMode(VimMode p_mode);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
// Emit when current mode has been changed.
|
||||||
|
void modeChanged(VimMode p_mode);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
// When user use mouse to select texts in Normal mode, we should change to
|
||||||
|
// Visual mode.
|
||||||
|
void selectionToVisualMode(bool p_hasText);
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Struct for a key press.
|
||||||
|
struct Key
|
||||||
|
{
|
||||||
|
Key(int p_key, int p_modifiers = Qt::NoModifier)
|
||||||
|
: m_key(p_key), m_modifiers(p_modifiers)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
int m_key;
|
||||||
|
int m_modifiers;
|
||||||
|
|
||||||
|
bool isDigit() const
|
||||||
|
{
|
||||||
|
return m_key >= Qt::Key_0
|
||||||
|
&& m_key <= Qt::Key_9
|
||||||
|
&& m_modifiers == Qt::NoModifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
int toDigit() const
|
||||||
|
{
|
||||||
|
V_ASSERT(isDigit());
|
||||||
|
return m_key - Qt::Key_0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const Key &p_key) const
|
||||||
|
{
|
||||||
|
return p_key.m_key == m_key && p_key.m_modifiers == m_modifiers;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Supported actions.
|
||||||
|
enum class Action
|
||||||
|
{
|
||||||
|
Move = 0,
|
||||||
|
Delete,
|
||||||
|
Copy,
|
||||||
|
Paste,
|
||||||
|
Change,
|
||||||
|
Indent,
|
||||||
|
UnIndent,
|
||||||
|
ToUpper,
|
||||||
|
ToLower,
|
||||||
|
DeleteToClipboard,
|
||||||
|
CopyToClipboard,
|
||||||
|
PasteFromClipboard,
|
||||||
|
ChangeToClipboard,
|
||||||
|
Invalid
|
||||||
|
};
|
||||||
|
|
||||||
|
// Supported movements.
|
||||||
|
enum class Movement
|
||||||
|
{
|
||||||
|
Left = 0,
|
||||||
|
Right,
|
||||||
|
Up,
|
||||||
|
Down,
|
||||||
|
VisualUp,
|
||||||
|
VisualDown,
|
||||||
|
PageUp,
|
||||||
|
PageDown,
|
||||||
|
HalfPageUp,
|
||||||
|
HalfPageDown,
|
||||||
|
StartOfLine,
|
||||||
|
EndOfLine,
|
||||||
|
FirstCharacter,
|
||||||
|
LineJump,
|
||||||
|
StartOfDocument,
|
||||||
|
EndOfDocument,
|
||||||
|
Invalid
|
||||||
|
};
|
||||||
|
|
||||||
|
// Supported ranges.
|
||||||
|
enum class Range
|
||||||
|
{
|
||||||
|
Line = 0,
|
||||||
|
Word,
|
||||||
|
Invalid
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class TokenType { Action = 0, Repeat, Movement, Range, Invalid };
|
||||||
|
|
||||||
|
struct Token
|
||||||
|
{
|
||||||
|
Token(Action p_action)
|
||||||
|
: m_type(TokenType::Action), m_action(p_action) {}
|
||||||
|
|
||||||
|
Token(int p_repeat)
|
||||||
|
: m_type(TokenType::Repeat), m_repeat(p_repeat) {}
|
||||||
|
|
||||||
|
Token(Movement p_movement)
|
||||||
|
: m_type(TokenType::Movement), m_movement(p_movement) {}
|
||||||
|
|
||||||
|
Token(Range p_range)
|
||||||
|
: m_type(TokenType::Range), m_range(p_range) {}
|
||||||
|
|
||||||
|
Token() : m_type(TokenType::Invalid) {}
|
||||||
|
|
||||||
|
bool isRepeat() const
|
||||||
|
{
|
||||||
|
return m_type == TokenType::Repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isAction() const
|
||||||
|
{
|
||||||
|
return m_type == TokenType::Action;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isMovement() const
|
||||||
|
{
|
||||||
|
return m_type == TokenType::Movement;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isRange() const
|
||||||
|
{
|
||||||
|
return m_type == TokenType::Range;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString toString() const
|
||||||
|
{
|
||||||
|
QString str;
|
||||||
|
switch (m_type) {
|
||||||
|
case TokenType::Action:
|
||||||
|
str = QString("action %1").arg((int)m_action);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TokenType::Repeat:
|
||||||
|
str = QString("repeat %1").arg(m_repeat);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TokenType::Movement:
|
||||||
|
str = QString("movement %1").arg((int)m_movement);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TokenType::Range:
|
||||||
|
str = QString("range %1").arg((int)m_range);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
str = "invalid";
|
||||||
|
}
|
||||||
|
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
TokenType m_type;
|
||||||
|
|
||||||
|
union
|
||||||
|
{
|
||||||
|
Action m_action;
|
||||||
|
int m_repeat;
|
||||||
|
Movement m_movement;
|
||||||
|
Range m_range;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Reset all key state info.
|
||||||
|
void resetState();
|
||||||
|
|
||||||
|
// Now m_tokens constitute a command. Execute it.
|
||||||
|
// Will clear @p_tokens.
|
||||||
|
void processCommand(QList<Token> &p_tokens);
|
||||||
|
|
||||||
|
// Return the number represented by @p_keys.
|
||||||
|
// Return -1 if @p_keys is not a valid digit sequence.
|
||||||
|
int numberFromKeySequence(const QList<Key> &p_keys);
|
||||||
|
|
||||||
|
// Try to generate a Repeat token from @p_keys and insert it to @p_tokens.
|
||||||
|
// If succeed, clear @p_keys and return true.
|
||||||
|
bool tryGetRepeatToken(QList<Key> &p_keys, QList<Token> &p_tokens);
|
||||||
|
|
||||||
|
// @p_tokens is the arguments of the Action::Move action.
|
||||||
|
void processMoveAction(QList<Token> &p_tokens);
|
||||||
|
|
||||||
|
// Clear selection if there is any.
|
||||||
|
// Returns true if there is selection.
|
||||||
|
bool clearSelection();
|
||||||
|
|
||||||
|
// Get the block count of one page step in vertical scroll bar.
|
||||||
|
int blockCountOfPageStep() const;
|
||||||
|
|
||||||
|
// Expand selection in the VisualLiine mode which will change the position
|
||||||
|
// of @p_cursor.
|
||||||
|
void expandSelectionInVisualLineMode(QTextCursor &p_cursor);
|
||||||
|
|
||||||
|
VEdit *m_editor;
|
||||||
|
const VEditConfig *m_editConfig;
|
||||||
|
VimMode m_mode;
|
||||||
|
QList<Key> m_keys;
|
||||||
|
QList<Token> m_tokens;
|
||||||
|
|
||||||
|
// Whether reset the position in block when moving cursor.
|
||||||
|
bool m_resetPositionInBlock;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // VVIM_H
|
@ -146,6 +146,9 @@ void VConfigManager::initialize()
|
|||||||
|
|
||||||
m_enableTrailingSpaceHighlight = getConfigFromSettings("global",
|
m_enableTrailingSpaceHighlight = getConfigFromSettings("global",
|
||||||
"enable_trailing_space_highlight").toBool();
|
"enable_trailing_space_highlight").toBool();
|
||||||
|
|
||||||
|
m_enableVimMode = getConfigFromSettings("global",
|
||||||
|
"enable_vim_mode").toBool();
|
||||||
}
|
}
|
||||||
|
|
||||||
void VConfigManager::readPredefinedColorsFromSettings()
|
void VConfigManager::readPredefinedColorsFromSettings()
|
||||||
@ -347,34 +350,48 @@ void VConfigManager::updateMarkdownEditStyle()
|
|||||||
QMap<QString, QMap<QString, QString>> styles;
|
QMap<QString, QMap<QString, QString>> styles;
|
||||||
parser.fetchMarkdownEditorStyles(mdEditPalette, mdEditFont, styles);
|
parser.fetchMarkdownEditorStyles(mdEditPalette, mdEditFont, styles);
|
||||||
|
|
||||||
m_editorCurrentLineBackground = defaultCurrentLineBackground;
|
m_editorCurrentLineBg = defaultCurrentLineBackground;
|
||||||
m_editorCurrentLineVimBackground = defaultCurrentLineVimBackground;
|
m_editorVimInsertBg = defaultCurrentLineBackground;
|
||||||
|
m_editorVimNormalBg = defaultCurrentLineVimBackground;
|
||||||
|
m_editorVimVisualBg = m_editorVimNormalBg;
|
||||||
|
m_editorVimReplaceBg = m_editorVimNormalBg;
|
||||||
auto editorCurrentLineIt = styles.find("editor-current-line");
|
auto editorCurrentLineIt = styles.find("editor-current-line");
|
||||||
if (editorCurrentLineIt != styles.end()) {
|
if (editorCurrentLineIt != styles.end()) {
|
||||||
auto backgroundIt = editorCurrentLineIt->find("background");
|
auto backgroundIt = editorCurrentLineIt->find("background");
|
||||||
if (backgroundIt != editorCurrentLineIt->end()) {
|
if (backgroundIt != editorCurrentLineIt->end()) {
|
||||||
// Do not need to add "#" here, since this is a built-in attribute.
|
// Do not need to add "#" here, since this is a built-in attribute.
|
||||||
m_editorCurrentLineBackground = *backgroundIt;
|
m_editorCurrentLineBg = *backgroundIt;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto vimBackgroundIt = editorCurrentLineIt->find("vim-background");
|
auto vimBgIt = editorCurrentLineIt->find("vim-insert-background");
|
||||||
if (vimBackgroundIt != editorCurrentLineIt->end()) {
|
if (vimBgIt != editorCurrentLineIt->end()) {
|
||||||
m_editorCurrentLineVimBackground = "#" + *vimBackgroundIt;
|
m_editorVimInsertBg = "#" + *vimBgIt;
|
||||||
|
}
|
||||||
|
|
||||||
|
vimBgIt = editorCurrentLineIt->find("vim-normal-background");
|
||||||
|
if (vimBgIt != editorCurrentLineIt->end()) {
|
||||||
|
m_editorVimNormalBg = "#" + *vimBgIt;
|
||||||
|
}
|
||||||
|
|
||||||
|
vimBgIt = editorCurrentLineIt->find("vim-visual-background");
|
||||||
|
if (vimBgIt != editorCurrentLineIt->end()) {
|
||||||
|
m_editorVimVisualBg = "#" + *vimBgIt;
|
||||||
|
}
|
||||||
|
|
||||||
|
vimBgIt = editorCurrentLineIt->find("vim-replace-background");
|
||||||
|
if (vimBgIt != editorCurrentLineIt->end()) {
|
||||||
|
m_editorVimReplaceBg = "#" + *vimBgIt;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_editorTrailingSpaceBackground = defaultTrailingSpaceBackground;
|
m_editorTrailingSpaceBg = defaultTrailingSpaceBackground;
|
||||||
auto editorIt = styles.find("editor");
|
auto editorIt = styles.find("editor");
|
||||||
if (editorIt != styles.end()) {
|
if (editorIt != styles.end()) {
|
||||||
auto trailingIt = editorIt->find("trailing-space");
|
auto trailingIt = editorIt->find("trailing-space");
|
||||||
if (trailingIt != editorIt->end()) {
|
if (trailingIt != editorIt->end()) {
|
||||||
m_editorTrailingSpaceBackground = "#" + *trailingIt;
|
m_editorTrailingSpaceBg = "#" + *trailingIt;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
qDebug() << "editor-current-line" << m_editorCurrentLineBackground;
|
|
||||||
qDebug() << "editor-current-line-vim" << m_editorCurrentLineVimBackground;
|
|
||||||
qDebug() << "editor-trailing-space" << m_editorTrailingSpaceBackground;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void VConfigManager::updateEditStyle()
|
void VConfigManager::updateEditStyle()
|
||||||
|
@ -156,10 +156,14 @@ public:
|
|||||||
void setWebZoomFactor(qreal p_factor);
|
void setWebZoomFactor(qreal p_factor);
|
||||||
inline bool isCustomWebZoomFactor();
|
inline bool isCustomWebZoomFactor();
|
||||||
|
|
||||||
inline QString getEditorCurrentLineBackground() const;
|
inline const QString &getEditorCurrentLineBg() const;
|
||||||
inline QString getEditorCurrentLineVimBackground() const;
|
|
||||||
inline QString getEditorTrailingSpaceBackground() const;
|
inline QString getEditorTrailingSpaceBackground() const;
|
||||||
|
|
||||||
|
inline const QString &getEditorVimNormalBg() const;
|
||||||
|
inline const QString &getEditorVimInsertBg() const;
|
||||||
|
inline const QString &getEditorVimVisualBg() const;
|
||||||
|
inline const QString &getEditorVimReplaceBg() const;
|
||||||
|
|
||||||
inline bool getEnableCodeBlockHighlight() const;
|
inline bool getEnableCodeBlockHighlight() const;
|
||||||
inline void setEnableCodeBlockHighlight(bool p_enabled);
|
inline void setEnableCodeBlockHighlight(bool p_enabled);
|
||||||
|
|
||||||
@ -183,6 +187,9 @@ public:
|
|||||||
inline bool getEnableTrailingSpaceHighlight() const;
|
inline bool getEnableTrailingSpaceHighlight() const;
|
||||||
inline void setEnableTrailingSapceHighlight(bool p_enabled);
|
inline void setEnableTrailingSapceHighlight(bool p_enabled);
|
||||||
|
|
||||||
|
inline bool getEnableVimMode() const;
|
||||||
|
inline void setEnableVimMode(bool p_enabled);
|
||||||
|
|
||||||
// Get the folder the ini file exists.
|
// Get the folder the ini file exists.
|
||||||
QString getConfigFolder() const;
|
QString getConfigFolder() const;
|
||||||
|
|
||||||
@ -292,13 +299,22 @@ private:
|
|||||||
qreal m_webZoomFactor;
|
qreal m_webZoomFactor;
|
||||||
|
|
||||||
// Current line background color in editor.
|
// Current line background color in editor.
|
||||||
QString m_editorCurrentLineBackground;
|
QString m_editorCurrentLineBg;
|
||||||
|
|
||||||
// Current line background color in editor in Vim mode.
|
// Current line background color in editor in Vim normal mode.
|
||||||
QString m_editorCurrentLineVimBackground;
|
QString m_editorVimNormalBg;
|
||||||
|
|
||||||
|
// Current line background color in editor in Vim insert mode.
|
||||||
|
QString m_editorVimInsertBg;
|
||||||
|
|
||||||
|
// Current line background color in editor in Vim visual mode.
|
||||||
|
QString m_editorVimVisualBg;
|
||||||
|
|
||||||
|
// Current line background color in editor in Vim replace mode.
|
||||||
|
QString m_editorVimReplaceBg;
|
||||||
|
|
||||||
// Trailing space background color in editor.
|
// Trailing space background color in editor.
|
||||||
QString m_editorTrailingSpaceBackground;
|
QString m_editorTrailingSpaceBg;
|
||||||
|
|
||||||
// Enable colde block syntax highlight.
|
// Enable colde block syntax highlight.
|
||||||
bool m_enableCodeBlockHighlight;
|
bool m_enableCodeBlockHighlight;
|
||||||
@ -322,6 +338,9 @@ private:
|
|||||||
// Enable trailing-space highlight.
|
// Enable trailing-space highlight.
|
||||||
bool m_enableTrailingSpaceHighlight;
|
bool m_enableTrailingSpaceHighlight;
|
||||||
|
|
||||||
|
// Enable Vim mode.
|
||||||
|
bool m_enableVimMode;
|
||||||
|
|
||||||
// The name of the config file in each directory, obsolete.
|
// The name of the config file in each directory, obsolete.
|
||||||
// Use c_dirConfigFile instead.
|
// Use c_dirConfigFile instead.
|
||||||
static const QString c_obsoleteDirConfigFile;
|
static const QString c_obsoleteDirConfigFile;
|
||||||
@ -722,19 +741,34 @@ inline bool VConfigManager::isCustomWebZoomFactor()
|
|||||||
return factorFromIni > 0;
|
return factorFromIni > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline QString VConfigManager::getEditorCurrentLineBackground() const
|
inline const QString &VConfigManager::getEditorCurrentLineBg() const
|
||||||
{
|
{
|
||||||
return m_editorCurrentLineBackground;
|
return m_editorCurrentLineBg;
|
||||||
}
|
|
||||||
|
|
||||||
inline QString VConfigManager::getEditorCurrentLineVimBackground() const
|
|
||||||
{
|
|
||||||
return m_editorCurrentLineVimBackground;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline QString VConfigManager::getEditorTrailingSpaceBackground() const
|
inline QString VConfigManager::getEditorTrailingSpaceBackground() const
|
||||||
{
|
{
|
||||||
return m_editorTrailingSpaceBackground;
|
return m_editorTrailingSpaceBg;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const QString &VConfigManager::getEditorVimNormalBg() const
|
||||||
|
{
|
||||||
|
return m_editorVimNormalBg;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const QString &VConfigManager::getEditorVimInsertBg() const
|
||||||
|
{
|
||||||
|
return m_editorVimInsertBg;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const QString &VConfigManager::getEditorVimVisualBg() const
|
||||||
|
{
|
||||||
|
return m_editorVimVisualBg;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const QString &VConfigManager::getEditorVimReplaceBg() const
|
||||||
|
{
|
||||||
|
return m_editorVimReplaceBg;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool VConfigManager::getEnableCodeBlockHighlight() const
|
inline bool VConfigManager::getEnableCodeBlockHighlight() const
|
||||||
@ -857,4 +891,20 @@ inline void VConfigManager::setEnableTrailingSapceHighlight(bool p_enabled)
|
|||||||
m_enableTrailingSpaceHighlight);
|
m_enableTrailingSpaceHighlight);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline bool VConfigManager::getEnableVimMode() const
|
||||||
|
{
|
||||||
|
return m_enableVimMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void VConfigManager::setEnableVimMode(bool p_enabled)
|
||||||
|
{
|
||||||
|
if (m_enableVimMode == p_enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_enableVimMode = p_enabled;
|
||||||
|
setConfigToSettings("global", "enable_vim_mode",
|
||||||
|
m_enableVimMode);
|
||||||
|
}
|
||||||
|
|
||||||
#endif // VCONFIGMANAGER_H
|
#endif // VCONFIGMANAGER_H
|
||||||
|
@ -13,6 +13,27 @@
|
|||||||
extern VConfigManager vconfig;
|
extern VConfigManager vconfig;
|
||||||
extern VNote *g_vnote;
|
extern VNote *g_vnote;
|
||||||
|
|
||||||
|
void VEditConfig::init(const QFontMetrics &p_metric)
|
||||||
|
{
|
||||||
|
if (vconfig.getTabStopWidth() > 0) {
|
||||||
|
m_tabStopWidth = vconfig.getTabStopWidth() * p_metric.width(' ');
|
||||||
|
} else {
|
||||||
|
m_tabStopWidth = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_expandTab = vconfig.getIsExpandTab();
|
||||||
|
|
||||||
|
if (m_expandTab && (vconfig.getTabStopWidth() > 0)) {
|
||||||
|
m_tabSpaces = QString(vconfig.getTabStopWidth(), ' ');
|
||||||
|
} else {
|
||||||
|
m_tabSpaces = "\t";
|
||||||
|
}
|
||||||
|
|
||||||
|
m_enableVimMode = vconfig.getEnableVimMode();
|
||||||
|
|
||||||
|
m_cursorLineBg = QColor(vconfig.getEditorCurrentLineBg());
|
||||||
|
}
|
||||||
|
|
||||||
VEdit::VEdit(VFile *p_file, QWidget *p_parent)
|
VEdit::VEdit(VFile *p_file, QWidget *p_parent)
|
||||||
: QTextEdit(p_parent), m_file(p_file), m_editOps(NULL)
|
: QTextEdit(p_parent), m_file(p_file), m_editOps(NULL)
|
||||||
{
|
{
|
||||||
@ -20,7 +41,6 @@ VEdit::VEdit(VFile *p_file, QWidget *p_parent)
|
|||||||
const int extraSelectionHighlightTimer = 500;
|
const int extraSelectionHighlightTimer = 500;
|
||||||
const int labelSize = 64;
|
const int labelSize = 64;
|
||||||
|
|
||||||
m_cursorLineColor = QColor(g_vnote->getColorFromPalette("Indigo1"));
|
|
||||||
m_selectedWordColor = QColor("Yellow");
|
m_selectedWordColor = QColor("Yellow");
|
||||||
m_searchedWordColor = QColor(g_vnote->getColorFromPalette("Green4"));
|
m_searchedWordColor = QColor(g_vnote->getColorFromPalette("Green4"));
|
||||||
m_trailingSpaceColor = QColor(vconfig.getEditorTrailingSpaceBackground());
|
m_trailingSpaceColor = QColor(vconfig.getEditorTrailingSpaceBackground());
|
||||||
@ -45,8 +65,11 @@ VEdit::VEdit(VFile *p_file, QWidget *p_parent)
|
|||||||
(VFile *)m_file, &VFile::setModified);
|
(VFile *)m_file, &VFile::setModified);
|
||||||
|
|
||||||
m_extraSelections.resize((int)SelectionId::MaxSelection);
|
m_extraSelections.resize((int)SelectionId::MaxSelection);
|
||||||
|
|
||||||
updateFontAndPalette();
|
updateFontAndPalette();
|
||||||
|
|
||||||
|
updateConfig();
|
||||||
|
|
||||||
connect(this, &VEdit::cursorPositionChanged,
|
connect(this, &VEdit::cursorPositionChanged,
|
||||||
this, &VEdit::handleCursorPositionChanged);
|
this, &VEdit::handleCursorPositionChanged);
|
||||||
|
|
||||||
@ -62,10 +85,23 @@ VEdit::~VEdit()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VEdit::updateConfig()
|
||||||
|
{
|
||||||
|
m_config.init(QFontMetrics(font()));
|
||||||
|
|
||||||
|
if (m_config.m_tabStopWidth > 0) {
|
||||||
|
setTabStopWidth(m_config.m_tabStopWidth);
|
||||||
|
}
|
||||||
|
|
||||||
|
emit configUpdated();
|
||||||
|
}
|
||||||
|
|
||||||
void VEdit::beginEdit()
|
void VEdit::beginEdit()
|
||||||
{
|
{
|
||||||
updateFontAndPalette();
|
updateFontAndPalette();
|
||||||
|
|
||||||
|
updateConfig();
|
||||||
|
|
||||||
setReadOnly(false);
|
setReadOnly(false);
|
||||||
setModified(false);
|
setModified(false);
|
||||||
}
|
}
|
||||||
@ -404,7 +440,7 @@ void VEdit::highlightCurrentLine()
|
|||||||
if (vconfig.getHighlightCursorLine() && !isReadOnly()) {
|
if (vconfig.getHighlightCursorLine() && !isReadOnly()) {
|
||||||
// Need to highlight current line.
|
// Need to highlight current line.
|
||||||
QTextEdit::ExtraSelection select;
|
QTextEdit::ExtraSelection select;
|
||||||
select.format.setBackground(m_cursorLineColor);
|
select.format.setBackground(m_config.m_cursorLineBg);
|
||||||
select.format.setProperty(QTextFormat::FullWidthSelection, true);
|
select.format.setProperty(QTextFormat::FullWidthSelection, true);
|
||||||
select.cursor = textCursor();
|
select.cursor = textCursor();
|
||||||
select.cursor.clearSelection();
|
select.cursor.clearSelection();
|
||||||
@ -651,3 +687,7 @@ void VEdit::handleCursorPositionChanged()
|
|||||||
lastCursor = cursor;
|
lastCursor = cursor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VEditConfig &VEdit::getConfig()
|
||||||
|
{
|
||||||
|
return m_config;
|
||||||
|
}
|
||||||
|
39
src/vedit.h
39
src/vedit.h
@ -7,6 +7,7 @@
|
|||||||
#include <QVector>
|
#include <QVector>
|
||||||
#include <QList>
|
#include <QList>
|
||||||
#include <QColor>
|
#include <QColor>
|
||||||
|
#include <QFontMetrics>
|
||||||
#include "vconstants.h"
|
#include "vconstants.h"
|
||||||
#include "vtoc.h"
|
#include "vtoc.h"
|
||||||
#include "vfile.h"
|
#include "vfile.h"
|
||||||
@ -23,6 +24,27 @@ enum class SelectionId {
|
|||||||
MaxSelection
|
MaxSelection
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class VEditConfig {
|
||||||
|
public:
|
||||||
|
VEditConfig() : m_tabStopWidth(0), m_tabSpaces("\t"),
|
||||||
|
m_enableVimMode(false) {}
|
||||||
|
|
||||||
|
void init(const QFontMetrics &p_metric);
|
||||||
|
|
||||||
|
// Width in pixels.
|
||||||
|
int m_tabStopWidth;
|
||||||
|
|
||||||
|
bool m_expandTab;
|
||||||
|
|
||||||
|
// The literal string for Tab. It is spaces if Tab is expanded.
|
||||||
|
QString m_tabSpaces;
|
||||||
|
|
||||||
|
bool m_enableVimMode;
|
||||||
|
|
||||||
|
// The background color of cursor line.
|
||||||
|
QColor m_cursorLineBg;
|
||||||
|
};
|
||||||
|
|
||||||
class VEdit : public QTextEdit
|
class VEdit : public QTextEdit
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@ -51,11 +73,19 @@ public:
|
|||||||
void clearSearchedWordHighlight();
|
void clearSearchedWordHighlight();
|
||||||
VFile *getFile() const;
|
VFile *getFile() const;
|
||||||
|
|
||||||
|
VEditConfig &getConfig();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void saveAndRead();
|
void saveAndRead();
|
||||||
void discardAndRead();
|
void discardAndRead();
|
||||||
void editNote();
|
void editNote();
|
||||||
|
|
||||||
|
// Emit when m_config has been updated.
|
||||||
|
void configUpdated();
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
virtual void highlightCurrentLine();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void labelTimerTimeout();
|
void labelTimerTimeout();
|
||||||
void highlightSelectedWord();
|
void highlightSelectedWord();
|
||||||
@ -65,17 +95,18 @@ private slots:
|
|||||||
void highlightTrailingSpace();
|
void highlightTrailingSpace();
|
||||||
void handleCursorPositionChanged();
|
void handleCursorPositionChanged();
|
||||||
|
|
||||||
protected slots:
|
|
||||||
virtual void highlightCurrentLine();
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
QPointer<VFile> m_file;
|
QPointer<VFile> m_file;
|
||||||
VEditOperations *m_editOps;
|
VEditOperations *m_editOps;
|
||||||
QColor m_cursorLineColor;
|
|
||||||
|
VEditConfig m_config;
|
||||||
|
|
||||||
virtual void updateFontAndPalette();
|
virtual void updateFontAndPalette();
|
||||||
virtual void contextMenuEvent(QContextMenuEvent *p_event) Q_DECL_OVERRIDE;
|
virtual void contextMenuEvent(QContextMenuEvent *p_event) Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
|
// Update m_config according to VConfigManager.
|
||||||
|
void updateConfig();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QLabel *m_wrapLabel;
|
QLabel *m_wrapLabel;
|
||||||
QTimer *m_labelTimer;
|
QTimer *m_labelTimer;
|
||||||
|
@ -4,14 +4,20 @@
|
|||||||
#include "vedit.h"
|
#include "vedit.h"
|
||||||
#include "veditoperations.h"
|
#include "veditoperations.h"
|
||||||
#include "vconfigmanager.h"
|
#include "vconfigmanager.h"
|
||||||
|
#include "utils/vutils.h"
|
||||||
|
|
||||||
extern VConfigManager vconfig;
|
extern VConfigManager vconfig;
|
||||||
|
|
||||||
VEditOperations::VEditOperations(VEdit *p_editor, VFile *p_file)
|
VEditOperations::VEditOperations(VEdit *p_editor, VFile *p_file)
|
||||||
: QObject(p_editor), m_editor(p_editor), m_file(p_file), m_expandTab(false),
|
: QObject(p_editor), m_editor(p_editor), m_file(p_file),
|
||||||
m_keyState(KeyState::Normal), m_pendingTime(2)
|
m_editConfig(&p_editor->getConfig())
|
||||||
{
|
{
|
||||||
updateTabSettings();
|
m_vim = new VVim(m_editor);
|
||||||
|
|
||||||
|
connect(m_editor, &VEdit::configUpdated,
|
||||||
|
this, &VEditOperations::handleEditConfigUpdated);
|
||||||
|
connect(m_vim, &VVim::modeChanged,
|
||||||
|
this, &VEditOperations::handleVimModeChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VEditOperations::insertTextAtCurPos(const QString &p_text)
|
void VEditOperations::insertTextAtCurPos(const QString &p_text)
|
||||||
@ -25,14 +31,46 @@ VEditOperations::~VEditOperations()
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void VEditOperations::updateTabSettings()
|
void VEditOperations::updateCursorLineBg()
|
||||||
{
|
{
|
||||||
if (vconfig.getTabStopWidth() > 0) {
|
if (m_editConfig->m_enableVimMode) {
|
||||||
QFontMetrics metrics(vconfig.getMdEditFont());
|
switch (m_vim->getMode()) {
|
||||||
m_editor->setTabStopWidth(vconfig.getTabStopWidth() * metrics.width(' '));
|
case VimMode::Normal:
|
||||||
}
|
m_editConfig->m_cursorLineBg = QColor(vconfig.getEditorVimNormalBg());
|
||||||
m_expandTab = vconfig.getIsExpandTab();
|
break;
|
||||||
if (m_expandTab && (vconfig.getTabStopWidth() > 0)) {
|
|
||||||
m_tabSpaces = QString(vconfig.getTabStopWidth(), ' ');
|
case VimMode::Insert:
|
||||||
|
m_editConfig->m_cursorLineBg = QColor(vconfig.getEditorVimInsertBg());
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VimMode::Visual:
|
||||||
|
case VimMode::VisualLine:
|
||||||
|
m_editConfig->m_cursorLineBg = QColor(vconfig.getEditorVimVisualBg());
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VimMode::Replace:
|
||||||
|
m_editConfig->m_cursorLineBg = QColor(vconfig.getEditorVimReplaceBg());
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
V_ASSERT(false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
m_editConfig->m_cursorLineBg = QColor(vconfig.getEditorCurrentLineBg());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_editor->highlightCurrentLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
void VEditOperations::handleEditConfigUpdated()
|
||||||
|
{
|
||||||
|
updateCursorLineBg();
|
||||||
|
}
|
||||||
|
|
||||||
|
void VEditOperations::handleVimModeChanged(VimMode p_mode)
|
||||||
|
{
|
||||||
|
Q_UNUSED(p_mode);
|
||||||
|
|
||||||
|
updateCursorLineBg();
|
||||||
}
|
}
|
||||||
|
@ -6,13 +6,13 @@
|
|||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QList>
|
#include <QList>
|
||||||
#include "vfile.h"
|
#include "vfile.h"
|
||||||
|
#include "utils/vvim.h"
|
||||||
|
|
||||||
class VEdit;
|
class VEdit;
|
||||||
|
class VEditConfig;
|
||||||
class QMimeData;
|
class QMimeData;
|
||||||
class QKeyEvent;
|
class QKeyEvent;
|
||||||
|
|
||||||
enum class KeyState { Normal = 0, Vim, VimVisual};
|
|
||||||
|
|
||||||
class VEditOperations: public QObject
|
class VEditOperations: public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@ -22,25 +22,29 @@ public:
|
|||||||
virtual bool insertImageFromMimeData(const QMimeData *source) = 0;
|
virtual bool insertImageFromMimeData(const QMimeData *source) = 0;
|
||||||
virtual bool insertImage() = 0;
|
virtual bool insertImage() = 0;
|
||||||
virtual bool insertImageFromURL(const QUrl &p_imageUrl) = 0;
|
virtual bool insertImageFromURL(const QUrl &p_imageUrl) = 0;
|
||||||
|
|
||||||
// Return true if @p_event has been handled and no need to be further
|
// Return true if @p_event has been handled and no need to be further
|
||||||
// processed.
|
// processed.
|
||||||
virtual bool handleKeyPressEvent(QKeyEvent *p_event) = 0;
|
virtual bool handleKeyPressEvent(QKeyEvent *p_event) = 0;
|
||||||
void updateTabSettings();
|
|
||||||
|
|
||||||
signals:
|
protected slots:
|
||||||
void keyStateChanged(KeyState p_state);
|
// Handle the update of VEditConfig of the editor.
|
||||||
|
virtual void handleEditConfigUpdated();
|
||||||
|
|
||||||
|
// Vim mode changed.
|
||||||
|
void handleVimModeChanged(VimMode p_mode);
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Update m_editConfig->m_cursorLineBg.
|
||||||
|
void updateCursorLineBg();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void insertTextAtCurPos(const QString &p_text);
|
void insertTextAtCurPos(const QString &p_text);
|
||||||
|
|
||||||
VEdit *m_editor;
|
VEdit *m_editor;
|
||||||
QPointer<VFile> m_file;
|
QPointer<VFile> m_file;
|
||||||
bool m_expandTab;
|
VEditConfig *m_editConfig;
|
||||||
QString m_tabSpaces;
|
VVim *m_vim;
|
||||||
KeyState m_keyState;
|
|
||||||
// Seconds for pending mode.
|
|
||||||
int m_pendingTime;
|
|
||||||
QList<QString> m_pendingKey;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // VEDITOPERATIONS_H
|
#endif // VEDITOPERATIONS_H
|
||||||
|
@ -605,6 +605,13 @@ void VMainWindow::initEditMenu()
|
|||||||
connect(autoListAct, &QAction::triggered,
|
connect(autoListAct, &QAction::triggered,
|
||||||
this, &VMainWindow::changeAutoList);
|
this, &VMainWindow::changeAutoList);
|
||||||
|
|
||||||
|
// Vim Mode.
|
||||||
|
QAction *vimAct = new QAction(tr("Vim Mode"), this);
|
||||||
|
vimAct->setToolTip(tr("Enable Vim mode for editing (re-enter edit mode to make it work)"));
|
||||||
|
vimAct->setCheckable(true);
|
||||||
|
connect(vimAct, &QAction::triggered,
|
||||||
|
this, &VMainWindow::changeVimMode);
|
||||||
|
|
||||||
// Highlight current cursor line.
|
// Highlight current cursor line.
|
||||||
QAction *cursorLineAct = new QAction(tr("Highlight Cursor Line"), this);
|
QAction *cursorLineAct = new QAction(tr("Highlight Cursor Line"), this);
|
||||||
cursorLineAct->setToolTip(tr("Highlight current cursor line"));
|
cursorLineAct->setToolTip(tr("Highlight current cursor line"));
|
||||||
@ -687,6 +694,9 @@ void VMainWindow::initEditMenu()
|
|||||||
}
|
}
|
||||||
Q_ASSERT(!(autoListAct->isChecked() && !m_autoIndentAct->isChecked()));
|
Q_ASSERT(!(autoListAct->isChecked() && !m_autoIndentAct->isChecked()));
|
||||||
|
|
||||||
|
editMenu->addAction(vimAct);
|
||||||
|
vimAct->setChecked(vconfig.getEnableVimMode());
|
||||||
|
|
||||||
editMenu->addSeparator();
|
editMenu->addSeparator();
|
||||||
|
|
||||||
initEditorStyleMenu(editMenu);
|
initEditorStyleMenu(editMenu);
|
||||||
@ -1381,6 +1391,11 @@ void VMainWindow::changeAutoList(bool p_checked)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void VMainWindow::changeVimMode(bool p_checked)
|
||||||
|
{
|
||||||
|
vconfig.setEnableVimMode(p_checked);
|
||||||
|
}
|
||||||
|
|
||||||
void VMainWindow::enableCodeBlockHighlight(bool p_checked)
|
void VMainWindow::enableCodeBlockHighlight(bool p_checked)
|
||||||
{
|
{
|
||||||
vconfig.setEnableCodeBlockHighlight(p_checked);
|
vconfig.setEnableCodeBlockHighlight(p_checked);
|
||||||
|
@ -76,6 +76,7 @@ private slots:
|
|||||||
void handleCaptainModeChanged(bool p_enabled);
|
void handleCaptainModeChanged(bool p_enabled);
|
||||||
void changeAutoIndent(bool p_checked);
|
void changeAutoIndent(bool p_checked);
|
||||||
void changeAutoList(bool p_checked);
|
void changeAutoList(bool p_checked);
|
||||||
|
void changeVimMode(bool p_checked);
|
||||||
void enableCodeBlockHighlight(bool p_checked);
|
void enableCodeBlockHighlight(bool p_checked);
|
||||||
void enableImagePreview(bool p_checked);
|
void enableImagePreview(bool p_checked);
|
||||||
void enableImagePreviewConstraint(bool p_checked);
|
void enableImagePreviewConstraint(bool p_checked);
|
||||||
|
@ -45,8 +45,6 @@ VMdEdit::VMdEdit(VFile *p_file, VDocument *p_vdoc, MarkdownConverterType p_type,
|
|||||||
m_imagePreviewer = new VImagePreviewer(this, 500);
|
m_imagePreviewer = new VImagePreviewer(this, 500);
|
||||||
|
|
||||||
m_editOps = new VMdEditOperations(this, m_file);
|
m_editOps = new VMdEditOperations(this, m_file);
|
||||||
connect(m_editOps, &VEditOperations::keyStateChanged,
|
|
||||||
this, &VMdEdit::handleEditStateChanged);
|
|
||||||
|
|
||||||
connect(this, &VMdEdit::cursorPositionChanged,
|
connect(this, &VMdEdit::cursorPositionChanged,
|
||||||
this, &VMdEdit::updateCurHeader);
|
this, &VMdEdit::updateCurHeader);
|
||||||
@ -56,22 +54,23 @@ VMdEdit::VMdEdit(VFile *p_file, VDocument *p_vdoc, MarkdownConverterType p_type,
|
|||||||
connect(QApplication::clipboard(), &QClipboard::changed,
|
connect(QApplication::clipboard(), &QClipboard::changed,
|
||||||
this, &VMdEdit::handleClipboardChanged);
|
this, &VMdEdit::handleClipboardChanged);
|
||||||
|
|
||||||
m_editOps->updateTabSettings();
|
|
||||||
updateFontAndPalette();
|
updateFontAndPalette();
|
||||||
|
|
||||||
|
updateConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
void VMdEdit::updateFontAndPalette()
|
void VMdEdit::updateFontAndPalette()
|
||||||
{
|
{
|
||||||
setFont(vconfig.getMdEditFont());
|
setFont(vconfig.getMdEditFont());
|
||||||
setPalette(vconfig.getMdEditPalette());
|
setPalette(vconfig.getMdEditPalette());
|
||||||
m_cursorLineColor = vconfig.getEditorCurrentLineBackground();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void VMdEdit::beginEdit()
|
void VMdEdit::beginEdit()
|
||||||
{
|
{
|
||||||
m_editOps->updateTabSettings();
|
|
||||||
updateFontAndPalette();
|
updateFontAndPalette();
|
||||||
|
|
||||||
|
updateConfig();
|
||||||
|
|
||||||
Q_ASSERT(m_file->getContent() == toPlainTextWithoutImg());
|
Q_ASSERT(m_file->getContent() == toPlainTextWithoutImg());
|
||||||
|
|
||||||
initInitImages();
|
initInitImages();
|
||||||
@ -366,17 +365,6 @@ int VMdEdit::removeObjectReplacementLine(QString &p_text, int p_index) const
|
|||||||
return prevLineIdx - 1;
|
return prevLineIdx - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VMdEdit::handleEditStateChanged(KeyState p_state)
|
|
||||||
{
|
|
||||||
qDebug() << "edit state" << (int)p_state;
|
|
||||||
if (p_state == KeyState::Normal) {
|
|
||||||
m_cursorLineColor = vconfig.getEditorCurrentLineBackground();
|
|
||||||
} else {
|
|
||||||
m_cursorLineColor = vconfig.getEditorCurrentLineVimBackground();
|
|
||||||
}
|
|
||||||
highlightCurrentLine();
|
|
||||||
}
|
|
||||||
|
|
||||||
void VMdEdit::handleSelectionChanged()
|
void VMdEdit::handleSelectionChanged()
|
||||||
{
|
{
|
||||||
if (!vconfig.getEnablePreviewImages()) {
|
if (!vconfig.getEnablePreviewImages()) {
|
||||||
|
@ -53,7 +53,6 @@ private slots:
|
|||||||
// When there is no header in current cursor, will signal an invalid header.
|
// When there is no header in current cursor, will signal an invalid header.
|
||||||
void updateCurHeader();
|
void updateCurHeader();
|
||||||
|
|
||||||
void handleEditStateChanged(KeyState p_state);
|
|
||||||
void handleSelectionChanged();
|
void handleSelectionChanged();
|
||||||
void handleClipboardChanged(QClipboard::Mode p_mode);
|
void handleClipboardChanged(QClipboard::Mode p_mode);
|
||||||
|
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
#include "vfile.h"
|
#include "vfile.h"
|
||||||
#include "vmdedit.h"
|
#include "vmdedit.h"
|
||||||
#include "vconfigmanager.h"
|
#include "vconfigmanager.h"
|
||||||
|
#include "utils/vvim.h"
|
||||||
|
|
||||||
extern VConfigManager vconfig;
|
extern VConfigManager vconfig;
|
||||||
|
|
||||||
@ -29,10 +30,6 @@ const QString VMdEditOperations::c_defaultImageTitle = "image";
|
|||||||
VMdEditOperations::VMdEditOperations(VEdit *p_editor, VFile *p_file)
|
VMdEditOperations::VMdEditOperations(VEdit *p_editor, VFile *p_file)
|
||||||
: VEditOperations(p_editor, p_file), m_autoIndentPos(-1)
|
: VEditOperations(p_editor, p_file), m_autoIndentPos(-1)
|
||||||
{
|
{
|
||||||
m_pendingTimer = new QTimer(this);
|
|
||||||
m_pendingTimer->setSingleShot(true);
|
|
||||||
m_pendingTimer->setInterval(m_pendingTime * 1000); // milliseconds
|
|
||||||
connect(m_pendingTimer, &QTimer::timeout, this, &VMdEditOperations::pendingTimerTimeout);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VMdEditOperations::insertImageFromMimeData(const QMimeData *source)
|
bool VMdEditOperations::insertImageFromMimeData(const QMimeData *source)
|
||||||
@ -193,162 +190,137 @@ bool VMdEditOperations::insertImage()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Will modify m_pendingKey.
|
|
||||||
bool VMdEditOperations::shouldTriggerVimMode(QKeyEvent *p_event)
|
|
||||||
{
|
|
||||||
int modifiers = p_event->modifiers();
|
|
||||||
int key = p_event->key();
|
|
||||||
if (key == Qt::Key_Escape ||
|
|
||||||
(key == Qt::Key_BracketLeft && modifiers == Qt::ControlModifier)) {
|
|
||||||
return false;
|
|
||||||
} else if (m_keyState == KeyState::Vim || m_keyState == KeyState::VimVisual) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool VMdEditOperations::handleKeyPressEvent(QKeyEvent *p_event)
|
bool VMdEditOperations::handleKeyPressEvent(QKeyEvent *p_event)
|
||||||
{
|
{
|
||||||
|
if (m_editConfig->m_enableVimMode && m_vim->handleKeyPressEvent(p_event)) {
|
||||||
|
m_autoIndentPos = -1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool ret = false;
|
bool ret = false;
|
||||||
int key = p_event->key();
|
int key = p_event->key();
|
||||||
int modifiers = p_event->modifiers();
|
int modifiers = p_event->modifiers();
|
||||||
|
|
||||||
if (shouldTriggerVimMode(p_event)) {
|
switch (key) {
|
||||||
if (handleKeyPressVim(p_event)) {
|
case Qt::Key_1:
|
||||||
|
case Qt::Key_2:
|
||||||
|
case Qt::Key_3:
|
||||||
|
case Qt::Key_4:
|
||||||
|
case Qt::Key_5:
|
||||||
|
case Qt::Key_6:
|
||||||
|
{
|
||||||
|
if (modifiers == Qt::ControlModifier) {
|
||||||
|
// Ctrl + <N>: insert title at level <N>.
|
||||||
|
if (insertTitle(key - Qt::Key_0)) {
|
||||||
|
p_event->accept();
|
||||||
|
ret = true;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case Qt::Key_Tab:
|
||||||
|
{
|
||||||
|
if (handleKeyTab(p_event)) {
|
||||||
ret = true;
|
ret = true;
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
} else {
|
break;
|
||||||
switch (key) {
|
}
|
||||||
case Qt::Key_1:
|
|
||||||
case Qt::Key_2:
|
|
||||||
case Qt::Key_3:
|
|
||||||
case Qt::Key_4:
|
|
||||||
case Qt::Key_5:
|
|
||||||
case Qt::Key_6:
|
|
||||||
{
|
|
||||||
if (modifiers == Qt::ControlModifier) {
|
|
||||||
// Ctrl + <N>: insert title at level <N>.
|
|
||||||
if (insertTitle(key - Qt::Key_0)) {
|
|
||||||
p_event->accept();
|
|
||||||
ret = true;
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case Qt::Key_Tab:
|
case Qt::Key_Backtab:
|
||||||
{
|
{
|
||||||
if (handleKeyTab(p_event)) {
|
if (handleKeyBackTab(p_event)) {
|
||||||
ret = true;
|
ret = true;
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case Qt::Key_Backtab:
|
case Qt::Key_B:
|
||||||
{
|
{
|
||||||
if (handleKeyBackTab(p_event)) {
|
if (handleKeyB(p_event)) {
|
||||||
ret = true;
|
ret = true;
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case Qt::Key_B:
|
case Qt::Key_H:
|
||||||
{
|
{
|
||||||
if (handleKeyB(p_event)) {
|
if (handleKeyH(p_event)) {
|
||||||
ret = true;
|
ret = true;
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case Qt::Key_D:
|
case Qt::Key_I:
|
||||||
{
|
{
|
||||||
if (handleKeyD(p_event)) {
|
if (handleKeyI(p_event)) {
|
||||||
ret = true;
|
ret = true;
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case Qt::Key_H:
|
case Qt::Key_O:
|
||||||
{
|
{
|
||||||
if (handleKeyH(p_event)) {
|
if (handleKeyO(p_event)) {
|
||||||
ret = true;
|
ret = true;
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case Qt::Key_I:
|
case Qt::Key_U:
|
||||||
{
|
{
|
||||||
if (handleKeyI(p_event)) {
|
if (handleKeyU(p_event)) {
|
||||||
ret = true;
|
ret = true;
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case Qt::Key_O:
|
case Qt::Key_W:
|
||||||
{
|
{
|
||||||
if (handleKeyO(p_event)) {
|
if (handleKeyW(p_event)) {
|
||||||
ret = true;
|
ret = true;
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case Qt::Key_U:
|
case Qt::Key_BracketLeft:
|
||||||
{
|
{
|
||||||
if (handleKeyU(p_event)) {
|
if (handleKeyBracketLeft(p_event)) {
|
||||||
ret = true;
|
ret = true;
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case Qt::Key_W:
|
case Qt::Key_Escape:
|
||||||
{
|
{
|
||||||
if (handleKeyW(p_event)) {
|
if (handleKeyEsc(p_event)) {
|
||||||
ret = true;
|
ret = true;
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case Qt::Key_BracketLeft:
|
case Qt::Key_Return:
|
||||||
{
|
{
|
||||||
if (handleKeyBracketLeft(p_event)) {
|
if (handleKeyReturn(p_event)) {
|
||||||
ret = true;
|
ret = true;
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case Qt::Key_Escape:
|
default:
|
||||||
{
|
break;
|
||||||
if (handleKeyEsc(p_event)) {
|
|
||||||
ret = true;
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case Qt::Key_Return:
|
|
||||||
{
|
|
||||||
if (handleKeyReturn(p_event)) {
|
|
||||||
ret = true;
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
exit:
|
exit:
|
||||||
@ -357,44 +329,32 @@ exit:
|
|||||||
key != Qt::Key_Shift) {
|
key != Qt::Key_Shift) {
|
||||||
m_autoIndentPos = -1;
|
m_autoIndentPos = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Let Ctrl+[ behave exactly like ESC.
|
// Let Ctrl+[ behave exactly like ESC.
|
||||||
bool VMdEditOperations::handleKeyBracketLeft(QKeyEvent *p_event)
|
bool VMdEditOperations::handleKeyBracketLeft(QKeyEvent *p_event)
|
||||||
{
|
{
|
||||||
// 1. If it is not in Normal state, just go back to Normal state;
|
// 1. If there is any selection, clear it.
|
||||||
// 2. If it is already Normal state, try to clear selection;
|
// 2. Otherwise, ignore this event and let parent handles it.
|
||||||
// 3. Otherwise, ignore this event and let parent handles it.
|
|
||||||
bool accept = false;
|
|
||||||
if (p_event->modifiers() == Qt::ControlModifier) {
|
if (p_event->modifiers() == Qt::ControlModifier) {
|
||||||
if (m_keyState != KeyState::Normal) {
|
QTextCursor cursor = m_editor->textCursor();
|
||||||
m_pendingTimer->stop();
|
if (cursor.hasSelection()) {
|
||||||
setKeyState(KeyState::Normal);
|
cursor.clearSelection();
|
||||||
m_pendingKey.clear();
|
m_editor->setTextCursor(cursor);
|
||||||
accept = true;
|
p_event->accept();
|
||||||
} else {
|
return true;
|
||||||
QTextCursor cursor = m_editor->textCursor();
|
|
||||||
if (cursor.hasSelection()) {
|
|
||||||
cursor.clearSelection();
|
|
||||||
m_editor->setTextCursor(cursor);
|
|
||||||
accept = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (accept) {
|
|
||||||
p_event->accept();
|
return false;
|
||||||
}
|
|
||||||
return accept;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VMdEditOperations::handleKeyTab(QKeyEvent *p_event)
|
bool VMdEditOperations::handleKeyTab(QKeyEvent *p_event)
|
||||||
{
|
{
|
||||||
QTextDocument *doc = m_editor->document();
|
QTextDocument *doc = m_editor->document();
|
||||||
QString text("\t");
|
QString text(m_editConfig->m_tabSpaces);
|
||||||
if (m_expandTab) {
|
|
||||||
text = m_tabSpaces;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (p_event->modifiers() == Qt::NoModifier) {
|
if (p_event->modifiers() == Qt::NoModifier) {
|
||||||
QTextCursor cursor = m_editor->textCursor();
|
QTextCursor cursor = m_editor->textCursor();
|
||||||
@ -483,8 +443,8 @@ bool VMdEditOperations::handleKeyBackTab(QKeyEvent *p_event)
|
|||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
// Spaces.
|
// Spaces.
|
||||||
if (m_expandTab) {
|
if (m_editConfig->m_expandTab) {
|
||||||
int width = m_tabSpaces.size();
|
int width = m_editConfig->m_tabSpaces.size();
|
||||||
for (int i = 0; i < width; ++i) {
|
for (int i = 0; i < width; ++i) {
|
||||||
if (text[i] == ' ') {
|
if (text[i] == ' ') {
|
||||||
blockCursor.deleteChar();
|
blockCursor.deleteChar();
|
||||||
@ -555,20 +515,6 @@ bool VMdEditOperations::handleKeyB(QKeyEvent *p_event)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VMdEditOperations::handleKeyD(QKeyEvent *p_event)
|
|
||||||
{
|
|
||||||
if (p_event->modifiers() == Qt::ControlModifier) {
|
|
||||||
// Ctrl+D, enter Vim-pending mode.
|
|
||||||
// Will accept the key stroke in m_pendingTime as Vim normal command.
|
|
||||||
setKeyState(KeyState::Vim);
|
|
||||||
m_pendingTimer->stop();
|
|
||||||
m_pendingTimer->start();
|
|
||||||
p_event->accept();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool VMdEditOperations::handleKeyH(QKeyEvent *p_event)
|
bool VMdEditOperations::handleKeyH(QKeyEvent *p_event)
|
||||||
{
|
{
|
||||||
if (p_event->modifiers() == Qt::ControlModifier) {
|
if (p_event->modifiers() == Qt::ControlModifier) {
|
||||||
@ -713,27 +659,17 @@ bool VMdEditOperations::handleKeyW(QKeyEvent *p_event)
|
|||||||
|
|
||||||
bool VMdEditOperations::handleKeyEsc(QKeyEvent *p_event)
|
bool VMdEditOperations::handleKeyEsc(QKeyEvent *p_event)
|
||||||
{
|
{
|
||||||
// 1. If it is not in Normal state, just go back to Normal state;
|
// 1. If there is any selection, clear it.
|
||||||
// 2. If it is already Normal state, try to clear selection;
|
// 2. Otherwise, ignore this event and let parent handles it.
|
||||||
// 3. Otherwise, ignore this event and let parent handles it.
|
QTextCursor cursor = m_editor->textCursor();
|
||||||
bool accept = false;
|
if (cursor.hasSelection()) {
|
||||||
if (m_keyState != KeyState::Normal) {
|
cursor.clearSelection();
|
||||||
m_pendingTimer->stop();
|
m_editor->setTextCursor(cursor);
|
||||||
setKeyState(KeyState::Normal);
|
|
||||||
m_pendingKey.clear();
|
|
||||||
accept = true;
|
|
||||||
} else {
|
|
||||||
QTextCursor cursor = m_editor->textCursor();
|
|
||||||
if (cursor.hasSelection()) {
|
|
||||||
cursor.clearSelection();
|
|
||||||
m_editor->setTextCursor(cursor);
|
|
||||||
accept = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (accept) {
|
|
||||||
p_event->accept();
|
p_event->accept();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
return accept;
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VMdEditOperations::handleKeyReturn(QKeyEvent *p_event)
|
bool VMdEditOperations::handleKeyReturn(QKeyEvent *p_event)
|
||||||
@ -935,399 +871,6 @@ void VMdEditOperations::deleteIndentAndListMark()
|
|||||||
m_editor->setTextCursor(cursor);
|
m_editor->setTextCursor(cursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VMdEditOperations::handleKeyPressVim(QKeyEvent *p_event)
|
|
||||||
{
|
|
||||||
int modifiers = p_event->modifiers();
|
|
||||||
bool visualMode = m_keyState == KeyState::VimVisual;
|
|
||||||
QTextCursor::MoveMode mode = visualMode ? QTextCursor::KeepAnchor
|
|
||||||
: QTextCursor::MoveAnchor;
|
|
||||||
|
|
||||||
switch (p_event->key()) {
|
|
||||||
// Ctrl and Shift may be sent out first.
|
|
||||||
case Qt::Key_Control:
|
|
||||||
// Fall through.
|
|
||||||
case Qt::Key_Shift:
|
|
||||||
{
|
|
||||||
goto pending;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case Qt::Key_H:
|
|
||||||
case Qt::Key_J:
|
|
||||||
case Qt::Key_K:
|
|
||||||
case Qt::Key_L:
|
|
||||||
{
|
|
||||||
if (modifiers == Qt::NoModifier) {
|
|
||||||
QTextCursor::MoveOperation op = QTextCursor::Left;
|
|
||||||
switch (p_event->key()) {
|
|
||||||
case Qt::Key_H:
|
|
||||||
op = QTextCursor::Left;
|
|
||||||
break;
|
|
||||||
case Qt::Key_J:
|
|
||||||
op = QTextCursor::Down;
|
|
||||||
break;
|
|
||||||
case Qt::Key_K:
|
|
||||||
op = QTextCursor::Up;
|
|
||||||
break;
|
|
||||||
case Qt::Key_L:
|
|
||||||
op = QTextCursor::Right;
|
|
||||||
}
|
|
||||||
// Move cursor <repeat> characters left/Down/Up/Right.
|
|
||||||
int repeat = keySeqToNumber(m_pendingKey);
|
|
||||||
m_pendingKey.clear();
|
|
||||||
QTextCursor cursor = m_editor->textCursor();
|
|
||||||
cursor.movePosition(op, mode, repeat == 0 ? 1 : repeat);
|
|
||||||
m_editor->setTextCursor(cursor);
|
|
||||||
goto pending;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case Qt::Key_1:
|
|
||||||
case Qt::Key_2:
|
|
||||||
case Qt::Key_3:
|
|
||||||
case Qt::Key_4:
|
|
||||||
case Qt::Key_5:
|
|
||||||
case Qt::Key_6:
|
|
||||||
case Qt::Key_7:
|
|
||||||
case Qt::Key_8:
|
|
||||||
case Qt::Key_9:
|
|
||||||
{
|
|
||||||
if (modifiers == Qt::NoModifier) {
|
|
||||||
if (!suffixNumAllowed(m_pendingKey)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
int num = p_event->key() - Qt::Key_0;
|
|
||||||
m_pendingKey.append(QString::number(num));
|
|
||||||
goto pending;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case Qt::Key_X:
|
|
||||||
{
|
|
||||||
// Delete characters.
|
|
||||||
if (modifiers == Qt::NoModifier) {
|
|
||||||
int repeat = keySeqToNumber(m_pendingKey);
|
|
||||||
m_pendingKey.clear();
|
|
||||||
QTextCursor cursor = m_editor->textCursor();
|
|
||||||
if (repeat == 0) {
|
|
||||||
repeat = 1;
|
|
||||||
}
|
|
||||||
cursor.beginEditBlock();
|
|
||||||
if (cursor.hasSelection()) {
|
|
||||||
QClipboard *clipboard = QApplication::clipboard();
|
|
||||||
clipboard->setText(cursor.selectedText());
|
|
||||||
cursor.removeSelectedText();
|
|
||||||
} else {
|
|
||||||
for (int i = 0; i < repeat; ++i) {
|
|
||||||
cursor.deleteChar();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cursor.endEditBlock();
|
|
||||||
m_editor->setTextCursor(cursor);
|
|
||||||
goto pending;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case Qt::Key_W:
|
|
||||||
{
|
|
||||||
if (modifiers == Qt::NoModifier) {
|
|
||||||
// Move to the start of the next word.
|
|
||||||
// Slightly different from the Vim behavior.
|
|
||||||
int repeat = keySeqToNumber(m_pendingKey);
|
|
||||||
m_pendingKey.clear();
|
|
||||||
QTextCursor cursor = m_editor->textCursor();
|
|
||||||
if (repeat == 0) {
|
|
||||||
repeat = 1;
|
|
||||||
}
|
|
||||||
cursor.movePosition(QTextCursor::NextWord, mode, repeat);
|
|
||||||
m_editor->setTextCursor(cursor);
|
|
||||||
goto pending;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case Qt::Key_E:
|
|
||||||
{
|
|
||||||
if (modifiers == Qt::NoModifier) {
|
|
||||||
// Move to the end of the next word.
|
|
||||||
// Slightly different from the Vim behavior.
|
|
||||||
int repeat = keySeqToNumber(m_pendingKey);
|
|
||||||
m_pendingKey.clear();
|
|
||||||
QTextCursor cursor = m_editor->textCursor();
|
|
||||||
if (repeat == 0) {
|
|
||||||
repeat = 1;
|
|
||||||
}
|
|
||||||
cursor.beginEditBlock();
|
|
||||||
int pos = cursor.position();
|
|
||||||
// First move to the end of current word.
|
|
||||||
cursor.movePosition(QTextCursor::EndOfWord, mode);
|
|
||||||
if (cursor.position() != pos) {
|
|
||||||
// We did move.
|
|
||||||
repeat--;
|
|
||||||
}
|
|
||||||
if (repeat) {
|
|
||||||
cursor.movePosition(QTextCursor::NextWord, mode, repeat);
|
|
||||||
cursor.movePosition(QTextCursor::EndOfWord, mode);
|
|
||||||
}
|
|
||||||
cursor.endEditBlock();
|
|
||||||
m_editor->setTextCursor(cursor);
|
|
||||||
goto pending;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case Qt::Key_B:
|
|
||||||
{
|
|
||||||
if (modifiers == Qt::NoModifier) {
|
|
||||||
// Move to the start of the previous word.
|
|
||||||
// Slightly different from the Vim behavior.
|
|
||||||
int repeat = keySeqToNumber(m_pendingKey);
|
|
||||||
m_pendingKey.clear();
|
|
||||||
QTextCursor cursor = m_editor->textCursor();
|
|
||||||
if (repeat == 0) {
|
|
||||||
repeat = 1;
|
|
||||||
}
|
|
||||||
cursor.beginEditBlock();
|
|
||||||
int pos = cursor.position();
|
|
||||||
// First move to the start of current word.
|
|
||||||
cursor.movePosition(QTextCursor::StartOfWord, mode);
|
|
||||||
if (cursor.position() != pos) {
|
|
||||||
// We did move.
|
|
||||||
repeat--;
|
|
||||||
}
|
|
||||||
if (repeat) {
|
|
||||||
cursor.movePosition(QTextCursor::PreviousWord, mode, repeat);
|
|
||||||
}
|
|
||||||
cursor.endEditBlock();
|
|
||||||
m_editor->setTextCursor(cursor);
|
|
||||||
goto pending;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case Qt::Key_0:
|
|
||||||
{
|
|
||||||
if (modifiers == Qt::NoModifier) {
|
|
||||||
if (keySeqToNumber(m_pendingKey) == 0) {
|
|
||||||
QTextCursor cursor = m_editor->textCursor();
|
|
||||||
cursor.movePosition(QTextCursor::StartOfLine, mode);
|
|
||||||
m_editor->setTextCursor(cursor);
|
|
||||||
} else {
|
|
||||||
m_pendingKey.append("0");
|
|
||||||
}
|
|
||||||
goto pending;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case Qt::Key_Dollar:
|
|
||||||
{
|
|
||||||
if (modifiers == Qt::ShiftModifier) {
|
|
||||||
if (m_pendingKey.isEmpty()) {
|
|
||||||
// Go to end of line.
|
|
||||||
QTextCursor cursor = m_editor->textCursor();
|
|
||||||
cursor.movePosition(QTextCursor::EndOfLine, mode);
|
|
||||||
m_editor->setTextCursor(cursor);
|
|
||||||
goto pending;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case Qt::Key_AsciiCircum:
|
|
||||||
{
|
|
||||||
if (modifiers == Qt::ShiftModifier) {
|
|
||||||
if (m_pendingKey.isEmpty()) {
|
|
||||||
// Go to first non-space character of current line.
|
|
||||||
QTextCursor cursor = m_editor->textCursor();
|
|
||||||
QTextBlock block = cursor.block();
|
|
||||||
QString text = block.text();
|
|
||||||
cursor.beginEditBlock();
|
|
||||||
if (text.trimmed().isEmpty()) {
|
|
||||||
cursor.movePosition(QTextCursor::StartOfLine, mode);
|
|
||||||
} else {
|
|
||||||
cursor.movePosition(QTextCursor::StartOfLine, mode);
|
|
||||||
int pos = cursor.positionInBlock();
|
|
||||||
while (pos < text.size() && text[pos].isSpace()) {
|
|
||||||
cursor.movePosition(QTextCursor::NextWord, mode);
|
|
||||||
pos = cursor.positionInBlock();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cursor.endEditBlock();
|
|
||||||
m_editor->setTextCursor(cursor);
|
|
||||||
goto pending;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case Qt::Key_G:
|
|
||||||
{
|
|
||||||
if (modifiers == Qt::NoModifier) {
|
|
||||||
// g, pending or go to first line.
|
|
||||||
if (m_pendingKey.isEmpty()) {
|
|
||||||
m_pendingKey.append("g");
|
|
||||||
goto pending;
|
|
||||||
} else if (m_pendingKey.size() == 1 && m_pendingKey.at(0) == "g") {
|
|
||||||
m_pendingKey.clear();
|
|
||||||
QTextCursor cursor = m_editor->textCursor();
|
|
||||||
cursor.movePosition(QTextCursor::Start, mode);
|
|
||||||
m_editor->setTextCursor(cursor);
|
|
||||||
goto pending;
|
|
||||||
}
|
|
||||||
} else if (modifiers == Qt::ShiftModifier) {
|
|
||||||
// G, go to a certain line or the end of document.
|
|
||||||
int lineNum = keySeqToNumber(m_pendingKey);
|
|
||||||
m_pendingKey.clear();
|
|
||||||
QTextCursor cursor = m_editor->textCursor();
|
|
||||||
if (lineNum == 0) {
|
|
||||||
cursor.movePosition(QTextCursor::End, mode);
|
|
||||||
} else {
|
|
||||||
QTextDocument *doc = m_editor->document();
|
|
||||||
QTextBlock block = doc->findBlockByNumber(lineNum - 1);
|
|
||||||
if (block.isValid()) {
|
|
||||||
cursor.setPosition(block.position(), mode);
|
|
||||||
} else {
|
|
||||||
// Go beyond the document.
|
|
||||||
cursor.movePosition(QTextCursor::End, mode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
m_editor->setTextCursor(cursor);
|
|
||||||
goto pending;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case Qt::Key_V:
|
|
||||||
{
|
|
||||||
if (modifiers == Qt::NoModifier) {
|
|
||||||
// V to enter visual mode.
|
|
||||||
if (m_pendingKey.isEmpty() && m_keyState != KeyState::VimVisual) {
|
|
||||||
setKeyState(KeyState::VimVisual);
|
|
||||||
goto pending;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case Qt::Key_Y:
|
|
||||||
{
|
|
||||||
if (modifiers == Qt::NoModifier) {
|
|
||||||
if (m_pendingKey.isEmpty()) {
|
|
||||||
QTextCursor cursor = m_editor->textCursor();
|
|
||||||
if (cursor.hasSelection()) {
|
|
||||||
QString text = cursor.selectedText();
|
|
||||||
QClipboard *clipboard = QApplication::clipboard();
|
|
||||||
clipboard->setText(text);
|
|
||||||
}
|
|
||||||
goto pending;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case Qt::Key_D:
|
|
||||||
{
|
|
||||||
if (modifiers == Qt::NoModifier) {
|
|
||||||
// d, pending or delete current line.
|
|
||||||
QTextCursor cursor = m_editor->textCursor();
|
|
||||||
if (m_pendingKey.isEmpty()) {
|
|
||||||
if (cursor.hasSelection()) {
|
|
||||||
cursor.deleteChar();
|
|
||||||
m_editor->setTextCursor(cursor);
|
|
||||||
} else {
|
|
||||||
m_pendingKey.append("d");
|
|
||||||
}
|
|
||||||
goto pending;
|
|
||||||
} else if (m_pendingKey.size() == 1 && m_pendingKey.at(0) == "d") {
|
|
||||||
m_pendingKey.clear();
|
|
||||||
cursor.select(QTextCursor::BlockUnderCursor);
|
|
||||||
cursor.removeSelectedText();
|
|
||||||
m_editor->setTextCursor(cursor);
|
|
||||||
goto pending;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
// Unknown key. End Vim mode.
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_pendingTimer->stop();
|
|
||||||
if (m_keyState == KeyState::VimVisual) {
|
|
||||||
// Clear the visual selection.
|
|
||||||
QTextCursor cursor = m_editor->textCursor();
|
|
||||||
cursor.clearSelection();
|
|
||||||
m_editor->setTextCursor(cursor);
|
|
||||||
}
|
|
||||||
setKeyState(KeyState::Normal);
|
|
||||||
m_pendingKey.clear();
|
|
||||||
p_event->accept();
|
|
||||||
return true;
|
|
||||||
|
|
||||||
pending:
|
|
||||||
// When pending in Ctrl+Alt, we just want to clear m_pendingKey.
|
|
||||||
if (m_pendingTimer->isActive()) {
|
|
||||||
m_pendingTimer->stop();
|
|
||||||
m_pendingTimer->start();
|
|
||||||
}
|
|
||||||
p_event->accept();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
int VMdEditOperations::keySeqToNumber(const QList<QString> &p_seq)
|
|
||||||
{
|
|
||||||
int num = 0;
|
|
||||||
for (int i = 0; i < p_seq.size(); ++i) {
|
|
||||||
QString tmp = p_seq.at(i);
|
|
||||||
bool ok;
|
|
||||||
int tmpInt = tmp.toInt(&ok);
|
|
||||||
if (!ok) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
num = num * 10 + tmpInt;
|
|
||||||
}
|
|
||||||
return num;
|
|
||||||
}
|
|
||||||
|
|
||||||
void VMdEditOperations::pendingTimerTimeout()
|
|
||||||
{
|
|
||||||
qDebug() << "key pending timer timeout";
|
|
||||||
if (m_keyState == KeyState::VimVisual) {
|
|
||||||
m_pendingTimer->start();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setKeyState(KeyState::Normal);
|
|
||||||
m_pendingKey.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool VMdEditOperations::suffixNumAllowed(const QList<QString> &p_seq)
|
|
||||||
{
|
|
||||||
if (!p_seq.isEmpty()) {
|
|
||||||
QString firstEle = p_seq.at(0);
|
|
||||||
if (firstEle[0].isDigit()) {
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void VMdEditOperations::setKeyState(KeyState p_state)
|
|
||||||
{
|
|
||||||
if (m_keyState == p_state) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
m_keyState = p_state;
|
|
||||||
emit keyStateChanged(m_keyState);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool VMdEditOperations::insertTitle(int p_level)
|
bool VMdEditOperations::insertTitle(int p_level)
|
||||||
{
|
{
|
||||||
Q_ASSERT(p_level > 0 && p_level < 7);
|
Q_ASSERT(p_level > 0 && p_level < 7);
|
||||||
|
@ -21,9 +21,6 @@ public:
|
|||||||
bool handleKeyPressEvent(QKeyEvent *p_event) Q_DECL_OVERRIDE;
|
bool handleKeyPressEvent(QKeyEvent *p_event) Q_DECL_OVERRIDE;
|
||||||
bool insertImageFromURL(const QUrl &p_imageUrl) Q_DECL_OVERRIDE;
|
bool insertImageFromURL(const QUrl &p_imageUrl) Q_DECL_OVERRIDE;
|
||||||
|
|
||||||
private slots:
|
|
||||||
void pendingTimerTimeout();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void insertImageFromPath(const QString &title, const QString &path, const QString &oriImagePath);
|
void insertImageFromPath(const QString &title, const QString &path, const QString &oriImagePath);
|
||||||
|
|
||||||
@ -32,13 +29,10 @@ private:
|
|||||||
// @image: the image to be inserted;
|
// @image: the image to be inserted;
|
||||||
void insertImageFromQImage(const QString &title, const QString &path, const QImage &image);
|
void insertImageFromQImage(const QString &title, const QString &path, const QImage &image);
|
||||||
|
|
||||||
void setKeyState(KeyState p_state);
|
|
||||||
|
|
||||||
// Key press handlers.
|
// Key press handlers.
|
||||||
bool handleKeyTab(QKeyEvent *p_event);
|
bool handleKeyTab(QKeyEvent *p_event);
|
||||||
bool handleKeyBackTab(QKeyEvent *p_event);
|
bool handleKeyBackTab(QKeyEvent *p_event);
|
||||||
bool handleKeyB(QKeyEvent *p_event);
|
bool handleKeyB(QKeyEvent *p_event);
|
||||||
bool handleKeyD(QKeyEvent *p_event);
|
|
||||||
bool handleKeyH(QKeyEvent *p_event);
|
bool handleKeyH(QKeyEvent *p_event);
|
||||||
bool handleKeyI(QKeyEvent *p_event);
|
bool handleKeyI(QKeyEvent *p_event);
|
||||||
bool handleKeyO(QKeyEvent *p_event);
|
bool handleKeyO(QKeyEvent *p_event);
|
||||||
@ -46,11 +40,7 @@ private:
|
|||||||
bool handleKeyW(QKeyEvent *p_event);
|
bool handleKeyW(QKeyEvent *p_event);
|
||||||
bool handleKeyEsc(QKeyEvent *p_event);
|
bool handleKeyEsc(QKeyEvent *p_event);
|
||||||
bool handleKeyReturn(QKeyEvent *p_event);
|
bool handleKeyReturn(QKeyEvent *p_event);
|
||||||
bool handleKeyPressVim(QKeyEvent *p_event);
|
|
||||||
bool handleKeyBracketLeft(QKeyEvent *p_event);
|
bool handleKeyBracketLeft(QKeyEvent *p_event);
|
||||||
bool shouldTriggerVimMode(QKeyEvent *p_event);
|
|
||||||
int keySeqToNumber(const QList<QString> &p_seq);
|
|
||||||
bool suffixNumAllowed(const QList<QString> &p_seq);
|
|
||||||
bool insertTitle(int p_level);
|
bool insertTitle(int p_level);
|
||||||
bool insertNewBlockWithIndent();
|
bool insertNewBlockWithIndent();
|
||||||
bool insertListMarkAsPreviousLine();
|
bool insertListMarkAsPreviousLine();
|
||||||
@ -67,7 +57,6 @@ private:
|
|||||||
// Change the sequence number of a list block.
|
// Change the sequence number of a list block.
|
||||||
void changeListBlockSeqNumber(QTextBlock &p_block, int p_seq);
|
void changeListBlockSeqNumber(QTextBlock &p_block, int p_seq);
|
||||||
|
|
||||||
QTimer *m_pendingTimer;
|
|
||||||
// The cursor position after auto indent or auto list.
|
// The cursor position after auto indent or auto list.
|
||||||
// It will be -1 if last key press do not trigger the auto indent or auto list.
|
// It will be -1 if last key press do not trigger the auto indent or auto list.
|
||||||
int m_autoIndentPos;
|
int m_autoIndentPos;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user