src/search.psm1
Import-Module -Scope Local "$PSScriptRoot/api.psm1" $RepositoryFields = Get-Content -Raw "$PSScriptRoot/../queries/RepositoryFields.graphql" $SearchQuery = (Get-Content -Raw "$PSScriptRoot/../queries/Search.graphql") + $RepositoryFields $SuggestionsQuery = (Get-Content -Raw "$PSScriptRoot/../queries/Suggestions.graphql") # Note: The default name of this function is Search-Sourcegraph, # the prefix/suffix is added automatically as configured in PSSourcegraph.psd1 # or overridden when calling Import-Module. function Search- { <# .SYNOPSIS Get users on a Sourcegraph instance .PARAMETER Query The search query. .PARAMETER CaseSensitive Match the query case-sensitive. Only for regexp and literal search. .PARAMETER Structural Interpret the query as a structural search query. .PARAMETER RegularExpression Interpret the query as a regular expression. .PARAMETER Endpoint The endpoint URL of the Sourcegraph instance (default https://sourcegraph.com) .PARAMETER Token The authentication token (if needed). Go to the settings page on Sourcegraph to generate one. #> [CmdletBinding(SupportsPaging, DefaultParameterSetName = 'literal')] param( [Parameter(Mandatory, Position = 0)] [string] $Query, [Parameter(ParameterSetName = 'regexp')] [Parameter(ParameterSetName = 'literal')] [switch] $CaseSensitive, [Parameter(ParameterSetName = 'regexp', Mandatory)] [Alias('Regexp')] [switch] $RegularExpression, [Parameter(ParameterSetName = 'structural', Mandatory)] [switch] $Structural, [Uri] $Endpoint = 'https://sourcegraph.com', [SecureString] $Token ) process { if ($CaseSensitive) { $Query += ' case:yes' } $variables = @{ query = $Query patternType = $PSCmdlet.ParameterSetName } $data = Invoke-ApiRequest -Query $SearchQuery -Variables $variables -Endpoint $Endpoint -Token $Token if ($data.search.results.cloning.Count -gt 0) { Write-Warning "Cloning:" $data.search.results.cloning.name | Write-Warning } if ($data.search.results.missing.Count -gt 0) { Write-Warning "Missing:" $data.search.results.missing.name | Write-Warning } if ($data.search.results.timedout.Count -gt 0) { Write-Warning "Timed out:" $data.search.results.timedout.name | Write-Warning } if ($PSCmdlet.PagingParameters.IncludeTotalCount) { $PSCmdlet.PagingParameters.NewTotalCount($data.search.results.matchCount, 1) } if ($data.search.results.limitHit) { Write-Warning "Result limit hit" } foreach ($result in $data.search.results.results) { $result.PSObject.TypeNames.Insert(0, 'Sourcegraph.' + $result.__typename) # Make the metadata accessible from the match objects Add-Member -InputObject $result -MemberType NoteProperty -Name 'SearchResults' -Value $data.search.results if ($result.__typename -eq 'FileMatch') { # Make URL absolute $result.File.Url = [Uri]::new($Endpoint, $result.File.Url) $result.Repository.Url = [Uri]::new($Endpoint, $result.Repository.Url) if ($result.LineMatches -or $result.Symbols) { # Instead of nesting LineMatches and Symbols in FileMatches, we flat out the list and let PowerShell formatting do the grouping foreach ($lineMatch in $result.LineMatches) { $lineMatch.PSObject.TypeNames.Insert(0, 'Sourcegraph.LineMatch') Add-Member -InputObject $lineMatch -MemberType NoteProperty -Name 'FileMatch' -Value $result $lineMatch } foreach ($symbol in $result.Symbols) { $symbol.PSObject.TypeNames.Insert(0, 'Sourcegraph.Symbol') Add-Member -InputObject $symbol -MemberType NoteProperty -Name 'FileMatch' -Value $result $symbol } } else { # The FileMatch has no line or symbol matches, which means the file name matched, so add the FileMatch itself as a result $result } } else { $result } } } } Export-ModuleMember -Function Search- function Get-SearchSuggestions { [CmdletBinding(DefaultParameterSetName = 'literal')] param ( [Parameter(Mandatory)] [string] $Query, [Parameter(ParameterSetName = 'regexp')] [Parameter(ParameterSetName = 'literal')] [switch] $CaseSensitive, [Parameter(ParameterSetName = 'regexp', Mandatory)] [Alias('Regexp')] [switch] $RegularExpression, [Parameter(ParameterSetName = 'structural', Mandatory)] [switch] $Structural, [Uri] $Endpoint = 'https://sourcegraph.com', [SecureString] $Token ) process { if ($CaseSensitive) { $Query += ' case:yes' } $vars = @{ query = $Query first = 10 patternType = $PSCmdlet.ParameterSetName } Invoke-ApiRequest -Query $SuggestionsQuery -Variables $vars -Endpoint $Endpoint -Token $Token | ForEach-Object { $_.search.suggestions } } } # Merges two potentially overlapping strings function Merge-Strings([string] $a, [string] $b) { for ($i = $b.Length; $i -gt 0; $i--) { if ($a.EndsWith($b.Substring(0, $i))) { return $a.Substring(0, $a.Length - $i) + $b } } # replace last word return ($a -replace "\b\S*$", "") + $b } Register-ArgumentCompleter -CommandName Search-Sourcegraph -ParameterName Query -ScriptBlock { [CmdletBinding()] param([string]$command, [string]$parameter, [string]$wordToComplete, [CommandAst]$commandAst, [Hashtable]$params) $suggestionParams = @{} if ($params.ContainsKey('Token')) { $suggestionParams.Token = $params.Token } if ($params.ContainsKey('Endpoint')) { $suggestionParams.Endpoint = $params.Endpoint } if ($params.ContainsKey('CaseSensitive')) { $suggestionParams.CaseSensitive = $params.CaseSensitive } if ($params.ContainsKey('RegularExpression')) { $suggestionParams.RegularExpression = $params.RegularExpression } if ($params.ContainsKey('Structural')) { $suggestionParams.Structural = $params.Structural } Get-SourcegraphSearchSuggestions @suggestionParams -Query $wordToComplete.Trim(@("'", '"')) | ForEach-Object { $suggestion = $_ $insertText, $tooltip = switch ($suggestion.__typename) { 'Repository' { 'repo:' + $suggestion.Name "Repository $($suggestion.Name)" } 'File' { 'file:' + $suggestion.Path "File $($suggestion.Name)" } 'Symbol' { $suggestion.Name $kind = $suggestion.Kind[0] + $suggestion.Kind.Substring(1).ToLower() "$kind symbol $($suggestion.Name)`n$($suggestion.Location.Resource.Path)" } } $replaceText = Merge-Strings $wordToComplete.TrimEnd(@('"', "'")) $insertText [CompletionResult]::new($replaceText, $suggestion.Name, [CompletionResultType]::ParameterValue, $tooltip) } } |