Yazi: Keymap Fails With Multiple Shell Commands And `quit`
Introduction
Hey guys! Have you ever run into a situation where your keymap configuration just doesn't seem to work as expected, especially when dealing with multiple shell commands and the quit
command in Yazi? Well, you're not alone! In this article, we're going to dive deep into a peculiar bug reported by a Yazi user where the quit
command, when used as the last command in a keymap, prevents the preceding commands from executing. We'll explore the issue, understand the user's specific use case, and discuss potential workarounds. So, buckle up and let's get started!
The Bug: quit
Command Preventing Prior Commands
The core of the issue lies in how Yazi handles multiple shell commands within a keymap, particularly when the quit
command is involved. According to the documentation, you should be able to chain multiple commands using run = ["shell ...", "..."]
. However, a user reported that when quit
is the final command in the sequence, the preceding commands fail to execute. This behavior is quite perplexing, as it deviates from the expected functionality.
To illustrate this, consider the following keymap configuration:
[[mgr.prepend_keymap]]
on = "A"
run = [ "shell -- touch /tmp/yazi-abort", "quit"]
desc = "Create abort marker then quit"
In this scenario, the user expected Yazi to first create a file named /tmp/yazi-abort
and then quit. Unfortunately, the file is never created, indicating that the touch
command is not executed before Yazi exits. This behavior was observed consistently, even with variations using the --block
option. Removing the quit
command makes the touch
command work as expected, further highlighting the issue's connection to the quit
command.
Debugging Information
To provide a clearer picture, let's examine the debug information provided by the user. This information is crucial for understanding the environment in which the bug occurs and identifying potential conflicts or dependencies.
The debug output includes details about the system, terminal, Yazi version, and various environment variables. Key aspects to note are:
- System: Linux Wayland
- Terminal: Kitty 0.42.2
- Yazi Version: 25.6.11
- Shell: /usr/bin/zsh
Additionally, the output lists various dependencies and their versions, such as file
, ffmpeg
, pdftoppm
, magick
, fzf
, fd
, rg
, zoxide
, and clipboard utilities. This comprehensive overview helps rule out potential issues related to outdated or missing dependencies.
The user also confirmed that the bug is reproducible on the latest nightly build and after disabling all custom configurations and plugins. This eliminates the possibility of the issue being caused by custom settings or outdated versions.
The User's Use Case: Custom Exit Codes for git difftool
Understanding the user's motivation behind this particular keymap configuration is essential for grasping the impact of the bug. The user is attempting to use Yazi as a git difftool
for images. In this context, Yazi is invoked to display the differences between image versions.
The challenge arises from the need to signal different outcomes to the git difftool
based on the user's actions within Yazi. Specifically, the user wants to quit Yazi with a non-zero exit code to indicate that no further action is required, effectively stopping the diff tool process. However, Yazi doesn't natively support setting custom exit codes (at least not without writing a plugin).
To work around this limitation, the user devised a clever hack: create a temporary file (e.g., /tmp/yazi-abort
) as a marker. A wrapper script could then check for the existence of this file to determine whether to proceed with the next image in the diff or to exit the difftool
entirely.
The problematic keymap, therefore, was intended to create this marker file before quitting Yazi. The failure of the touch
command to execute before quit
effectively breaks this workaround, preventing the user from cleanly integrating Yazi into their git difftool
workflow.
Potential Causes and Solutions
So, what could be causing this peculiar behavior? Let's explore some potential explanations and solutions.
1. Asynchronous Execution
One possibility is that the commands are being executed asynchronously, and the quit
command is being triggered before the touch
command has a chance to complete. This would explain why the file is not being created.
Potential Solution:
- Ensure Blocking Execution: The
--block
option in theshell
command is intended to ensure that the command completes before the next one is executed. However, the user mentioned that even with--block
, the issue persists. This suggests that either the--block
option is not functioning as expected, or there's another mechanism at play.
2. Command Queueing and Interruption
Another possibility is that Yazi has a command queue, and the quit
command is interrupting the queue before the touch
command can be processed.
Potential Solution:
- Check for Command Queue Behavior: Investigate Yazi's internal command processing mechanism to see if there's a queue and how commands are handled. If there's a queue, ensure that commands are processed in the order they are added and that
quit
doesn't prematurely terminate the queue.
3. Event Loop and Signal Handling
Yazi, like many interactive applications, likely uses an event loop to handle user input and other events. The quit
command might be triggering a signal or event that bypasses the normal command execution flow.
Potential Solution:
- Examine Event Loop and Signal Handling: Analyze how Yazi's event loop handles the
quit
command and whether it interferes with other command executions. Ensure that signals or events related toquit
are handled in a way that doesn't disrupt the command sequence.
4. Bug in Yazi's Code
Of course, there's always the possibility of a bug in Yazi's code that specifically affects the execution of multiple shell commands when quit
is the last command.
Potential Solution:
- Code Review: A thorough review of Yazi's codebase, particularly the sections related to keymap handling, shell command execution, and the
quit
command, is necessary to identify any potential bugs.
Workarounds
While the underlying issue is being investigated, here are some potential workarounds that the user (and others facing similar problems) can consider:
1. Execute Commands in a Single Shell Command
Instead of using multiple commands in the run
array, you can combine them into a single shell command using &&
or ;
to chain the commands.
[[mgr.prepend_keymap]]
on = "A"
run = [ "shell -- touch /tmp/yazi-abort && quit" ]
desc = "Create abort marker then quit"
This approach ensures that the touch
command is executed before quit
because they are part of the same shell command.
2. Use a Script
Another workaround is to create a shell script that contains the desired commands and then execute the script from the keymap.
#!/bin/bash
touch /tmp/yazi-abort
exit
Save this script (e.g., yazi-quit.sh
) and then use the following keymap:
[[mgr.prepend_keymap]]
on = "A"
run = [ "shell -- /path/to/yazi-quit.sh" ]
desc = "Create abort marker then quit"
This method provides a clear separation of concerns and ensures that the commands are executed sequentially within the script.
3. Explore Yazi Plugins
While the user mentioned that they wanted to avoid writing a plugin, it might be the most robust solution in the long run. A plugin could provide a direct way to set the exit code, eliminating the need for the temporary file workaround.
Conclusion
The bug where the quit
command prevents preceding shell commands from executing in Yazi's keymaps is a tricky issue. It highlights the complexities of command execution and event handling in interactive applications. While the exact cause remains to be pinpointed, understanding the potential explanations and workarounds can help users mitigate the problem.
Whether it's asynchronous execution, command queueing, event loop interference, or a simple bug in the code, a systematic investigation is crucial. In the meantime, combining commands in a single shell command or using a script can serve as effective workarounds.
Thanks for reading, guys! Stay tuned for more updates as we delve deeper into the world of Yazi and its intricacies.