From 13948ecb941a4a684cc1ed29f903af54684c1378 Mon Sep 17 00:00:00 2001 From: Johannes Altmanninger Date: Sat, 12 Feb 2022 16:12:29 +0100 Subject: [PATCH] rc diff: pass diff to diff-jump via stdin instead of env Passing large diff buffers via the environment can quickly result in the error "execve failed: Argument list too long". Use a pipe like in format.kak When running | (or ), Kakoune does not use %arg{@} to populate "$@" (missing feature?). Work around this by moving %arg{@} to a temporary register. Apparently $kak_quoted_reg_a will never be an empty list, so work around that too. When diff parsing fails, we take care to run "fail" in the calling client, unlike :format (probably a bug in format.kak). (This patch is best viewed while ignoring whitespace changes (diff -w)) --- rc/filetype/diff.kak | 191 ++++++++++++++++++++++--------------------- 1 file changed, 97 insertions(+), 94 deletions(-) diff --git a/rc/filetype/diff.kak b/rc/filetype/diff.kak index 29cbad841..f360dda4a 100644 --- a/rc/filetype/diff.kak +++ b/rc/filetype/diff.kak @@ -27,7 +27,7 @@ define-command diff-jump -params .. -docstring %{ - jump to the old file instead of the new file - strip leading directory components, like -p in patch(1). Defaults to 1 if there is a 'diff' line (as printed by 'diff -r'), or 0 otherwise. } %{ - evaluate-commands -draft -save-regs c %{ + evaluate-commands -draft -save-regs ac| %{ # Save the column because we will move the cursor. set-register c %val{cursor_column} # If there is a "diff" line, we don't need to look further back. @@ -39,111 +39,114 @@ define-command diff-jump -params .. -docstring %{ # or content. execute-keys Gk } - evaluate-commands %sh{ - printf %s "$kak_selection" | - column=$kak_reg_c perl -we ' - sub quote { - $SQ = "'\''"; - $token = shift; - $token =~ s/$SQ/$SQ$SQ/g; - return "$SQ$token$SQ"; + set-register a %arg{@} + set-register | %{ + [ -n "$kak_reg_a" ] && eval set -- $kak_quoted_reg_a + cmd=$(column=$kak_reg_c perl -we ' + sub quote { + $SQ = "'\''"; + $token = shift; + $token =~ s/$SQ/$SQ$SQ/g; + return "$SQ$token$SQ"; + } + sub fail { + $reason = shift; + print "fail " . quote("diff-jump: $reason"); + exit; + } + $version = "+", $other_version = "-"; + $strip = undef; + $directory = $ENV{PWD}; + $seen_ddash = 0; + foreach (@ARGV) { + if ($seen_ddash or !m{^-}) { + $directory = $_; + } elsif ($_ eq "-") { + $version = "-", $other_version = "+"; + } elsif (m{^-(\d+)$}) { + $strip = $1; + } elsif ($_ eq "--") { + $seen_ddash = 1; + } else { + fail "unknown option: $_"; } - sub fail { - $reason = shift; - print "fail " . quote("diff-jump: $reason"); - exit 1; - } - $version = "+", $other_version = "-"; - $strip = undef; - $directory = $ENV{PWD}; - $seen_ddash = 0; - foreach (@ARGV) { - if ($seen_ddash or !m{^-}) { - $directory = $_; - } elsif ($_ eq "-") { - $version = "-", $other_version = "+"; - } elsif (m{^-(\d+)$}) { - $strip = $1; - } elsif ($_ eq "--") { - $seen_ddash = 1; - } else { - fail "unknown option: $_"; + } + $have_diff_line = 0; + $state = "header"; + while () { + s/^(> )*//g; + $last_line = $_; + if (m{^diff\b}) { + $state = "header"; + $have_diff_line = 1; + if (m{^diff -\S* (\S+) (\S+)$}) { + $fallback_file = $version eq "+" ? $2 : $1; } + next; } - $have_diff_line = 0; - $state = "header"; - while () { - s/^(> )*//g; - $last_line = $_; - if (m{^diff\b}) { - $state = "header"; - $have_diff_line = 1; - if (m{^diff -\S* (\S+) (\S+)$}) { - $fallback_file = $version eq "+" ? $2 : $1; - } + if ($state eq "header") { + if (m{^[$version]{3} ([^\t\n]+)}) { + $file = $1; next; } - if ($state eq "header") { - if (m{^[$version]{3} ([^\t\n]+)}) { - $file = $1; + if (m{^[$other_version]{3} ([^\t\n]+)}) { + $fallback_file = $1; + next; + } + } + if (m{^@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@}) { + $state = "contents"; + $line = ($version eq "+" ? $2 : $1) - 1; + } elsif (m{^[ $version]}) { + $line++ if defined $line; + } + } + if (not defined $file) { + $file = $fallback_file; + } + if (not defined $file) { + fail "missing diff header"; + } + if (not defined $strip) { + # A "diff -r" or "git diff" adds "diff" lines to + # the output. If no such line is present, we have + # a plain diff between files (not directories), so + # there should be no need to strip the directory. + $strip = $have_diff_line ? 1 : 0; + } + if ($file !~ m{^/}) { + $file =~ s,^([^/]+/+){$strip},, or fail "directory prefix underflow"; + $file = "$directory/$file"; + } + + if (defined $line) { + $column = $ENV{column} - 1; # Account for [ +-] diff prefix. + # If the cursor was on a hunk header, go to the section header if possible. + if ($last_line =~ m{^(@@ -\d+(?:,\d+)? \+\d+(?:,\d+) @@ )([^\n]*)}) { + $hunk_header_prefix = $1; + $hunk_header_from_userdiff = $2; + open FILE, "<", $file or fail "failed to open file: $!: $file"; + @lines = ; + for (my $i = $line - 1; $i >= 0 && $i < scalar @lines; $i--) { + if ($lines[$i] !~ m{\Q$hunk_header_from_userdiff}) { next; } - if (m{^[$other_version]{3} ([^\t\n]+)}) { - $fallback_file = $1; - next; - } - } - if (m{^@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@}) { - $state = "contents"; - $line = ($version eq "+" ? $2 : $1) - 1; - } elsif (m{^[ $version]}) { - $line++ if defined $line; + $line = $i + 1; + # Re-add 1 because the @@ line does not have a [ +-] diff prefix. + $column = $column + 1 - length $hunk_header_prefix; + last; } } - if (not defined $file) { - $file = $fallback_file; - } - if (not defined $file) { - fail "missing diff header"; - } - if (not defined $strip) { - # A "diff -r" or "git diff" adds "diff" lines to - # the output. If no such line is present, we have - # a plain diff between files (not directories), so - # there should be no need to strip the directory. - $strip = $have_diff_line ? 1 : 0; - } - if ($file !~ m{^/}) { - $file =~ s,^([^/]+/+){$strip},, or fail "directory prefix underflow"; - $file = "$directory/$file"; - } + } - if (defined $line) { - $column = $ENV{column} - 1; # Account for [ +-] diff prefix. - # If the cursor was on a hunk header, go to the section header if possible. - if ($last_line =~ m{^(@@ -\d+(?:,\d+)? \+\d+(?:,\d+) @@ )([^\n]*)}) { - $hunk_header_prefix = $1; - $hunk_header_from_userdiff = $2; - open FILE, "<", $file or fail "failed to open file: $!: $file"; - @lines = ; - for (my $i = $line - 1; $i >= 0 && $i < scalar @lines; $i--) { - if ($lines[$i] !~ m{\Q$hunk_header_from_userdiff}) { - next; - } - $line = $i + 1; - # Re-add 1 because the @@ line does not have a [ +-] diff prefix. - $column = $column + 1 - length $hunk_header_prefix; - last; - } - } - } - - printf "set-register c %s $line $column", quote($file); - ' -- "$@" + printf "edit -existing -- %s $line $column", quote($file); + ' -- "$@") + echo "set-register c $cmd" >"$kak_command_fifo" } + execute-keys evaluate-commands -client %val{client} %{ evaluate-commands -try-client %opt{jumpclient} %{ - edit -existing -- %reg{c} + %reg{c} } } } @@ -156,7 +159,7 @@ define-command \ -docstring %{diff-select-file: Select surrounding patch file} \ -params 0 \ diff-select-file %{ - evaluate-commands -itersel -save-regs 'ose/' %{ + evaluate-commands -itersel -save-regs 'ose/' %{ try %{ execute-keys '"oZgl^diff ;"sZ' 'Ge"eZ' try %{ execute-keys '"sz?\n(?=diff )"e' }