12 import re |
12 import re |
13 import shutil |
13 import shutil |
14 import tempfile |
14 import tempfile |
15 import sys |
15 import sys |
16 import io |
16 import io |
|
17 import contextlib |
17 |
18 |
18 from PyQt5.QtCore import pyqtSlot, pyqtSignal, Qt, QUrl, QThread |
19 from PyQt5.QtCore import pyqtSlot, pyqtSignal, Qt, QUrl, QThread |
19 from PyQt5.QtGui import QCursor |
20 from PyQt5.QtGui import QCursor |
20 from PyQt5.QtWidgets import ( |
21 from PyQt5.QtWidgets import ( |
21 QWidget, QVBoxLayout, QLabel, QCheckBox, QSizePolicy, QToolTip |
22 QWidget, QVBoxLayout, QLabel, QCheckBox, QSizePolicy, QToolTip |
35 """ |
36 """ |
36 Constructor |
37 Constructor |
37 |
38 |
38 @param parent reference to the parent widget (QWidget) |
39 @param parent reference to the parent widget (QWidget) |
39 """ |
40 """ |
40 super(PreviewerHTML, self).__init__(parent) |
41 super().__init__(parent) |
41 |
42 |
42 self.__layout = QVBoxLayout(self) |
43 self.__layout = QVBoxLayout(self) |
43 |
44 |
44 self.titleLabel = QLabel(self) |
45 self.titleLabel = QLabel(self) |
45 self.titleLabel.setWordWrap(True) |
46 self.titleLabel.setWordWrap(True) |
235 self.__saveScrollBarPositions() |
236 self.__saveScrollBarPositions() |
236 self.previewView.page().loadFinished.connect( |
237 self.previewView.page().loadFinished.connect( |
237 self.__restoreScrollBarPositions) |
238 self.__restoreScrollBarPositions) |
238 if not filePath: |
239 if not filePath: |
239 filePath = "/" |
240 filePath = "/" |
240 if rootPath: |
241 baseUrl = ( |
241 baseUrl = QUrl.fromLocalFile(rootPath + "/index.html") |
242 QUrl.fromLocalFile(rootPath + "/index.html") |
242 else: |
243 if rootPath else |
243 baseUrl = QUrl.fromLocalFile(filePath) |
244 QUrl.fromLocalFile(filePath) |
|
245 ) |
244 self.previewView.setHtml(html, baseUrl=baseUrl) |
246 self.previewView.setHtml(html, baseUrl=baseUrl) |
245 if self.__previewedEditor: |
247 if self.__previewedEditor: |
246 self.__previewedEditor.setFocus() |
248 self.__previewedEditor.setFocus() |
247 |
249 |
248 @pyqtSlot(str) |
250 @pyqtSlot(str) |
274 "res.x = window.scrollX;" |
276 "res.x = window.scrollX;" |
275 "res.y = window.scrollY;" |
277 "res.y = window.scrollY;" |
276 "return res;" |
278 "return res;" |
277 "})()" |
279 "})()" |
278 ) |
280 ) |
279 if pos is not None: |
281 pos = QPoint(0, 0) if pos is None else QPoint(pos["x"], pos["y"]) |
280 pos = QPoint(pos["x"], pos["y"]) |
|
281 else: |
|
282 pos = QPoint(0, 0) |
|
283 self.__scrollBarPositions[self.__previewedPath] = pos |
282 self.__scrollBarPositions[self.__previewedPath] = pos |
284 self.__hScrollBarAtEnd[self.__previewedPath] = False |
283 self.__hScrollBarAtEnd[self.__previewedPath] = False |
285 self.__vScrollBarAtEnd[self.__previewedPath] = False |
284 self.__vScrollBarAtEnd[self.__previewedPath] = False |
286 |
285 |
287 def __restoreScrollBarPositions(self): |
286 def __restoreScrollBarPositions(self): |
335 """ |
334 """ |
336 Constructor |
335 Constructor |
337 |
336 |
338 @param parent reference to the parent object (QObject) |
337 @param parent reference to the parent object (QObject) |
339 """ |
338 """ |
340 super(PreviewProcessingThread, self).__init__() |
339 super().__init__() |
341 |
340 |
342 self.__lock = threading.Lock() |
341 self.__lock = threading.Lock() |
343 |
342 |
344 def process(self, filePath, language, text, ssiEnabled, rootPath, |
343 def process(self, filePath, language, text, ssiEnabled, rootPath, |
345 useSphinx, convertNewLineToBreak, usePyMdownExtensions, |
344 useSphinx, convertNewLineToBreak, usePyMdownExtensions, |
686 from . import MarkdownExtensions |
685 from . import MarkdownExtensions |
687 |
686 |
688 extensions = [] |
687 extensions = [] |
689 |
688 |
690 mermaidNeeded = False |
689 mermaidNeeded = False |
691 if Preferences.getEditor("PreviewMarkdownMermaid"): |
690 if ( |
692 if MarkdownExtensions.MermaidRegexFullText.search(text): |
691 Preferences.getEditor("PreviewMarkdownMermaid") and |
693 extensions.append(MarkdownExtensions.MermaidExtension()) |
692 MarkdownExtensions.MermaidRegexFullText.search(text) |
694 mermaidNeeded = True |
693 ): |
|
694 extensions.append(MarkdownExtensions.MermaidExtension()) |
|
695 mermaidNeeded = True |
695 |
696 |
696 if convertNewLineToBreak: |
697 if convertNewLineToBreak: |
697 extensions.append('nl2br') |
698 extensions.append('nl2br') |
698 |
699 |
699 pyMdown = False |
700 pyMdown = False |
700 if usePyMdownExtensions: |
701 if usePyMdownExtensions: |
701 try: |
702 with contextlib.suppress(ImportError): |
702 import pymdownx # __IGNORE_EXCEPTION__ __IGNORE_WARNING__ |
703 import pymdownx # __IGNORE_EXCEPTION__ __IGNORE_WARNING__ |
703 # PyPI package is 'pymdown-extensions' |
704 # PyPI package is 'pymdown-extensions' |
704 |
705 |
705 extensions.extend([ |
706 extensions.extend([ |
706 'toc', |
707 'toc', |
707 'pymdownx.extra', 'pymdownx.caret', 'pymdownx.emoji', |
708 'pymdownx.extra', 'pymdownx.caret', 'pymdownx.emoji', |
708 'pymdownx.mark', 'pymdownx.tilde', 'pymdownx.keys', |
709 'pymdownx.mark', 'pymdownx.tilde', 'pymdownx.keys', |
709 'pymdownx.tasklist', 'pymdownx.smartsymbols', |
710 'pymdownx.tasklist', 'pymdownx.smartsymbols', |
710 ]) |
711 ]) |
711 pyMdown = True |
712 pyMdown = True |
712 except ImportError: |
|
713 pass |
|
714 |
713 |
715 if not pyMdown: |
714 if not pyMdown: |
716 extensions.extend(['extra', 'toc']) |
715 extensions.extend(['extra', 'toc']) |
717 |
716 |
718 # version 2.0 supports only extension names, not instances |
717 # version 2.0 supports only extension names, not instances |
766 mermaid_initialize = "" |
765 mermaid_initialize = "" |
767 |
766 |
768 htmlFormat = Preferences.getEditor("PreviewMarkdownHTMLFormat").lower() |
767 htmlFormat = Preferences.getEditor("PreviewMarkdownHTMLFormat").lower() |
769 body = markdown.markdown(text, extensions=extensions, |
768 body = markdown.markdown(text, extensions=extensions, |
770 output_format=htmlFormat.lower()) |
769 output_format=htmlFormat.lower()) |
771 if e5App().usesDarkPalette(): |
770 style = ( |
772 style = ( |
771 (PreviewerHTMLStyles.css_markdown_dark + |
773 PreviewerHTMLStyles.css_markdown_dark + |
772 PreviewerHTMLStyles.css_pygments_dark) |
774 PreviewerHTMLStyles.css_pygments_dark |
773 if e5App().usesDarkPalette() else |
775 ) |
774 (PreviewerHTMLStyles.css_markdown_light + |
776 else: |
775 PreviewerHTMLStyles.css_pygments_light) |
777 style = ( |
776 ) |
778 PreviewerHTMLStyles.css_markdown_light + |
|
779 PreviewerHTMLStyles.css_pygments_light |
|
780 ) |
|
781 |
777 |
782 if htmlFormat == "xhtml1": |
778 if htmlFormat == "xhtml1": |
783 head = ( |
779 head = ( |
784 '''<!DOCTYPE html PUBLIC "-//W3C//DTD''' |
780 '''<!DOCTYPE html PUBLIC "-//W3C//DTD''' |
785 ''' XHTML 1.0 Transitional//EN"\n''' |
781 ''' XHTML 1.0 Transitional//EN"\n''' |