src/eric7/UI/Previewers/PreviewerHTML.py

branch
eric7
changeset 9221
bf71ee032bb4
parent 9209
b99e7fd55fd3
child 9268
0d414630a28e
diff -r e9e7eca7efee -r bf71ee032bb4 src/eric7/UI/Previewers/PreviewerHTML.py
--- a/src/eric7/UI/Previewers/PreviewerHTML.py	Wed Jul 13 11:16:20 2022 +0200
+++ b/src/eric7/UI/Previewers/PreviewerHTML.py	Wed Jul 13 14:55:47 2022 +0200
@@ -19,7 +19,12 @@
 from PyQt6.QtCore import pyqtSlot, pyqtSignal, Qt, QUrl, QThread
 from PyQt6.QtGui import QCursor
 from PyQt6.QtWidgets import (
-    QWidget, QVBoxLayout, QLabel, QCheckBox, QSizePolicy, QToolTip
+    QWidget,
+    QVBoxLayout,
+    QLabel,
+    QCheckBox,
+    QSizePolicy,
+    QToolTip,
 )
 
 from EricWidgets.EricApplication import ericApp
@@ -32,194 +37,199 @@
     """
     Class implementing a previewer widget for HTML, Markdown and ReST files.
     """
+
     def __init__(self, parent=None):
         """
         Constructor
-        
+
         @param parent reference to the parent widget (QWidget)
         """
         super().__init__(parent)
-        
+
         self.__layout = QVBoxLayout(self)
-        
+
         self.titleLabel = QLabel(self)
         self.titleLabel.setWordWrap(True)
         self.titleLabel.setTextInteractionFlags(
-            Qt.TextInteractionFlag.NoTextInteraction)
+            Qt.TextInteractionFlag.NoTextInteraction
+        )
         self.__layout.addWidget(self.titleLabel)
-        
+
         self.__previewAvailable = True
-        
+
         try:
             from PyQt6.QtWebEngineWidgets import QWebEngineView
+
             self.previewView = QWebEngineView(self)
             self.previewView.page().linkHovered.connect(self.__showLink)
         except ImportError:
             self.__previewAvailable = False
-            self.titleLabel.setText(self.tr(
-                "<b>HTML Preview is not available!<br/>"
-                "Install PyQt6-WebEngine.</b>"))
+            self.titleLabel.setText(
+                self.tr(
+                    "<b>HTML Preview is not available!<br/>"
+                    "Install PyQt6-WebEngine.</b>"
+                )
+            )
             self.titleLabel.setAlignment(Qt.AlignmentFlag.AlignHCenter)
             self.__layout.addStretch()
             return
-        
-        sizePolicy = QSizePolicy(QSizePolicy.Policy.Preferred,
-                                 QSizePolicy.Policy.Expanding)
+
+        sizePolicy = QSizePolicy(
+            QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Expanding
+        )
         sizePolicy.setHorizontalStretch(0)
         sizePolicy.setVerticalStretch(0)
-        sizePolicy.setHeightForWidth(
-            self.previewView.sizePolicy().hasHeightForWidth())
+        sizePolicy.setHeightForWidth(self.previewView.sizePolicy().hasHeightForWidth())
         self.previewView.setSizePolicy(sizePolicy)
-        self.previewView.setContextMenuPolicy(
-            Qt.ContextMenuPolicy.NoContextMenu)
+        self.previewView.setContextMenuPolicy(Qt.ContextMenuPolicy.NoContextMenu)
         self.previewView.setUrl(QUrl("about:blank"))
         self.__layout.addWidget(self.previewView)
-        
+
         self.jsCheckBox = QCheckBox(self.tr("Enable JavaScript"), self)
-        self.jsCheckBox.setToolTip(self.tr(
-            "Select to enable JavaScript for HTML previews"))
+        self.jsCheckBox.setToolTip(
+            self.tr("Select to enable JavaScript for HTML previews")
+        )
         self.__layout.addWidget(self.jsCheckBox)
-        
-        self.ssiCheckBox = QCheckBox(self.tr("Enable Server Side Includes"),
-                                     self)
-        self.ssiCheckBox.setToolTip(self.tr(
-            "Select to enable support for Server Side Includes"))
+
+        self.ssiCheckBox = QCheckBox(self.tr("Enable Server Side Includes"), self)
+        self.ssiCheckBox.setToolTip(
+            self.tr("Select to enable support for Server Side Includes")
+        )
         self.__layout.addWidget(self.ssiCheckBox)
-        
+
         self.jsCheckBox.clicked[bool].connect(self.on_jsCheckBox_clicked)
         self.ssiCheckBox.clicked[bool].connect(self.on_ssiCheckBox_clicked)
         self.previewView.titleChanged.connect(self.on_previewView_titleChanged)
-        
-        self.jsCheckBox.setChecked(
-            Preferences.getUI("ShowFilePreviewJS"))
-        self.ssiCheckBox.setChecked(
-            Preferences.getUI("ShowFilePreviewSSI"))
-        
+
+        self.jsCheckBox.setChecked(Preferences.getUI("ShowFilePreviewJS"))
+        self.ssiCheckBox.setChecked(Preferences.getUI("ShowFilePreviewSSI"))
+
         self.__scrollBarPositions = {}
         self.__vScrollBarAtEnd = {}
         self.__hScrollBarAtEnd = {}
-        
+
         self.__processingThread = PreviewProcessingThread()
         self.__processingThread.htmlReady.connect(self.__setHtml)
 
         self.__previewedPath = None
         self.__previewedEditor = None
-    
+
     def shutdown(self):
         """
         Public method to perform shutdown actions.
         """
         if self.__previewAvailable:
             self.__processingThread.wait()
-    
+
     @pyqtSlot(bool)
     def on_jsCheckBox_clicked(self, checked):
         """
         Private slot to enable/disable JavaScript.
-        
+
         @param checked state of the checkbox (boolean)
         """
         Preferences.setUI("ShowFilePreviewJS", checked)
         self.__setJavaScriptEnabled(checked)
-    
+
     def __setJavaScriptEnabled(self, enable):
         """
         Private method to enable/disable JavaScript.
-        
+
         @param enable flag indicating the enable state (boolean)
         """
         self.jsCheckBox.setChecked(enable)
-        
+
         settings = self.previewView.settings()
         settings.setAttribute(settings.JavascriptEnabled, enable)
-        
+
         self.processEditor()
-    
+
     @pyqtSlot(bool)
     def on_ssiCheckBox_clicked(self, checked):
         """
         Private slot to enable/disable SSI.
-        
+
         @param checked state of the checkbox (boolean)
         """
         Preferences.setUI("ShowFilePreviewSSI", checked)
         self.processEditor()
-    
+
     @pyqtSlot(str)
     def __showLink(self, urlStr):
         """
         Private slot to show the hovered link in a tooltip.
-        
+
         @param urlStr hovered URL
         @type str
         """
         QToolTip.showText(QCursor.pos(), urlStr, self.previewView)
-    
+
     def processEditor(self, editor=None):
         """
         Public slot to process an editor's text.
-        
+
         @param editor editor to be processed (Editor)
         """
         if not self.__previewAvailable:
             return
-        
+
         if editor is None:
             editor = self.__previewedEditor
         else:
             self.__previewedEditor = editor
-        
+
         if editor is not None:
             fn = editor.getFileName()
-            
+
             if fn:
                 extension = os.path.normcase(os.path.splitext(fn)[1][1:])
             else:
                 extension = ""
             if (
-                extension in Preferences.getEditor(
-                    "PreviewHtmlFileNameExtensions") or
-                editor.getLanguage() == "HTML"
+                extension in Preferences.getEditor("PreviewHtmlFileNameExtensions")
+                or editor.getLanguage() == "HTML"
             ):
                 language = "HTML"
             elif (
-                extension in Preferences.getEditor(
-                    "PreviewMarkdownFileNameExtensions") or
-                editor.getLanguage().lower() == "markdown"
+                extension in Preferences.getEditor("PreviewMarkdownFileNameExtensions")
+                or editor.getLanguage().lower() == "markdown"
             ):
                 language = "Markdown"
             elif (
-                extension in Preferences.getEditor(
-                    "PreviewRestFileNameExtensions") or
-                editor.getLanguage().lower() == "restructuredtext"
+                extension in Preferences.getEditor("PreviewRestFileNameExtensions")
+                or editor.getLanguage().lower() == "restructuredtext"
             ):
                 language = "ReST"
             else:
-                self.__setHtml(fn, self.tr(
-                    "<p>No preview available for this type of file.</p>"))
+                self.__setHtml(
+                    fn, self.tr("<p>No preview available for this type of file.</p>")
+                )
                 return
-            
+
             if fn:
                 rootPath = os.path.dirname(os.path.abspath(fn))
             else:
                 rootPath = ""
-            
+
             if bool(editor.text()):
                 self.__processingThread.process(
-                    fn, language, editor.text(),
-                    self.ssiCheckBox.isChecked(), rootPath,
+                    fn,
+                    language,
+                    editor.text(),
+                    self.ssiCheckBox.isChecked(),
+                    rootPath,
                     Preferences.getEditor("PreviewRestUseSphinx"),
                     Preferences.getEditor("PreviewMarkdownNLtoBR"),
-                    Preferences.getEditor(
-                        "PreviewMarkdownUsePyMdownExtensions"),
+                    Preferences.getEditor("PreviewMarkdownUsePyMdownExtensions"),
                     Preferences.getEditor("PreviewMarkdownHTMLFormat"),
-                    Preferences.getEditor("PreviewRestDocutilsHTMLFormat"))
+                    Preferences.getEditor("PreviewRestDocutilsHTMLFormat"),
+                )
 
     def __setHtml(self, filePath, html, rootPath):
         """
         Private method to set the HTML to the view and restore the scroll bars
         positions.
-        
+
         @param filePath file path of the previewed editor
         @type str
         @param html processed HTML text ready to be shown
@@ -228,38 +238,39 @@
         @type str
         """
         self.__previewedPath = Utilities.normcasepath(
-            Utilities.fromNativeSeparators(filePath))
+            Utilities.fromNativeSeparators(filePath)
+        )
         self.__saveScrollBarPositions()
-        self.previewView.page().loadFinished.connect(
-            self.__restoreScrollBarPositions)
+        self.previewView.page().loadFinished.connect(self.__restoreScrollBarPositions)
         if not filePath:
             filePath = "/"
         baseUrl = (
             QUrl.fromLocalFile(rootPath + "/index.html")
-            if rootPath else
-            QUrl.fromLocalFile(filePath)
+            if rootPath
+            else QUrl.fromLocalFile(filePath)
         )
         self.previewView.setHtml(html, baseUrl=baseUrl)
         if self.__previewedEditor:
             self.__previewedEditor.setFocus()
-    
+
     @pyqtSlot(str)
     def on_previewView_titleChanged(self, title):
         """
         Private slot to handle a change of the title.
-        
+
         @param title new title (string)
         """
         if title:
             self.titleLabel.setText(self.tr("Preview - {0}").format(title))
         else:
             self.titleLabel.setText(self.tr("Preview"))
-    
+
     def __saveScrollBarPositions(self):
         """
         Private method to save scroll bar positions for a previewed editor.
         """
         from PyQt6.QtCore import QPoint
+
         try:
             pos = self.previewView.scrollPosition()
         except AttributeError:
@@ -285,32 +296,33 @@
         """
         if self.__previewedPath not in self.__scrollBarPositions:
             return
-        
+
         pos = self.__scrollBarPositions[self.__previewedPath]
         self.previewView.page().runJavaScript(
-            "window.scrollTo({0}, {1});".format(pos.x(), pos.y()))
-    
+            "window.scrollTo({0}, {1});".format(pos.x(), pos.y())
+        )
+
     def __execJavaScript(self, script):
         """
         Private function to execute a JavaScript function Synchroneously.
-        
+
         @param script JavaScript script source to be executed
         @type str
         @return result of the script
         @rtype depending upon script result
         """
         from PyQt6.QtCore import QEventLoop
+
         loop = QEventLoop()
         resultDict = {"res": None}
-        
+
         def resultCallback(res, resDict=resultDict):
             if loop and loop.isRunning():
                 resDict["res"] = res
                 loop.quit()
-        
-        self.previewView.page().runJavaScript(
-            script, resultCallback)
-        
+
+        self.previewView.page().runJavaScript(script, resultCallback)
+
         loop.exec()
         return resultDict["res"]
 
@@ -319,29 +331,40 @@
     """
     Class implementing a thread to process some text into HTML usable by the
     previewer view.
-    
+
     @signal htmlReady(str, str, str) emitted with the file name, the processed
         HTML and the web site root path to signal the availability of the
         processed HTML
     """
+
     htmlReady = pyqtSignal(str, str, str)
-    
+
     def __init__(self, parent=None):
         """
         Constructor
-        
+
         @param parent reference to the parent object (QObject)
         """
         super().__init__()
-        
+
         self.__lock = threading.Lock()
-    
-    def process(self, filePath, language, text, ssiEnabled, rootPath,
-                useSphinx, convertNewLineToBreak, usePyMdownExtensions,
-                markdownHtmlFormat, restDocutilsHtmlFormat):
+
+    def process(
+        self,
+        filePath,
+        language,
+        text,
+        ssiEnabled,
+        rootPath,
+        useSphinx,
+        convertNewLineToBreak,
+        usePyMdownExtensions,
+        markdownHtmlFormat,
+        restDocutilsHtmlFormat,
+    ):
         """
         Public method to convert the given text to HTML.
-        
+
         @param filePath file path of the text
         @type str
         @param language language of the text
@@ -380,7 +403,7 @@
             self.__restDocutilsHtmlFormat = restDocutilsHtmlFormat
             if not self.isRunning():
                 self.start(QThread.Priority.LowPriority)
-    
+
     def run(self):
         """
         Public thread method to convert the stored data.
@@ -398,27 +421,45 @@
                 usePyMdownExtensions = self.__usePyMdownExtensions
                 markdownHtmlFormat = self.__markdownHtmlFormat
                 restDocutilsHtmlFormat = self.__restDocutilsHtmlFormat
-            
+
                 self.__haveData = False
 
-            html = self.__getHtml(language, text, ssiEnabled, filePath,
-                                  rootPath, useSphinx, convertNewLineToBreak,
-                                  usePyMdownExtensions, markdownHtmlFormat,
-                                  restDocutilsHtmlFormat)
-            
+            html = self.__getHtml(
+                language,
+                text,
+                ssiEnabled,
+                filePath,
+                rootPath,
+                useSphinx,
+                convertNewLineToBreak,
+                usePyMdownExtensions,
+                markdownHtmlFormat,
+                restDocutilsHtmlFormat,
+            )
+
             with self.__lock:
                 if not self.__haveData:
                     self.htmlReady.emit(filePath, html, rootPath)
                     break
                 # else - next iteration
-    
-    def __getHtml(self, language, text, ssiEnabled, filePath, rootPath,
-                  useSphinx, convertNewLineToBreak, usePyMdownExtensions,
-                  markdownHtmlFormat, restDocutilsHtmlFormat):
+
+    def __getHtml(
+        self,
+        language,
+        text,
+        ssiEnabled,
+        filePath,
+        rootPath,
+        useSphinx,
+        convertNewLineToBreak,
+        usePyMdownExtensions,
+        markdownHtmlFormat,
+        restDocutilsHtmlFormat,
+    ):
         """
         Private method to process the given text depending upon the given
         language.
-        
+
         @param language language of the text
         @type str
         @param text to be processed
@@ -453,20 +494,19 @@
             return self.__processRootPath(html, rootPath)
         elif language == "Markdown":
             return self.__convertMarkdown(
-                text, convertNewLineToBreak, usePyMdownExtensions,
-                markdownHtmlFormat)
+                text, convertNewLineToBreak, usePyMdownExtensions, markdownHtmlFormat
+            )
         elif language == "ReST":
             return self.__convertReST(text, useSphinx, restDocutilsHtmlFormat)
         else:
-            return self.tr(
-                "<p>No preview available for this type of file.</p>")
-    
+            return self.tr("<p>No preview available for this type of file.</p>")
+
     def __processSSI(self, txt, filename, root):
         """
         Private method to process the given text for SSI statements.
-        
+
         Note: Only a limited subset of SSI statements are supported.
-        
+
         @param txt text to be processed (string)
         @param filename name of the file associated with the given text
             (string)
@@ -475,18 +515,19 @@
         """
         if not filename:
             return txt
-        
+
         # SSI include
         incRe = re.compile(
             r"""<!--#include[ \t]+(virtual|file)=[\"']([^\"']+)[\"']\s*-->""",
-            re.IGNORECASE)
+            re.IGNORECASE,
+        )
         baseDir = os.path.dirname(os.path.abspath(filename))
         docRoot = root if root != "" else baseDir
         while True:
             incMatch = incRe.search(txt)
             if incMatch is None:
                 break
-            
+
             if incMatch.group(1) == "virtual":
                 incFile = Utilities.normjoinpath(docRoot, incMatch.group(2))
             elif incMatch.group(1) == "file":
@@ -503,14 +544,14 @@
             else:
                 # remove SSI include
                 incTxt = ""
-            txt = txt[:incMatch.start(0)] + incTxt + txt[incMatch.end(0):]
-        
+            txt = txt[: incMatch.start(0)] + incTxt + txt[incMatch.end(0) :]
+
         return txt
-    
+
     def __processRootPath(self, txt, root):
         """
         Private method to adjust absolute references to the given root path.
-        
+
         @param txt text to be processed
         @type str
         @param root directory of the document root
@@ -520,31 +561,36 @@
         """
         if not root:
             return txt
-        
+
         root = Utilities.fromNativeSeparators(root)
         if not root.endswith("/"):
             root += "/"
         rootLen = len(root)
-        
-        refRe = re.compile(
-            r"""(href|src)=[\\"']/([^\\"']+)[\\"']""",
-            re.IGNORECASE)
+
+        refRe = re.compile(r"""(href|src)=[\\"']/([^\\"']+)[\\"']""", re.IGNORECASE)
         pos = 0
         while True:
             refMatch = refRe.search(txt, pos)
             if refMatch is None:
                 break
-            
-            txt = (txt[:refMatch.start(0)] + refMatch.group(1) + '="' + root +
-                   refMatch.group(2) + '"' + txt[refMatch.end(0):])
+
+            txt = (
+                txt[: refMatch.start(0)]
+                + refMatch.group(1)
+                + '="'
+                + root
+                + refMatch.group(2)
+                + '"'
+                + txt[refMatch.end(0) :]
+            )
             pos = refMatch.end(0) + rootLen
-        
+
         return txt
-    
+
     def __convertReST(self, text, useSphinx, restDocutilsHtmlFormat):
         """
         Private method to convert ReST text into HTML.
-        
+
         @param text text to be processed (string)
         @param useSphinx flag indicating to use Sphinx to generate the
             ReST preview (boolean)
@@ -556,16 +602,16 @@
             return self.__convertReSTSphinx(text)
         else:
             return self.__convertReSTDocutils(text, restDocutilsHtmlFormat)
-    
+
     def __convertReSTSphinx(self, text):
         """
         Private method to convert ReST text into HTML using 'sphinx'.
-        
+
         @param text text to be processed (string)
         @return processed HTML (string)
         """
         try:
-            from sphinx.application import Sphinx   # __IGNORE_EXCEPTION__
+            from sphinx.application import Sphinx  # __IGNORE_EXCEPTION__
         except ImportError:
             return self.tr(
                 """<p>ReStructuredText preview requires the"""
@@ -574,86 +620,99 @@
                 """ <a href="http://pypi.python.org/pypi/Sphinx">"""
                 """this page.</a></p>"""
                 """<p>Alternatively you may disable Sphinx usage"""
-                """ on the Editor, Filehandling configuration page.</p>""")
-        
+                """ on the Editor, Filehandling configuration page.</p>"""
+            )
+
         srcTempDir = tempfile.mkdtemp(prefix="eric-rest-src-")
         outTempDir = tempfile.mkdtemp(prefix="eric-rest-out-")
         doctreeTempDir = tempfile.mkdtemp(prefix="eric-rest-doctree-")
         try:
-            filename = 'sphinx_preview'
+            filename = "sphinx_preview"
             basePath = os.path.join(srcTempDir, filename)
-            with open(basePath + '.rst', 'w', encoding='utf-8') as fh:
+            with open(basePath + ".rst", "w", encoding="utf-8") as fh:
                 fh.write(text)
-            
-            overrides = {'html_add_permalinks': False,
-                         'html_copy_source': False,
-                         'html_title': 'Sphinx preview',
-                         'html_use_index': False,
-                         'html_use_modindex': False,
-                         'html_use_smartypants': True,
-                         'master_doc': filename}
-            app = Sphinx(srcdir=srcTempDir, confdir=None, outdir=outTempDir,
-                         doctreedir=doctreeTempDir, buildername='html',
-                         confoverrides=overrides, status=None,
-                         warning=io.StringIO())
+
+            overrides = {
+                "html_add_permalinks": False,
+                "html_copy_source": False,
+                "html_title": "Sphinx preview",
+                "html_use_index": False,
+                "html_use_modindex": False,
+                "html_use_smartypants": True,
+                "master_doc": filename,
+            }
+            app = Sphinx(
+                srcdir=srcTempDir,
+                confdir=None,
+                outdir=outTempDir,
+                doctreedir=doctreeTempDir,
+                buildername="html",
+                confoverrides=overrides,
+                status=None,
+                warning=io.StringIO(),
+            )
             app.build(force_all=True, filenames=None)
 
             basePath = os.path.join(outTempDir, filename)
-            with open(basePath + '.html', 'r', encoding='utf-8') as fh:
+            with open(basePath + ".html", "r", encoding="utf-8") as fh:
                 html = fh.read()
         finally:
             shutil.rmtree(srcTempDir)
             shutil.rmtree(outTempDir)
             shutil.rmtree(doctreeTempDir)
-        
+
         return html
-    
+
     def __convertReSTDocutils(self, text, htmlFormat):
         """
         Private method to convert ReST text into HTML using 'docutils'.
-        
+
         @param text text to be processed (string)
         @param htmlFormat HTML format to be generated (string)
         @return processed HTML (string)
         """
-        if 'sphinx' in sys.modules:
+        if "sphinx" in sys.modules:
             # Make sure any Sphinx polution of docutils has been removed.
-            unloadKeys = [k for k in sys.modules.keys()
-                          if k.startswith(('docutils', 'sphinx'))]
+            unloadKeys = [
+                k for k in sys.modules.keys() if k.startswith(("docutils", "sphinx"))
+            ]
             for key in unloadKeys:
                 sys.modules.pop(key)
-        
+
         try:
-            import docutils.core    # __IGNORE_EXCEPTION__
-            import docutils.utils   # __IGNORE_EXCEPTION__
+            import docutils.core  # __IGNORE_EXCEPTION__
+            import docutils.utils  # __IGNORE_EXCEPTION__
         except ImportError:
             return self.tr(
                 """<p>ReStructuredText preview requires the"""
                 """ <b>python-docutils</b> package.<br/>Install it with"""
                 """ your package manager, 'pip install docutils' or see"""
                 """ <a href="http://pypi.python.org/pypi/docutils">"""
-                """this page.</a></p>""")
-        
+                """this page.</a></p>"""
+            )
+
         # redirect sys.stderr because we are not interested in it here
         origStderr = sys.stderr
         sys.stderr = io.StringIO()
         try:
             html = docutils.core.publish_string(
-                text, writer_name=htmlFormat.lower()).decode("utf-8")
+                text, writer_name=htmlFormat.lower()
+            ).decode("utf-8")
         except docutils.utils.SystemMessage as err:
             errStr = str(err).split(":")[-1].replace("\n", "<br/>")
-            return self.tr(
-                """<p>Docutils returned an error:</p><p>{0}</p>"""
-            ).format(errStr)
-        
+            return self.tr("""<p>Docutils returned an error:</p><p>{0}</p>""").format(
+                errStr
+            )
+
         sys.stderr = origStderr
         return html
-    
-    def __convertMarkdown(self, text, convertNewLineToBreak,
-                          usePyMdownExtensions, htmlFormat):
+
+    def __convertMarkdown(
+        self, text, convertNewLineToBreak, usePyMdownExtensions, htmlFormat
+    ):
         """
         Private method to convert Markdown text into HTML.
-        
+
         @param text text to be processed
         @type str
         @param convertNewLineToBreak flag indicating to convert new lines
@@ -668,53 +727,59 @@
         @rtype str
         """
         try:
-            import markdown     # __IGNORE_EXCEPTION__
+            import markdown  # __IGNORE_EXCEPTION__
         except ImportError:
             return self.tr(
                 """<p>Markdown preview requires the <b>Markdown</b> """
                 """package.<br/>Install it with your package manager,"""
                 """ 'pip install Markdown' or see """
                 """<a href="http://pythonhosted.org/Markdown/install.html">"""
-                """installation instructions.</a></p>""")
-        
+                """installation instructions.</a></p>"""
+            )
+
         from . import PreviewerHTMLStyles
         from . import MarkdownExtensions
-        
+
         extensions = []
-        
+
         mermaidNeeded = False
-        if (
-            Preferences.getEditor("PreviewMarkdownMermaid") and
-            MarkdownExtensions.MermaidRegexFullText.search(text)
-        ):
+        if Preferences.getEditor(
+            "PreviewMarkdownMermaid"
+        ) and MarkdownExtensions.MermaidRegexFullText.search(text):
             extensions.append(MarkdownExtensions.MermaidExtension())
             mermaidNeeded = True
-        
+
         if convertNewLineToBreak:
-            extensions.append('nl2br')
-        
+            extensions.append("nl2br")
+
         pyMdown = False
         if usePyMdownExtensions:
             with contextlib.suppress(ImportError):
-                import pymdownx     # __IGNORE_EXCEPTION__ __IGNORE_WARNING__
+                import pymdownx  # __IGNORE_EXCEPTION__ __IGNORE_WARNING__
+
                 # PyPI package is 'pymdown-extensions'
-                
-                extensions.extend([
-                    'toc',
-                    'pymdownx.extra', 'pymdownx.caret', 'pymdownx.emoji',
-                    'pymdownx.mark', 'pymdownx.tilde', 'pymdownx.keys',
-                    'pymdownx.tasklist', 'pymdownx.smartsymbols',
-                ])
+
+                extensions.extend(
+                    [
+                        "toc",
+                        "pymdownx.extra",
+                        "pymdownx.caret",
+                        "pymdownx.emoji",
+                        "pymdownx.mark",
+                        "pymdownx.tilde",
+                        "pymdownx.keys",
+                        "pymdownx.tasklist",
+                        "pymdownx.smartsymbols",
+                    ]
+                )
                 pyMdown = True
-        
+
         if not pyMdown:
-            extensions.extend(['extra', 'toc'])
-            
+            extensions.extend(["extra", "toc"])
+
             # version 2.0 supports only extension names, not instances
-            if (
-                markdown.version_info[0] > 2 or
-                (markdown.version_info[0] == 2 and
-                 markdown.version_info[1] > 0)
+            if markdown.version_info[0] > 2 or (
+                markdown.version_info[0] == 2 and markdown.version_info[1] > 0
             ):
                 extensions.append(MarkdownExtensions.SimplePatternExtension())
 
@@ -727,15 +792,14 @@
             )
             # prepare text for mathjax
             text = (
-                text
-                .replace(r"\(", r"\\(")
+                text.replace(r"\(", r"\\(")
                 .replace(r"\)", r"\\)")
                 .replace(r"\[", r"\\[")
                 .replace(r"\]", r"\\]")
             )
         else:
             mathjax = ""
-        
+
         if mermaidNeeded:
             mermaid = (
                 "<script type='text/javascript' id='Mermaid-script'"
@@ -759,47 +823,49 @@
         else:
             mermaid = ""
             mermaid_initialize = ""
-        
+
         htmlFormat = Preferences.getEditor("PreviewMarkdownHTMLFormat").lower()
-        body = markdown.markdown(text, extensions=extensions,
-                                 output_format=htmlFormat.lower())
+        body = markdown.markdown(
+            text, extensions=extensions, output_format=htmlFormat.lower()
+        )
         style = (
-            (PreviewerHTMLStyles.css_markdown_dark +
-             PreviewerHTMLStyles.css_pygments_dark)
-            if ericApp().usesDarkPalette() else
-            (PreviewerHTMLStyles.css_markdown_light +
-             PreviewerHTMLStyles.css_pygments_light)
+            (
+                PreviewerHTMLStyles.css_markdown_dark
+                + PreviewerHTMLStyles.css_pygments_dark
+            )
+            if ericApp().usesDarkPalette()
+            else (
+                PreviewerHTMLStyles.css_markdown_light
+                + PreviewerHTMLStyles.css_pygments_light
+            )
         )
-        
+
         if htmlFormat == "xhtml1":
             head = (
-                '''<!DOCTYPE html PUBLIC "-//W3C//DTD'''
-                ''' XHTML 1.0 Transitional//EN"\n'''
-                ''' "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional'''
-                '''.dtd">\n'''
-                '''<html xmlns="http://www.w3.org/1999/xhtml">\n'''
+                """<!DOCTYPE html PUBLIC "-//W3C//DTD"""
+                """ XHTML 1.0 Transitional//EN"\n"""
+                """ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional"""
+                """.dtd">\n"""
+                """<html xmlns="http://www.w3.org/1999/xhtml">\n"""
             )
         elif htmlFormat == "html5":
-            head = (
-                '''<!DOCTYPE html>\n'''
-                '''<html lang="EN">\n'''
-            )
+            head = """<!DOCTYPE html>\n""" """<html lang="EN">\n"""
         else:
             head = '<html lang="EN">\n'
-        head += '''<head>\n'''
+        head += """<head>\n"""
         head += (
-            '''<meta name="Generator" content="eric" />\n'''
-            '''<meta http-equiv="Content-Type" '''
-            '''content="text/html; charset=utf-8" />\n'''
-            '''{0}'''
-            '''{1}'''
-            '''<style type="text/css">'''
-            '''{2}'''
-            '''</style>\n'''
-            '''</head>\n'''
-            '''<body>\n'''
+            """<meta name="Generator" content="eric" />\n"""
+            """<meta http-equiv="Content-Type" """
+            """content="text/html; charset=utf-8" />\n"""
+            """{0}"""
+            """{1}"""
+            """<style type="text/css">"""
+            """{2}"""
+            """</style>\n"""
+            """</head>\n"""
+            """<body>\n"""
         ).format(mathjax, mermaid, style)
-        
-        foot = '''\n</body>\n</html>\n'''
-        
+
+        foot = """\n</body>\n</html>\n"""
+
         return head + body + mermaid_initialize + foot

eric ide

mercurial