209 self.__processingThread.process( |
209 self.__processingThread.process( |
210 fn, language, editor.text(), |
210 fn, language, editor.text(), |
211 self.ssiCheckBox.isChecked(), rootPath, |
211 self.ssiCheckBox.isChecked(), rootPath, |
212 Preferences.getEditor("PreviewRestUseSphinx"), |
212 Preferences.getEditor("PreviewRestUseSphinx"), |
213 Preferences.getEditor("PreviewMarkdownNLtoBR"), |
213 Preferences.getEditor("PreviewMarkdownNLtoBR"), |
|
214 Preferences.getEditor( |
|
215 "PreviewMarkdownUsePyMdownExtensions"), |
214 Preferences.getEditor("PreviewMarkdownHTMLFormat"), |
216 Preferences.getEditor("PreviewMarkdownHTMLFormat"), |
215 Preferences.getEditor("PreviewRestDocutilsHTMLFormat")) |
217 Preferences.getEditor("PreviewRestDocutilsHTMLFormat")) |
216 |
218 |
217 def __setHtml(self, filePath, html, rootPath): |
219 def __setHtml(self, filePath, html, rootPath): |
218 """ |
220 """ |
336 super(PreviewProcessingThread, self).__init__() |
338 super(PreviewProcessingThread, self).__init__() |
337 |
339 |
338 self.__lock = threading.Lock() |
340 self.__lock = threading.Lock() |
339 |
341 |
340 def process(self, filePath, language, text, ssiEnabled, rootPath, |
342 def process(self, filePath, language, text, ssiEnabled, rootPath, |
341 useSphinx, convertNewLineToBreak, markdownHtmlFormat, |
343 useSphinx, convertNewLineToBreak, usePyMdownExtensions, |
342 restDocutilsHtmlFormat): |
344 markdownHtmlFormat, restDocutilsHtmlFormat): |
343 """ |
345 """ |
344 Public method to convert the given text to HTML. |
346 Public method to convert the given text to HTML. |
345 |
347 |
346 @param filePath file path of the text (string) |
348 @param filePath file path of the text |
347 @param language language of the text (string) |
349 @type str |
348 @param text text to be processed (string) |
350 @param language language of the text |
|
351 @type str |
|
352 @param text text to be processed |
|
353 @type str |
349 @param ssiEnabled flag indicating to do some (limited) SSI processing |
354 @param ssiEnabled flag indicating to do some (limited) SSI processing |
350 (boolean) |
355 @type bool |
351 @param rootPath root path to be used for SSI processing (str) |
356 @param rootPath root path to be used for SSI processing |
|
357 @type str |
352 @param useSphinx flag indicating to use Sphinx to generate the |
358 @param useSphinx flag indicating to use Sphinx to generate the |
353 ReST preview (boolean) |
359 ReST preview |
|
360 @type bool |
354 @param convertNewLineToBreak flag indicating to convert new lines |
361 @param convertNewLineToBreak flag indicating to convert new lines |
355 to HTML break (Markdown only) (boolean) |
362 to HTML break (Markdown only) |
|
363 @type bool |
|
364 @param usePyMdownExtensions flag indicating to enable the PyMdown |
|
365 extensions, if they are available |
|
366 @type bool |
356 @param markdownHtmlFormat HTML format to be generated by markdown |
367 @param markdownHtmlFormat HTML format to be generated by markdown |
357 (string) |
368 @type str |
358 @param restDocutilsHtmlFormat HTML format to be generated by docutils |
369 @param restDocutilsHtmlFormat HTML format to be generated by docutils |
359 (string) |
370 @type str |
360 """ |
371 """ |
361 with self.__lock: |
372 with self.__lock: |
362 self.__filePath = filePath |
373 self.__filePath = filePath |
363 self.__language = language |
374 self.__language = language |
364 self.__text = text |
375 self.__text = text |
365 self.__ssiEnabled = ssiEnabled |
376 self.__ssiEnabled = ssiEnabled |
366 self.__rootPath = rootPath |
377 self.__rootPath = rootPath |
367 self.__haveData = True |
378 self.__haveData = True |
368 self.__useSphinx = useSphinx |
379 self.__useSphinx = useSphinx |
369 self.__convertNewLineToBreak = convertNewLineToBreak |
380 self.__convertNewLineToBreak = convertNewLineToBreak |
|
381 self.__usePyMdownExtensions = usePyMdownExtensions |
370 self.__markdownHtmlFormat = markdownHtmlFormat |
382 self.__markdownHtmlFormat = markdownHtmlFormat |
371 self.__restDocutilsHtmlFormat = restDocutilsHtmlFormat |
383 self.__restDocutilsHtmlFormat = restDocutilsHtmlFormat |
372 if not self.isRunning(): |
384 if not self.isRunning(): |
373 self.start(QThread.LowPriority) |
385 self.start(QThread.LowPriority) |
374 |
386 |
384 text = self.__text |
396 text = self.__text |
385 ssiEnabled = self.__ssiEnabled |
397 ssiEnabled = self.__ssiEnabled |
386 rootPath = self.__rootPath |
398 rootPath = self.__rootPath |
387 useSphinx = self.__useSphinx |
399 useSphinx = self.__useSphinx |
388 convertNewLineToBreak = self.__convertNewLineToBreak |
400 convertNewLineToBreak = self.__convertNewLineToBreak |
|
401 usePyMdownExtensions = self.__usePyMdownExtensions |
389 markdownHtmlFormat = self.__markdownHtmlFormat |
402 markdownHtmlFormat = self.__markdownHtmlFormat |
390 restDocutilsHtmlFormat = self.__restDocutilsHtmlFormat |
403 restDocutilsHtmlFormat = self.__restDocutilsHtmlFormat |
391 |
404 |
392 self.__haveData = False |
405 self.__haveData = False |
393 |
406 |
394 html = self.__getHtml(language, text, ssiEnabled, filePath, |
407 html = self.__getHtml(language, text, ssiEnabled, filePath, |
395 rootPath, useSphinx, convertNewLineToBreak, |
408 rootPath, useSphinx, convertNewLineToBreak, |
396 markdownHtmlFormat, restDocutilsHtmlFormat) |
409 usePyMdownExtensions, markdownHtmlFormat, |
|
410 restDocutilsHtmlFormat) |
397 |
411 |
398 with self.__lock: |
412 with self.__lock: |
399 if not self.__haveData: |
413 if not self.__haveData: |
400 self.htmlReady.emit(filePath, html, rootPath) |
414 self.htmlReady.emit(filePath, html, rootPath) |
401 break |
415 break |
402 # else - next iteration |
416 # else - next iteration |
403 |
417 |
404 def __getHtml(self, language, text, ssiEnabled, filePath, rootPath, |
418 def __getHtml(self, language, text, ssiEnabled, filePath, rootPath, |
405 useSphinx, convertNewLineToBreak, markdownHtmlFormat, |
419 useSphinx, convertNewLineToBreak, usePyMdownExtensions, |
406 restDocutilsHtmlFormat): |
420 markdownHtmlFormat, restDocutilsHtmlFormat): |
407 """ |
421 """ |
408 Private method to process the given text depending upon the given |
422 Private method to process the given text depending upon the given |
409 language. |
423 language. |
410 |
424 |
411 @param language language of the text (string) |
425 @param language language of the text |
412 @param text to be processed (string) |
426 @type str |
|
427 @param text to be processed |
|
428 @type str |
413 @param ssiEnabled flag indicating to do some (limited) SSI processing |
429 @param ssiEnabled flag indicating to do some (limited) SSI processing |
414 (boolean) |
430 @type bool |
415 @param filePath file path of the text (string) |
431 @param filePath file path of the text |
416 @param rootPath root path to be used for SSI processing (str) |
432 @type str |
|
433 @param rootPath root path to be used for SSI processing |
|
434 @type str |
417 @param useSphinx flag indicating to use Sphinx to generate the |
435 @param useSphinx flag indicating to use Sphinx to generate the |
418 ReST preview (boolean) |
436 ReST preview |
|
437 @type bool |
419 @param convertNewLineToBreak flag indicating to convert new lines |
438 @param convertNewLineToBreak flag indicating to convert new lines |
420 to HTML break (Markdown only) (boolean) |
439 to HTML break (Markdown only) |
|
440 @type bool |
|
441 @param usePyMdownExtensions flag indicating to enable the PyMdown |
|
442 extensions, if they are available |
|
443 @type bool |
421 @param markdownHtmlFormat HTML format to be generated by markdown |
444 @param markdownHtmlFormat HTML format to be generated by markdown |
422 (string) |
445 @type str |
423 @param restDocutilsHtmlFormat HTML format to be generated by docutils |
446 @param restDocutilsHtmlFormat HTML format to be generated by docutils |
424 (string) |
447 @type str |
425 @return processed HTML text (string) |
448 @return processed HTML text |
|
449 @rtype str |
426 """ |
450 """ |
427 if language == "HTML": |
451 if language == "HTML": |
428 if ssiEnabled: |
452 if ssiEnabled: |
429 html = self.__processSSI(text, filePath, rootPath) |
453 html = self.__processSSI(text, filePath, rootPath) |
430 else: |
454 else: |
431 html = text |
455 html = text |
432 return self.__processRootPath(html, rootPath) |
456 return self.__processRootPath(html, rootPath) |
433 elif language == "Markdown": |
457 elif language == "Markdown": |
434 return self.__convertMarkdown(text, convertNewLineToBreak, |
458 return self.__convertMarkdown( |
435 markdownHtmlFormat) |
459 text, convertNewLineToBreak, usePyMdownExtensions, |
|
460 markdownHtmlFormat) |
436 elif language == "ReST": |
461 elif language == "ReST": |
437 return self.__convertReST(text, useSphinx, restDocutilsHtmlFormat) |
462 return self.__convertReST(text, useSphinx, restDocutilsHtmlFormat) |
438 else: |
463 else: |
439 return self.tr( |
464 return self.tr( |
440 "<p>No preview available for this type of file.</p>") |
465 "<p>No preview available for this type of file.</p>") |
623 ).format(errStr) |
648 ).format(errStr) |
624 |
649 |
625 sys.stderr = origStderr |
650 sys.stderr = origStderr |
626 return html |
651 return html |
627 |
652 |
628 def __convertMarkdown(self, text, convertNewLineToBreak, htmlFormat): |
653 def __convertMarkdown(self, text, convertNewLineToBreak, |
|
654 usePyMdownExtensions, htmlFormat): |
629 """ |
655 """ |
630 Private method to convert Markdown text into HTML. |
656 Private method to convert Markdown text into HTML. |
631 |
657 |
632 @param text text to be processed (string) |
658 @param text text to be processed |
|
659 @type str |
633 @param convertNewLineToBreak flag indicating to convert new lines |
660 @param convertNewLineToBreak flag indicating to convert new lines |
634 to HTML break (Markdown only) (boolean) |
661 to HTML break (Markdown only) |
635 @param htmlFormat HTML format to be generated by markdown (string) |
662 @type bool |
636 @return processed HTML (string) |
663 @param usePyMdownExtensions flag indicating to enable the PyMdown |
|
664 extensions, if they are available |
|
665 @type bool |
|
666 @param htmlFormat HTML format to be generated by markdown |
|
667 @type str |
|
668 @return processed HTML |
|
669 @rtype str |
637 """ |
670 """ |
638 try: |
671 try: |
639 import markdown # __IGNORE_EXCEPTION__ |
672 import markdown # __IGNORE_EXCEPTION__ |
640 except ImportError: |
673 except ImportError: |
641 return self.tr( |
674 return self.tr( |
650 except ImportError: |
683 except ImportError: |
651 # mathjax doesn't require import statement if installed |
684 # mathjax doesn't require import statement if installed |
652 # as extension |
685 # as extension |
653 pass |
686 pass |
654 |
687 |
655 if convertNewLineToBreak: |
688 extensions = [] |
656 extensions = ['fenced_code', 'nl2br', 'extra'] |
689 if usePyMdownExtensions: |
657 else: |
690 try: |
658 extensions = ['fenced_code', 'extra'] |
691 import pymdownx # __IGNORE_EXCEPTION__ __IGNORE_WARNING__ |
659 |
692 # PyPI package is 'pymdown-extensions' |
660 # version 2.0 supports only extension names, not instances |
|
661 if ( |
|
662 markdown.version_info[0] > 2 or |
|
663 (markdown.version_info[0] == 2 and |
|
664 markdown.version_info[1] > 0) |
|
665 ): |
|
666 class _StrikeThroughExtension(markdown.Extension): |
|
667 """ |
|
668 Class is placed here, because it depends on imported markdown, |
|
669 and markdown import is lazy. |
|
670 |
693 |
671 (see https://pythonhosted.org/Markdown/extensions/api.html |
694 extensions = [ |
672 this page for details) |
695 'pymdownx.extra', 'pymdownx.caret', 'pymdownx.emoji', |
673 """ |
696 'pymdownx.mark', 'pymdownx.tilde', 'pymdownx.keys', |
674 DEL_RE = r'(~~)(.*?)~~' |
697 'pymdownx.tasklist', 'pymdownx.smartsymbols', |
675 |
698 ] |
676 def extendMarkdown(self, md, md_globals): |
699 if convertNewLineToBreak: |
677 # Create the del pattern |
700 extensions.append('nl2br') |
678 del_tag = markdown.inlinepatterns.SimpleTagPattern( |
701 except ImportError: |
679 self.DEL_RE, 'del') |
702 pass |
680 # Insert del pattern into markdown parser |
703 if not extensions: |
681 md.inlinePatterns.add('del', del_tag, '>not_strong') |
704 if convertNewLineToBreak: |
|
705 extensions = ['extra', 'toc', 'nl2br'] |
|
706 else: |
|
707 extensions = ['extra', 'toc'] |
682 |
708 |
683 extensions.append(_StrikeThroughExtension()) |
709 # version 2.0 supports only extension names, not instances |
684 |
710 if ( |
685 try: |
711 markdown.version_info[0] > 2 or |
686 return markdown.markdown(text, extensions=extensions + ['mathjax'], |
712 (markdown.version_info[0] == 2 and |
687 output_format=htmlFormat.lower()) |
713 markdown.version_info[1] > 0) |
|
714 ): |
|
715 class _TildeExtension(markdown.Extension): |
|
716 """ |
|
717 Class is placed here, because it depends on imported |
|
718 markdown, and markdown import is lazy. |
|
719 |
|
720 (see https://pythonhosted.org/Markdown/extensions/api.html |
|
721 this page for details) |
|
722 """ |
|
723 DEL_RE = r'(~~)(.+?)~~' |
|
724 SUB_RE = r'(~)(.+?)~' |
|
725 |
|
726 def extendMarkdown(self, md, md_globals): |
|
727 # Create the sub pattern and insert it into markdown |
|
728 # parser |
|
729 sub_tag = markdown.inlinepatterns.SimpleTagPattern( |
|
730 self.SUB_RE, 'sub') |
|
731 md.inlinePatterns.add('sub', sub_tag, '>not_strong') |
|
732 |
|
733 # Create the del pattern and insert it into markdown |
|
734 # parser |
|
735 del_tag = markdown.inlinepatterns.SimpleTagPattern( |
|
736 self.DEL_RE, 'del') |
|
737 md.inlinePatterns.add('del', del_tag, '>not_strong') |
|
738 |
|
739 class _CaretExtension(markdown.Extension): |
|
740 """ |
|
741 Class is placed here, because it depends on imported |
|
742 markdown, and markdown import is lazy. |
|
743 |
|
744 (see https://pythonhosted.org/Markdown/extensions/api.html |
|
745 this page for details) |
|
746 """ |
|
747 INS_RE = r'(\^\^)(.*?)\^\^' |
|
748 SUP_RE = r'(\^)(.*?)\^' |
|
749 |
|
750 def extendMarkdown(self, md, md_globals): |
|
751 # Create the sup pattern and insert it into markdown |
|
752 # parser |
|
753 sup_tag = markdown.inlinepatterns.SimpleTagPattern( |
|
754 self.SUP_RE, 'sup') |
|
755 md.inlinePatterns.add('sup', sup_tag, '>not_strong') |
|
756 |
|
757 # Create the ins pattern and insert it into markdown |
|
758 # parser |
|
759 ins_tag = markdown.inlinepatterns.SimpleTagPattern( |
|
760 self.INS_RE, 'ins') |
|
761 md.inlinePatterns.add('ins', ins_tag, '>not_strong') |
|
762 |
|
763 class _MarkExtension(markdown.Extension): |
|
764 """ |
|
765 Class is placed here, because it depends on imported |
|
766 markdown, and markdown import is lazy. |
|
767 |
|
768 (see https://pythonhosted.org/Markdown/extensions/api.html |
|
769 this page for details) |
|
770 """ |
|
771 MARK_RE = r'(==)(.*?)==' |
|
772 |
|
773 def extendMarkdown(self, md, md_globals): |
|
774 # Create the mark pattern and insert it into markdown |
|
775 # parser |
|
776 mark_tag = markdown.inlinepatterns.SimpleTagPattern( |
|
777 self.MARK_RE, 'mark') |
|
778 md.inlinePatterns.add('mark', mark_tag, '>not_strong') |
|
779 |
|
780 extensions.extend([ |
|
781 _TildeExtension(), _CaretExtension(), _MarkExtension() |
|
782 ]) |
|
783 |
|
784 try: |
|
785 return markdown.markdown( |
|
786 text, extensions=extensions + ['mdx_mathjax'], |
|
787 output_format=htmlFormat.lower()) |
688 except (ImportError, ValueError): |
788 except (ImportError, ValueError): |
689 # markdown raises ValueError or ImportError, depends on version |
789 # markdown raises ValueError or ImportError, depends on version |
690 # It is not clear, how to distinguish missing mathjax from other |
790 # It is not clear, how to distinguish missing mathjax from other |
691 # errors. So keep going without mathjax. |
791 # errors. So keep going without mathjax. |
692 return markdown.markdown(text, extensions=extensions, |
792 return markdown.markdown( |
693 output_format=htmlFormat.lower()) |
793 text, extensions=extensions, |
|
794 output_format=htmlFormat.lower()) |