From a4d6480085c09c32963a1381f0d845637c9741a9 Mon Sep 17 00:00:00 2001 From: bh Date: Tue, 23 Dec 2025 20:51:15 +0800 Subject: Riced yazi --- yazi/.config/yazi/init.lua | 2 + yazi/.config/yazi/keymap.toml | 46 +++ yazi/.config/yazi/package.toml | 7 + .../yazi/plugins/relative-motions.yazi/LICENSE | 19 + .../yazi/plugins/relative-motions.yazi/README.md | 145 ++++++++ .../yazi/plugins/relative-motions.yazi/main.lua | 406 +++++++++++++++++++++ yazi/.config/yazi/theme.toml | 48 ++- 7 files changed, 667 insertions(+), 6 deletions(-) create mode 100644 yazi/.config/yazi/init.lua create mode 100644 yazi/.config/yazi/keymap.toml create mode 100644 yazi/.config/yazi/package.toml create mode 100644 yazi/.config/yazi/plugins/relative-motions.yazi/LICENSE create mode 100644 yazi/.config/yazi/plugins/relative-motions.yazi/README.md create mode 100644 yazi/.config/yazi/plugins/relative-motions.yazi/main.lua (limited to 'yazi') diff --git a/yazi/.config/yazi/init.lua b/yazi/.config/yazi/init.lua new file mode 100644 index 0000000..223def6 --- /dev/null +++ b/yazi/.config/yazi/init.lua @@ -0,0 +1,2 @@ +-- ~/.config/yazi/init.lua +require("relative-motions"):setup({ show_numbers="relative", show_motion=true, enter_mode ="cache_or_first" }) diff --git a/yazi/.config/yazi/keymap.toml b/yazi/.config/yazi/keymap.toml new file mode 100644 index 0000000..45f4dda --- /dev/null +++ b/yazi/.config/yazi/keymap.toml @@ -0,0 +1,46 @@ +# Relative Motions + +[[mgr.prepend_keymap]] +on = [ "1" ] +run = "plugin relative-motions 1" +desc = "Move in relative steps" + +[[mgr.prepend_keymap]] +on = [ "2" ] +run = "plugin relative-motions 2" +desc = "Move in relative steps" + +[[mgr.prepend_keymap]] +on = [ "3" ] +run = "plugin relative-motions 3" +desc = "Move in relative steps" + +[[mgr.prepend_keymap]] +on = [ "4" ] +run = "plugin relative-motions 4" +desc = "Move in relative steps" + +[[mgr.prepend_keymap]] +on = [ "5" ] +run = "plugin relative-motions 5" +desc = "Move in relative steps" + +[[mgr.prepend_keymap]] +on = [ "6" ] +run = "plugin relative-motions 6" +desc = "Move in relative steps" + +[[mgr.prepend_keymap]] +on = [ "7" ] +run = "plugin relative-motions 7" +desc = "Move in relative steps" + +[[mgr.prepend_keymap]] +on = [ "8" ] +run = "plugin relative-motions 8" +desc = "Move in relative steps" + +[[mgr.prepend_keymap]] +on = [ "9" ] +run = "plugin relative-motions 9" +desc = "Move in relative steps" diff --git a/yazi/.config/yazi/package.toml b/yazi/.config/yazi/package.toml new file mode 100644 index 0000000..269762a --- /dev/null +++ b/yazi/.config/yazi/package.toml @@ -0,0 +1,7 @@ +[[plugin.deps]] +use = "dedukun/relative-motions" +rev = "a603d9e" +hash = "e02a788e5b8ae0fb47fd0193dda589cc" + +[flavor] +deps = [] diff --git a/yazi/.config/yazi/plugins/relative-motions.yazi/LICENSE b/yazi/.config/yazi/plugins/relative-motions.yazi/LICENSE new file mode 100644 index 0000000..f8ead01 --- /dev/null +++ b/yazi/.config/yazi/plugins/relative-motions.yazi/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2024 dedukun + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/yazi/.config/yazi/plugins/relative-motions.yazi/README.md b/yazi/.config/yazi/plugins/relative-motions.yazi/README.md new file mode 100644 index 0000000..106d575 --- /dev/null +++ b/yazi/.config/yazi/plugins/relative-motions.yazi/README.md @@ -0,0 +1,145 @@ +# relative-motions.yazi + +A [Yazi](https://github.com/sxyazi/yazi) plugin based about vim motions. + + + +## Requirements + +- [Yazi](https://github.com/sxyazi/yazi) v25.5.28+ + +## Installation + +```sh +ya pkg add dedukun/relative-motions +``` + +## Configuration + +If you want to use the numbers directly to start a motion add this to your `keymap.toml`: + +
+ +```toml +[[mgr.prepend_keymap]] +on = [ "1" ] +run = "plugin relative-motions 1" +desc = "Move in relative steps" + +[[mgr.prepend_keymap]] +on = [ "2" ] +run = "plugin relative-motions 2" +desc = "Move in relative steps" + +[[mgr.prepend_keymap]] +on = [ "3" ] +run = "plugin relative-motions 3" +desc = "Move in relative steps" + +[[mgr.prepend_keymap]] +on = [ "4" ] +run = "plugin relative-motions 4" +desc = "Move in relative steps" + +[[mgr.prepend_keymap]] +on = [ "5" ] +run = "plugin relative-motions 5" +desc = "Move in relative steps" + +[[mgr.prepend_keymap]] +on = [ "6" ] +run = "plugin relative-motions 6" +desc = "Move in relative steps" + +[[mgr.prepend_keymap]] +on = [ "7" ] +run = "plugin relative-motions 7" +desc = "Move in relative steps" + +[[mgr.prepend_keymap]] +on = [ "8" ] +run = "plugin relative-motions 8" +desc = "Move in relative steps" + +[[mgr.prepend_keymap]] +on = [ "9" ] +run = "plugin relative-motions 9" +desc = "Move in relative steps" +``` + +
+ +Alternatively you can use a key to trigger a new motion without any initial value, for that add the following in `keymap.toml`: + +```toml +[[mgr.prepend_keymap]] +on = [ "m" ] +run = "plugin relative-motions" +desc = "Trigger a new relative motion" +``` + +--- + +Additionally there are a couple of initial configurations that can be given to the plugin's `setup` function: + +| Configuration | Values | Default | Description | +| -------------- | ----------------------------------------------------- | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------- | +| `show_numbers` | `relative`, `absolute`, `relative_absolute` or `none` | `none` | Shows relative or absolute numbers before the file icon | +| `show_motion` | `true` or `false` | `false` | Shows current motion in Status bar | +| `only_motions` | `true` or `false` | `false` | If true, only the motion movements will be enabled, i.e., the commands for delete, cut, yank and visual selection will be disabled | +| `enter_mode` | `cache`, `first` or `cache_or_first` | `cache_or_first` | The method to enter folders | + +If you want, for example, to enable relative numbers as well as to show the motion in the status bar, +add the following to Yazi's `init.lua`, i.e. `~/.config/yazi/init.lua`: + +```lua +-- ~/.config/yazi/init.lua +require("relative-motions"):setup({ show_numbers="relative", show_motion = true, enter_mode ="first" }) +``` + +> [!NOTE] +> The `show_numbers` and `show_motion` functionalities overwrite [`Current:redraw`](https://github.com/sxyazi/yazi/blob/e3c91115a2c096724303a0b364e7625691e4beba/yazi-plugin/preset/components/current.lua#L28) +> and [`Status:children_redraw`](https://github.com/sxyazi/yazi/blob/e3c91115a2c096724303a0b364e7625691e4beba/yazi-plugin/preset/components/status.lua#L177) respectively. +> If you have custom implementations for any of this functions +> you can add the provided `Entity:number` and `Status:motion` to your implementations, just check [here](https://github.com/dedukun/relative-motions.yazi/blob/main/init.lua#L126) how we are doing things. + +## Usage + +This plugin adds the some basic vim motions like `3k`, `12j`, `10gg`, etc. +The following table show all the available motions: + +| Command | Description | +| -------------- | --------------------- | +| `j`/`` | Move `n` lines down | +| `k`/`` | Move `n` lines up | +| `h`/`` | Move `n` folders back | +| `l`/`` | Enter `n` folders | +| `gj`/`g` | Go `n` lines down | +| `gk`/`g` | Go `n` lines up | +| `gg` | Go to line | + +Furthermore, the following operations were also added: + +| Command | Description | +| ------- | ------------- | +| `v` | visual select | +| `y` | Yank | +| `x` | Cut | +| `d` | Delete motion | + +This however must be followed by a direction, which can be `j`/``, `k`/`` or repeating the command key, +which will operate from the cursor down, e.g. `2yy` will copy two files. + +Finally, we also support some tab operations: + +| Command | Description | +| ------- | ------------------------------------ | +| `t` | create `n` tabs | +| `H` | Move `n` tabs left | +| `L` | Move `n` tabs right | +| `gt` | Go to the `n` tab | +| `w` | Close tab `n` | +| `W` | Close `n` tabs right | +| `<` | Swap current tab with `n` tabs left | +| `>` | Swap current tab with `n` tabs right | +| `~` | Swap current tab with tab `n` | diff --git a/yazi/.config/yazi/plugins/relative-motions.yazi/main.lua b/yazi/.config/yazi/plugins/relative-motions.yazi/main.lua new file mode 100644 index 0000000..356b132 --- /dev/null +++ b/yazi/.config/yazi/plugins/relative-motions.yazi/main.lua @@ -0,0 +1,406 @@ +--- @since 25.5.28 +-- stylua: ignore +local MOTIONS_AND_OP_KEYS = { + { on = "0" }, { on = "1" }, { on = "2" }, { on = "3" }, { on = "4" }, + { on = "5" }, { on = "6" }, { on = "7" }, { on = "8" }, { on = "9" }, + -- commands + { on = "d" }, { on = "v" }, { on = "y" }, { on = "x" }, + -- tab commands + { on = "t" }, { on = "L" }, { on = "H" }, { on = "w" }, + { on = "W" }, { on = "<" }, { on = ">" }, { on = "~" }, + -- movement + { on = "g" }, { on = "j" }, { on = "k" }, { on = "h" }, { on = "l" }, { on = "" }, { on = "" }, { on = "" }, { on = "" } +} + +-- stylua: ignore +local MOTION_KEYS = { + { on = "0" }, { on = "1" }, { on = "2" }, { on = "3" }, { on = "4" }, + { on = "5" }, { on = "6" }, { on = "7" }, { on = "8" }, { on = "9" }, + -- movement + { on = "g" }, { on = "j" }, { on = "k" }, { on = "h" }, { on = "l" }, { on = "" }, { on = "" }, { on = "" }, { on = "" } +} + +-- stylua: ignore +local DIRECTION_KEYS = { + { on = "j" }, { on = "k" }, { on = "" }, { on = "" }, + -- tab movement + { on = "t" } +} + +local SHOW_NUMBERS_ABSOLUTE = 0 +local SHOW_NUMBERS_RELATIVE = 1 +local SHOW_NUMBERS_RELATIVE_ABSOLUTE = 2 + +local ENTER_MODE_FIRST = 0 +local ENTER_MODE_CACHE = 1 +local ENTER_MODE_CACHE_OR_FIRST = 2 + +----------------------------------------------- +----------------- R E N D E R ----------------- +----------------------------------------------- + +local render_motion_setup = ya.sync(function(_) + if ui.render then + ui.render() + else + ya.render() + end + + Status.motion = function() return ui.Span("") end + + Status.children_redraw = function(self, side) + local lines = {} + if side == self.RIGHT then + lines[1] = self:motion(self) + end + for _, c in ipairs(side == self.RIGHT and self._right or self._left) do + lines[#lines + 1] = (type(c[1]) == "string" and self[c[1]] or c[1])(self) + end + return ui.Line(lines) + end + + -- TODO: check why it doesn't work line this + -- Status:children_add(function() return ui.Span("") end, 1000, Status.RIGHT) +end) + +local render_motion = ya.sync(function(_, motion_num, motion_cmd) + if ui.render then + ui.render() + else + ya.render() + end + + Status.motion = function(self) + if not motion_num then + return ui.Span("") + end + + local style = self:style() + + local motion_span + if not motion_cmd then + motion_span = ui.Span(string.format(" %3d ", motion_num)) + else + motion_span = ui.Span(string.format(" %3d%s ", motion_num, motion_cmd)) + end + + local status_config = th.status + local separator_open = status_config.sep_right.open + local separator_close = status_config.sep_right.close + + -- TODO: REMOVE THIS IN NEXT RELEASE + local bg_style + if type(style.main.bg) == "function" then + bg_style = style.main:bg() + else + bg_style = style.main.bg + end + + return ui.Line { + ui.Span(separator_open):fg(bg_style), + motion_span:style(style.main), + ui.Span(separator_close):fg(bg_style), + ui.Span(" "), + } + end +end) + +local render_numbers = ya.sync(function(_, mode) + if ui.render then + ui.render() + else + ya.render() + end + + Entity.number = function(_, index, total, file, hovered) + local idx + if mode == SHOW_NUMBERS_RELATIVE then + idx = math.abs(hovered - index) + elseif mode == SHOW_NUMBERS_ABSOLUTE then + idx = file.idx + else -- SHOW_NUMBERS_RELATIVE_ABSOLUTE + if hovered == index then + idx = file.idx + else + idx = math.abs(hovered - index) + end + end + + local num_format = "%" .. #tostring(total) .. "d" + + -- emulate vim's hovered offset + if hovered == index then + return ui.Span(string.format(num_format .. " ", idx)) + else + return ui.Span(string.format(" " .. num_format, idx)) + end + end + + Current.redraw = function(self) + local files = self._folder.window + if #files == 0 then + return self:empty() + end + + local hovered_index + for i, f in ipairs(files) do + if f.is_hovered then + hovered_index = i + break + end + end + + local entities, linemodes = {}, {} + for i, f in ipairs(files) do + linemodes[#linemodes + 1] = Linemode:new(f):redraw() + + local entity = Entity:new(f) + entities[#entities + 1] = ui.Line({ Entity:number(i, #self._folder.files, f, hovered_index), entity:redraw() }) + :style(entity:style()) + end + + return { + ui.List(entities):area(self._area), + ui.Text(linemodes):area(self._area):align(ui.Align.RIGHT), + } + end +end) + +local function render_clear() render_motion() end + +----------------------------------------------- +--------- C O M M A N D P A R S E R --------- +----------------------------------------------- + +local get_keys = ya.sync(function(state) return state._only_motions and MOTION_KEYS or MOTIONS_AND_OP_KEYS end) + +local function normal_direction(dir) + if dir == "" then + return "j" + elseif dir == "" then + return "k" + elseif dir == "" then + return "h" + elseif dir == "" then + return "l" + end + return dir +end + +local function get_cmd(first_char, keys) + local last_key + local lines = first_char or "" + + while true do + render_motion(tonumber(lines)) + local key = ya.which { cands = keys, silent = true } + if not key then + return nil, nil, nil + end + + last_key = keys[key].on + if not tonumber(last_key) then + last_key = normal_direction(last_key) + break + end + + lines = lines .. last_key + end + + render_motion(tonumber(lines), last_key) + + -- command direction + local direction + if last_key == "g" or last_key == "v" or last_key == "d" or last_key == "y" or last_key == "x" then + DIRECTION_KEYS[#DIRECTION_KEYS + 1] = { + on = last_key, + } + local direction_key = ya.which { cands = DIRECTION_KEYS, silent = true } + if not direction_key then + return nil, nil, nil + end + + direction = DIRECTION_KEYS[direction_key].on + direction = normal_direction(direction) + end + + return tonumber(lines), last_key, direction +end + +local function is_tab_command(command) + local tab_commands = { "t", "L", "H", "w", "W", "<", ">", "~" } + for _, cmd in ipairs(tab_commands) do + if command == cmd then + return true + end + end + return false +end + +local get_active_tab = ya.sync(function(_) return cx.tabs.idx end) + +local get_cache_or_first_dir = ya.sync(function(state) + if state._enter_mode == ENTER_MODE_CACHE then + return nil + elseif state._enter_mode == ENTER_MODE_CACHE_OR_FIRST then + local hovered_file = cx.active.current.hovered + + if hovered_file ~= nil and hovered_file.cha.is_dir then + return cx.active.current.cursor + end + end + + local files = cx.active.current.files + local index = 1 + + for i = 1, #files do + if files[i].cha.is_dir then + index = i + break + end + end + + return index - 1 +end) +----------------------------------------------- +---------- E N T R Y / S E T U P ---------- +----------------------------------------------- + +return { + entry = function(_, job) + local initial_value + + local args = job.args + -- this is checking if the argument is a valid number + if #args > 0 then + initial_value = tostring(tonumber(args[1])) + if initial_value == "nil" then + return + end + end + + local lines, cmd, direction = get_cmd(initial_value, get_keys()) + if not lines or not cmd then + -- command was cancelled + render_clear() + return + end + + if cmd == "g" then + if direction == "g" then + ya.mgr_emit("arrow", { "top" }) + ya.mgr_emit("arrow", { lines - 1 }) + render_clear() + return + elseif direction == "j" then + cmd = "j" + elseif direction == "k" then + cmd = "k" + elseif direction == "t" then + ya.mgr_emit("tab_switch", { lines - 1 }) + render_clear() + return + else + -- no valid direction + render_clear() + return + end + end + + if cmd == "j" then + ya.mgr_emit("arrow", { lines }) + elseif cmd == "k" then + ya.mgr_emit("arrow", { -lines }) + elseif cmd == "h" then + for _ = 1, lines do + ya.mgr_emit("leave", {}) + end + elseif cmd == "l" then + for _ = 1, lines do + ya.mgr_emit("enter", {}) + local file_idx = get_cache_or_first_dir() + if file_idx then + ya.mgr_emit("arrow", { "top" }) + ya.mgr_emit("arrow", { file_idx }) + end + end + elseif is_tab_command(cmd) then + if cmd == "t" then + for _ = 1, lines do + ya.mgr_emit("tab_create", {}) + end + elseif cmd == "H" then + ya.mgr_emit("tab_switch", { -lines, relative = true }) + elseif cmd == "L" then + ya.mgr_emit("tab_switch", { lines, relative = true }) + elseif cmd == "w" then + ya.mgr_emit("tab_close", { lines - 1 }) + elseif cmd == "W" then + local curr_tab = get_active_tab() + local del_tab = curr_tab + lines - 1 + for _ = curr_tab, del_tab do + ya.mgr_emit("tab_close", { curr_tab - 1 }) + end + ya.mgr_emit("tab_switch", { curr_tab - 1 }) + elseif cmd == "<" then + ya.mgr_emit("tab_swap", { -lines }) + elseif cmd == ">" then + ya.mgr_emit("tab_swap", { lines }) + elseif cmd == "~" then + local jump = lines - get_active_tab() + ya.mgr_emit("tab_swap", { jump }) + end + else + ya.mgr_emit("visual_mode", {}) + -- invert direction when user specifies it + if direction == "k" then + ya.mgr_emit("arrow", { -lines }) + elseif direction == "j" then + ya.mgr_emit("arrow", { lines }) + else + ya.mgr_emit("arrow", { lines - 1 }) + end + ya.mgr_emit("escape", {}) + + if cmd == "d" then + ya.mgr_emit("remove", {}) + elseif cmd == "y" then + ya.mgr_emit("yank", {}) + elseif cmd == "x" then + ya.mgr_emit("yank", { cut = true }) + end + end + + render_clear() + end, + setup = function(state, args) + if not args then + return + end + + -- initialize state variables + state._only_motions = args["only_motions"] or false + + if args["show_motion"] then + render_motion_setup() + end + + if args["enter_mode"] == "cache" then + state._enter_mode = ENTER_MODE_CACHE + elseif args["enter_mode"] == "first" then + state._enter_mode = ENTER_MODE_FIRST + elseif args["enter_mode"] == "cache_or_first" then + state._enter_mode = ENTER_MODE_CACHE_OR_FIRST + else + state._enter_mode = ENTER_MODE_CACHE_OR_FIRST + end + + if args["show_numbers"] == "absolute" then + render_numbers(SHOW_NUMBERS_ABSOLUTE) + elseif args["show_numbers"] == "relative" then + render_numbers(SHOW_NUMBERS_RELATIVE) + elseif args["show_numbers"] == "relative_absolute" then + render_numbers(SHOW_NUMBERS_RELATIVE_ABSOLUTE) + end + end, +} diff --git a/yazi/.config/yazi/theme.toml b/yazi/.config/yazi/theme.toml index b6a9639..0de801b 100644 --- a/yazi/.config/yazi/theme.toml +++ b/yazi/.config/yazi/theme.toml @@ -1,8 +1,13 @@ [mgr] cwd = { fg = "#94e2d5" } # current working directory color -hovered = { fg = "#003636", bg = "#FFC600" } -preview_hovered = { fg = "#003636", bg = "#00afd7" } +#hovered = { fg = "#003636", bg = "#8affff" } +#preview_hovered = { fg = "#003636", bg = "#8affff" } +#hovered = { fg = "#FFFFFF", bg = "#395e5e" } +#preview_hovered = { fg = "#FFFFFF", bg = "#395e5e" } +hovered = { bg = "#395e5e" } +preview_hovered = { bg = "#395e5e" } +marker_marked = { fg = "#8affff", bg = "#8affff" } marker_selected = { fg = "#00afd7", bg = "#00afd7" } marker_copied = { fg = "#00e6a9", bg = "#00e6a9" } marker_cut = { fg = "#ff5370", bg = "#ff5370" } @@ -11,14 +16,15 @@ count_selected = { fg = "#003636", bg = "#00afd7" } count_copied = { fg = "#003636", bg = "#00e6a9" } count_cut = { fg = "#003636", bg = "#ff5370" } -find_keyword = { fg = "#00e6a9", bold = true, underline=true } +find_keyword = { fg = "#FFC600", bold = true} border_symbol = " " [tabs] -active = { fg = "#1e1e2e", bg = "#cdd6f4" } -inactive = { fg = "#cdd6f4", bg = "#45475a" } -sep_inner = { open = "[", close = "]" } +active = { fg = "#015959", bg = "#8affff" } +inactive = { fg = "#8affff", bg = "#015959"} +sep_inner = { open = "", close = "" } +sep_outer = { open = "", close = "" } [filetype] rules = [ @@ -28,3 +34,33 @@ rules = [ [input] border = {fg = "#00afd7"} + +[tasks] +border = {fg = "#00afd7"} + +[spot] +border = {fg = "#00afd7"} +title = {fg = "#8affff"} +tbl_col = {fg = "#00e6a9"} + +[status] +# entire bar text / background +# separators between blocks +sep_left = { open = "", close = "" } +sep_right = { open = "", close = "" } + +[mode] +normal_main = { fg = "#003636", bg = "#2dc5fc", bold = true } +select_main = { fg = "#003636", bg = "#d38cff", bold = true } +unset_main = { fg = "#003636", bg = "#f07178", bold = true } + +normal_alt = { fg = "#8affff", bg = "#008282", bold = true } +select_alt = { fg = "#8affff", bg = "#008282", bold = true } +unset_alt= { fg = "#8affff", bg = "#008282", bold = true } + +[which] + +# description text ("Go to top", etc.) +desc = { fg = "#8affff"} +mask = {} # empty background +cand = { fg = "#FFC600"} -- cgit v1.2.3