Add a custom text document in the editor

This commit is contained in:
Veronica Berglyd Olsen
2023-09-07 19:20:00 +02:00
parent e08790e889
commit 16e2e0cc83
6 changed files with 154 additions and 95 deletions
+48 -51
View File
@@ -56,6 +56,7 @@ from novelwriter.common import minmax, transferCase
from novelwriter.constants import nwKeyWords, nwUnicode
from novelwriter.core.index import countWords
from novelwriter.gui.dochighlight import GuiDocHighlighter
from novelwriter.gui.editordocument import GuiTextDocument
from novelwriter.extensions.wheeleventfilter import WheelEventFilter
if TYPE_CHECKING: # pragma: no cover
@@ -120,9 +121,12 @@ class GuiDocEditor(QPlainTextEdit):
self._typPadBefore = ""
self._typPadAfter = ""
# Core Elements and Signals
qDoc = self.document()
qDoc.contentsChange.connect(self._docChange)
# Create Custom Document
self._qDocument = GuiTextDocument(self)
self.setDocument(self._qDocument)
# Connect Signals
self._qDocument.contentsChange.connect(self._docChange)
self.selectionChanged.connect(self._updateSelectedStatus)
# Document Title
@@ -130,9 +134,6 @@ class GuiDocEditor(QPlainTextEdit):
self.docFooter = GuiDocEditFooter(self)
self.docSearch = GuiDocEditSearch(self)
# Syntax
self.highLight = GuiDocHighlighter(qDoc)
# Context Menu
self.setContextMenuPolicy(Qt.CustomContextMenu)
self.customContextMenuRequested.connect(self._openContextMenu)
@@ -212,7 +213,7 @@ class GuiDocEditor(QPlainTextEdit):
@property
def isEmpty(self) -> bool:
"""Check if the current document is empty."""
return self.document().isEmpty()
return self._qDocument.isEmpty()
##
# Methods
@@ -266,7 +267,7 @@ class GuiDocEditor(QPlainTextEdit):
self.docHeader.matchColours()
self.docFooter.matchColours()
self.highLight.initHighlighter()
self._qDocument.syntaxHighlighter.initHighlighter()
return
@@ -312,8 +313,7 @@ class GuiDocEditor(QPlainTextEdit):
# Due to cursor visibility, a part of the margin must be
# allocated to the document itself. See issue #1112.
cW = 2*self.cursorWidth()
qDoc = self.document()
qDoc.setDocumentMargin(cW)
self._qDocument.setDocumentMargin(cW)
self._vpMargin = max(CONFIG.getTextMargin() - cW, 0)
self.setViewportMargins(self._vpMargin, self._vpMargin, self._vpMargin, self._vpMargin)
@@ -327,7 +327,7 @@ class GuiDocEditor(QPlainTextEdit):
if CONFIG.showLineEndings:
theOpt.setFlags(theOpt.flags() | QTextOption.ShowLineAndParagraphSeparators)
qDoc.setDefaultTextOption(theOpt)
self._qDocument.setDefaultTextOption(theOpt)
# Scroll bars
if CONFIG.hideVScroll:
@@ -377,13 +377,12 @@ class GuiDocEditor(QPlainTextEdit):
qApp.setOverrideCursor(QCursor(Qt.WaitCursor))
self._docHandle = tHandle
self.highLight.setHandle(tHandle)
tStart = time()
self._allowAutoReplace(False)
self.setPlainText(docText)
self._qDocument.setTextContent(docText, tHandle)
self._allowAutoReplace(True)
logger.debug("Document text loaded in %.3f ms", 1000*(time() - tStart))
logger.debug("Document text set in %.3f ms", 1000*(time() - tStart))
qApp.processEvents()
self._lastEdit = time()
@@ -404,14 +403,15 @@ class GuiDocEditor(QPlainTextEdit):
self.docFooter.updateLineCount()
# This is a hack to fix invisible cursor on an empty document
if self.document().characterCount() <= 1:
if self._qDocument.characterCount() <= 1:
self.setPlainText("\n")
self.setPlainText("")
self.setCursorPosition(0)
qApp.processEvents()
self.document().clearUndoRedoStacks()
self.setDocumentChanged(False)
self._qDocument.clearUndoRedoStacks()
qApp.restoreOverrideCursor()
# Update the status bar
@@ -422,12 +422,12 @@ class GuiDocEditor(QPlainTextEdit):
def updateTagHighLighting(self) -> None:
"""Rerun the syntax highlighter on all meta data lines."""
self.highLight.rehighlightByType(GuiDocHighlighter.BLOCK_META)
self._qDocument.syntaxHighlighter.rehighlightByType(GuiDocHighlighter.BLOCK_META)
return
def redrawText(self) -> None:
"""Redraw the text by marking the document content as dirty."""
self.document().markContentsDirty(0, self.document().characterCount())
self._qDocument.markContentsDirty(0, self._qDocument.characterCount())
self.updateDocMargins()
return
@@ -561,7 +561,7 @@ class GuiDocEditor(QPlainTextEdit):
paragraph and line separators though.
See: https://doc.qt.io/qt-5/qtextdocument.html#toPlainText
"""
text = self.document().toRawText()
text = self._qDocument.toRawText()
text = text.replace(nwUnicode.U_LSEP, "\n") # Line separators
text = text.replace(nwUnicode.U_PSEP, "\n") # Paragraph separators
return text
@@ -586,7 +586,7 @@ class GuiDocEditor(QPlainTextEdit):
def setCursorPosition(self, position: int) -> None:
"""Move the cursor to a given position in the document."""
nChars = self.document().characterCount()
nChars = self._qDocument.characterCount()
if nChars > 1 and isinstance(position, int):
cursor = self.textCursor()
cursor.setPosition(minmax(position, 0, nChars-1))
@@ -605,7 +605,7 @@ class GuiDocEditor(QPlainTextEdit):
def setCursorLine(self, line: int | None) -> None:
"""Move the cursor to a given line in the document."""
if isinstance(line, int) and line > 0:
block = self.document().findBlockByNumber(line - 1)
block = self._qDocument.findBlockByNumber(line - 1)
if block:
self.setCursorPosition(block.position())
logger.debug("Cursor moved to line %d", line)
@@ -638,7 +638,7 @@ class GuiDocEditor(QPlainTextEdit):
self._spellCheck = state
self.mainGui.mainMenu.setSpellCheck(state)
SHARED.project.data.setSpellCheck(state)
self.highLight.setSpellCheck(state)
self._qDocument.syntaxHighlighter.setSpellCheck(state)
if state is False:
self.spellCheckDocument()
@@ -655,7 +655,7 @@ class GuiDocEditor(QPlainTextEdit):
logger.debug("Running spell checker")
start = time()
qApp.setOverrideCursor(QCursor(Qt.WaitCursor))
self.highLight.rehighlight()
self._qDocument.syntaxHighlighter.rehighlight()
qApp.restoreOverrideCursor()
logger.debug("Document highlighted in %.3f ms", 1000*(time() - start))
self.statusMessage.emit(self.tr("Spell check complete"))
@@ -994,7 +994,7 @@ class GuiDocEditor(QPlainTextEdit):
self.wcTimerDoc.start()
if self._doReplace and added == 1:
self._docAutoReplace(self.document().findBlock(pos))
self._docAutoReplace(self._qDocument.findBlock(pos))
return
@@ -1122,7 +1122,7 @@ class GuiDocEditor(QPlainTextEdit):
theWord = cursor.selectedText().strip().strip(self._nonWord)
logger.debug("Added '%s' to project dictionary", theWord)
SHARED.spelling.addWord(theWord)
self.highLight.rehighlightBlock(cursor.block())
self._qDocument.syntaxHighlighter.rehighlightBlock(cursor.block())
return
@pyqtSlot()
@@ -1418,8 +1418,8 @@ class GuiDocEditor(QPlainTextEdit):
posS = theCursor.selectionStart()
posE = theCursor.selectionEnd()
blockS = self.document().findBlock(posS)
blockE = self.document().findBlock(posE)
blockS = self._qDocument.findBlock(posS)
blockE = self._qDocument.findBlock(posE)
if blockS != blockE:
posE = blockS.position() + blockS.length() - 1
@@ -1430,14 +1430,14 @@ class GuiDocEditor(QPlainTextEdit):
numB = 0
for n in range(fLen):
if self.document().characterAt(posS-n-1) == fChar:
if self._qDocument.characterAt(posS-n-1) == fChar:
numB += 1
else:
break
numA = 0
for n in range(fLen):
if self.document().characterAt(posE+n) == fChar:
if self._qDocument.characterAt(posE+n) == fChar:
numA += 1
else:
break
@@ -1487,9 +1487,8 @@ class GuiDocEditor(QPlainTextEdit):
posS = theCursor.selectionStart()
posE = theCursor.selectionEnd()
qDoc = self.document()
blockS = qDoc.findBlock(posS)
blockE = qDoc.findBlock(posE)
blockS = self._qDocument.findBlock(posS)
blockE = self._qDocument.findBlock(posE)
if blockS != blockE:
posE = blockS.position() + blockS.length() - 1
@@ -1690,16 +1689,15 @@ class GuiDocEditor(QPlainTextEdit):
def _removeInParLineBreaks(self) -> None:
"""Strip line breaks within paragraphs in the selected text."""
theCursor = self.textCursor()
theDoc = self.document()
cursor = self.textCursor()
iS = 0
iE = theDoc.blockCount() - 1
iE = self._qDocument.blockCount() - 1
rS = 0
rE = theDoc.characterCount()
if theCursor.hasSelection():
sBlock = theDoc.findBlock(theCursor.selectionStart())
eBlock = theDoc.findBlock(theCursor.selectionEnd())
rE = self._qDocument.characterCount()
if cursor.hasSelection():
sBlock = self._qDocument.findBlock(cursor.selectionStart())
eBlock = self._qDocument.findBlock(cursor.selectionEnd())
iS = sBlock.blockNumber()
iE = eBlock.blockNumber()
rS = sBlock.position()
@@ -1709,7 +1707,7 @@ class GuiDocEditor(QPlainTextEdit):
currPar = []
cleanText = ""
for i in range(iS, iE+1):
cBlock = theDoc.findBlockByNumber(i)
cBlock = self._qDocument.findBlockByNumber(i)
cText = cBlock.text()
if cText.strip() == "":
if currPar:
@@ -1726,12 +1724,12 @@ class GuiDocEditor(QPlainTextEdit):
cleanText += " ".join(currPar) + "\n\n"
# Replace the text with the cleaned up text
theCursor.beginEditBlock()
theCursor.clearSelection()
theCursor.setPosition(rS)
theCursor.movePosition(QTextCursor.Right, QTextCursor.KeepAnchor, rE-rS)
theCursor.insertText(cleanText.rstrip() + "\n")
theCursor.endEditBlock()
cursor.beginEditBlock()
cursor.clearSelection()
cursor.setPosition(rS)
cursor.movePosition(QTextCursor.Right, QTextCursor.KeepAnchor, rE-rS)
cursor.insertText(cleanText.rstrip() + "\n")
cursor.endEditBlock()
return
@@ -1913,11 +1911,10 @@ class GuiDocEditor(QPlainTextEdit):
# Underscore counts as a part of the word, so check that the
# selection isn't wrapped in italics markers.
reSelect = False
qDoc = self.document()
if qDoc.characterAt(posS) == "_":
if self._qDocument.characterAt(posS) == "_":
posS += 1
reSelect = True
if qDoc.characterAt(posE) == "_":
if self._qDocument.characterAt(posE) == "_":
posE -= 1
reSelect = True
if reSelect:
@@ -2838,7 +2835,7 @@ class GuiDocEditFooter(QWidget):
else:
theCursor = self.docEditor.textCursor()
iLine = theCursor.blockNumber() + 1
iDist = 100*iLine/self.docEditor.document().blockCount()
iDist = 100*iLine/self.docEditor._qDocument.blockCount()
self.linesText.setText(
self.tr("Line: {0} ({1})").format(f"{iLine:n}", f"{iDist:.0f} %")
)
@@ -2869,7 +2866,7 @@ class GuiDocEditFooter(QWidget):
self.tr("Words: {0} ({1})").format(f"{wCount:n}", f"{wDiff:+n}")
)
byteSize = self.docEditor.document().characterCount()
byteSize = self.docEditor._qDocument.characterCount()
self.wordsText.setToolTip(
self.tr("Document size is {0} bytes").format(f"{byteSize:n}")
)