Consider
sh -c 'sleep 5 & sleep inf'
Since the shell is non-interactive, there is no job control.
This makes the shell spawn the "sleep 5" process in the shell's own
process group[1] - presumably, because only interactive shells have
a need to forward signals to all processes in its foreground job.
When this non-interactive shell process is cancelled with SIGINT,
"sleep 5" keeps running [2]. At least the dash shell implements this
by running "signal(SIGINT, SIG_IGN)" in the forked child. Unless the
child process explicitly overrides that (to SIG_DFL for example), it
will ignore SIGINT. Probably the reason for this behavior is to feign
consistency with interactive shells, without needing to actually run
background jobs in a dedicated process group like interactive shells
do. Bash documents this behavior[3]:
> When job control is not in effect, asynchronous commands ignore
> SIGINT and SIGQUIT in addition to these inherited handlers.
Several of our scripts[4] - most prominently ":make" - use the
"</dev/null >/dev/null 2>&1 &" pattern to run potentially long-running
processes in the background, without blocking the editor.
On <c-c>, we send SIGINT to our process group.
As explained above, this will generally not terminate any background processes.
This problem has been masked by a behavior that is unique to using
both Bash and its "eval" builtin. Given
nop %sh{
rm -f /tmp/fifo
mkfifo /tmp/fifo
(
eval make >/tmp/fifo 2>&1 &
) >/dev/null 2>&1 </dev/null
}
edit -fifo /tmp/fifo *my-fifo*
When running this and pressing Control+C, Bash actually terminates
the Make processes. However if I remove the "eval", it no longer does.
This doesn't seems like something we should rely on.
Other commands like ":git blame" don't use "eval" so they cannot be
cancelled today.
Fix these issues by sending SIGTERM instead of SIGINT, which should
apply to the whole process group with pretty much the same effect.
Barely tested, let's see if this breaks some weird build system.
In future we might allow more fine-grained control over which processes
are cancelled by <c-c>.
{{{
Alternative solution:
With the above fix, scripts can opt-out of being terminated by <c-c>
by using setsid (though that's not POSIX unfortunately, and may
require nesting quotes) or the classic Unix double-forking trick to
create a daemon process.
Though it is certainly possible that someone expects commands like
this to survive <c-c>:
nop %sh{ tail -f my-log </dev/null 2>&1 | grep some-error > some-file 2>&1 & }
I think it would be ideal to stick to SIGINT and match semantics of
a noninteractive shell, to avoid muddying the waters.
Background processes could still **opt into** being terminated by
<c-c>. For example by providing a simple program in libexec/ that does
// interruptible.c
int main(int argc, char** argv) {
signal(SIGINT, SIG_DFL);
execv(argv[1], &argv[1]);
}
used as
diff --git a/rc/tools/make.kak b/rc/tools/make.kak
index b88f7e538..f6e041908 100644
--- a/rc/tools/make.kak
+++ b/rc/tools/make.kak
@@ -16,3 +16,3 @@ define-command -params .. \
mkfifo ${output}
- ( eval "${kak_opt_makecmd}" "$@" > ${output} 2>&1 & ) > /dev/null 2>&1 < /dev/null
+ ( eval "interruptible ${kak_opt_makecmd}" "$@" > ${output} 2>&1 & ) > /dev/null 2>&1 < /dev/null
Unfortunately, it's inconvenient to add "interruptible" to commands
like clang-parse and git-blame because they background a whole subshell
with many commands, so we'd need to nest quotes. Also I'm not sure
if this brings any benefit.
So I didn't explore this further yet although we can definitely do that.
}}}
Fixes#3751
[1]: https://stackoverflow.com/questions/45106725/why-do-shells-ignore-sigint-and-sigquit-in-backgrounded-processes/45106961#45106961
[2]: https://unix.stackexchange.com/questions/372541/why-doesnt-sigint-work-on-a-background-process-in-a-script/677742#677742
[3]: https://www.gnu.org/software/bash/manual/html_node/Signals.html
[4]: clang-parse, ctags-*, git blame, git log, gopls references,
grep, jedi-complete, lint-*, make; I don't think any of these
should be uninterruptible.
The tmux-terminal commands typically run
tmux split-window kak -c ${kak_session} </dev/null >/dev/null 2>&1 &
The tmux process runs in the background with output silenced. This is
not necessary because "tmux split-window" is a thin client that
merely forwards its arguments to the tmux server. All our wrappers
for other terminal-servers (kitty, iterm, screen, wezterm, zellij)
simply run in the foreground, not silencing any errors.
The tmux backgrounding was added in 208b91627 (Move client.kak as
x11.kak and change tmux.kak to be its peer, 2015-11-17), probably for
consistency with x11.kak. That one is different however because it
potentially spawns a full terminal, not just a client that briefly
talks to a terminal server - that's why x11-terminal needs to use
"setsid," to avoid killing said terminal when we signal our process
group.
Remove the backgrounding from tmux.kak for consistency and to reduce
surprise.
`grep-next-match` works only on the `*grep*` buffer so it can't be used
on buffers that were preserved by renaming or on other grep-flavored
buffers created by 3rd party plugins kakoune-find and kakoune-lsp,
like `*find*` and `*references*`.
Let's generalize `grep-next-match` with a `jump-next` command that
takes a buffer argument.
This renames some options but I think they're not commonly used.
kakoune-lsp is an exception that uses grep_current_line but it's no big
deal, things will fail loud and early if options are missing.
Since grep.kak and friends now depend on jump.kak, move the jumpclient
declaration there as well.
Rename suffix to tag as it ends up not being the filename suffix,
apply that tag to .o and .d files so that changing the build type
does not wipe/reuse files from another build type.
Make .d file hidden files, this does not seem possible for the .o
files as they are targets and posix support inference rules when only
the suffix changes. This is not a big issue as Kakoune ignores those
files by default.
I dedicate any and all copyright interest in this software to the
public domain. I make this dedication for the benefit of the public at
large and to the detriment of my heirs and successors. I intend this
dedication to be an overt act of relinquishment in perpetuity of all
present and future rights to this software under copyright law.
Since the default make error pattern spans until the end of the
line, make-jump select the whole line, moving the cursor to the end.
This is especially bad when the error message is very long and hence
the cursor movement scrolls the viewport so the file:line:col is no
longer visible.
Stop moving the cursor in `*make*` and `*grep*` buffers.
We already have highlighting to indicate the current line.
Given
make[1]: Entering directory '/home/johannes/git/kakoune/src'
main.cc:66:9: error: expected ‘}’ before ‘asdf’
If I select the whole second line, including the newline, make-jump
fails with an enigmatic "no such file or directory main.cc". This is
because `gl` does not move the cursor away from the newline. Fix it.
This appears to have regressed in 80d661e6a (rc/: More consistent
uses of regex syntax, 2017-09-29).
If a user modifies a grep buffer, we can end up in weird situations
where we try match a filename over multiple lines.
Let's rule out newlines in filenames here. There is an argument
this is a case of GIGO but we already do this for the corresponding
highlighters.
We also do it in make.kak, see ca225ad4d (Cleanup make.kak and optimize
the make-next/make-prev regexes, 2016-12-09). There is one case left
where a filename would theoretically span multiple lines. Fix and
optimize this too.
grep-jump and make-jump[*] support Windows-style paths like C:\path.
However grep-next-match and make-next-error don't, which suggests
that no one uses this feature. IIRC Cygwin mounts Windows drives in
proper Unix paths like /mnt/drive_c.
Let's remove it for simplicity and consistency.
This reverts commit 6c47b204e (Support windows style path in grep
output, 2014-11-11).
[*]: Though make-jump recently regressed in 8e5ca3f21 (rc/make.kak
introduce a new option to be back compatible, 2023-10-27) by failing
to capture the "C:" prefix.
Commit 36efbf4cb (rc filetype diff: extract diff parsing from
diff-jump, 2024-02-03) moved the "set-register |" command but failed
to move the "-save-regs |". This surfaces as register leakage in
"git blame-jump". Fix that.
Similar to the previous patch, when git blame fails it usually means
that the blamed file is not tracked by Git. I don't know of other
common failure scenarios.
Since git blame runs in detached process, failure handling is a bit
more involved. Let's forward stderr to the debug buffer and escalate
the error. I'm not sure how to get the exit code from git blame in
"git blame | perl" - we can't use $PIPESTATUS.
As a workaround, interpret empty output as failure.
In case of failure, also hide blame; this makes the UI more consistent
I think.
git blame typically fails when the buffile is not tracked by Git.
Let's escalate the failure early instead of continuing witout blame
data, only to eventually hit the generic
"git blame-jump: missing blame info" error.
I frequently run ":git diff" followed by ":git blame-jump" to find
the commit that added the lines I deleted. I wasn't sure whether
":git blame" should allow this use case too. I think it should,
I don't think this is too confusing.
I personally mostly use "git blame-jump" so this "git blame" bug has
flown under the radar. When we run git blame in a git-diff buffer,
we want to move one column to the left since the lines in the target
blob don't have the +- prefix. We already subtract one but we add it
back accidentally when using "l" to go to this column. Fix it.
In future we should try to preserve more of the selection(s), not
just the main cursor.
Insert repeat will now only record non-synthesized keys, and when played back
execute mappings as well. Constructing some tests, and with the specific goal
of fixing https://github.com/alexherbo2/auto-pairs.kak/issues/38, this appeared to
be the best approach. Other options could be evaluating the maps only when recording,
but this gave other issues (see tests/normal/repeat-insert/repeat-insert-mapped)
At this point, repeat-insert may be essentially just a hardcoded macro, at least I
haven't identified the difference. If this really is the case, it may make sense to
give it a dedicated register, and implement it as a macro.
Fixes#3600
This test fails occasionally[1] because the order of events and the
number of events varies across runs.
We should always call draw_status exactly 3 times:
[*git*][fifo]
[*git*]
Commit subject etc. [*git*]
Let's check it this way.
This seems to work; this time I took the time to run it a couple
hundred times and in Cirrus CI.
[1] https://builds.sr.ht/~mawww/job/1151239
When running git blame in a "git show" buffer, we annotate the youngest
version of the file that has the line referenced by the diff line
at cursor.
In case the cursor is on an added or context line, we simply show
the version from the surrounding commit.
When the cursor is on a deleted line, we show the parent commit,
which still has the deleted line. However there is a bug: we use
the line number in the new version of the file. Fix that.