[meld/pathlabel: 8/10] ui.pathlabel: Update path display to support cross-pane summarisation
- From: Kai Willadsen <kaiw src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [meld/pathlabel: 8/10] ui.pathlabel: Update path display to support cross-pane summarisation
- Date: Sun, 10 Jan 2021 02:57:36 +0000 (UTC)
commit e353761e2e834843bc3db5a55021536c5b16d0d9
Author: Kai Willadsen <kai willadsen gmail com>
Date: Sun Jan 10 09:58:46 2021 +1000
ui.pathlabel: Update path display to support cross-pane summarisation
meld/filediff.py | 11 +++-
meld/ui/pathlabel.py | 161 +++++++++++++++++++++++++++++++++++++++++++++++----
2 files changed, 159 insertions(+), 13 deletions(-)
---
diff --git a/meld/filediff.py b/meld/filediff.py
index 46448726..e7de5c5b 100644
--- a/meld/filediff.py
+++ b/meld/filediff.py
@@ -33,7 +33,7 @@ from meld.const import (
FileComparisonMode,
)
from meld.gutterrendererchunk import GutterRendererChunkLines
-from meld.iohelpers import prompt_save_filename
+from meld.iohelpers import find_shared_parent_path, prompt_save_filename
from meld.matchers.diffutil import Differ, merged_chunk_order
from meld.matchers.helpers import CachedSequenceMatcher
from meld.matchers.merge import AutoMergeDiffer, Merger
@@ -1392,10 +1392,11 @@ class FileDiff(Gtk.VBox, MeldDoc):
def recompute_label(self):
self._set_save_action_sensitivity()
- filenames = [b.data.label for b in self.textbuffer[:self.num_panes]]
+ buffers = self.textbuffer[:self.num_panes]
+ filenames = [b.data.label for b in buffers]
shortnames = misc.shorten_names(*filenames)
- for i, buf in enumerate(self.textbuffer[:self.num_panes]):
+ for i, buf in enumerate(buffers):
if buf.get_modified():
shortnames[i] += "*"
self.file_save_button[i].set_sensitive(buf.get_modified())
@@ -1403,6 +1404,10 @@ class FileDiff(Gtk.VBox, MeldDoc):
'document-save-symbolic' if buf.data.writable else
'document-save-as-symbolic')
+ parent_path = find_shared_parent_path([b.data.gfile for b in buffers])
+ for pathlabel in self.filelabel:
+ pathlabel.props.parent_gfile = parent_path
+
label = self.meta.get("tablabel", "")
if label:
self.label_text = label
diff --git a/meld/ui/pathlabel.py b/meld/ui/pathlabel.py
index a8cf83bc..eb876283 100644
--- a/meld/ui/pathlabel.py
+++ b/meld/ui/pathlabel.py
@@ -13,31 +13,83 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
+import logging
+import pathlib
-from gi.repository import Gdk, Gio, GObject, Gtk
+from gi.repository import Gdk, Gio, GLib, GObject, Gtk
from meld.conf import _
from meld.melddoc import open_files_external
+log = logging.getLogger(__name__)
+
@Gtk.Template(resource_path='/org/gnome/meld/ui/path-label.ui')
class PathLabel(Gtk.MenuButton):
__gtype_name__ = 'PathLabel'
- full_path_label = Gtk.Template.Child()
+ MISSING_FILE_NAME: str = _('Unnamed file')
+
+ full_path_label: Gtk.Entry = Gtk.Template.Child()
custom_label = GObject.Property(type=str, nick='Custom label override')
- gfile = GObject.Property(type=Gio.File, nick='GFile being displayed')
+
+ _gfile: Gio.File
+ _parent_gfile: Gio.File
+
+ def get_file(self) -> Gio.File:
+ return self._gfile
+
+ def set_file(self, file: Gio.File) -> None:
+ if file == self._gfile:
+ return
+
+ try:
+ self._update_paths(self._parent_gfile, file)
+ except ValueError as e:
+ log.warning(f'Error setting GFile: {str(e)}')
+
+ def get_parent_file(self) -> Gio.File:
+ return self._parent_gfile
+
+ def set_parent_file(self, parent_file: Gio.File) -> None:
+ if parent_file == self._parent_gfile:
+ return
+
+ try:
+ self._update_paths(parent_file, self._gfile)
+ except ValueError as e:
+ log.warning(f'Error setting parent GFile: {str(e)}')
+
+ gfile = GObject.Property(
+ type=Gio.File,
+ nick='File being displayed',
+ getter=get_file,
+ setter=set_file,
+ )
+
+ parent_gfile = GObject.Property(
+ type=Gio.File,
+ nick=(
+ 'Parent folder of the current file being displayed that '
+ 'determines where the path display will terminate'
+ ),
+ getter=get_parent_file,
+ setter=set_parent_file,
+ )
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
- self.bind_property(
- 'gfile', self, 'label',
- GObject.BindingFlags.DEFAULT | GObject.BindingFlags.SYNC_CREATE,
- self.get_display_label,
- )
+ self._gfile = None
+ self._parent_gfile = None
+
+ # self.bind_property(
+ # 'gfile', self, 'label',
+ # GObject.BindingFlags.DEFAULT | GObject.BindingFlags.SYNC_CREATE,
+ # self.get_display_label,
+ # )
self.bind_property(
'custom_label', self, 'label',
GObject.BindingFlags.DEFAULT | GObject.BindingFlags.SYNC_CREATE,
@@ -74,15 +126,104 @@ class PathLabel(Gtk.MenuButton):
elif self.gfile:
# TODO: Ideally we'd do some cross-filename summarisation here
# instead of just showing the basename.
- return self.gfile.get_basename()
+ basename = self.gfile.get_basename()
+ return basename if basename else self.MISSING_FILE_NAME
else:
- return _('Unnamed file')
+ return self.MISSING_FILE_NAME
def get_display_path(self, binding, from_value):
if from_value:
return from_value.get_parse_name()
return ''
+ def _update_paths(self, parent: Gio.File, descendant: Gio.File) -> None:
+ # If either of the parent or the main gfiles are not set, the
+ # relationship is fine (because it's not yet established).
+ if not parent or not descendant:
+ self._parent_gfile = parent
+ self._gfile = descendant
+ return
+
+ descendant_parent = descendant.get_parent()
+ if not descendant_parent:
+ raise ValueError(
+ f'Path {descendant.get_path()} has no parent')
+
+ descendant_or_equal = bool(
+ parent.equal(descendant_parent) or
+ parent.get_relative_path(descendant_parent),
+ )
+
+ if not descendant_or_equal:
+ raise ValueError(
+ f'Path {descendant.get_path()} is not a descendant '
+ f'of {parent.get_path()}')
+
+ self._parent_gfile = parent
+ self._gfile = descendant
+
+ # When thinking about the segmentation we do here, there are
+ # four path components that we care about:
+ #
+ # * any path components above the non-common parent
+ # * the earliest non-common parent
+ # * any path components between the actual filename and the
+ # earliest non-common parent
+ # * the actual filename
+ #
+ # This is easiest to think about with an example of comparing
+ # two files in a parallel repository structure (or similar).
+ # Let's say that you have two copies of Meld at
+ # /home/foo/checkouts/meld and /home/foo/checkouts/meld-new,
+ # and you're comparing meld/filediff.py within those checkouts.
+ # The components we want would then be (left to right):
+ #
+ # ---------------------------------------------
+ # | /home/foo/checkouts | /home/foo/checkouts |
+ # | meld | meld-new |
+ # | meld | meld |
+ # | filediff.py | filediff.py |
+ # ---------------------------------------------
+ #
+ # Of all of these, the first (the first common parent) is the
+ # *only* one that's actually guaranteed to be the same. The
+ # second will *always* be different (or won't exist if e.g.,
+ # you're comparing files in the same folder or similar). The
+ # third component can be basically anything. The fourth
+ # components will often be the same but that's not guaranteed.
+
+ base_path_str = None
+ elided_path = None
+
+ # FIXME: move all of this (and above) path segmenting logic into a
+ # unit-testable helper
+
+ relative_path_str = parent.get_relative_path(descendant_parent)
+
+ if relative_path_str:
+ relative_path = pathlib.Path(relative_path_str)
+
+ base_path_str = relative_path.parts[0]
+ if len(relative_path.parts) == 1:
+ # No directory components, so we have no elided path
+ # segment
+ elided_path = None
+ else:
+ base_path_gfile = parent.get_child(base_path_str)
+ elided_path = base_path_gfile.get_relative_path(
+ descendant_parent)
+
+ label_segments = [
+ '…' if base_path_str else None,
+ base_path_str,
+ '…' if elided_path else None,
+ descendant.get_basename(),
+ ]
+ label_text = GLib.build_filenamev([s for s in label_segments if s])
+
+ # FIXME: this doesn't handle custom labels at all....
+ self.set_label(label_text)
+
def action_copy_full_path(self, *args):
if not self.gfile:
return
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]