eric6/UI/Previewers/PreviewerHTML.py

branch
maintenance
changeset 7322
cd8ee889589f
parent 7286
7eb04391adf7
parent 7314
c32c24345ca7
child 7362
028bf21bb5a2
--- a/eric6/UI/Previewers/PreviewerHTML.py	Thu Oct 03 11:12:50 2019 +0200
+++ b/eric6/UI/Previewers/PreviewerHTML.py	Fri Nov 01 16:11:27 2019 +0100
@@ -211,6 +211,8 @@
                     self.ssiCheckBox.isChecked(), rootPath,
                     Preferences.getEditor("PreviewRestUseSphinx"),
                     Preferences.getEditor("PreviewMarkdownNLtoBR"),
+                    Preferences.getEditor(
+                        "PreviewMarkdownUsePyMdownExtensions"),
                     Preferences.getEditor("PreviewMarkdownHTMLFormat"),
                     Preferences.getEditor("PreviewRestDocutilsHTMLFormat"))
 
@@ -338,25 +340,34 @@
         self.__lock = threading.Lock()
     
     def process(self, filePath, language, text, ssiEnabled, rootPath,
-                useSphinx, convertNewLineToBreak, markdownHtmlFormat,
-                restDocutilsHtmlFormat):
+                useSphinx, convertNewLineToBreak, usePyMdownExtensions,
+                markdownHtmlFormat, restDocutilsHtmlFormat):
         """
         Public method to convert the given text to HTML.
         
-        @param filePath file path of the text (string)
-        @param language language of the text (string)
-        @param text text to be processed (string)
+        @param filePath file path of the text
+        @type str
+        @param language language of the text
+        @type str
+        @param text text to be processed
+        @type str
         @param ssiEnabled flag indicating to do some (limited) SSI processing
-            (boolean)
-        @param rootPath root path to be used for SSI processing (str)
+        @type bool
+        @param rootPath root path to be used for SSI processing
+        @type str
         @param useSphinx flag indicating to use Sphinx to generate the
-            ReST preview (boolean)
+            ReST preview
+        @type bool
         @param convertNewLineToBreak flag indicating to convert new lines
-            to HTML break (Markdown only) (boolean)
+            to HTML break (Markdown only)
+        @type bool
+        @param usePyMdownExtensions flag indicating to enable the PyMdown
+            extensions, if they are available
+        @type bool
         @param markdownHtmlFormat HTML format to be generated by markdown
-            (string)
+        @type str
         @param restDocutilsHtmlFormat HTML format to be generated by docutils
-            (string)
+        @type str
         """
         with self.__lock:
             self.__filePath = filePath
@@ -367,6 +378,7 @@
             self.__haveData = True
             self.__useSphinx = useSphinx
             self.__convertNewLineToBreak = convertNewLineToBreak
+            self.__usePyMdownExtensions = usePyMdownExtensions
             self.__markdownHtmlFormat = markdownHtmlFormat
             self.__restDocutilsHtmlFormat = restDocutilsHtmlFormat
             if not self.isRunning():
@@ -386,6 +398,7 @@
                 rootPath = self.__rootPath
                 useSphinx = self.__useSphinx
                 convertNewLineToBreak = self.__convertNewLineToBreak
+                usePyMdownExtensions = self.__usePyMdownExtensions
                 markdownHtmlFormat = self.__markdownHtmlFormat
                 restDocutilsHtmlFormat = self.__restDocutilsHtmlFormat
             
@@ -393,7 +406,8 @@
 
             html = self.__getHtml(language, text, ssiEnabled, filePath,
                                   rootPath, useSphinx, convertNewLineToBreak,
-                                  markdownHtmlFormat, restDocutilsHtmlFormat)
+                                  usePyMdownExtensions, markdownHtmlFormat,
+                                  restDocutilsHtmlFormat)
             
             with self.__lock:
                 if not self.__haveData:
@@ -402,27 +416,37 @@
                 # else - next iteration
     
     def __getHtml(self, language, text, ssiEnabled, filePath, rootPath,
-                  useSphinx, convertNewLineToBreak, markdownHtmlFormat,
-                  restDocutilsHtmlFormat):
+                  useSphinx, convertNewLineToBreak, usePyMdownExtensions,
+                  markdownHtmlFormat, restDocutilsHtmlFormat):
         """
         Private method to process the given text depending upon the given
         language.
         
-        @param language language of the text (string)
-        @param text to be processed (string)
+        @param language language of the text
+        @type str
+        @param text to be processed
+        @type str
         @param ssiEnabled flag indicating to do some (limited) SSI processing
-            (boolean)
-        @param filePath file path of the text (string)
-        @param rootPath root path to be used for SSI processing (str)
+        @type bool
+        @param filePath file path of the text
+        @type str
+        @param rootPath root path to be used for SSI processing
+        @type str
         @param useSphinx flag indicating to use Sphinx to generate the
-            ReST preview (boolean)
+            ReST preview
+        @type bool
         @param convertNewLineToBreak flag indicating to convert new lines
-            to HTML break (Markdown only) (boolean)
+            to HTML break (Markdown only)
+        @type bool
+        @param usePyMdownExtensions flag indicating to enable the PyMdown
+            extensions, if they are available
+        @type bool
         @param markdownHtmlFormat HTML format to be generated by markdown
-            (string)
+        @type str
         @param restDocutilsHtmlFormat HTML format to be generated by docutils
-            (string)
-        @return processed HTML text (string)
+        @type str
+        @return processed HTML text
+        @rtype str
         """
         if language == "HTML":
             if ssiEnabled:
@@ -431,8 +455,9 @@
                 html = text
             return self.__processRootPath(html, rootPath)
         elif language == "Markdown":
-            return self.__convertMarkdown(text, convertNewLineToBreak,
-                                          markdownHtmlFormat)
+            return self.__convertMarkdown(
+                text, convertNewLineToBreak, usePyMdownExtensions,
+                markdownHtmlFormat)
         elif language == "ReST":
             return self.__convertReST(text, useSphinx, restDocutilsHtmlFormat)
         else:
@@ -625,15 +650,23 @@
         sys.stderr = origStderr
         return html
     
-    def __convertMarkdown(self, text, convertNewLineToBreak, htmlFormat):
+    def __convertMarkdown(self, text, convertNewLineToBreak,
+                          usePyMdownExtensions, htmlFormat):
         """
         Private method to convert Markdown text into HTML.
         
-        @param text text to be processed (string)
+        @param text text to be processed
+        @type str
         @param convertNewLineToBreak flag indicating to convert new lines
-            to HTML break (Markdown only) (boolean)
-        @param htmlFormat HTML format to be generated by markdown (string)
-        @return processed HTML (string)
+            to HTML break (Markdown only)
+        @type bool
+        @param usePyMdownExtensions flag indicating to enable the PyMdown
+            extensions, if they are available
+        @type bool
+        @param htmlFormat HTML format to be generated by markdown
+        @type str
+        @return processed HTML
+        @rtype str
         """
         try:
             import markdown     # __IGNORE_EXCEPTION__
@@ -645,49 +678,111 @@
                 """<a href="http://pythonhosted.org/Markdown/install.html">"""
                 """installation instructions.</a></p>""")
         
-        try:
-            import mdx_mathjax  # __IGNORE_EXCEPTION__ __IGNORE_WARNING__
-        except ImportError:
-            # mathjax doesn't require import statement if installed
-            # as extension
-            pass
+        from . import PreviewerHTMLStyles
+        from . import MarkdownExtensions
+        
+        extensions = []
+        
+        mermaidNeeded = False
+        if Preferences.getEditor("PreviewMarkdownMermaid"):
+            if MarkdownExtensions.MermaidRegexFullText.search(text):
+                extensions.append(MarkdownExtensions.MermaidExtension())
+                mermaidNeeded = True
         
         if convertNewLineToBreak:
-            extensions = ['fenced_code', 'nl2br', 'extra']
-        else:
-            extensions = ['fenced_code', 'extra']
+            extensions.append('nl2br')
         
-        # 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)
-        ):
-            class _StrikeThroughExtension(markdown.Extension):
-                """
-                Class is placed here, because it depends on imported markdown,
-                and markdown import is lazy.
+        pyMdown = False
+        if usePyMdownExtensions:
+            try:
+                import pymdownx     # __IGNORE_EXCEPTION__ __IGNORE_WARNING__
+                # PyPI package is 'pymdown-extensions'
                 
-                (see https://pythonhosted.org/Markdown/extensions/api.html
-                this page for details)
-                """
-                DEL_RE = r'(~~)(.*?)~~'
-
-                def extendMarkdown(self, md, md_globals):
-                    # Create the del pattern
-                    del_tag = markdown.inlinepatterns.SimpleTagPattern(
-                        self.DEL_RE, 'del')
-                    # Insert del pattern into markdown parser
-                    md.inlinePatterns.add('del', del_tag, '>not_strong')
+                extensions.extend([
+                    'toc',
+                    'pymdownx.extra', 'pymdownx.caret', 'pymdownx.emoji',
+                    'pymdownx.mark', 'pymdownx.tilde', 'pymdownx.keys',
+                    'pymdownx.tasklist', 'pymdownx.smartsymbols',
+                ])
+                pyMdown = True
+            except ImportError:
+                pass
+        
+        if not pyMdown:
+            extensions.extend(['extra', 'toc'])
             
-            extensions.append(_StrikeThroughExtension())
+            # 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)
+            ):
+                extensions.append(MarkdownExtensions.SimplePatternExtension())
 
-        try:
-            return markdown.markdown(text, extensions=extensions + ['mathjax'],
-                                     output_format=htmlFormat.lower())
-        except (ImportError, ValueError):
-            # markdown raises ValueError or ImportError, depends on version
-            # It is not clear, how to distinguish missing mathjax from other
-            # errors. So keep going without mathjax.
-            return markdown.markdown(text, extensions=extensions,
-                                     output_format=htmlFormat.lower())
+        if Preferences.getEditor("PreviewMarkdownMathJax"):
+            mathjax = (
+                "<script type='text/javascript' id='MathJax-script' async"
+                " src='https://cdn.jsdelivr.net/npm/mathjax@3/es5/"
+                "tex-chtml.js'>\n"
+                "</script>\n"
+            )
+            # prepare text for mathjax
+            text = (
+                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'"
+                " src='https://unpkg.com/mermaid@8/dist/mermaid.min.js'>\n"
+                "</script>\n"
+            )
+        else:
+            mermaid = ""
+        
+        htmlFormat = Preferences.getEditor("PreviewMarkdownHTMLFormat").lower()
+        body = markdown.markdown(text, extensions=extensions,
+                                 output_format=htmlFormat.lower())
+        
+        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'''
+            )
+        elif htmlFormat == "html5":
+            head = (
+                '''<!DOCTYPE html>\n'''
+                '''<html lang="EN">\n'''
+            )
+        else:
+            head = '<html lang="EN">\n'
+        head += '''<head>\n'''
+        head += (
+            '''<meta name="Generator" content="eric6" />\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,
+            PreviewerHTMLStyles.css_markdown +
+            PreviewerHTMLStyles.css_pygments
+        )
+        
+        foot = '''\n</body>\n</html>\n'''
+        
+        return head + body + foot

eric ide

mercurial