Skip to main content

DDS

DDS (Data Distribution Service) is designed to achieve communication and state synchronization between multiple Yazi instances, as well as state persistence. It is built on a client-server architecture but does not require running additional server processes.

It deeply integrates with a publish-subscribe model based on the Lua API.

Concept

  • Local: the current instance, that is, the current Yazi process.
  • Remote: instances other than the current instance.
  • Static message: A message with a kind that starts with @ will be persistently stored and automatically restored when a new instance starts. To un-persist, send nil to that kind.

Usage

The DDS has three usage:

ya pub and ya pub-to

If you're in a Yazi subshell where the $YAZI_ID environment variable is set, you can send a message to the current instance using ya pub. It requires a kind argument, which is consistent with ps.pub():

ya pub <kind> --str "string body"
ya pub <kind> --list "a" "b" "c"
ya pub <kind> --json '{"key":"json body"}'

# For example, request the current instance to extract `a.zip` and `b.7z`
ya pub extract --list "/root/a.zip" "/root/b.7z"

You can also send a message to a specified remote instance(s) using ya pub-to, with the required receiver and kind arguments, consistent with ps.pub_to():

ya pub-to <receiver> <kind> --str "string body"
ya pub-to <receiver> <kind> --list "a" "b" "c"
ya pub-to <receiver> <kind> --json '{"key":"json body"}'

# If you're in a Yazi subshell,
# you can obtain the ID of the current instance through `$YAZI_ID`.
ya pub-to "$YAZI_ID" my-event --str "Hello world!"

For greater convenience in integrating within the command-line environment, they support two body formats:

  • String: a straightforward format, suitable for most scenarios, without the need for additional tools for encoding
  • List: An array of strings, it is useful for carrying a file list to the message, through $@ in your shell
  • JSON: for advanced needs, support for types and more complex data can be represented through the JSON format

ya emit and ya emit-to

If you're in a Yazi subshell where the $YAZI_ID environment variable is set, you can use ya emit to send a command to the current instance for execution.

The command format is the same as what you'd write in the keymap.toml:

ya emit <command> <args>

For example:

ya emit cd /tmp
ya emit reveal /tmp/foo

You can also send commands to a specific remote instance using ya emit-to:

ya emit-to <receiver> <command> <args>

For example:

ya emit-to "$YAZI_ID" cd /tmp

Real-time stdout reporting

You can specify the --local-events and --remote-events options when starting Yazi:

# Local events
yazi --local-events=kind1,kind2
# Remote events
yazi --remote-events=kind1,kind2
# Both local and remote events
yazi --local-events=kind1,kind2 --remote-events=kind1,kind2

When an event of the specified kind is received, it will be output to stdout:

hover,0,200,{"tab":0,"url":"/root/Downloads"}
cd,0,100,{"tab":0,"url":"/root/Downloads"}

One payload per line, each payload contains the following fields separated by commas:

FieldDescription
kindThe kind of the message
receiverThe remote instance ID that receives the message; if it's 0, broadcasts to all remote instances
senderThe sender of the message
bodyThe body of the message, which is a JSON string

This provides the ability to report Yazi's internal events in real-time, which is useful for external tool integration (such as Neovim), as they will be able to subscribe to the events triggered by the user behavior.

Builtin kinds

cd - change directory

sub() callback body:

{
tab = 0
}

sub_remote() callback body:

{
tab = 0,
url = Url("/root/Downloads")
}

--local-events stdout payload:

cd,1711957542289249,1711957542289249,{"tab":0,"url":"/root/Downloads"}

--remote-events stdout payload:

cd,0,100,{"tab":0,"url":"/root/Downloads"}

hover - hover on a file

sub() callback body:

{
tab = 0
}

sub_remote() callback body:

{
tab = 0,
url = Url("/root/foo.txt")
}

--local-events stdout payload:

hover,1711957283332834,1711957283332834,{"tab":0,"url":"/root/foo.txt"}

--remote-events stdout payload:

hover,0,200,{"tab":0,"url":"/root/foo.txt"}

rename - rename a file

sub() / sub_remote() callback body:

{
tab = 0,
from = Url("/root/foo.txt"),
to = Url("/root/bar.txt"),
}

--local-events stdout payload:

rename,1711957878076791,1711957878076791,{"tab":0,"from":"/root/foo.txt","to":"/root/bar.txt"}

--remote-events stdout payload:

rename,0,1711957878076791,{"tab":0,"from":"/root/foo.txt","to":"/root/bar.txt"}

bulk - bulk rename files

sub() / sub_remote() callback body:

-- Since `Iterator` implementing `__pairs()`,
-- you can iterate over all URL pairs using `pairs(body)`
Iterator {
__len = function(self)
-- Returns the number of files changed
end,
__pairs = function(self)
-- Returns (Url("/path/from.txt"), Url("/path/to.txt"))
end
}

--local-events stdout payload:

bulk,1711957542289249,1711957542289249,{"changes":{"/path/from.txt":"/path/to.txt"}}

--remote-events stdout payload:

bulk,0,1711957542289249,{"changes":{"/path/from.txt":"/path/to.txt"}}

yank - yank files

sub() callback body:

{}

sub_remote() callback body:

-- Since `Iterator` implementing `__index()`, you can access the yanked URLs by index,
-- such as `body[1]`, or iterate over all URLs using `ipairs(body)`
Iterator {
cut = false,
__len = function(self)
-- Returns the number of URLs yanked
end,
__index = function(self, idx)
-- Returns the URL at the given index
end
}

--local-events stdout payload:

yank,1711960311454247,1711960311454247,{"cut":false,"urls":["/root/foo.txt","/root/bar.txt"]}

--remote-events stdout payload:

yank,0,300,{"cut":false,"urls":["/root/foo.txt","/root/bar.txt"]}

move - move files

sub() callback body:

{
items = {
{ from = Url("/root/foo.txt"), to = Url("/root/bar.txt") },
-- ...
}
}

sub_remote() callback body:

{
items = {
{ from = Url("/root/foo.txt"), to = Url("/root/bar.txt") },
-- ...
}
}

--local-events stdout payload:

move,1711957542289249,1711957542289249,{"items":[{"from":"/root/foo.txt","to":"/root/bar.txt"}]}

--remote-events stdout payload:

move,0,1711957542289249,{"items":[{"from":"/root/foo.txt","to":"/root/bar.txt"}]}

trash - trash files

sub() callback body:

{
urls = {
Url("/root/foo.txt"),
-- ...
}
}

sub_remote() callback body:

{
urls = {
Url("/root/foo.txt"),
-- ...
}
}

--local-events stdout payload:

trash,1711957542289249,1711957542289249,{"urls":["/root/foo.txt"]}

--remote-events stdout payload:

trash,0,1711957542289249,{"urls":["/root/foo.txt"]}

delete - delete files

sub() callback body:

{
urls = {
Url("/root/foo.txt"),
-- ...
}
}

sub_remote() callback body:

{
urls = {
Url("/root/foo.txt"),
-- ...
}
}

--local-events stdout payload:

delete,1711957542289249,1711957542289249,{"urls":["/root/foo.txt"]}

--remote-events stdout payload:

delete,0,1711957542289249,{"urls":["/root/foo.txt"]}

hi - client handshake

System reserves kind.

hey - server handshake

System reserves kind.

bye

System reserves kind.

Builtin plugins

dds.lua

This plugin provides a dds-emit event kind, which is used for the implementation of the ya emit subcommand — ya emit is a shorthand for ya pub, and the emitted command will be converted into an equivalent ya pub event message.

With ya emit, you can implement many interesting features, such as synchronizing the CWD of the current Yazi instance when exiting from a subshell:

# Change Yazi's CWD to PWD on subshell exit
if [[ -n "$YAZI_ID" ]]; then
function _yazi_cd() {
ya emit cd "$PWD"
}
add-zsh-hook zshexit _yazi_cd
fi

Source code: https://github.com/sxyazi/yazi/blob/main/yazi-plugin/preset/plugins/dds.lua

session.lua

This plugin provides cross-instance yank ability, which means you can yank files in one instance, and then paste them in another instance.

To enable it, add these lines to your init.lua, then restart all Yazi instances to apply the changes:

require("session"):setup {
sync_yanked = true,
}

Source code: https://github.com/sxyazi/yazi/blob/main/yazi-plugin/preset/plugins/session.lua

extract.lua

This plugin provides an extract event kind for archive extraction, which accepts an array of file URL. You can bind it as the opener for archives:

# ~/.config/yazi/yazi.toml
[opener]
extract = [
{ run = 'ya pub extract --list "$@"', desc = "Extract here", for = "unix" },
{ run = 'ya pub extract --list %*', desc = "Extract here", for = "windows" },
]

Source code: https://github.com/sxyazi/yazi/blob/main/yazi-plugin/preset/plugins/extract.lua