ZLocation2.psm1
Set-StrictMode -Version Latest $ErrorActionPreference = 'Stop' . "$PSScriptRoot\LiteDB.ps1" . "$PSScriptRoot\Service.ps1" . "$PSScriptRoot\Search.ps1" . "$PSScriptRoot\Storage.ps1" # I currently consider number of commands executed in directory to be a better metric, than total time spent in a directory. # See [corresponding issue](https://github.com/vors/ZLocation/issues/6) for details. # If you prefer the old behavior, uncomment this code. <# # # Weight function. # function Update-ZLocation([string]$path) { $now = [datetime]::Now if (Test-Path variable:global:__zlocation_current) { $prev = $global:__zlocation_current $weight = $now.Subtract($prev.Time).TotalSeconds Add-ZWeight ($prev.Location) $weight } $global:__zlocation_current = @{ Location = $path Time = [datetime]::Now } # populate folder immediately after the first cd Add-ZWeight $path 0 } # this approach hurts `cd` performance (0.0008 sec vs 0.025 sec). # Consider replacing it with OnIdle Event. (Get-Variable pwd).attributes.Add((new-object ValidateScript { Update-ZLocation $_.Path; return $true })) #> function Update-ZLocation { param ( [Parameter(Mandatory=$true)] [string]$Path ) Add-ZWeight $path 1.0 } function Register-PromptHook { param() # Insert a call to Update-Zlocation in the prompt function but only once. if (-not (Test-Path function:\global:ZlocationOrigPrompt)) { Copy-Item function:\prompt function:\global:ZlocationOrigPrompt $global:ZLocationPromptScriptBlock = { Update-ZLocation $pwd ZLocationOrigPrompt } Set-Content -Path function:\prompt -Value $global:ZLocationPromptScriptBlock -Force } } # # End of weight function. # # # Tab completion. # if(Get-Command -Name Register-ArgumentCompleter -ErrorAction Ignore) { 'Set-ZLocation', 'Invoke-ZLocation' | % { Register-ArgumentCompleter -CommandName $_ -ParameterName match -ScriptBlock { param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter) # Omit first item (command name) and empty strings $i = $commandAst.CommandElements.Count [string[]]$query = if($i -gt 1) { $commandAst.CommandElements[1..($i-1)] | ForEach-Object { $_.toString()} } Find-Matches (Get-ZLocation) $query | Get-EscapedPath } } } function Get-EscapedPath { param( [Parameter( Position=0, Mandatory=$true, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true) ] [string]$path ) process { if ($path.Contains(' ')) { return '"' + $path + '"' } return $path } } # # End of tab completion. # # Default location stack is local for module. Users cannot use 'Pop-Location' directly, so we need to provide a command inside the module for that. function Pop-ZLocation { Pop-Location } function Set-ZLocation([Parameter(ValueFromRemainingArguments)][string[]]$match) { Register-PromptHook if (-not $match) { $match= @() } # Special case to enable Pop-Location. if (($match.Count -eq 1) -and ($match[0] -eq '-')) { Pop-ZLocation return } $matchingPaths = Find-Matches (Get-ZLocation) $match $pushDone = $false foreach ($m in $matchingPaths) { if (Test-path $m) { Push-Location $m $pushDone = $true break } else { Write-Warning "There is no path $m on the file system. Removing obsolete data from database." Remove-ZLocation $m } } if (-not $pushDone) { if (($match.Count -eq 1) -and (Test-Path "$match")) { Write-Debug "No matches for $match, attempting Push-Location" Push-Location "$match" } else { Write-Warning "Cannot find matching location" } } } <# .SYNOPSIS Jump to popular directories. .DESCRIPTION This is the main entry point in the interactive usage of ZLocation. It's intended to be used as an alias z. Usage: z - prints available directories z -l foo - prints available directories scoped to foo query z foo - jumps into the location that matches foo z foo <TAB> - pressing tab cycles through different matches for foo z - - undos the last z jump. .EXAMPLE PS>z Weight Path ------ ---- 7 C:\Windows 1 C:\Windows\System 27 C:\WINDOWS\system32 [...] .EXAMPLE C:\>z foo C:\foo> .EXAMPLE C:\>z foo <PRESS TAB; NOT ENTER> C:\>z C:\foo <PRESS TAB AGAIN> C:\>z C:\another_less_popular\foo <PRESS TAB AGAIN> C:\>z C:\least_popular\foo C:\least_popular\foo> .EXAMPLE PS>z -l sys Weight Path ------ ---- 27 C:\WINDOWS\system32 1 C:\Windows\System .EXAMPLE C:\>z foo C:\foo>z bar C:\baz\bar> z - C:\foo>z - C:\>z - C:\>#no-op .LINK https://github.com/vors/ZLocation #> function Invoke-ZLocation { param( [Parameter(ValueFromRemainingArguments)][string[]]$match ) $sortProperty = "Path" $sortDescending = $false $locations = $null if ($null -eq $match) { $locations = Get-ZLocation } elseif (($match.Length -gt 0) -and ($match[0] -eq '-l')) { $locations = Get-ZLocation ($match | Select-Object -Skip 1) $sortProperty = "Weight" $sortDescending = $true } if ($locations) { $locations | ForEach-Object {$_.GetEnumerator()} | ForEach-Object {[PSCustomObject]@{Weight = $_.Value; Path = $_.Name}} | Sort-Object -Property $sortProperty -Descending:$sortDescending return } Set-ZLocation -match $match } function Get-FrequentFolders { If (((Get-Variable IsWindows -ErrorAction Ignore | Select-Object -ExpandProperty Value) -or $PSVersionTable.PSVersion.Major -lt 6) -and ([environment]::OSVersion.Version.Major -ge 10)) { if (-not $ExecutionContext.SessionState.LanguageMode -eq 'ConstrainedLanguage') { $QuickAccess = New-Object -ComObject shell.application $QuickAccess.Namespace("shell:::{679f85cb-0220-4080-b29b-5540cc05aab6}").Items() | Where-Object IsFolder | Select-Object -ExpandProperty Path } } } function Clear-NonExistentZLocation { $paths=(Get-ZLocation).Keys foreach ($path in $paths) { if (!(Test-Path $path)) { Remove-ZLocation $path Write-Host $path "removed from ZLocation" } } } # Get-FrequentFolders | ForEach-Object { # if (Test-Path $_) { # Add-ZWeight -Path $_ -Weight 0 # } # } # On removal/unload of the module, restore original prompt or LocationChangedAction event handler. $ExecutionContext.SessionState.Module.OnRemove = { Write-Host "Removing prompt." Copy-Item function:\global:ZlocationOrigPrompt function:\global:prompt Remove-Item function:\ZlocationOrigPrompt Remove-Variable ZLocationPromptScriptBlock -Scope Global } Register-PromptHook Set-Alias -Name z -Value Invoke-ZLocation Export-ModuleMember -Function @('Invoke-ZLocation', 'Set-ZLocation', 'Get-ZLocation', 'Pop-ZLocation', 'Remove-ZLocation', 'Clear-NonexistentZLocation') -Alias z # export this function to make it accessible from prompt Export-ModuleMember -Function Update-ZLocation |