From 692f2b7b8c261983ceec9963a0b37eb08ae9e9b3 Mon Sep 17 00:00:00 2001 From: David Aguilar Date: Sat, 3 Jun 2023 03:10:34 -0700 Subject: [PATCH] diff: teach the diff editor to append to existing patches Add a context menu that allows appending to an existing patch. Signed-off-by: David Aguilar --- CHANGES.rst | 7 ++++-- cola/widgets/diff.py | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 73 insertions(+), 3 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 2cf10137..0fcb6caa 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -13,8 +13,11 @@ Usability, bells and whistles is now configurable using the `cola.patchesdirectory` configuration variable and the Preferences dialog. -* The Diff Editor can now export the diff selection, or the - current diff hunk, to a `*.patch` file. +* The Diff Editor can now export the diff selection, or the current + diff hunk, to a `*.patch` file from the `Patches` context menu action. + +* Existing patches can be appended to by choosing a patch file from + the `Append Patch` sub-menu in the `Patches` context menu action. Fixes ----- diff --git a/cola/widgets/diff.py b/cola/widgets/diff.py index a5871bb7..1b87c066 100644 --- a/cola/widgets/diff.py +++ b/cola/widgets/diff.py @@ -29,6 +29,7 @@ from .text import TextDecorator from .text import VimHintedPlainTextEdit from .text import TextSearchWidget from . import defs +from . import patch as patch_mod from . import imageview @@ -1191,6 +1192,57 @@ def _add_patch_actions(widget, context, menu): export_action.setIcon(icons.save()) patches_menu.addAction(export_action) + # Build the "Append Patch" menu dynamically. + append_menu = patches_menu.addMenu(N_('Append Patch')) + append_menu.setIcon(icons.add()) + append_menu.aboutToShow.connect( + lambda: _build_patch_append_menu(widget, context, append_menu) + ) + + +def _build_patch_append_menu(widget, context, menu): + """Build the "Append Patch" submenu""" + # Build the menu when first displayed only. This initial check avoids + # re-populating the menu with duplicate actions. + menu_actions = menu.actions() + if menu_actions: + return + subdir_menus = {} + path = prefs.patches_directory(context) + patches = patch_mod.get_patches_from_dir(path) + for patch in patches: + relpath = os.path.relpath(patch, start=path) + sub_menu = _add_patch_subdirs(menu, subdir_menus, relpath) + patch_basename = os.path.basename(relpath) + append_action = qtutils.add_action( + sub_menu, + patch_basename, + lambda patch_file=patch: _append_patch(widget, patch_file), + ) + append_action.setIcon(icons.save()) + sub_menu.addAction(append_action) + + +def _add_patch_subdirs(menu, subdir_menus, relpath): + """Build menu leading up to the patch""" + # If the path contains no directory separators then add it to the + # root of the menu. + if os.sep not in relpath: + return menu + + # Loop over each directory component and build a menu if it doesn't already exist. + components = [] + for dirname in os.path.dirname(relpath).split(os.sep): + components.append(dirname) + current_dir = os.sep.join(components) + try: + menu = subdir_menus[current_dir] + except KeyError: + menu = subdir_menus[current_dir] = menu.addMenu(dirname) + menu.setIcon(icons.folder()) + + return menu + def _export_patch(diff_editor, context): """Export the selected diff to a patch file""" @@ -1203,10 +1255,25 @@ def _export_patch(diff_editor, context): filename = qtutils.save_as(default_filename) if not filename: return + _write_patch_to_file(diff_editor, patch, filename) + + +def _append_patch(diff_editor, filename): + """Append diffs to the specified patch file""" + if diff_editor.selection_model.is_empty(): + return + patch = diff_editor.extract_patch(reverse=False) + if not patch.has_changes(): + return + _write_patch_to_file(diff_editor, patch, filename, append=True) + + +def _write_patch_to_file(diff_editor, patch, filename, append=False): + """Write diffs from the Diff Editor to the specified patch file""" encoding = diff_editor.patch_encoding() content = patch.as_text() try: - core.write(filename, content, encoding=encoding) + core.write(filename, content, encoding=encoding, append=append) except (IOError, OSError) as exc: _, details = utils.format_exception(exc) title = N_('Error writing patch') -- 2.11.4.GIT