Make some improvements to the doc editor code

This commit is contained in:
Veronica Berglyd Olsen
2025-01-22 00:35:01 +01:00
parent 3672ae8be6
commit 5b840ada83
+50 -80
View File
@@ -219,7 +219,7 @@ class GuiDocEditor(QPlainTextEdit):
self.changeFocusState = self.docHeader.changeFocusState
# Finalise
self.updateSyntaxColours()
self.updateSyntaxColors()
self.initEditor()
logger.debug("Ready: GuiDocEditor")
@@ -287,7 +287,7 @@ class GuiDocEditor(QPlainTextEdit):
self.docToolBar.updateTheme()
return
def updateSyntaxColours(self) -> None:
def updateSyntaxColors(self) -> None:
"""Update the syntax highlighting theme."""
syntax = SHARED.theme.syntaxTheme
@@ -304,7 +304,7 @@ class GuiDocEditor(QPlainTextEdit):
viewport.setPalette(palette)
self.docHeader.matchColours()
self.docFooter.matchColours()
self.docFooter.matchColors()
return
@@ -339,14 +339,12 @@ class GuiDocEditor(QPlainTextEdit):
# Also set the document text options for the document text flow
options = QTextOption()
if CONFIG.doJustify:
options.setAlignment(QtAlignJustify)
if CONFIG.showTabsNSpaces:
options.setFlags(options.flags() | QTextOption.Flag.ShowTabsAndSpaces)
if CONFIG.showLineEndings:
options.setFlags(options.flags() | QTextOption.Flag.ShowLineAndParagraphSeparators)
self._qDocument.setDefaultTextOption(options)
# Scrolling
@@ -379,19 +377,19 @@ class GuiDocEditor(QPlainTextEdit):
"""Load text from a document into the editor. If we have an I/O
error, we must handle this and clear the editor so that we don't
risk overwriting the file if it exists. This can for instance
happen of the file contains binary elements or an encoding that
happen if the file contains binary elements or an encoding that
novelWriter does not support. If loading is successful, or the
document is new (empty string), we set up the editor for editing
the file.
"""
self._nwDocument = SHARED.project.storage.getDocument(tHandle)
self._nwItem = self._nwDocument.nwItem
if not ((nwItem := self._nwItem) and nwItem.itemType == nwItemType.FILE):
if not (self._nwItem and self._nwItem.itemType == nwItemType.FILE):
logger.debug("Requested item '%s' is not a document", tHandle)
self.clearEditor()
return False
if (docText := self._nwDocument.readDocument()) is None:
if (text := self._nwDocument.readDocument()) is None:
# There was an I/O error
self.clearEditor()
return False
@@ -400,7 +398,7 @@ class GuiDocEditor(QPlainTextEdit):
self._docHandle = tHandle
self._allowAutoReplace(False)
self._qDocument.setTextContent(docText, tHandle)
self._qDocument.setTextContent(text, tHandle)
self._allowAutoReplace(True)
QApplication.processEvents()
@@ -415,7 +413,7 @@ class GuiDocEditor(QPlainTextEdit):
if isinstance(tLine, int):
self.setCursorLine(tLine)
else:
self.setCursorPosition(nwItem.cursorPos)
self.setCursorPosition(self._nwItem.cursorPos)
self.docHeader.setHandle(tHandle)
self.docFooter.setHandle(tHandle)
@@ -438,7 +436,7 @@ class GuiDocEditor(QPlainTextEdit):
# Finalise
QApplication.restoreOverrideCursor()
self.updateStatusMessage.emit(
self.tr("Opened Document: {0}").format(nwItem.itemName)
self.tr("Opened Document: {0}").format(self._nwItem.itemName)
)
return True
@@ -469,19 +467,17 @@ class GuiDocEditor(QPlainTextEdit):
)
return False
docText = self.getText()
cC, wC, pC = standardCounter(docText)
text = self.getText()
cC, wC, pC = standardCounter(text)
self._updateDocCounts(cC, wC, pC)
if not self._nwDocument.writeDocument(docText):
if not self._nwDocument.writeDocument(text):
saveOk = False
if self._nwDocument.hashError:
msgYes = SHARED.question(self.tr(
"This document has been changed outside of novelWriter "
"while it was open. Overwrite the file on disk?"
))
if msgYes:
saveOk = self._nwDocument.writeDocument(docText, forceWrite=True)
if self._nwDocument.hashError and SHARED.question(self.tr(
"This document has been changed outside of novelWriter "
"while it was open. Overwrite the file on disk?"
)):
saveOk = self._nwDocument.writeDocument(text, forceWrite=True)
if not saveOk:
SHARED.error(
@@ -494,10 +490,8 @@ class GuiDocEditor(QPlainTextEdit):
self.setDocumentChanged(False)
self.docTextChanged.emit(self._docHandle, self._lastEdit)
oldHeader = self._nwItem.mainHeading
oldCount = SHARED.project.index.getHandleHeaderCount(tHandle)
SHARED.project.index.scanText(tHandle, docText)
newHeader = self._nwItem.mainHeading
SHARED.project.index.scanText(tHandle, text)
newCount = SHARED.project.index.getHandleHeaderCount(tHandle)
if self._nwItem.itemClass == nwItemClass.NOVEL:
@@ -506,9 +500,6 @@ class GuiDocEditor(QPlainTextEdit):
else:
self.novelStructureChanged.emit()
if oldHeader != newHeader:
self.docFooter.updateInfo()
# Update the status bar
self.updateStatusMessage.emit(self.tr("Saved Document: {0}").format(self._nwItem.itemName))
@@ -589,7 +580,7 @@ class GuiDocEditor(QPlainTextEdit):
QTextDocument->toRawText instead of toPlainText. The former preserves
non-breaking spaces, the latter does not. We still want to get rid of
paragraph and line separators though.
See: https://doc.qt.io/qt-5/qtextdocument.html#toPlainText
See: https://doc.qt.io/qt-6/qtextdocument.html#toPlainText
"""
text = self._qDocument.toRawText()
text = text.replace(nwUnicode.U_LSEP, "\n") # Line separators
@@ -627,10 +618,9 @@ class GuiDocEditor(QPlainTextEdit):
def setCursorPosition(self, position: int) -> None:
"""Move the cursor to a given position in the document."""
nChars = self._qDocument.characterCount()
if nChars > 1 and isinstance(position, int):
if (chars := self._qDocument.characterCount()) > 1 and isinstance(position, int):
cursor = self.textCursor()
cursor.setPosition(minmax(position, 0, nChars-1))
cursor.setPosition(minmax(position, 0, chars-1))
self.setTextCursor(cursor)
self.centerCursor()
return
@@ -693,12 +683,10 @@ class GuiDocEditor(QPlainTextEdit):
def spellCheckDocument(self) -> None:
"""Rerun the highlighter to update spell checking status of the
currently loaded text. The fastest way to do this, at least as
of Qt 5.13, is to clear the text and put it back. This clears
the undo stack, so we only do it for big documents.
currently loaded text.
"""
logger.debug("Running spell checker")
start = time()
logger.debug("Running spell checker")
QApplication.setOverrideCursor(QCursor(Qt.CursorShape.WaitCursor))
self._qDocument.syntaxHighlighter.rehighlight()
QApplication.restoreOverrideCursor()
@@ -939,9 +927,7 @@ class GuiDocEditor(QPlainTextEdit):
* We also handle automatic scrolling here.
"""
self._lastActive = time()
isReturn = event.key() == Qt.Key.Key_Return
isReturn |= event.key() == Qt.Key.Key_Enter
if isReturn and self.docSearch.anyFocus():
if self.docSearch.anyFocus() and event.key() in (Qt.Key.Key_Return, Qt.Key.Key_Enter):
return
elif event == QKeySequence.StandardKey.Redo:
self.docAction(nwDocAction.REDO)
@@ -1094,7 +1080,7 @@ class GuiDocEditor(QPlainTextEdit):
if (block := self._qDocument.findBlock(pos)).isValid():
text = block.text()
if text.startswith("@") and added + removed == 1:
# Only run on single keypresses, otherwise it will trigger
# Only run on single character changes, or it will trigger
# at unwanted times when other changes are made to the document
cursor = self.textCursor()
bPos = cursor.positionInBlock()
@@ -1106,10 +1092,10 @@ class GuiDocEditor(QPlainTextEdit):
else:
self._completer.setVisible(False)
if self._doReplace and added == 1:
cursor = self.textCursor()
if self._autoReplace.process(text, cursor):
self._qDocument.syntaxHighlighter.rehighlightBlock(cursor.block())
if self._doReplace and added == 1:
cursor = self.textCursor()
if self._autoReplace.process(text, cursor):
self._qDocument.syntaxHighlighter.rehighlightBlock(cursor.block())
return
@@ -1123,11 +1109,10 @@ class GuiDocEditor(QPlainTextEdit):
def _insertCompletion(self, pos: int, length: int, text: str) -> None:
"""Insert choice from the completer menu."""
cursor = self.textCursor()
block = cursor.block()
if block.isValid():
pos += block.position()
cursor.setPosition(pos, QtMoveAnchor)
cursor.setPosition(pos + length, QtKeepAnchor)
if (block := cursor.block()).isValid():
check = pos + block.position()
cursor.setPosition(check, QtMoveAnchor)
cursor.setPosition(check + length, QtKeepAnchor)
cursor.insertText(text)
self._completer.hide()
return
@@ -1220,7 +1205,8 @@ class GuiDocEditor(QPlainTextEdit):
# Execute the context menu
if viewport := self.viewport():
ctxMenu.exec(viewport.mapToGlobal(pos))
ctxMenu.deleteLater()
ctxMenu.setParent(None)
return
@@ -1277,15 +1263,11 @@ class GuiDocEditor(QPlainTextEdit):
@pyqtSlot()
def _runSelCounter(self) -> None:
"""Update the selection word count."""
if self._docHandle is None:
return
if self._wCounterSel.isRunning():
logger.debug("Selection word counter is busy")
return
SHARED.runInThreadPool(self._wCounterSel)
if self._docHandle:
if self._wCounterSel.isRunning():
logger.debug("Selection word counter is busy")
return
SHARED.runInThreadPool(self._wCounterSel)
return
@pyqtSlot(int, int, int)
@@ -1565,8 +1547,10 @@ class GuiDocEditor(QPlainTextEdit):
return
def _wrapSelection(self, before: str, after: str | None = None, pos: int | None = None,
select: _SelectAction = _SelectAction.NO_DECISION) -> None:
def _wrapSelection(
self, before: str, after: str | None = None, pos: int | None = None,
select: _SelectAction = _SelectAction.NO_DECISION
) -> None:
"""Wrap the selected text in whatever is in tBefore and tAfter.
If there is no selection, the autoSelect setting decides the
action. AutoSelect will select the word under the cursor before
@@ -1928,8 +1912,9 @@ class GuiDocEditor(QPlainTextEdit):
self._qDocument.syntaxHighlighter.rehighlightBlock(block)
return
def _processTag(self, cursor: QTextCursor | None = None,
follow: bool = True, create: bool = False) -> nwTrinary:
def _processTag(
self, cursor: QTextCursor | None = None, follow: bool = True, create: bool = False
) -> nwTrinary:
"""Activated by Ctrl+Enter. Checks that we're in a block
starting with '@'. We then find the tag under the cursor and
check that it is not the tag itself. If all this is fine, we
@@ -2163,13 +2148,10 @@ class BackgroundWordCounter(QRunnable):
def __init__(self, docEditor: GuiDocEditor, forSelection: bool = False) -> None:
super().__init__()
self._docEditor = docEditor
self._forSelection = forSelection
self._isRunning = False
self.signals = BackgroundWordCounterSignals()
return
def isRunning(self) -> bool:
@@ -3079,42 +3061,30 @@ class GuiDocEditFooter(QWidget):
# Status
self.statusIcon = QLabel("", self)
self.statusIcon.setContentsMargins(0, 0, 0, 0)
self.statusIcon.setFixedHeight(iPx)
self.statusIcon.setAlignment(QtAlignLeftTop)
self.statusText = QLabel(self.tr("Status"), self)
self.statusText.setIndent(0)
self.statusText.setMargin(0)
self.statusText.setContentsMargins(0, 0, 0, 0)
self.statusText = QLabel("", self)
self.statusText.setAutoFillBackground(True)
self.statusText.setFixedHeight(fPx)
self.statusText.setAlignment(QtAlignLeftTop)
# Lines
self.linesIcon = QLabel("", self)
self.linesIcon.setContentsMargins(0, 0, 0, 0)
self.linesIcon.setFixedHeight(iPx)
self.linesIcon.setAlignment(QtAlignLeftTop)
self.linesText = QLabel("", self)
self.linesText.setIndent(0)
self.linesText.setMargin(0)
self.linesText.setContentsMargins(0, 0, 0, 0)
self.linesText.setAutoFillBackground(True)
self.linesText.setFixedHeight(fPx)
self.linesText.setAlignment(QtAlignLeftTop)
# Words
self.wordsIcon = QLabel("", self)
self.wordsIcon.setContentsMargins(0, 0, 0, 0)
self.wordsIcon.setFixedHeight(iPx)
self.wordsIcon.setAlignment(QtAlignLeftTop)
self.wordsText = QLabel("", self)
self.wordsText.setIndent(0)
self.wordsText.setMargin(0)
self.wordsText.setContentsMargins(0, 0, 0, 0)
self.wordsText.setAutoFillBackground(True)
self.wordsText.setFixedHeight(fPx)
self.wordsText.setAlignment(QtAlignLeftTop)
@@ -3167,10 +3137,10 @@ class GuiDocEditFooter(QWidget):
iPx = round(0.9*SHARED.theme.baseIconHeight)
self.linesIcon.setPixmap(SHARED.theme.getPixmap("lines", (iPx, iPx)))
self.wordsIcon.setPixmap(SHARED.theme.getPixmap("stats", (iPx, iPx)))
self.matchColours()
self.matchColors()
return
def matchColours(self) -> None:
def matchColors(self) -> None:
"""Update the colours of the widget to match those of the syntax
theme rather than the main GUI.
"""