├── .editorconfig ├── .gitattributes ├── .gitignore ├── .vscode ├── extensions.json └── settings.json ├── LICENSE ├── PSScriptAnalyzerSettings.psd1 ├── README.md ├── bin ├── install.ps1 ├── pshazz.ps1 └── refresh.ps1 ├── lib ├── commands.ps1 ├── completion.ps1 ├── config.ps1 ├── core.ps1 ├── edit.ps1 ├── help.ps1 ├── plugin.ps1 ├── prompt.ps1 └── theme.ps1 ├── libexec ├── g-complete.ps1 ├── git-complete.ps1 ├── hg-complete.ps1 ├── pshazz-config.ps1 ├── pshazz-edit.ps1 ├── pshazz-get.ps1 ├── pshazz-help.ps1 ├── pshazz-init.ps1 ├── pshazz-list.ps1 ├── pshazz-new.ps1 ├── pshazz-rm.ps1 ├── pshazz-use.ps1 ├── pshazz-which.ps1 └── ssh-complete.ps1 ├── plugins ├── aliases.ps1 ├── dircolors.ps1 ├── g.ps1 ├── g.psd1 ├── g.psm1 ├── git.ps1 ├── hg.ps1 ├── k8s.ps1 ├── resetcolor.ps1 ├── ssh.ps1 ├── svn.ps1 ├── virtualenv.ps1 ├── z.ps1 ├── z.psd1 └── z.psm1 └── themes ├── aag.json ├── agnoster-alternate.json ├── agnoster.json ├── awan.json ├── babun.json ├── bash.json ├── borin.json ├── default.json ├── itscaro.json ├── keepprompt.json ├── kiedix.json ├── kiedtl.json ├── kubernetes.json ├── lambda-simple.json ├── lambda.json ├── linux.json ├── lukes.json ├── microsoft.json ├── msys.json ├── myty.json ├── steeef.json ├── tonic.json ├── unix-lambda.json ├── unix.json ├── xpander.json ├── xpando.json ├── ys.json └── zor.json /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = crlf 6 | indent_size = 4 7 | indent_style = space 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Set attributes so Windows EOL gets maintained 2 | *.json text eol=crlf 3 | *.ps1 text eol=crlf 4 | *.md text eol=crlf 5 | LICENSE text eol=crlf 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.sublime-workspace 2 | *~ 3 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "EditorConfig.EditorConfig", 4 | "ms-vscode.PowerShell" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "powershell.scriptAnalysis.settingsPath": "PSScriptAnalyzerSettings.psd1", 3 | "powershell.codeFormatting.alignPropertyValuePairs": true, 4 | "powershell.codeFormatting.ignoreOneLineBlock": true, 5 | "editor.rulers": [ 6 | 80, 7 | 120 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /PSScriptAnalyzerSettings.psd1: -------------------------------------------------------------------------------- 1 | @{ 2 | # Only diagnostic records of the specified severity will be generated. 3 | # Uncomment the following line if you only want Errors and Warnings but 4 | # not Information diagnostic records. 5 | Severity = @('Error','Warning') 6 | 7 | # Analyze **only** the following rules. Use IncludeRules when you want 8 | # to invoke only a small subset of the defualt rules. 9 | # IncludeRules = @('PSAvoidDefaultValueSwitchParameter', 10 | # 'PSMisleadingBacktick', 11 | # 'PSMissingModuleManifestField', 12 | # 'PSReservedCmdletChar', 13 | # 'PSReservedParams', 14 | # 'PSShouldProcess', 15 | # 'PSUseApprovedVerbs', 16 | # 'PSAvoidUsingCmdletAliases', 17 | # 'PSUseDeclaredVarsMoreThanAssignments') 18 | 19 | # Do not analyze the following rules. Use ExcludeRules when you have 20 | # commented out the IncludeRules settings above and want to include all 21 | # the default rules except for those you exclude below. 22 | # Note: if a rule is in both IncludeRules and ExcludeRules, the rule 23 | # will be excluded. 24 | ExcludeRules = @( 25 | # Pshazz uses global vars. 26 | 'PSAvoidGlobalVars', 27 | # Pshazz uses Write-Host to output colored text. 28 | 'PSAvoidUsingWriteHost', 29 | # PSUseDeclaredVarsMoreThanAssignments doesn't currently work due to: 30 | # https://github.com/PowerShell/PSScriptAnalyzer/issues/636 31 | 'PSUseDeclaredVarsMoreThanAssignments', 32 | # Do not check functions whose verbs change system state 33 | 'PSUseShouldProcessForStateChangingFunctions' 34 | ) 35 | } 36 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Pshazz 2 | ##### Give your powershell some pizazz. 3 | 4 | Pshazz extends your powershell profile to add things like 5 | 6 | * A nicer prompt, including Git and Mercurial info 7 | * Git and Mercurial tab completion 8 | * An SSH helper that lets you never enter your private key password again 9 | * Sensible aliases, and an easy way to add your own aliases and remove ones you don't like 10 | 11 | Pshazz is designed to work with themes, so you can create your own [theme](https://github.com/lukesampson/pshazz/wiki/Themes) if the defaults aren't quite what you want. Make sure to send a pull request to include your theme with pshazz if you come up with something cool! 12 | 13 | ### Installation 14 | Using [Scoop](http://scoop.sh): 15 | 16 | ``` 17 | scoop install pshazz 18 | ``` 19 | 20 | If you don't have Scoop installed, you can download a zip of this repository, and add `bin\pshazz.ps1` to your PATH. 21 | 22 | ### On the shoulders of giants... 23 | Pshazz borrows heavily from: 24 | 25 | * [Posh-Git](https://github.com/dahlbyk/posh-git) by [Keith Dahlby](http://lostechies.com/keithdahlby/) for Git completions 26 | * [Posh-Hg](https://github.com/JeremySkinner/posh-hg) by [Jeremy Skinner](http://www.jeremyskinner.co.uk/) for Mercurial completions 27 | * [git-credential-winstore](http://gitcredentialstore.codeplex.com/) by [Andrew Nurse](http://vibrantcode.com/) and others, for saving SSH passwords. 28 | * [z.ps](https://github.com/JannesMeyer/z.ps) by [Jannes Meyer](https://github.com/JannesMeyer) for rapid system navigation 29 | 30 | Inspired by [Oh-My-Zsh](https://github.com/robbyrussell/oh-my-zsh). 31 | 32 | ### License 33 | 34 | Public Domain 35 | -------------------------------------------------------------------------------- /bin/install.ps1: -------------------------------------------------------------------------------- 1 | if (!(Test-Path $PROFILE)) { 2 | $profileDir = Split-Path $PROFILE 3 | 4 | if (!(Test-Path $profileDir)) { 5 | New-Item -Path $profileDir -ItemType Directory | Out-Null 6 | } 7 | 8 | '' > $PROFILE 9 | } 10 | 11 | $old_init = "try { `$null = gcm pshazz -ea stop; pshazz init 'default' } catch { }" 12 | $new_init = "try { `$null = gcm pshazz -ea stop; pshazz init } catch { }" 13 | 14 | $text = Get-Content $PROFILE 15 | 16 | if ($null -eq ($text | Select-String 'pshazz')) { 17 | Write-Output 'Adding pshazz to your powershell profile.' 18 | 19 | # read and write whole profile to avoid problems with line endings and encodings 20 | $new_profile = @($text) + "try { `$null = gcm pshazz -ea stop; pshazz init 'default' } catch { }" 21 | $new_profile > $PROFILE 22 | } elseif ($text -contains $old_init) { 23 | Write-Output 'Updating pshazz init in your powershell profile.' 24 | $new_profile = $text -replace [Regex]::Escape($old_init), $new_init 25 | $new_profile > $PROFILE 26 | } else { 27 | Write-Output 'It looks like pshazz is already in your powershell profile, skipping.' 28 | } 29 | 30 | "" 31 | " _ _ " 32 | " _ __ ___| |__ __ _ _______| |" 33 | "| '_ \/ __| '_ \ / _`` |_ /_ / |" 34 | "| |_) \__ \ | | | (_| |/ / / /|_|" 35 | "| .__/|___/_| |_|\__,_/___/___(_)" 36 | "|_|" 37 | "" 38 | 39 | & "$PSScriptRoot\pshazz" init 40 | 41 | Write-Host "Your PowerShell is now powered by pshazz!" -f DarkGreen 42 | -------------------------------------------------------------------------------- /bin/pshazz.ps1: -------------------------------------------------------------------------------- 1 | #Requires -Version 3 2 | param($cmd) 3 | 4 | Set-StrictMode -Off 5 | 6 | . "$PSScriptRoot\..\lib\core.ps1" 7 | . "$PSScriptRoot\..\lib\config.ps1" 8 | . "$PSScriptRoot\..\lib\commands.ps1" 9 | . "$PSScriptRoot\..\lib\edit.ps1" 10 | . "$PSScriptRoot\..\lib\help.ps1" 11 | . "$PSScriptRoot\..\lib\plugin.ps1" 12 | . "$PSScriptRoot\..\lib\theme.ps1" 13 | 14 | $commands = commands 15 | 16 | if (@($null, '-h', '--help', '/?') -contains $cmd) { 17 | exec 'help' $args 18 | } elseif ($commands -contains $cmd) { 19 | exec $cmd $args 20 | } else { 21 | Write-Output "pshazz: '$cmd' isn't a pshazz command. See 'pshazz help'" 22 | exit 1 23 | } 24 | -------------------------------------------------------------------------------- /bin/refresh.ps1: -------------------------------------------------------------------------------- 1 | # The refresh script is for development only 2 | $src = Resolve-Path "$PSScriptRoot\.." 3 | $dest = Resolve-Path "$(Split-Path (scoop which pshazz))\.." 4 | 5 | # make sure not running from the installed directory 6 | if ("$src" -eq "$dest") { 7 | Write-Output "The refresh script is for development only." 8 | return 9 | } 10 | 11 | Write-Host -NoNewline 'Copying files.' 12 | robocopy $src $dest /mir /njh /njs /nfl /ndl /xd .git /xf .DS_Store manifest.json install.json > $null 13 | Write-Host ' Done.' -f DarkGreen 14 | 15 | Write-Host 'Reloading pshazz.' 16 | pshazz init 17 | -------------------------------------------------------------------------------- /lib/commands.ps1: -------------------------------------------------------------------------------- 1 | function command_files { 2 | Get-ChildItem "$PSScriptRoot\..\libexec" | Where-Object { 3 | $_.Name -match 'pshazz-.*?\.ps1$' 4 | } 5 | } 6 | 7 | function commands { 8 | command_files | ForEach-Object { command_name $_ } 9 | } 10 | 11 | function command_name($filename) { 12 | $filename.Name | Select-String 'pshazz-(.*?)\.ps1$' | ForEach-Object { 13 | $_.matches[0].groups[1].Value 14 | } 15 | } 16 | 17 | function exec($cmd, $arguments) { 18 | & "$PSScriptRoot\..\libexec\pshazz-$cmd.ps1" @arguments 19 | } 20 | -------------------------------------------------------------------------------- /lib/completion.ps1: -------------------------------------------------------------------------------- 1 | # pshazz tab completion 2 | 3 | # Backup previous TabExpansion function 4 | if ((Test-Path Function:\TabExpansion) -and !$global:PshazzTabExpansionPatched) { 5 | Rename-Item Function:\TabExpansion global:PshazzTabExpansionBackup 6 | } 7 | 8 | # Override TabExpansion function 9 | # FIXME: DO NOT override global TabExpansion function 10 | function global:TabExpansion($line, $lastWord) { 11 | $expression = [regex]::Split($line, '[|;]')[-1].TrimStart() 12 | 13 | foreach($cmd in $global:pshazz.completions.keys) { 14 | if ($expression -match "^$cmd\s+(?.*)") { 15 | return & $global:pshazz.completions[$cmd] $matches['fragment'] 16 | } 17 | } 18 | 19 | # Fall back on existing tab expansion 20 | if (Test-Path Function:\PshazzTabExpansionBackup) { 21 | PshazzTabExpansionBackup $line $lastWord 22 | } 23 | } 24 | 25 | # Rememeber that we've patched TabExpansion, to avoid doing it a second time. 26 | $global:PshazzTabExpansionPatched = $true 27 | -------------------------------------------------------------------------------- /lib/config.ps1: -------------------------------------------------------------------------------- 1 | function load_cfg { 2 | if (!(Test-Path $configFile)) { 3 | return $null 4 | } 5 | 6 | try { 7 | return (Get-Content $configFile -Raw | ConvertFrom-Json -ErrorAction Stop) 8 | } catch { 9 | Write-Host "ERROR loading $cfgpath`: $($_.Exception.Message)" 10 | } 11 | } 12 | 13 | function get_config($name) { 14 | return $cfg.$name 15 | } 16 | 17 | function set_config($name, $val) { 18 | # Ensure configFile exists 19 | if (!(Test-Path $configFile)) { 20 | New-Item $configFile -Force -ErrorAction Ignore | Out-Null 21 | } 22 | 23 | if (!$cfg) { 24 | $cfg = @{ $name = $val } 25 | } else { 26 | if ($null -eq $cfg.$name) { 27 | $cfg | Add-Member -MemberType NoteProperty -Name $name -Value $val 28 | } else { 29 | $cfg.$name = $val 30 | } 31 | } 32 | 33 | ConvertTo-Json $cfg | Set-Content $configFile -Encoding ASCII 34 | } 35 | 36 | $cfg = load_cfg 37 | -------------------------------------------------------------------------------- /lib/core.ps1: -------------------------------------------------------------------------------- 1 | function fullpath($path) { 2 | $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($path) 3 | } 4 | 5 | # checks if the current theme's prompt will use a variable 6 | function prompt_uses($varname) { 7 | foreach($item in $global:pshazz.theme.prompt) { 8 | if ($item[2] -match "\`$$varname\b") { 9 | return $true 10 | } 11 | } 12 | return $false 13 | } 14 | 15 | # Pshazz Env 16 | $configHome = $env:XDG_CONFIG_HOME, "$env:USERPROFILE\.config" | Select-Object -First 1 17 | $configFile = "$configHome\pshazz\config.json" 18 | $pluginDir = fullpath "$PSScriptRoot\..\plugins" 19 | $userPluginDir = "$configHome\pshazz\plugins" 20 | $themeDir = fullpath "$PSScriptRoot\..\themes" 21 | $userThemeDir = "$configHome\pshazz\themes" 22 | 23 | # Migration 24 | 25 | if ((Test-Path "$env:USERPROFILE\.pshazz") -and !(Test-Path $configFile)) { 26 | New-Item -ItemType Directory (Split-Path -Path $configFile) -ErrorAction Ignore | Out-Null 27 | Move-Item "$env:USERPROFILE\.pshazz" $configFile 28 | Write-Host "WARNING: pshazz configuration has been migrated from '~/.pshazz' to '$configFile'" -f DarkYellow 29 | } 30 | 31 | if ((Test-Path "$env:USERPROFILE\pshazz\plugins") -and !(Test-Path $userPluginDir)) { 32 | Move-Item "$env:USERPROFILE\pshazz\plugins" "$userPluginDir\..\" -Force 33 | Write-Host "WARNING: pshazz user plugins have been migrated from '~/pshazz/plugins' to '$userPluginDir'" -f DarkYellow 34 | } 35 | 36 | if ((Test-Path "$env:USERPROFILE\pshazz") -and !(Test-Path $userThemeDir)) { 37 | New-Item -Path $userThemeDir -ItemType Directory -ErrorAction Ignore | Out-Null 38 | Move-Item "$env:USERPROFILE\pshazz\*.json" "$userThemeDir" -Force 39 | Write-Host "WARNING: pshazz user themes have been migrated from '~/pshazz/' to '$userThemeDir'" -f DarkYellow 40 | # Leave old files there 41 | # Remove-Item -Recurse -Force "$env:USERPROFILE\pshazz" 42 | } 43 | -------------------------------------------------------------------------------- /lib/edit.ps1: -------------------------------------------------------------------------------- 1 | $known_editors = "gvim", "vim", "nano", "notepad2", "notepad++", "notepad" 2 | 3 | function editor { 4 | $editor = get_config 'editor' 5 | if ($editor) { 6 | return $editor 7 | } 8 | 9 | foreach($editor in $known_editors) { 10 | if (has_editor $editor) { 11 | return $editor 12 | } 13 | } 14 | 15 | return $null 16 | } 17 | 18 | function has_editor($name) { 19 | try { Get-Command $name -ErrorAction Stop; $true } catch { $false } 20 | } 21 | -------------------------------------------------------------------------------- /lib/help.ps1: -------------------------------------------------------------------------------- 1 | function usage($text) { 2 | $text | Select-String '(?m)^# Usage: ([^\n]*)$' | ForEach-Object { 3 | "Usage: " + $_.matches[0].groups[1].value 4 | } 5 | } 6 | 7 | function summary($text) { 8 | $text | Select-String '(?m)^# Summary: ([^\n]*)$' | ForEach-Object { 9 | $_.matches[0].groups[1].value 10 | } 11 | } 12 | 13 | function help($text) { 14 | $help_lines = $text | Select-String '(?ms)^# Help:(.(?!^[^#]))*' | ForEach-Object { 15 | $_.matches[0].value 16 | } 17 | $help_lines -replace '(?ms)^#\s?(Help: )?', '' 18 | } 19 | 20 | function my_usage { 21 | # gets usage for the calling script 22 | usage (Get-Content $myInvocation.PSCommandPath -Raw) 23 | } 24 | -------------------------------------------------------------------------------- /lib/plugin.ps1: -------------------------------------------------------------------------------- 1 | function plugin:init($name) { 2 | # try user plugin dir first 3 | $path = "$userPluginDir\$name.ps1" 4 | if (!(Test-Path $path)) { 5 | # fallback to defaults 6 | $path = "$pluginDir\$name.ps1" 7 | if (!(Test-Path $path)) { 8 | Write-Warning "Couldn't find pshazz plugin '$name'." 9 | return $false 10 | } 11 | } 12 | 13 | . $path 14 | 15 | $initfn = "pshazz:$name`:init" 16 | if (Test-Path "function:\$initfn") { 17 | & $initfn 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/prompt.ps1: -------------------------------------------------------------------------------- 1 | function global:pshazz_time { 2 | return (Get-Date -DisplayHint Time -Format T) 3 | } 4 | 5 | function global:pshazz_dir { 6 | $h = (Get-PsProvider 'FileSystem').Home 7 | if ($PWD -like $h) { 8 | return '~' 9 | } 10 | 11 | $dir = Split-Path $PWD -Leaf 12 | if ($dir -imatch '[a-z]:\\') { 13 | return '\' 14 | } 15 | return $dir 16 | } 17 | 18 | function global:pshazz_two_dir { 19 | $h = (Get-PsProvider 'FileSystem').Home 20 | if ($PWD -like $h) { 21 | return '~' 22 | } 23 | 24 | $dir = Split-Path $PWD -Leaf 25 | $parent_pwd = Split-Path $PWD -Parent 26 | if ($dir -imatch '[a-z]:\\') { 27 | return '\' 28 | } 29 | 30 | if ($parent_pwd) { 31 | if ($parent_pwd -like $h) { 32 | $parent = '~' 33 | } else { 34 | $parent = Split-Path $parent_pwd -Leaf 35 | } 36 | 37 | if ( $parent -imatch '[a-z]:\\') { 38 | $dir = "\$dir" 39 | } else { 40 | $dir = "$parent\$dir" 41 | } 42 | } 43 | 44 | return $dir 45 | } 46 | 47 | function global:pshazz_path { 48 | # Replace $HOME with '~' 49 | return $PWD -replace [Regex]::Escape((Get-PsProvider 'FileSystem').Home), "~" 50 | } 51 | 52 | function global:pshazz_rightarrow { 53 | return ([char]0xe0b0) 54 | } 55 | 56 | # Based on posh-git 57 | function global:pshazz_local_or_parent_path($path) { 58 | $check_in = Get-Item -Force . 59 | if ($check_in.PSProvider.Name -ne 'FileSystem') { 60 | return $null 61 | } 62 | while ($null -ne $check_in) { 63 | $path_to_test = [System.IO.Path]::Combine($check_in.FullName, $path) 64 | if (Test-Path -LiteralPath $path_to_test) { 65 | return $check_in.FullName 66 | } else { 67 | $check_in = $check_in.Parent 68 | } 69 | } 70 | return $null 71 | } 72 | 73 | 74 | function global:pshazz_write_prompt($prompt, $vars) { 75 | $vars.keys | ForEach-Object { set-variable $_ $vars[$_] } 76 | function eval($str) { 77 | $executionContext.invokeCommand.expandString($str) 78 | } 79 | 80 | $fg_default = $Host.UI.RawUI.ForegroundColor 81 | $bg_default = $Host.UI.RawUI.BackgroundColor 82 | 83 | # write each element of the prompt, stripping out portions 84 | # that evaluate to blank strings 85 | $prompt | ForEach-Object { 86 | $str = eval $_[2] 87 | 88 | # check if there is additional conditional parameter for prompt part 89 | if ($_.Count -ge 4) { 90 | $cond = eval $_[3] 91 | $condition = ([String]::IsNullOrWhiteSpace($_[3]) -or $cond) 92 | } else { 93 | $condition = $true 94 | } 95 | 96 | # empty up the prompt part if condition fails 97 | if (!$condition) { 98 | $str = "" 99 | } 100 | 101 | if (![String]::IsNullOrWhiteSpace($str)) { 102 | $fg = eval $_[0]; $bg = eval $_[1] 103 | if (!$fg) { $fg = $fg_default } 104 | if (!$bg) { $bg = $bg_default } 105 | Write-Host $str -NoNewline -ForegroundColor $fg -BackgroundColor $bg 106 | } 107 | } 108 | } 109 | 110 | if (!$global:pshazz.theme.prompt) { return } # no prompt specified, keep existing 111 | 112 | function global:prompt { 113 | $saved_lastoperationstatus = $? # status of win32 AND powershell command (False on interrupts) 114 | $saved_lastexitcode = $lastexitcode 115 | 116 | $global:pshazz.prompt_vars = @{ 117 | time = pshazz_time; 118 | dir = pshazz_dir; 119 | two_dir = pshazz_two_dir; 120 | path = pshazz_path; 121 | user = $env:username; 122 | hostname = $env:computername; 123 | rightarrow = pshazz_rightarrow; 124 | } 125 | 126 | # get plugins to populate prompt vars 127 | $global:pshazz.theme.plugins | ForEach-Object { 128 | $prompt_fn = "pshazz:$_`:prompt" 129 | if (Test-Path "function:\$prompt_fn") { 130 | & $prompt_fn 131 | } 132 | } 133 | 134 | pshazz_write_prompt $global:pshazz.theme.prompt $global:pshazz.prompt_vars 135 | 136 | $global:lastexitcode = $saved_lastexitcode 137 | " " 138 | } 139 | -------------------------------------------------------------------------------- /lib/theme.ps1: -------------------------------------------------------------------------------- 1 | function theme($name) { 2 | $path = find_path $name 3 | 4 | if ([bool]$path) { 5 | $theme = load_theme $path 6 | if ($theme) { 7 | return $theme 8 | } 9 | } 10 | } 11 | 12 | function find_path($name) { 13 | # try user dir first 14 | $path = "$userThemeDir\$name.json" 15 | if (Test-Path $path) { 16 | return $path 17 | } 18 | 19 | # fallback to builtin dir 20 | $path = "$themeDir\$name.json" 21 | if (Test-Path $path) { 22 | return $path 23 | } 24 | } 25 | 26 | function load_theme($path) { 27 | try { 28 | return (Get-Content $path -Raw | ConvertFrom-Json -ErrorAction Stop) 29 | } catch { 30 | Write-Host "ERROR loading JSON for '$path'`:" 31 | Write-Host "$($_.Exception.Message)" -f DarkRed 32 | } 33 | } 34 | 35 | function new_theme($name) { 36 | if (!(Test-Path $userThemeDir)) { 37 | New-Item -Path $userThemeDir -ItemType Directory | Out-Null 38 | } 39 | Copy-Item "$themeDir\default.json" "$userThemeDir\$name.json" 40 | } 41 | -------------------------------------------------------------------------------- /libexec/g-complete.ps1: -------------------------------------------------------------------------------- 1 | # everything after ^g\s* 2 | param($fragment) 3 | 4 | $cfgpath = "$env:USERPROFILE\.gconfig" 5 | 6 | $textContent = Get-Content $cfgpath 7 | $inputKeys = $fragment.Split(' ') 8 | $matchingKey = $inputKeys[$inputKeys.length - 1] 9 | 10 | if ($textContent) { 11 | $fileHash = @{} 12 | 13 | $textContent | ForEach-Object { 14 | $keys = $_.Split("|") 15 | 16 | if($keys[0] -ne $matchingKey) { 17 | $fileHash.Add($keys[0], $keys[1]) 18 | } 19 | } 20 | 21 | if($fileHash.Count -gt 0) { 22 | $fileHash.Keys | ForEach-Object { 23 | if ($_.StartsWith($matchingKey)) 24 | { 25 | #this will output the auto filled key to the screen. 26 | $_ | Sort-Object 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /libexec/git-complete.ps1: -------------------------------------------------------------------------------- 1 | # Based on Keith Dahlby's GitTabExpansion.ps1 from PoshGit 2 | # https://github.com/dahlbyk/posh-git 3 | # 4 | # Initial implementation by Jeremy Skinner 5 | # http://www.jeremyskinner.co.uk/2010/03/07/using-git-with-windows-powershell/ 6 | param($fragment) # everything after ^git\s* 7 | 8 | $subcommands = @{ 9 | bisect = 'start bad good skip reset visualize replay log run' 10 | notes = 'edit show' 11 | reflog = 'expire delete show' 12 | remote = 'add rename rm set-head show prune update' 13 | stash = 'list show drop pop apply branch save clear create' 14 | submodule = 'add status init update summary foreach sync' 15 | svn = 'init fetch clone rebase dcommit branch tag log blame find-rev set-tree create-ignore show-ignore mkdirs commit-diff info proplist propget show-externals gc reset' 16 | tfs = 'bootstrap checkin checkintool ct cleanup cleanup-workspaces clone diagnostics fetch help init pull quick-clone rcheckin shelve shelve-list unshelve verify' 17 | flow = 'init feature release hotfix' 18 | } 19 | 20 | $gitflowsubcommands = @{ 21 | feature = 'list start finish publish track diff rebase checkout pull' 22 | release = 'list start finish publish track' 23 | hotfix = 'list start finish publish track' 24 | } 25 | 26 | function gitCmdOperations($commands, $command, $filter) { 27 | $commands.$command -split ' ' | Where-Object { $_ -like "$filter*" } 28 | } 29 | 30 | function gitAliases($filter) { 31 | git config --get-regexp "^alias.$filter" | ForEach-Object { 32 | [regex]::match($_, 'alias\.([^\s]+)').groups[1].value 33 | } | Sort-Object 34 | } 35 | 36 | function gitCommands($filter, $includeAliases) { 37 | $cmdList = @() 38 | $cmdList += git help --all | 39 | Where-Object { $_ -match '^ \S.*' } | 40 | ForEach-Object { $_.Split(' ', [StringSplitOptions]::RemoveEmptyEntries) } | 41 | Where-Object { $_ -like "$filter*" } 42 | 43 | if ($includeAliases) { 44 | $cmdList += gitAliases $filter 45 | } 46 | $cmdList | Sort-Object 47 | } 48 | 49 | function gitRemotes($filter) { 50 | git remote | Where-Object { $_ -like "$filter*" } 51 | } 52 | 53 | function gitBranches($filter, $includeHEAD = $false) { 54 | $prefix = $null 55 | if ($filter -match "^(?\S*\.{2,3})(?.*)") { 56 | $prefix = $matches['from'] 57 | $filter = $matches['to'] 58 | } 59 | $branches = @(git branch --no-color | ForEach-Object { if($_ -match "^\*?\s*(?.*)") { $matches['ref'] } }) + 60 | @(git branch --no-color -r | ForEach-Object { if($_ -match "^ (?\S+)(?: -> .+)?") { $matches['ref'] } }) + 61 | @(if ($includeHEAD) { 'HEAD','FETCH_HEAD','ORIG_HEAD','MERGE_HEAD' }) 62 | $branches | 63 | Where-Object { $_ -ne '(no branch)' -and $_ -like "$filter*" } | 64 | ForEach-Object { $prefix + $_ } 65 | } 66 | 67 | function gitRemoteBranches($remote, $ref, $filter) { 68 | git branch --no-color -r | 69 | Where-Object { $_ -like " $remote/$filter*" } | 70 | ForEach-Object { $ref + ($_ -replace " $remote/","") } 71 | } 72 | 73 | function gitStashes($filter) { 74 | (git stash list) -replace ':.*','' | 75 | Where-Object { $_ -like "$filter*" } | 76 | ForEach-Object { "'$_'" } 77 | } 78 | 79 | function gitTfsShelvesets($filter) { 80 | (git tfs shelve-list) | 81 | Where-Object { $_ -like "$filter*" } | 82 | ForEach-Object { "'$_'" } 83 | } 84 | 85 | function gitFiles($filter, $files) { 86 | $files | Sort-Object | 87 | Where-Object { $_ -like "$filter*" } | 88 | ForEach-Object { if($_ -like '* *') { "'$_'" } else { $_ } } 89 | } 90 | 91 | # returns an array of files changes, where the file change is 92 | # an array of (x,y,path1,path2). see `git help status` for meanings 93 | function gitStatus { 94 | git -c color.status=false status --short 2>$null | Select-String '^(.)(.) (.*?)(?: -> (.*))?$' | ForEach-Object { 95 | $_all, $groups = $_.matches.groups; # discard group 0 (complete match) 96 | ,($groups | ForEach-Object { $_.value }) # ',' forces array of arrays 97 | } 98 | } 99 | 100 | function gitIndexedFiles { 101 | gitStatus | Where-Object { $_[0] -and ($_[0] -ne '?') } | ForEach-Object { $_[2] } 102 | } 103 | 104 | function gitChangedFiles { 105 | gitStatus | Where-Object { $_[1] -ne ' ' } | ForEach-Object { $_[2] } 106 | } 107 | 108 | function gitStagedFiles { 109 | gitStatus | Where-Object { 'M','A','D','R','C' -contains $_[0] } | ForEach-Object { $_[2] } 110 | } 111 | 112 | function gitUnmergedFiles { 113 | gitStatus | Where-Object { $_[1] -eq 'U' } | ForEach-Object { $_[2] } 114 | } 115 | 116 | function gitDeletedFiles { 117 | gitStatus | Where-Object { $_[1] -eq 'D' } | ForEach-Object { $_[2] } 118 | } 119 | 120 | function gitIndex($filter) { 121 | gitFiles $filter @(gitIndexedFiles) 122 | } 123 | 124 | function gitAddFiles($filter) { 125 | gitFiles $filter @(gitChangedFiles) 126 | } 127 | 128 | function gitCheckoutFiles($filter) { 129 | gitFiles $filter @(gitChangedFiles) 130 | } 131 | 132 | function gitDiffFiles($filter, $staged) { 133 | if ($staged) { 134 | gitFiles $filter @(gitStagedFiles) 135 | } else { 136 | gitFiles $filter @(gitChangedFiles) 137 | } 138 | } 139 | 140 | function gitMergeFiles($filter) { 141 | gitFiles $filter @(gitUnmergedFiles) 142 | } 143 | 144 | function gitDeleted($filter) { 145 | gitFiles $filter @(gitDeletedFiles) 146 | } 147 | 148 | function expandGitAlias($cmd, $rest) { 149 | if((git config --get-regexp "^alias\.$cmd`$") -match "^alias\.$cmd (?[^!].*)`$") { 150 | return "$($Matches['cmd'])$rest" 151 | } else { 152 | return "$cmd$rest" 153 | } 154 | } 155 | 156 | if($fragment -match "^(?\S+)(? .*)$") { 157 | $fragment = expandGitAlias $Matches['cmd'] $Matches['args'] 158 | } 159 | 160 | switch -regex ($fragment) { 161 | 162 | # Handles git 163 | "^(?$($subcommands.Keys -join '|'))\s+(?\S*)$" { 164 | gitCmdOperations $subcommands $matches['cmd'] $matches['op'] 165 | } 166 | 167 | # Handles git flow 168 | "^flow (?$($gitflowsubcommands.Keys -join '|'))\s+(?\S*)$" { 169 | gitCmdOperations $gitflowsubcommands $matches['cmd'] $matches['op'] 170 | } 171 | 172 | # Handles git remote (rename|rm|set-head|set-branches|set-url|show|prune) 173 | "^remote.* (?:rename|rm|set-head|set-branches|set-url|show|prune).* (?\S*)$" { 174 | gitRemotes $matches['remote'] 175 | } 176 | 177 | # Handles git stash (show|apply|drop|pop|branch) 178 | "^stash (?:show|apply|drop|pop|branch).* (?\S*)$" { 179 | gitStashes $matches['stash'] 180 | } 181 | 182 | # Handles git bisect (bad|good|reset|skip) 183 | "^bisect (?:bad|good|reset|skip).* (?\S*)$" { 184 | gitBranches $matches['ref'] $true 185 | } 186 | 187 | # Handles git tfs unshelve 188 | "^tfs +unshelve.* (?\S*)$" { 189 | gitTfsShelvesets $matches['shelveset'] 190 | } 191 | 192 | # Handles git branch -d|-D|-m|-M 193 | # Handles git branch 194 | "^branch.* (?\S*)$" { 195 | gitBranches $matches['branch'] 196 | } 197 | 198 | # Handles git (commands & aliases) 199 | "^(?\S*)$" { 200 | gitCommands $matches['cmd'] $true 201 | } 202 | 203 | # Handles git help (commands only) 204 | "^help (?\S*)$" { 205 | gitCommands $matches['cmd'] $false 206 | } 207 | 208 | # Handles git push remote : 209 | "^push.* (?\S+) (?[^\s\:]*\:)(?\S*)$" { 210 | gitRemoteBranches $matches['remote'] $matches['ref'] $matches['branch'] 211 | } 212 | 213 | # Handles git push remote 214 | # Handles git pull remote 215 | "^(?:push|pull).* (?:\S+) (?[^\s\:]*)$" { 216 | gitBranches $matches['branch'] 217 | } 218 | 219 | # Handles git pull 220 | # Handles git push 221 | # Handles git fetch 222 | "^(?:push|pull|fetch).* (?\S*)$" { 223 | gitRemotes $matches['remote'] 224 | } 225 | 226 | # Handles git reset HEAD 227 | # Handles git reset HEAD -- 228 | "^reset.* HEAD(?:\s+--)? (?\S*)$" { 229 | gitIndex $matches['path'] 230 | } 231 | 232 | # Handles git 233 | "^commit.*-C\s+(?\S*)$" { 234 | gitBranches $matches['ref'] $true 235 | } 236 | 237 | # Handles git add 238 | "^add.* (?\S*)$" { 239 | gitAddFiles $matches['files'] 240 | } 241 | 242 | # Handles git checkout -- 243 | "^checkout.* -- (?\S*)$" { 244 | gitCheckoutFiles $matches['files'] 245 | } 246 | 247 | # Handles git rm 248 | "^rm.* (?\S*)$" { 249 | gitDeleted $matches['index'] 250 | } 251 | 252 | # Handles git diff/difftool 253 | "^(?:diff|difftool)(?:.* (?(?:--cached|--staged))|.*) (?\S*)$" { 254 | gitDiffFiles $matches['files'] $matches['staged'] 255 | } 256 | 257 | # Handles git merge/mergetool 258 | "^(?:merge|mergetool).* (?\S*)$" { 259 | gitMergeFiles $matches['files'] 260 | } 261 | 262 | # Handles git 263 | "^(?:checkout|cherry|cherry-pick|diff|difftool|log|merge|rebase|reflog\s+show|reset|revert|show).* (?\S*)$" { 264 | gitBranches $matches['ref'] $true 265 | } 266 | } 267 | -------------------------------------------------------------------------------- /libexec/hg-complete.ps1: -------------------------------------------------------------------------------- 1 | # based on Jeremy Skinner's HgTabExpansion from Posh-Hg 2 | # https://github.com/JeremySkinner/posh-hg 3 | param($fragment) # everything after ^hg\s* 4 | 5 | $script:hgCommands = @() 6 | $script:hgflowStreams = @() 7 | 8 | function hgFiles($filter, $pattern) { 9 | hg status $(hg root) | 10 | ForEach-Object { 11 | if($_ -match "($pattern){1} (.*)") { 12 | $matches[2] 13 | } 14 | } | 15 | Where-Object { $_ -like "*$filter*" } | 16 | ForEach-Object { if($_ -like '* *') { "'$_'" } else { $_ } } 17 | } 18 | 19 | function hgRemotes($filter) { 20 | hg paths | ForEach-Object { 21 | $path = $_.Split("=")[0].Trim(); 22 | if($filter -and $path.StartsWith($filter)) { 23 | $path 24 | } elseif(-not $filter) { 25 | $path 26 | } 27 | } 28 | } 29 | 30 | # By default the hg command list is populated the first time hgCommands is invoked. 31 | # Invoke PopulateHgCommands in your profile if you don't want the initial hit. 32 | function hgCommands($filter) { 33 | if($script:hgCommands.Length -eq 0) { 34 | populateHgCommands 35 | } 36 | 37 | if($filter) { 38 | $hgCommands | Where-Object { $_.StartsWith($filter) } | ForEach-Object { $_.Trim() } | Sort-Object 39 | } 40 | else { 41 | $hgCommands | ForEach-Object { $_.Trim() } | Sort-Object 42 | } 43 | } 44 | 45 | # By default the hg command list is populated the first time hgCommands is invoked. 46 | # Invoke PopulateHgCommands in your profile if you don't want the initial hit. 47 | function PopulateHgCommands() { 48 | $hgCommands = foreach($cmd in (hg help)) { 49 | # Stop once we reach the "Enabled Extensions" section of help. 50 | # Not sure if there's a better way to do this... 51 | if($cmd -eq "enabled extensions:") { 52 | break 53 | } 54 | 55 | if($cmd -match '^ (\S+) (.*)') { 56 | $matches[1] 57 | } 58 | } 59 | 60 | if($global:PoshHgSettings.ShowPatches) { 61 | # MQ integration must be explicitly enabled as the user may not have the extension 62 | $hgCommands += (hg help mq) | ForEach-Object { 63 | if($_ -match '^ (\S+) (.*)') { 64 | $matches[1] 65 | } 66 | } 67 | } 68 | 69 | $script:hgCommands = $hgCommands 70 | } 71 | 72 | function findBranchOrBookmarkOrTags($filter){ 73 | hgLocalBranches($filter) 74 | hgLocalTags($filter) 75 | hgLocalBookmarks($filter) 76 | } 77 | 78 | function hgLocalBranches($filter) { 79 | hg branches -a | ForEach-Object { 80 | if($_ -match "(\S+) .*") { 81 | if($filter -and $matches[1].StartsWith($filter)) { 82 | $matches[1] 83 | } 84 | elseif(-not $filter) { 85 | $matches[1] 86 | } 87 | } 88 | } 89 | } 90 | 91 | function hgLocalTags($filter) { 92 | hg tags | ForEach-Object { 93 | if($_ -match "(\S+) .*") { 94 | if($filter -and $matches[1].StartsWith($filter)) { 95 | $matches[1] 96 | } 97 | elseif(-not $filter) { 98 | $matches[1] 99 | } 100 | } 101 | } 102 | } 103 | 104 | function bookmarkName($bookmark) { 105 | $split = $bookmark.Split(" "); 106 | 107 | if($bookmark.StartsWith("*")) { 108 | $split[1] 109 | } 110 | else{ 111 | $split[0] 112 | } 113 | } 114 | 115 | function hgLocalBookmarks($filter) { 116 | hg bookmarks --quiet | ForEach-Object { 117 | if($_ -match "(\S+) .*") { 118 | $bookmark = bookmarkName($matches[0]) 119 | if($filter -and $bookmark.StartsWith($filter)) { 120 | $bookmark 121 | } 122 | elseif(-not $filter) { 123 | $bookmark 124 | } 125 | } 126 | } 127 | } 128 | 129 | function hgRemoteBookmarks($filter) { 130 | hg incoming -B | ForEach-Object { 131 | if($_ -match "(\S+) .*") { 132 | if($filter -and $matches[1].StartsWith($filter)) { 133 | $matches[1] 134 | } 135 | elseif(-not $filter) { 136 | $matches[1] 137 | } 138 | } 139 | } 140 | } 141 | 142 | function hgOptions($cmd, $filter) { 143 | $optList = @() 144 | $output = hg help $cmd 145 | foreach($line in $output) { 146 | if($line -match '^ ((-\S)| ) --(\S+) .*$') { 147 | $opt = $matches[3] 148 | if($filter -and $opt.StartsWith($filter)) { 149 | $optList += '--' + $opt.Trim() 150 | } 151 | elseif(-not $filter) { 152 | $optList += '--' + $opt.Trim() 153 | } 154 | } 155 | } 156 | 157 | $optList | Sort-Object 158 | } 159 | 160 | function thgCommands($filter) { 161 | $cmdList = @() 162 | $output = thg help 163 | foreach($line in $output) { 164 | if($line -match '^ (\S+) (.*)') { 165 | $cmd = $matches[1] 166 | if($filter -and $cmd.StartsWith($filter)) { 167 | $cmdList += $cmd.Trim() 168 | } 169 | elseif(-not $filter) { 170 | $cmdList += $cmd.Trim() 171 | } 172 | } 173 | } 174 | 175 | $cmdList | Sort-Object 176 | } 177 | 178 | function hgflowStreams($filter) { 179 | if($script:hgflowStreams.Length -eq 0) { 180 | $hgflow = ((hg root) + "\.flow") 181 | if (Test-Path $hgflow) { 182 | populatehgflowStreams($hgflow) 183 | } else { 184 | $hgflow = ((hg root) + "\.hgflow") 185 | if (Test-Path $hgflow) { 186 | populatehgflowStreams($hgflow) 187 | } 188 | } 189 | 190 | $script:hgflowStreams = $script:hgflowStreams 191 | } 192 | 193 | if($filter) { 194 | $hgflowStreams | Where-Object { $_.StartsWith($filter) } | ForEach-Object { $_.Trim() } | Sort-Object 195 | } 196 | else { 197 | $hgflowStreams | ForEach-Object { $_.Trim() } | Sort-Object 198 | } 199 | } 200 | 201 | function populatehgflowStreams($filename) { 202 | $ini = @{} 203 | 204 | switch -regex -file $filename 205 | { 206 | "^\[(.+)\]" # Section 207 | { 208 | $section = $matches[1] 209 | $ini[$section] = @() 210 | } 211 | "(.+?)\s*=(.*)" # Key 212 | { 213 | $name,$value = $matches[1..2] 214 | $ini[$section] += $name 215 | } 216 | } 217 | 218 | # Supporting by 0.4 and 0.9 files 219 | $script:hgflowStreams = if ($ini["Basic"]) { $ini["Basic"] } else { $ini["branchname"] } 220 | } 221 | 222 | switch -regex ($fragment) { 223 | 224 | #handles hg update 225 | #handles hg merge 226 | '^(up|update|merge|co|checkout) (\S*)$' { 227 | findBranchOrBookmarkOrTags($matches[2]) 228 | } 229 | 230 | #Handles hg pull -B 231 | '^pull (-\S* )*-(B) (\S*)$' { 232 | hgRemoteBookmarks($matches[3]) 233 | hgLocalBookmarks($matches[3]) 234 | } 235 | 236 | #Handles hg push -B 237 | '^push (-\S* )*-(B) (\S*)$' { 238 | hgLocalBookmarks($matches[3]) 239 | } 240 | 241 | #Handles hg bookmark 242 | '^(book|bookmark) (\S*)$' { 243 | hgLocalBookmarks($matches[2]) 244 | } 245 | 246 | #Handles hg push 247 | #Handles hg pull 248 | #Handles hg outgoing 249 | #Handles hg incoming 250 | '^(push|pull|outgoing|incoming) (-\S* )*(\S*)$' { 251 | hgRemotes($matches[3]) 252 | } 253 | 254 | #handles hg help 255 | #handles hg 256 | '^(help )?(\S*)$' { 257 | hgCommands($matches[2]); 258 | } 259 | 260 | #handles hg --