[gimp/gimp-2-10] tools: in performance-log-viewer.py, allow viewing source files ...



commit 33269cc8ff4ab92d03b68143bc393df841ca45b0
Author: Ell <ell_se yahoo com>
Date:   Sun Sep 30 04:51:19 2018 -0400

    tools: in performance-log-viewer.py, allow viewing source files ...
    
    ... in backtraces
    
    In the performance-log viewer's backtrace viewer, show a document
    icon next to stack frames with source-location information, whose
    source file is found locally.  Clicking the icon opens the source
    file in a text editor at the relevant line.
    
    Two environment variables control this feature:
    
      - PERFORMANCE_LOG_VIEWER_PATH is a list of colon-separated
        directories in which to look for source files.  If this
        variable is undefined, the current directory is used.
    
      - PERFORMANCE_LOG_VIEWER_EDITOR is the command to use to launch
        the text editor, for editing a specific file at a specific
        line.  The special strings "{file}" and "{line}" are replaced
        with the filename and line-number, respectively.  If this
        variable is undefined, "xdg-open {file}" is used.
    
    (cherry picked from commit 0f38709259f52554f5ed61ec69bde21fe73c0e7d)

 tools/performance-log-viewer.py | 100 ++++++++++++++++++++++++++++++++++++++--
 1 file changed, 96 insertions(+), 4 deletions(-)
---
diff --git a/tools/performance-log-viewer.py b/tools/performance-log-viewer.py
index a27bf8a118..ab80089bf9 100755
--- a/tools/performance-log-viewer.py
+++ b/tools/performance-log-viewer.py
@@ -21,7 +21,7 @@ along with this program.  If not, see <https://www.gnu.org/licenses/>.
 Usage: performance-log-viewer.py < infile
 """
 
-import builtins, sys, math, statistics, functools, enum, re
+import builtins, sys, os, math, statistics, functools, enum, re, subprocess
 
 from collections import namedtuple
 from xml.etree   import ElementTree
@@ -72,6 +72,39 @@ def get_basename (path):
 
     return match[1] if match else path
 
+search_path = list (filter (
+    bool,
+    os.environ.get ("PERFORMANCE_LOG_VIEWER_PATH", ".").split (":")
+))
+
+editor_command  = os.environ.get ("PERFORMANCE_LOG_VIEWER_EDITOR",
+                                  "xdg-open {file}")
+editor_command += " &"
+
+def find_file (filename):
+    filename = re.sub ("[\\\\/]", GLib.DIR_SEPARATOR_S, filename)
+
+    if GLib.path_is_absolute (filename):
+        file = Gio.File.new_for_path (filename)
+
+        if file.query_exists ():
+            return file
+
+    for path in search_path:
+        rest = filename
+
+        while rest:
+            file = Gio.File.new_for_path (GLib.build_filenamev ((path, rest)))
+
+            if file.query_exists ():
+                return file
+
+            sep = rest.find (GLib.DIR_SEPARATOR_S)
+
+            rest = rest[sep + 1:] if sep >= 0 else ""
+
+    return None
+
 VariableType = namedtuple ("VariableType",
                            ("parse", "format", "format_numeric"))
 
@@ -1737,6 +1770,36 @@ class BacktraceViewer (Gtk.Box):
         def __init__ (self):
             Gtk.ListStore.__init__ (self, int, str, str, str, str, str, str)
 
+    class CellRendererViewSource (Gtk.CellRendererPixbuf):
+        file = GObject.Property (type = Gio.File, default = None)
+        line = GObject.Property (type = int,      default = 0)
+
+        def __init__ (self, *args, **kwargs):
+            Gtk.CellRendererPixbuf.__init__ (
+                self,
+                *args,
+                icon_name = "text-x-generic-symbolic",
+                mode      = Gtk.CellRendererMode.ACTIVATABLE,
+                **kwargs)
+
+            self.connect ("notify::file",
+                          lambda *args:
+                              self.set_property ("visible", bool (self.file)))
+
+        def do_activate (self, event, widget, path, *args):
+            if self.file:
+                subprocess.call (
+                    editor_command.format (
+                        file = "\"%s\"" % self.file.get_path (),
+                        line = self.line
+                    ),
+                    shell = True
+                )
+
+                return True
+
+            return False
+
     def __init__ (self, *args, **kwargs):
         Gtk.Box.__init__ (self,
                           *args,
@@ -1893,9 +1956,33 @@ class BacktraceViewer (Gtk.Box):
         col.pack_start (cell, False)
         col.add_attribute (cell, "text", self.FrameStore.LINE)
 
+        def format_view_source_col (tree_col, cell, model, iter, cols):
+            filename = model[iter][cols[0]] or None
+            line     = model[iter][cols[1]] or "0"
+
+            cell.set_property ("file", filename and find_file (filename))
+            cell.set_property ("line", int (line))
+
+        def format_view_source_tooltip (row):
+            filename = row[store.SOURCE]
+
+            if filename:
+                file = find_file (filename)
+
+                if file:
+                    return file.get_path ()
+
+            return None
+
         col = Gtk.TreeViewColumn ()
+        self.tooltip_columns[col] = format_view_source_tooltip
         tree.append_column (col)
 
+        cell = self.CellRendererViewSource (xalign = 0)
+        col.pack_start (cell, False)
+        col.set_cell_data_func (cell, format_view_source_col, (store.SOURCE,
+                                                               store.LINE))
+
         selection.connect ("change-complete", self.selection_change_complete)
 
     @GObject.Property (type = bool, default = False)
@@ -2030,7 +2117,7 @@ class BacktraceViewer (Gtk.Box):
                                                                   keyboard_mode)
 
         if hit:
-            column = -1
+            column = None
 
             if keyboard_mode:
                 cursor_path, cursor_col = tree.get_cursor ()
@@ -2047,8 +2134,13 @@ class BacktraceViewer (Gtk.Box):
 
                         break
 
-            if column >= 0:
-                value = model[iter][column]
+            if column is not None:
+                value = None
+
+                if type (column) == int:
+                    value = model[iter][column]
+                else:
+                    value = column (model[iter])
 
                 if value:
                     tooltip.set_text (str (value))


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]