From 6b8cd30b4d03e5d52f5f940b2105869ad1bcd0b4 Mon Sep 17 00:00:00 2001 From: "alex.tenkoff" Date: Mon, 24 Nov 2025 20:21:00 +0700 Subject: [PATCH] Add fg.yazi plugin --- config/yazi/plugins/fg.yazi | 1 - config/yazi/plugins/fg.yazi/LICENSE | 21 ++++++ config/yazi/plugins/fg.yazi/README.md | 55 ++++++++++++++ config/yazi/plugins/fg.yazi/main.lua | 104 ++++++++++++++++++++++++++ 4 files changed, 180 insertions(+), 1 deletion(-) delete mode 160000 config/yazi/plugins/fg.yazi create mode 100644 config/yazi/plugins/fg.yazi/LICENSE create mode 100644 config/yazi/plugins/fg.yazi/README.md create mode 100644 config/yazi/plugins/fg.yazi/main.lua diff --git a/config/yazi/plugins/fg.yazi b/config/yazi/plugins/fg.yazi deleted file mode 160000 index 2cb5b49..0000000 --- a/config/yazi/plugins/fg.yazi +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 2cb5b49c3471195ebc2fab83fd9fef6fd8a16d5f diff --git a/config/yazi/plugins/fg.yazi/LICENSE b/config/yazi/plugins/fg.yazi/LICENSE new file mode 100644 index 0000000..85c5dfd --- /dev/null +++ b/config/yazi/plugins/fg.yazi/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 DreamMaoMao + +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/config/yazi/plugins/fg.yazi/README.md b/config/yazi/plugins/fg.yazi/README.md new file mode 100644 index 0000000..e8b6702 --- /dev/null +++ b/config/yazi/plugins/fg.yazi/README.md @@ -0,0 +1,55 @@ +# fg.yazi + +https://github.com/DreamMaoMao/fg.yazi/assets/30348075/4b34ff25-800f-4250-b109-172f12a8b0ce + +A Yazi plugin for searching file content or filenames using `ripgrep` with `fzf` preview + +> [!NOTE] +> The latest main branch of Yazi is required at the moment. +> +> Support shell: `bash`, `zsh` ,`fish` ,`nushell` + +## Dependencies + +- fzf +- ripgrep +- bat +- nullshell(only windows need) + +## Install + +```bash +git clone https://github.com/DreamMaoMao/fg.yazi.git ~/.config/yazi/plugins/fg.yazi +``` + +```powershell +git clone https://gitee.com/DreamMaoMao/fg.yazi.git $env:APPDATA\yazi\config\plugins\fg.yazi +``` + + +## Usage + +This option uses `ripgrep` to output all the lines of all files, and then uses `fzf` to fuzzy matching. + +```toml +[[manager.prepend_keymap]] +on = [ "f","g" ] +run = "plugin fg" +desc = "find file by content (fuzzy match)" +``` + +The following option passes the input to `ripgrep` for a match search, reusing the `rg` search each time the input is changed. This is useful for searching in large folders due to increased speed, but it does not support fuzzy matching. + +```toml +[[manager.prepend_keymap]] +on = [ "f","G" ] +run = "plugin fg --args='rg'" +desc = "find file by content (ripgrep match)" +``` + +```toml +[[manager.prepend_keymap]] +on = [ "f","f" ] +run = "plugin fg --args='fzf'" +desc = "find file by filename" +``` diff --git a/config/yazi/plugins/fg.yazi/main.lua b/config/yazi/plugins/fg.yazi/main.lua new file mode 100644 index 0000000..6903aa1 --- /dev/null +++ b/config/yazi/plugins/fg.yazi/main.lua @@ -0,0 +1,104 @@ +local function splitAndGetFirst(inputstr, sep) + if sep == nil then + sep = "%s" + end + local sepStart, sepEnd = string.find(inputstr, sep) + if sepStart then + return string.sub(inputstr, 1, sepStart - 1) + end + return inputstr +end + +local state = ya.sync(function() return tostring(cx.active.current.cwd) end) + +local function fail(s, ...) ya.notify { title = "Fzf", content = string.format(s, ...), timeout = 5, level = "error" } end + +local function entry(_, job) + local args = job.args + local _permit = ya.hide() + local cwd = state() + local shell_value = ya.target_family() == "windows" and "nu" or os.getenv("SHELL"):match(".*/(.*)") + local cmd_args = "" + + local preview_cmd = [===[line={2} && begin=$( if [[ $line -lt 7 ]]; then echo $((line-1)); else echo 6; fi ) && bat --highlight-line={2} --color=always --line-range $((line-begin)):$((line+10)) {1}]===] + if ya.target_family() == "windows" then + preview_cmd = [[bat --highlight-line={2} --color=always --line-range {2}: {1}]] + elseif shell_value == "fish" then + preview_cmd = [[set line {2} && set begin ( test $line -lt 7 && echo (math "$line-1") || echo 6 ) && bat --highlight-line={2} --color=always --line-range (math "$line-$begin"):(math "$line+10") {1}]] + elseif shell_value == "nu" then + preview_cmd = [[let line = ({2} | into int); let begin = if $line < 7 { $line - 1 } else { 6 }; bat --highlight-line={2} --color=always --line-range $'($line - $begin):($line + 10)' {1}]] + end + if ya.target_family() == "windows" and args[1] == "fzf" then + cmd_args = [[fzf --preview="bat --color=always {}"]] + elseif ya.target_family() == "windows" and args[1] == "rg" then + local rg_prefix = [[rg --colors "path:fg:blue" --colors "line:fg:red" --colors "column:fg:yellow" --column --line-number --no-heading --color=always --smart-case ]] + cmd_args = [[fzf --ansi --disabled --bind "start:reload:]] + .. rg_prefix + .. [[{q}" --bind "change:reload:]] + .. rg_prefix + .. [[{q}" --delimiter ":" --preview "]] + .. preview_cmd + .. [[" --preview-window "up,60%" --nth "3.."]] + elseif ya.target_family() == "windows" then + cmd_args = [[rg --color=always --line-number --no-heading --smart-case "" | fzf --ansi --preview="]] .. preview_cmd .. [[" --delimiter=":" --preview-window="up:60%" --nth="3.."]] + elseif args[1] == "fzf" then + cmd_args = [[fzf --preview="bat --color=always {}"]] + elseif args[1] == "rg" and shell_value == "fish" then + cmd_args = [[ + RG_PREFIX="rg --colors 'path:fg:blue' --colors 'line:fg:red' --colors 'column:fg:yellow' --column --line-number --no-heading --color=always --smart-case " \ + fzf --ansi --disabled \ + --bind "start:reload:$RG_PREFIX {q}" \ + --bind "change:reload:sleep 0.1; $RG_PREFIX {q} || true" \ + --delimiter : \ + --preview ']] .. preview_cmd .. [[' \ + --preview-window 'up,60%' \ + --nth '3..' + ]] + elseif args[1] == "rg" and (shell_value == "bash" or shell_value == "zsh") then + cmd_args = [[ + RG_PREFIX="rg --colors 'path:fg:blue' --colors 'line:fg:red' --colors 'column:fg:yellow' --column --line-number --no-heading --color=always --smart-case " + fzf --ansi --disabled \ + --bind "start:reload:$RG_PREFIX {q}" \ + --bind "change:reload:sleep 0.1; $RG_PREFIX {q} || true" \ + --delimiter : \ + --preview ']] .. preview_cmd .. [[' \ + --preview-window 'up,60%' \ + --nth '3..' + ]] + elseif args[1] == "rg" and shell_value == "nu" then + local rg_prefix = "rg --colors 'path:fg:blue' --colors 'line:fg:red' --colors 'column:fg:yellow' --column --line-number --no-heading --color=always --smart-case " + cmd_args = [[fzf --ansi --disabled --bind "start:reload:]] + .. rg_prefix + .. [[{q}" --bind "change:reload:sleep 100ms; try { ]] + .. rg_prefix + .. [[{q} }" --delimiter : --preview ']] + .. preview_cmd + .. [[' --preview-window 'up,60%' --nth '3..']] + else + cmd_args = [[rg --color=always --line-number --no-heading --smart-case '' | fzf --ansi --preview=']] .. preview_cmd .. [[' --delimiter=':' --preview-window='up:60%' --nth='3..']] + end + + local child, err = + Command(shell_value):args({"-c", cmd_args}):cwd(cwd):stdin(Command.INHERIT):stdout(Command.PIPED):stderr(Command.INHERIT):spawn() + + if not child then + return fail("Spawn `rfzf` failed with error code %s. Do you have it installed?", err) + end + + local output, err = child:wait_with_output() + if not output then + return fail("Cannot read `fzf` output, error code %s", err) + elseif not output.status.success and output.status.code ~= 130 then + return fail("`fzf` exited with error code %s", output.status.code) + end + + local target = output.stdout:gsub("\n$", "") + + local file_url = splitAndGetFirst(target,":") + + if file_url ~= "" then + ya.manager_emit(file_url:match("[/\\]$") and "cd" or "reveal", { file_url }) + end +end + +return { entry = entry }