linrongbin16/fzfx.nvim

github github
pluginfuzzy-finder
stars 64
issues 1
subscribers 5
forks 1
CREATED

2023-07-26

UPDATED

17 hours ago


fzfx.nvim

https://github.com/linrongbin16/fzfx.nvim/assets/6496887/aa5ef18c-26b4-4a93-bd0c-bfeba6f6caf1

Search fzfx with rg's -g *ch.lua option.

✨ Feature

  • Icons & colors.
  • Windows support.
  • Lua support: preview lua function defined commands and key mappings (todo).
  • Fully dynamic parsing user query and selection, a typical use case is passing raw rg options via -- flag (see Demo).
  • Multiple variants to avoid manual input:
    • Search by visual select.
    • Search by cursor word.
    • Search by yank text.
  • Easily switch on multiple data sources:
    • Whether to filter hidden/ignored files or include them (unrestricted) when searching files.
    • Local branches or remote branches when searching git branches.
    • All diagnostics in workspace or only in current buffer when searching diagnostics.
    • ...
  • Maximized configuration.
  • ...

Actually all above features are built on an engine that support fully dynamic runtime & pipeline control, it allows you to do almost anything you want, please see Configuration and Wiki.

Please see Demo for more features & use cases.

✅ Requirement

  • Neovim ≥ v0.6.0.
  • Nerd fonts (optional for icons).
  • rg (optional for live grep, by default use grep).
  • fd (optional for files, by default use find).
  • bat (optional for preview files, e.g. the right side of live grep, files, by default use cat).
  • git (optional for git commands).

Note: grep, find and cat are unix/linux builtin commands, while on Windows we don't have a builtin shell environment, so install rg, fd and bat should be a better choice. Also see Windows for how to install linux commands on Windows.

Windows

There're many ways to install portable linux shell and builtin commands on Windows, but personally I would recommend below two methods.

Git for Windows

Install with the below 3 options:

  • In Select Components, select Associate .sh files to be run with Bash.

  • In Adjusting your PATH environment, select Use Git and optional Unix tools from the Command Prompt.

  • In Configuring the terminal emulator to use with Git Bash, select Use Windows's default console window.

After this step, git.exe and builtin linux commands(such as sh.exe, grep.exe, find.exe, sleep.exe, cd.exe, ls.exe) will be available in %PATH%.

scoop

Run below powershell commands:

# scoop
Set-ExecutionPolicy RemoteSigned -Scope CurrentUser
irm get.scoop.sh | iex

scoop bucket add extras
scoop install git
scoop install mingw
scoop install coreutils
scoop install sleep
scoop install grep
scoop install findutils

Fix conflicts between embeded commands in C:\Windows\System32 and portable linux commands

Windows actually already provide some commands (find.exe, bash.exe) in C:\Windows\System32 (or %SystemRoot%\system32), which could override our installations. To fix this issue, we could prioritize the git or scoop environment variables in %PATH%.

Path containing whitespace & Escaping issue

This plugin internally extends nvim, fzf and lua scripts to full path when launching command.

But when there're whitespaces on the path, launching correct shell command becomes quite difficult, since it will seriously affected escaping characters. Here're two typical cases:

  1. C:\Program Files\Neovim\bin\nvim.exe - nvim.exe installed in C:\Program Files directory.

    Please add executables (nvim.exe, fzf.exe) to %PATH% ($env:PATH in PowerShell), and set the env configuration:

    require("fzfx").setup({
        env = {
            nvim = 'nvim',
            fzf = 'fzf',
        }
    })
    

    This will help fzfx.nvim avoid the shell command issue.

  2. C:\Users\Lin Rongbin\opt\Neovim\bin\nvim.exe or /Users/linrongbin/Library/Application\ Support/Neovim/bin/nvim - Lin Rongbin (user name) or Application Support (macOS application) contains whitespace.

    We still cannot handle the 2nd case for now, please always try to avoid whitespaces in path.

    Here's an example of searching files command (macOS):

    • /opt/homebrew/bin/nvim -n --clean --headless -l /Users/linrongbin/.local/share/nvim/lazy/fzfx.nvim/bin/files/provider.lua /tmp/nvim.linrongbin/3NXwys/0

    Here's an example of launching fzf command (Windows 10):

    • C:/Users/linrongbin/github/junegunn/fzf/bin/fzf --query "" --header ":: Press \27[38;2;255;121;198mCTRL-U\27[0m to unrestricted mode" --prompt "~/g/l/fzfx.nvim > " --bind "start:unbind(ctrl-r)" --bind "ctrl-u:unbind(ctrl-u)+execute-silent(C:\\Users\\linrongbin\\scoop\\apps\\neovim\\current\\bin\\nvim.exe -n --clean --headless -l C:\\Users\\linrongbin\\github\\linrongbin16\\fzfx.nvim\\bin\\rpc\\client.lua 1)+change-header(:: Press \27[38;2;255;121;198mCTRL-R\27[0m to restricted mode)+rebind(ctrl-r)+reload(C:\\Users\\linrongbin\\scoop\\apps\\neovim\\current\\bin\\nvim.exe -n --clean --headless -l C:\\Users\\linrongbin\\github\\linrongbin16\\fzfx.nvim\\bin\\files\\provider.lua C:\\Users\\linrongbin\\AppData\\Local\\nvim-data\\fzfx.nvim\\switch_files_provider)" --bind "ctrl-r:unbind(ctrl-r)+execute-silent(C:\\Users\\linrongbin\\scoop\\apps\\neovim\\current\\bin\\nvim.exe -n --clean --headless -l C:\\Users\\linrongbin\\github\\linrongbin16\\fzfx.nvim\\bin\\rpc\\client.lua 1)+change-header(:: Press \27[38;2;255;121;198mCTRL-U\27[0m to unrestricted mode)+rebind(ctrl-u)+reload(C:\\Users\\linrongbin\\scoop\\apps\\neovim\\current\\bin\\nvim.exe -n --clean --headless -l C:\\Users\\linrongbin\\github\\linrongbin16\\fzfx.nvim\\bin\\files\\provider.lua C:\\Users\\linrongbin\\AppData\\Local\\nvim-data\\fzfx.nvim\\switch_files_provider)" --preview "C:\\Users\\linrongbin\\scoop\\apps\\neovim\\current\\bin\\nvim.exe -n --clean --headless -l C:\\Users\\linrongbin\\github\\linrongbin16\\fzfx.nvim\\bin\\files\\previewer.lua {}" --bind "ctrl-l:toggle-preview" --expect "enter" --expect "double-click" >C:\\Users\\LINRON~1\\AppData\\Local\\Temp\\nvim.0\\JSmP06\\2

    If the path contains whitespace, that will make all lua scripts contains whitespace, thus the shell command cannot being correctly evaluated.

📦 Install

vim-plug

call plug#begin()

" optional for icons
Plug 'nvim-tree/nvim-web-devicons'

" mandatory
Plug 'junegunn/fzf', { 'do': { -> fzf#install() } }
Plug 'linrongbin16/fzfx.nvim'

call plug#end()

lua require('fzfx').setup()

packer.nvim

return require('packer').startup(function(use)
    -- optional for icons
    use { "nvim-tree/nvim-web-devicons" }

    -- mandatory
    use { "junegunn/fzf", run = ":call fzf#install()" }
    use {
        "linrongbin16/fzfx.nvim",
        config = function()
            require("fzfx").setup()
        end
    }
end)

lazy.nvim

require("lazy").setup({
    -- optional for icons
    { "nvim-tree/nvim-web-devicons" },

    -- mandatory
    { "junegunn/fzf", build = ":call fzf#install()" },
    {
        "linrongbin16/fzfx.nvim",
        dependencies = { "junegunn/fzf" },
        config = function()
            require("fzfx").setup()
        end
    },

})

🚀 Commands

Naming Rules

Commands are named following below rules:

  • All commands are named with prefix Fzfx.
  • The main command name has no suffix.
  • The unrestricted variant is named with U suffix.
  • The visual select variant is named with V suffix.
  • The cursor word variant is named with W suffix.
  • The yank text variant is named with P suffix (just like press the p key).
  • The only current buffer variant is named with B suffix.

Especially for git commands:

  • The remote git branch variant is named with R suffix.
  • The git files in current directory variant is named with C suffix.

Note: command names can be configured, see Configuration.

Bind Keys

  • Exit keys (--expect option)
    • esc: quit.
    • double-click/enter: open/jump to file (behave different on some specific commands).
  • Preview keys
    • alt-p: toggle preview.
    • ctrl-f: preview half page down.
    • ctrl-b: preview half page up.
  • Multi keys
    • ctrl-e: toggle select.
    • ctrl-a: toggle select all.

Note: builtin keys can be configured, see Configuration.

📌 Recommended Key Mappings

Vimscript


" ======== files ========

" find files
nnoremap <space>f :\<C-U>FzfxFiles<CR>
" by visual select
xnoremap <space>f :\<C-U>FzfxFilesV<CR>
" by cursor word
nnoremap <space>wf :\<C-U>FzfxFilesW<CR>
" by yank text
nnoremap <space>pf :\<C-U>FzfxFilesP<CR>

" ======== live grep ========

" live grep
nnoremap <space>l :\<C-U>FzfxLiveGrep<CR>
" by visual select
xnoremap <space>l :\<C-U>FzfxLiveGrepV<CR>
" by cursor word
nnoremap <space>wl :\<C-U>FzfxLiveGrepW<CR>
" by yank text
nnoremap <space>pl :\<C-U>FzfxLiveGrepP<CR>

" ======== buffers ========

" buffers
nnoremap <space>bf :\<C-U>FzfxBuffers<CR>
" by visual select
xnoremap <space>bf :\<C-U>FzfxBuffersV<CR>
" by cursor word
nnoremap <space>wbf :\<C-U>FzfxBuffersW<CR>
" by yank text
nnoremap <space>pbf :\<C-U>FzfxBuffersP<CR>

" ======== git files ========

" git files
nnoremap <space>gf :\<C-U>FzfxGFiles<CR>
" by visual select
xnoremap <space>gf :\<C-U>FzfxGFilesV<CR>
" by cursor word
nnoremap <space>wgf :\<C-U>FzfxGFilesW<CR>
" by yank text
nnoremap <space>pgf :\<C-U>FzfxGFilesP<CR>

" ======== git branches ========

" git branches
nnoremap <space>br :\<C-U>FzfxGBranches<CR>
" by visual select
xnoremap <space>br :\<C-U>FzfxGBranchesV<CR>
" by cursor word
nnoremap <space>wbr :\<C-U>FzfxGBranchesW<CR>
" by yank text
nnoremap <space>pbr :\<C-U>FzfxGBranchesP<CR>

" ======== git commits ========

" git commits
nnoremap <space>gc :\<C-U>FzfxGCommits<CR>
" by visual select
xnoremap <space>gc :\<C-U>FzfxGCommitsV<CR>
" by cursor word
nnoremap <space>wgc :\<C-U>FzfxGCommitsW<CR>
" by yank text
nnoremap <space>pgc :\<C-U>FzfxGCommitsP<CR>

" ======== git blame ========

" git blame
nnoremap <space>gb :\<C-U>FzfxGBlame<CR>
" by visual select
xnoremap <space>gb :\<C-U>FzfxGBlameV<CR>
" by cursor word
nnoremap <space>wgb :\<C-U>FzfxGBlameW<CR>
" by yank text
nnoremap <space>pgb :\<C-U>FzfxGBlameP<CR>

" ======== lsp diagnostics ========

" lsp diagnostics
nnoremap <space>dg :\<C-U>FzfxLspDiagnostics<CR>
" by visual select
xnoremap <space>dg :\<C-U>FzfxLspDiagnosticsV<CR>
" by cursor word
nnoremap <space>wdg :\<C-U>FzfxLspDiagnosticsW<CR>
" by yank text
nnoremap <space>pdg :\<C-U>FzfxLspDiagnosticsP<CR>


" ======== lsp definitions/type definitions/references/implementations ========

" lsp definitions
nnoremap gd :\<C-U>FzfxLspDefinitions<CR>

" lsp type definitions
nnoremap gt :\<C-U>FzfxLspTypeDefinitions<CR>

" lsp references
nnoremap gr :\<C-U>FzfxLspReferences<CR>

" lsp implementations
nnoremap gi :\<C-U>FzfxLspImplementations<CR>

Lua


-- ======== files ========

-- find files
vim.keymap.set('n', '<space>f', '<cmd>FzfxFiles<cr>',
        {silent=true, noremap=true, desc="Find files"})
-- by visual select
vim.keymap.set('x', '<space>f', '<cmd>FzfxFilesV<CR>',
        {silent=true, noremap=true, desc="Find files"})
-- by cursor word
vim.keymap.set('n', '<space>wf', '<cmd>FzfxFilesW<cr>',
        {silent=true, noremap=true, desc="Find files by cursor word"})
-- by yank text
vim.keymap.set('n', '<space>pf', '<cmd>FzfxFilesP<cr>',
        {silent=true, noremap=true, desc="Find files by yank text"})

-- ======== live grep ========

-- live grep
vim.keymap.set('n', '<space>l',
        '<cmd>FzfxLiveGrep<cr>',
        {silent=true, noremap=true, desc="Live grep"})
-- by visual select
vim.keymap.set('x', '<space>l',
        "<cmd>FzfxLiveGrepV<cr>",
        {silent=true, noremap=true, desc="Live grep"})
-- by cursor word
vim.keymap.set('n', '<space>wl',
        '<cmd>FzfxLiveGrepW<cr>',
        {silent=true, noremap=true, desc="Live grep by cursor word"})
-- by yank text
vim.keymap.set('n', '<space>pl',
        '<cmd>FzfxLiveGrepP<cr>',
        {silent=true, noremap=true, desc="Live grep by cursor word"})

-- ======== buffers ========

-- buffers
vim.keymap.set('n', '<space>bf',
        '<cmd>FzfxBuffers<cr>',
        {silent=true, noremap=true, desc="Find buffers"})
-- by visual select
vim.keymap.set('x', '<space>bf',
        "<cmd>FzfxBuffersV<cr>",
        {silent=true, noremap=true, desc="Find buffers"})
-- by cursor word
vim.keymap.set('n', '<space>wbf',
        '<cmd>FzfxBuffersW<cr>',
        {silent=true, noremap=true, desc="Find buffers by cursor word"})
-- by yank text
vim.keymap.set('n', '<space>pbf',
        '<cmd>FzfxBuffersP<cr>',
        {silent=true, noremap=true, desc="Find buffers by yank text"})

-- ======== git files ========

-- git files
vim.keymap.set('n', '<space>gf',
        '<cmd>FzfxGFiles<cr>',
        {silent=true, noremap=true, desc="Find git files"})
-- by visual select
vim.keymap.set('x', '<space>gf',
        "<cmd>FzfxGFilesV<cr>",
        {silent=true, noremap=true, desc="Find git files"})
-- by cursor word
vim.keymap.set('n', '<space>wgf',
        '<cmd>FzfxGFilesW<cr>',
        {silent=true, noremap=true, desc="Find git files by cursor word"})
-- by yank text
vim.keymap.set('n', '<space>pgf',
        '<cmd>FzfxGFilesP<cr>',
        {silent=true, noremap=true, desc="Find git files by yank text"})

-- ======== git branches ========

-- git branches
vim.keymap.set('n', '<space>br', '<cmd>FzfxGBranches<cr>',
        {silent=true, noremap=true, desc="Search git branches"})
-- by visual select
vim.keymap.set('x', '<space>br', '<cmd>FzfxGBranchesV<CR>',
        {silent=true, noremap=true, desc="Search git branches"})
-- by cursor word
vim.keymap.set('n', '<space>wbr', '<cmd>FzfxGBranchesW<cr>',
        {silent=true, noremap=true, desc="Search git branches by cursor word"})
-- by yank text
vim.keymap.set('n', '<space>pbr', '<cmd>FzfxGBranchesP<cr>',
        {silent=true, noremap=true, desc="Search git branches by yank text"})

-- ======== git commits ========

-- git commits
vim.keymap.set('n', '<space>gc', '<cmd>FzfxGCommits<cr>',
        {silent=true, noremap=true, desc="Search git commits"})
-- by visual select
vim.keymap.set('x', '<space>gc', '<cmd>FzfxGCommitsV<CR>',
        {silent=true, noremap=true, desc="Search git commits"})
-- by cursor word
vim.keymap.set('n', '<space>wgc', '<cmd>FzfxGCommitsW<cr>',
        {silent=true, noremap=true, desc="Search git commits by cursor word"})
-- by yank text
vim.keymap.set('n', '<space>pgc', '<cmd>FzfxGCommitsP<cr>',
        {silent=true, noremap=true, desc="Search git commits by yank text"})

-- ======== git blame ========

-- git blame
vim.keymap.set('n', '<space>gb',
        '<cmd>FzfxGBlame<cr>',
        {silent=true, noremap=true, desc="Search git blame"})
-- by visual select
vim.keymap.set('x', '<space>gb',
        "<cmd>FzfxGBlameV<cr>",
        {silent=true, noremap=true, desc="Search git blame"})
-- by cursor word
vim.keymap.set('n', '<space>wgb',
        '<cmd>FzfxGBlameW<cr>',
        {silent=true, noremap=true, desc="Search git blame by cursor word"})
-- by yank text
vim.keymap.set('n', '<space>pgb',
        '<cmd>FzfxGBlameP<cr>',
        {silent=true, noremap=true, desc="Search git blame by yank text"})

-- ======== lsp diagnostics ========

-- lsp diagnostics
vim.keymap.set('n', '<space>dg', '<cmd>FzfxLspDiagnostics<cr>',
        {silent=true, noremap=true, desc="Search lsp diagnostics"})
-- by visual select
vim.keymap.set('x', '<space>dg', '<cmd>FzfxLspDiagnosticsV<CR>',
        {silent=true, noremap=true, desc="Search lsp diagnostics"})
-- by cursor word
vim.keymap.set('n', '<space>wdg', '<cmd>FzfxLspDiagnosticsW<cr>',
        {silent=true, noremap=true, desc="Search lsp diagnostics by cursor word"})
-- by yank text
vim.keymap.set('n', '<space>pdg', '<cmd>FzfxLspDiagnosticsP<cr>',
        {silent=true, noremap=true, desc="Search lsp diagnostics by yank text"})

-- ======== lsp definitions/type definitions/references/implementations ========

-- lsp definitions
vim.keymap.set('n', 'gd', '<cmd>FzfxLspDefinitions<cr>',
        {silent=true, noremap=true, desc="Search lsp definitions"})

-- lsp type definitions
vim.keymap.set('n', 'gt', '<cmd>FzfxLspTypeDefinitions<cr>',
        {silent=true, noremap=true, desc="Search lsp type definitions"})

-- lsp references
vim.keymap.set('n', 'gr', '<cmd>FzfxLspReferences<cr>',
        {silent=true, noremap=true, desc="Search lsp references"})

-- lsp implementations
vim.keymap.set('n', 'gi', '<cmd>FzfxLspImplementations<cr>',
        {silent=true, noremap=true, desc="Search lsp implementations"})

🔧 Configuration

For complete options and default configurations, please check config.lua.

If you have encounter some breaks on configuration, please see Break Changes.

Create your own commands

To create your own commands, please see A General Schema for Creating FZF Command and schema.lua.

💩 Break Changes

  • 2023-08-17
    • Re-bind keys 'ctrl-e'(select), 'ctrl-a'(select-all) to 'toggle', 'toggle-all'.
    • Remove default bind keys 'ctrl-d'(deselect), 'alt-a'(deselect-all).
    • Re-bind key 'ctrl-x' (delete buffer on FzfxBuffers) to 'ctrl-d'.
    • Re-bind key 'ctrl-l' (toggle-preview) to 'alt-p'.
  • 2023-08-19
    • Refactor configs schema for general fzf-based searching commands.
  • 2023-08-28
    • Deprecate 'git_commits' (FzfxGCommits) configs, notify user migrate to new schema.
  • 2023-08-30
    • Deprecate 'buffers' (FzfxBuffers) configs, notify user migrate to new schema.
  • 2023-09-11
    • Deprecate 'git_branches' (FzfxGBranches) configs, notify user migrate to new schema.
  • 2023-09-12
    • Deprecate 'git_files' (FzfxGFiles) configs, notify user migrate to new schema.
  • 2023-09-19
    • Deprecate 'live_grep' (FzfxLiveGrep) configs, notify user migrate to new schema.
    • Deprecate 'files' (FzfxFiles) configs, notify user migrate to new schema.
  • 2023-09-26
    • Break: require minimal Neovim version ≥ v0.6.0.

🍀 Credit

  • fzf.vim: Things you can do with fzf and Vim.
  • fzf-lua: Improved fzf.vim written in lua.

✏️ Development

To develop the project and make PR, please setup with:

To run unit tests, please install below dependencies:

Then test with vusted ./test.

🎁 Contribute

Please open issue/PR for anything about fzfx.nvim.

Like fzfx.nvim? Consider

Github Sponsor Wechat Pay Alipay