[meld] Initial URI support for file diffs
- From: Kai Willadsen <kaiw src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [meld] Initial URI support for file diffs
- Date: Sat, 18 Nov 2017 21:41:16 +0000 (UTC)
commit 3a2fd99159871aae3ccad32bce00de6b219f2f8e
Author: Chris Mayo <aklhfex gmail com>
Date: Sat Sep 30 20:12:49 2017 +0100
Initial URI support for file diffs
- Use GFiles in FileDiff
- Use GFiles in MeldWindow file functions
- Store URIs in recent comparison files
TODO:
- Fix "Password dialogue cancelled" on accessing remote location
- fileentry support for URIs
- shorten_names() specific support for URIs
meld/dirdiff.py | 16 +++++----
meld/filediff.py | 33 ++++++++++----------
meld/meldapp.py | 13 ++++----
meld/meldbuffer.py | 5 ++-
meld/melddoc.py | 2 +-
meld/meldwindow.py | 86 ++++++++++++++++++++++++++++------------------------
meld/misc.py | 4 +-
meld/newdifftab.py | 18 ++++-------
meld/recent.py | 59 ++++++++++++++++++++----------------
meld/vcview.py | 8 +++--
10 files changed, 130 insertions(+), 114 deletions(-)
---
diff --git a/meld/dirdiff.py b/meld/dirdiff.py
index 425b97a..0920b37 100644
--- a/meld/dirdiff.py
+++ b/meld/dirdiff.py
@@ -641,10 +641,11 @@ class DirDiff(melddoc.MeldDoc, gnomeglade.Component):
def get_comparison(self):
root = self.model.get_iter_first()
if root:
- folders = self.model.value_paths(root)
+ uris = [Gio.File.new_for_path(d).get_uri()
+ for d in self.model.value_paths(root)]
else:
- folders = []
- return recent.TYPE_FOLDER, folders
+ uris = []
+ return recent.TYPE_FOLDER, uris
def recursively_update( self, path ):
"""Recursively update from tree path 'path'.
@@ -1144,8 +1145,8 @@ class DirDiff(melddoc.MeldDoc, gnomeglade.Component):
if not rows[pane]:
return
if os.path.isfile(rows[pane]):
- self.emit("create-diff", [r for r in rows if os.path.isfile(r)],
- {})
+ self.emit("create-diff", [Gio.File.new_for_path(r)
+ for r in rows if os.path.isfile(r)], {})
elif os.path.isdir(rows[pane]):
if view.row_expanded(path):
view.collapse_row(path)
@@ -1189,8 +1190,9 @@ class DirDiff(melddoc.MeldDoc, gnomeglade.Component):
def run_diff_from_iter(self, it):
row_paths = self.model.value_paths(it)
- paths = [p for p in row_paths if os.path.exists(p)]
- self.emit("create-diff", paths, {})
+ gfiles = [Gio.File.new_for_path(p)
+ for p in row_paths if os.path.exists(p)]
+ self.emit("create-diff", gfiles, {})
def on_button_diff_clicked(self, button):
pane = self._get_focused_pane()
diff --git a/meld/filediff.py b/meld/filediff.py
index b17fcdf..6c8f754 100644
--- a/meld/filediff.py
+++ b/meld/filediff.py
@@ -1025,12 +1025,12 @@ class FileDiff(melddoc.MeldDoc, gnomeglade.Component):
self.tooltip_text = self.label_text
self.label_changed()
- def set_files(self, files):
+ def set_files(self, gfiles):
"""Load the given files
If an element is None, the text of a pane is left as is.
"""
- if len(files) != self.num_panes:
+ if len(gfiles) != self.num_panes:
return
self._disconnect_buffer_handlers()
@@ -1039,14 +1039,16 @@ class FileDiff(melddoc.MeldDoc, gnomeglade.Component):
custom_candidates = get_custom_encoding_candidates()
- files = [(pane, Gio.File.new_for_path(filename))
- for pane, filename in enumerate(files) if filename]
+ gfiles_enum = [(pane, gfile)
+ for pane, gfile in enumerate(gfiles) if gfile]
- if not files:
+ if not gfiles_enum:
self.scheduler.add_task(self._compare_files_internal())
- for pane, gfile in files:
+ for pane, gfile in gfiles_enum:
self.fileentry[pane].set_file(gfile)
+ # TODO: filentry handling of URIs
+ self.fileentry[pane].set_sensitive(gfile.is_native())
self.msgarea_mgr[pane].clear()
buf = self.textbuffer[pane]
@@ -1067,8 +1069,9 @@ class FileDiff(melddoc.MeldDoc, gnomeglade.Component):
)
def get_comparison(self):
- files = [b.data.filename for b in self.textbuffer[:self.num_panes]]
- return recent.TYPE_FILE, files
+ uris = [b.data.gfile.get_uri()
+ for b in self.textbuffer[:self.num_panes]]
+ return recent.TYPE_FILE, uris
def file_loaded(self, loader, result, user_data):
@@ -1176,8 +1179,7 @@ class FileDiff(melddoc.MeldDoc, gnomeglade.Component):
except ValueError:
# Notification for unknown buffer
return
- gfile = Gio.File.new_for_path(data.filename)
- display_name = gfile.get_parse_name()
+ display_name = data.gfile.get_parse_name()
primary = _("File %s has changed on disk") % display_name
secondary = _("Do you want to reload the file?")
self.msgarea_mgr[pane].add_action_msg(
@@ -1463,7 +1465,7 @@ class FileDiff(melddoc.MeldDoc, gnomeglade.Component):
def save_file(self, pane, saveas=False, force_overwrite=False):
buf = self.textbuffer[pane]
bufdata = buf.data
- if saveas or not (bufdata.filename or bufdata.savefile) \
+ if saveas or not (bufdata.gfile or bufdata.savefile) \
or not bufdata.writable:
if pane == 0:
prompt = _("Save Left Pane As")
@@ -1622,9 +1624,8 @@ class FileDiff(melddoc.MeldDoc, gnomeglade.Component):
def on_fileentry_file_set(self, entry):
entries = self.fileentry[:self.num_panes]
if self.check_save_modified() != Gtk.ResponseType.CANCEL:
- files = [e.get_file() for e in entries]
- paths = [f.get_path() if f is not None else f for f in files]
- self.set_files(paths)
+ gfiles = [e.get_file() for e in entries]
+ self.set_files(gfiles)
else:
idx = entries.index(entry)
existing_path = self.textbuffer[idx].data.filename
@@ -1651,8 +1652,8 @@ class FileDiff(melddoc.MeldDoc, gnomeglade.Component):
dialog.widget.destroy()
if response == Gtk.ResponseType.OK:
- files = [b.data.filename for b in self.textbuffer[:self.num_panes]]
- self.set_files(files)
+ gfiles = [b.data.gfile for b in self.textbuffer[:self.num_panes]]
+ self.set_files(gfiles)
def on_refresh_activate(self, *extra):
self.refresh_comparison()
diff --git a/meld/meldapp.py b/meld/meldapp.py
index f826371..8027c8a 100644
--- a/meld/meldapp.py
+++ b/meld/meldapp.py
@@ -142,19 +142,17 @@ class MeldApp(Gtk.Application):
return self.get_active_window().meldwindow
def open_files(
- self, files, *, window=None, close_on_error=False, **kwargs):
+ self, gfiles, *, window=None, close_on_error=False, **kwargs):
"""Open a comparison between files in a Meld window
- :param files: list of Gio.File to be compared
+ :param gfiles: list of Gio.File to be compared
:param window: window in which to open comparison tabs; if
None, the current window is used
:param close_on_error: if true, close window if an error occurs
"""
window = window or self.get_meld_window()
-
- paths = [f.get_path() for f in files]
try:
- return window.open_paths(paths, **kwargs)
+ return window.open_paths(gfiles, **kwargs)
except ValueError:
if close_on_error:
self.remove_window(window.widget)
@@ -345,7 +343,10 @@ class MeldApp(Gtk.Application):
auto_merge = options.auto_merge and i == 0
try:
for p, f in zip(paths, files):
- if f.get_path() is None:
+ # TODO: support for directories specified by URIs
+ if f.get_uri() is None or (not f.is_native() and
+ f.query_file_type(Gio.FileQueryInfoFlags.NONE, None) ==
+ Gio.FileType.DIRECTORY):
raise ValueError(_("invalid path or URI ā%sā") % p)
tab = self.open_files(
files, window=window,
diff --git a/meld/meldbuffer.py b/meld/meldbuffer.py
index e26cfe6..ce9cb57 100644
--- a/meld/meldbuffer.py
+++ b/meld/meldbuffer.py
@@ -118,7 +118,10 @@ class MeldBufferData(GObject.GObject):
def reset(self, gfile):
same_file = gfile and self._gfile and gfile.equal(self._gfile)
self.gfile = gfile
- self.label = self._label if same_file else self.filename
+ if same_file:
+ self.label = self._label
+ else:
+ self.label = gfile.get_parse_name() if gfile else None
self.loaded = False
self.savefile = None
diff --git a/meld/melddoc.py b/meld/melddoc.py
index d3e329d..723e2f3 100644
--- a/meld/melddoc.py
+++ b/meld/melddoc.py
@@ -107,7 +107,7 @@ class MeldDoc(LabeledObjectMixin, GObject.GObject):
self._state = value
def get_comparison(self):
- """Get the comparison type and path(s) being compared"""
+ """Get the comparison type and URI(s) being compared"""
pass
def save(self):
diff --git a/meld/meldwindow.py b/meld/meldwindow.py
index cbfeb93..7f97af8 100644
--- a/meld/meldwindow.py
+++ b/meld/meldwindow.py
@@ -271,11 +271,8 @@ class MeldWindow(gnomeglade.Component):
def on_widget_drag_data_received(self, wid, context, x, y, selection_data,
info, time):
- if len(selection_data.get_uris()) != 0:
- paths = []
- for uri in selection_data.get_uris():
- paths.append(Gio.File.new_for_uri(uri).get_path())
- self.open_paths(paths)
+ if len(selection_data.get_files()) != 0:
+ self.open_paths(selection_data.get_files())
return True
def on_idle(self):
@@ -569,7 +566,8 @@ class MeldWindow(gnomeglade.Component):
doc.connect("diff-created", diff_created_cb)
return doc
- def append_dirdiff(self, dirs, auto_compare=False):
+ def append_dirdiff(self, gfiles, auto_compare=False):
+ dirs = [d.get_path() for d in gfiles if d]
assert len(dirs) in (1, 2, 3)
doc = dirdiff.DirDiff(len(dirs))
self._append_page(doc, "folder")
@@ -578,93 +576,101 @@ class MeldWindow(gnomeglade.Component):
doc.scheduler.add_task(doc.auto_compare)
return doc
- def append_filediff(self, files, merge_output=None, meta=None):
- assert len(files) in (1, 2, 3)
- doc = filediff.FileDiff(len(files))
+ def append_filediff(self, gfiles, merge_output=None, meta=None):
+ assert len(gfiles) in (1, 2, 3)
+ doc = filediff.FileDiff(len(gfiles))
self._append_page(doc, "text-x-generic")
- doc.set_files(files)
+ doc.set_files(gfiles)
if merge_output is not None:
doc.set_merge_output_file(merge_output)
if meta is not None:
doc.set_meta(meta)
return doc
- def append_filemerge(self, files, merge_output=None):
- if len(files) != 3:
+ def append_filemerge(self, gfiles, merge_output=None):
+ if len(gfiles) != 3:
raise ValueError(
- _("Need three files to auto-merge, got: %r") % files)
- doc = filemerge.FileMerge(len(files))
+ _("Need three files to auto-merge, got: %r") %
+ [f.get_parse_name() for f in gfiles])
+ doc = filemerge.FileMerge(len(gfiles))
self._append_page(doc, "text-x-generic")
- doc.set_files(files)
+ doc.set_files(gfiles)
if merge_output is not None:
doc.set_merge_output_file(merge_output)
return doc
- def append_diff(self, paths, auto_compare=False, auto_merge=False,
+ def append_diff(self, gfiles, auto_compare=False, auto_merge=False,
merge_output=None, meta=None):
- dirslist = [p for p in paths if os.path.isdir(p)]
- fileslist = [p for p in paths if os.path.isfile(p)]
- if dirslist and fileslist:
+ have_directories = False
+ have_files = False
+ for f in gfiles:
+ if f.query_file_type(
+ Gio.FileQueryInfoFlags.NONE, None) == Gio.FileType.DIRECTORY:
+ have_directories = True
+ else:
+ have_files = True
+ if have_directories and have_files:
raise ValueError(
_("Cannot compare a mixture of files and directories"))
- elif dirslist:
- return self.append_dirdiff(paths, auto_compare)
+ elif have_directories:
+ return self.append_dirdiff(gfiles, auto_compare)
elif auto_merge:
- return self.append_filemerge(paths, merge_output=merge_output)
+ return self.append_filemerge(gfiles, merge_output=merge_output)
else:
return self.append_filediff(
- paths, merge_output=merge_output, meta=meta)
+ gfiles, merge_output=merge_output, meta=meta)
def append_vcview(self, location, auto_compare=False):
doc = vcview.VcView()
self._append_page(doc, "meld-version-control")
location = location[0] if isinstance(location, list) else location
- doc.set_location(location)
+ doc.set_location(location.get_path())
if auto_compare:
doc.scheduler.add_task(doc.auto_compare)
return doc
def append_recent(self, uri):
- comparison_type, files, flags = recent_comparisons.read(uri)
+ comparison_type, gfiles, flags = recent_comparisons.read(uri)
if comparison_type == recent.TYPE_MERGE:
- tab = self.append_filemerge(files)
+ tab = self.append_filemerge(gfiles)
elif comparison_type == recent.TYPE_FOLDER:
- tab = self.append_dirdiff(files)
+ tab = self.append_dirdiff(gfiles)
elif comparison_type == recent.TYPE_VC:
# Files should be a single-element iterable
- tab = self.append_vcview(files[0])
+ tab = self.append_vcview(gfiles[0])
else: # comparison_type == recent.TYPE_FILE:
- tab = self.append_filediff(files)
+ tab = self.append_filediff(gfiles)
self.notebook.set_current_page(self.notebook.page_num(tab.widget))
recent_comparisons.add(tab)
return tab
- def _single_file_open(self, path):
+ def _single_file_open(self, gfile):
doc = vcview.VcView()
def cleanup():
self.scheduler.remove_scheduler(doc.scheduler)
self.scheduler.add_task(cleanup)
self.scheduler.add_scheduler(doc.scheduler)
- path = os.path.abspath(path)
+ path = gfile.get_path()
doc.set_location(path)
doc.connect("create-diff", lambda obj, arg, kwargs:
self.append_diff(arg, **kwargs))
doc.run_diff(path)
- def open_paths(self, paths, auto_compare=False, auto_merge=False,
+ def open_paths(self, gfiles, auto_compare=False, auto_merge=False,
focus=False):
tab = None
- if len(paths) == 1:
- a = paths[0]
- if os.path.isfile(a):
- self._single_file_open(a)
- else:
+ if len(gfiles) == 1:
+ a = gfiles[0]
+ if a.query_file_type(Gio.FileQueryInfoFlags.NONE, None) == \
+ Gio.FileType.DIRECTORY:
tab = self.append_vcview(a, auto_compare)
+ else:
+ self._single_file_open(a)
- elif len(paths) in (2, 3):
- tab = self.append_diff(
- paths, auto_compare=auto_compare, auto_merge=auto_merge)
+ elif len(gfiles) in (2, 3):
+ tab = self.append_diff(gfiles, auto_compare=auto_compare,
+ auto_merge=auto_merge)
if tab:
recent_comparisons.add(tab)
if focus:
diff --git a/meld/misc.py b/meld/misc.py
index 26b275f..f368eab 100644
--- a/meld/misc.py
+++ b/meld/misc.py
@@ -249,7 +249,7 @@ def all_same(lst):
def shorten_names(*names):
"""Remove redunant parts of a list of names (e.g. /tmp/foo{1,2} -> foo{1,2}
"""
- # TODO: Update for different path separators
+ # TODO: Update for different path separators and URIs
prefix = os.path.commonprefix(names)
prefixslash = prefix.rfind("/") + 1
@@ -263,7 +263,7 @@ def shorten_names(*names):
else:
if all_same(basenames):
def firstpart(alist):
- if len(alist) > 1:
+ if len(alist) > 1 and alist[0]:
return "[%s] " % alist[0]
else:
return ""
diff --git a/meld/newdifftab.py b/meld/newdifftab.py
index 3326c84..2e4fb0c 100644
--- a/meld/newdifftab.py
+++ b/meld/newdifftab.py
@@ -15,6 +15,7 @@
import os
+from gi.repository import Gio
from gi.repository import GLib
from gi.repository import GObject
from gi.repository import Gtk
@@ -105,16 +106,9 @@ class NewDiffTab(LabeledObjectMixin, GObject.GObject, gnomeglade.Component):
def on_button_compare_clicked(self, *args):
type_choosers = (self.file_chooser, self.dir_chooser, self.vc_chooser)
-
- compare_paths = []
- num_paths = self._get_num_paths()
- for chooser in type_choosers[self.diff_type][:num_paths]:
- gfile = chooser.get_file()
- path = gfile.get_path() if gfile else ""
- compare_paths.append(path)
-
- # TODO: We should be migrating to passing around GFiles
- tab = self.diff_methods[self.diff_type](compare_paths)
+ compare_gfiles = [c.get_file() for c in
+ type_choosers[self.diff_type][:self._get_num_paths()]]
+ tab = self.diff_methods[self.diff_type](compare_gfiles)
recent_comparisons.add(tab)
self.emit('diff-created', tab)
@@ -122,8 +116,8 @@ class NewDiffTab(LabeledObjectMixin, GObject.GObject, gnomeglade.Component):
# TODO: This doesn't work the way I'd like for DirDiff and VCView.
# It should do something similar to FileDiff; give a tab with empty
# file entries and no comparison done.
- compare_paths = [""] * self._get_num_paths()
- tab = self.diff_methods[self.diff_type](compare_paths)
+ compare_gfiles = [Gio.File.new_for_path("")] * self._get_num_paths()
+ tab = self.diff_methods[self.diff_type](compare_gfiles)
self.emit('diff-created', tab)
def on_container_switch_in_event(self, *args):
diff --git a/meld/recent.py b/meld/recent.py
index adf8275..380684b 100644
--- a/meld/recent.py
+++ b/meld/recent.py
@@ -81,35 +81,35 @@ class RecentFiles(object):
The passed flags are currently ignored. In the future these are to be
used for extra initialisation not captured by the tab itself.
"""
- comp_type, paths = tab.get_comparison()
+ comp_type, uris = tab.get_comparison()
# While Meld handles comparisons including None, recording these as
# recently-used comparisons just isn't that sane.
- if None in paths:
+ if None in uris:
return
- # If a (type, paths) comparison is already registered, then re-add
+ # If a (type, uris) comparison is already registered, then re-add
# the corresponding comparison file
- comparison_key = (comp_type, tuple(paths))
- paths = [unicodeify(p) for p in paths]
+ comparison_key = (comp_type, tuple(uris))
+ uris = [unicodeify(u) for u in uris]
if comparison_key in self._stored_comparisons:
- gio_file = Gio.File.new_for_uri(
+ gfile = Gio.File.new_for_uri(
self._stored_comparisons[comparison_key])
else:
- recent_path = self._write_recent_file(comp_type, paths)
- gio_file = Gio.File.new_for_path(recent_path)
+ recent_path = self._write_recent_file(comp_type, uris)
+ gfile = Gio.File.new_for_path(recent_path)
- if len(paths) > 1:
- display_name = " : ".join(meld.misc.shorten_names(*paths))
+ if len(uris) > 1:
+ display_name = " : ".join(meld.misc.shorten_names(*uris))
else:
- display_path = paths[0]
+ display_path = uris[0]
userhome = os.path.expanduser("~")
if display_path.startswith(userhome):
# FIXME: What should we show on Windows?
display_path = "~" + display_path[len(userhome):]
display_name = _("Version control:") + " " + display_path
# FIXME: Should this be translatable? It's not actually used anywhere.
- description = "%s comparison\n%s" % (comp_type, ", ".join(paths))
+ description = "%s comparison\n%s" % (comp_type, ", ".join(uris))
recent_metadata = Gtk.RecentData()
recent_metadata.mime_type = self.mime_type
@@ -118,38 +118,45 @@ class RecentFiles(object):
recent_metadata.display_name = display_name
recent_metadata.description = description
recent_metadata.is_private = True
- self.recent_manager.add_full(gio_file.get_uri(), recent_metadata)
+ self.recent_manager.add_full(gfile.get_uri(), recent_metadata)
def read(self, uri):
"""Read stored comparison from URI
- Returns the comparison type, the paths involved and the comparison
+ Returns the comparison type, the URIs involved and the comparison
flags.
"""
- gio_file = Gio.File.new_for_uri(uri)
- path = gio_file.get_path()
- if not gio_file.query_exists(None) or not path:
- raise IOError("File does not exist")
+ comp_gfile = Gio.File.new_for_uri(uri)
+ comp_path = comp_gfile.get_path()
+ if not comp_gfile.query_exists(None) or not comp_path:
+ raise IOError("Recent comparison file does not exist")
+ # TODO: remove reading paths in next release
try:
config = configparser.RawConfigParser()
- config.read(path)
+ config.read(comp_path)
assert (config.has_section("Comparison") and
config.has_option("Comparison", "type") and
- config.has_option("Comparison", "paths"))
+ (config.has_option("Comparison", "paths") or
+ config.has_option("Comparison", "uris")))
except (configparser.Error, AssertionError):
raise ValueError("Invalid recent comparison file")
comp_type = config.get("Comparison", "type")
- paths = tuple(config.get("Comparison", "paths").split(";"))
- flags = tuple()
-
if comp_type not in COMPARISON_TYPES:
raise ValueError("Invalid recent comparison file")
- return comp_type, paths, flags
+ if config.has_option("Comparison", "uris"):
+ gfiles = tuple([Gio.File.new_for_uri(u)
+ for u in tuple(config.get("Comparison", "uris").split(";"))])
+ else:
+ gfiles = tuple([Gio.File.new_for_path(p)
+ for p in tuple(config.get("Comparison", "paths").split(";"))])
+ flags = tuple()
+
+ return comp_type, gfiles, flags
- def _write_recent_file(self, comp_type, paths):
+ def _write_recent_file(self, comp_type, uris):
# TODO: Use GKeyFile instead, and return a Gio.File. This is why we're
# using ';' to join comparison paths.
with tempfile.NamedTemporaryFile(
@@ -158,7 +165,7 @@ class RecentFiles(object):
config = configparser.RawConfigParser()
config.add_section("Comparison")
config.set("Comparison", "type", comp_type)
- config.set("Comparison", "paths", ";".join(paths))
+ config.set("Comparison", "uris", ";".join(uris))
config.write(f)
name = f.name
return name
diff --git a/meld/vcview.py b/meld/vcview.py
index 19b839b..13ba513 100644
--- a/meld/vcview.py
+++ b/meld/vcview.py
@@ -325,7 +325,8 @@ class VcView(melddoc.MeldDoc, gnomeglade.Component):
self.scheduler.add_task(self.on_treeview_cursor_changed)
def get_comparison(self):
- return recent.TYPE_VC, [self.location]
+ uris = [Gio.File.new_for_path(self.location).get_uri()]
+ return recent.TYPE_VC, uris
def recompute_label(self):
self.label_text = os.path.basename(self.location)
@@ -436,7 +437,7 @@ class VcView(melddoc.MeldDoc, gnomeglade.Component):
def run_diff(self, path):
if os.path.isdir(path):
- self.emit("create-diff", [path], {})
+ self.emit("create-diff", [Gio.File.new_for_path(path)], {})
return
basename = os.path.basename(path)
@@ -492,7 +493,8 @@ class VcView(melddoc.MeldDoc, gnomeglade.Component):
os.chmod(temp_file, 0o444)
_temp_files.append(temp_file)
- self.emit("create-diff", diffs, kwargs)
+ self.emit("create-diff",
+ [Gio.File.new_for_path(d) for d in diffs], kwargs)
def do_popup_treeview_menu(self, widget, event):
if event:
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]