Sat, 14 Oct 2017 20:08:47 +0200
Merged with fix done by Tobias.
--- a/Preferences/__init__.py Fri Oct 13 22:42:35 2017 +0200 +++ b/Preferences/__init__.py Sat Oct 14 20:08:47 2017 +0200 @@ -428,6 +428,9 @@ "CallTipsScintillaOnFail": False, # show QScintilla calltips, if plug-in fails + "ShowInfoOnOpenBracket": True, + # TODO: add to editor configuration page + "AutoCheckSyntax": True, "OnlineSyntaxCheck": True, "OnlineSyntaxCheckInterval": 5,
--- a/QScintilla/Editor.py Fri Oct 13 22:42:35 2017 +0200 +++ b/QScintilla/Editor.py Sat Oct 14 20:08:47 2017 +0200 @@ -261,6 +261,7 @@ self.cursorPositionChanged.connect(self.__cursorPositionChanged) self.modificationAttempted.connect(self.__modificationReadOnly) self.userListActivated.connect(self.__completionListSelected) + self.SCN_CHARADDED.connect(self.__charAddedPermanent) # margins layout if QSCINTILLA_VERSION() >= 0x020301: @@ -4971,6 +4972,23 @@ bool(self.__ctHookFunctions)) ################################################################# + ## Methods needed by the code documentation viewer + ################################################################# + + def __charAddedPermanent(self, charNumber): + """ + Private slot called to handle the user entering a character. + + Note: This slot is always connected independent of the auto-completion + and calltips handling __charAdded() slot. + + @param charNumber value of the character entered (integer) + """ + char = chr(charNumber) + if char == "(" and Preferences.getEditor("ShowInfoOnOpenBracket"): + self.vm.showEditorInfo(self) + + ################################################################# ## Methods needed by the context menu #################################################################
--- a/README-MacOSX.rst Fri Oct 13 22:42:35 2017 +0200 +++ b/README-MacOSX.rst Sat Oct 14 20:08:47 2017 +0200 @@ -179,13 +179,13 @@ :: - port search hunspell-dict + port search hunspell Then install them with a command like this :: - sudo port install hunspell-dict-de_DE + sudo port install hunspell-de_DE hunspell-en_US_large replacing the 'de_DE' part with the language code of your desire.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/UI/CodeDocumentationViewer.py Sat Oct 14 20:08:47 2017 +0200 @@ -0,0 +1,183 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2017 Detlev Offenbach <detlev@die-offenbachs.de> +# + +""" +Module implementing a widget to show some source code information provided by +plug-ins. +""" + +from __future__ import unicode_literals + +from PyQt5.QtCore import pyqtSlot, pyqtSignal +from PyQt5.QtWidgets import QWidget + +from .Ui_CodeDocumentationViewer import Ui_CodeDocumentationViewer + +import Preferences + + +class CodeDocumentationViewer(QWidget, Ui_CodeDocumentationViewer): + """ + Class implementing a widget to show some source code information provided + by plug-ins. + """ + providerAdded = pyqtSignal() + + def __init__(self, parent=None): + """ + Constructor + + @param parent reference to the parent widget + @type QWidget + """ + super(CodeDocumentationViewer, self).__init__(parent) + self.setupUi(self) + + self.__ui = parent + + self.__providers = {} + self.__selectedProvider = "" + self.__disabledProvider = "disabled" + + self.__shuttingDown = False + self.__startingUp = True + + self.__noDocumentationString = self.tr("No documentation available") + self.__disabledString = self.tr( + "No source code documentation provider has been registered or" + " this function has been disabled.") + + self.providerComboBox.addItem(self.tr("<disabled>"), "disabled") + + def finalizeSetup(self): + """ + Public method to finalize the setup of the documentation viewer. + """ + self.__startingUp = False + provider = Preferences.Prefs.settings.value( + "CodeDocumentationViewer/Provider", "disabled") + if provider in self.__providers: + index = self.providerComboBox.findData(provider) + else: + index = 0 + self.providerComboBox.setCurrentIndex(index) + + def registerProvider(self, providerName, providerDisplay, provider): + """ + Public method register a source docu provider. + + @param providerName name of the provider (must be unique) + @type str + @param providerDisplay visible name of the provider + @type str + @param provider function to be called to determine source docu + @type function + @exception KeyError raised if a provider with the given name was + already registered + """ + if providerName in self.__providers: + raise KeyError( + "Provider '{0}' already registered.".format(providerName)) + + self.__providers[providerName] = provider + self.providerComboBox.addItem(providerDisplay, providerName) + + def unregisterProvider(self, providerName): + """ + Public method register a source docu provider. + + @param providerName name of the provider (must be unique) + @type str + """ + if providerName in self.__providers: + if providerName == self.__selectedProvider: + self.providerComboBox.setCurrentIndex(0) + + del self.__providers[providerName] + index = self.providerComboBox.findData(providerName) + self.providerComboBox.removeItem(index) + + def showInfo(self, editor): + """ + Public method to request code documentation data from a provider. + + @param editor reference to the editor to request code docu for + @type Editor + """ + if self.__selectedProvider != self.__disabledProvider: + self.contents.clear() + self.__providers[self.__selectedProvider](editor) + + def documentationReady(self, documentationInfo): + """ + Public method to provide the documentation info to the viewer. + + If documentationInfo is a dictionary, it should contains these keys + and data: + + name: the name of the inspected object + argspec: its argspec + note: A phrase describing the type of object (function or method) and + the module it belongs to. + docstring: its documentation string + + @param documentationInfo dictionary containing the source docu data + @type dict or str + """ + self.__ui.activateCodeDocumentationViewer(switchFocus=False) + + if not documentationInfo: + fullText = self.__noDocumentationString + elif isinstance(documentationInfo, str): + fullText = documentationInfo + elif isinstance(documentationInfo, dict): + name = documentationInfo["name"] + if name: + title = "".join(["=" * len(name), "\n", name, "\n", + "=" * len(name), "\n\n"]) + else: + title = "" + + if documentationInfo["argspec"]: + definition = self.tr("Definition: {0}{1}\n").format( + name, documentationInfo["argspec"]) + else: + definition = '' + + if documentationInfo["note"]: + note = self.tr("Info: {0}\n\n----\n\n").format( + documentationInfo["note"]) + else: + note = "" + + fullText = "".join([title, definition, note, + documentationInfo['docstring']]) + + self.contents.setPlainText(fullText) + + @pyqtSlot(int) + def on_providerComboBox_currentIndexChanged(self, index): + """ + Private slot to handle the selection of a provider. + + @param index index of the selected provider + @type int + """ + if not self.__shuttingDown and not self.__startingUp: + provider = self.providerComboBox.itemData(index) + if provider == self.__disabledProvider: + self.documentationReady(self.__disabledString) + elif provider in self.__providers: + Preferences.Prefs.settings.setValue( + "CodeDocumentationViewer/Provider", provider) + self.__selectedProvider = provider + + def shutdown(self): + """ + Public method to perform shutdown actions. + """ + self.__shuttingDown = True + Preferences.Prefs.settings.setValue( + "CodeDocumentationViewer/Provider", self.__selectedProvider)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/UI/CodeDocumentationViewer.ui Sat Oct 14 20:08:47 2017 +0200 @@ -0,0 +1,83 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>CodeDocumentationViewer</class> + <widget class="QWidget" name="CodeDocumentationViewer"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>639</width> + <height>595</height> + </rect> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Code Info Provider:</string> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="providerComboBox"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="toolTip"> + <string>Select the code info provider</string> + </property> + <property name="sizeAdjustPolicy"> + <enum>QComboBox::AdjustToContents</enum> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="objectLineEdit"> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </item> + <item> + <widget class="QTextEdit" name="contents"> + <property name="tabChangesFocus"> + <bool>true</bool> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="E5TextEditSearchWidget" name="searchWidget" native="true"> + <property name="focusPolicy"> + <enum>Qt::WheelFocus</enum> + </property> + </widget> + </item> + </layout> + </widget> + <customwidgets> + <customwidget> + <class>E5TextEditSearchWidget</class> + <extends>QWidget</extends> + <header>E5Gui/E5TextEditSearchWidget.h</header> + <container>1</container> + </customwidget> + </customwidgets> + <tabstops> + <tabstop>providerComboBox</tabstop> + <tabstop>objectLineEdit</tabstop> + <tabstop>contents</tabstop> + <tabstop>searchWidget</tabstop> + </tabstops> + <resources/> + <connections/> +</ui>
--- a/UI/UserInterface.py Fri Oct 13 22:42:35 2017 +0200 +++ b/UI/UserInterface.py Sat Oct 14 20:08:47 2017 +0200 @@ -531,6 +531,7 @@ e5App().registerObject("IRC", self.irc) e5App().registerObject("Symbols", self.symbolsViewer) e5App().registerObject("Numbers", self.numbersViewer) + e5App().registerObject("DocuViewer", self.codeDocumentationViewer) # list of web addresses serving the versions file self.__httpAlternatives = Preferences.getUI("VersionsUrls6") @@ -643,6 +644,9 @@ if interval > 0: QApplication.setKeyboardInputInterval(interval) + # finalize the initialization of the code documentation viewer + self.codeDocumentationViewer.finalizeSetup() + def __createLayout(self, debugServer): """ Private method to create the layout of the various windows. @@ -701,6 +705,8 @@ """ from E5Gui.E5ToolBox import E5VerticalToolBox, E5HorizontalToolBox + logging.debug("Creating Toolboxes Layout...") + # Create the left toolbox self.lToolboxDock = self.__createDockWindow("lToolboxDock") self.lToolbox = E5VerticalToolBox(self.lToolboxDock) @@ -721,6 +727,7 @@ self.rToolbox, self.tr("Right Toolbox")) # Create the project browser + logging.debug("Creating Project Browser...") from Project.ProjectBrowser import ProjectBrowser self.projectBrowser = ProjectBrowser( self.project, None, @@ -730,6 +737,7 @@ self.tr("Project-Viewer")) # Create the multi project browser + logging.debug("Creating Multiproject Browser...") from MultiProject.MultiProjectBrowser import MultiProjectBrowser self.multiProjectBrowser = MultiProjectBrowser(self.multiProject, self.project) @@ -738,6 +746,7 @@ self.tr("Multiproject-Viewer")) # Create the template viewer part of the user interface + logging.debug("Creating Template Viewer...") from Templates.TemplateViewer import TemplateViewer self.templateViewer = TemplateViewer(None, self.viewmanager) @@ -745,7 +754,16 @@ UI.PixmapCache.getIcon("templateViewer.png"), self.tr("Template-Viewer")) + # Create the code documentation viewer + logging.debug("Creating Code Documentation Viewer...") + from .CodeDocumentationViewer import CodeDocumentationViewer + self.codeDocumentationViewer = CodeDocumentationViewer(self) + self.rToolbox.addItem(self.codeDocumentationViewer, + UI.PixmapCache.getIcon("codeDocuViewer.png"), + self.tr("Code Documentation Viewer")) + # Create the debug viewer maybe without the embedded shell + logging.debug("Creating Debug Viewer...") from Debugger.DebugViewer import DebugViewer self.debugViewer = DebugViewer( debugServer, True, self.viewmanager, None, @@ -756,6 +774,7 @@ self.tr("Debug-Viewer")) # Create the chat part of the user interface + logging.debug("Creating Chat Widget...") from Cooperation.ChatWidget import ChatWidget self.cooperation = ChatWidget(self) self.rToolbox.addItem(self.cooperation, @@ -763,6 +782,7 @@ self.tr("Cooperation")) # Create the IRC part of the user interface + logging.debug("Creating IRC Widget...") from Network.IRC.IrcWidget import IrcWidget self.irc = IrcWidget(self) self.rToolbox.addItem(self.irc, @@ -770,6 +790,7 @@ self.tr("IRC")) # Create the task viewer part of the user interface + logging.debug("Creating Task Viewer...") from Tasks.TaskViewer import TaskViewer self.taskViewer = TaskViewer(None, self.project) self.hToolbox.addItem(self.taskViewer, @@ -777,6 +798,7 @@ self.tr("Task-Viewer")) # Create the log viewer part of the user interface + logging.debug("Creating Log Viewer...") from .LogView import LogViewer self.logViewer = LogViewer(self) self.hToolbox.addItem(self.logViewer, @@ -787,6 +809,7 @@ self.shell = self.debugViewer.shell else: # Create the shell + logging.debug("Creating Shell...") from QScintilla.Shell import ShellAssembly self.shellAssembly = \ ShellAssembly(debugServer, self.viewmanager, True) @@ -797,6 +820,7 @@ if self.embeddedFileBrowser == 0: # separate window # Create the file browser + logging.debug("Creating File Browser...") from .Browser import Browser self.browser = Browser() self.lToolbox.addItem(self.browser, @@ -808,6 +832,7 @@ self.browser = self.projectBrowser.fileBrowser # Create the symbols viewer + logging.debug("Creating Symbols Viewer...") from .SymbolsWidget import SymbolsWidget self.symbolsViewer = SymbolsWidget() self.lToolbox.addItem(self.symbolsViewer, @@ -815,6 +840,7 @@ self.tr("Symbols")) # Create the numbers viewer + logging.debug("Creating Numbers Viewer...") from .NumbersWidget import NumbersWidget self.numbersViewer = NumbersWidget() self.hToolbox.addItem(self.numbersViewer, @@ -831,6 +857,8 @@ """ from E5Gui.E5SideBar import E5SideBar + logging.debug("Creating Sidebars Layout...") + delay = Preferences.getUI("SidebarDelay") # Create the left sidebar self.leftSidebar = E5SideBar(E5SideBar.West, delay) @@ -872,6 +900,15 @@ UI.PixmapCache.getIcon("templateViewer.png"), self.tr("Template-Viewer")) + # Create the code documentation viewer + logging.debug("Creating Code Documentation Viewer...") + from .CodeDocumentationViewer import CodeDocumentationViewer + self.codeDocumentationViewer = CodeDocumentationViewer(self) + self.rightSidebar.addTab( + self.codeDocumentationViewer, + UI.PixmapCache.getIcon("codeDocuViewer.png"), + self.tr("Code Documentation Viewer")) + # Create the debug viewer maybe without the embedded shell logging.debug("Creating Debug Viewer...") from Debugger.DebugViewer import DebugViewer @@ -4165,6 +4202,24 @@ if aw is not None: aw.setFocus(Qt.ActiveWindowFocusReason) + def activateCodeDocumentationViewer(self, switchFocus=True): + """ + Public slot to handle the activation of the Code Documentation Viewer. + + @param switchFocus flag indicating to transfer the input focus + @type bool + """ + if self.layoutType == "Toolboxes": + self.rToolboxDock.show() + self.rToolbox.setCurrentWidget(self.codeDocumentationViewer) + elif self.layoutType == "Sidebars": + self.rightSidebar.show() + self.rightSidebar.setCurrentWidget(self.codeDocumentationViewer) + else: + self.codeDocumentationViewer.show() + if switchFocus: + self.codeDocumentationViewer.setFocus(Qt.ActiveWindowFocusReason) + def __toggleWindow(self, w): """ Private method to toggle a workspace editor window. @@ -6267,6 +6322,8 @@ if sessionCreated and not self.__disableCrashSession: self.__deleteCrashSession() + self.codeDocumentationViewer.shutdown() + self.__previewer.shutdown() self.shell.closeShell() @@ -6798,3 +6855,16 @@ Private slot handling the automatic connection of the IRC client. """ self.__activateIRC() + + ############################################### + ## Support for Code Documentation Viewer below + ############################################### + + def documentationViewer(self): + """ + Public method to provide a reference to the code documentation viewer. + + @return reference to the code documentation viewer + @rtype CodeDocumentationViewer + """ + return self.codeDocumentationViewer
--- a/ViewManager/ViewManager.py Fri Oct 13 22:42:35 2017 +0200 +++ b/ViewManager/ViewManager.py Sat Oct 14 20:08:47 2017 +0200 @@ -84,6 +84,7 @@ super(QuickSearchLineEdit, self).focusInEvent(evt) # pass it on +# TODO: add an action to show code info for the object under the cursor (Ctrl-Alt-I) class ViewManager(QWidget): """ Base class inherited by all specific viewmanager classes. @@ -6782,6 +6783,16 @@ if editor: editor.sortLines() + def showEditorInfo(self, editor): + """ + Public method to show some information for a given editor. + + @param editor editor to show information text for + @type Editor + """ + # TODO: implement CodeDocumentationViewer + self.ui.documentationViewer().showInfo(editor) + ################################################################## ## Below are protected utility methods ##################################################################
--- a/changelog Fri Oct 13 22:42:35 2017 +0200 +++ b/changelog Sat Oct 14 20:08:47 2017 +0200 @@ -6,6 +6,8 @@ -- added capability to enter an interpreter in the Start... dialog - Editor -- added support for auto-completion lists being provided asynchronously + -- added a viewer for source code documentation extracted by providers to + be implemented by plug-ins (like rope and jedi) Version 17.10: - bug fixes
--- a/eric6.e4p Fri Oct 13 22:42:35 2017 +0200 +++ b/eric6.e4p Sat Oct 14 20:08:47 2017 +0200 @@ -1221,6 +1221,7 @@ <Source>UI/BrowserModel.py</Source> <Source>UI/BrowserSortFilterProxyModel.py</Source> <Source>UI/ClearPrivateDataDialog.py</Source> + <Source>UI/CodeDocumentationViewer.py</Source> <Source>UI/CompareDialog.py</Source> <Source>UI/Config.py</Source> <Source>UI/DeleteFilesConfirmationDialog.py</Source> @@ -1898,6 +1899,7 @@ <Form>Tasks/TaskPropertiesDialog.ui</Form> <Form>Templates/TemplatePropertiesDialog.ui</Form> <Form>Templates/TemplateSingleVariableDialog.ui</Form> + <Form>UI/CodeDocumentationViewer.ui</Form> <Form>UI/AuthenticationDialog.ui</Form> <Form>UI/ClearPrivateDataDialog.ui</Form> <Form>UI/CompareDialog.ui</Form> @@ -2015,14 +2017,14 @@ <Interfaces/> <Others> <Other>.hgignore</Other> - <Other>APIs/Python/zope-2.10.7.api</Other> - <Other>APIs/Python/zope-2.11.2.api</Other> - <Other>APIs/Python/zope-3.3.1.api</Other> <Other>APIs/Python3/PyQt4.bas</Other> <Other>APIs/Python3/PyQt5.bas</Other> <Other>APIs/Python3/QScintilla2.bas</Other> <Other>APIs/Python3/eric6.api</Other> <Other>APIs/Python3/eric6.bas</Other> + <Other>APIs/Python/zope-2.10.7.api</Other> + <Other>APIs/Python/zope-2.11.2.api</Other> + <Other>APIs/Python/zope-3.3.1.api</Other> <Other>APIs/QSS/qss.api</Other> <Other>APIs/Ruby/Ruby-1.8.7.api</Other> <Other>APIs/Ruby/Ruby-1.8.7.bas</Other>