diff -r 7622b6330e62 -r 6df711503ec0 eric6/UI/Previewers/PreviewerHTML.py --- a/eric6/UI/Previewers/PreviewerHTML.py Sun Oct 20 11:19:38 2019 +0200 +++ b/eric6/UI/Previewers/PreviewerHTML.py Tue Oct 22 19:56:56 2019 +0200 @@ -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__ @@ -652,42 +685,110 @@ # as extension pass - if convertNewLineToBreak: - extensions = ['fenced_code', 'nl2br', 'extra'] - else: - extensions = ['fenced_code', 'extra'] - - # 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. + extensions = [] + 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'(~~)(.*?)~~' + extensions = [ + 'pymdownx.extra', 'pymdownx.caret', 'pymdownx.emoji', + 'pymdownx.mark', 'pymdownx.tilde', 'pymdownx.keys', + 'pymdownx.tasklist', 'pymdownx.smartsymbols', + ] + if convertNewLineToBreak: + extensions.append('nl2br') + except ImportError: + pass + if not extensions: + if convertNewLineToBreak: + extensions = ['extra', 'toc', 'nl2br'] + else: + extensions = ['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) + ): + class _TildeExtension(markdown.Extension): + """ + Class is placed here, because it depends on imported + markdown, and markdown import is lazy. + + (see https://pythonhosted.org/Markdown/extensions/api.html + this page for details) + """ + DEL_RE = r'(~~)(.+?)~~' + SUB_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.append(_StrikeThroughExtension()) + def extendMarkdown(self, md, md_globals): + # Create the sub pattern and insert it into markdown + # parser + sub_tag = markdown.inlinepatterns.SimpleTagPattern( + self.SUB_RE, 'sub') + md.inlinePatterns.add('sub', sub_tag, '>not_strong') + + # Create the del pattern and insert it into markdown + # parser + del_tag = markdown.inlinepatterns.SimpleTagPattern( + self.DEL_RE, 'del') + md.inlinePatterns.add('del', del_tag, '>not_strong') + + class _CaretExtension(markdown.Extension): + """ + Class is placed here, because it depends on imported + markdown, and markdown import is lazy. + + (see https://pythonhosted.org/Markdown/extensions/api.html + this page for details) + """ + INS_RE = r'(\^\^)(.*?)\^\^' + SUP_RE = r'(\^)(.*?)\^' + + def extendMarkdown(self, md, md_globals): + # Create the sup pattern and insert it into markdown + # parser + sup_tag = markdown.inlinepatterns.SimpleTagPattern( + self.SUP_RE, 'sup') + md.inlinePatterns.add('sup', sup_tag, '>not_strong') + + # Create the ins pattern and insert it into markdown + # parser + ins_tag = markdown.inlinepatterns.SimpleTagPattern( + self.INS_RE, 'ins') + md.inlinePatterns.add('ins', ins_tag, '>not_strong') + + class _MarkExtension(markdown.Extension): + """ + Class is placed here, because it depends on imported + markdown, and markdown import is lazy. + + (see https://pythonhosted.org/Markdown/extensions/api.html + this page for details) + """ + MARK_RE = r'(==)(.*?)==' + + def extendMarkdown(self, md, md_globals): + # Create the mark pattern and insert it into markdown + # parser + mark_tag = markdown.inlinepatterns.SimpleTagPattern( + self.MARK_RE, 'mark') + md.inlinePatterns.add('mark', mark_tag, '>not_strong') + + extensions.extend([ + _TildeExtension(), _CaretExtension(), _MarkExtension() + ]) try: - return markdown.markdown(text, extensions=extensions + ['mathjax'], - output_format=htmlFormat.lower()) + return markdown.markdown( + text, extensions=extensions + ['mdx_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()) + return markdown.markdown( + text, extensions=extensions, + output_format=htmlFormat.lower())