Yazi: Keymap Fails With Multiple Shell Commands And `quit`

by Pedro Alvarez 59 views

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 the shell 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 to quit 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.