refine auto indent and auto list

1. Enter will cancel the auto indent and auto list;
2. Tab/Shift+Tab will increase or descrease the indentation of auto list;
This commit is contained in:
Le Tan 2017-04-10 23:06:17 +08:00
parent 177e5495d8
commit f1c101b1d8
2 changed files with 154 additions and 33 deletions

View File

@ -27,7 +27,7 @@ extern VConfigManager vconfig;
const QString VMdEditOperations::c_defaultImageTitle = "image"; 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) : VEditOperations(p_editor, p_file), m_autoIndentPos(-1)
{ {
m_pendingTimer = new QTimer(this); m_pendingTimer = new QTimer(this);
m_pendingTimer->setSingleShot(true); m_pendingTimer->setSingleShot(true);
@ -176,13 +176,17 @@ bool VMdEditOperations::shouldTriggerVimMode(QKeyEvent *p_event)
bool VMdEditOperations::handleKeyPressEvent(QKeyEvent *p_event) bool VMdEditOperations::handleKeyPressEvent(QKeyEvent *p_event)
{ {
bool ret = false;
int key = p_event->key();
int modifiers = p_event->modifiers();
if (shouldTriggerVimMode(p_event)) { if (shouldTriggerVimMode(p_event)) {
if (handleKeyPressVim(p_event)) { if (handleKeyPressVim(p_event)) {
return true; ret = true;
goto exit;
} }
} else { } else {
int modifiers = p_event->modifiers(); switch (key) {
switch (p_event->key()) {
case Qt::Key_1: case Qt::Key_1:
case Qt::Key_2: case Qt::Key_2:
case Qt::Key_3: case Qt::Key_3:
@ -192,9 +196,10 @@ bool VMdEditOperations::handleKeyPressEvent(QKeyEvent *p_event)
{ {
if (modifiers == (Qt::ControlModifier | Qt::AltModifier)) { if (modifiers == (Qt::ControlModifier | Qt::AltModifier)) {
// Ctrl + Alt + <N>: insert title at level <N>. // Ctrl + Alt + <N>: insert title at level <N>.
if (insertTitle(p_event->key() - Qt::Key_0)) { if (insertTitle(key - Qt::Key_0)) {
p_event->accept(); p_event->accept();
return true; ret = true;
goto exit;
} }
} }
break; break;
@ -203,7 +208,8 @@ bool VMdEditOperations::handleKeyPressEvent(QKeyEvent *p_event)
case Qt::Key_Tab: case Qt::Key_Tab:
{ {
if (handleKeyTab(p_event)) { if (handleKeyTab(p_event)) {
return true; ret = true;
goto exit;
} }
break; break;
} }
@ -211,7 +217,8 @@ bool VMdEditOperations::handleKeyPressEvent(QKeyEvent *p_event)
case Qt::Key_Backtab: case Qt::Key_Backtab:
{ {
if (handleKeyBackTab(p_event)) { if (handleKeyBackTab(p_event)) {
return true; ret = true;
goto exit;
} }
break; break;
} }
@ -219,7 +226,8 @@ bool VMdEditOperations::handleKeyPressEvent(QKeyEvent *p_event)
case Qt::Key_B: case Qt::Key_B:
{ {
if (handleKeyB(p_event)) { if (handleKeyB(p_event)) {
return true; ret = true;
goto exit;
} }
break; break;
} }
@ -227,7 +235,8 @@ bool VMdEditOperations::handleKeyPressEvent(QKeyEvent *p_event)
case Qt::Key_D: case Qt::Key_D:
{ {
if (handleKeyD(p_event)) { if (handleKeyD(p_event)) {
return true; ret = true;
goto exit;
} }
break; break;
} }
@ -235,7 +244,8 @@ bool VMdEditOperations::handleKeyPressEvent(QKeyEvent *p_event)
case Qt::Key_H: case Qt::Key_H:
{ {
if (handleKeyH(p_event)) { if (handleKeyH(p_event)) {
return true; ret = true;
goto exit;
} }
break; break;
} }
@ -243,7 +253,8 @@ bool VMdEditOperations::handleKeyPressEvent(QKeyEvent *p_event)
case Qt::Key_I: case Qt::Key_I:
{ {
if (handleKeyI(p_event)) { if (handleKeyI(p_event)) {
return true; ret = true;
goto exit;
} }
break; break;
} }
@ -251,7 +262,8 @@ bool VMdEditOperations::handleKeyPressEvent(QKeyEvent *p_event)
case Qt::Key_O: case Qt::Key_O:
{ {
if (handleKeyO(p_event)) { if (handleKeyO(p_event)) {
return true; ret = true;
goto exit;
} }
break; break;
} }
@ -259,7 +271,8 @@ bool VMdEditOperations::handleKeyPressEvent(QKeyEvent *p_event)
case Qt::Key_U: case Qt::Key_U:
{ {
if (handleKeyU(p_event)) { if (handleKeyU(p_event)) {
return true; ret = true;
goto exit;
} }
break; break;
} }
@ -267,7 +280,8 @@ bool VMdEditOperations::handleKeyPressEvent(QKeyEvent *p_event)
case Qt::Key_W: case Qt::Key_W:
{ {
if (handleKeyW(p_event)) { if (handleKeyW(p_event)) {
return true; ret = true;
goto exit;
} }
break; break;
} }
@ -275,7 +289,8 @@ bool VMdEditOperations::handleKeyPressEvent(QKeyEvent *p_event)
case Qt::Key_BracketLeft: case Qt::Key_BracketLeft:
{ {
if (handleKeyBracketLeft(p_event)) { if (handleKeyBracketLeft(p_event)) {
return true; ret = true;
goto exit;
} }
break; break;
} }
@ -283,7 +298,8 @@ bool VMdEditOperations::handleKeyPressEvent(QKeyEvent *p_event)
case Qt::Key_Escape: case Qt::Key_Escape:
{ {
if (handleKeyEsc(p_event)) { if (handleKeyEsc(p_event)) {
return true; ret = true;
goto exit;
} }
break; break;
} }
@ -291,7 +307,8 @@ bool VMdEditOperations::handleKeyPressEvent(QKeyEvent *p_event)
case Qt::Key_Return: case Qt::Key_Return:
{ {
if (handleKeyReturn(p_event)) { if (handleKeyReturn(p_event)) {
return true; ret = true;
goto exit;
} }
break; break;
} }
@ -301,7 +318,13 @@ bool VMdEditOperations::handleKeyPressEvent(QKeyEvent *p_event)
} }
} }
return false; exit:
// Qt::Key_Return, Qt::Key_Tab and Qt::Key_Backtab will handle m_autoIndentPos.
if (key != Qt::Key_Return && key != Qt::Key_Tab && key != Qt::Key_Backtab &&
key != Qt::Key_Shift) {
m_autoIndentPos = -1;
}
return ret;
} }
// Let Ctrl+[ behave exactly like ESC. // Let Ctrl+[ behave exactly like ESC.
@ -342,8 +365,9 @@ bool VMdEditOperations::handleKeyTab(QKeyEvent *p_event)
if (p_event->modifiers() == Qt::NoModifier) { if (p_event->modifiers() == Qt::NoModifier) {
QTextCursor cursor = m_editor->textCursor(); QTextCursor cursor = m_editor->textCursor();
cursor.beginEditBlock();
if (cursor.hasSelection()) { if (cursor.hasSelection()) {
m_autoIndentPos = -1;
cursor.beginEditBlock();
// Indent each selected line. // Indent each selected line.
QTextBlock block = doc->findBlock(cursor.selectionStart()); QTextBlock block = doc->findBlock(cursor.selectionStart());
QTextBlock endBlock = doc->findBlock(cursor.selectionEnd()); QTextBlock endBlock = doc->findBlock(cursor.selectionEnd());
@ -358,12 +382,23 @@ bool VMdEditOperations::handleKeyTab(QKeyEvent *p_event)
} }
block = block.next(); block = block.next();
} }
cursor.endEditBlock();
} else { } else {
// Just insert "tab". // If it is a Tab key following auto list, increase the indent level.
insertTextAtCurPos(text); QTextBlock block = cursor.block();
if (m_autoIndentPos == cursor.position() && isListBlock(block)) {
QTextCursor blockCursor(block);
blockCursor.insertText(text);
// Change m_autoIndentPos to let it can be repeated.
m_autoIndentPos = m_editor->textCursor().position();
} else {
// Just insert "tab".
insertTextAtCurPos(text);
m_autoIndentPos = -1;
}
} }
cursor.endEditBlock();
} else { } else {
m_autoIndentPos = -1;
return false; return false;
} }
p_event->accept(); p_event->accept();
@ -373,12 +408,19 @@ bool VMdEditOperations::handleKeyTab(QKeyEvent *p_event)
bool VMdEditOperations::handleKeyBackTab(QKeyEvent *p_event) bool VMdEditOperations::handleKeyBackTab(QKeyEvent *p_event)
{ {
if (p_event->modifiers() != Qt::ShiftModifier) { if (p_event->modifiers() != Qt::ShiftModifier) {
m_autoIndentPos = -1;
return false; return false;
} }
QTextDocument *doc = m_editor->document(); QTextDocument *doc = m_editor->document();
QTextCursor cursor = m_editor->textCursor(); QTextCursor cursor = m_editor->textCursor();
QTextBlock block = doc->findBlock(cursor.selectionStart()); QTextBlock block = doc->findBlock(cursor.selectionStart());
QTextBlock endBlock = doc->findBlock(cursor.selectionEnd()); QTextBlock endBlock = doc->findBlock(cursor.selectionEnd());
bool continueAutoIndent = false;
if (cursor.position() == m_autoIndentPos && isListBlock(block) &&
!cursor.hasSelection()) {
continueAutoIndent = true;
}
int endBlockNum = endBlock.blockNumber(); int endBlockNum = endBlock.blockNumber();
cursor.beginEditBlock(); cursor.beginEditBlock();
for (; block.isValid() && block.blockNumber() <= endBlockNum; for (; block.isValid() && block.blockNumber() <= endBlockNum;
@ -411,6 +453,12 @@ bool VMdEditOperations::handleKeyBackTab(QKeyEvent *p_event)
} }
} }
cursor.endEditBlock(); cursor.endEditBlock();
if (continueAutoIndent) {
m_autoIndentPos = m_editor->textCursor().position();
} else {
m_autoIndentPos = -1;
}
p_event->accept(); p_event->accept();
return true; return true;
} }
@ -644,26 +692,57 @@ bool VMdEditOperations::handleKeyEsc(QKeyEvent *p_event)
bool VMdEditOperations::handleKeyReturn(QKeyEvent *p_event) bool VMdEditOperations::handleKeyReturn(QKeyEvent *p_event)
{ {
if (p_event->modifiers() & Qt::ControlModifier) { if (p_event->modifiers() & Qt::ControlModifier) {
m_autoIndentPos = -1;
return false; return false;
} }
bool ret = false; // See if we need to cancel auto indent.
if (m_autoIndentPos > -1) {
// Cancel the auto indent/list if the pos is the same and cursor is at
// the end of a block.
bool cancelAutoIndent = false;
QTextCursor cursor = m_editor->textCursor();
QTextBlock block = cursor.block();
if (cursor.position() == m_autoIndentPos && !cursor.hasSelection()) {
if (isListBlock(block)) {
if (cursor.atBlockEnd()) {
cancelAutoIndent = true;
}
} else if (isSpaceToBlockStart(block, cursor.positionInBlock())) {
cancelAutoIndent = true;
}
}
if (cancelAutoIndent) {
m_autoIndentPos = -1;
deleteIndentAndListMark();
return true;
}
}
bool handled = false;
m_autoIndentPos = -1;
if (vconfig.getAutoIndent()) { if (vconfig.getAutoIndent()) {
ret = true; handled = true;
bool textInserted = false;
// Indent the new line as previous line. // Indent the new line as previous line.
insertNewBlockWithIndent(); textInserted = insertNewBlockWithIndent();
// Continue the list from previous line. // Continue the list from previous line.
if (vconfig.getAutoList()) { if (vconfig.getAutoList()) {
insertListMarkAsPreviousLine(); textInserted = insertListMarkAsPreviousLine() || textInserted;
}
if (textInserted) {
m_autoIndentPos = m_editor->textCursor().position();
} }
} }
return ret; return handled;
} }
void VMdEditOperations::insertNewBlockWithIndent() bool VMdEditOperations::insertNewBlockWithIndent()
{ {
QTextCursor cursor = m_editor->textCursor(); QTextCursor cursor = m_editor->textCursor();
bool ret = false;
cursor.beginEditBlock(); cursor.beginEditBlock();
cursor.removeSelectedText(); cursor.removeSelectedText();
@ -674,13 +753,18 @@ void VMdEditOperations::insertNewBlockWithIndent()
V_ASSERT(regExp.captureCount() == 1); V_ASSERT(regExp.captureCount() == 1);
QString leadingSpaces = regExp.capturedTexts()[1]; QString leadingSpaces = regExp.capturedTexts()[1];
cursor.insertBlock(); cursor.insertBlock();
cursor.insertText(leadingSpaces); if (!leadingSpaces.isEmpty()) {
cursor.insertText(leadingSpaces);
ret = true;
}
cursor.endEditBlock(); cursor.endEditBlock();
m_editor->setTextCursor(cursor); m_editor->setTextCursor(cursor);
return ret;
} }
void VMdEditOperations::insertListMarkAsPreviousLine() bool VMdEditOperations::insertListMarkAsPreviousLine()
{ {
bool ret = false;
QTextCursor cursor = m_editor->textCursor(); QTextCursor cursor = m_editor->textCursor();
QTextBlock block = cursor.block(); QTextBlock block = cursor.block();
QTextBlock preBlock = block.previous(); QTextBlock preBlock = block.previous();
@ -688,6 +772,7 @@ void VMdEditOperations::insertListMarkAsPreviousLine()
QRegExp regExp("^\\s*(-|\\d+\\.)\\s"); QRegExp regExp("^\\s*(-|\\d+\\.)\\s");
int regIdx = regExp.indexIn(text); int regIdx = regExp.indexIn(text);
if (regIdx != -1) { if (regIdx != -1) {
ret = true;
V_ASSERT(regExp.captureCount() == 1); V_ASSERT(regExp.captureCount() == 1);
cursor.beginEditBlock(); cursor.beginEditBlock();
QString markText = regExp.capturedTexts()[1]; QString markText = regExp.capturedTexts()[1];
@ -706,6 +791,34 @@ void VMdEditOperations::insertListMarkAsPreviousLine()
cursor.endEditBlock(); cursor.endEditBlock();
m_editor->setTextCursor(cursor); m_editor->setTextCursor(cursor);
} }
return ret;
}
bool VMdEditOperations::isListBlock(const QTextBlock &p_block)
{
QString text = p_block.text();
QRegExp regExp("^\\s*(-|\\d+\\.)\\s");
int regIdx = regExp.indexIn(text);
return regIdx != -1;
}
bool VMdEditOperations::isSpaceToBlockStart(const QTextBlock &p_block, int p_posInBlock)
{
if (p_posInBlock <= 0) {
return true;
}
QString text = p_block.text();
V_ASSERT(text.size() >= p_posInBlock);
return text.left(p_posInBlock).trimmed().isEmpty();
}
void VMdEditOperations::deleteIndentAndListMark()
{
QTextCursor cursor = m_editor->textCursor();
V_ASSERT(!cursor.hasSelection());
cursor.movePosition(QTextCursor::StartOfBlock, QTextCursor::KeepAnchor);
cursor.removeSelectedText();
m_editor->setTextCursor(cursor);
} }
bool VMdEditOperations::handleKeyPressVim(QKeyEvent *p_event) bool VMdEditOperations::handleKeyPressVim(QKeyEvent *p_event)

View File

@ -5,6 +5,7 @@
#include <QString> #include <QString>
#include <QUrl> #include <QUrl>
#include <QImage> #include <QImage>
#include <QTextBlock>
#include "veditoperations.h" #include "veditoperations.h"
class QTimer; class QTimer;
@ -46,10 +47,17 @@ private:
int keySeqToNumber(const QList<QString> &p_seq); int keySeqToNumber(const QList<QString> &p_seq);
bool suffixNumAllowed(const QList<QString> &p_seq); bool suffixNumAllowed(const QList<QString> &p_seq);
bool insertTitle(int p_level); bool insertTitle(int p_level);
void insertNewBlockWithIndent(); bool insertNewBlockWithIndent();
void insertListMarkAsPreviousLine(); bool insertListMarkAsPreviousLine();
void deleteIndentAndListMark();
bool isListBlock(const QTextBlock &p_block);
// If the start of @p_block to postition @p_posInBlock are spaces.
bool isSpaceToBlockStart(const QTextBlock &p_block, int p_posInBlock);
QTimer *m_pendingTimer; QTimer *m_pendingTimer;
// 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.
int m_autoIndentPos;
static const QString c_defaultImageTitle; static const QString c_defaultImageTitle;
}; };