以Vim的操作之高效被稱(chēng)為編輯器之神肛响,是絕對(duì)不夸張的岭粤。小編遇上大規(guī)模修改代碼,一直靠Vim臨時(shí)寫(xiě)個(gè)宏操作特笋,一路確認(rèn)下去幾分鐘就能搞定剃浇。換個(gè)編輯器就很難這樣操作了。
但vim終究是老了猎物,界面弄的再漂亮虎囚,總要開(kāi)個(gè)命令行才行。又不支持圖片蔫磨,處理有格式的文本更是力不從心淘讥。雖然后起之輩很多,但總歸沒(méi)有vim編輯模式下移動(dòng)那么方便堤如。
用了PyQt里的QTextEditor后蒲列,發(fā)現(xiàn)模擬個(gè)Vim真的很方便。Vim與其它文本編輯器最大的區(qū)別是模式煤惩。通過(guò)Esc鍵切換到編輯模式嫉嘀,就可以快速修改文本。QTextEditor有只讀模式魄揉,但是光標(biāo)會(huì)消失剪侮。那只好自定義一個(gè)editing布爾類(lèi)型變量來(lái)切換模式。
首先定義一個(gè)QTextEditor的子類(lèi),然后在keyReleaseEvent方法里判斷按下的鍵瓣俯,如果是Esc, 就設(shè)置editing變量為真杰标。
def keyReleaseEvent(self, event):
if event.key() == Qt.Key_Escape:
self.editing = True
當(dāng)進(jìn)入到編輯模式后,輸入字母鍵就不應(yīng)該有回應(yīng)了彩匕,除非是hjkl這樣的光標(biāo)操作鍵腔剂。QTextEditor的textCursor屬性就是用來(lái)操作光標(biāo)的。向下移動(dòng)光標(biāo)的實(shí)現(xiàn)代碼如下
def moveDown(self, cursor=None):
if cursor == None:
cursor = self.textCursor()
cursor.movePosition(QTextCursor.Up, QTextCursor.MoveAnchor, 1)
self.setTextCursor(cursor)
return cursor
同理驼仪,只需要替換QTextCursor.Up為QTextCursor.Down, QTextCursor.Left, QTextCursor.Right 就可以實(shí)現(xiàn)hjkl鍵的功能
def keyPressEvent(self, event):
if self.editing:
if event.key() == Qt.Key_K:
self.moveUp()
elif event.key() == Qt.Key_J:
self.moveDown()
elif event.key() == Qt.Key_H:
self.moveLeft()
elif event.key() == Qt.Key_L:
self.moveRight()
elif event.key() == Qt.Key_I:
self.editing = False
elif event.key() == Qt.Key_0:
self.moveStartOfLine()
elif event.key() == Qt.Key_Dollar:
self.moveEndOfLine()
elif event.key() == Qt.Key_A:
self.moveEndOfLine()
self.editing = False
else:
super().keyPressEvent(event)
else:
super().keyPressEvent(event)
上面的代碼還實(shí)現(xiàn)移動(dòng)到行首掸犬,行尾以及添加和功能,一樣可以用QTextCursor.StartOfLine绪爸, QTextCursor.EndOfLine 來(lái)實(shí)現(xiàn)湾碎。有的同學(xué)可能會(huì)奇怪為什么我們要用單獨(dú)的方法,而不直接在keyPressEvent實(shí)現(xiàn)奠货。這其實(shí)是一個(gè)簡(jiǎn)單的封裝來(lái)隱藏Qt API介褥。 如果將來(lái)要實(shí)現(xiàn)數(shù)字鍵加操作符或者解析vim腳本操作的話,大量調(diào)用Qt API就容易亂递惋,不容易維護(hù)柔滔。而實(shí)現(xiàn)一套我們自己的API更好理解一些。
最后我們來(lái)實(shí)現(xiàn)一個(gè)markdown的功能萍虽,并且實(shí)時(shí)渲染睛廊。 這是vim或者neovim絕對(duì)做不到的。
def keyReleaseEvent(self, event):
if event.key() == Qt.Key_Return:
text = self.toPlainText().splitlines()
last_line = text[-1]
print(last_line)
if last_line.startswith('#'):
self.moveUp()
self.deleteLine()
self.moveDown()
self.deleteLine()
self.textCursor().insertFragment(QTextDocumentFragment.fromHtml(f"<h1>{last_line}</h1>"))
self.appendPlainText("")
def deleteLine(self):
self.moveStartOfLine()
cursor = self.textCursor()
cursor.select(QTextCursor.LineUnderCursor)
cursor.removeSelectedText()
cursor.deletePreviousChar()
cursor.deletePreviousChar()
self.setTextCursor(cursor)
如果最后一行也就是最新輸入的一行以#開(kāi)頭的話贩挣,就代表是markdown里的H1, 我們刪除最后一行以及回車(chē)鍵產(chǎn)生的空白行喉前,再加上一行html代碼。同時(shí)增加一行(因?yàn)榘戳嘶剀?chē)鍵)
其實(shí)這樣的代碼跟vim腳本沒(méi)有太大區(qū)別王财,當(dāng)我們將來(lái)實(shí)現(xiàn)腳本解析引擎時(shí),就可以按操作符替換為對(duì)應(yīng)的方法名裕便。