support Insert Code Block tool bar button

Ctrl+M to insert a code block.
This commit is contained in:
Le Tan 2017-10-19 19:43:10 +08:00
parent e66b70b6ff
commit 30dfc24a28
12 changed files with 189 additions and 25 deletions

View File

@ -49,13 +49,17 @@ Save current changes and exit edit mode.
#### Text Editing
- `Ctrl+B`
Insert bold. Press `Ctrl+B` again to exit. Current selected text will be changed to bold if exist.
Insert bold. Press `Ctrl+B` again to exit. Current selected text will be changed to bold if exists.
- `Ctrl+I`
Insert italic. Press `Ctrl+I` again to exit. Current selected text will be changed to italic if exist.
Insert italic. Press `Ctrl+I` again to exit. Current selected text will be changed to italic if exists.
- `Ctrl+D`
Insert strikethrought. Press `Ctrl+D` again to exit. Current selected text will be changed to strikethrough if exist.
Insert strikethrought. Press `Ctrl+D` again to exit. Current selected text will be changed to strikethrough if exists.
- `Ctrl+O`
Insert inline code. Press `Ctrl+O` again to exit. Current selected text will be changed to inline code if exist.
Insert inline code. Press `Ctrl+O` again to exit. Current selected text will be changed to inline code if exists.
- `Ctrl+M`
Insert fenced code block. Press `Ctrl+M` again to exit. Current selected text will be wrapped into a code block if exists.
- `Ctrl+L`
Insert link.
- `Ctrl+H`
Backspace. Delete a character backward.
- `Ctrl+W`
@ -63,7 +67,7 @@ Delete all the characters from current cursor to the first space backward.
- `Ctrl+U`
Delete all the characters from current cursor to the beginning of current line.
- `Ctrl+<Num>`
Insert title at level `<Num>`. `<Num>` should be 1 to 6. Current selected text will be changed to title if exist.
Insert title at level `<Num>`. `<Num>` should be 1 to 6. Current selected text will be changed to title if exists.
- `Tab`/`Shift+Tab`
Increase or decrease the indentation. If any text is selected, the indentation will operate on all these selected lines.
- `Shift+Enter`

View File

@ -56,6 +56,10 @@
插入删除线;再次按`Ctrl+D`退出。如果已经选择文本,则将当前选择文本改为删除线。
- `Ctrl+O`
插入行内代码;再次按`Ctrl+O`退出。如果已经选择文本,则将当前选择文本改为行内代码。
- `Ctrl+M`
插入代码块;再次按`Ctrl+M`退出。如果已经选择文本,则将当前选择文本嵌入到代码块中。
- `Ctrl+L`
插入链接。
- `Ctrl+H`
退格键,向前删除一个字符。
- `Ctrl+W`

View File

@ -0,0 +1,6 @@
<svg width="512" height="512" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg">
<g>
<title>Layer 1</title>
<text style="cursor: move;" fill="#000000" stroke-width="0" x="-146.75684" y="248.49038" id="svg_4" font-size="24" font-family="serif" text-anchor="middle" xml:space="preserve" font-weight="bold" transform="matrix(16.72881317138672,0,0,16.72881317138672,2707.567729830742,-3759.186347961426) " stroke="#000000">#</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 470 B

View File

@ -48,7 +48,7 @@ bool VEditUtils::insertBlockWithIndent(QTextCursor &p_cursor)
{
V_ASSERT(!p_cursor.hasSelection());
p_cursor.insertBlock();
return indentBlockAsPreviousBlock(p_cursor);
return indentBlockAsBlock(p_cursor, false);
}
bool VEditUtils::insertListMarkAsPreviousBlock(QTextCursor &p_cursor)
@ -85,21 +85,16 @@ bool VEditUtils::insertListMarkAsPreviousBlock(QTextCursor &p_cursor)
}
bool VEditUtils::indentBlockAsPreviousBlock(QTextCursor &p_cursor)
bool VEditUtils::indentBlockAsBlock(QTextCursor &p_cursor, bool p_next)
{
bool changed = false;
QTextBlock block = p_cursor.block();
if (block.blockNumber() == 0) {
// The first block.
QTextBlock refBlock = p_next ? block.next() : block.previous();
if (!refBlock.isValid()) {
return false;
}
QTextBlock preBlock = block.previous();
QString text = preBlock.text();
QRegExp regExp("(^\\s*)");
regExp.indexIn(text);
V_ASSERT(regExp.captureCount() == 1);
QString leadingSpaces = regExp.capturedTexts()[1];
QString leadingSpaces = fetchIndentSpaces(refBlock);
moveCursorFirstNonSpaceCharacter(p_cursor, QTextCursor::MoveAnchor);
if (!p_cursor.atBlockStart()) {
@ -789,3 +784,30 @@ void VEditUtils::findCurrentWORD(const QTextCursor &p_cursor,
p_end += block.position();
}
QString VEditUtils::fetchIndentSpaces(const QTextBlock &p_block)
{
QString text = p_block.text();
QRegExp regExp("(^\\s*)");
regExp.indexIn(text);
Q_ASSERT(regExp.captureCount() == 1);
return regExp.capturedTexts()[1];
}
void VEditUtils::insertBlock(QTextCursor &p_cursor,
bool p_above)
{
p_cursor.movePosition(p_above ? QTextCursor::StartOfBlock
: QTextCursor::EndOfBlock,
QTextCursor::MoveAnchor,
1);
p_cursor.insertBlock();
if (p_above) {
p_cursor.movePosition(QTextCursor::PreviousBlock,
QTextCursor::MoveAnchor,
1);
}
p_cursor.movePosition(QTextCursor::EndOfBlock);
}

View File

@ -24,10 +24,11 @@ public:
// Need to call setTextCursor() to make it take effect.
static void moveCursorFirstNonSpaceCharacter(QTextCursor &p_cursor,
QTextCursor::MoveMode p_mode);
// Indent current block as previous block.
// Indent current block as next/previous block.
// Return true if some changes have been made.
// @p_cursor will be placed at the position after inserting leading spaces.
static bool indentBlockAsPreviousBlock(QTextCursor &p_cursor);
// @p_next: indent as next block or previous block.
static bool indentBlockAsBlock(QTextCursor &p_cursor, bool p_next);
// Returns true if two blocks has the same indent.
static bool hasSameIndent(const QTextBlock &p_blocka, const QTextBlock &p_blockb);
@ -157,6 +158,14 @@ public:
int &p_start,
int &p_end);
// Return the leading spaces of @p_block.
static QString fetchIndentSpaces(const QTextBlock &p_block);
// Insert a block above/below current block. Move the cursor to the start of
// the new block after insertion.
static void insertBlock(QTextCursor &p_cursor,
bool p_above);
private:
VEditUtils() {}
};

View File

@ -296,7 +296,7 @@ static void insertChangeBlockAfterDeletion(QTextCursor &p_cursor, int p_deletion
}
if (g_config->getAutoIndent()) {
VEditUtils::indentBlockAsPreviousBlock(p_cursor);
VEditUtils::indentBlockAsBlock(p_cursor, false);
}
}
@ -911,7 +911,7 @@ bool VVim::handleKeyPressEvent(int key, int modifiers, int *p_autoIndentPos)
bool textInserted = false;
if (g_config->getAutoIndent()) {
textInserted = VEditUtils::indentBlockAsPreviousBlock(cursor);
textInserted = VEditUtils::indentBlockAsBlock(cursor, false);
if (g_config->getAutoList()) {
textInserted = VEditUtils::insertListMarkAsPreviousBlock(cursor)
|| textInserted;

View File

@ -9,6 +9,7 @@
class QKeyEvent;
class VNavigationMode;
class QShortcut;
// void func(void *p_target, void *p_data);
typedef std::function<void(void *, void *)> CaptainFunc;

View File

@ -52,12 +52,16 @@ namespace DirConfig
static const QString c_emptyHeaderName = "[EMPTY]";
enum class TextDecoration { None,
Bold,
Italic,
Underline,
Strikethrough,
InlineCode };
enum class TextDecoration
{
None,
Bold,
Italic,
Underline,
Strikethrough,
InlineCode,
CodeBlock
};
enum FindOption
{

View File

@ -463,6 +463,21 @@ void VMainWindow::initEditToolBar(QSize p_iconSize)
m_editToolBar->addAction(inlineCodeAct);
QAction *codeBlockAct = new QAction(QIcon(":/resources/icons/code_block.svg"),
tr("Code Block (Ctrl+M)"),
this);
codeBlockAct->setStatusTip(tr("Insert fenced code block text or wrap selected text into a fenced code block"));
connect(codeBlockAct, &QAction::triggered,
this, [this](){
if (m_curTab) {
m_curTab->decorateText(TextDecoration::CodeBlock);
}
});
m_editToolBar->addAction(codeBlockAct);
m_editToolBar->addSeparator();
// Insert link.
QAction *insetLinkAct = new QAction(QIcon(":/resources/icons/link.svg"),
tr("Insert Link (Ctrl+L)"), this);

View File

@ -289,6 +289,17 @@ bool VMdEditOperations::handleKeyPressEvent(QKeyEvent *p_event)
break;
}
case Qt::Key_M:
{
if (modifiers == Qt::ControlModifier) {
decorateCodeBlock();
p_event->accept();
ret = true;
}
break;
}
case Qt::Key_O:
{
if (modifiers == Qt::ControlModifier) {
@ -684,6 +695,10 @@ void VMdEditOperations::decorateText(TextDecoration p_decoration)
decorateInlineCode();
break;
case TextDecoration::CodeBlock:
decorateCodeBlock();
break;
default:
validDecoration = false;
qDebug() << "decoration" << (int)p_decoration << "is not implemented yet";
@ -807,6 +822,86 @@ void VMdEditOperations::decorateInlineCode()
m_editor->setTextCursor(cursor);
}
void VMdEditOperations::decorateCodeBlock()
{
const QString marker("```");
QTextCursor cursor = m_editor->textCursor();
cursor.beginEditBlock();
if (cursor.hasSelection()) {
// Insert ``` around the selected text.
int start = cursor.selectionStart();
int end = cursor.selectionEnd();
QString indentation = VEditUtils::fetchIndentSpaces(cursor.block());
// Insert the end marker first.
cursor.setPosition(end, QTextCursor::MoveAnchor);
VEditUtils::insertBlock(cursor, false);
VEditUtils::indentBlock(cursor, indentation);
cursor.insertText(marker);
// Insert the start marker.
cursor.setPosition(start, QTextCursor::MoveAnchor);
VEditUtils::insertBlock(cursor, true);
VEditUtils::indentBlock(cursor, indentation);
cursor.insertText(marker);
} else {
// Insert ``` ``` and place cursor after the first marker.
// Or if current block or next block is ```, we will skip it.
QTextBlock block = cursor.block();
int state = block.userState();
if (state == HighlightBlockState::CodeBlock
|| state == HighlightBlockState::CodeBlockStart
|| state == HighlightBlockState::CodeBlockEnd) {
// Find the block end.
while (block.isValid()) {
if (block.userState() == HighlightBlockState::CodeBlockEnd) {
break;
}
block = block.next();
}
if (block.isValid()) {
// It is CodeBlockEnd.
cursor.setPosition(block.position());
if (block.next().isValid()) {
cursor.movePosition(QTextCursor::NextBlock);
cursor.movePosition(QTextCursor::StartOfBlock);
} else {
cursor.movePosition(QTextCursor::EndOfBlock);
}
} else {
// Reach the end of the document.
cursor.movePosition(QTextCursor::End);
}
} else {
bool insertInline = false;
if (!cursor.atBlockEnd()) {
cursor.insertBlock();
cursor.movePosition(QTextCursor::PreviousBlock);
} else if (cursor.atBlockStart()) {
insertInline = true;
}
if (!insertInline) {
VEditUtils::insertBlock(cursor, false);
VEditUtils::indentBlockAsBlock(cursor, false);
}
cursor.insertText(marker);
VEditUtils::insertBlock(cursor, true);
VEditUtils::indentBlockAsBlock(cursor, true);
cursor.insertText(marker);
}
}
cursor.endEditBlock();
m_editor->setTextCursor(cursor);
}
void VMdEditOperations::decorateStrikethrough()
{
QTextCursor cursor = m_editor->textCursor();

View File

@ -72,6 +72,9 @@ private:
// Insert inline-code marker or set selected text inline-coded.
void decorateInlineCode();
// Insert inline-code marker or set selected text inline-coded.
void decorateCodeBlock();
// Insert strikethrough marker or set selected text strikethrough.
void decorateStrikethrough();

View File

@ -133,5 +133,6 @@
<file>resources/icons/compact_mode.svg</file>
<file>resources/icons/heading_sequence.svg</file>
<file>resources/icons/link.svg</file>
<file>resources/icons/code_block.svg</file>
</qresource>
</RCC>