[gnome-builder/wip/chergert/layout: 115/118] find-other-file: add simple find-other-file plugin



commit 8c882677f0223defe41b0be7de7aa37442d5afe7
Author: Christian Hergert <chergert redhat com>
Date:   Wed Jul 5 04:24:55 2017 -0700

    find-other-file: add simple find-other-file plugin
    
    This was previously implemented as an action to the editor view and only
    knew how to cycle between a couple file types. This is more generic and
    only looks at file prefixes.
    
    We will still need some work on the interaction and HUD design, but this
    contains the basic mechanics of what needs to be done.

 meson_options.txt                              |    1 +
 plugins/find-other-file/find-other-file.plugin |   10 ++
 plugins/find-other-file/find_other_file.py     |  186 ++++++++++++++++++++++++
 plugins/find-other-file/meson.build            |   13 ++
 plugins/meson.build                            |    2 +
 5 files changed, 212 insertions(+), 0 deletions(-)
---
diff --git a/meson_options.txt b/meson_options.txt
index 99b5d0e..c27ef4f 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -36,6 +36,7 @@ option('with_ctags', type: 'boolean')
 option('with_devhelp', type: 'boolean')
 option('with_eslint', type: 'boolean')
 option('with_file_search', type: 'boolean')
+option('with_find_other_file', type: 'boolean')
 option('with_flatpak', type: 'boolean')
 option('with_fpaste', type: 'boolean')
 option('with_gcc', type: 'boolean')
diff --git a/plugins/find-other-file/find-other-file.plugin b/plugins/find-other-file/find-other-file.plugin
new file mode 100644
index 0000000..36a1a91
--- /dev/null
+++ b/plugins/find-other-file/find-other-file.plugin
@@ -0,0 +1,10 @@
+[Plugin]
+Module=find_other_file
+Loader=python3
+Name=Find other files
+Description=Allows the user to rotate through other files similarly named to the open document.
+Authors=Christian Hergert <christian hergert me>
+Copyright=Copyright © 2017 Christian Hergert
+Builtin=true
+Hidden=true
+Depends=editor
diff --git a/plugins/find-other-file/find_other_file.py b/plugins/find-other-file/find_other_file.py
new file mode 100644
index 0000000..16f73d4
--- /dev/null
+++ b/plugins/find-other-file/find_other_file.py
@@ -0,0 +1,186 @@
+#!/usr/bin/env python3
+
+#
+# find_other_file.py
+#
+# Copyright (C) 2017 Christian Hergert <chergert redhat com>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+from gi.repository import GLib
+from gi.repository import GObject
+from gi.repository import Gio
+from gi.repository import Gdk
+from gi.repository import Gtk
+from gi.repository import Ide
+
+_ATTRIBUTES = ",".join([
+    Gio.FILE_ATTRIBUTE_STANDARD_NAME,
+    Gio.FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME,
+    Gio.FILE_ATTRIBUTE_STANDARD_SYMBOLIC_ICON,
+])
+
+class FindOtherFile(GObject.Object, Ide.WorkbenchAddin):
+    workbench = None
+    window = None
+    model = None
+
+    def do_load(self, workbench):
+        self.workbench = workbench
+
+        action = Gio.SimpleAction.new('find-other-file', None)
+        action.connect('activate', self.on_activate)
+        self.workbench.add_action(action)
+
+        self.window = Gtk.Window(resizable=False,
+                                 width_request=400,
+                                 transient_for=self.workbench,
+                                 title=_("Find other file"),
+                                 window_position=Gtk.WindowPosition.CENTER_ON_PARENT)
+        self.window.connect('key-press-event', self.on_key_press)
+        self.window.connect('delete-event', self.on_delete_event)
+
+        scroller = Gtk.ScrolledWindow(visible=True,
+                                      propagate_natural_width=True,
+                                      propagate_natural_height=True,
+                                      hscrollbar_policy=Gtk.PolicyType.NEVER)
+        self.window.add(scroller)
+
+        self.model = Gtk.ListStore.new([OtherFile])
+        treeview = Gtk.TreeView(visible=True, model=self.model, headers_visible=False)
+        treeview.connect('row-activated', self.on_row_activated)
+        scroller.add(treeview)
+
+        column = Gtk.TreeViewColumn()
+        treeview.append_column(column)
+
+        cell = Gtk.CellRendererPixbuf(xpad=6, ypad=6)
+        column.pack_start(cell, False)
+        column.set_cell_data_func(cell, self.icon_cell_func)
+
+        cell = Gtk.CellRendererText(ypad=6)
+        column.pack_start(cell, True)
+        column.set_cell_data_func(cell, self.text_cell_func)
+
+    def do_unload(self, workbench):
+        self.window.destroy()
+        self.window = None
+
+        self.model.clear()
+        self.model = None
+
+        self.workbench = None
+
+    def on_activate(self, *args):
+        editor = self.workbench.get_perspective_by_name('editor')
+        view = editor.get_active_view()
+        if type(view) is not Ide.EditorView:
+            return
+
+        buffer = view.get_buffer()
+        file = buffer.get_file().get_file()
+        parent = file.get_parent()
+
+        basename = file.get_basename()
+        if '.' in basename:
+            basename = basename[:basename.rindex('.')+1]
+
+        parent.enumerate_children_async(_ATTRIBUTES,
+                                        Gio.FileQueryInfoFlags.NONE,
+                                        GLib.PRIORITY_LOW,
+                                        None,
+                                        self.on_enumerator_loaded,
+                                        basename)
+
+    def on_enumerator_loaded(self, parent, result, prefix):
+        try:
+            files = []
+
+            enumerator = parent.enumerate_children_finish(result)
+            info = enumerator.next_file(None)
+
+            while info is not None:
+                name = info.get_name()
+
+                if name.startswith(prefix):
+                    content_type = info.get_attribute_string(Gio.FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE)
+                    display_name = info.get_attribute_string(Gio.FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME)
+                    icon = info.get_attribute_object(Gio.FILE_ATTRIBUTE_STANDARD_SYMBOLIC_ICON)
+
+                    file = OtherFile(parent.get_child(name), display_name, icon)
+                    files.append(file)
+
+                info = enumerator.next_file(None)
+
+            enumerator.close()
+
+            if files:
+                self.present_results(files)
+
+        except Exception as ex:
+            Ide.warning(repr(ex))
+            return
+
+    def on_delete_event(self, window, event):
+        window.hide()
+        return True
+
+    def on_key_press(self, window, event):
+        if event.keyval == Gdk.KEY_Escape:
+            window.hide()
+            self.workbench.present()
+            self.workbench.grab_focus()
+            return True
+        return False
+
+    def on_row_activated(self, treeview, path, column):
+        model = treeview.get_model()
+        iter = model.get_iter(path)
+        file, = model.get(iter, 0)
+
+        self.window.hide()
+
+        self.workbench.open_files_async([file.file], 'editor', 0, None, None)
+
+    def icon_cell_func(self, layout, cell, model, iter, data):
+        file, = model.get(iter, 0)
+        cell.props.gicon = file.icon
+
+    def text_cell_func(self, layout, cell, model, iter, data):
+        file, = model.get(iter, 0)
+        cell.props.text = file.display_name
+
+    def populate(self, results):
+        self.model.clear()
+        for row in results:
+            iter = self.model.append()
+            self.model.set(iter, {0: row})
+
+    def present_results(self, results):
+        self.populate(results)
+        self.window.present()
+        self.window.grab_focus()
+
+class OtherFile(GObject.Object):
+    icon = GObject.Property(type=Gio.Icon)
+    display_name = GObject.Property(type=str)
+    file = GObject.Property(type=Gio.File)
+
+    def __init__(self, file, display_name, icon):
+        super().__init__()
+        self.file = file
+        self.display_name = display_name
+        self.icon = icon
+
diff --git a/plugins/find-other-file/meson.build b/plugins/find-other-file/meson.build
new file mode 100644
index 0000000..8781c95
--- /dev/null
+++ b/plugins/find-other-file/meson.build
@@ -0,0 +1,13 @@
+if get_option('with_find_other_file')
+
+install_data('find_other_file.py', install_dir: plugindir)
+
+configure_file(
+          input: 'find-other-file.plugin',
+         output: 'find-other-file.plugin',
+  configuration: configuration_data(),
+        install: true,
+    install_dir: plugindir,
+)
+
+endif
diff --git a/plugins/meson.build b/plugins/meson.build
index 4b0792a..60b4a45 100644
--- a/plugins/meson.build
+++ b/plugins/meson.build
@@ -25,6 +25,7 @@ subdir('ctags')
 subdir('devhelp')
 subdir('eslint')
 subdir('file-search')
+subdir('find-other-file')
 subdir('flatpak')
 subdir('fpaste')
 subdir('gcc')
@@ -77,6 +78,7 @@ status += [
   'Devhelp ............... : @0@'.format(get_option('with_devhelp')),
   'ESLint ................ : @0@'.format(get_option('with_eslint')),
   'File Search ........... : @0@'.format(get_option('with_file_search')),
+  'Find other file ....... : @0@'.format(get_option('with_find_other_file')),
   'Flatpak ............... : @0@'.format(get_option('with_flatpak')),
   'Fpaste ................ : @0@'.format(get_option('with_fpaste')),
   'GCC ................... : @0@'.format(get_option('with_gcc')),


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