Resolve HTML render bug (#951)
* Fix bug breaking HTML rendering with angle brackets in other blocks than normal text * Restore cursor if document viewer fails to load * Improve test coverage
This commit is contained in:
committed by
GitHub
parent
59e10fbf0a
commit
9be2519406
+23
-17
@@ -154,25 +154,31 @@ class ToHtml(Tokenizer):
|
||||
parStyle = None
|
||||
tmpResult = []
|
||||
|
||||
for tType, tLine, tDirty, tFormat, tStyle in self._theTokens:
|
||||
for tType, tLine, tText, tFormat, tStyle in self._theTokens:
|
||||
|
||||
# Replace < and > and recompute formatting positions
|
||||
cText = []
|
||||
i = 0
|
||||
for c in tDirty:
|
||||
if c == "<":
|
||||
cText.append("<")
|
||||
tFormat = [[a + 3 if a > i else a, b, c] for a, b, c in tFormat]
|
||||
i += 4
|
||||
elif c == ">":
|
||||
cText.append(">")
|
||||
tFormat = [[a + 3 if a > i else a, b, c] for a, b, c in tFormat]
|
||||
i += 4
|
||||
else:
|
||||
cText.append(c)
|
||||
i += 1
|
||||
# Replace < and > with HTML entities
|
||||
if tFormat:
|
||||
# If we have formatting, we must recompute the locations
|
||||
cText = []
|
||||
i = 0
|
||||
for c in tText:
|
||||
if c == "<":
|
||||
cText.append("<")
|
||||
tFormat = [[a + 3 if a > i else a, b, c] for a, b, c in tFormat]
|
||||
i += 4
|
||||
elif c == ">":
|
||||
cText.append(">")
|
||||
tFormat = [[a + 3 if a > i else a, b, c] for a, b, c in tFormat]
|
||||
i += 4
|
||||
else:
|
||||
cText.append(c)
|
||||
i += 1
|
||||
|
||||
tText = "".join(cText)
|
||||
tText = "".join(cText)
|
||||
|
||||
else:
|
||||
# If we don't have formatting, we can do a plain replace
|
||||
tText = tText.replace("<", "<").replace(">", ">")
|
||||
|
||||
# Styles
|
||||
aStyle = []
|
||||
|
||||
@@ -184,6 +184,7 @@ class GuiDocViewer(QTextBrowser):
|
||||
logger.error("Failed to generate preview for document with handle '%s'", tHandle)
|
||||
logException()
|
||||
self.setText(self.tr("An error occurred while generating the preview."))
|
||||
qApp.restoreOverrideCursor()
|
||||
return False
|
||||
|
||||
# Refresh the tab stops
|
||||
|
||||
@@ -417,6 +417,27 @@ def testCoreToHtml_SpecialCases(mockGUI):
|
||||
"<p>Test > text <em><<strong>bold</strong>></em> and more.</p>\n"
|
||||
)
|
||||
|
||||
# Test for bug #950
|
||||
# =================
|
||||
# See: https://github.com/vkbo/novelWriter/issues/950
|
||||
|
||||
theHtml.setComments(True)
|
||||
theHtml._theText = "% Test > text _<**bold**>_ and more.\n"
|
||||
theHtml.tokenizeText()
|
||||
theHtml.doConvert()
|
||||
assert theHtml.theResult == (
|
||||
"<p class='comment'>"
|
||||
"<strong>Comment:</strong> Test > text _<**bold**>_ and more."
|
||||
"</p>\n"
|
||||
)
|
||||
|
||||
theHtml._theText = "## Heading <1>\n"
|
||||
theHtml.tokenizeText()
|
||||
theHtml.doConvert()
|
||||
assert theHtml.theResult == (
|
||||
"<h1 style='page-break-before: always;'>Heading <1></h1>\n"
|
||||
)
|
||||
|
||||
# END Test testCoreToHtml_SpecialCases
|
||||
|
||||
|
||||
|
||||
@@ -24,8 +24,10 @@ import pytest
|
||||
from PyQt5.QtCore import Qt, QUrl
|
||||
from PyQt5.QtGui import QTextCursor
|
||||
from PyQt5.QtWidgets import qApp, QAction, QMessageBox
|
||||
from mock import causeException
|
||||
|
||||
from novelwriter.enum import nwDocAction
|
||||
from novelwriter.core import ToHtml
|
||||
|
||||
keyDelay = 2
|
||||
typeDelay = 1
|
||||
@@ -52,7 +54,7 @@ def testGuiViewer_Main(qtbot, monkeypatch, nwGUI, nwLipsum):
|
||||
assert nwGUI.theIndex._refIndex != {}
|
||||
|
||||
# Select a document in the project tree
|
||||
assert nwGUI.treeView.setSelectedHandle("88243afbe5ed8")
|
||||
nwGUI.treeView.setSelectedHandle("88243afbe5ed8")
|
||||
|
||||
# Middle-click the selected item
|
||||
theItem = nwGUI.treeView._getTreeItem("88243afbe5ed8")
|
||||
@@ -67,14 +69,14 @@ def testGuiViewer_Main(qtbot, monkeypatch, nwGUI, nwLipsum):
|
||||
assert nwGUI.docViewer.toPlainText() == origText
|
||||
|
||||
# Cursor line
|
||||
assert not nwGUI.docViewer.setCursorLine("not a number")
|
||||
assert nwGUI.docViewer.setCursorLine(3)
|
||||
assert nwGUI.docViewer.setCursorLine("not a number") is False
|
||||
assert nwGUI.docViewer.setCursorLine(3) is True
|
||||
theCursor = nwGUI.docViewer.textCursor()
|
||||
assert theCursor.position() == 40
|
||||
|
||||
# Cursor position
|
||||
assert not nwGUI.docViewer.setCursorPosition("not a number")
|
||||
assert nwGUI.docViewer.setCursorPosition(100)
|
||||
assert nwGUI.docViewer.setCursorPosition("not a number") is False
|
||||
assert nwGUI.docViewer.setCursorPosition(100) is True
|
||||
|
||||
# Select word
|
||||
nwGUI.docViewer._makeSelection(QTextCursor.WordUnderCursor)
|
||||
@@ -83,17 +85,17 @@ def testGuiViewer_Main(qtbot, monkeypatch, nwGUI, nwLipsum):
|
||||
qClip.clear()
|
||||
|
||||
# Cut
|
||||
assert nwGUI.docViewer.docAction(nwDocAction.CUT)
|
||||
assert nwGUI.docViewer.docAction(nwDocAction.CUT) is True
|
||||
assert qClip.text() == "laoreet"
|
||||
qClip.clear()
|
||||
|
||||
# Copy
|
||||
assert nwGUI.docViewer.docAction(nwDocAction.COPY)
|
||||
assert nwGUI.docViewer.docAction(nwDocAction.COPY) is True
|
||||
assert qClip.text() == "laoreet"
|
||||
qClip.clear()
|
||||
|
||||
# Select Paragraph
|
||||
assert nwGUI.docViewer.docAction(nwDocAction.SEL_PARA)
|
||||
assert nwGUI.docViewer.docAction(nwDocAction.SEL_PARA) is True
|
||||
theCursor = nwGUI.docViewer.textCursor()
|
||||
assert theCursor.selectedText() == (
|
||||
"Synopsis: Aenean ut placerat velit. Etiam laoreet ullamcorper risus, "
|
||||
@@ -103,26 +105,26 @@ def testGuiViewer_Main(qtbot, monkeypatch, nwGUI, nwLipsum):
|
||||
)
|
||||
|
||||
# Select All
|
||||
assert nwGUI.docViewer.docAction(nwDocAction.SEL_ALL)
|
||||
assert nwGUI.docViewer.docAction(nwDocAction.SEL_ALL) is True
|
||||
theCursor = nwGUI.docViewer.textCursor()
|
||||
assert len(theCursor.selectedText()) == 3061
|
||||
|
||||
# Other actions
|
||||
assert not nwGUI.docViewer.docAction(nwDocAction.NO_ACTION)
|
||||
assert nwGUI.docViewer.docAction(nwDocAction.NO_ACTION) is False
|
||||
|
||||
# Close document
|
||||
nwGUI.docViewer.docHeader._closeDocument()
|
||||
assert nwGUI.docViewer.docHandle() is None
|
||||
|
||||
# Action on no document
|
||||
assert not nwGUI.docViewer.docAction(nwDocAction.COPY)
|
||||
assert nwGUI.docViewer.docAction(nwDocAction.COPY) is False
|
||||
|
||||
# Open again via menu
|
||||
assert nwGUI.treeView.setSelectedHandle("88243afbe5ed8")
|
||||
nwGUI.mainMenu.aViewDoc.activate(QAction.Trigger)
|
||||
|
||||
# Select "Bod" link
|
||||
assert nwGUI.docViewer.setCursorPosition(27)
|
||||
assert nwGUI.docViewer.setCursorPosition(27) is True
|
||||
nwGUI.docViewer._makeSelection(QTextCursor.WordUnderCursor)
|
||||
theRect = nwGUI.docViewer.cursorRect()
|
||||
# qtbot.mouseClick(nwGUI.docViewer.viewport(), Qt.LeftButton, pos=theRect.center(), delay=100)
|
||||
@@ -168,17 +170,23 @@ def testGuiViewer_Main(qtbot, monkeypatch, nwGUI, nwLipsum):
|
||||
assert nwGUI.docViewer.stickyRef is viewState
|
||||
|
||||
# Document footer show/hide synopsis
|
||||
assert nwGUI.viewDocument("f96ec11c6a3da")
|
||||
assert nwGUI.viewDocument("f96ec11c6a3da") is True
|
||||
assert len(nwGUI.docViewer.toPlainText()) == 4315
|
||||
nwGUI.docViewer.docFooter._doToggleSynopsis(False)
|
||||
assert len(nwGUI.docViewer.toPlainText()) == 4099
|
||||
|
||||
# Document footer show/hide comments
|
||||
assert nwGUI.viewDocument("846352075de7d")
|
||||
assert nwGUI.viewDocument("846352075de7d") is True
|
||||
assert len(nwGUI.docViewer.toPlainText()) == 675
|
||||
nwGUI.docViewer.docFooter._doToggleComments(False)
|
||||
assert len(nwGUI.docViewer.toPlainText()) == 635
|
||||
|
||||
# Crash the HTML rendering
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr(ToHtml, "doConvert", causeException)
|
||||
assert nwGUI.docViewer.loadText("846352075de7d") is False
|
||||
assert nwGUI.docViewer.toPlainText() == "An error occurred while generating the preview."
|
||||
|
||||
# qtbot.stopForInteraction()
|
||||
|
||||
# END Test testGuiViewer_Main
|
||||
|
||||
Reference in New Issue
Block a user