Browse Source

refactor edit operations

- Ctrl+B/I/K/D/M: Revert if content between the markers is empty.
- Ctrl+7: delete title mark.
Le Tan 8 years ago
parent
commit
1f0ee88770

+ 4 - 0
src/resources/docs/shortcuts_en.md

@@ -17,6 +17,8 @@ VNote supports `Ctrl+J` and `Ctrl+K` for navigation in the notebooks list, direc
 Scroll in all directions.
 - `Ctrl+Shift+T`  
 Recover last closed file.
+- `Ctrl+Alt+L`  
+Open Flash Page.
 
 ### Read Mode
 - `Ctrl+W`  
@@ -70,6 +72,8 @@ Delete all the characters from current cursor to the first space backward.
 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 exists.
+- `Ctrl+7`  
+Delete the title mark of current line or selected text.
 - `Tab`/`Shift+Tab`  
 Increase or decrease the indentation. If any text is selected, the indentation will operate on all these selected lines.
 - `Shift+Enter`  

+ 4 - 0
src/resources/docs/shortcuts_zh.md

@@ -17,6 +17,8 @@
 任意滚动。
 - `Ctrl+Shift+T`  
 恢复上一个关闭的文件。
+- `Ctrl+Alt+L`  
+打开灵犀页。
 
 ### 阅读模式
 - `Ctrl+W`  
@@ -70,6 +72,8 @@
 删除光标位置到行首的所有字符。
 - `Ctrl+<Num>`  
 插入级别为`<Num>`的标题。`<Num>`应该是1到6的一个数字。如果已经选择文本,则将当前选择文本改为标题。
+- `Ctrl+7`  
+删除当前行或所选择文本的标题标记。
 - `Tab`/`Shift+Tab`  
 增加或减小缩进。如果已经选择文本,则对所有选择的行进行缩进操作。
 - `Shift+Enter`  

+ 12 - 3
src/utils/veditutils.cpp

@@ -785,7 +785,7 @@ void VEditUtils::insertTitleMark(QTextCursor &p_cursor,
         return;
     }
 
-    Q_ASSERT(p_level >= 1 && p_level <= 6);
+    Q_ASSERT(p_level >= 0 && p_level <= 6);
 
     bool needInsert = true;
 
@@ -801,15 +801,24 @@ void VEditUtils::insertTitleMark(QTextCursor &p_cursor,
             needInsert = false;
         } else {
             // Remove the title mark.
+            int length = level;
+            if (p_level == 0) {
+                // Remove all the prefix.
+                QRegExp prefixReg(VUtils::c_headerPrefixRegExp);
+                bool preMatched = prefixReg.exactMatch(text);
+                Q_ASSERT(preMatched);
+                length = prefixReg.cap(1).length();
+            }
+
             p_cursor.movePosition(QTextCursor::NextCharacter,
                                   QTextCursor::KeepAnchor,
-                                  level);
+                                  length);
             p_cursor.removeSelectedText();
         }
     }
 
     // Insert titleMark + " " at the front of the block.
-    if (needInsert) {
+    if (p_level > 0 && needInsert) {
         // Remove the spaces at front.
         // insertText() will remove the selection.
         moveCursorFirstNonSpaceCharacter(p_cursor, QTextCursor::KeepAnchor);

+ 1 - 0
src/utils/veditutils.h

@@ -151,6 +151,7 @@ public:
     // Insert title Mark at level @p_level in front of block @p_block
     // If there already exists title marks, remove it first.
     // Move cursor at the end of the block after insertion.
+    // If @p_level is 0, remove the title mark.
     static void insertTitleMark(QTextCursor &p_cursor,
                                 const QTextBlock &p_block,
                                 int p_level);

+ 83 - 19
src/vmdeditoperations.cpp

@@ -219,10 +219,11 @@ bool VMdEditOperations::handleKeyPressEvent(QKeyEvent *p_event)
     case Qt::Key_4:
     case Qt::Key_5:
     case Qt::Key_6:
+    case Qt::Key_7:
     {
         if (modifiers == Qt::ControlModifier) {
             // Ctrl + <N>: insert title at level <N>.
-            if (insertTitle(key - Qt::Key_0)) {
+            if (insertTitle(key == Qt::Key_7 ? 0 : key - Qt::Key_0)) {
                 p_event->accept();
                 ret = true;
                 goto exit;
@@ -728,18 +729,32 @@ void VMdEditOperations::decorateBold()
         cursor.insertText("**");
     } else {
         // Insert **** and place cursor in the middle.
-        // Or if there are two * after current cursor, just skip them.
+        // Or if there are two * after current cursor, just skip them or delete
+        // them if four * appear.
         int pos = cursor.positionInBlock();
         bool hasStars = false;
+        bool emptyMarkers = false;
         QString text = cursor.block().text();
         if (pos <= text.size() - 2) {
             if (text[pos] == '*' && text[pos + 1] == '*') {
                 hasStars = true;
+
+                if (pos >= 2
+                    && text[pos - 1] == '*'
+                    && text[pos - 2] == '*') {
+                    emptyMarkers = true;
+                }
             }
         }
 
         if (hasStars) {
-            cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, 2);
+            if (emptyMarkers) {
+                cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::MoveAnchor, 2);
+                cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, 4);
+                cursor.removeSelectedText();
+            } else {
+                cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, 2);
+            }
         } else {
             cursor.insertText("****");
             cursor.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor, 2);
@@ -765,18 +780,31 @@ void VMdEditOperations::decorateItalic()
         cursor.insertText("*");
     } else {
         // Insert ** and place cursor in the middle.
-        // Or if there are one * after current cursor, just skip it.
+        // Or if there are one * after current cursor, just skip them or delete
+        // them if two * appear.
         int pos = cursor.positionInBlock();
         bool hasStar = false;
+        bool emptyMarkers = false;
         QString text = cursor.block().text();
         if (pos <= text.size() - 1) {
-            if (text[pos] == '*') {
+            if (text[pos] == '*'
+                && (pos == text.size() - 1 || text[pos + 1] != '*')) {
                 hasStar = true;
+
+                if (pos >= 1 && text[pos - 1] == '*') {
+                    emptyMarkers = true;
+                }
             }
         }
 
         if (hasStar) {
-            cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, 1);
+            if (emptyMarkers) {
+                cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::MoveAnchor, 1);
+                cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, 2);
+                cursor.removeSelectedText();
+            } else {
+                cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, 1);
+            }
         } else {
             cursor.insertText("**");
             cursor.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor, 1);
@@ -802,18 +830,30 @@ void VMdEditOperations::decorateInlineCode()
         cursor.insertText("`");
     } else {
         // Insert `` and place cursor in the middle.
-        // Or if there are one ` after current cursor, just skip it.
+        // Or if there are one ` after current cursor, just skip them or delete
+        // them if two ` appear.
         int pos = cursor.positionInBlock();
         bool hasBackquote = false;
+        bool emptyMarkers = false;
         QString text = cursor.block().text();
         if (pos <= text.size() - 1) {
             if (text[pos] == '`') {
                 hasBackquote = true;
+
+                if (pos >= 1 && text[pos - 1] == '`') {
+                    emptyMarkers = true;
+                }
             }
         }
 
         if (hasBackquote) {
-            cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, 1);
+            if (emptyMarkers) {
+                cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::MoveAnchor, 1);
+                cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, 2);
+                cursor.removeSelectedText();
+            } else {
+                cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, 1);
+            }
         } else {
             cursor.insertText("``");
             cursor.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor, 1);
@@ -857,22 +897,32 @@ void VMdEditOperations::decorateCodeBlock()
             || state == HighlightBlockState::CodeBlockStart
             || state == HighlightBlockState::CodeBlockEnd) {
             // Find the block end.
-            while (block.isValid()) {
-                if (block.userState() == HighlightBlockState::CodeBlockEnd) {
+            QTextBlock endBlock = block;
+            while (endBlock.isValid()) {
+                if (endBlock.userState() == HighlightBlockState::CodeBlockEnd) {
                     break;
                 }
 
-                block = block.next();
+                endBlock = endBlock.next();
             }
 
-            if (block.isValid()) {
+            if (endBlock.isValid()) {
                 // It is CodeBlockEnd.
-                cursor.setPosition(block.position());
-                if (block.next().isValid()) {
-                    cursor.movePosition(QTextCursor::NextBlock);
-                    cursor.movePosition(QTextCursor::StartOfBlock);
+                if (endBlock.previous().isValid()
+                    && endBlock.previous().userState() == HighlightBlockState::CodeBlockStart) {
+                    // Delete empty code blocks.
+                    cursor.setPosition(endBlock.previous().position());
+                    cursor.movePosition(QTextCursor::NextBlock, QTextCursor::KeepAnchor);
+                    cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
+                    cursor.removeSelectedText();
                 } else {
-                    cursor.movePosition(QTextCursor::EndOfBlock);
+                    cursor.setPosition(endBlock.position());
+                    if (endBlock.next().isValid()) {
+                        cursor.movePosition(QTextCursor::NextBlock);
+                        cursor.movePosition(QTextCursor::StartOfBlock);
+                    } else {
+                        cursor.movePosition(QTextCursor::EndOfBlock);
+                    }
                 }
             } else {
                 // Reach the end of the document.
@@ -919,18 +969,32 @@ void VMdEditOperations::decorateStrikethrough()
         cursor.insertText("~~");
     } else {
         // Insert ~~~~ and place cursor in the middle.
-        // Or if there are one ~~ after current cursor, just skip it.
+        // Or if there are one ~~ after current cursor, just skip it or delete
+        // it if for ~ appear.
         int pos = cursor.positionInBlock();
         bool hasStrikethrough = false;
+        bool emptyMarkers = false;
         QString text = cursor.block().text();
         if (pos <= text.size() - 2) {
             if (text[pos] == '~' && text[pos + 1] == '~') {
                 hasStrikethrough = true;
+
+                if (pos >= 2
+                    && text[pos - 1] == '~'
+                    && text[pos - 2] == '~') {
+                    emptyMarkers = true;
+                }
             }
         }
 
         if (hasStrikethrough) {
-            cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, 2);
+            if (emptyMarkers) {
+                cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::MoveAnchor, 2);
+                cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, 4);
+                cursor.removeSelectedText();
+            } else {
+                cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, 2);
+            }
         } else {
             cursor.insertText("~~~~");
             cursor.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor, 2);

+ 1 - 0
src/vmdeditoperations.h

@@ -58,6 +58,7 @@ private:
     // Insert title of level @p_level.
     // Will detect if current block already has some leading #s. If yes,
     // will delete it and insert the correct #s.
+    // @p_level: 0 to cancel title.
     bool insertTitle(int p_level);
 
     // Change the sequence number of a list block.