Skip to content

Commit

Permalink
feat: add targeted communication with the yazi instance (opt-in)
Browse files Browse the repository at this point in the history
This is a technical change with no change in features for the end user.
Right now you can opt in to trying it out by setting
`use_yazi_client_id_flag = true` in your config (run `:checkhealth
yazi` afterwards).

This will probably be the default mode of communication in the future.

Before this change, real time communication with the embedded yazi
instance has been possible, but it has had some limitations:

- yazi.nvim had the possibility of sending commands to yazi, but the
  communication has been global, meaning that all yazi instances running
  on the system have been able to receive these messages. This has been
  too dangerous to implement, as making yazi do any actions could have
  unintended side effects if multiple yazis execute the same actions,
  potentially in different directories on the system.
- similarly, receiving events from yazi has been limited to receiving
  events from all yazis running on the system. This has not been a
  problem so far since the events we use (rename, delete, trash, move,
  cd, hover, bulk) are not dangerous, and typically the user only
  focuses on one yazi.nvim instance at a time anyway.

This change removes this limitation from future features by giving the
yazi instance shown by this plugin a unique identifier. This identifier
can later be used to send and receive messages with this specific yazi
instance.

See these for more information and context:

- sxyazi/yazi#989 (comment)
- sxyazi/yazi#1305
  • Loading branch information
mikavilpas committed Jul 19, 2024
1 parent e84665e commit de742bd
Show file tree
Hide file tree
Showing 10 changed files with 135 additions and 17 deletions.
12 changes: 6 additions & 6 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,19 +29,19 @@ jobs:
# yazi-fm is the `yazi` executable
crate: yazi-fm
git: https://github.com/sxyazi/yazi
# feat: ownership linemode (#1238)
# https://github.com/sxyazi/yazi/commit/11547eefe0346006a1a82455577784a34d67c9b7
commit: 11547eefe0346006a1a82455577784a34d67c9b7
# refactor: reimplement the signal system (#1307)
# https://github.com/sxyazi/yazi/commit/d6081fbe6f57
commit: d6081fbe6f57

- name: Compile and install yazi from source
uses: baptiste0928/cargo-install@v3
with:
# yazi-cli is the `ya` command line interface
crate: yazi-cli
git: https://github.com/sxyazi/yazi
# feat: ownership linemode (#1238)
# https://github.com/sxyazi/yazi/commit/11547eefe0346006a1a82455577784a34d67c9b7
commit: 11547eefe0346006a1a82455577784a34d67c9b7
# refactor: reimplement the signal system (#1307)
# https://github.com/sxyazi/yazi/commit/d6081fbe6f57
commit: d6081fbe6f57

- name: Run tests
uses: nvim-neorocks/nvim-busted-action@v1
Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,9 @@ You can optionally configure yazi.nvim by setting any of the options below.
-- https://github.com/mikavilpas/yazi.nvim/pull/152
use_ya_for_events_reading = false,

-- an upcoming optional feature
use_yazi_client_id_flag = false,

-- an upcoming optional feature. See
-- https://github.com/mikavilpas/yazi.nvim/pull/180
highlight_groups = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@ require('yazi').setup(
---@type YaziConfig
{
use_ya_for_events_reading = true,
use_yazi_client_id_flag = true,
}
)
1 change: 1 addition & 0 deletions lua/yazi/config.lua
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ function M.default()
open_for_directories = false,
-- NOTE: right now this is opt-in, but will be the default in the future
use_ya_for_events_reading = false,
use_yazi_client_id_flag = false,
enable_mouse_support = false,
open_file_function = openers.open_file,
set_keymappings_function = M.default_set_keymappings_function,
Expand Down
15 changes: 14 additions & 1 deletion lua/yazi/health.lua
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,22 @@ return {
vim.health.info('yazi.nvim log file is at ' .. logfile_location)
vim.health.info(' hint: use `gf` to open the file path under the cursor')

local config = require('yazi').config

if config.use_yazi_client_id_flag == true then
local output = vim.fn.system('yazi --help')

if output:find('--client-id', 1, true) == nil then
vim.health.warn(
'You have enabled `use_yazi_client_id_flag` in your config, which means using the `--client-id` flag with yazi. However, this flag is not found in the `yazi --help` output. Please upgrade to the newest version of yazi or disable `use_yazi_client_id_flag`.'
)
vim.health.info(string.format('`yazi --help` output: %s', output))
end
end

-- TODO validate that the highlight_config is present in the configuration

if require('yazi').config.use_ya_for_events_reading == true then
if config.use_ya_for_events_reading == true then
if vim.fn.executable('ya') ~= 1 then
vim.health.error(
'You have opted in to using `ya` for events reading, but `ya` is not found on PATH. Please install `ya` or disable `use_ya_for_events_reading` in your config.'
Expand Down
2 changes: 1 addition & 1 deletion lua/yazi/process/legacy_events_from_file.lua
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ function LegacyEventReadingFromEventFile:new(config)
end

---@param path Path
function LegacyEventReadingFromEventFile:get_yazi_command(path)
function LegacyEventReadingFromEventFile:get_yazi_command(path, _)
return string.format(
'yazi %s --local-events "rename,delete,trash,move,cd" --chooser-file "%s" > "%s"',
vim.fn.shellescape(path.filename),
Expand Down
15 changes: 10 additions & 5 deletions lua/yazi/process/ya_process.lua
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,20 @@ local highlight_hovered_buffer =

---@class (exact) YaProcess
---@field public events YaziEvent[] "The events that have been received from yazi"
---@field public new fun(config: YaziConfig): YaProcess
---@field public new fun(config: YaziConfig, yazi_id: string): YaProcess
---@field private config YaziConfig
---@field private yazi_id string
---@field private ya_process vim.SystemObj
---@field private retries integer
local YaProcess = {}
---@diagnostic disable-next-line: inject-field
YaProcess.__index = YaProcess

---@param config YaziConfig
function YaProcess.new(config)
---@param yazi_id string
function YaProcess.new(config, yazi_id)
local self = setmetatable({}, YaProcess)
self.yazi_id = yazi_id
self.config = config
self.events = {}
self.retries = 0
Expand All @@ -28,11 +31,13 @@ function YaProcess.new(config)
end

---@param path Path
function YaProcess:get_yazi_command(path)
---@param yazi_id string
function YaProcess:get_yazi_command(path, yazi_id)
return string.format(
'yazi %s --chooser-file "%s"',
'yazi %s --chooser-file "%s" --client-id "%s"',
vim.fn.shellescape(path.filename),
self.config.chosen_file_path
self.config.chosen_file_path,
yazi_id
)
end

Expand Down
1 change: 1 addition & 0 deletions lua/yazi/types.lua
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
---@field public chosen_file_path? string "the path to a temporary file that will be created by yazi to store the chosen file path"
---@field public events_file_path? string "the path to a temporary file that will be created by yazi to store events. A random path will be used by default"
---@field public use_ya_for_events_reading? boolean "use `ya`, the yazi command line application to read events from the yazi process. Right now this is opt-in, but will be the default in the future"
---@field public use_yazi_client_id_flag? boolean "use the `--client-id` flag with yazi, allowing communication with that specific instance as opposed to all yazis on the system"
---@field public enable_mouse_support? boolean
---@field public open_file_function? fun(chosen_file: string, config: YaziConfig, state: YaziClosedState): nil "a function that will be called when a file is chosen in yazi"
---@field public set_keymappings_function? fun(buffer: integer, config: YaziConfig): nil "the function that will set the keymappings for the yazi floating window. It will be called after the floating window is created."
Expand Down
12 changes: 9 additions & 3 deletions lua/yazi/yazi_process.lua
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,19 @@ function YaziProcess:start(config, path, on_exit)
config.use_ya_for_events_reading
)
)

-- The YAZI_ID of the yazi process, used to uniquely identify this specific
-- instance, so that we can communicate with it specifically, instead of
-- possibly multiple other yazis that are running on this computer.
local yazi_id = string.format('%.0f', vim.uv.hrtime())

self.event_reader = config.use_ya_for_events_reading == true
and YaProcess.new(config)
and YaProcess.new(config, yazi_id)
or LegacyEventReadingFromEventFile:new(config)

local yazi_cmd = self.event_reader:get_yazi_command(path)
local yazi_cmd = self.event_reader:get_yazi_command(path, yazi_id)
Log:debug(string.format('Opening yazi with the command: (%s).', yazi_cmd))

Log:debug(string.format('Opening yazi with the command: (%s)', yazi_cmd))
self.yazi_job_id = vim.fn.termopen(yazi_cmd, {
on_exit = function(_, code)
self.event_reader:kill()
Expand Down
90 changes: 89 additions & 1 deletion spec/yazi/health_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,19 @@ local function assert_buffer_contains_text(needle)
assert(found, message)
end

local function assert_buffer_does_not_contain_text(needle)
local buffer_text = vim.api.nvim_buf_get_lines(0, 0, -1, false)
local text = table.concat(buffer_text, '\n')
local message = string.format(
"Expected the main string to not contain the substring.\nMain string: '%s'\nSubstring: '%s'",
text,
needle
)

local found = string.find(text, needle, 1, true) ~= nil
assert.is_false(found, message)
end

-- make nvim find the health check file so that it can be executed by :checkhealth
-- without this, the health check will not be found
vim.opt.rtp:append('.')
Expand All @@ -28,6 +41,22 @@ describe('the healthcheck', function()
snapshot = assert:snapshot()
mock_app_versions = {
['yazi'] = 'yazi 0.2.5 (f5a7ace 2024-06-23)',
['yazi --help'] = [[Usage: yazi [OPTIONS] [ENTRY]
Arguments:
[ENTRY] Set the current working entry
Options:
--cwd-file <CWD_FILE> Write the cwd on exit to this file
--chooser-file <CHOOSER_FILE> Write the selected files to this file on open fired
--clear-cache Clear the cache directory
--client-id <CLIENT_ID> Use the specified client ID, must be a globally unique number
--local-events <LOCAL_EVENTS> Report the specified local events to stdout
--remote-events <REMOTE_EVENTS> Report the specified remote events to stdout
--debug Print debug information
-V, --version Print version
-h, --help Print help
]],
['ya'] = 'Ya 0.2.5 (f5a7ace 2024-06-23)',
['nvim-0.10.0'] = true,
}
Expand All @@ -49,8 +78,10 @@ describe('the healthcheck', function()
return mock_app_versions['yazi']
elseif command == 'ya --version' then
return mock_app_versions['ya']
elseif command == 'yazi --help' then
return mock_app_versions['yazi --help']
else
error('unexpected command: ' .. command)
error('the command is not mocked in the test: ' .. vim.inspect(command))
end
end)
end)
Expand Down Expand Up @@ -138,4 +169,61 @@ describe('the healthcheck', function()
'WARNING The versions of `yazi` and `ya` do not match.'
)
end)

describe('the checks for `use_yazi_client_id_flag`', function()
local yazi_help_output_with_client_id_flag_missing =
[[Usage: yazi [OPTIONS] [ENTRY]
Arguments:
[ENTRY] Set the current working entry
Options:
--cwd-file <CWD_FILE> Write the cwd on exit to this file
--chooser-file <CHOOSER_FILE> Write the selected files to this file on open fired
--clear-cache Clear the cache directory
--local-events <LOCAL_EVENTS> Report the specified local events to stdout
--remote-events <REMOTE_EVENTS> Report the specified remote events to stdout
--debug Print debug information
-V, --version Print version
-h, --help Print help
]]

before_each(function() end)

it(
'warns when the `--client-id` flag is not found in the yazi --help output',
function()
mock_app_versions['yazi --help'] =
yazi_help_output_with_client_id_flag_missing
require('yazi').setup({ use_yazi_client_id_flag = true })
vim.cmd('checkhealth yazi')

assert_buffer_contains_text(
'You have enabled `use_yazi_client_id_flag` in your config, which means using the `--client-id` flag with yazi. However, this flag is not found in the `yazi --help` output. Please upgrade to the newest version of yazi or disable `use_yazi_client_id_flag`.'
)
end
)

it(
"does not warn when `use_yazi_client_id_flag` is not set and yazi doesn't support --client-id",
function()
mock_app_versions['yazi --help'] =
yazi_help_output_with_client_id_flag_missing
require('yazi').setup({ use_yazi_client_id_flag = false })
vim.cmd('checkhealth yazi')

assert_buffer_does_not_contain_text('use_yazi_client_id_flag')
end
)

it(
'does not warn when the `--client-id` flag is found in the yazi --help output',
function()
require('yazi').setup({ use_yazi_client_id_flag = true })
vim.cmd('checkhealth yazi')

assert_buffer_does_not_contain_text('use_yazi_client_id_flag')
end
)
end)
end)

0 comments on commit de742bd

Please sign in to comment.