πͺ 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 π
π₯
β€οΈβπ₯