diff --git a/PSFzf.Functions.ps1 b/PSFzf.Functions.ps1 index 68ccdc5..568de16 100644 --- a/PSFzf.Functions.ps1 +++ b/PSFzf.Functions.ps1 @@ -351,7 +351,8 @@ function Invoke-PsFzfRipgrep() { # this function is adapted from https://github.com/junegunn/fzf/blob/master/ADVANCED.md#switching-between-ripgrep-mode-and-fzf-mode param([Parameter(Mandatory)]$SearchString, [switch]$NoEditor) - $RG_PREFIX = "rg --column --line-number --no-heading --color=always --smart-case " + $DEFAULT_RG_PREFIX = "rg --column --line-number --no-heading --color=always --smart-case " + $RG_PREFIX = if ($env:PSFZF_RG_PREFIX) { $env:PSFZF_RG_PREFIX } else { $DEFAULT_RG_PREFIX } $INITIAL_QUERY = $SearchString $script:OverrideFzfDefaultCommand = [FzfDefaultCmd]::new('') diff --git a/PSFzf.tests.ps1 b/PSFzf.tests.ps1 index c370453..38265e0 100644 --- a/PSFzf.tests.ps1 +++ b/PSFzf.tests.ps1 @@ -100,6 +100,132 @@ Describe "Find-CurrentPath" { } } +Describe 'Invoke-PsFzfRipgrep' { + InModuleScope PsFzf { + $OriginalPSFZF_RG_PREFIX = $null + $script:CapturedCommand = $null + $script:MockedFzfDefaultCmd = $null + + BeforeEach { + # Store and clear environment variable + $OriginalPSFZF_RG_PREFIX = $env:PSFZF_RG_PREFIX + $env:PSFZF_RG_PREFIX = $null + + # Reset captured command + $script:CapturedCommand = $null + + # Mock Invoke-Expression to capture the command + Mock Invoke-Expression { + param($Command) + $script:CapturedCommand = $Command + # Simulate fzf returning no selection to allow the function to complete + return $null + } -ModuleName PsFzf + + # Mock Get-EditorLaunch to prevent actual editor launch + Mock Get-EditorLaunch { + param($FileList, $LineNum = 0) + # Do nothing, just prevent original function call + return "MockedEditorLaunch $FileList $LineNum" + } -ModuleName PsFzf + + # Mock Resolve-Path for -NoEditor switch + Mock Resolve-Path { + param($Path) + return "Resolved_$Path" # Simulate path resolution + } -ModuleName PsFzf + + # Mock FzfDefaultCmd - new and Restore. + $script:MockedFzfDefaultCmd = @{ Restored = $false } + Mock ([FzfDefaultCmd]::new) { + param([string]$Cmd) + return @{ + OriginalCommand = $Cmd + Restore = { + $script:MockedFzfDefaultCmd.Restored = $true + # Write-Warning "MockedFzfDefaultCmd Restore() called." # Uncomment for debugging + } + ToString = { "MockedFzfDefaultCmdInstance" } + } + } -ModuleName PsFzf + } + + AfterEach { + # Restore environment variable + $env:PSFZF_RG_PREFIX = $OriginalPSFZF_RG_PREFIX + + # Mocks are generally cleaned up by Pester between Describe blocks or by re-mocking. + # If explicit removal is needed, uncomment these: + # Remove-Mock -CommandName 'Invoke-Expression' -ModuleName PsFzf -ErrorAction SilentlyContinue + # Remove-Mock -CommandName 'Get-EditorLaunch' -ModuleName PsFzf -ErrorAction SilentlyContinue + # Remove-Mock -CommandName 'Resolve-Path' -ModuleName PsFzf -ErrorAction SilentlyContinue + # Remove-Mock -CommandName '([FzfDefaultCmd]::new)' -ModuleName PsFzf -ErrorAction SilentlyContinue # Note: Mocking type constructors might need specific syntax for removal if supported + } + + Context 'Default rg command' { + It 'Should use the default rg prefix when PSFZF_RG_PREFIX is not set' { + Invoke-PsFzfRipgrep -SearchString 'testsearch' | Out-Null + + $defaultRgPrefix = "rg --column --line-number --no-heading --color=always --smart-case " + # Check that the default rg prefix is part of the command passed to Invoke-Expression (indirectly, via $env:FZF_DEFAULT_COMMAND) + # This specifically checks the 'reload' part of the fzf --bind arguments + $script:CapturedCommand | Should -Match ([regex]::Escape($defaultRgPrefix)) + $script:MockedFzfDefaultCmd.Restored | Should -BeTrue ("FzfDefaultCmd.Restore() was not called.") + } + } + + Context 'Custom rg command via PSFZF_RG_PREFIX' { + It 'Should use the custom rg prefix when PSFZF_RG_PREFIX is set' { + $customRgPrefix = 'my-custom-rg --awesome ' + $env:PSFZF_RG_PREFIX = $customRgPrefix + + Invoke-PsFzfRipgrep -SearchString 'testsearch' | Out-Null + + $script:CapturedCommand | Should -Match ([regex]::Escape($customRgPrefix)) + $script:CapturedCommand | Should -Not -Match ([regex]::Escape("rg --column --line-number")) + $script:MockedFzfDefaultCmd.Restored | Should -BeTrue ("FzfDefaultCmd.Restore() was not called.") + } + } + + Context 'NoEditor switch' { + It 'Should call Resolve-Path and not Get-EditorLaunch when -NoEditor is used and fzf returns a value' { + # Override Invoke-Expression mock for this specific test to return a value + Mock Invoke-Expression { + param($Command) + $script:CapturedCommand = $Command + return "somefile.txt:123:content" # Simulate fzf selection + } -ModuleName PsFzf + + $result = Invoke-PsFzfRipgrep -SearchString 'testsearch' -NoEditor + + $result | Should -Be "Resolved_somefile.txt" + Should -Invoke 'Resolve-Path' -Times 1 -ModuleName PsFzf -ParameterFilter { $Path -eq 'somefile.txt' } + Should -Not -Invoke 'Get-EditorLaunch' -ModuleName PsFzf + $script:MockedFzfDefaultCmd.Restored | Should -BeTrue ("FzfDefaultCmd.Restore() was not called.") + } + } + + Context 'Editor launch' { + It 'Should call Get-EditorLaunch when -NoEditor is not used and fzf returns a value' { + # Override Invoke-Expression mock for this specific test to return a value + Mock Invoke-Expression { + param($Command) + $script:CapturedCommand = $Command + return "anotherfile.txt:45:foobar" # Simulate fzf selection + } -ModuleName PsFzf + + Invoke-PsFzfRipgrep -SearchString 'testsearch' | Out-Null + + Should -Invoke 'Get-EditorLaunch' -Times 1 -ModuleName PsFzf -ParameterFilter { + $FileList -eq 'anotherfile.txt' -and $LineNum -eq '45' + } + Should -Not -Invoke 'Resolve-Path' -ModuleName PsFzf + $script:MockedFzfDefaultCmd.Restored | Should -BeTrue ("FzfDefaultCmd.Restore() was not called.") + } + } + } +} + Describe 'Invoke-FuzzySetLocation' { InModuleScope PsFzf { diff --git a/docs/Invoke-PsFzfRipgrep.md b/docs/Invoke-PsFzfRipgrep.md index c6098b1..7c8d19c 100644 --- a/docs/Invoke-PsFzfRipgrep.md +++ b/docs/Invoke-PsFzfRipgrep.md @@ -65,5 +65,17 @@ This cmdlet does not generate any output. ## NOTES This function is adapted from [Fzf's advanced document](https://github.com/junegunn/fzf/blob/master/ADVANCED.md#switching-between-ripgrep-mode-and-fzf-mode). This function requires the installation of [ripgrep](https://github.com/BurntSushi/ripgrep) and [bat](https://github.com/sharkdp/bat). + +## CUSTOMIZATION +### `PSFZF_RG_PREFIX` +You can customize the `rg` command used by `Invoke-PsFzfRipgrep` by setting the `PSFZF_RG_PREFIX` environment variable. This allows you to add default arguments to `rg`, such as enabling hidden file search or excluding specific directories. + +For example, to make `rg` search hidden files and exclude the `node_modules` directory, you can set the environment variable as follows: + +```powershell +$env:PSFZF_RG_PREFIX = "rg --hidden --glob '!node_modules'" +``` +The default `rg` command is `"rg --column --line-number --no-heading --color=always --smart-case "`. + ## RELATED LINKS