Boost Your Productivity - Setting Up and Customizing Windows Terminal with Git Bash and Oh My Posh
Windows Terminal is a versatile and powerful tool that allows you to work with multiple command-line environments in one place, including PowerShell, Command Prompt, WSL (Windows Subsystem for Linux), and now Git Bash. By customizing Windows Terminal with “Oh My Posh”, you can enhance your productivity with better themes, prompt customization, intellisence and more. This guide will walk you through configuring Windows Terminal, setting up Oh My Posh, integrating Git Bash, and switching between different terminal profiles.
Step 1: Install Windows Terminal
Before we dive into customizations, make sure Windows Terminal is installed on your system.
- Install Windows Terminal via the Microsoft Store:
- Open the Microsoft Store on your Windows PC.
- Search for Windows Terminal and click Install.
Or, install it using the following PowerShell command:
winget install --id Microsoft.WindowsTerminal -e --source winget
Step 2: Install and Set Up Git Bash
Git Bash is a popular terminal for developers who want a Unix-like experience on Windows. It comes bundled with Git for Windows. To integrate Git Bash into Windows Terminal:
- Download Git for Windows:
- Go to Git for Windows and download the installer.
- Follow the installation instructions. Ensure that the option “Git Bash Here” is selected, so you can access Git Bash easily from your terminal.
- You can also install Git for Windows using the following command:
winget install Git.Git -e --source winget
-
Add Git Bash to Windows Terminal: Once installed, you’ll need to add Git Bash as a profile in Windows Terminal.
- Open Windows Terminal and go to Settings (press Ctrl + , or click on the drop-down menu and select Settings).
-
In the Settings window, click on Profiles and then click on the Open JSON file button.
-
In the profiles.json file, scroll down to the
"profiles"
section and add Git Bash as a new profile like this:{ "guid": "{some-guid-here}", "hidden": false, "name": "Git Bash", "commandline": "\"C:\\Program Files\\Git\\bin\\bash.exe\" --login -i", "icon": "ms-appx:///ProfileIcons/git-bash.png", "startingDirectory": "%USERPROFILE%" }
- Replace
{some-guid-here}
with a new GUID. You can generate a new GUID using a tool like GUID generator. - Save the file and close the editor.
or,
- You can also add it by clicking on the “+” button in the profiles section called “Add a new profile”
- Click on New empty profile
- Name the profile “Git Bash”
- Add the commandline
"C:\\Program Files\\Git\\bin\\bash.exe" --login -i
or browse to the location of thebash.exe
file. - Icon: You can find the Git Bash icon in the Git installation directory. For example,
C:\Program Files\Git\mingw64\share\git\git-for-windows.ico
. - Starting Directory:%USERPROFILE%
- Save the profile.
- Switch to Git Bash:
- Once you’ve saved the profile, Git Bash will now be available in the Windows Terminal dropdown menu.
- Simply select Git Bash from the profiles list, and you can start using it in the same terminal window.
Step 3: Install PowerShell Core (Optional)
While Git Bash is now available, you might want to also use PowerShell Core (the cross-platform version of PowerShell). To install PowerShell Core:
- Download PowerShell Core from the official GitHub repository.
- Follow the installation instructions for your system.
- Add PowerShell Core to Windows Terminal:
- In Windows Terminal Settings, add a new profile for PowerShell Core by specifying the path to
pwsh.exe
. - Save the settings.
- In Windows Terminal Settings, add a new profile for PowerShell Core by specifying the path to
Step 4: Install a Font with Powerline Symbols To display special symbols properly, install a Powerline font like CaskaydiaCove or FiraCode from the Nerd Fonts website.
- Download the font and install it on your system.
- In my case, I have used CaskaydiaCove Nerd Font.
- Or, copy the font to the font directory:
C:\Windows\Fonts
. - Set the installed font in Windows Terminal under Settings > Profiles > PowerShell > Appearance > Font face.
Step 5: Install and Set Up Oh My Posh for PowerShell
Oh My Posh is a prompt theme engine for PowerShell that can beautify your terminal with themes, segments, and additional functionality. To install it:
- Install Oh My Posh:
- Go to https://ohmyposh.dev/docs/installation/windows and follow the instructions to set up Oh My Posh. or,
- Open a PowerShell prompt and run the following command:
winget install JanDeDobbeleer.OhMyPosh -s winget
- This installs a couple of things:
- oh-my-posh.exe - Windows executable
- themes - The latest Oh My Posh themes
- You can find the themes in the directory
C:\Users\{your-username}\AppData\Local\Programs\oh-my-posh\themes
.
- Run following command to update oh-my-posh
winget upgrade JanDeDobbeleer.OhMyPosh -s winget
- Run the following command to check oh-my-posh installed or not
oh-my-posh --version or oh-my-posh.exe
- Configure Powershell Profile:
- Open PowerShell and run the following command to edit your profile. If you don’t have a profile, a window will prompt you to create one. or you can create a profile in the directory
C:\Users\{your-username}\Documents\PowerShell\Microsoft.PowerShell_profile.ps1
.
notepad $PROFILE
-
Add the following lines to the profile script to enable Oh My Posh:
oh-my-posh init pwsh | Invoke-Expression
-
For advanced feature, I have customized my profile script. You can copy and paste it into your profile script. Here is my profile script. https://gist.github.com/mahedee/f7468bd2c59fa463235e877a8d719f98
Microsoft.PowerShell_profile.ps1
using namespace System.Management.Automation using namespace System.Management.Automation.Language if ($host.Name -eq 'ConsoleHost') { Import-Module PSReadLine } Import-Module -Name Terminal-Icons # oh-my-posh init pwsh --config C:\Users\mahedee\AppData\Local\Programs\oh-my-posh\themes\jandedobbeleer.omp.json | Invoke-Expression oh-my-posh init pwsh --config C:\Users\mahedee\AppData\Local\Programs\oh-my-posh\themes\myposh-mahedee.omp.json | Invoke-Expression # oh-my-posh init pwsh --config C:\Users\mahedee\AppData\Local\Programs\oh-my-posh\themes\myposh-hanselman.omp.json | Invoke-Expression # oh-my-posh init pwsh --config C:\Users\mahedee\AppData\Local\Programs\oh-my-posh\themes\cobalt2.omp.json | Invoke-Expression Register-ArgumentCompleter -Native -CommandName winget -ScriptBlock { param($wordToComplete, $commandAst, $cursorPosition) [Console]::InputEncoding = [Console]::OutputEncoding = $OutputEncoding = [System.Text.Utf8Encoding]::new() $Local:word = $wordToComplete.Replace('"', '""') $Local:ast = $commandAst.ToString().Replace('"', '""') winget complete --word="$Local:word" --commandline "$Local:ast" --position $cursorPosition | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) } } # PowerShell parameter completion shim for the dotnet CLI Register-ArgumentCompleter -Native -CommandName dotnet -ScriptBlock { param($commandName, $wordToComplete, $cursorPosition) dotnet complete --position $cursorPosition "$wordToComplete" | ForEach-Object { [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) } } # --- # This is an example profile for PSReadLine. # # This is roughly what I use so there is some emphasis on emacs bindings, # but most of these bindings make sense in Windows mode as well. # Searching for commands with up/down arrow is really handy. The # option "moves to end" is useful if you want the cursor at the end # of the line while cycling through history like it does w/o searching, # without that option, the cursor will remain at the position it was # when you used up arrow, which can be useful if you forget the exact # string you started the search on. Set-PSReadLineKeyHandler -Key UpArrow -Function HistorySearchBackward Set-PSReadLineKeyHandler -Key DownArrow -Function HistorySearchForward # This key handler shows the entire or filtered history using Out-GridView. The # typed text is used as the substring pattern for filtering. A selected command # is inserted to the command line without invoking. Multiple command selection # is supported, e.g. selected by Ctrl + Click. Set-PSReadLineKeyHandler -Key F7 ` -BriefDescription History ` -LongDescription 'Show command history' ` -ScriptBlock { $pattern = $null [Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$pattern, [ref]$null) if ($pattern) { $pattern = [regex]::Escape($pattern) } $history = [System.Collections.ArrayList]@( $last = '' $lines = '' foreach ($line in [System.IO.File]::ReadLines((Get-PSReadLineOption).HistorySavePath)) { if ($line.EndsWith('`')) { $line = $line.Substring(0, $line.Length - 1) $lines = if ($lines) { "$lines`n$line" } else { $line } continue } if ($lines) { $line = "$lines`n$line" $lines = '' } if (($line -cne $last) -and (!$pattern -or ($line -match $pattern))) { $last = $line $line } } ) $history.Reverse() $command = $history | Out-GridView -Title History -PassThru if ($command) { [Microsoft.PowerShell.PSConsoleReadLine]::RevertLine() [Microsoft.PowerShell.PSConsoleReadLine]::Insert(($command -join "`n")) } } # CaptureScreen is good for blog posts or email showing a transaction # of what you did when asking for help or demonstrating a technique. Set-PSReadLineKeyHandler -Chord 'Ctrl+d,Ctrl+c' -Function CaptureScreen # The built-in word movement uses character delimiters, but token based word # movement is also very useful - these are the bindings you'd use if you # prefer the token based movements bound to the normal emacs word movement # key bindings. Set-PSReadLineKeyHandler -Key Alt+d -Function ShellKillWord Set-PSReadLineKeyHandler -Key Alt+Backspace -Function ShellBackwardKillWord Set-PSReadLineKeyHandler -Key Alt+b -Function ShellBackwardWord Set-PSReadLineKeyHandler -Key Alt+f -Function ShellForwardWord Set-PSReadLineKeyHandler -Key Alt+B -Function SelectShellBackwardWord Set-PSReadLineKeyHandler -Key Alt+F -Function SelectShellForwardWord #region Smart Insert/Delete # The next four key handlers are designed to make entering matched quotes # parens, and braces a nicer experience. I'd like to include functions # in the module that do this, but this implementation still isn't as smart # as ReSharper, so I'm just providing it as a sample. Set-PSReadLineKeyHandler -Key '"',"'" ` -BriefDescription SmartInsertQuote ` -LongDescription "Insert paired quotes if not already on a quote" ` -ScriptBlock { param($key, $arg) $quote = $key.KeyChar $selectionStart = $null $selectionLength = $null [Microsoft.PowerShell.PSConsoleReadLine]::GetSelectionState([ref]$selectionStart, [ref]$selectionLength) $line = $null $cursor = $null [Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$line, [ref]$cursor) # If text is selected, just quote it without any smarts if ($selectionStart -ne -1) { [Microsoft.PowerShell.PSConsoleReadLine]::Replace($selectionStart, $selectionLength, $quote + $line.SubString($selectionStart, $selectionLength) + $quote) [Microsoft.PowerShell.PSConsoleReadLine]::SetCursorPosition($selectionStart + $selectionLength + 2) return } $ast = $null $tokens = $null $parseErrors = $null [Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$ast, [ref]$tokens, [ref]$parseErrors, [ref]$null) function FindToken { param($tokens, $cursor) foreach ($token in $tokens) { if ($cursor -lt $token.Extent.StartOffset) { continue } if ($cursor -lt $token.Extent.EndOffset) { $result = $token $token = $token -as [StringExpandableToken] if ($token) { $nested = FindToken $token.NestedTokens $cursor if ($nested) { $result = $nested } } return $result } } return $null } $token = FindToken $tokens $cursor # If we're on or inside a **quoted** string token (so not generic), we need to be smarter if ($token -is [StringToken] -and $token.Kind -ne [TokenKind]::Generic) { # If we're at the start of the string, assume we're inserting a new string if ($token.Extent.StartOffset -eq $cursor) { [Microsoft.PowerShell.PSConsoleReadLine]::Insert("$quote$quote ") [Microsoft.PowerShell.PSConsoleReadLine]::SetCursorPosition($cursor + 1) return } # If we're at the end of the string, move over the closing quote if present. if ($token.Extent.EndOffset -eq ($cursor + 1) -and $line[$cursor] -eq $quote) { [Microsoft.PowerShell.PSConsoleReadLine]::SetCursorPosition($cursor + 1) return } } if ($null -eq $token -or $token.Kind -eq [TokenKind]::RParen -or $token.Kind -eq [TokenKind]::RCurly -or $token.Kind -eq [TokenKind]::RBracket) { if ($line[0..$cursor].Where{$_ -eq $quote}.Count % 2 -eq 1) { # Odd number of quotes before the cursor, insert a single quote [Microsoft.PowerShell.PSConsoleReadLine]::Insert($quote) } else { # Insert matching quotes, move cursor to be in between the quotes [Microsoft.PowerShell.PSConsoleReadLine]::Insert("$quote$quote") [Microsoft.PowerShell.PSConsoleReadLine]::SetCursorPosition($cursor + 1) } return } # If cursor is at the start of a token, enclose it in quotes. if ($token.Extent.StartOffset -eq $cursor) { if ($token.Kind -eq [TokenKind]::Generic -or $token.Kind -eq [TokenKind]::Identifier -or $token.Kind -eq [TokenKind]::Variable -or $token.TokenFlags.hasFlag([TokenFlags]::Keyword)) { $end = $token.Extent.EndOffset $len = $end - $cursor [Microsoft.PowerShell.PSConsoleReadLine]::Replace($cursor, $len, $quote + $line.SubString($cursor, $len) + $quote) [Microsoft.PowerShell.PSConsoleReadLine]::SetCursorPosition($end + 2) return } } # We failed to be smart, so just insert a single quote [Microsoft.PowerShell.PSConsoleReadLine]::Insert($quote) } Set-PSReadLineKeyHandler -Key '(','{','[' ` -BriefDescription InsertPairedBraces ` -LongDescription "Insert matching braces" ` -ScriptBlock { param($key, $arg) $closeChar = switch ($key.KeyChar) { <#case#> '(' { [char]')'; break } <#case#> '{' { [char]'}'; break } <#case#> '[' { [char]']'; break } } $selectionStart = $null $selectionLength = $null [Microsoft.PowerShell.PSConsoleReadLine]::GetSelectionState([ref]$selectionStart, [ref]$selectionLength) $line = $null $cursor = $null [Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$line, [ref]$cursor) if ($selectionStart -ne -1) { # Text is selected, wrap it in brackets [Microsoft.PowerShell.PSConsoleReadLine]::Replace($selectionStart, $selectionLength, $key.KeyChar + $line.SubString($selectionStart, $selectionLength) + $closeChar) [Microsoft.PowerShell.PSConsoleReadLine]::SetCursorPosition($selectionStart + $selectionLength + 2) } else { # No text is selected, insert a pair [Microsoft.PowerShell.PSConsoleReadLine]::Insert("$($key.KeyChar)$closeChar") [Microsoft.PowerShell.PSConsoleReadLine]::SetCursorPosition($cursor + 1) } } Set-PSReadLineKeyHandler -Key ')',']','}' ` -BriefDescription SmartCloseBraces ` -LongDescription "Insert closing brace or skip" ` -ScriptBlock { param($key, $arg) $line = $null $cursor = $null [Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$line, [ref]$cursor) if ($line[$cursor] -eq $key.KeyChar) { [Microsoft.PowerShell.PSConsoleReadLine]::SetCursorPosition($cursor + 1) } else { [Microsoft.PowerShell.PSConsoleReadLine]::Insert("$($key.KeyChar)") } } Set-PSReadLineKeyHandler -Key Backspace ` -BriefDescription SmartBackspace ` -LongDescription "Delete previous character or matching quotes/parens/braces" ` -ScriptBlock { param($key, $arg) $line = $null $cursor = $null [Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$line, [ref]$cursor) if ($cursor -gt 0) { $toMatch = $null if ($cursor -lt $line.Length) { switch ($line[$cursor]) { <#case#> '"' { $toMatch = '"'; break } <#case#> "'" { $toMatch = "'"; break } <#case#> ')' { $toMatch = '('; break } <#case#> ']' { $toMatch = '['; break } <#case#> '}' { $toMatch = '{'; break } } } if ($toMatch -ne $null -and $line[$cursor-1] -eq $toMatch) { [Microsoft.PowerShell.PSConsoleReadLine]::Delete($cursor - 1, 2) } else { [Microsoft.PowerShell.PSConsoleReadLine]::BackwardDeleteChar($key, $arg) } } } #endregion Smart Insert/Delete # Sometimes you enter a command but realize you forgot to do something else first. # This binding will let you save that command in the history so you can recall it, # but it doesn't actually execute. It also clears the line with RevertLine so the # undo stack is reset - though redo will still reconstruct the command line. Set-PSReadLineKeyHandler -Key Alt+w ` -BriefDescription SaveInHistory ` -LongDescription "Save current line in history but do not execute" ` -ScriptBlock { param($key, $arg) $line = $null $cursor = $null [Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$line, [ref]$cursor) [Microsoft.PowerShell.PSConsoleReadLine]::AddToHistory($line) [Microsoft.PowerShell.PSConsoleReadLine]::RevertLine() } # Insert text from the clipboard as a here string Set-PSReadLineKeyHandler -Key Ctrl+V ` -BriefDescription PasteAsHereString ` -LongDescription "Paste the clipboard text as a here string" ` -ScriptBlock { param($key, $arg) Add-Type -Assembly PresentationCore if ([System.Windows.Clipboard]::ContainsText()) { # Get clipboard text - remove trailing spaces, convert \r\n to \n, and remove the final \n. $text = ([System.Windows.Clipboard]::GetText() -replace "\p{Zs}*`r?`n","`n").TrimEnd() [Microsoft.PowerShell.PSConsoleReadLine]::Insert("@'`n$text`n'@") } else { [Microsoft.PowerShell.PSConsoleReadLine]::Ding() } } # Sometimes you want to get a property of invoke a member on what you've entered so far # but you need parens to do that. This binding will help by putting parens around the current selection, # or if nothing is selected, the whole line. Set-PSReadLineKeyHandler -Key 'Alt+(' ` -BriefDescription ParenthesizeSelection ` -LongDescription "Put parenthesis around the selection or entire line and move the cursor to after the closing parenthesis" ` -ScriptBlock { param($key, $arg) $selectionStart = $null $selectionLength = $null [Microsoft.PowerShell.PSConsoleReadLine]::GetSelectionState([ref]$selectionStart, [ref]$selectionLength) $line = $null $cursor = $null [Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$line, [ref]$cursor) if ($selectionStart -ne -1) { [Microsoft.PowerShell.PSConsoleReadLine]::Replace($selectionStart, $selectionLength, '(' + $line.SubString($selectionStart, $selectionLength) + ')') [Microsoft.PowerShell.PSConsoleReadLine]::SetCursorPosition($selectionStart + $selectionLength + 2) } else { [Microsoft.PowerShell.PSConsoleReadLine]::Replace(0, $line.Length, '(' + $line + ')') [Microsoft.PowerShell.PSConsoleReadLine]::EndOfLine() } } # Each time you press Alt+', this key handler will change the token # under or before the cursor. It will cycle through single quotes, double quotes, or # no quotes each time it is invoked. Set-PSReadLineKeyHandler -Key "Alt+'" ` -BriefDescription ToggleQuoteArgument ` -LongDescription "Toggle quotes on the argument under the cursor" ` -ScriptBlock { param($key, $arg) $ast = $null $tokens = $null $errors = $null $cursor = $null [Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$ast, [ref]$tokens, [ref]$errors, [ref]$cursor) $tokenToChange = $null foreach ($token in $tokens) { $extent = $token.Extent if ($extent.StartOffset -le $cursor -and $extent.EndOffset -ge $cursor) { $tokenToChange = $token # If the cursor is at the end (it's really 1 past the end) of the previous token, # we only want to change the previous token if there is no token under the cursor if ($extent.EndOffset -eq $cursor -and $foreach.MoveNext()) { $nextToken = $foreach.Current if ($nextToken.Extent.StartOffset -eq $cursor) { $tokenToChange = $nextToken } } break } } if ($tokenToChange -ne $null) { $extent = $tokenToChange.Extent $tokenText = $extent.Text if ($tokenText[0] -eq '"' -and $tokenText[-1] -eq '"') { # Switch to no quotes $replacement = $tokenText.Substring(1, $tokenText.Length - 2) } elseif ($tokenText[0] -eq "'" -and $tokenText[-1] -eq "'") { # Switch to double quotes $replacement = '"' + $tokenText.Substring(1, $tokenText.Length - 2) + '"' } else { # Add single quotes $replacement = "'" + $tokenText + "'" } [Microsoft.PowerShell.PSConsoleReadLine]::Replace( $extent.StartOffset, $tokenText.Length, $replacement) } } # This example will replace any aliases on the command line with the resolved commands. Set-PSReadLineKeyHandler -Key "Alt+%" ` -BriefDescription ExpandAliases ` -LongDescription "Replace all aliases with the full command" ` -ScriptBlock { param($key, $arg) $ast = $null $tokens = $null $errors = $null $cursor = $null [Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$ast, [ref]$tokens, [ref]$errors, [ref]$cursor) $startAdjustment = 0 foreach ($token in $tokens) { if ($token.TokenFlags -band [TokenFlags]::CommandName) { $alias = $ExecutionContext.InvokeCommand.GetCommand($token.Extent.Text, 'Alias') if ($alias -ne $null) { $resolvedCommand = $alias.ResolvedCommandName if ($resolvedCommand -ne $null) { $extent = $token.Extent $length = $extent.EndOffset - $extent.StartOffset [Microsoft.PowerShell.PSConsoleReadLine]::Replace( $extent.StartOffset + $startAdjustment, $length, $resolvedCommand) # Our copy of the tokens won't have been updated, so we need to # adjust by the difference in length $startAdjustment += ($resolvedCommand.Length - $length) } } } } } # F1 for help on the command line - naturally Set-PSReadLineKeyHandler -Key F1 ` -BriefDescription CommandHelp ` -LongDescription "Open the help window for the current command" ` -ScriptBlock { param($key, $arg) $ast = $null $tokens = $null $errors = $null $cursor = $null [Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$ast, [ref]$tokens, [ref]$errors, [ref]$cursor) $commandAst = $ast.FindAll( { $node = $args[0] $node -is [CommandAst] -and $node.Extent.StartOffset -le $cursor -and $node.Extent.EndOffset -ge $cursor }, $true) | Select-Object -Last 1 if ($commandAst -ne $null) { $commandName = $commandAst.GetCommandName() if ($commandName -ne $null) { $command = $ExecutionContext.InvokeCommand.GetCommand($commandName, 'All') if ($command -is [AliasInfo]) { $commandName = $command.ResolvedCommandName } if ($commandName -ne $null) { Get-Help $commandName -ShowWindow } } } } # # Ctrl+Shift+j then type a key to mark the current directory. # Ctrj+j then the same key will change back to that directory without # needing to type cd and won't change the command line. # $global:PSReadLineMarks = @{} Set-PSReadLineKeyHandler -Key Ctrl+J ` -BriefDescription MarkDirectory ` -LongDescription "Mark the current directory" ` -ScriptBlock { param($key, $arg) $key = [Console]::ReadKey($true) $global:PSReadLineMarks[$key.KeyChar] = $pwd } Set-PSReadLineKeyHandler -Key Ctrl+j ` -BriefDescription JumpDirectory ` -LongDescription "Goto the marked directory" ` -ScriptBlock { param($key, $arg) $key = [Console]::ReadKey() $dir = $global:PSReadLineMarks[$key.KeyChar] if ($dir) { cd $dir [Microsoft.PowerShell.PSConsoleReadLine]::InvokePrompt() } } Set-PSReadLineKeyHandler -Key Alt+j ` -BriefDescription ShowDirectoryMarks ` -LongDescription "Show the currently marked directories" ` -ScriptBlock { param($key, $arg) $global:PSReadLineMarks.GetEnumerator() | % { [PSCustomObject]@{Key = $_.Key; Dir = $_.Value} } | Format-Table -AutoSize | Out-Host [Microsoft.PowerShell.PSConsoleReadLine]::InvokePrompt() } # Auto correct 'git cmt' to 'git commit' Set-PSReadLineOption -CommandValidationHandler { param([CommandAst]$CommandAst) switch ($CommandAst.GetCommandName()) { 'git' { $gitCmd = $CommandAst.CommandElements[1].Extent switch ($gitCmd.Text) { 'cmt' { [Microsoft.PowerShell.PSConsoleReadLine]::Replace( $gitCmd.StartOffset, $gitCmd.EndOffset - $gitCmd.StartOffset, 'commit') } } } } } # `ForwardChar` accepts the entire suggestion text when the cursor is at the end of the line. # This custom binding makes `RightArrow` behave similarly - accepting the next word instead of the entire suggestion text. Set-PSReadLineKeyHandler -Key RightArrow ` -BriefDescription ForwardCharAndAcceptNextSuggestionWord ` -LongDescription "Move cursor one character to the right in the current editing line and accept the next word in suggestion when it's at the end of current editing line" ` -ScriptBlock { param($key, $arg) $line = $null $cursor = $null [Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$line, [ref]$cursor) if ($cursor -lt $line.Length) { [Microsoft.PowerShell.PSConsoleReadLine]::ForwardChar($key, $arg) } else { [Microsoft.PowerShell.PSConsoleReadLine]::AcceptNextSuggestionWord($key, $arg) } } # Cycle through arguments on current line and select the text. This makes it easier to quickly change the argument if re-running a previously run command from the history # or if using a psreadline predictor. You can also use a digit argument to specify which argument you want to select, i.e. Alt+1, Alt+a selects the first argument # on the command line. Set-PSReadLineKeyHandler -Key Alt+a ` -BriefDescription SelectCommandArguments ` -LongDescription "Set current selection to next command argument in the command line. Use of digit argument selects argument by position" ` -ScriptBlock { param($key, $arg) $ast = $null $cursor = $null [Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$ast, [ref]$null, [ref]$null, [ref]$cursor) $asts = $ast.FindAll( { $args[0] -is [System.Management.Automation.Language.ExpressionAst] -and $args[0].Parent -is [System.Management.Automation.Language.CommandAst] -and $args[0].Extent.StartOffset -ne $args[0].Parent.Extent.StartOffset }, $true) if ($asts.Count -eq 0) { [Microsoft.PowerShell.PSConsoleReadLine]::Ding() return } $nextAst = $null if ($null -ne $arg) { $nextAst = $asts[$arg - 1] } else { foreach ($ast in $asts) { if ($ast.Extent.StartOffset -ge $cursor) { $nextAst = $ast break } } if ($null -eq $nextAst) { $nextAst = $asts[0] } } $startOffsetAdjustment = 0 $endOffsetAdjustment = 0 if ($nextAst -is [System.Management.Automation.Language.StringConstantExpressionAst] -and $nextAst.StringConstantType -ne [System.Management.Automation.Language.StringConstantType]::BareWord) { $startOffsetAdjustment = 1 $endOffsetAdjustment = 2 } [Microsoft.PowerShell.PSConsoleReadLine]::SetCursorPosition($nextAst.Extent.StartOffset + $startOffsetAdjustment) [Microsoft.PowerShell.PSConsoleReadLine]::SetMark($null, $null) [Microsoft.PowerShell.PSConsoleReadLine]::SelectForwardChar($null, ($nextAst.Extent.EndOffset - $nextAst.Extent.StartOffset) - $endOffsetAdjustment) } Set-PSReadLineOption -PredictionSource History Set-PSReadLineOption -PredictionViewStyle ListView Set-PSReadLineOption -EditMode Windows # This is an example of a macro that you might use to execute a command. # This will add the command to history. Set-PSReadLineKeyHandler -Key Ctrl+Shift+b ` -BriefDescription BuildCurrentDirectory ` -LongDescription "Build the current directory" ` -ScriptBlock { [Microsoft.PowerShell.PSConsoleReadLine]::RevertLine() [Microsoft.PowerShell.PSConsoleReadLine]::Insert("dotnet build") [Microsoft.PowerShell.PSConsoleReadLine]::AcceptLine() } Set-PSReadLineKeyHandler -Key Ctrl+Shift+t ` -BriefDescription BuildCurrentDirectory ` -LongDescription "Build the current directory" ` -ScriptBlock { [Microsoft.PowerShell.PSConsoleReadLine]::RevertLine() [Microsoft.PowerShell.PSConsoleReadLine]::Insert("dotnet test") [Microsoft.PowerShell.PSConsoleReadLine]::AcceptLine() }
- Open PowerShell and run the following command to edit your profile. If you don’t have a profile, a window will prompt you to create one. or you can create a profile in the directory
- You can follow this reference: https://ohmyposh.dev/docs/installation/prompt
- Configure Terminal Settings
- Open Windows Terminal and go to Settings.
- At the bottom, click on Open JSON file.
- The
settings.json
file will open in your default text editor. - Add the following lines to the
settings.json
file to enable Oh My Posh: - Here is my settings.json file. You can copy and paste it into your settings.json file. https://gist.github.com/mahedee/44fa5b644aa06597456d8cac5c2f3dd2
settings.json
{
"$help": "https://aka.ms/terminal-documentation",
"$schema": "https://aka.ms/terminal-profiles-schema",
"actions":
[
{
"command":
{
"action": "copy",
"singleLine": false
},
"id": "User.copy.644BA8F2",
"keys": "ctrl+c"
},
{
"command":
{
"action": "newTab"
},
"id": "User.newTab.5DEADB41",
"keys": "ctrl+t"
},
{
"command":
{
"action": "commandPalette"
},
"id": "User.commandPalette.D3F0B923",
"keys": "ctrl+shift+p"
},
{
"command":
{
"action": "splitPane",
"split": "auto",
"splitMode": "duplicate"
},
"id": "User.splitPane.A6751878",
"keys": "alt+shift+d"
},
{
"command": "paste",
"id": "User.paste",
"keys": "ctrl+v"
},
{
"command": "toggleAlwaysOnTop",
"id": "User.toggleAlwaysOnTop",
"keys": "alt+shift+f11"
},
{
"command": "closePane",
"id": "User.closePane",
"keys": "ctrl+w"
},
{
"command":
{
"action": "splitPane"
},
"id": "User.splitPane.91AB55B1",
"keys": "ctrl+shift+minus"
},
{
"command": "toggleFocusMode",
"id": "User.toggleFocusMode",
"keys": "shift+f11"
},
{
"command": "unbound",
"keys": "ctrl+d"
},
{
"command": "find",
"id": "User.find",
"keys": "ctrl+shift+f"
},
{
"command":
{
"action": "openSettings",
"target": "settingsUI"
},
"id": "User.openSettings.6CD791B",
"keys": "ctrl+shift+comma"
}
],
"copyFormatting": "none",
"copyOnSelect": false,
"defaultProfile": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
"newTabMenu":
[
{
"type": "remainingProfiles"
}
],
"profiles":
{
"defaults": {},
"list":
[
{
"commandline": "%SystemRoot%\\System32\\WindowsPowerShell\\v1.0\\powershell.exe",
"font":
{
"face": "CaskaydiaCove Nerd Font"
},
"guid": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}",
"hidden": false,
"name": "Windows PowerShell"
},
{
"commandline": "%SystemRoot%\\System32\\cmd.exe",
"guid": "{0caa0dad-35be-5f56-a8ff-afceeeaa6101}",
"hidden": false,
"name": "Command Prompt"
},
{
"guid": "{b453ae62-4e3d-5e58-b989-0a998ec441b8}",
"hidden": false,
"name": "Azure Cloud Shell",
"source": "Windows.Terminal.Azure"
},
{
"guid": "{8d610568-1d18-58b9-abbd-c7eec0a40dd4}",
"hidden": false,
"name": "Developer Command Prompt for VS 2022",
"source": "Windows.Terminal.VisualStudio"
},
{
"guid": "{97f5749f-a899-50f2-a042-7cf1e52bf252}",
"hidden": false,
"name": "Developer PowerShell for VS 2022",
"source": "Windows.Terminal.VisualStudio"
},
{
"commandline": "C:\\Program Files\\Git\\bin\\bash.exe",
"font":
{
"face": "CaskaydiaCove Nerd Font"
},
"guid": "{0b98aac8-d3d9-404f-a6a5-236565bb4610}",
"hidden": false,
"icon": "C:\\Program Files\\Git\\mingw64\\share\\git\\git-for-windows.ico",
"name": "Git Bash",
"startingDirectory": "%USERPROFILE%"
},
{
"guid": "{16208362-94fc-5b1f-a491-5b2624d5ab56}",
"hidden": true,
"name": "Visual Studio Debug Console",
"source": "VSDebugConsole"
},
{
"guid": "{72b5d9e6-2c71-5e9a-bf26-e8f53ae4c661}",
"hidden": false,
"name": "Developer Command Prompt for VS 2022 (2)",
"source": "Windows.Terminal.VisualStudio"
},
{
"guid": "{62090c5f-ff51-5b36-8031-449b2b91dff2}",
"hidden": false,
"name": "Developer PowerShell for VS 2022 (2)",
"source": "Windows.Terminal.VisualStudio"
}
]
},
"schemes":
[
{
"background": "#300A24",
"black": "#171421",
"blue": "#0037DA",
"brightBlack": "#767676",
"brightBlue": "#08458F",
"brightCyan": "#2C9FB3",
"brightGreen": "#26A269",
"brightPurple": "#A347BA",
"brightRed": "#C01C28",
"brightWhite": "#F2F2F2",
"brightYellow": "#A2734C",
"cursorColor": "#FFFFFF",
"cyan": "#3A96DD",
"foreground": "#FFFFFF",
"green": "#26A269",
"name": "Ubuntu-ColorScheme",
"purple": "#881798",
"red": "#C21A23",
"selectionBackground": "#FFFFFF",
"white": "#CCCCCC",
"yellow": "#A2734C"
}
],
"themes": []
}
- Run the following command to Install PowerShell Gallery Modules. This is used to install the Terminal-Icons module, which is required by Oh My Posh:
Install-Module -Name Terminal-Icons -Repository PSGallery
- Configure Theme:
- You can choose a theme from the Oh My Posh theme gallery.
- Generally themes are located in
C:\Users\{your-username}\AppData\Local\Programs\oh-my-posh\themes
. -
You can initialize Oh My Posh with a specific theme by running the following command:
oh-my-posh init pwsh --config C:\Users\{your-username}\AppData\Local\Programs\oh-my-posh\themes\jandedobbeleer.omp.json | Invoke-Expression
- In my case, I have created a custom theme named
myposh-mahedee.omp.json
-
You can copy it from here: https://gist.github.com/mahedee/e93c0f1f145dcb6db804c73fa4ed859e
- Create a file named
myposh-mahedee.omp.json
and copy it to the themes directory. -
Now initialize Oh My Posh with this theme:
oh-my-posh init pwsh --config C:\Users\{your-username}\AppData\Local\Programs\oh-my-posh\themes\myposh-mahedee.omp.json | Invoke-Expression
-
In my case and as per example below, you will see the theme is configured in the PowerShell profile script - Microsoft.PowerShell_profile.ps1 as follows:
oh-my-posh init pwsh --config C:\Users\mahedee\AppData\Local\Programs\oh-my-posh\themes\myposh-mahedee.omp.json | Invoke-Expression
Step 6: Configure IntelliSense in PowerShell using PSReadLine
- Install the PSReadLine module by running the following command in PowerShell:
Install-Module -Name PSReadLine -AllowClobber -Force
References: https://learn.microsoft.com/en-us/powershell/module/psreadline/about/about_psreadline?view=powershell-7.4
- Enable Predictive IntelliSense by running the following command:
Set-PSReadLineOption -PredictionSource History
Step 7: Restart Windows Terminal
- After saving the profile, close and reopen Windows Terminal to see the changes.
- Now you should see the Oh My Posh theme applied to your PowerShell prompt, making it visually appealing and informative.
- Use the terminal as you normally would, and enjoy the enhanced experience provided by Oh My Posh.
- You can see the theme is applied in the PowerShell prompt as shown below:
Efficient Command Usage and History
Windows Terminal makes it easy to work efficiently and navigate your history:
-
Use the Up and Down Arrow Keys: To quickly scroll through the history of commands.
-
Search Through History: Press Ctrl + R and start typing a command to search for it.
-
Save Command History: By default, PowerShell saves the history. If you want to ensure your history is saved across sessions, add this to your
$PROFILE
:$historyPath = "$env:USERPROFILE\Documents\PowerShell_history.txt" if (Test-Path $historyPath) { Get-Content $historyPath | Add-History } Register-EngineEvent PowerShell.Exiting -Action { Get-History | Export-Csv $historyPath -NoTypeInformation }
-
Use Aliases for Common Commands: In PowerShell or Git Bash, create aliases for long commands to make them quicker to type:
Set-Alias gs git status Set-Alias gl git log
Additional Tips and Tricks
- Split Panes: You can split your terminal into multiple panes.
- Ctrl + Shift + D: Split horizontally.
- Alt + Shift + D: Split vertically.
- Ctrl + Shift + W: Close a pane.
- Quick Access to Profiles: You can easily switch between profiles in Windows Terminal via the dropdown menu or by using the keyboard shortcut.
- Ctrl + Tab: Cycle through profiles.
- Ctrl + Shift + Tab: Cycle through profiles in reverse order.
- Use Profiles for Different Environments: Create multiple profiles for PowerShell, Git Bash, WSL, or other environments and switch seamlessly between them.
Conclusion
By following these steps, you’ve successfully set up a productive and efficient Windows Terminal environment. With Oh My Posh, you’ve made your PowerShell experience visually appealing and informative. You’ve also integrated Git Bash for a seamless Unix-like experience on Windows, making it easier to switch between different tools and environments. Similar way you can include Ubuntu, putty etc.
Windows Terminal offers unmatched flexibility in managing your development tools, and with the addition of themes, custom profiles, and persistent command history, your workflow will be more efficient than ever before.
Happy coding!