🪟 INTRO
If you want to integrate fzf
with rg
, fd
, bat
to fuzzy find files, directories or ripgrep the content of a file and preview using bat
, but the fzf document only has commands for Linux shell (bash,...), and you want to achieve that on your Windows Machine using Powershell, this post may be for you.
💫 INTRODUCTION FOR THOSE MENTIONED CLI TOOL
-
fzf
: 🌸 A command-line fuzzy finder -
ripgrep
(rg): ripgrep recursively searches directories for a regex pattern while respecting your gitignore -
fd
: A simple, fast and user-friendly alternative to 'find' -
bat
: A cat(1) clone with wings. -
eza
(optional): A modern, maintained replacement for ls
NOTE
You can install those tool via
scoop
orChocolatey
for easy installation and update.
💻 COMMANDS
NOTE
fzf
usesCMD
for external command, notPowershell
orpwsh
.I won't explain the commands in details.
1️⃣ Find files / directories
Preview
- Find files
- Find directories
Command
fd --type file --follow --hidden --exclude .git |
fzf --prompt 'Files> ' `
--header-first `
--header 'CTRL-S: Switch between Files/Directories' `
--bind 'ctrl-s:transform:if not "%FZF_PROMPT%"=="Files> " (echo ^change-prompt^(Files^> ^)^+^reload^(fd --type file^)) else (echo ^change-prompt^(Directory^> ^)^+^reload^(fd --type directory^))' `
--preview 'if "%FZF_PROMPT%"=="Files> " (bat --color=always {} --style=plain) else (eza -T --colour=always --icons=always {})'
- The command above pipes the output of
fd
(find files) tofzf
. Press Ctrl + S to change the mode to find directories. The preview pane usingbat
to preview file's content is on the right side, andeza
for directory tree. - You can use
tree
command on Windows instead ofeza
in the last argument. But it wouldn't be colourful and have files icons.
- eza -T --colour=always --icons=always {}
+ tree /A {}
2️⃣ Ripgrep content + fzf
Preview
- Ripgrep
- Fzf
Command
$INITIAL_QUERY = "${*:-}"
$RG_PREFIX = "rg --column --line-number --no-heading --color=always --smart-case"
"" |
fzf --ansi --disabled --query "$INITIAL_QUERY" `
--bind "start:reload:$RG_PREFIX {q}" `
--bind "change:reload:sleep 0.1 & $RG_PREFIX {q} || rem" `
--bind 'ctrl-s:transform:if not "%FZF_PROMPT%" == "1. ripgrep> " (echo ^rebind^(change^)^+^change-prompt^(1. ripgrep^> ^)^+^disable-search^+^transform-query:echo ^{q^} ^> %TEMP%\rg-fzf-f ^& type %TEMP%\rg-fzf-r) else (echo ^unbind^(change^)^+^change-prompt^(2. fzf^> ^)^+^enable-search^+^transform-query:echo ^{q^} ^> %TEMP%\rg-fzf-r ^& type %TEMP%\rg-fzf-f)' `
--color 'hl:-1:underline,hl+:-1:underline:reverse' `
--delimiter ':' `
--prompt '1. ripgrep> ' `
--preview-label 'Preview' `
--header 'CTRL-S: Switch between ripgrep/fzf' `
--header-first `
--preview 'bat --color=always {1} --highlight-line {2} --style=plain' `
--preview-window 'up,60%,border-bottom,+{2}+3/3'
```
- The command above use `ripgrep` to find the content of the file and preview using `bat`. Then you can find more specifically by pressing <kbd>Ctrl</kbd> + <kbd>S</kbd> to switch to `fzf` mode. Press <kbd>Ctrl</kbd> + <kbd>S</kbd> again to back to the first step _(`ripgrep`)_.
---
## ⚙️ ADVANCED USE
You can put them all in a function, choose what to do with the output of the command, like `Set-Location`, or open the file / directory in your editor. Just open your `$PROFILE` file using your editor (ex: `nvim $PROFILE`).
Take my config as reference _(require [`PsReadLine`](https://github.com/PowerShell/PSReadLine) - may be builtin - to config keyboard shortcut to fast call function)_.
```powershell
$env:FZF_DEFAULT_OPTS=@"
--layout=reverse
--cycle
--scroll-off=5
--border
--preview-window=right,60%,border-left
--bind ctrl-u:preview-half-page-up
--bind ctrl-d:preview-half-page-down
--bind ctrl-f:preview-page-down
--bind ctrl-b:preview-page-up
--bind ctrl-g:preview-top
--bind ctrl-h:preview-bottom
--bind alt-w:toggle-preview-wrap
--bind ctrl-e:toggle-preview
"@
function _fzf_open_path
{
param (
[Parameter(Mandatory=$true)]
[string]$input_path
)
if ($input_path -match "^.*:\d+:.*$")
{
$input_path = ($input_path -split ":")[0]
}
if (-not (Test-Path $input_path))
{
return
}
$cmds = @{
'bat' = { bat $input_path }
'cat' = { Get-Content $input_path }
'cd' = {
if (Test-Path $input_path -PathType Leaf)
{
$input_path = Split-Path $input_path -Parent
}
Set-Location $input_path
}
'nvim' = { nvim $input_path }
'remove' = { Remove-Item -Recurse -Force $input_path }
'echo' = { Write-Output $input_path }
}
$cmd = $cmds.Keys | fzf --prompt 'Select command> '
& $cmds[$cmd]
}
function _fzf_get_path_using_fd
{
$input_path = fd --type file --follow --hidden --exclude .git |
fzf --prompt 'Files> ' `
--header-first `
--header 'CTRL-S: Switch between Files/Directories' `
--bind 'ctrl-s:transform:if not "%FZF_PROMPT%"=="Files> " (echo ^change-prompt^(Files^> ^)^+^reload^(fd --type file^)) else (echo ^change-prompt^(Directory^> ^)^+^reload^(fd --type directory^))' `
--preview 'if "%FZF_PROMPT%"=="Files> " (bat --color=always {} --style=plain) else (eza -T --colour=always --icons=always {})'
return $input_path
}
function _fzf_get_path_using_rg
{
$INITIAL_QUERY = "${*:-}"
$RG_PREFIX = "rg --column --line-number --no-heading --color=always --smart-case"
$input_path = "" |
fzf --ansi --disabled --query "$INITIAL_QUERY" `
--bind "start:reload:$RG_PREFIX {q}" `
--bind "change:reload:sleep 0.1 & $RG_PREFIX {q} || rem" `
--bind 'ctrl-s:transform:if not "%FZF_PROMPT%" == "1. ripgrep> " (echo ^rebind^(change^)^+^change-prompt^(1. ripgrep^> ^)^+^disable-search^+^transform-query:echo ^{q^} ^> %TEMP%\rg-fzf-f ^& type %TEMP%\rg-fzf-r) else (echo ^unbind^(change^)^+^change-prompt^(2. fzf^> ^)^+^enable-search^+^transform-query:echo ^{q^} ^> %TEMP%\rg-fzf-r ^& type %TEMP%\rg-fzf-f)' `
--color 'hl:-1:underline,hl+:-1:underline:reverse' `
--delimiter ':' `
--prompt '1. ripgrep> ' `
--preview-label 'Preview' `
--header 'CTRL-S: Switch between ripgrep/fzf' `
--header-first `
--preview 'bat --color=always {1} --highlight-line {2} --style=plain' `
--preview-window 'up,60%,border-bottom,+{2}+3/3'
return $input_path
}
function fdg
{
_fzf_open_path $(_fzf_get_path_using_fd)
}
function rgg
{
_fzf_open_path $(_fzf_get_path_using_rg)
}
# SET KEYBOARD SHORTCUTS TO CALL FUNCTION
Set-PSReadLineKeyHandler -Key "Ctrl+f" -ScriptBlock {
[Microsoft.PowerShell.PSConsoleReadLine]::RevertLine()
[Microsoft.PowerShell.PSConsoleReadLine]::Insert("fdg")
[Microsoft.PowerShell.PSConsoleReadLine]::AcceptLine()
}
Set-PSReadLineKeyHandler -Key "Ctrl+g" -ScriptBlock {
[Microsoft.PowerShell.PSConsoleReadLine]::RevertLine()
[Microsoft.PowerShell.PSConsoleReadLine]::Insert("rgg")
[Microsoft.PowerShell.PSConsoleReadLine]::AcceptLine()
}
NOTE
- Due to
become
action on Windows doesn't work well as we expected to echo out the{1}
, we can only handle in the if check in_fzf_open_path
- Also because
echo
command in Windows cmd is weird, there will be some backslash when switching mode in_fzf_get_path_using_rg
. Just delete them and use normally
😎 LAST WORDS
- You can read more on document of those tool for further use (custom fzf layout, color schemes,...)
- Please change the command according to your demands.
- Read more about the amazing tool
PSFzf
, a PowerShell wrapper around the fuzzy finder fzf. - By the way you can come visit my windows dotfiles and grab some stuff you like 😁.
- My English is not good and this is the first time I write a post in English. If there are any mistakes, please forgive me.
- Thank you.
Top comments (5)
Oh man I'm a long time linux users currently making windows and cli feel like linux. I'm getting quite proficient with it and have a nice starter dotfiles now. But I am learning loads from your dotfiles!
I hope you will be happy on Windows. I have started to use Linux along with Windows recently 😁
🔥
❤️🔥