UI/CodeDocumentationViewer.py

changeset 5922
4ee909600092
parent 5919
d0de2b378b24
child 5923
1eb08ac3b848
equal deleted inserted replaced
5921:d4797da58218 5922:4ee909600092
209 209
210 self.__noDocumentationString = self.tr("No documentation available") 210 self.__noDocumentationString = self.tr("No documentation available")
211 self.__disabledString = self.tr( 211 self.__disabledString = self.tr(
212 "No source code documentation provider has been registered or" 212 "No source code documentation provider has been registered or"
213 " this function has been disabled.") 213 " this function has been disabled.")
214
215 self.__processingThread = DocumentProcessingThread()
216 self.__processingThread.htmlReady.connect(self.__setHtml)
217 self.__processingThread.warning.connect(self.__setHtmlWarning)
218 214
219 def __setupUi(self): 215 def __setupUi(self):
220 """ 216 """
221 Private method to generate the UI layout. 217 Private method to generate the UI layout.
222 """ 218 """
310 else: 306 else:
311 index = 0 307 index = 0
312 provider = self.__disabledProvider 308 provider = self.__disabledProvider
313 self.providerComboBox.setCurrentIndex(index) 309 self.providerComboBox.setCurrentIndex(index)
314 self.__selectedProvider = provider 310 self.__selectedProvider = provider
311 if index == 0:
312 self.documentationReady(self.__disabledString, isWarning=True)
315 313
316 # TODO: document this hook in the plug-in document 314 # TODO: document this hook in the plug-in document
317 def registerProvider(self, providerName, providerDisplay, provider, 315 def registerProvider(self, providerName, providerDisplay, provider,
318 supported): 316 supported):
319 """ 317 """
431 @param isDocWarning flag indicating a documentation warning page 429 @param isDocWarning flag indicating a documentation warning page
432 @type bool 430 @type bool
433 """ 431 """
434 self.__ui.activateCodeDocumentationViewer(switchFocus=False) 432 self.__ui.activateCodeDocumentationViewer(switchFocus=False)
435 433
436 self.__lastDocumentation = documentationInfo 434 if not isWarning and not isDocWarning:
437 435 self.__lastDocumentation = documentationInfo
438 if documentationInfo is not None: 436
439 if not documentationInfo: 437 if not documentationInfo:
440 if self.__selectedProvider == self.__disabledProvider: 438 if self.__selectedProvider == self.__disabledProvider:
441 self.documentationReady(self.__disabledString, 439 self.documentationReady(self.__disabledString,
442 isWarning=True) 440 isWarning=True)
443 else: 441 else:
444 self.documentationReady(self.__noDocumentationString, 442 self.documentationReady(self.__noDocumentationString,
445 isDocWarning=True) 443 isDocWarning=True)
446 return 444 else:
447
448 if self.__showMarkdown: 445 if self.__showMarkdown:
449 if isWarning: 446 if isWarning:
450 html = prepareDocumentationViewerHtmlWarningDocument( 447 html = prepareDocumentationViewerHtmlWarningDocument(
451 documentationInfo) 448 documentationInfo)
452 elif isDocWarning: 449 elif isDocWarning:
454 documentationInfo) 451 documentationInfo)
455 elif isinstance(documentationInfo, dict): 452 elif isinstance(documentationInfo, dict):
456 html = prepareDocumentationViewerHtmlDocument( 453 html = prepareDocumentationViewerHtmlDocument(
457 documentationInfo) 454 documentationInfo)
458 else: 455 else:
459 html = "" 456 html = documentationInfo
460 if html: 457 self.__setHtml(html)
461 self.__setHtml(html) 458 else:
462 return 459 if isinstance(documentationInfo, str):
463 460 fullText = documentationInfo
464 if isinstance(documentationInfo, str): 461 elif isinstance(documentationInfo, dict):
465 fullText = documentationInfo 462 name = documentationInfo["name"]
466 elif isinstance(documentationInfo, dict): 463 if name:
467 name = documentationInfo["name"] 464 title = "".join([name, "\n",
468 if name: 465 "=" * len(name), "\n\n"])
469 title = "".join([name, "\n", 466 else:
470 "=" * len(name), "\n\n"]) 467 title = ""
471 else: 468
472 title = "" 469 if documentationInfo["argspec"]:
473 470 definition = self.tr("Definition: {0}{1}\n").format(
474 if documentationInfo["argspec"]: 471 name, documentationInfo["argspec"])
475 definition = self.tr("Definition: {0}{1}\n").format( 472 else:
476 name, documentationInfo["argspec"]) 473 definition = ''
477 else: 474
478 definition = '' 475 if documentationInfo["note"]:
479 476 note = self.tr("Info: {0}\n\n----\n\n").format(
480 if documentationInfo["note"]: 477 documentationInfo["note"])
481 note = self.tr("Info: {0}\n\n----\n\n").format( 478 else:
482 documentationInfo["note"]) 479 note = ""
483 else: 480
484 note = "" 481 if documentationInfo["docstring"] is None:
482 docString = ""
483 else:
484 docString = documentationInfo["docstring"]
485
486 fullText = "".join([title, definition, note, docString])
485 487
486 if documentationInfo["docstring"] is None:
487 docString = ""
488 else:
489 docString = documentationInfo["docstring"]
490
491 fullText = "".join([title, definition, note, docString])
492
493 if self.__showMarkdown:
494 self.__processingThread.process("markdown", fullText)
495 else:
496 self.__plainTextViewer.setText(fullText) 488 self.__plainTextViewer.setText(fullText)
497 489
498 def __setHtml(self, html): 490 def __setHtml(self, html):
499 """ 491 """
500 Private slot to set the prepared HTML text. 492 Private slot to set the prepared HTML text.
577 self.__richTextAct.setChecked(richText) 569 self.__richTextAct.setChecked(richText)
578 570
579 self.documentationReady(self.__lastDocumentation) 571 self.documentationReady(self.__lastDocumentation)
580 572
581 Preferences.setDocuViewer("ShowInfoAsRichText", richText) 573 Preferences.setDocuViewer("ShowInfoAsRichText", richText)
582
583
584 class DocumentProcessingThread(QThread):
585 """
586 Class implementing a thread to process some text into HTML usable by the
587 viewer.
588
589 @signal htmlReady(str) emitted with the processed HTML to signal the
590 availability of the processed HTML
591 @signal warning(str) emitted with an error or warning message
592 """
593 htmlReady = pyqtSignal(str)
594 warning = pyqtSignal(str)
595
596 def __init__(self, parent=None):
597 """
598 Constructor
599
600 @param parent reference to the parent object (QObject)
601 """
602 super(DocumentProcessingThread, self).__init__()
603
604 def process(self, language, text):
605 """
606 Public method to convert the given text to HTML.
607
608 @param language language of the text
609 @type str
610 @param text text to be processed
611 @type str
612 """
613 if self.wait():
614 self.__language = language
615 self.__text = text
616 self.start()
617
618 def run(self):
619 """
620 Public thread method to convert the stored data.
621 """
622 language = self.__language
623 text = self.__text
624
625 if language == "markdown":
626 html = self.__convertMarkdown(text, True, "html5")
627 self.htmlReady.emit(html)
628 else:
629 html = self.tr("Format <b>{0}</b> is not supported.")\
630 .format(language)
631 self.warning.emit(html)
632
633 def __convertMarkdown(self, text, convertNewLineToBreak, htmlFormat):
634 """
635 Private method to convert Markdown text into HTML.
636
637 @param text text to be processed
638 @type str
639 @param convertNewLineToBreak flag indicating to convert new lines
640 to HTML break
641 @type bool
642 @param htmlFormat HTML format to be generated by markdown
643 @type str
644 @return processed HTML
645 @rtype str
646 """
647 try:
648 import markdown # __IGNORE_EXCEPTION__
649 except ImportError:
650 return self.tr(
651 """<p>Markdown view requires the <b>Markdown</b> """
652 """package.<br/>Install it with your package manager,"""
653 """ 'pip install Markdown' or see """
654 """<a href="http://pythonhosted.org/Markdown/install.html">"""
655 """installation instructions.</a></p>""")
656
657 try:
658 import mdx_mathjax # __IGNORE_EXCEPTION__ __IGNORE_WARNING__
659 except ImportError:
660 # mathjax doesn't require import statement if installed
661 # as extension
662 pass
663
664 if convertNewLineToBreak:
665 extensions = ['fenced_code', 'nl2br', 'extra']
666 else:
667 extensions = ['fenced_code', 'extra']
668
669 # version 2.0 supports only extension names, not instances
670 if markdown.version_info[0] > 2 or \
671 (markdown.version_info[0] == 2 and
672 markdown.version_info[1] > 0):
673 class _StrikeThroughExtension(markdown.Extension):
674 """
675 Class is placed here, because it depends on imported markdown,
676 and markdown import is lazy.
677
678 (see https://pythonhosted.org/Markdown/extensions/api.html
679 this page for details)
680 """
681 DEL_RE = r'(~~)(.*?)~~'
682
683 def extendMarkdown(self, md, md_globals):
684 # Create the del pattern
685 del_tag = markdown.inlinepatterns.SimpleTagPattern(
686 self.DEL_RE, 'del')
687 # Insert del pattern into markdown parser
688 md.inlinePatterns.add('del', del_tag, '>not_strong')
689
690 extensions.append(_StrikeThroughExtension())
691
692 try:
693 return markdown.markdown(text, extensions=extensions + ['mathjax'],
694 output_format=htmlFormat.lower())
695 except (ImportError, ValueError):
696 # markdown raises ValueError or ImportError, depends on version
697 # It is not clear, how to distinguish missing mathjax from other
698 # errors. So keep going without mathjax.
699 return markdown.markdown(text, extensions=extensions,
700 output_format=htmlFormat.lower())

eric ide

mercurial