Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions ReText/editor.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,16 @@ class ReTextEdit(QTextEdit):
orderedListPattern = re.compile("^([\\s]*)(\\d+)\\. $")
wordPattern = re.compile(r"\w+")
nonAlphaNumPattern = re.compile(r"\W")
surroundKeysSelfClose = [
Qt.Key_Underscore,
Qt.Key_Asterisk,
Qt.Key_QuoteDbl,
Qt.Key_Apostrophe
]
surroundKeysOtherClose = {
Qt.Key_ParenLeft: ')',
Qt.Key_BracketLeft: ']'
}

def __init__(self, parent):
QTextEdit.__init__(self)
Expand Down Expand Up @@ -234,6 +244,23 @@ def addNewWord(self, newword):
dictionary.add(newword)
self.tab.highlighter.rehighlightBlock(block)

def isSurroundKey(self, key):
return key in self.surroundKeysSelfClose or key in self.surroundKeysOtherClose

def getCloseKey(self, event, key):
if key in self.surroundKeysSelfClose:
return event.text()

if key in self.surroundKeysOtherClose:
return self.surroundKeysOtherClose[key]

def surroundText(self, cursor, event, key):
text = cursor.selectedText()
keyStr = event.text()
keyClose = self.getCloseKey(event, key)

cursor.insertText(keyStr + text + keyClose)

def keyPressEvent(self, event):
key = event.key()
cursor = self.textCursor()
Expand All @@ -259,6 +286,8 @@ def keyPressEvent(self, event):
# Insert Markdown-style line break
cursor.insertText(' ')
self.handleReturn(cursor)
elif cursor.selectedText() and self.isSurroundKey(key):
self.surroundText(cursor, event, key)
else:
if event.text() and self.tableModeEnabled:
cursor.beginEditBlock()
Expand Down
75 changes: 73 additions & 2 deletions tests/test_editor.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@

from ReText.editor import ReTextEdit
from ReText.editor import documentIndentMore, documentIndentLess
from PyQt5.QtGui import QImage, QTextCursor, QTextDocument
from PyQt5.QtCore import Qt, QMimeData
from PyQt5.QtGui import QImage, QTextCursor, QTextDocument, QKeyEvent
from PyQt5.QtCore import Qt, QMimeData, QEvent
from PyQt5.QtWidgets import QApplication
from markups import MarkdownMarkup, ReStructuredTextMarkup

Expand Down Expand Up @@ -135,5 +135,76 @@ def test_pasteImage_RestructuredText(self, _mock_image, _mock_editor):
self.editor.pasteImage()
self.assertTrue('.. image:: myimage.jpg' in self.editor.toPlainText())


class TestSurround(unittest.TestCase):

def setUp(self):
self.p = self
self.editor = ReTextEdit(self)
self.document = QTextDocument()
self.document.setPlainText('foo bar baz qux corge grault')
self.cursor = QTextCursor(self.document)

def getText(self, key):
if key == Qt.Key_ParenLeft: return '('
if key == Qt.Key_BracketLeft: return '['
if key == Qt.Key_Underscore: return '_'
if key == Qt.Key_Asterisk: return '*'
if key == Qt.Key_QuoteDbl: return '"'
if key == Qt.Key_Apostrophe: return '\''

def getEvent(self, key):
return QKeyEvent(QEvent.KeyPress, key, Qt.NoModifier, text=self.getText(key))

def test_isSurroundKey(self):
# close keys should not start a surrounding
self.assertFalse(self.editor.isSurroundKey(Qt.Key_ParenRight))
self.assertFalse(self.editor.isSurroundKey(Qt.Key_BracketRight))

self.assertTrue(self.editor.isSurroundKey(Qt.Key_ParenLeft))
self.assertTrue(self.editor.isSurroundKey(Qt.Key_BracketLeft))
self.assertTrue(self.editor.isSurroundKey(Qt.Key_Underscore))
self.assertTrue(self.editor.isSurroundKey(Qt.Key_Asterisk))
self.assertTrue(self.editor.isSurroundKey(Qt.Key_QuoteDbl))
self.assertTrue(self.editor.isSurroundKey(Qt.Key_Apostrophe))

def test_getCloseKey(self):
self.assertEqual(self.editor.getCloseKey(self.getEvent(Qt.Key_Underscore), Qt.Key_Underscore), '_')
self.assertEqual(self.editor.getCloseKey(self.getEvent(Qt.Key_Asterisk), Qt.Key_Asterisk), '*')
self.assertEqual(self.editor.getCloseKey(self.getEvent(Qt.Key_QuoteDbl), Qt.Key_QuoteDbl), '"')
self.assertEqual(self.editor.getCloseKey(self.getEvent(Qt.Key_Apostrophe), Qt.Key_Apostrophe), '\'')
self.assertEqual(self.editor.getCloseKey(self.getEvent(Qt.Key_ParenLeft), Qt.Key_ParenLeft), ')')
self.assertEqual(self.editor.getCloseKey(self.getEvent(Qt.Key_BracketLeft), Qt.Key_BracketLeft), ']')

def changeCursor(self, posI, posF):
self.cursor.setPosition(posI)
self.cursor.setPosition(posF, QTextCursor.KeepAnchor)

def test_surroundText(self):

self.changeCursor(0, 3)
self.editor.surroundText(self.cursor, self.getEvent(Qt.Key_Underscore), Qt.Key_Underscore)
self.assertEqual(self.document.toPlainText(), '_foo_ bar baz qux corge grault')

self.changeCursor(6, 9)
self.editor.surroundText(self.cursor, self.getEvent(Qt.Key_Asterisk), Qt.Key_Asterisk)
self.assertEqual(self.document.toPlainText(), '_foo_ *bar* baz qux corge grault')

self.changeCursor(12, 15)
self.editor.surroundText(self.cursor, self.getEvent(Qt.Key_QuoteDbl), Qt.Key_QuoteDbl)
self.assertEqual(self.document.toPlainText(), '_foo_ *bar* "baz" qux corge grault')

self.changeCursor(18, 21)
self.editor.surroundText(self.cursor, self.getEvent(Qt.Key_Apostrophe), Qt.Key_Apostrophe)
self.assertEqual(self.document.toPlainText(), '_foo_ *bar* "baz" \'qux\' corge grault')

self.changeCursor(24, 29)
self.editor.surroundText(self.cursor, self.getEvent(Qt.Key_ParenLeft), Qt.Key_ParenLeft)
self.assertEqual(self.document.toPlainText(), '_foo_ *bar* "baz" \'qux\' (corge) grault')

self.changeCursor(32, 38)
self.editor.surroundText(self.cursor, self.getEvent(Qt.Key_BracketLeft), Qt.Key_BracketLeft)
self.assertEqual(self.document.toPlainText(), '_foo_ *bar* "baz" \'qux\' (corge) [grault]')

if __name__ == '__main__':
unittest.main()