Continued implementing the Python AST Viewer.

Mon, 08 Apr 2019 19:08:44 +0200

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Mon, 08 Apr 2019 19:08:44 +0200
changeset 6932
8a3df4c6ac9a
parent 6931
faac36ec9d76
child 6933
7dc49eb45a06

Continued implementing the Python AST Viewer.

Plugins/ViewManagerPlugins/Listspace/Listspace.py file | annotate | diff | comparison | revisions
Plugins/ViewManagerPlugins/Tabview/Tabview.py file | annotate | diff | comparison | revisions
Preferences/ConfigurationPages/EditorStylesPage.py file | annotate | diff | comparison | revisions
Preferences/ConfigurationPages/EditorStylesPage.ui file | annotate | diff | comparison | revisions
Preferences/__init__.py file | annotate | diff | comparison | revisions
QScintilla/Editor.py file | annotate | diff | comparison | revisions
QScintilla/QsciScintillaCompat.py file | annotate | diff | comparison | revisions
UI/PythonAstViewer.py file | annotate | diff | comparison | revisions
ViewManager/ViewManager.py file | annotate | diff | comparison | revisions
changelog file | annotate | diff | comparison | revisions
eric6.e4p file | annotate | diff | comparison | revisions
--- a/Plugins/ViewManagerPlugins/Listspace/Listspace.py	Sun Apr 07 19:55:21 2019 +0200
+++ b/Plugins/ViewManagerPlugins/Listspace/Listspace.py	Mon Apr 08 19:08:44 2019 +0200
@@ -173,6 +173,8 @@
         preview state
     @signal previewStateChanged(bool) emitted to signal a change in the
         preview state
+    @signal astViewerStateChanged(bool) emitted to signal a change in the
+        AST viewer state
     @signal editorLanguageChanged(Editor) emitted to signal a change of an
         editors language
     @signal editorTextChanged(Editor) emitted to signal a change of an
--- a/Plugins/ViewManagerPlugins/Tabview/Tabview.py	Sun Apr 07 19:55:21 2019 +0200
+++ b/Plugins/ViewManagerPlugins/Tabview/Tabview.py	Mon Apr 08 19:08:44 2019 +0200
@@ -766,6 +766,8 @@
         preview state
     @signal previewStateChanged(bool) emitted to signal a change in the
         preview state
+    @signal astViewerStateChanged(bool) emitted to signal a change in the
+        AST viewer state
     @signal editorLanguageChanged(Editor) emitted to signal a change of an
         editors language
     @signal editorTextChanged(Editor) emitted to signal a change of an
--- a/Preferences/ConfigurationPages/EditorStylesPage.py	Sun Apr 07 19:55:21 2019 +0200
+++ b/Preferences/ConfigurationPages/EditorStylesPage.py	Mon Apr 08 19:08:44 2019 +0200
@@ -293,6 +293,11 @@
         self.initColour("IndentationGuidesForeground",
                         self.indentationGuidesForegroundButton,
                         Preferences.getEditorColour)
+        
+        self.initColour("HighlightMarker",
+                        self.highlightingBackgroundButton,
+                        Preferences.getEditorColour,
+                        hasAlpha=True)
     
     def save(self):
         """
--- a/Preferences/ConfigurationPages/EditorStylesPage.ui	Sun Apr 07 19:55:21 2019 +0200
+++ b/Preferences/ConfigurationPages/EditorStylesPage.ui	Mon Apr 08 19:08:44 2019 +0200
@@ -10,7 +10,7 @@
     <height>2891</height>
    </rect>
   </property>
-  <layout class="QVBoxLayout" name="verticalLayout_10">
+  <layout class="QVBoxLayout" name="verticalLayout_11">
    <item>
     <widget class="QLabel" name="headerLabel">
      <property name="text">
@@ -46,74 +46,113 @@
      <property name="title">
       <string>Colours</string>
      </property>
-     <layout class="QGridLayout" name="gridLayout_6">
-      <item row="0" column="0" colspan="4">
-       <widget class="QCheckBox" name="editAreaOverrideCheckBox">
+     <layout class="QVBoxLayout" name="verticalLayout_10">
+      <item>
+       <widget class="QGroupBox" name="editAreaOverrideCheckBox">
         <property name="toolTip">
          <string>Select to set the colour of the edit area different to the default style</string>
         </property>
-        <property name="text">
+        <property name="title">
          <string>Override edit area colours</string>
         </property>
-       </widget>
-      </item>
-      <item row="1" column="0">
-       <widget class="QLabel" name="TextLabel2_2_2_2_2_12">
-        <property name="enabled">
-         <bool>false</bool>
+        <property name="checkable">
+         <bool>true</bool>
         </property>
-        <property name="text">
-         <string>Edit area foreground:</string>
-        </property>
-       </widget>
-      </item>
-      <item row="1" column="1">
-       <widget class="QPushButton" name="editAreaForegroundButton">
-        <property name="enabled">
+        <property name="checked">
          <bool>false</bool>
         </property>
-        <property name="minimumSize">
-         <size>
-          <width>100</width>
-          <height>0</height>
-         </size>
-        </property>
-        <property name="toolTip">
-         <string>Select the foreground colour for the edit area.</string>
-        </property>
-        <property name="text">
-         <string/>
-        </property>
+        <layout class="QHBoxLayout" name="horizontalLayout_7">
+         <item>
+          <widget class="QLabel" name="TextLabel2_2_2_2_2_12">
+           <property name="text">
+            <string>Edit area foreground:</string>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <widget class="QPushButton" name="editAreaForegroundButton">
+           <property name="minimumSize">
+            <size>
+             <width>100</width>
+             <height>0</height>
+            </size>
+           </property>
+           <property name="toolTip">
+            <string>Select the foreground colour for the edit area.</string>
+           </property>
+           <property name="text">
+            <string/>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <widget class="QLabel" name="TextLabel2_2_2_2_2_11">
+           <property name="text">
+            <string>Edit area background:</string>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <widget class="QPushButton" name="editAreaBackgroundButton">
+           <property name="minimumSize">
+            <size>
+             <width>100</width>
+             <height>0</height>
+            </size>
+           </property>
+           <property name="toolTip">
+            <string>Select the background colour for the edit area.</string>
+           </property>
+           <property name="text">
+            <string/>
+           </property>
+          </widget>
+         </item>
+        </layout>
        </widget>
       </item>
-      <item row="1" column="2">
-       <widget class="QLabel" name="TextLabel2_2_2_2_2_11">
-        <property name="enabled">
-         <bool>false</bool>
-        </property>
-        <property name="text">
-         <string>Edit area background:</string>
-        </property>
-       </widget>
-      </item>
-      <item row="1" column="3">
-       <widget class="QPushButton" name="editAreaBackgroundButton">
-        <property name="enabled">
-         <bool>false</bool>
-        </property>
-        <property name="minimumSize">
-         <size>
-          <width>100</width>
-          <height>0</height>
-         </size>
-        </property>
-        <property name="toolTip">
-         <string>Select the background colour for the edit area.</string>
-        </property>
-        <property name="text">
-         <string/>
-        </property>
-       </widget>
+      <item>
+       <layout class="QHBoxLayout" name="horizontalLayout_8">
+        <item>
+         <widget class="QLabel" name="label_20">
+          <property name="text">
+           <string>Highlighting:</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QPushButton" name="highlightingBackgroundButton">
+          <property name="minimumSize">
+           <size>
+            <width>100</width>
+            <height>0</height>
+           </size>
+          </property>
+          <property name="toolTip">
+           <string>Select the colour for the caret.</string>
+          </property>
+          <property name="text">
+           <string/>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <spacer name="spacer">
+          <property name="orientation">
+           <enum>Qt::Horizontal</enum>
+          </property>
+          <property name="sizeType">
+           <enum>QSizePolicy::Expanding</enum>
+          </property>
+          <property name="sizeHint" stdset="0">
+           <size>
+            <width>347</width>
+            <height>20</height>
+           </size>
+          </property>
+         </spacer>
+        </item>
+       </layout>
       </item>
      </layout>
     </widget>
@@ -2080,6 +2119,7 @@
   <tabstop>editAreaOverrideCheckBox</tabstop>
   <tabstop>editAreaForegroundButton</tabstop>
   <tabstop>editAreaBackgroundButton</tabstop>
+  <tabstop>highlightingBackgroundButton</tabstop>
   <tabstop>defaultFontButton</tabstop>
   <tabstop>monospacedFontButton</tabstop>
   <tabstop>monospacedCheckBox</tabstop>
@@ -2169,8 +2209,8 @@
      <y>1144</y>
     </hint>
     <hint type="destinationlabel">
-     <x>544</x>
-     <y>1148</y>
+     <x>625</x>
+     <y>1164</y>
     </hint>
    </hints>
   </connection>
@@ -2185,72 +2225,8 @@
      <y>1265</y>
     </hint>
     <hint type="destinationlabel">
-     <x>555</x>
-     <y>1269</y>
-    </hint>
-   </hints>
-  </connection>
-  <connection>
-   <sender>editAreaOverrideCheckBox</sender>
-   <signal>toggled(bool)</signal>
-   <receiver>TextLabel2_2_2_2_2_12</receiver>
-   <slot>setEnabled(bool)</slot>
-   <hints>
-    <hint type="sourcelabel">
-     <x>75</x>
-     <y>117</y>
-    </hint>
-    <hint type="destinationlabel">
-     <x>75</x>
-     <y>137</y>
-    </hint>
-   </hints>
-  </connection>
-  <connection>
-   <sender>editAreaOverrideCheckBox</sender>
-   <signal>toggled(bool)</signal>
-   <receiver>editAreaForegroundButton</receiver>
-   <slot>setEnabled(bool)</slot>
-   <hints>
-    <hint type="sourcelabel">
-     <x>201</x>
-     <y>115</y>
-    </hint>
-    <hint type="destinationlabel">
-     <x>209</x>
-     <y>140</y>
-    </hint>
-   </hints>
-  </connection>
-  <connection>
-   <sender>editAreaOverrideCheckBox</sender>
-   <signal>toggled(bool)</signal>
-   <receiver>TextLabel2_2_2_2_2_11</receiver>
-   <slot>setEnabled(bool)</slot>
-   <hints>
-    <hint type="sourcelabel">
-     <x>313</x>
-     <y>112</y>
-    </hint>
-    <hint type="destinationlabel">
-     <x>348</x>
-     <y>141</y>
-    </hint>
-   </hints>
-  </connection>
-  <connection>
-   <sender>editAreaOverrideCheckBox</sender>
-   <signal>toggled(bool)</signal>
-   <receiver>editAreaBackgroundButton</receiver>
-   <slot>setEnabled(bool)</slot>
-   <hints>
-    <hint type="sourcelabel">
-     <x>447</x>
-     <y>117</y>
-    </hint>
-    <hint type="destinationlabel">
-     <x>489</x>
-     <y>142</y>
+     <x>635</x>
+     <y>1273</y>
     </hint>
    </hints>
   </connection>
--- a/Preferences/__init__.py	Sun Apr 07 19:55:21 2019 +0200
+++ b/Preferences/__init__.py	Mon Apr 08 19:08:44 2019 +0200
@@ -677,6 +677,7 @@
         "OnlineChangeTraceMarkerSaved": QColor("#88ff88"),
         "IndentationGuidesBackground": QColor(Qt.white),
         "IndentationGuidesForeground": QColor(Qt.black),
+        "HighlightMarker": QColor("#200000FF"),     # ARGB format
         # colors for the marker map
         "BookmarksMap": QColor("#f8c700"),
         "ErrorsMap": QColor("#dd0000"),
--- a/QScintilla/Editor.py	Sun Apr 07 19:55:21 2019 +0200
+++ b/QScintilla/Editor.py	Mon Apr 08 19:08:44 2019 +0200
@@ -18,7 +18,7 @@
 import difflib
 
 from PyQt5.QtCore import QDir, QTimer, QModelIndex, QFileInfo, pyqtSignal, \
-    pyqtSlot, QCryptographicHash, QEvent, QDateTime, QRegExp, Qt
+    pyqtSlot, QCryptographicHash, QEvent, QDateTime, QRegExp, Qt, QPoint
 from PyQt5.QtGui import QCursor, QPalette, QFont, QPixmap, QPainter
 from PyQt5.QtWidgets import QLineEdit, QActionGroup, QDialog, QInputDialog, \
     QApplication, QMenu
@@ -90,6 +90,8 @@
     @signal refreshed() emitted to signal a refresh of the editor contents
     @signal settingsRead() emitted to signal, that the settings have been read
         and set
+    @signal mouseDoubleClick(position, buttons) emitted to signal a mouse
+        double click somewhere in the editor area
     """
     modificationStatusChanged = pyqtSignal(bool, QsciScintillaCompat)
     undoAvailable = pyqtSignal(bool)
@@ -114,6 +116,7 @@
     lastEditPositionAvailable = pyqtSignal()
     refreshed = pyqtSignal()
     settingsRead = pyqtSignal()
+    mouseDoubleClick = pyqtSignal(QPoint, int)
     
     WarningCode = 1
     WarningStyle = 2
@@ -3536,6 +3539,48 @@
         self.__markerMap.update()
     
     ###########################################################################
+    ## Highlightinh marker handling methods below
+    ###########################################################################
+    
+    def setHighlight(self, startLine, startIndex, endLine, endIndex):
+        """
+        Public method to set a text highlight.
+        
+        @param startLine line of the highlight start
+        @type int
+        @param startIndex index of the highlight start
+        @type int
+        @param endLine line of the highlight end
+        @type int
+        @param endIndex index of the highlight end
+        @type int
+        """
+        self.setIndicator(self.highlightIndicator, startLine, startIndex,
+                          endLine, endIndex)
+    
+    def clearAllHighlights(self):
+        """
+        Public method to clear all highlights.
+        """
+        self.clearAllIndicators(self.highlightIndicator)
+    
+    def clearHighlight(self, startLine, startIndex, endLine, endIndex):
+        """
+        Public method to clear a text highlight.
+        
+        @param startLine line of the highlight start
+        @type int
+        @param startIndex index of the highlight start
+        @type int
+        @param endLine line of the highlight end
+        @type int
+        @param endIndex index of the highlight end
+        @type int
+        """
+        self.clearIndicator(self.highlightIndicator, startLine, startIndex,
+                            endLine, endIndex)
+    
+    ###########################################################################
     ## Comment handling methods below
     ###########################################################################
     
@@ -4385,7 +4430,10 @@
             Preferences.getEditorColour("SpellingMarkers"))
         self.__setSpelling()
         
-        # TODO: add support for a highlight indicator (if not possible via multiple selections)
+        self.highlightIndicator = QsciScintilla.INDIC_CONTAINER + 2
+        self.indicatorDefine(
+            self.highlightIndicator, QsciScintilla.INDIC_FULLBOX,
+            Preferences.getEditorColour("HighlightMarker"))
         
         self.setCursorFlashTime(QApplication.cursorFlashTime())
         
@@ -8182,3 +8230,14 @@
         @rtype any
         """
         return self.__getEditorConfig(option)
+    
+    def mouseDoubleClickEvent(self, evt):
+        """
+        Protected method to handle mouse double click events.
+        
+        @param evt reference to the mouse event
+        @type QMouseEvent
+        """
+        super(Editor, self).mouseDoubleClickEvent(evt)
+        
+        self.mouseDoubleClick.emit(evt.pos(), evt.buttons())
--- a/QScintilla/QsciScintillaCompat.py	Sun Apr 07 19:55:21 2019 +0200
+++ b/QScintilla/QsciScintillaCompat.py	Mon Apr 08 19:08:44 2019 +0200
@@ -52,8 +52,12 @@
         IndicatorStyleMax = QsciScintilla.INDIC_DOTBOX
     elif QSCINTILLA_VERSION() < 0x020800:
         IndicatorStyleMax = QsciScintilla.INDIC_SQUIGGLEPIXMAP
+    elif QSCINTILLA_VERSION() < 0x020900:
+        IndicatorStyleMax = QsciScintilla.INDIC_TEXTFORE
+    elif QSCINTILLA_VERSION() < 0x020A00:
+        IndicatorStyleMax = QsciScintilla.INDIC_POINTCHARACTER
     else:
-        IndicatorStyleMax = QsciScintilla.INDIC_COMPOSITIONTHICK
+        IndicatorStyleMax = QsciScintilla.INDIC_GRADIENTCENTRE
     
     def __init__(self, parent=None):
         """
@@ -971,11 +975,15 @@
             QsciScintilla.INDIC_TT, QsciScintilla.INDIC_DIAGONAL,
             QsciScintilla.INDIC_STRIKE, QsciScintilla.INDIC_HIDDEN,
             QsciScintilla.INDIC_BOX, QsciScintilla.INDIC_ROUNDBOX,
-            QsciScintilla.INDIC_STRAIGHTBOX, QsciScintilla.INDIC_DASH,
-            QsciScintilla.INDIC_DOTS, QsciScintilla.INDIC_SQUIGGLELOW,
-            QsciScintilla.INDIC_DOTBOX, QsciScintilla.INDIC_SQUIGGLEPIXMAP,
-            QsciScintilla.INDIC_COMPOSITIONTHICK depending upon QScintilla
-            version)
+            QsciScintilla.INDIC_STRAIGHTBOX, QsciScintilla.INDIC_FULLBOX,
+            QsciScintilla.INDIC_DASH, QsciScintilla.INDIC_DOTS,
+            QsciScintilla.INDIC_SQUIGGLELOW, QsciScintilla.INDIC_DOTBOX,
+            QsciScintilla.INDIC_GRADIENT, QsciScintilla.INDIC_GRADIENTCENTRE,
+            QsciScintilla.INDIC_SQUIGGLEPIXMAP,
+            QsciScintilla.INDIC_COMPOSITIONTHICK,
+            QsciScintilla.INDIC_COMPOSITIONTHIN, QsciScintilla.INDIC_TEXTFORE,
+            QsciScintilla.INDIC_POINT, QsciScintilla.INDIC_POINTCHARACTER
+            depending upon QScintilla version)
         @param color color to be used by the indicator (QColor)
         @exception ValueError the indicator or style are not valid
         """
@@ -992,6 +1000,13 @@
         try:
             self.SendScintilla(QsciScintilla.SCI_INDICSETALPHA, indicator,
                                color.alpha())
+            if style in (
+                QsciScintilla.INDIC_ROUNDBOX, QsciScintilla.INDIC_STRAIGHTBOX,
+                QsciScintilla.INDIC_DOTBOX, QsciScintilla.INDIC_FULLBOX,
+            ):
+                # set outline alpha less transparent
+                self.SendScintilla(QsciScintilla.SCI_INDICSETOUTLINEALPHA,
+                                   indicator, color.alpha() + 20)
         except AttributeError:
             pass
     
--- a/UI/PythonAstViewer.py	Sun Apr 07 19:55:21 2019 +0200
+++ b/UI/PythonAstViewer.py	Mon Apr 08 19:08:44 2019 +0200
@@ -17,7 +17,7 @@
 
 import ast
 
-from PyQt5.QtCore import Qt, QTimer
+from PyQt5.QtCore import pyqtSlot, Qt, QTimer
 from PyQt5.QtGui import QCursor, QBrush
 from PyQt5.QtWidgets import QTreeWidget, QApplication, QTreeWidgetItem, \
     QAbstractItemView, QWidget, QVBoxLayout
@@ -25,9 +25,6 @@
 from ThirdParty.asttokens.asttokens import ASTTokens
 
 
-# TODO: highlight code area in editor when a tree node is clicked
-# TODO: jump to node when a double click in the editor is detected
-#       (rebuild the tree, if the source code has changed)
 class PythonAstViewer(QWidget):
     """
     Class implementing a widget to visualize the Python AST for some Python
@@ -68,6 +65,8 @@
         self.__astWidget.setSelectionMode(QAbstractItemView.SingleSelection)
         self.__astWidget.setAlternatingRowColors(True)
         
+        self.__astWidget.itemClicked.connect(self.__astItemClicked)
+        
         self.__vm.astViewerStateChanged.connect(self.__astViewerStateChanged)
         
         self.hide()
@@ -80,6 +79,8 @@
         @type Editor
         """
         if editor is not self.__editor:
+            if self.__editor:
+                self.__editor.clearAllHighlights()
             self.__editor = editor
             if self.__editor:
                 self.__loadAST()
@@ -94,6 +95,26 @@
         if editor and editor is self.__editor:
             self.__loadAST()
     
+    def __editorDoubleClicked(self, editor, pos, buttons):
+        """
+        Private slot to handle a mouse button double click in the editor.
+        
+        @param editor reference to the editor, that emitted the signal
+        @type Editor
+        @param pos position of the double click
+        @type QPoint
+        @param buttons mouse buttons that were double clicked
+        @type Qt.MouseButtons
+        """
+        if editor is self.__editor and buttons == Qt.LeftButton:
+            if editor.isModified():
+                # reload the source
+                QTimer.singleShot(0, self.__loadAST)
+            else:
+                # highlight the corresponding entry
+                QTimer.singleShot(0, self.__selectItemForEditorSelection)
+                QTimer.singleShot(0, self.__grabFocus)
+    
     def __lastEditorClosed(self):
         """
         Private slot to handle the last editor closed signal of the view
@@ -110,6 +131,7 @@
         if not self.__vmConnected:
             self.__vm.editorChangedEd.connect(self.__editorChanged)
             self.__vm.editorSavedEd.connect(self.__editorSaved)
+            self.__vm.editorDoubleClickedEd.connect(self.__editorDoubleClicked)
             self.__vmConnected = True
     
     def hide(self):
@@ -118,9 +140,14 @@
         """
         super(PythonAstViewer, self).hide()
         
+        if self.__editor:
+            self.__editor.clearAllHighlights()
+        
         if self.__vmConnected:
             self.__vm.editorChangedEd.disconnect(self.__editorChanged)
             self.__vm.editorSavedEd.disconnect(self.__editorSaved)
+            self.__vm.editorDoubleClickedEd.disconnect(
+                self.__editorDoubleClicked)
             self.__vmConnected = False
     
     def shutdown(self):
@@ -143,8 +170,8 @@
             self.show()
             self.__loadAST()
         else:
+            self.hide()
             self.__editor = None
-            self.hide()
     
     def __createErrorItem(self, error):
         """
@@ -169,6 +196,7 @@
             return
         
         self.__astWidget.clear()
+        self.__editor.clearAllHighlights()
         
         if not self.__editor.isPyFile():
             self.__createErrorItem(self.tr(
@@ -202,6 +230,8 @@
             self.setUpdatesEnabled(True)
         
         QApplication.restoreOverrideCursor()
+        
+        self.__grabFocus()
     
     def __populateNode(self, name, nodeOrFields, parent):
         """
@@ -293,7 +323,7 @@
         @return best matching node
         @rtype ast.AST
         """
-        if textRange == (-1, -1, -1, -1):
+        if textRange in [(-1, -1, -1, -1), (0, -1, 0, -1)]:
             # no valid range, i.e. no selection
             return None
         
@@ -324,10 +354,17 @@
         @return best matching tree item
         @rtype QTreeWidgetItem
         """
-        if textRange == (-1, -1, -1, -1):
+        if textRange in [(-1, -1, -1, -1), (0, -1, 0, -1)]:
             # no valid range, i.e. no selection
             return None
         
+        lineno = itm.data(0, self.StartLineRole)
+        if lineno is not None and not self.__rangeContainsSmallerOrEqual(
+           (itm.data(0, self.StartLineRole), itm.data(0, self.StartIndexRole),
+            itm.data(0, self.EndLineRole), itm.data(0, self.EndIndexRole)),
+           textRange):
+            return None
+        
         # first look among children
         for index in range(itm.childCount()):
             child = itm.child(index)
@@ -337,7 +374,7 @@
         
         # no suitable child was found
         lineno = itm.data(0, self.StartLineRole)
-        if lineno is not None and self.__rangeContainsSmaller(
+        if lineno is not None and self.__rangeContainsSmallerOrEqual(
            (itm.data(0, self.StartLineRole), itm.data(0, self.StartIndexRole),
             itm.data(0, self.EndLineRole), itm.data(0, self.EndIndexRole)),
            textRange):
@@ -391,6 +428,19 @@
             (firstStart < secondStart and firstEnd == secondEnd)
         )
     
+    def __rangeContainsSmallerOrEqual(self, first, second):
+        """
+        Private method to check, if second is contained in or equal to first.
+        
+        @param first text range to check against
+        @type tuple of (int, int, int, int)
+        @param second text range to check for
+        @type tuple of (int, int, int, int)
+        @return flag indicating second is contained in or equal to first
+        @rtype bool
+        """
+        return first == second or self.__rangeContainsSmaller(first, second)
+    
     def __clearSelection(self):
         """
         Private method to clear all selected items.
@@ -418,3 +468,33 @@
             self.__astWidget.scrollToItem(
                 itm, QAbstractItemView.PositionAtCenter)
             itm.setSelected(True)
+    
+    def __grabFocus(self):
+        """
+        Private method to grab the input focus.
+        """
+        self.__astWidget.setFocus(Qt.OtherFocusReason)
+    
+    @pyqtSlot(QTreeWidgetItem, int)
+    def __astItemClicked(self, itm, column):
+        """
+        Private slot handling a user click on an AST node item.
+        
+        @param itm reference to the clicked item
+        @type QTreeWidgetItem
+        @param column column number of the click
+        @type int
+        """
+        self.__editor.clearAllHighlights()
+        
+        if itm is not None:
+            startLine = itm.data(0, self.StartLineRole)
+            if startLine is not None:
+                startIndex = itm.data(0, self.StartIndexRole)
+                endLine = itm.data(0, self.EndLineRole)
+                endIndex = itm.data(0, self.EndIndexRole)
+                
+                self.__editor.gotoLine(startLine, firstVisible=True,
+                                       expand=True)
+                self.__editor.setHighlight(startLine - 1, startIndex,
+                                           endLine - 1, endIndex)
--- a/ViewManager/ViewManager.py	Sun Apr 07 19:55:21 2019 +0200
+++ b/ViewManager/ViewManager.py	Mon Apr 08 19:08:44 2019 +0200
@@ -12,7 +12,7 @@
 import os
 
 from PyQt5.QtCore import pyqtSignal, pyqtSlot, QSignalMapper, QTimer, \
-    QFileInfo, QRegExp, Qt, QCoreApplication
+    QFileInfo, QRegExp, Qt, QCoreApplication, QPoint
 from PyQt5.QtGui import QColor, QKeySequence, QPalette, QPixmap
 from PyQt5.QtWidgets import QLineEdit, QToolBar, QWidgetAction, QDialog, \
     QApplication, QMenu, QComboBox, QWidget
@@ -122,6 +122,8 @@
         editor's text
     @signal editorLineChanged(str,int) emitted to signal a change of an
         editor's current line (line is given one based)
+    @signal editorDoubleClickedEd(Editor, position, buttons) emitted to signal
+        a mouse double click in an editor
     """
     changeCaption = pyqtSignal(str)
     editorChanged = pyqtSignal(str)
@@ -145,6 +147,7 @@
     editorLanguageChanged = pyqtSignal(Editor)
     editorTextChanged = pyqtSignal(Editor)
     editorLineChanged = pyqtSignal(str, int)
+    editorDoubleClickedEd = pyqtSignal(Editor, QPoint, int)
     
     def __init__(self):
         """
@@ -4742,6 +4745,9 @@
             self.__lastEditPositionAvailable)
         editor.zoomValueChanged.connect(
             lambda v: self.zoomValueChanged(v, editor))
+        editor.mouseDoubleClick.connect(
+            lambda pos, buttons: self.__editorDoubleClicked(editor, pos,
+                                                            buttons))
         
         editor.languageChanged.connect(
             lambda: self.editorLanguageChanged.emit(editor))
@@ -6914,7 +6920,23 @@
         eol = editor.getEolIndicator()
         self.__setSbFile(fn, line, pos, enc, lang, eol)
         self.cursorChanged.emit(editor)
-        
+    
+    def __editorDoubleClicked(self, editor, pos, buttons):
+        """
+        Private slot handling mouse double clicks of an editor.
+        
+        Note: This method is simply a multiplexer to re-emit the signal
+        with the editor prepended.
+        
+        @param editor reference to the editor, that emitted the signal
+        @type Editor
+        @param pos position of the double click
+        @type QPoint
+        @param buttons mouse buttons that were double clicked
+        @type Qt.MouseButtons
+        """
+        self.editorDoubleClickedEd.emit(editor, pos, buttons)
+    
     def __breakpointToggled(self, editor):
         """
         Private slot to handle the breakpointToggled signal.
--- a/changelog	Sun Apr 07 19:55:21 2019 +0200
+++ b/changelog	Mon Apr 08 19:08:44 2019 +0200
@@ -2,6 +2,8 @@
 ----------
 Version 19.05:
 - bug fixes
+- Python AST Viewer
+  -- added a tool to visualize the AST for a Python source file
 - Snapshot Tool
   -- added code to support screenshots on Wayland desktops (KDE and Gnome)
 
--- a/eric6.e4p	Sun Apr 07 19:55:21 2019 +0200
+++ b/eric6.e4p	Mon Apr 08 19:08:44 2019 +0200
@@ -2269,14 +2269,14 @@
   </Resources>
   <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>

eric ide

mercurial