1
1
mirror of https://github.com/mawww/kakoune.git synced 2024-11-26 16:36:49 +03:00
kakoune/rc/tools/patch.kak
Johannes Altmanninger 8c0424b521 rc tools patch: "patch" command to apply selections in diffs to file
One of the features I miss most from Magit/Fugitive/Tig is to
apply/revert/stage/unstage individual hunks or even exactly the
selected line(s).  This provides a much more convenient way of
splitting changes than "git add/restore -p".

Implement a "patch" command that applies the selected lines within
a diff by piping them to the "patch" program.
It can also feed other programs like "git apply" (see the next commit).

Original discussion: https://discuss.kakoune.com/t/atomic-commits-in-kakoune/1446

Interestingly, :patch is defined outside the "patch" module. This is
to make it readily available for interactive use.
Putting it into the module does not save any work.
I tentatively added a patch module anyway so we can explicitly declare
this dependency.. although there is the argument that this is not
really needed?
2023-11-04 12:14:48 +01:00

67 lines
2.9 KiB
Plaintext

define-command patch -params .. -docstring %{
patch [<arguments>]: apply selections in diff to a file
Given some selections within a unified diff, apply the changed lines in
each selection by piping them to "patch <arguments> 1>&2"
(or "<arguments> 1>&2" if <arguments> starts with a non-option argument).
If successful, the in-buffer diff will be updated to reflect the applied
changes.
For selections that contain no newline, the entire enclosing diff hunk
is applied (unless the cursor is inside a diff header, in which case
the entire diff is applied).
To revert changes, <arguments> must contain "--reverse" or "-R".
} %exp{
evaluate-commands -draft -itersel -save-regs aefs|^ %%{
set-register f %val{source}
%{
try %{
execute-keys <a-k>\n<ret>
} catch %{
# The selection contains no newline.
execute-keys -save-regs '' Z
execute-keys <a-l><semicolon><a-?>^diff<ret>
try %{
execute-keys <a-k>^@@<ret>
# If the cursor is in a diff hunk, stage the entire hunk.
execute-keys z
execute-keys /.*?(?:(?=\n@@)|(?=\ndiff)|(?=\n\n)|\z)<ret>x<semicolon><a-?>^@@<ret>
} catch %{
# If the cursor is in a diff header, stage the entire diff.
execute-keys <a-semicolon>?.*?(?:(?=\ndiff)|(?=\n\n)|\z)<ret>
}
}
# We want to apply only the selected lines. Remember them.
execute-keys <a-:>
set-register s %val{selection_desc}
# Select forward until the end of the last hunk.
execute-keys H?.*?(?:(?=\n@@)|(?=\ndiff)|(?=\n\n)|\z)<ret>x
# Select backward to the beginning of the first hunk's diff header.
execute-keys <a-semicolon><a-L><a-?>^diff<ret>
# Move cursor to the beginning so we know the diff's offset within the buffer.
execute-keys <a-:><a-semicolon>
set-register a %arg{@}
set-register e nop
set-register | %{
# The selected range to apply.
IFS=' .,' read min_line _ max_line _ <<-EOF
$kak_reg_s
EOF
min_line=$((min_line - kak_cursor_line + 1))
max_line=$((max_line - kak_cursor_line + 1))
# Since registers are never empty, we get an empty arg even if
# there were no args. This does no harm because we pass it to
# a shell where it expands to nothing.
eval set -- $kak_quoted_reg_a
"${kak_reg_f%/*}/patch-range.pl" $min_line $max_line "$@" ||
echo >$kak_command_fifo "set-register e fail 'patch: failed to apply selections, see *debug* buffer'"
}
execute-keys |<ret>
%reg{e}
}
}
}
provide-module patch %§§