diff --git a/README.md b/README.md index 4521291..6b9b602 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ open yazi in a floating window in Neovim. buffers in Neovim - The files are also kept in sync with currently running LSP servers - Customizable keybindings -- 🆕 Plugin manager for Yazi plugins +- 🆕 Plugin manager for Yazi plugins and flavors ([documentation](./documentation/plugin-manager.md)). Please provide your feedback! diff --git a/documentation/plugin-manager.md b/documentation/plugin-manager.md index c8abe94..cd4c8cc 100644 --- a/documentation/plugin-manager.md +++ b/documentation/plugin-manager.md @@ -7,7 +7,8 @@ allows you to fully manage your yazi and neovim plugins from inside neovim. In this example, we will install the yazi plugin [DreamMaoMao/keyjump.yazi](https://github.com/DreamMaoMao/keyjump.yazi), which -adds a "jump-to-line" feature to yazi. +adds a "jump-to-line" feature to yazi. We will also install a _flavor_ which +applies a color scheme to yazi. In your yazi.nvim configuration, add a new lazy.nvim plugin specification for `DreamMaoMao/keyjump.yazi`: @@ -29,24 +30,39 @@ return { }, }, { + -- example: include a plugin "DreamMaoMao/keyjump.yazi", lazy = true, build = function(plugin) require("yazi.plugin").build_plugin(plugin) end, }, + { + -- example: include a flavor + "BennyOe/onedark.yazi", + lazy = true, + build = function(plugin) + require("yazi.plugin").build_flavor(plugin) + end, + }, } ``` Make sure to add the `lazy` and `build` keys to the plugin specification . -Next, run `:Lazy` in neovim to install the plugin. +Next, run `:Lazy` in neovim to install the plugin and flavor. + +Finally, make changes in your yazi configuration: -Finally, add a keybinding to your `~/.config/yazi/keymap.toml` according to the -instructions provided by the plugin author. +- add a keybinding to your `~/.config/yazi/keymap.toml` according to the + [instructions](https://github.com/DreamMaoMao/keyjump.yazi?tab=readme-ov-file#usage) + provided by the plugin author. +- include the flavor in your `~/.config/yazi/theme.toml` according to the + [instructions](https://github.com/BennyOe/onedark.yazi?tab=readme-ov-file#%EF%B8%8F-usage) + provided by the flavor author. -You're all set! You can now use the new plugin in yazi, and update it using -lazy.nvim. +You're all set! You can now use the new plugin and flavor in yazi, and update +them using lazy.nvim. > Demo: installing a new Yazi plugin with lazy.nvim, and then using `l` > to view its commits @@ -154,3 +170,5 @@ For further reading, please refer to the following resources: - Yazi plugin documentation - lazy.nvim documentation +- General discussion on the idea + diff --git a/lua/yazi/plugin.lua b/lua/yazi/plugin.lua index 16ade53..b608aa6 100644 --- a/lua/yazi/plugin.lua +++ b/lua/yazi/plugin.lua @@ -1,5 +1,13 @@ local M = {} +--- A specification that represents information about the plugin or flavor to +--- install. Note that this is compatible with LazyPlugin, the lazy.nvim plugin +--- specification table, so you can just pass that. +---@alias YaziLazyNvimSpec { name: string, dir: string } + +---@alias YaziSpecInstallationResultSuccess { message: string, from: string, to: string } +---@alias YaziSpecInstallationResultFailure { message: string, from: string, to?: string, error: string } + --- Helper utility for compatibility with --- [lazy.nvim](https://github.com/folke/lazy.nvim). --- @@ -19,33 +27,61 @@ local M = {} --- --- For more information, see the yazi.nvim documentation. --- ----@param plugin YaziLazyNvimPlugin +---@param plugin YaziLazyNvimSpec ---@param options? { yazi_dir: string } ----@return YaziPluginInstallationResultSuccess | YaziPluginInstallationResultFailure function M.build_plugin(plugin, options) local yazi_dir = options and options.yazi_dir or vim.fn.expand('~/.config/yazi') - local to = vim.fs.normalize(vim.fs.joinpath(yazi_dir, 'plugins', plugin.name)) - local dir = vim.loop.fs_stat(plugin.dir) + ---@type string + local yazi_plugins_dir = vim.fn.expand(vim.fs.joinpath(yazi_dir, 'plugins')) + vim.fn.mkdir(yazi_plugins_dir, 'p') + + local to = vim.fs.normalize(vim.fs.joinpath(yazi_plugins_dir, plugin.name)) + + return M.symlink(plugin, to) +end + +---@param flavor YaziLazyNvimSpec +---@param options? { yazi_dir: string } +function M.build_flavor(flavor, options) + local yazi_dir = options and options.yazi_dir + or vim.fn.expand('~/.config/yazi') + + ---@type string + local flavor_dir = vim.fn.expand(vim.fs.joinpath(yazi_dir, 'flavors')) + vim.fn.mkdir(flavor_dir, 'p') + + local to = vim.fs.normalize(vim.fs.joinpath(flavor_dir, flavor.name)) + + return M.symlink(flavor, to) +end + +--- A general implementation of a symlink operation. For yazi plugins and +--- flavors, prefer using `build_plugin` and `build_flavor` instead. +---@param spec YaziLazyNvimSpec +---@param to string +---@return YaziSpecInstallationResultSuccess | YaziSpecInstallationResultFailure +function M.symlink(spec, to) + local dir = vim.uv.fs_stat(spec.dir) if dir == nil or dir.type ~= 'directory' then - ---@type YaziPluginInstallationResultFailure + ---@type YaziSpecInstallationResultFailure local result = { - error = 'plugin directory does not exist', - from = plugin.dir, - message = 'yazi.nvim: failed to install plugin', + error = 'yazi plugin/flavor directory does not exist', + from = spec.dir, + message = 'yazi.nvim: failed to install', } vim.notify(vim.inspect(result)) return result end - local success, error = vim.uv.fs_symlink(plugin.dir, to) + local success, error = vim.uv.fs_symlink(spec.dir, to) if not success then - ---@type YaziPluginInstallationResultFailure + ---@type YaziSpecInstallationResultFailure local result = { - message = 'yazi.nvim: failed to install plugin', - from = plugin.dir, + message = 'yazi.nvim: failed to install', + from = spec.dir, to = to, error = error or 'unknown error', } @@ -54,10 +90,10 @@ function M.build_plugin(plugin, options) return result end - ---@type YaziPluginInstallationResultSuccess + ---@type YaziSpecInstallationResultSuccess local result = { - message = 'yazi.nvim: successfully installed plugin ' .. plugin.name, - from = plugin.dir, + message = 'yazi.nvim: successfully installed ' .. spec.name, + from = spec.dir, to = to, } @@ -65,12 +101,4 @@ function M.build_plugin(plugin, options) return result end ---- Represents information about the plugin to install. Note that this is ---- compatible with LazyPlugin, the lazy.nvim plugin specification table, so ---- you can just pass that. ----@alias YaziLazyNvimPlugin { name: string, dir: string } - ----@alias YaziPluginInstallationResultSuccess { message: string, from: string, to: string } ----@alias YaziPluginInstallationResultFailure { message: string, from: string, to?: string, error: string } - return M diff --git a/tests/yazi/plugin_spec.lua b/tests/yazi/plugin_spec.lua index 66f072d..39cad4b 100644 --- a/tests/yazi/plugin_spec.lua +++ b/tests/yazi/plugin_spec.lua @@ -15,41 +15,63 @@ describe('installing a plugin', function() vim.fn.delete(base_dir, 'rf') end) - it('can install if everything goes well', function() - local plugin_dir = vim.fs.joinpath(base_dir, 'test-plugin') - local yazi_dir = vim.fs.joinpath(base_dir, 'fake-yazi-dir') + describe('installing a plugin', function() + it('can install if everything goes well', function() + local plugin_dir = vim.fs.joinpath(base_dir, 'test-plugin') + local yazi_dir = vim.fs.joinpath(base_dir, 'fake-yazi-dir') - vim.fn.mkdir(plugin_dir) - vim.fn.mkdir(yazi_dir) - vim.fn.mkdir(vim.fs.joinpath(yazi_dir, 'plugins')) + vim.fn.mkdir(plugin_dir) + vim.fn.mkdir(yazi_dir) + vim.fn.mkdir(vim.fs.joinpath(yazi_dir, 'plugins')) - plugin.build_plugin({ - dir = plugin_dir, - name = 'test-plugin', - }, { yazi_dir = yazi_dir }) + plugin.build_plugin({ + dir = plugin_dir, + name = 'test-plugin', + }, { yazi_dir = yazi_dir }) - -- verify that the plugin was symlinked - -- yazi_dir/plugins/test-plugin -> plugin_dir - local symlink = - vim.loop.fs_readlink(vim.fs.joinpath(yazi_dir, 'plugins', 'test-plugin')) + -- verify that the plugin was symlinked + -- yazi_dir/plugins/test-plugin -> plugin_dir + local symlink = + vim.uv.fs_readlink(vim.fs.joinpath(yazi_dir, 'plugins', 'test-plugin')) - assert.are.same(plugin_dir, symlink) + assert.are.same(plugin_dir, symlink) + end) + + it('warns the user if the plugin directory does not exist', function() + local plugin_dir = vim.fs.joinpath(base_dir, 'test-plugin') + local yazi_dir = vim.fs.joinpath(base_dir, 'fake-yazi-dir') + vim.fn.mkdir(yazi_dir) + + local result = plugin.build_plugin({ + dir = plugin_dir, + name = 'test-plugin-2', + }, { yazi_dir = yazi_dir }) + + assert.is_equal( + result.error, + 'yazi plugin/flavor directory does not exist' + ) + assert.is_equal(result.from, plugin_dir) + end) end) - it('warns the user if the plugin directory does not exist', function() - local plugin_dir = vim.fs.joinpath(base_dir, 'test-plugin') - local yazi_dir = vim.fs.joinpath(base_dir, 'fake-yazi-dir') + describe('installing a flavor', function() + it('can install if everything goes well', function() + local flavor_dir = vim.fs.joinpath(base_dir, 'test-flavor') + local yazi_dir = vim.fs.joinpath(base_dir, 'fake-yazi-dir') + + vim.fn.mkdir(flavor_dir) + vim.fn.mkdir(yazi_dir) - -- NOTE: we are not creating the plugin directory - -- vim.fn.mkdir(plugin_dir) - vim.fn.mkdir(yazi_dir) - vim.fn.mkdir(vim.fs.joinpath(yazi_dir, 'plugins')) + plugin.build_flavor({ + dir = flavor_dir, + name = 'test-flavor', + }, { yazi_dir = yazi_dir }) - local result = plugin.build_plugin({ - dir = plugin_dir, - name = 'test-plugin-2', - }, { yazi_dir = yazi_dir }) + local symlink = + vim.uv.fs_readlink(vim.fs.joinpath(yazi_dir, 'flavors', 'test-flavor')) - assert.is_equal(result.error, 'plugin directory does not exist') + assert.are.same(flavor_dir, symlink) + end) end) end)