From 53e4a3e54df94416a74da376b7a444fc2cd2ba5a Mon Sep 17 00:00:00 2001 From: Keith Hill Date: Sun, 5 Mar 2017 00:58:44 -0700 Subject: [PATCH 01/19] Basics working with C# classes. --- .vscode/launch.json | 2 +- src/Ansi.cs | 354 ++++++++++++++++++++++++++++++++++++++++++++ src/AnsiUtils.ps1 | 244 ++++++++++++++++++++++++++++++ src/GitPrompt.ps1 | 128 ++++++++++++---- src/posh-git.psm1 | 9 +- 5 files changed, 708 insertions(+), 29 deletions(-) create mode 100644 src/Ansi.cs create mode 100644 src/AnsiUtils.ps1 diff --git a/.vscode/launch.json b/.vscode/launch.json index 608c8f608..42a031619 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -21,7 +21,7 @@ "type": "PowerShell", "request": "attach", "name": "PowerShell Attach to Host Process", - "processId": "${command.PickPSHostProcess}", + "processId": "${command:PickPSHostProcess}", "runspaceId": 1 } ] diff --git a/src/Ansi.cs b/src/Ansi.cs new file mode 100644 index 000000000..8066fd77e --- /dev/null +++ b/src/Ansi.cs @@ -0,0 +1,354 @@ +using System; +using System.Collections.Generic; +using System.Management.Automation; + +namespace PoshGit { + public enum ColorMode { + DefaultColor, + ConsoleColor, + XTerm256, + Rgb + } + + public enum AnsiTextOption { + Default = 0, // Returns all attributes to the default state prior to modification + Bold = 1, // Applies brightness/intensity flag to foreground color + Underline = 4, // Adds underline + NoUnderline = 24, // Removes underline + Negative = 7, // Swaps foreground and background colors + Positive = 27, // Returns foreground/background to normal + FgBlack = 30, // Applies non-bold/bright black to foreground + FgRed = 31, // Applies non-bold/bright red to foreground + FgGreen = 32, // Applies non-bold/bright green to foreground + FgYellow = 33, // Applies non-bold/bright yellow to foreground + FgBlue = 34, // Applies non-bold/bright blue to foreground + FgMagenta = 35, // Applies non-bold/bright magenta to foreground + FgCyan = 36, // Applies non-bold/bright cyan to foreground + FgWhite = 37, // Applies non-bold/bright white to foreground + FgExtended = 38, // Applies extended color value to the foreground (see details below) + FgDefault = 39, // Applies only the foreground portion of the defaults (see 0) + BgBlack = 40, // Applies non-bold/bright black to background + BgRed = 41, // Applies non-bold/bright red to background + BgGreen = 42, // Applies non-bold/bright green to background + BgYellow = 43, // Applies non-bold/bright yellow to background + BgBlue = 44, // Applies non-bold/bright blue to background + BgMagenta = 45, // Applies non-bold/bright magenta to background + BgCyan = 46, // Applies non-bold/bright cyan to background + BgWhite = 47, // Applies non-bold/bright white to background + BgExtended = 48, // Applies extended color value to the background (see details below) + BgDefault = 49, // Applies only the background portion of the defaults (see 0) + FgBrightBlack = 90, // Applies bold/bright black to foreground + FgBrightRed = 91, // Applies bold/bright red to foreground + FgBrightGreen = 92, // Applies bold/bright green to foreground + FgBrightYellow = 93, // Applies bold/bright yellow to foreground + FgBrightBlue = 94, // Applies bold/bright blue to foreground + FgBrightMagenta = 95, // Applies bold/bright magenta to foreground + FgBrightCyan = 96, // Applies bold/bright cyan to foreground + FgBrightWhite = 97, // Applies bold/bright white to foreground + BgBrightBlack = 100, // Applies bold/bright black to background + BgBrightRed = 101, // Applies bold/bright red to background + BgBrightGreen = 102, // Applies bold/bright green to background + BgBrightYellow = 103, // Applies bold/bright yellow to background + BgBrightBlue = 104, // Applies bold/bright blue to background + BgBrightMagenta = 105, // Applies bold/bright magenta to background + BgBrightCyan = 106, // Applies bold/bright cyan to background + BgBrightWhite = 107, // Applies bold/bright white to background + } + + public static class Ansi { + public static Dictionary ConsoleColorToAnsi = new Dictionary() { + {"Black", 30}, + {"DarkBlue", 34}, + {"DarkGreen", 32}, + {"DarkCyan", 36}, + {"DarkRed", 31}, + {"DarkMagenta", 35}, + {"DarkYellow", 33}, + {"Gray", 37}, + {"DarkGray", 90}, + {"Blue", 94}, + {"Green", 92}, + {"Cyan", 96}, + {"Red", 91}, + {"Magenta", 95}, + {"Yellow", 93}, + {"White", 97} + }; + + public static string GetAnsiSequence(Color color, bool isForeground) + { + var extended = isForeground ? AnsiTextOption.FgExtended : AnsiTextOption.BgExtended; + var sgrSubSeq256 = "5"; + var sgrSubSeqRgb = "2"; + + switch (color.ColorMode) + { + case ColorMode.ConsoleColor: + string consoleColorName = Enum.GetName(typeof(ConsoleColor), color.ConsoleColor); + int ansiValue = ConsoleColorToAnsi[consoleColorName]; + ansiValue += isForeground ? 0 : 10; + return ansiValue.ToString(); + + case ColorMode.XTerm256: + byte index = color.XTerm256Index; + var xtermAnsiSeq = String.Format("{0};{1};{2}", extended, sgrSubSeq256, index); + return xtermAnsiSeq; + + case ColorMode.Rgb: + var rgbAnsiSeq = String.Format("{0};{1};{2};{3};{4}", extended, sgrSubSeqRgb, color.Red, color.Green, color.Blue); + return rgbAnsiSeq; + + case ColorMode.DefaultColor: + var defaultColor = (int)(isForeground ? AnsiTextOption.FgDefault : AnsiTextOption.BgDefault); + return defaultColor.ToString(); + + default: + throw new ArgumentException("Unexpected ColorMode value " + color.ColorMode.ToString()); + } + } + + public static string GetAnsiSequence(TextSpan textSpan) + { + return GetAnsiSequence(textSpan.Text, textSpan.ForegroundColor, textSpan.BackgroundColor); + } + + public static string GetAnsiSequence(string text, Color foregroundColor, Color backgroundColor) + { + string fgSeq = GetAnsiSequence(foregroundColor, true); + string bgSeq = GetAnsiSequence(backgroundColor, false); + + var ansiSeq = String.Format("\x1b[{0};{1}m{2}\x1b[{3}m", fgSeq, bgSeq, text, (int)AnsiTextOption.Default); + return ansiSeq; + + } + } + + public class Color + { + private ColorMode _mode; + private int _value; + + // Use this constructor to specify the default color + public Color() + { + _mode = ColorMode.DefaultColor; + _value = -1; + } + + public Color(Color color) + { + _mode = color._mode; + _value = color._value; + } + + public Color(ConsoleColor consoleColor) + { + _mode = ColorMode.ConsoleColor; + _value = (int)consoleColor; + } + + public Color(byte xterm256Index) + { + _mode = ColorMode.XTerm256; + _value = xterm256Index; + } + + public Color(byte red, byte green, byte blue) + { + _mode = ColorMode.Rgb; + _value = (red << 16) + (green << 8) + blue; + } + + public Color(int rgb) { + _mode = ColorMode.Rgb; + _value = rgb & 0x00FFFFFF; + } + + public ColorMode ColorMode + { + get { return _mode; } + } + + public ConsoleColor ConsoleColor + { + get + { + if (_mode != ColorMode.ConsoleColor) + { + throw new InvalidOperationException("ConsoleColor is only valid when ColorMode is set to ConsoleColor."); + } + + return (ConsoleColor)_value; + } + } + + public byte XTerm256Index + { + get + { + if (_mode != ColorMode.XTerm256) + { + throw new InvalidOperationException("XTerm256Index is only valid when ColorMode is set to XTerm256."); + } + + return (byte)_value; + } + } + + public int Rgb + { + get + { + VerifyColorModeRgb(); + return _value; + } + } + + public byte Red + { + get + { + VerifyColorModeRgb(); + return (byte)((_value & 0x00FF0000) >> 16); + } + } + + public byte Green + { + get + { + VerifyColorModeRgb(); + return (byte)((_value & 0x0000FF00) >> 8); + } + } + + public byte Blue + { + get + { + VerifyColorModeRgb(); + return (byte)(_value & 0x000000FF); + } + } + + private void VerifyColorModeRgb() + { + if (_mode != ColorMode.Rgb) + { + throw new InvalidOperationException("Rgb is only valid when ColorMode is set to Rgb."); + } + } + } + + public class TextSpan { + private string _text; + private Color _backgroundColor; + private Color _foregroundColor; + private string _customAnsiSeq; + + public TextSpan(string text) : this(text, new Color(), new Color()) + { + } + + public TextSpan(string text, Color foregroundColor) : this(text, foregroundColor, new Color()) + { + } + + public TextSpan(string text, Color foregroundColor, Color backgroundColor) + { + _text = text; + _foregroundColor = foregroundColor; + _backgroundColor = backgroundColor; + _customAnsiSeq = string.Empty; + } + + public TextSpan(string text, string customAnsiSeq) + { + _text = text; + _customAnsiSeq = customAnsiSeq; + } + + public string Text + { + get { return _text; } + } + + public Color ForegroundColor + { + get { return _foregroundColor; } + } + + public Color BackgroundColor + { + get { return _backgroundColor; } + } + + public string CustomAnsiSeq + { + get { return _customAnsiSeq; } + } + } + + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false)] + public sealed class ColorTransformAttribute : ArgumentTransformationAttribute + { + public override object Transform(EngineIntrinsics engineIntrinsics, object inputData) + { + if (inputData == null) + { + return new Color(); + } + + if (inputData is Color) + { + return inputData; + } + + if (inputData is ConsoleColor) + { + return new Color((ConsoleColor)inputData); + } + + if (inputData is string) + { + var consoleColorName = (string)inputData; + ConsoleColor consoleColor; + if (Enum.TryParse(consoleColorName, true, out consoleColor)) + { + return new Color(consoleColor); + } + else + { + throw new PSArgumentException("Unrecognized ConsoleColor name " + consoleColorName); + } + } + + if (inputData is byte) + { + var index = (byte)inputData; + if (index < 16) + { + var consoleColor = (ConsoleColor)index; + return new Color(consoleColor); + } + else + { + return new Color(index); + } + } + + if (inputData is int) + { + var rgb = (int)inputData; + return new Color(rgb); + } + + throw new PSArgumentException("Could not transform type '" + inputData.GetType().FullName + "' to PoshGit.Color"); + } + + public override bool TransformNullOptionalParameters + { + get { return true; } + } + } +} diff --git a/src/AnsiUtils.ps1 b/src/AnsiUtils.ps1 new file mode 100644 index 000000000..7f6c6745a --- /dev/null +++ b/src/AnsiUtils.ps1 @@ -0,0 +1,244 @@ +# enum ColorMode { +# ColorModeDefaultColor +# ColorModeConsole +# ColorMode256 +# ColorMode24Bit +# } + +# enum AnsiTextOption { +# Default = 0 # Returns all attributes to the default state prior to modification +# Bold = 1 # Applies brightness/intensity flag to foreground color +# Underline = 4 # Adds underline +# NoUnderline = 24 # Removes underline +# Negative = 7 # Swaps foreground and background colors +# Positive = 27 # Returns foreground/background to normal +# FgBlack = 30 # Applies non-bold/bright black to foreground +# FgRed = 31 # Applies non-bold/bright red to foreground +# FgGreen = 32 # Applies non-bold/bright green to foreground +# FgYellow = 33 # Applies non-bold/bright yellow to foreground +# FgBlue = 34 # Applies non-bold/bright blue to foreground +# FgMagenta = 35 # Applies non-bold/bright magenta to foreground +# FgCyan = 36 # Applies non-bold/bright cyan to foreground +# FgWhite = 37 # Applies non-bold/bright white to foreground +# FgExtended = 38 # Applies extended color value to the foreground (see details below) +# FgDefault = 39 # Applies only the foreground portion of the defaults (see 0) +# BgBlack = 40 # Applies non-bold/bright black to background +# BgRed = 41 # Applies non-bold/bright red to background +# BgGreen = 42 # Applies non-bold/bright green to background +# BgYellow = 43 # Applies non-bold/bright yellow to background +# BgBlue = 44 # Applies non-bold/bright blue to background +# BgMagenta = 45 # Applies non-bold/bright magenta to background +# BgCyan = 46 # Applies non-bold/bright cyan to background +# BgWhite = 47 # Applies non-bold/bright white to background +# BgExtended = 48 # Applies extended color value to the background (see details below) +# BgDefault = 49 # Applies only the background portion of the defaults (see 0) +# FgBrightBlack = 90 # Applies bold/bright black to foreground +# FgBrightRed = 91 # Applies bold/bright red to foreground +# FgBrightGreen = 92 # Applies bold/bright green to foreground +# FgBrightYellow = 93 # Applies bold/bright yellow to foreground +# FgBrightBlue = 94 # Applies bold/bright blue to foreground +# FgBrightMagenta = 95 # Applies bold/bright magenta to foreground +# FgBrightCyan = 96 # Applies bold/bright cyan to foreground +# FgBrightWhite = 97 # Applies bold/bright white to foreground +# BgBrightBlack = 100 # Applies bold/bright black to background +# BgBrightRed = 101 # Applies bold/bright red to background +# BgBrightGreen = 102 # Applies bold/bright green to background +# BgBrightYellow = 103 # Applies bold/bright yellow to background +# BgBrightBlue = 104 # Applies bold/bright blue to background +# BgBrightMagenta = 105 # Applies bold/bright magenta to background +# BgBrightCyan = 106 # Applies bold/bright cyan to background +# BgBrightWhite = 107 # Applies bold/bright white to background +# } + +# class Ansi { +# hidden static $ConsoleColorToAnsi = @( +# 30 # Black +# 34 # DarkBlue +# 32 # DarkGreen +# 36 # DarkCyan +# 31 # DarkRed +# 35 # DarkMagenta +# 33 # DarkYellow +# 37 # Gray +# 90 # DarkGray +# 94 # Blue +# 92 # Green +# 96 # Cyan +# 91 # Red +# 95 # Magenta +# 93 # Yellow +# 97 # White +# ) + +# static [bool] HostSupportsAnsi() { +# return $global:Host.UI.SupportsVirtualTerminal +# } + +# # TODO: Not sure we need this. +# static [bool] HostSupports24BitColor() { +# # On Windows, if we are on 10.0.15048.0 (or whatever the CU update's version number is) 24bit color is supported. +# # On Linux/macOS, need to figure out how to determine this. +# return $false +# } + +# static [string] GetAnsiSequence([Color]$color, [bool]$IsForeground) { +# $ansiSeq = "" +# $sgrSubSeq256 = "5" +# $sgrSubSeqRgb = "2" +# [int]$extended = if ($IsForeground) { [AnsiTextOption]::FgExtended } else { [AnsiTextOption]::BgExtended } + +# switch ($color.ColorMode()) { +# ([ColorMode]::ColorModeConsole) { +# $ansiValue = [Ansi]::ConsoleColorToAnsi[$color.ConsoleColor()] +# if (!$IsForeground) { +# $ansiValue += 10 +# } +# $ansiSeq = $ansiValue +# break +# } +# ([ColorMode]::ColorMode256) { +# $colorIndex = $color.Color256Index() +# $ansiSeq = "$extended;$sgrSubSeq256;$colorIndex" +# break +# } +# ([ColorMode]::ColorMode24Bit) { +# $r = $color.Red() +# $g = $color.Green() +# $b = $color.Blue() +# $ansiSeq = "$extended;$sgrSubSeqRgb;$r;$g;$b" +# break +# } +# ([ColorMode]::ColorModeDefaultColor) { +# [int]$defaultColor = if ($IsForeground) { [AnsiTextOption]::FgDefault } else { [AnsiTextOption]::BgDefault } +# $ansiSeq = "$defaultColor" +# break +# } +# default { +# throw "Unexpected ColorMode '$($color.ColorMode())'" +# } +# } + +# return $ansiSeq; +# } + +# static [string] GetAnsiSequence([TextSpan]$TextSpan) { +# return [Ansi]::GetAnsiSequence($TextSpan.Text, $TextSpan.ForegroundColor, $TextSpan.BackgroundColor) +# } + +# static [string] GetAnsiSequence([string]$Text, [Color]$ForegroundColor, [Color]$BackgroundColor) { +# $ESC = [char]0x1B + +# $fgSeq = [Ansi]::GetAnsiSequence($ForegroundColor, $true) +# $bgSeq = [Ansi]::GetAnsiSequence($BackgroundColor, $false) +# $def = [int][AnsiTextOption]::Default + +# $ansiSeq = "$ESC[${fgSeq};${bgSeq}m${Text}$ESC[${def}m" +# return $ansiSeq +# } +# } + +# class Color { +# hidden [ColorMode]$mode +# hidden [int]$value + +# # Use this constructor to specify the default color +# Color() { +# $this.mode = [ColorMode]::ColorModeDefaultColor +# $this.value = -1 +# } + +# Color([Color]$color) { +# $this.mode = $color.mode +# $this.value = $color.value +# } + +# Color([System.ConsoleColor]$consoleColor) { +# $this.mode = [ColorMode]::ColorModeConsole +# $this.value = [int]$consoleColor +# } + +# Color([byte]$color256Index) { +# $this.mode = [ColorMode]::ColorMode256 +# $this.value = $color256Index +# } + +# Color([byte]$red, [byte]$green, [byte]$blue) { +# $this.mode = [ColorMode]::ColorMode24Bit +# $this.value = ($red -shl 16) + ($green -shl 8) + $blue +# } + +# Color([int]$rgb) { +# $this.mode = [ColorMode]::ColorMode24Bit +# $this.value = $rgb -band 0x00FFFFFF +# } + +# [ColorMode] ColorMode() { +# return $this.mode +# } + +# [System.ConsoleColor] ConsoleColor() { +# if ($this.mode -ne [ColorMode]::ColorModeConsole) { +# throw "ConsoleColor() is only valid when ColorMode is ColorModeConsole." +# } +# return [System.ConsoleColor]$this.value +# } + +# [byte] Color256Index() { +# if ($this.mode -ne [ColorMode]::ColorMode256) { +# throw "Color256Index() is only valid when ColorMode is ColorMode256." +# } +# return [byte]$this.value +# } + +# [int] Rgb() { +# if ($this.mode -ne [ColorMode]::ColorMode24Bit) { +# throw "Rgb() is only valid when ColorMode is ColorMode24Bit." +# } +# return $this.value +# } + +# [byte] Red() { +# if ($this.mode -ne [ColorMode]::ColorMode24Bit) { +# throw "Red() is only valid when ColorMode is ColorMode24Bit." +# } +# return ($this.value -band 0x00FF0000) -shr 16 +# } + +# [byte] Green() { +# if ($this.mode -ne [ColorMode]::ColorMode24Bit) { +# throw "Green() is only valid when ColorMode is ColorMode24Bit." +# } +# return ($this.value -band 0x0000FF00) -shr 8 +# } + +# [byte] Blue() { +# if ($this.mode -ne [ColorMode]::ColorMode24Bit) { +# throw "Blue() is only valid when ColorMode is ColorMode24Bit." +# } +# return ($this.value -band 0x000000FF) +# } +# } + +# class TextSpan { +# [string]$Text +# [Color]$BackgroundColor +# [Color]$ForegroundColor + +# TextSpan([string]$text) { +# $this.Text = $text +# $this.ForegroundColor = [Color]::new() +# $this.BackgroundColor = [Color]::new() +# } + +# TextSpan([string]$text, [Color]$foregroundColor) { +# $this.Text = $text +# $this.ForegroundColor = $foregroundColor +# $this.BackgroundColor = [Color]::new() +# } + +# TextSpan([string]$text, [Color]$foregroundColor, [Color]$backgroundColor) { +# $this.Text = $text +# $this.ForegroundColor = $foregroundColor +# $this.BackgroundColor = $backgroundColor +# } +# } diff --git a/src/GitPrompt.ps1 b/src/GitPrompt.ps1 index ce3f9b2bd..9ba9eca4f 100644 --- a/src/GitPrompt.ps1 +++ b/src/GitPrompt.ps1 @@ -127,11 +127,76 @@ if (Get-Module NuGet) { $WindowTitleSupported = $false } -function Write-Prompt($Object, $ForegroundColor, $BackgroundColor = -1) { - if ($BackgroundColor -lt 0) { - Write-Host $Object -NoNewLine -ForegroundColor $ForegroundColor - } else { - Write-Host $Object -NoNewLine -ForegroundColor $ForegroundColor -BackgroundColor $BackgroundColor +$ansiStrBldStack = New-Object 'System.Collections.Generic.Stack[System.Text.StringBuilder]' + +function Get-AnsiStringBuilder() { + if ($ansiStrBldStack.Count -gt 0) { + $ansiStrBldStack.Peek(); + } +} + +function Push-AnsiStringBuilder([System.Text.StringBuilder]$StringBuilder = (New-Object System.Text.StringBuilder)) { + $ansiStrBldStack.Push($StringBuilder) +} + +function Pop-AnsiStringBuilder() { + if ($ansiStrBldStack.Count -gt 0) { + $ansiStrBldStack.Pop(); + } +} + +function Write-Prompt { + param( + [Parameter(Mandatory=$true, Position=0, ParameterSetName="TextSpan")] + [PoshGit.TextSpan] + $TextSpan, + + [Parameter(Mandatory=$true, Position=0, ParameterSetName="Object", ValueFromPipeline=$true)] + [Object] + $Object, + + [Parameter(Position=1, ParameterSetName="Object")] + [PoshGit.ColorTransformAttribute()] + [PoshGit.Color] + $ForegroundColor = [PoshGit.Color]::new(), + + [Parameter(Position=2, ParameterSetName="Object")] + [PoshGit.ColorTransformAttribute()] + [PoshGit.Color] + $BackgroundColor = [PoshGit.Color]::new(), + + [Parameter()] + [System.Text.StringBuilder]$StringBuilder + ) + + process { + if ($PSCmdlet.ParameterSetName -eq 'Object') { + $TextSpan = [PoshGit.TextSpan]::new($Object, $ForegroundColor, $BackgroundColor) + } + + if ($global:Host.UI.SupportsVirtualTerminal) { + $ansiSeq = [PoshGit.Ansi]::GetAnsiSequence($TextSpan) + if ($StringBuilder) { + $StringBuilder.Append($ansiSeq) > $null + } + elseif ($ansiBuffer = Get-AnsiStringBuilder) { + $ansiBuffer.Append($ansiSeq) > $null + } + else { + $ansiSeq + } + } + else { + $params = @{Object = $textSpan.Text; NoNewLine = $true} + if ($textSpan.ForegroundColor.ColorMode() -eq [ColorMode]::ColorModeConsole) { + $params.ForegroundColor = $textSpan.ForegroundColor.ConsoleColor() + } + if ($textSpan.BackgroundColor.ColorMode() -eq [ColorMode]::ColorModeConsole) { + $params.BackgroundColor = $textSpan.BackgroundColor.ConsoleColor() + } + + Write-Host @params + } } } @@ -146,10 +211,19 @@ function Format-BranchName($branchName){ return $branchName } -function Write-GitStatus($status) { +function Write-GitStatus($status, [System.Text.StringBuilder]$StringBuilder) { $s = $global:GitPromptSettings if ($status -and $s) { - Write-Prompt $s.BeforeText -BackgroundColor $s.BeforeBackgroundColor -ForegroundColor $s.BeforeForegroundColor + # if ($StringBuilder) { + # Push-AnsiStringBuilder $StringBuilder + # } + # elseif (!(Get-AnsiStringBuilder)) { + # Push-AnsiStringBuilder + # } + + $strBld = New-Object System.Text.StringBuilder + + Write-Prompt $s.BeforeText -BackgroundColor $s.BeforeBackgroundColor -ForegroundColor $s.BeforeForegroundColor -StringBuilder $strBld $branchStatusText = $null $branchStatusBackgroundColor = $s.BranchBackgroundColor @@ -201,47 +275,47 @@ function Write-GitStatus($status) { $branchStatusText = "?" } - Write-Prompt (Format-BranchName($status.Branch)) -BackgroundColor $branchStatusBackgroundColor -ForegroundColor $branchStatusForegroundColor + Write-Prompt (Format-BranchName($status.Branch)) -BackgroundColor $branchStatusBackgroundColor -ForegroundColor $branchStatusForegroundColor -StringBuilder $strBld if ($branchStatusText) { - Write-Prompt (" {0}" -f $branchStatusText) -BackgroundColor $branchStatusBackgroundColor -ForegroundColor $branchStatusForegroundColor + Write-Prompt (" {0}" -f $branchStatusText) -BackgroundColor $branchStatusBackgroundColor -ForegroundColor $branchStatusForegroundColor -StringBuilder $strBld } if($s.EnableFileStatus -and $status.HasIndex) { - Write-Prompt $s.BeforeIndexText -BackgroundColor $s.BeforeIndexBackgroundColor -ForegroundColor $s.BeforeIndexForegroundColor + Write-Prompt $s.BeforeIndexText -BackgroundColor $s.BeforeIndexBackgroundColor -ForegroundColor $s.BeforeIndexForegroundColor -StringBuilder $strBld if($s.ShowStatusWhenZero -or $status.Index.Added) { - Write-Prompt (" $($s.FileAddedText)$($status.Index.Added.Count)") -BackgroundColor $s.IndexBackgroundColor -ForegroundColor $s.IndexForegroundColor + Write-Prompt (" $($s.FileAddedText)$($status.Index.Added.Count)") -BackgroundColor $s.IndexBackgroundColor -ForegroundColor $s.IndexForegroundColor -StringBuilder $strBld } if($s.ShowStatusWhenZero -or $status.Index.Modified) { - Write-Prompt (" $($s.FileModifiedText)$($status.Index.Modified.Count)") -BackgroundColor $s.IndexBackgroundColor -ForegroundColor $s.IndexForegroundColor + Write-Prompt (" $($s.FileModifiedText)$($status.Index.Modified.Count)") -BackgroundColor $s.IndexBackgroundColor -ForegroundColor $s.IndexForegroundColor -StringBuilder $strBld } if($s.ShowStatusWhenZero -or $status.Index.Deleted) { - Write-Prompt (" $($s.FileRemovedText)$($status.Index.Deleted.Count)") -BackgroundColor $s.IndexBackgroundColor -ForegroundColor $s.IndexForegroundColor + Write-Prompt (" $($s.FileRemovedText)$($status.Index.Deleted.Count)") -BackgroundColor $s.IndexBackgroundColor -ForegroundColor $s.IndexForegroundColor -StringBuilder $strBld } if ($status.Index.Unmerged) { - Write-Prompt (" $($s.FileConflictedText)$($status.Index.Unmerged.Count)") -BackgroundColor $s.IndexBackgroundColor -ForegroundColor $s.IndexForegroundColor + Write-Prompt (" $($s.FileConflictedText)$($status.Index.Unmerged.Count)") -BackgroundColor $s.IndexBackgroundColor -ForegroundColor $s.IndexForegroundColor -StringBuilder $strBld } if($status.HasWorking) { - Write-Prompt $s.DelimText -BackgroundColor $s.DelimBackgroundColor -ForegroundColor $s.DelimForegroundColor + Write-Prompt $s.DelimText -BackgroundColor $s.DelimBackgroundColor -ForegroundColor $s.DelimForegroundColor -StringBuilder $strBld } } if($s.EnableFileStatus -and $status.HasWorking) { if($s.ShowStatusWhenZero -or $status.Working.Added) { - Write-Prompt (" $($s.FileAddedText)$($status.Working.Added.Count)") -BackgroundColor $s.WorkingBackgroundColor -ForegroundColor $s.WorkingForegroundColor + Write-Prompt (" $($s.FileAddedText)$($status.Working.Added.Count)") -BackgroundColor $s.WorkingBackgroundColor -ForegroundColor $s.WorkingForegroundColor -StringBuilder $strBld } if($s.ShowStatusWhenZero -or $status.Working.Modified) { - Write-Prompt (" $($s.FileModifiedText)$($status.Working.Modified.Count)") -BackgroundColor $s.WorkingBackgroundColor -ForegroundColor $s.WorkingForegroundColor + Write-Prompt (" $($s.FileModifiedText)$($status.Working.Modified.Count)") -BackgroundColor $s.WorkingBackgroundColor -ForegroundColor $s.WorkingForegroundColor -StringBuilder $strBld } if($s.ShowStatusWhenZero -or $status.Working.Deleted) { - Write-Prompt (" $($s.FileRemovedText)$($status.Working.Deleted.Count)") -BackgroundColor $s.WorkingBackgroundColor -ForegroundColor $s.WorkingForegroundColor + Write-Prompt (" $($s.FileRemovedText)$($status.Working.Deleted.Count)") -BackgroundColor $s.WorkingBackgroundColor -ForegroundColor $s.WorkingForegroundColor -StringBuilder $strBld } if ($status.Working.Unmerged) { - Write-Prompt (" $($s.FileConflictedText)$($status.Working.Unmerged.Count)") -BackgroundColor $s.WorkingBackgroundColor -ForegroundColor $s.WorkingForegroundColor + Write-Prompt (" $($s.FileConflictedText)$($status.Working.Unmerged.Count)") -BackgroundColor $s.WorkingBackgroundColor -ForegroundColor $s.WorkingForegroundColor -StringBuilder $strBld } } @@ -263,16 +337,16 @@ function Write-GitStatus($status) { } if ($localStatusSymbol) { - Write-Prompt (" {0}" -f $localStatusSymbol) -BackgroundColor $localStatusBackgroundColor -ForegroundColor $localStatusForegroundColor + Write-Prompt (" {0}" -f $localStatusSymbol) -BackgroundColor $localStatusBackgroundColor -ForegroundColor $localStatusForegroundColor -StringBuilder $strBld } if ($s.EnableStashStatus -and ($status.StashCount -gt 0)) { - Write-Prompt $s.BeforeStashText -BackgroundColor $s.BeforeStashBackgroundColor -ForegroundColor $s.BeforeStashForegroundColor - Write-Prompt $status.StashCount -BackgroundColor $s.StashBackgroundColor -ForegroundColor $s.StashForegroundColor - Write-Prompt $s.AfterStashText -BackgroundColor $s.AfterStashBackgroundColor -ForegroundColor $s.AfterStashForegroundColor + Write-Prompt $s.BeforeStashText -BackgroundColor $s.BeforeStashBackgroundColor -ForegroundColor $s.BeforeStashForegroundColor -StringBuilder $strBld + Write-Prompt $status.StashCount -BackgroundColor $s.StashBackgroundColor -ForegroundColor $s.StashForegroundColor -StringBuilder $strBld + Write-Prompt $s.AfterStashText -BackgroundColor $s.AfterStashBackgroundColor -ForegroundColor $s.AfterStashForegroundColor -StringBuilder $strBld } - Write-Prompt $s.AfterText -BackgroundColor $s.AfterBackgroundColor -ForegroundColor $s.AfterForegroundColor + Write-Prompt $s.AfterText -BackgroundColor $s.AfterBackgroundColor -ForegroundColor $s.AfterForegroundColor -StringBuilder $strBld if ($WindowTitleSupported -and $s.EnableWindowTitle) { if( -not $Global:PreviousWindowTitle ) { @@ -282,8 +356,12 @@ function Write-GitStatus($status) { $prefix = if ($s.EnableWindowTitle -is [string]) { $s.EnableWindowTitle } else { '' } $Host.UI.RawUI.WindowTitle = "$script:adminHeader$prefix$repoName [$($status.Branch)]" } - } elseif ( $Global:PreviousWindowTitle ) { + + return $strBld.ToString() + } + elseif ( $Global:PreviousWindowTitle ) { $Host.UI.RawUI.WindowTitle = $Global:PreviousWindowTitle + return "" } } diff --git a/src/posh-git.psm1 b/src/posh-git.psm1 index b54a025ce..86f82d3ef 100644 --- a/src/posh-git.psm1 +++ b/src/posh-git.psm1 @@ -2,6 +2,9 @@ param([switch]$NoVersionWarn, [switch]$ForcePoshGitPrompt) & $PSScriptRoot\CheckRequirements.ps1 > $null +Add-Type -Path $PSScriptRoot\Ansi.cs + +#. $PSScriptRoot\AnsiUtils.ps1 . $PSScriptRoot\Utils.ps1 . $PSScriptRoot\GitUtils.ps1 . $PSScriptRoot\GitPrompt.ps1 @@ -66,11 +69,11 @@ if ($ForcePoshGitPrompt -or !$currentPromptDef -or ($currentPromptDef -eq $defau $defaultPromptPrefix = [string]$GitPromptSettings.DefaultPromptPrefix if ($defaultPromptPrefix) { $expandedDefaultPromptPrefix = $ExecutionContext.SessionState.InvokeCommand.ExpandString($defaultPromptPrefix) - Write-Host $expandedDefaultPromptPrefix -NoNewline + Write-Prompt $expandedDefaultPromptPrefix # TODO: eventually handle this as a TextSpan } # Write the abbreviated current path - Write-Host $currentPath -NoNewline + Write-Prompt $currentPath # Write the Git status summary information Write-VcsStatus @@ -91,7 +94,7 @@ if ($ForcePoshGitPrompt -or !$currentPromptDef -or ($currentPromptDef -eq $defau if ($GitPromptSettings.DefaultPromptEnableTiming) { $sw.Stop() $elapsed = $sw.ElapsedMilliseconds - Write-Host " ${elapsed}ms" -NoNewline + Write-Prompt " ${elapsed}ms" } $global:LASTEXITCODE = $origLastExitCode From 6057c1b1f0838d61fddaee516f368c35d89182b5 Mon Sep 17 00:00:00 2001 From: Keith Hill Date: Sun, 5 Mar 2017 03:13:28 -0700 Subject: [PATCH 02/19] Basic working ansi & non-ansi. --- src/Ansi.cs | 69 +++++++++++++++--- src/GitPrompt.ps1 | 178 +++++++++++++++++++++------------------------- src/posh-git.psm1 | 14 ++-- 3 files changed, 148 insertions(+), 113 deletions(-) diff --git a/src/Ansi.cs b/src/Ansi.cs index 8066fd77e..effb1b58a 100644 --- a/src/Ansi.cs +++ b/src/Ansi.cs @@ -147,6 +147,20 @@ public Color(ConsoleColor consoleColor) _value = (int)consoleColor; } + public Color(string consoleColorName) + { + ConsoleColor consoleColor; + if (Enum.TryParse(consoleColorName, true, out consoleColor)) + { + _mode = ColorMode.ConsoleColor; + _value = (int)consoleColor; + } + else + { + throw new ArgumentException("Unrecognized ConsoleColor name " + consoleColorName); + } + } + public Color(byte xterm256Index) { _mode = ColorMode.XTerm256; @@ -231,6 +245,24 @@ public byte Blue } } + public override string ToString() { + switch (_mode) { + case ColorMode.ConsoleColor: + return Enum.GetName(typeof(ConsoleColor), _value); + + case ColorMode.XTerm256: + return "XTerm256: " + _value.ToString(); + + case ColorMode.Rgb: + return String.Format("RGB: 0x{0:X8}", _value); + + case ColorMode.DefaultColor: + return ""; + } + + return base.ToString(); + } + private void VerifyColorModeRgb() { if (_mode != ColorMode.Rgb) @@ -246,11 +278,18 @@ public class TextSpan { private Color _foregroundColor; private string _customAnsiSeq; - public TextSpan(string text) : this(text, new Color(), new Color()) + public TextSpan(string text) + : this(text, new Color(), new Color()) { } - public TextSpan(string text, Color foregroundColor) : this(text, foregroundColor, new Color()) + public TextSpan(string text, Color foregroundColor) + : this(text, foregroundColor, new Color()) + { + } + + public TextSpan(string text, string foregroundConsoleColorName, Color backgroundColor) + : this(text, new Color(foregroundConsoleColorName), backgroundColor) { } @@ -271,21 +310,37 @@ public TextSpan(string text, string customAnsiSeq) public string Text { get { return _text; } + set { _text = value ?? string.Empty; } } public Color ForegroundColor { get { return _foregroundColor; } + set { _foregroundColor = value ?? new Color(); } } public Color BackgroundColor { get { return _backgroundColor; } + set { _backgroundColor = value ?? new Color(); } } public string CustomAnsiSeq { get { return _customAnsiSeq; } + set { _customAnsiSeq = value ?? string.Empty; } + } + + public override string ToString() + { + if (String.IsNullOrWhiteSpace(_customAnsiSeq)) + { + return String.Format("'{0}', fg:{1}, bg:{2}", _text, _foregroundColor, _backgroundColor); + } + else + { + return String.Format("'{0}', ansi:{1}", _text, _customAnsiSeq); + } } } @@ -312,15 +367,7 @@ public override object Transform(EngineIntrinsics engineIntrinsics, object input if (inputData is string) { var consoleColorName = (string)inputData; - ConsoleColor consoleColor; - if (Enum.TryParse(consoleColorName, true, out consoleColor)) - { - return new Color(consoleColor); - } - else - { - throw new PSArgumentException("Unrecognized ConsoleColor name " + consoleColorName); - } + return new Color(consoleColorName); } if (inputData is byte) diff --git a/src/GitPrompt.ps1 b/src/GitPrompt.ps1 index 9ba9eca4f..44389e5f2 100644 --- a/src/GitPrompt.ps1 +++ b/src/GitPrompt.ps1 @@ -1,20 +1,14 @@ # Inspired by Mark Embling # http://www.markembling.info/view/my-ideal-powershell-prompt-with-git-integration +$defColor = New-Object PoshGit.Color + $global:GitPromptSettings = [pscustomobject]@{ DefaultForegroundColor = $Host.UI.RawUI.ForegroundColor - BeforeText = ' [' - BeforeForegroundColor = [ConsoleColor]::Yellow - BeforeBackgroundColor = $Host.UI.RawUI.BackgroundColor - - DelimText = ' |' - DelimForegroundColor = [ConsoleColor]::Yellow - DelimBackgroundColor = $Host.UI.RawUI.BackgroundColor - - AfterText = ']' - AfterForegroundColor = [ConsoleColor]::Yellow - AfterBackgroundColor = $Host.UI.RawUI.BackgroundColor + BeforeText = New-Object PoshGit.TextSpan -Arg ' [', Yellow, $defColor + DelimText = New-Object PoshGit.TextSpan -Arg ' |', Yellow, $defColor + AfterText = New-Object PoshGit.TextSpan -Arg ']', Yellow, $defColor FileAddedText = '+' FileModifiedText = '~' @@ -43,26 +37,20 @@ $global:GitPromptSettings = [pscustomobject]@{ BranchGoneStatusForegroundColor = [ConsoleColor]::DarkCyan BranchGoneStatusBackgroundColor = $Host.UI.RawUI.BackgroundColor - BranchIdenticalStatusToSymbol = [char]0x2261 # ≡ Three horizontal lines - BranchIdenticalStatusToForegroundColor = [ConsoleColor]::Cyan - BranchIdenticalStatusToBackgroundColor = $Host.UI.RawUI.BackgroundColor + # ≡ Three horizontal lines + BranchIdenticalStatusSymbol = New-Object PoshGit.TextSpan -Arg ([char]0x2261), Cyan, $defColor - BranchAheadStatusSymbol = [char]0x2191 # ↑ Up arrow - BranchAheadStatusForegroundColor = [ConsoleColor]::Green - BranchAheadStatusBackgroundColor = $Host.UI.RawUI.BackgroundColor + # ↑ Up arrow + BranchAheadStatusSymbol = New-Object PoshGit.TextSpan -Arg ([char]0x2191), Green, $defColor - BranchBehindStatusSymbol = [char]0x2193 # ↓ Down arrow - BranchBehindStatusForegroundColor = [ConsoleColor]::Red - BranchBehindStatusBackgroundColor = $Host.UI.RawUI.BackgroundColor + # ↓ Down arrow + BranchBehindStatusSymbol = New-Object PoshGit.TextSpan -Arg ([char]0x2193), Red, $defColor - BranchBehindAndAheadStatusSymbol = [char]0x2195 # ↕ Up & Down arrow - BranchBehindAndAheadStatusForegroundColor = [ConsoleColor]::Yellow - BranchBehindAndAheadStatusBackgroundColor = $Host.UI.RawUI.BackgroundColor + # ↕ Up & Down arrow + BranchBehindAndAheadStatusSymbol = New-Object PoshGit.TextSpan -Arg ([char]0x2195), Yellow, $defColor - BeforeIndexText = "" - BeforeIndexForegroundColor = [ConsoleColor]::DarkGreen + BeforeIndexText = New-Object PoshGit.TextSpan -Arg '', DarkGreen, $defColor BeforeIndexForegroundBrightColor = [ConsoleColor]::Green - BeforeIndexBackgroundColor = $Host.UI.RawUI.BackgroundColor IndexForegroundColor = [ConsoleColor]::DarkGreen IndexForegroundBrightColor = [ConsoleColor]::Green @@ -98,8 +86,8 @@ $global:GitPromptSettings = [pscustomobject]@{ EnableWindowTitle = 'posh~git ~ ' DefaultPromptPrefix = '' - DefaultPromptSuffix = '$(''>'' * ($nestedPromptLevel + 1)) ' - DefaultPromptDebugSuffix = ' [DBG]$(''>'' * ($nestedPromptLevel + 1)) ' + DefaultPromptSuffix = '$(''>'' * ($nestedPromptLevel + 1))' + DefaultPromptDebugSuffix = ' [DBG]$(''>'' * ($nestedPromptLevel + 1))' DefaultPromptEnableTiming = $false DefaultPromptAbbreviateHomeDirectory = $false @@ -109,6 +97,16 @@ $global:GitPromptSettings = [pscustomobject]@{ TruncatedBranchSuffix = '...' } +# Make this a function for mocking and user may not want to use ANSI even on a system that supports ANSI. +function UseAnsi { + $global:Host.UI.SupportsVirtualTerminal +} + +if (UseAnsi) { + $global:GitPromptSettings.DefaultPromptSuffix += " " + $global:GitPromptSettings.DefaultPromptDebugSuffix += " " +} + # PowerShell 5.x only runs on Windows so use .NET types to determine isAdminProcess # Or if we are on v6 or higher, check the $IsWindows pre-defined variable. if (($PSVersionTable.PSVersion.Major -le 5) -or $IsWindows) { @@ -127,72 +125,58 @@ if (Get-Module NuGet) { $WindowTitleSupported = $false } -$ansiStrBldStack = New-Object 'System.Collections.Generic.Stack[System.Text.StringBuilder]' - -function Get-AnsiStringBuilder() { - if ($ansiStrBldStack.Count -gt 0) { - $ansiStrBldStack.Peek(); - } -} - -function Push-AnsiStringBuilder([System.Text.StringBuilder]$StringBuilder = (New-Object System.Text.StringBuilder)) { - $ansiStrBldStack.Push($StringBuilder) -} - -function Pop-AnsiStringBuilder() { - if ($ansiStrBldStack.Count -gt 0) { - $ansiStrBldStack.Pop(); - } -} - function Write-Prompt { param( - [Parameter(Mandatory=$true, Position=0, ParameterSetName="TextSpan")] - [PoshGit.TextSpan] - $TextSpan, - - [Parameter(Mandatory=$true, Position=0, ParameterSetName="Object", ValueFromPipeline=$true)] + [Parameter(Mandatory=$true, Position=0, ValueFromPipeline=$true)] [Object] $Object, - [Parameter(Position=1, ParameterSetName="Object")] + [Parameter(Position=1)] [PoshGit.ColorTransformAttribute()] [PoshGit.Color] - $ForegroundColor = [PoshGit.Color]::new(), + $ForegroundColor, - [Parameter(Position=2, ParameterSetName="Object")] + [Parameter(Position=2)] [PoshGit.ColorTransformAttribute()] [PoshGit.Color] - $BackgroundColor = [PoshGit.Color]::new(), + $BackgroundColor, [Parameter()] [System.Text.StringBuilder]$StringBuilder ) process { - if ($PSCmdlet.ParameterSetName -eq 'Object') { - $TextSpan = [PoshGit.TextSpan]::new($Object, $ForegroundColor, $BackgroundColor) + if ($Object -is [PoshGit.TextSpan]) { + $textSpan = $Object + } + else { + $textSpan = New-Object PoshGit.TextSpan $Object } - if ($global:Host.UI.SupportsVirtualTerminal) { + if ($ForegroundColor) { + $textSpan.ForegroundColor = $ForegroundColor + } + + if ($BackgroundColor) { + $textSpan.BackgroundColor = $BackgroundColor + } + + if (UseAnsi) { $ansiSeq = [PoshGit.Ansi]::GetAnsiSequence($TextSpan) if ($StringBuilder) { $StringBuilder.Append($ansiSeq) > $null } - elseif ($ansiBuffer = Get-AnsiStringBuilder) { - $ansiBuffer.Append($ansiSeq) > $null - } else { $ansiSeq } } else { $params = @{Object = $textSpan.Text; NoNewLine = $true} - if ($textSpan.ForegroundColor.ColorMode() -eq [ColorMode]::ColorModeConsole) { - $params.ForegroundColor = $textSpan.ForegroundColor.ConsoleColor() + if ($textSpan.ForegroundColor.ColorMode -eq [PoshGit.ColorMode]::ConsoleColor) { + $params.ForegroundColor = $textSpan.ForegroundColor.ConsoleColor } - if ($textSpan.BackgroundColor.ColorMode() -eq [ColorMode]::ColorModeConsole) { - $params.BackgroundColor = $textSpan.BackgroundColor.ConsoleColor() + if ($textSpan.BackgroundColor.ColorMode -eq [PoshGit.ColorMode]::ConsoleColor) { + $params.BackgroundColor = $textSpan.BackgroundColor.ConsoleColor } Write-Host @params @@ -214,16 +198,13 @@ function Format-BranchName($branchName){ function Write-GitStatus($status, [System.Text.StringBuilder]$StringBuilder) { $s = $global:GitPromptSettings if ($status -and $s) { - # if ($StringBuilder) { - # Push-AnsiStringBuilder $StringBuilder - # } - # elseif (!(Get-AnsiStringBuilder)) { - # Push-AnsiStringBuilder - # } - $strBld = New-Object System.Text.StringBuilder + $strBld = $StringBuilder + if (!$strBld) { + $strBld = New-Object System.Text.StringBuilder + } - Write-Prompt $s.BeforeText -BackgroundColor $s.BeforeBackgroundColor -ForegroundColor $s.BeforeForegroundColor -StringBuilder $strBld + Write-Prompt $s.BeforeText -StringBuilder $strBld $branchStatusText = $null $branchStatusBackgroundColor = $s.BranchBackgroundColor @@ -238,38 +219,38 @@ function Write-GitStatus($status, [System.Text.StringBuilder]$StringBuilder) { $branchStatusForegroundColor = $s.BranchGoneStatusForegroundColor } elseif ($status.BehindBy -eq 0 -and $status.AheadBy -eq 0) { # We are aligned with remote - $branchStatusText = $s.BranchIdenticalStatusToSymbol - $branchStatusBackgroundColor = $s.BranchIdenticalStatusToBackgroundColor - $branchStatusForegroundColor = $s.BranchIdenticalStatusToForegroundColor + $branchStatusText = $s.BranchIdenticalStatusSymbol.Text + $branchStatusBackgroundColor = $s.BranchIdenticalStatusSymbol.BackgroundColor + $branchStatusForegroundColor = $s.BranchIdenticalStatusSymbol.ForegroundColor } elseif ($status.BehindBy -ge 1 -and $status.AheadBy -ge 1) { # We are both behind and ahead of remote if ($s.BranchBehindAndAheadDisplay -eq "Full") { - $branchStatusText = ("{0}{1} {2}{3}" -f $s.BranchBehindStatusSymbol, $status.BehindBy, $s.BranchAheadStatusSymbol, $status.AheadBy) + $branchStatusText = ("{0}{1} {2}{3}" -f $s.BranchBehindStatusSymbol.Text, $status.BehindBy, $s.BranchAheadStatusSymbol.Text, $status.AheadBy) } elseif ($s.BranchBehindAndAheadDisplay -eq "Compact") { - $branchStatusText = ("{0}{1}{2}" -f $status.BehindBy, $s.BranchBehindAndAheadStatusSymbol, $status.AheadBy) + $branchStatusText = ("{0}{1}{2}" -f $status.BehindBy, $s.BranchBehindAndAheadStatusSymbol.Text, $status.AheadBy) } else { - $branchStatusText = $s.BranchBehindAndAheadStatusSymbol + $branchStatusText = $s.BranchBehindAndAheadStatusSymbol.Text } - $branchStatusBackgroundColor = $s.BranchBehindAndAheadStatusBackgroundColor - $branchStatusForegroundColor = $s.BranchBehindAndAheadStatusForegroundColor + $branchStatusBackgroundColor = $s.BranchBehindAndAheadStatusSymbol.BackgroundColor + $branchStatusForegroundColor = $s.BranchBehindAndAheadStatusSymbol.ForegroundColor } elseif ($status.BehindBy -ge 1) { # We are behind remote if ($s.BranchBehindAndAheadDisplay -eq "Full" -Or $s.BranchBehindAndAheadDisplay -eq "Compact") { - $branchStatusText = ("{0}{1}" -f $s.BranchBehindStatusSymbol, $status.BehindBy) + $branchStatusText = ("{0}{1}" -f $s.BranchBehindStatusSymbol.Text, $status.BehindBy) } else { - $branchStatusText = $s.BranchBehindStatusSymbol + $branchStatusText = $s.BranchBehindStatusSymbol.Text } - $branchStatusBackgroundColor = $s.BranchBehindStatusBackgroundColor - $branchStatusForegroundColor = $s.BranchBehindStatusForegroundColor + $branchStatusBackgroundColor = $s.BranchBehindStatusSymbol.BackgroundColor + $branchStatusForegroundColor = $s.BranchBehindStatusSymbol.ForegroundColor } elseif ($status.AheadBy -ge 1) { # We are ahead of remote if ($s.BranchBehindAndAheadDisplay -eq "Full" -Or $s.BranchBehindAndAheadDisplay -eq "Compact") { - $branchStatusText = ("{0}{1}" -f $s.BranchAheadStatusSymbol, $status.AheadBy) + $branchStatusText = ("{0}{1}" -f $s.BranchAheadStatusSymbol.Text, $status.AheadBy) } else { - $branchStatusText = $s.BranchAheadStatusSymbol + $branchStatusText = $s.BranchAheadStatusSymbol.Text } - $branchStatusBackgroundColor = $s.BranchAheadStatusBackgroundColor - $branchStatusForegroundColor = $s.BranchAheadStatusForegroundColor + $branchStatusBackgroundColor = $s.BranchAheadStatusSymbol.BackgroundColor + $branchStatusForegroundColor = $s.BranchAheadStatusSymbol.ForegroundColor } else { # This condition should not be possible but defaulting the variables to be safe $branchStatusText = "?" @@ -282,7 +263,7 @@ function Write-GitStatus($status, [System.Text.StringBuilder]$StringBuilder) { } if($s.EnableFileStatus -and $status.HasIndex) { - Write-Prompt $s.BeforeIndexText -BackgroundColor $s.BeforeIndexBackgroundColor -ForegroundColor $s.BeforeIndexForegroundColor -StringBuilder $strBld + Write-Prompt $s.BeforeIndexText -StringBuilder $strBld if($s.ShowStatusWhenZero -or $status.Index.Added) { Write-Prompt (" $($s.FileAddedText)$($status.Index.Added.Count)") -BackgroundColor $s.IndexBackgroundColor -ForegroundColor $s.IndexForegroundColor -StringBuilder $strBld @@ -298,8 +279,8 @@ function Write-GitStatus($status, [System.Text.StringBuilder]$StringBuilder) { Write-Prompt (" $($s.FileConflictedText)$($status.Index.Unmerged.Count)") -BackgroundColor $s.IndexBackgroundColor -ForegroundColor $s.IndexForegroundColor -StringBuilder $strBld } - if($status.HasWorking) { - Write-Prompt $s.DelimText -BackgroundColor $s.DelimBackgroundColor -ForegroundColor $s.DelimForegroundColor -StringBuilder $strBld + if ($status.HasWorking) { + Write-Prompt $s.DelimText -StringBuilder $strBld } } @@ -346,7 +327,7 @@ function Write-GitStatus($status, [System.Text.StringBuilder]$StringBuilder) { Write-Prompt $s.AfterStashText -BackgroundColor $s.AfterStashBackgroundColor -ForegroundColor $s.AfterStashForegroundColor -StringBuilder $strBld } - Write-Prompt $s.AfterText -BackgroundColor $s.AfterBackgroundColor -ForegroundColor $s.AfterForegroundColor -StringBuilder $strBld + Write-Prompt $s.AfterText -StringBuilder $strBld if ($WindowTitleSupported -and $s.EnableWindowTitle) { if( -not $Global:PreviousWindowTitle ) { @@ -357,7 +338,9 @@ function Write-GitStatus($status, [System.Text.StringBuilder]$StringBuilder) { $Host.UI.RawUI.WindowTitle = "$script:adminHeader$prefix$repoName [$($status.Branch)]" } - return $strBld.ToString() + if (!$StringBuilder) { + return $strBld.ToString() + } } elseif ( $Global:PreviousWindowTitle ) { $Host.UI.RawUI.WindowTitle = $Global:PreviousWindowTitle @@ -381,14 +364,15 @@ if ($Host.UI.RawUI.BackgroundColor -eq [ConsoleColor]::DarkMagenta) { $s.WorkingForegroundColor = $s.WorkingForegroundBrightColor } -function Global:Write-VcsStatus { - $Global:VcsPromptStatuses | ForEach-Object { & $_ } +function Global:Write-VcsStatus([System.Text.StringBuilder]$StringBuilder) { + $Global:VcsPromptStatuses | ForEach-Object { & $_ $StringBuilder } } # Add scriptblock that will execute for Write-VcsStatus $PoshGitVcsPrompt = { + param([System.Text.StringBuilder]$StringBuilder) $Global:GitStatus = Get-GitStatus - Write-GitStatus $GitStatus + Write-GitStatus $GitStatus -StringBuilder $StringBuilder } $Global:VcsPromptStatuses += $PoshGitVcsPrompt diff --git a/src/posh-git.psm1 b/src/posh-git.psm1 index 86f82d3ef..4edf8249a 100644 --- a/src/posh-git.psm1 +++ b/src/posh-git.psm1 @@ -43,6 +43,8 @@ if ($ForcePoshGitPrompt -or !$currentPromptDef -or ($currentPromptDef -eq $defau } $origLastExitCode = $global:LASTEXITCODE + $strBld = New-Object System.Text.StringBuilder + # A UNC path has no drive so it's better to use the ProviderPath e.g. "\\server\share". # However for any path with a drive defined, it's better to use the Path property. # In this case, ProviderPath is "\LocalMachine\My"" whereas Path is "Cert:\LocalMachine\My". @@ -69,14 +71,14 @@ if ($ForcePoshGitPrompt -or !$currentPromptDef -or ($currentPromptDef -eq $defau $defaultPromptPrefix = [string]$GitPromptSettings.DefaultPromptPrefix if ($defaultPromptPrefix) { $expandedDefaultPromptPrefix = $ExecutionContext.SessionState.InvokeCommand.ExpandString($defaultPromptPrefix) - Write-Prompt $expandedDefaultPromptPrefix # TODO: eventually handle this as a TextSpan + Write-Prompt $expandedDefaultPromptPrefix -StringBuilder $strBld } # Write the abbreviated current path - Write-Prompt $currentPath + Write-Prompt $currentPath -StringBuilder $strBld # Write the Git status summary information - Write-VcsStatus + Write-VcsStatus -StringBuilder $strBld # If stopped in the debugger, the prompt needs to indicate that in some fashion $hasInBreakpoint = [runspace]::DefaultRunspace.Debugger | Get-Member -Name InBreakpoint -MemberType property @@ -94,11 +96,13 @@ if ($ForcePoshGitPrompt -or !$currentPromptDef -or ($currentPromptDef -eq $defau if ($GitPromptSettings.DefaultPromptEnableTiming) { $sw.Stop() $elapsed = $sw.ElapsedMilliseconds - Write-Prompt " ${elapsed}ms" + Write-Prompt " ${elapsed}ms" -StringBuilder $strBld } + Write-Prompt $expandedPromptSuffix -StringBuilder $strBld + $global:LASTEXITCODE = $origLastExitCode - $expandedPromptSuffix + if ($strBld.Length -gt 0) { $strBld.ToString() } else { " " } } # Set the posh-git prompt as the default prompt From 05170874e556c371e4848f52d6be26e2c6d24101 Mon Sep 17 00:00:00 2001 From: Keith Hill Date: Sun, 5 Mar 2017 03:22:49 -0700 Subject: [PATCH 03/19] Handle CustomAnsiSeq. --- src/Ansi.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Ansi.cs b/src/Ansi.cs index effb1b58a..3817dcbd4 100644 --- a/src/Ansi.cs +++ b/src/Ansi.cs @@ -109,6 +109,11 @@ public static string GetAnsiSequence(Color color, bool isForeground) public static string GetAnsiSequence(TextSpan textSpan) { + if (!String.IsNullOrWhiteSpace(textSpan.CustomAnsiSeq)) { + // TODO: what if someone wants custom ansi after the text? + return String.Format("{0}{1}{2}m", textSpan.CustomAnsiSeq, textSpan.Text, (int)AnsiTextOption.Default); + } + return GetAnsiSequence(textSpan.Text, textSpan.ForegroundColor, textSpan.BackgroundColor); } From fae8006fc337ab773bd3e811c9f1a968f34ea71b Mon Sep 17 00:00:00 2001 From: Keith Hill Date: Sun, 5 Mar 2017 03:24:38 -0700 Subject: [PATCH 04/19] Added comment. --- src/GitPrompt.ps1 | 1 + 1 file changed, 1 insertion(+) diff --git a/src/GitPrompt.ps1 b/src/GitPrompt.ps1 index 44389e5f2..78236fcd4 100644 --- a/src/GitPrompt.ps1 +++ b/src/GitPrompt.ps1 @@ -125,6 +125,7 @@ if (Get-Module NuGet) { $WindowTitleSupported = $false } +# TODO: Real tempted to rename this to Write-AnsiHost function Write-Prompt { param( [Parameter(Mandatory=$true, Position=0, ValueFromPipeline=$true)] From c89ea5ce12e0ade75256b8d4c639f4d4172cb21f Mon Sep 17 00:00:00 2001 From: Keith Hill Date: Sun, 5 Mar 2017 16:07:51 -0700 Subject: [PATCH 05/19] Fix Add-Type compile issue on PS Core. Also add in -Force for Add-Member. --- src/GitUtils.ps1 | 16 ++++++++-------- src/posh-git.psm1 | 9 ++++++++- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/GitUtils.ps1 b/src/GitUtils.ps1 index 1c896276c..f0076e7fa 100644 --- a/src/GitUtils.ps1 +++ b/src/GitUtils.ps1 @@ -295,16 +295,16 @@ function Get-GitStatus($gitDir = (Get-GitDirectory)) { $indexPaths = @(GetUniquePaths $indexAdded,$indexModified,$indexDeleted,$indexUnmerged) $workingPaths = @(GetUniquePaths $filesAdded,$filesModified,$filesDeleted,$filesUnmerged) $index = (,$indexPaths) | - Add-Member -PassThru NoteProperty Added $indexAdded.ToArray() | - Add-Member -PassThru NoteProperty Modified $indexModified.ToArray() | - Add-Member -PassThru NoteProperty Deleted $indexDeleted.ToArray() | - Add-Member -PassThru NoteProperty Unmerged $indexUnmerged.ToArray() + Add-Member -PassThru -Force NoteProperty Added $indexAdded.ToArray() | + Add-Member -PassThru -Force NoteProperty Modified $indexModified.ToArray() | + Add-Member -PassThru -Force NoteProperty Deleted $indexDeleted.ToArray() | + Add-Member -PassThru -Force NoteProperty Unmerged $indexUnmerged.ToArray() $working = (,$workingPaths) | - Add-Member -PassThru NoteProperty Added $filesAdded | - Add-Member -PassThru NoteProperty Modified $filesModified.ToArray() | - Add-Member -PassThru NoteProperty Deleted $filesDeleted.ToArray() | - Add-Member -PassThru NoteProperty Unmerged $filesUnmerged.ToArray() + Add-Member -PassThru -Force NoteProperty Added $filesAdded | + Add-Member -PassThru -Force NoteProperty Modified $filesModified.ToArray() | + Add-Member -PassThru -Force NoteProperty Deleted $filesDeleted.ToArray() | + Add-Member -PassThru -Force NoteProperty Unmerged $filesUnmerged.ToArray() $result = New-Object PSObject -Property @{ GitDir = $gitDir diff --git a/src/posh-git.psm1 b/src/posh-git.psm1 index 4edf8249a..f5e9ef42f 100644 --- a/src/posh-git.psm1 +++ b/src/posh-git.psm1 @@ -2,7 +2,14 @@ param([switch]$NoVersionWarn, [switch]$ForcePoshGitPrompt) & $PSScriptRoot\CheckRequirements.ps1 > $null -Add-Type -Path $PSScriptRoot\Ansi.cs +if ($PSVersionTable.PSEdition -eq 'Core') { + Add-Type -Path $PSScriptRoot\Ansi.cs -ReferencedAssemblies @( + "$PSHome\System.Console.dll", + "$PSHome\System.Management.Automation.dll") +} +else { + Add-Type -Path $PSScriptRoot\Ansi.cs +} #. $PSScriptRoot\AnsiUtils.ps1 . $PSScriptRoot\Utils.ps1 From 3ce0be62f2e76e8e653ec54fead9f72712362723 Mon Sep 17 00:00:00 2001 From: Keith Hill Date: Sun, 5 Mar 2017 17:43:13 -0700 Subject: [PATCH 06/19] Integrate in ConsoleMode native methods. --- src/ConsoleMode.cs | 60 +++++++++++++++++++++++++++++++++++++++++++++ src/ConsoleMode.ps1 | 38 ++++++++++++++++++++++++++++ src/GitPrompt.ps1 | 6 +++-- src/posh-git.psm1 | 3 ++- 4 files changed, 104 insertions(+), 3 deletions(-) create mode 100644 src/ConsoleMode.cs create mode 100644 src/ConsoleMode.ps1 diff --git a/src/ConsoleMode.cs b/src/ConsoleMode.cs new file mode 100644 index 000000000..ec91ca1df --- /dev/null +++ b/src/ConsoleMode.cs @@ -0,0 +1,60 @@ +using System; +using System.Runtime.InteropServices; + +namespace PoshGit { + [Flags] + public enum ConsoleModeInputFlags + { + ENABLE_PROCESSED_INPUT = 0x0001, + ENABLE_LINE_INPUT = 0x0002, + ENABLE_ECHO_INPUT = 0x0004, + ENABLE_WINDOW_INPUT = 0x0008, + ENABLE_MOUSE_INPUT = 0x0010, + ENABLE_INSERT_MODE = 0x0020, + ENABLE_QUICK_EDIT_MODE = 0x0040, + ENABLE_EXTENDED_FLAGS = 0x0080, + ENABLE_AUTO_POSITION = 0x0100, + ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0200, + } + + [Flags] + public enum ConsoleModeOutputFlags + { + ENABLE_PROCESSED_OUTPUT = 0x0001, + ENABLE_WRAP_AT_EOL_OUTPUT = 0x0002, + ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004, + } + + public class NativeConsoleMethods + { + [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] + public static extern IntPtr GetStdHandle(int handleId); + + [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] + public static extern bool GetConsoleMode(IntPtr hConsoleOutput, out uint dwMode); + + [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] + public static extern bool SetConsoleMode(IntPtr hConsoleOutput, uint dwMode); + + public static uint GetConsoleMode(bool input = false) + { + var handle = GetStdHandle(input ? -10 : -11); + uint mode; + if (GetConsoleMode(handle, out mode)) + { + return mode; + } + return 0xffffffff; + } + + public static uint SetConsoleMode(bool input, uint mode) + { + var handle = GetStdHandle(input ? -10 : -11); + if (SetConsoleMode(handle, mode)) + { + return GetConsoleMode(input); + } + return 0xffffffff; + } + } +} diff --git a/src/ConsoleMode.ps1 b/src/ConsoleMode.ps1 new file mode 100644 index 000000000..f82756b60 --- /dev/null +++ b/src/ConsoleMode.ps1 @@ -0,0 +1,38 @@ +# Hack! https://gist.github.com/lzybkr/f2059cb2ee8d0c13c65ab933b75e998c + +function Set-ConsoleMode +{ + [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "")] + param( + [Parameter(ParameterSetName = "ANSI")] + [switch] + $ANSI, + + [Parameter(ParameterSetName = "Mode")] + [uint32] + $Mode, + + [switch] + $StandardInput + ) + + if ($PSVersionTable.PSEdition -eq 'Core') { + return + } + + if ($ANSI) + { + $outputMode = [PoshGit.NativeConsoleMethods]::GetConsoleMode($false) + $null = [PoshGit.NativeConsoleMethods]::SetConsoleMode($false, $outputMode -bor [PoshGit.ConsoleModeOutputFlags]::ENABLE_VIRTUAL_TERMINAL_PROCESSING) + + if ($StandardInput) + { + $inputMode = [PoshGit.NativeConsoleMethods]::GetConsoleMode($true) + $null = [PoshGit.NativeConsoleMethods]::SetConsoleMode($true, $inputMode -bor [PoshGit.ConsoleModeInputFlags]::ENABLE_VIRTUAL_TERMINAL_PROCESSING) + } + } + else + { + [PoshGit.NativeConsoleMethods]::SetConsoleMode($StandardInput, $Mode) + } +} diff --git a/src/GitPrompt.ps1 b/src/GitPrompt.ps1 index 78236fcd4..c83ff7bd9 100644 --- a/src/GitPrompt.ps1 +++ b/src/GitPrompt.ps1 @@ -1,4 +1,4 @@ -# Inspired by Mark Embling +# Inspired by Mark Embling # http://www.markembling.info/view/my-ideal-powershell-prompt-with-git-integration $defColor = New-Object PoshGit.Color @@ -365,7 +365,9 @@ if ($Host.UI.RawUI.BackgroundColor -eq [ConsoleColor]::DarkMagenta) { $s.WorkingForegroundColor = $s.WorkingForegroundBrightColor } -function Global:Write-VcsStatus([System.Text.StringBuilder]$StringBuilder) { +function Global:Write-VcsStatus([System.Text.StringBuilder]$StringBuilder) { + # Is this the right place for this call? If someone uses Write-GitStatus (Get-GitStatus) they lose. + Set-ConsoleMode -ANSI $Global:VcsPromptStatuses | ForEach-Object { & $_ $StringBuilder } } diff --git a/src/posh-git.psm1 b/src/posh-git.psm1 index f5e9ef42f..1fc661515 100644 --- a/src/posh-git.psm1 +++ b/src/posh-git.psm1 @@ -8,10 +8,11 @@ if ($PSVersionTable.PSEdition -eq 'Core') { "$PSHome\System.Management.Automation.dll") } else { - Add-Type -Path $PSScriptRoot\Ansi.cs + Add-Type -Path $PSScriptRoot\Ansi.cs, $PSScriptRoot\ConsoleMode.cs } #. $PSScriptRoot\AnsiUtils.ps1 +. $PSScriptRoot\ConsoleMode.ps1 . $PSScriptRoot\Utils.ps1 . $PSScriptRoot\GitUtils.ps1 . $PSScriptRoot\GitPrompt.ps1 From 56d04309db292cb6b57612946961bf3ca4029668 Mon Sep 17 00:00:00 2001 From: Keith Hill Date: Sun, 5 Mar 2017 17:43:32 -0700 Subject: [PATCH 07/19] Update Stash settings to use TextSpan. --- src/Ansi.cs | 5 + src/GitPrompt.ps1 | 756 +++++++++++++++++++++++----------------------- 2 files changed, 382 insertions(+), 379 deletions(-) diff --git a/src/Ansi.cs b/src/Ansi.cs index 3817dcbd4..d906840a4 100644 --- a/src/Ansi.cs +++ b/src/Ansi.cs @@ -283,6 +283,11 @@ public class TextSpan { private Color _foregroundColor; private string _customAnsiSeq; + public TextSpan(TextSpan textSpan) + : this(textSpan.Text, textSpan.ForegroundColor, textSpan.BackgroundColor) + { + } + public TextSpan(string text) : this(text, new Color(), new Color()) { diff --git a/src/GitPrompt.ps1 b/src/GitPrompt.ps1 index c83ff7bd9..d8f83b08b 100644 --- a/src/GitPrompt.ps1 +++ b/src/GitPrompt.ps1 @@ -1,381 +1,379 @@ -# Inspired by Mark Embling -# http://www.markembling.info/view/my-ideal-powershell-prompt-with-git-integration - -$defColor = New-Object PoshGit.Color - -$global:GitPromptSettings = [pscustomobject]@{ - DefaultForegroundColor = $Host.UI.RawUI.ForegroundColor - - BeforeText = New-Object PoshGit.TextSpan -Arg ' [', Yellow, $defColor - DelimText = New-Object PoshGit.TextSpan -Arg ' |', Yellow, $defColor - AfterText = New-Object PoshGit.TextSpan -Arg ']', Yellow, $defColor - - FileAddedText = '+' - FileModifiedText = '~' - FileRemovedText = '-' - FileConflictedText = '!' - - LocalDefaultStatusSymbol = $null - LocalDefaultStatusForegroundColor = [ConsoleColor]::DarkGreen - LocalDefaultStatusForegroundBrightColor = [ConsoleColor]::Green - LocalDefaultStatusBackgroundColor = $Host.UI.RawUI.BackgroundColor - - LocalWorkingStatusSymbol = '!' - LocalWorkingStatusForegroundColor = [ConsoleColor]::DarkRed - LocalWorkingStatusForegroundBrightColor = [ConsoleColor]::Red - LocalWorkingStatusBackgroundColor = $Host.UI.RawUI.BackgroundColor - - LocalStagedStatusSymbol = '~' - LocalStagedStatusForegroundColor = [ConsoleColor]::Cyan - LocalStagedStatusBackgroundColor = $Host.UI.RawUI.BackgroundColor - - BranchUntrackedSymbol = $null - BranchForegroundColor = [ConsoleColor]::Cyan - BranchBackgroundColor = $Host.UI.RawUI.BackgroundColor - - BranchGoneStatusSymbol = [char]0x00D7 # × Multiplication sign - BranchGoneStatusForegroundColor = [ConsoleColor]::DarkCyan - BranchGoneStatusBackgroundColor = $Host.UI.RawUI.BackgroundColor - - # ≡ Three horizontal lines - BranchIdenticalStatusSymbol = New-Object PoshGit.TextSpan -Arg ([char]0x2261), Cyan, $defColor - - # ↑ Up arrow - BranchAheadStatusSymbol = New-Object PoshGit.TextSpan -Arg ([char]0x2191), Green, $defColor - - # ↓ Down arrow - BranchBehindStatusSymbol = New-Object PoshGit.TextSpan -Arg ([char]0x2193), Red, $defColor - - # ↕ Up & Down arrow - BranchBehindAndAheadStatusSymbol = New-Object PoshGit.TextSpan -Arg ([char]0x2195), Yellow, $defColor - - BeforeIndexText = New-Object PoshGit.TextSpan -Arg '', DarkGreen, $defColor - BeforeIndexForegroundBrightColor = [ConsoleColor]::Green - - IndexForegroundColor = [ConsoleColor]::DarkGreen - IndexForegroundBrightColor = [ConsoleColor]::Green - IndexBackgroundColor = $Host.UI.RawUI.BackgroundColor - - WorkingForegroundColor = [ConsoleColor]::DarkRed - WorkingForegroundBrightColor = [ConsoleColor]::Red - WorkingBackgroundColor = $Host.UI.RawUI.BackgroundColor - - EnableStashStatus = $false - BeforeStashText = ' (' - BeforeStashBackgroundColor = $Host.UI.RawUI.BackgroundColor - BeforeStashForegroundColor = [ConsoleColor]::Red - AfterStashText = ')' - AfterStashBackgroundColor = $Host.UI.RawUI.BackgroundColor - AfterStashForegroundColor = [ConsoleColor]::Red - StashBackgroundColor = $Host.UI.RawUI.BackgroundColor - StashForegroundColor = [ConsoleColor]::Red - - ShowStatusWhenZero = $true - - AutoRefreshIndex = $true - - # Valid values are "Full", "Compact", and "Minimal" - BranchBehindAndAheadDisplay = "Full" - - EnablePromptStatus = !$Global:GitMissing - EnableFileStatus = $true - EnableFileStatusFromCache = $null - RepositoriesInWhichToDisableFileStatus = @( ) # Array of repository paths - DescribeStyle = '' - - EnableWindowTitle = 'posh~git ~ ' - - DefaultPromptPrefix = '' - DefaultPromptSuffix = '$(''>'' * ($nestedPromptLevel + 1))' - DefaultPromptDebugSuffix = ' [DBG]$(''>'' * ($nestedPromptLevel + 1))' - DefaultPromptEnableTiming = $false - DefaultPromptAbbreviateHomeDirectory = $false - - Debug = $false - - BranchNameLimit = 0 - TruncatedBranchSuffix = '...' -} - -# Make this a function for mocking and user may not want to use ANSI even on a system that supports ANSI. -function UseAnsi { - $global:Host.UI.SupportsVirtualTerminal -} - -if (UseAnsi) { - $global:GitPromptSettings.DefaultPromptSuffix += " " - $global:GitPromptSettings.DefaultPromptDebugSuffix += " " -} - -# PowerShell 5.x only runs on Windows so use .NET types to determine isAdminProcess -# Or if we are on v6 or higher, check the $IsWindows pre-defined variable. -if (($PSVersionTable.PSVersion.Major -le 5) -or $IsWindows) { - $currentUser = [Security.Principal.WindowsPrincipal]([Security.Principal.WindowsIdentity]::GetCurrent()) - $isAdminProcess = $currentUser.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) -} -else { - # Must be Linux or OSX, so use the id util. Root has userid of 0. - $isAdminProcess = 0 -eq (id -u) -} - -$adminHeader = if ($isAdminProcess) { 'Administrator: ' } else { '' } - -$WindowTitleSupported = $true -if (Get-Module NuGet) { - $WindowTitleSupported = $false -} - -# TODO: Real tempted to rename this to Write-AnsiHost -function Write-Prompt { - param( - [Parameter(Mandatory=$true, Position=0, ValueFromPipeline=$true)] - [Object] - $Object, - - [Parameter(Position=1)] - [PoshGit.ColorTransformAttribute()] - [PoshGit.Color] - $ForegroundColor, - - [Parameter(Position=2)] - [PoshGit.ColorTransformAttribute()] - [PoshGit.Color] - $BackgroundColor, - - [Parameter()] - [System.Text.StringBuilder]$StringBuilder - ) - - process { - if ($Object -is [PoshGit.TextSpan]) { - $textSpan = $Object - } - else { - $textSpan = New-Object PoshGit.TextSpan $Object - } - - if ($ForegroundColor) { - $textSpan.ForegroundColor = $ForegroundColor - } - - if ($BackgroundColor) { - $textSpan.BackgroundColor = $BackgroundColor - } - - if (UseAnsi) { - $ansiSeq = [PoshGit.Ansi]::GetAnsiSequence($TextSpan) - if ($StringBuilder) { - $StringBuilder.Append($ansiSeq) > $null - } - else { - $ansiSeq - } - } - else { - $params = @{Object = $textSpan.Text; NoNewLine = $true} - if ($textSpan.ForegroundColor.ColorMode -eq [PoshGit.ColorMode]::ConsoleColor) { - $params.ForegroundColor = $textSpan.ForegroundColor.ConsoleColor - } - if ($textSpan.BackgroundColor.ColorMode -eq [PoshGit.ColorMode]::ConsoleColor) { - $params.BackgroundColor = $textSpan.BackgroundColor.ConsoleColor - } - - Write-Host @params - } - } -} - -function Format-BranchName($branchName){ - $s = $global:GitPromptSettings - - if($s.BranchNameLimit -gt 0 -and $branchName.Length -gt $s.BranchNameLimit) - { - $branchName = "{0}{1}" -f $branchName.Substring(0,$s.BranchNameLimit), $s.TruncatedBranchSuffix - } - - return $branchName -} - -function Write-GitStatus($status, [System.Text.StringBuilder]$StringBuilder) { - $s = $global:GitPromptSettings - if ($status -and $s) { - - $strBld = $StringBuilder - if (!$strBld) { - $strBld = New-Object System.Text.StringBuilder - } - - Write-Prompt $s.BeforeText -StringBuilder $strBld - - $branchStatusText = $null - $branchStatusBackgroundColor = $s.BranchBackgroundColor - $branchStatusForegroundColor = $s.BranchForegroundColor - - if (!$status.Upstream) { - $branchStatusText = $s.BranchUntrackedSymbol - } elseif ($status.UpstreamGone -eq $true) { - # Upstream branch is gone - $branchStatusText = $s.BranchGoneStatusSymbol - $branchStatusBackgroundColor = $s.BranchGoneStatusBackgroundColor - $branchStatusForegroundColor = $s.BranchGoneStatusForegroundColor - } elseif ($status.BehindBy -eq 0 -and $status.AheadBy -eq 0) { - # We are aligned with remote - $branchStatusText = $s.BranchIdenticalStatusSymbol.Text - $branchStatusBackgroundColor = $s.BranchIdenticalStatusSymbol.BackgroundColor - $branchStatusForegroundColor = $s.BranchIdenticalStatusSymbol.ForegroundColor - } elseif ($status.BehindBy -ge 1 -and $status.AheadBy -ge 1) { - # We are both behind and ahead of remote - if ($s.BranchBehindAndAheadDisplay -eq "Full") { - $branchStatusText = ("{0}{1} {2}{3}" -f $s.BranchBehindStatusSymbol.Text, $status.BehindBy, $s.BranchAheadStatusSymbol.Text, $status.AheadBy) - } elseif ($s.BranchBehindAndAheadDisplay -eq "Compact") { - $branchStatusText = ("{0}{1}{2}" -f $status.BehindBy, $s.BranchBehindAndAheadStatusSymbol.Text, $status.AheadBy) - } else { - $branchStatusText = $s.BranchBehindAndAheadStatusSymbol.Text - } - $branchStatusBackgroundColor = $s.BranchBehindAndAheadStatusSymbol.BackgroundColor - $branchStatusForegroundColor = $s.BranchBehindAndAheadStatusSymbol.ForegroundColor - } elseif ($status.BehindBy -ge 1) { - # We are behind remote - if ($s.BranchBehindAndAheadDisplay -eq "Full" -Or $s.BranchBehindAndAheadDisplay -eq "Compact") { - $branchStatusText = ("{0}{1}" -f $s.BranchBehindStatusSymbol.Text, $status.BehindBy) - } else { - $branchStatusText = $s.BranchBehindStatusSymbol.Text - } - $branchStatusBackgroundColor = $s.BranchBehindStatusSymbol.BackgroundColor - $branchStatusForegroundColor = $s.BranchBehindStatusSymbol.ForegroundColor - } elseif ($status.AheadBy -ge 1) { - # We are ahead of remote - if ($s.BranchBehindAndAheadDisplay -eq "Full" -Or $s.BranchBehindAndAheadDisplay -eq "Compact") { - $branchStatusText = ("{0}{1}" -f $s.BranchAheadStatusSymbol.Text, $status.AheadBy) - } else { - $branchStatusText = $s.BranchAheadStatusSymbol.Text - } - $branchStatusBackgroundColor = $s.BranchAheadStatusSymbol.BackgroundColor - $branchStatusForegroundColor = $s.BranchAheadStatusSymbol.ForegroundColor - } else { - # This condition should not be possible but defaulting the variables to be safe - $branchStatusText = "?" - } - - Write-Prompt (Format-BranchName($status.Branch)) -BackgroundColor $branchStatusBackgroundColor -ForegroundColor $branchStatusForegroundColor -StringBuilder $strBld - - if ($branchStatusText) { - Write-Prompt (" {0}" -f $branchStatusText) -BackgroundColor $branchStatusBackgroundColor -ForegroundColor $branchStatusForegroundColor -StringBuilder $strBld - } - - if($s.EnableFileStatus -and $status.HasIndex) { - Write-Prompt $s.BeforeIndexText -StringBuilder $strBld - - if($s.ShowStatusWhenZero -or $status.Index.Added) { - Write-Prompt (" $($s.FileAddedText)$($status.Index.Added.Count)") -BackgroundColor $s.IndexBackgroundColor -ForegroundColor $s.IndexForegroundColor -StringBuilder $strBld - } - if($s.ShowStatusWhenZero -or $status.Index.Modified) { - Write-Prompt (" $($s.FileModifiedText)$($status.Index.Modified.Count)") -BackgroundColor $s.IndexBackgroundColor -ForegroundColor $s.IndexForegroundColor -StringBuilder $strBld - } - if($s.ShowStatusWhenZero -or $status.Index.Deleted) { - Write-Prompt (" $($s.FileRemovedText)$($status.Index.Deleted.Count)") -BackgroundColor $s.IndexBackgroundColor -ForegroundColor $s.IndexForegroundColor -StringBuilder $strBld - } - - if ($status.Index.Unmerged) { - Write-Prompt (" $($s.FileConflictedText)$($status.Index.Unmerged.Count)") -BackgroundColor $s.IndexBackgroundColor -ForegroundColor $s.IndexForegroundColor -StringBuilder $strBld - } - - if ($status.HasWorking) { - Write-Prompt $s.DelimText -StringBuilder $strBld - } - } - - if($s.EnableFileStatus -and $status.HasWorking) { - if($s.ShowStatusWhenZero -or $status.Working.Added) { - Write-Prompt (" $($s.FileAddedText)$($status.Working.Added.Count)") -BackgroundColor $s.WorkingBackgroundColor -ForegroundColor $s.WorkingForegroundColor -StringBuilder $strBld - } - if($s.ShowStatusWhenZero -or $status.Working.Modified) { - Write-Prompt (" $($s.FileModifiedText)$($status.Working.Modified.Count)") -BackgroundColor $s.WorkingBackgroundColor -ForegroundColor $s.WorkingForegroundColor -StringBuilder $strBld - } - if($s.ShowStatusWhenZero -or $status.Working.Deleted) { - Write-Prompt (" $($s.FileRemovedText)$($status.Working.Deleted.Count)") -BackgroundColor $s.WorkingBackgroundColor -ForegroundColor $s.WorkingForegroundColor -StringBuilder $strBld - } - - if ($status.Working.Unmerged) { - Write-Prompt (" $($s.FileConflictedText)$($status.Working.Unmerged.Count)") -BackgroundColor $s.WorkingBackgroundColor -ForegroundColor $s.WorkingForegroundColor -StringBuilder $strBld - } - } - - if ($status.HasWorking) { - # We have un-staged files in the working tree - $localStatusSymbol = $s.LocalWorkingStatusSymbol - $localStatusBackgroundColor = $s.LocalWorkingStatusBackgroundColor - $localStatusForegroundColor = $s.LocalWorkingStatusForegroundColor - } elseif ($status.HasIndex) { - # We have staged but uncommited files - $localStatusSymbol = $s.LocalStagedStatusSymbol - $localStatusBackgroundColor = $s.LocalStagedStatusBackgroundColor - $localStatusForegroundColor = $s.LocalStagedStatusForegroundColor - } else { - # No uncommited changes - $localStatusSymbol = $s.LocalDefaultStatusSymbol - $localStatusBackgroundColor = $s.LocalDefaultStatusBackgroundColor - $localStatusForegroundColor = $s.LocalDefaultStatusForegroundColor - } - - if ($localStatusSymbol) { - Write-Prompt (" {0}" -f $localStatusSymbol) -BackgroundColor $localStatusBackgroundColor -ForegroundColor $localStatusForegroundColor -StringBuilder $strBld - } - - if ($s.EnableStashStatus -and ($status.StashCount -gt 0)) { - Write-Prompt $s.BeforeStashText -BackgroundColor $s.BeforeStashBackgroundColor -ForegroundColor $s.BeforeStashForegroundColor -StringBuilder $strBld - Write-Prompt $status.StashCount -BackgroundColor $s.StashBackgroundColor -ForegroundColor $s.StashForegroundColor -StringBuilder $strBld - Write-Prompt $s.AfterStashText -BackgroundColor $s.AfterStashBackgroundColor -ForegroundColor $s.AfterStashForegroundColor -StringBuilder $strBld - } - - Write-Prompt $s.AfterText -StringBuilder $strBld - - if ($WindowTitleSupported -and $s.EnableWindowTitle) { - if( -not $Global:PreviousWindowTitle ) { - $Global:PreviousWindowTitle = $Host.UI.RawUI.WindowTitle - } - $repoName = Split-Path -Leaf (Split-Path $status.GitDir) - $prefix = if ($s.EnableWindowTitle -is [string]) { $s.EnableWindowTitle } else { '' } - $Host.UI.RawUI.WindowTitle = "$script:adminHeader$prefix$repoName [$($status.Branch)]" - } - - if (!$StringBuilder) { - return $strBld.ToString() - } - } - elseif ( $Global:PreviousWindowTitle ) { - $Host.UI.RawUI.WindowTitle = $Global:PreviousWindowTitle - return "" - } -} - -if(!(Test-Path Variable:Global:VcsPromptStatuses)) { - $Global:VcsPromptStatuses = @() -} -$s = $global:GitPromptSettings - -# Override some of the normal colors if the background color is set to the default DarkMagenta. -if ($Host.UI.RawUI.BackgroundColor -eq [ConsoleColor]::DarkMagenta) { - $s.LocalDefaultStatusForegroundColor = $s.LocalDefaultStatusForegroundBrightColor - $s.LocalWorkingStatusForegroundColor = $s.LocalWorkingStatusForegroundBrightColor - - $s.BeforeIndexForegroundColor = $s.BeforeIndexForegroundBrightColor - $s.IndexForegroundColor = $s.IndexForegroundBrightColor - - $s.WorkingForegroundColor = $s.WorkingForegroundBrightColor -} - +# Inspired by Mark Embling +# http://www.markembling.info/view/my-ideal-powershell-prompt-with-git-integration + +$defColor = New-Object PoshGit.Color + +$global:GitPromptSettings = [pscustomobject]@{ + DefaultForegroundColor = $Host.UI.RawUI.ForegroundColor + + BeforeText = New-Object PoshGit.TextSpan -Arg ' [', Yellow, $defColor + DelimText = New-Object PoshGit.TextSpan -Arg ' |', Yellow, $defColor + AfterText = New-Object PoshGit.TextSpan -Arg ']', Yellow, $defColor + + FileAddedText = '+' + FileModifiedText = '~' + FileRemovedText = '-' + FileConflictedText = '!' + + LocalDefaultStatusSymbol = $null + LocalDefaultStatusForegroundColor = [ConsoleColor]::DarkGreen + LocalDefaultStatusForegroundBrightColor = [ConsoleColor]::Green + LocalDefaultStatusBackgroundColor = $Host.UI.RawUI.BackgroundColor + + LocalWorkingStatusSymbol = '!' + LocalWorkingStatusForegroundColor = [ConsoleColor]::DarkRed + LocalWorkingStatusForegroundBrightColor = [ConsoleColor]::Red + LocalWorkingStatusBackgroundColor = $Host.UI.RawUI.BackgroundColor + + LocalStagedStatusSymbol = '~' + LocalStagedStatusForegroundColor = [ConsoleColor]::Cyan + LocalStagedStatusBackgroundColor = $Host.UI.RawUI.BackgroundColor + + BranchUntrackedSymbol = $null + BranchForegroundColor = [ConsoleColor]::Cyan + BranchBackgroundColor = $Host.UI.RawUI.BackgroundColor + + BranchGoneStatusSymbol = [char]0x00D7 # × Multiplication sign + BranchGoneStatusForegroundColor = [ConsoleColor]::DarkCyan + BranchGoneStatusBackgroundColor = $Host.UI.RawUI.BackgroundColor + + # ≡ Three horizontal lines + BranchIdenticalStatusSymbol = New-Object PoshGit.TextSpan -Arg ([char]0x2261), Cyan, $defColor + + # ↑ Up arrow + BranchAheadStatusSymbol = New-Object PoshGit.TextSpan -Arg ([char]0x2191), Green, $defColor + + # ↓ Down arrow + BranchBehindStatusSymbol = New-Object PoshGit.TextSpan -Arg ([char]0x2193), Red, $defColor + + # ↕ Up & Down arrow + BranchBehindAndAheadStatusSymbol = New-Object PoshGit.TextSpan -Arg ([char]0x2195), Yellow, $defColor + + BeforeIndexText = New-Object PoshGit.TextSpan -Arg '', DarkGreen, $defColor + BeforeIndexForegroundBrightColor = [ConsoleColor]::Green + + IndexForegroundColor = [ConsoleColor]::DarkGreen + IndexForegroundBrightColor = [ConsoleColor]::Green + IndexBackgroundColor = $Host.UI.RawUI.BackgroundColor + + WorkingForegroundColor = [ConsoleColor]::DarkRed + WorkingForegroundBrightColor = [ConsoleColor]::Red + WorkingBackgroundColor = $Host.UI.RawUI.BackgroundColor + + EnableStashStatus = $false + BeforeStashText = New-Object PoshGit.TextSpan -Arg ' (', Red, $defColor + AfterStashText = New-Object PoshGit.TextSpan -Arg ')', Red, $defColor + StashTextColor = New-Object PoshGit.TextSpan -Arg '', Red, $defColor + + ShowStatusWhenZero = $true + + AutoRefreshIndex = $true + + # Valid values are "Full", "Compact", and "Minimal" + BranchBehindAndAheadDisplay = "Full" + + EnablePromptStatus = !$Global:GitMissing + EnableFileStatus = $true + EnableFileStatusFromCache = $null + RepositoriesInWhichToDisableFileStatus = @( ) # Array of repository paths + DescribeStyle = '' + + EnableWindowTitle = 'posh~git ~ ' + + DefaultPromptPrefix = '' + DefaultPromptSuffix = '$(''>'' * ($nestedPromptLevel + 1))' + DefaultPromptDebugSuffix = ' [DBG]$(''>'' * ($nestedPromptLevel + 1))' + DefaultPromptEnableTiming = $false + DefaultPromptAbbreviateHomeDirectory = $false + + Debug = $false + + BranchNameLimit = 0 + TruncatedBranchSuffix = '...' +} + +# Make this a function for mocking and user may not want to use ANSI even on a system that supports ANSI. +function UseAnsi { + $global:Host.UI.SupportsVirtualTerminal +} + +if (UseAnsi) { + $global:GitPromptSettings.DefaultPromptSuffix += " " + $global:GitPromptSettings.DefaultPromptDebugSuffix += " " +} + +# PowerShell 5.x only runs on Windows so use .NET types to determine isAdminProcess +# Or if we are on v6 or higher, check the $IsWindows pre-defined variable. +if (($PSVersionTable.PSVersion.Major -le 5) -or $IsWindows) { + $currentUser = [Security.Principal.WindowsPrincipal]([Security.Principal.WindowsIdentity]::GetCurrent()) + $isAdminProcess = $currentUser.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) +} +else { + # Must be Linux or OSX, so use the id util. Root has userid of 0. + $isAdminProcess = 0 -eq (id -u) +} + +$adminHeader = if ($isAdminProcess) { 'Administrator: ' } else { '' } + +$WindowTitleSupported = $true +if (Get-Module NuGet) { + $WindowTitleSupported = $false +} + +# TODO: Real tempted to rename this to Write-AnsiHost +function Write-Prompt { + param( + [Parameter(Mandatory=$true, Position=0, ValueFromPipeline=$true)] + [Object] + $Object, + + [Parameter(Position=1)] + [PoshGit.ColorTransformAttribute()] + [PoshGit.Color] + $ForegroundColor, + + [Parameter(Position=2)] + [PoshGit.ColorTransformAttribute()] + [PoshGit.Color] + $BackgroundColor, + + [Parameter()] + [System.Text.StringBuilder]$StringBuilder + ) + + process { + if ($Object -is [PoshGit.TextSpan]) { + $textSpan = $Object + } + else { + $textSpan = New-Object PoshGit.TextSpan $Object + } + + if ($ForegroundColor) { + $textSpan.ForegroundColor = $ForegroundColor + } + + if ($BackgroundColor) { + $textSpan.BackgroundColor = $BackgroundColor + } + + if (UseAnsi) { + $ansiSeq = [PoshGit.Ansi]::GetAnsiSequence($TextSpan) + if ($StringBuilder) { + $StringBuilder.Append($ansiSeq) > $null + } + else { + $ansiSeq + } + } + else { + $params = @{Object = $textSpan.Text; NoNewLine = $true} + if ($textSpan.ForegroundColor.ColorMode -eq [PoshGit.ColorMode]::ConsoleColor) { + $params.ForegroundColor = $textSpan.ForegroundColor.ConsoleColor + } + if ($textSpan.BackgroundColor.ColorMode -eq [PoshGit.ColorMode]::ConsoleColor) { + $params.BackgroundColor = $textSpan.BackgroundColor.ConsoleColor + } + + Write-Host @params + } + } +} + +function Format-BranchName($branchName){ + $s = $global:GitPromptSettings + + if($s.BranchNameLimit -gt 0 -and $branchName.Length -gt $s.BranchNameLimit) + { + $branchName = "{0}{1}" -f $branchName.Substring(0,$s.BranchNameLimit), $s.TruncatedBranchSuffix + } + + return $branchName +} + +function Write-GitStatus($status, [System.Text.StringBuilder]$StringBuilder) { + $s = $global:GitPromptSettings + if ($status -and $s) { + + $strBld = $StringBuilder + if (!$strBld) { + $strBld = New-Object System.Text.StringBuilder + } + + Write-Prompt $s.BeforeText -StringBuilder $strBld + + $branchStatusText = $null + $branchStatusBackgroundColor = $s.BranchBackgroundColor + $branchStatusForegroundColor = $s.BranchForegroundColor + + if (!$status.Upstream) { + $branchStatusText = $s.BranchUntrackedSymbol + } elseif ($status.UpstreamGone -eq $true) { + # Upstream branch is gone + $branchStatusText = $s.BranchGoneStatusSymbol + $branchStatusBackgroundColor = $s.BranchGoneStatusBackgroundColor + $branchStatusForegroundColor = $s.BranchGoneStatusForegroundColor + } elseif ($status.BehindBy -eq 0 -and $status.AheadBy -eq 0) { + # We are aligned with remote + $branchStatusText = $s.BranchIdenticalStatusSymbol.Text + $branchStatusBackgroundColor = $s.BranchIdenticalStatusSymbol.BackgroundColor + $branchStatusForegroundColor = $s.BranchIdenticalStatusSymbol.ForegroundColor + } elseif ($status.BehindBy -ge 1 -and $status.AheadBy -ge 1) { + # We are both behind and ahead of remote + if ($s.BranchBehindAndAheadDisplay -eq "Full") { + $branchStatusText = ("{0}{1} {2}{3}" -f $s.BranchBehindStatusSymbol.Text, $status.BehindBy, $s.BranchAheadStatusSymbol.Text, $status.AheadBy) + } elseif ($s.BranchBehindAndAheadDisplay -eq "Compact") { + $branchStatusText = ("{0}{1}{2}" -f $status.BehindBy, $s.BranchBehindAndAheadStatusSymbol.Text, $status.AheadBy) + } else { + $branchStatusText = $s.BranchBehindAndAheadStatusSymbol.Text + } + $branchStatusBackgroundColor = $s.BranchBehindAndAheadStatusSymbol.BackgroundColor + $branchStatusForegroundColor = $s.BranchBehindAndAheadStatusSymbol.ForegroundColor + } elseif ($status.BehindBy -ge 1) { + # We are behind remote + if ($s.BranchBehindAndAheadDisplay -eq "Full" -Or $s.BranchBehindAndAheadDisplay -eq "Compact") { + $branchStatusText = ("{0}{1}" -f $s.BranchBehindStatusSymbol.Text, $status.BehindBy) + } else { + $branchStatusText = $s.BranchBehindStatusSymbol.Text + } + $branchStatusBackgroundColor = $s.BranchBehindStatusSymbol.BackgroundColor + $branchStatusForegroundColor = $s.BranchBehindStatusSymbol.ForegroundColor + } elseif ($status.AheadBy -ge 1) { + # We are ahead of remote + if ($s.BranchBehindAndAheadDisplay -eq "Full" -Or $s.BranchBehindAndAheadDisplay -eq "Compact") { + $branchStatusText = ("{0}{1}" -f $s.BranchAheadStatusSymbol.Text, $status.AheadBy) + } else { + $branchStatusText = $s.BranchAheadStatusSymbol.Text + } + $branchStatusBackgroundColor = $s.BranchAheadStatusSymbol.BackgroundColor + $branchStatusForegroundColor = $s.BranchAheadStatusSymbol.ForegroundColor + } else { + # This condition should not be possible but defaulting the variables to be safe + $branchStatusText = "?" + } + + Write-Prompt (Format-BranchName($status.Branch)) -BackgroundColor $branchStatusBackgroundColor -ForegroundColor $branchStatusForegroundColor -StringBuilder $strBld + + if ($branchStatusText) { + Write-Prompt (" {0}" -f $branchStatusText) -BackgroundColor $branchStatusBackgroundColor -ForegroundColor $branchStatusForegroundColor -StringBuilder $strBld + } + + if($s.EnableFileStatus -and $status.HasIndex) { + Write-Prompt $s.BeforeIndexText -StringBuilder $strBld + + if($s.ShowStatusWhenZero -or $status.Index.Added) { + Write-Prompt (" $($s.FileAddedText)$($status.Index.Added.Count)") -BackgroundColor $s.IndexBackgroundColor -ForegroundColor $s.IndexForegroundColor -StringBuilder $strBld + } + if($s.ShowStatusWhenZero -or $status.Index.Modified) { + Write-Prompt (" $($s.FileModifiedText)$($status.Index.Modified.Count)") -BackgroundColor $s.IndexBackgroundColor -ForegroundColor $s.IndexForegroundColor -StringBuilder $strBld + } + if($s.ShowStatusWhenZero -or $status.Index.Deleted) { + Write-Prompt (" $($s.FileRemovedText)$($status.Index.Deleted.Count)") -BackgroundColor $s.IndexBackgroundColor -ForegroundColor $s.IndexForegroundColor -StringBuilder $strBld + } + + if ($status.Index.Unmerged) { + Write-Prompt (" $($s.FileConflictedText)$($status.Index.Unmerged.Count)") -BackgroundColor $s.IndexBackgroundColor -ForegroundColor $s.IndexForegroundColor -StringBuilder $strBld + } + + if ($status.HasWorking) { + Write-Prompt $s.DelimText -StringBuilder $strBld + } + } + + if($s.EnableFileStatus -and $status.HasWorking) { + if($s.ShowStatusWhenZero -or $status.Working.Added) { + Write-Prompt (" $($s.FileAddedText)$($status.Working.Added.Count)") -BackgroundColor $s.WorkingBackgroundColor -ForegroundColor $s.WorkingForegroundColor -StringBuilder $strBld + } + if($s.ShowStatusWhenZero -or $status.Working.Modified) { + Write-Prompt (" $($s.FileModifiedText)$($status.Working.Modified.Count)") -BackgroundColor $s.WorkingBackgroundColor -ForegroundColor $s.WorkingForegroundColor -StringBuilder $strBld + } + if($s.ShowStatusWhenZero -or $status.Working.Deleted) { + Write-Prompt (" $($s.FileRemovedText)$($status.Working.Deleted.Count)") -BackgroundColor $s.WorkingBackgroundColor -ForegroundColor $s.WorkingForegroundColor -StringBuilder $strBld + } + + if ($status.Working.Unmerged) { + Write-Prompt (" $($s.FileConflictedText)$($status.Working.Unmerged.Count)") -BackgroundColor $s.WorkingBackgroundColor -ForegroundColor $s.WorkingForegroundColor -StringBuilder $strBld + } + } + + if ($status.HasWorking) { + # We have un-staged files in the working tree + $localStatusSymbol = $s.LocalWorkingStatusSymbol + $localStatusBackgroundColor = $s.LocalWorkingStatusBackgroundColor + $localStatusForegroundColor = $s.LocalWorkingStatusForegroundColor + } elseif ($status.HasIndex) { + # We have staged but uncommited files + $localStatusSymbol = $s.LocalStagedStatusSymbol + $localStatusBackgroundColor = $s.LocalStagedStatusBackgroundColor + $localStatusForegroundColor = $s.LocalStagedStatusForegroundColor + } else { + # No uncommited changes + $localStatusSymbol = $s.LocalDefaultStatusSymbol + $localStatusBackgroundColor = $s.LocalDefaultStatusBackgroundColor + $localStatusForegroundColor = $s.LocalDefaultStatusForegroundColor + } + + if ($localStatusSymbol) { + Write-Prompt (" {0}" -f $localStatusSymbol) -BackgroundColor $localStatusBackgroundColor -ForegroundColor $localStatusForegroundColor -StringBuilder $strBld + } + + if ($s.EnableStashStatus -and ($status.StashCount -gt 0)) { + $stashTextSpan = New-Object PoshGit.TextSpan $s.StashTextColor + $stashTextSpan.Text = "$($status.StashCount)" + + Write-Prompt $s.BeforeStashText -StringBuilder $strBld + Write-Prompt $stashTextSpan -StringBuilder $strBld + Write-Prompt $s.AfterStashText -StringBuilder $strBld + } + + Write-Prompt $s.AfterText -StringBuilder $strBld + + if ($WindowTitleSupported -and $s.EnableWindowTitle) { + if( -not $Global:PreviousWindowTitle ) { + $Global:PreviousWindowTitle = $Host.UI.RawUI.WindowTitle + } + $repoName = Split-Path -Leaf (Split-Path $status.GitDir) + $prefix = if ($s.EnableWindowTitle -is [string]) { $s.EnableWindowTitle } else { '' } + $Host.UI.RawUI.WindowTitle = "$script:adminHeader$prefix$repoName [$($status.Branch)]" + } + + if (!$StringBuilder) { + return $strBld.ToString() + } + } + elseif ( $Global:PreviousWindowTitle ) { + $Host.UI.RawUI.WindowTitle = $Global:PreviousWindowTitle + return "" + } +} + +if (!(Test-Path Variable:Global:VcsPromptStatuses)) { + $Global:VcsPromptStatuses = @() +} +$s = $global:GitPromptSettings + +# Override some of the normal colors if the background color is set to the default DarkMagenta. +if ($Host.UI.RawUI.BackgroundColor -eq [ConsoleColor]::DarkMagenta) { + $s.LocalDefaultStatusForegroundColor = $s.LocalDefaultStatusForegroundBrightColor + $s.LocalWorkingStatusForegroundColor = $s.LocalWorkingStatusForegroundBrightColor + + $s.BeforeIndexForegroundColor = $s.BeforeIndexForegroundBrightColor + $s.IndexForegroundColor = $s.IndexForegroundBrightColor + + $s.WorkingForegroundColor = $s.WorkingForegroundBrightColor +} + function Global:Write-VcsStatus([System.Text.StringBuilder]$StringBuilder) { # Is this the right place for this call? If someone uses Write-GitStatus (Get-GitStatus) they lose. - Set-ConsoleMode -ANSI - $Global:VcsPromptStatuses | ForEach-Object { & $_ $StringBuilder } -} - -# Add scriptblock that will execute for Write-VcsStatus -$PoshGitVcsPrompt = { - param([System.Text.StringBuilder]$StringBuilder) - $Global:GitStatus = Get-GitStatus - Write-GitStatus $GitStatus -StringBuilder $StringBuilder -} - -$Global:VcsPromptStatuses += $PoshGitVcsPrompt + Set-ConsoleMode -ANSI + $Global:VcsPromptStatuses | ForEach-Object { & $_ $StringBuilder } +} + +# Add scriptblock that will execute for Write-VcsStatus +$PoshGitVcsPrompt = { + param([System.Text.StringBuilder]$StringBuilder) + $Global:GitStatus = Get-GitStatus + Write-GitStatus $GitStatus -StringBuilder $StringBuilder +} + +$Global:VcsPromptStatuses += $PoshGitVcsPrompt From 96399d0b0686b83d4c918a97ef33636d09f205ca Mon Sep 17 00:00:00 2001 From: Keith Hill Date: Sun, 5 Mar 2017 18:26:42 -0700 Subject: [PATCH 08/19] Local status settings converted to TextSpan objs. --- src/Ansi.cs | 41 ++++++++++++++++++++++++++++++++--------- src/GitPrompt.ps1 | 40 +++++++++++++--------------------------- 2 files changed, 45 insertions(+), 36 deletions(-) diff --git a/src/Ansi.cs b/src/Ansi.cs index d906840a4..bc3fe540f 100644 --- a/src/Ansi.cs +++ b/src/Ansi.cs @@ -279,12 +279,13 @@ private void VerifyColorModeRgb() public class TextSpan { private string _text; + private string _customAnsiSeq; private Color _backgroundColor; private Color _foregroundColor; - private string _customAnsiSeq; + private Color _foregroundBrightColor; public TextSpan(TextSpan textSpan) - : this(textSpan.Text, textSpan.ForegroundColor, textSpan.BackgroundColor) + : this(textSpan.Text, textSpan.ForegroundColor, textSpan.ForegroundBrightColor, textSpan.BackgroundColor) { } @@ -303,10 +304,21 @@ public TextSpan(string text, string foregroundConsoleColorName, Color background { } + public TextSpan(string text, string foregroundConsoleColorName, string foregroundBrightConsoleColorName, Color backgroundColor) + : this(text, new Color(foregroundConsoleColorName), new Color(foregroundBrightConsoleColorName), backgroundColor) + { + } + public TextSpan(string text, Color foregroundColor, Color backgroundColor) + : this(text, foregroundColor, new Color(), backgroundColor) + { + } + + public TextSpan(string text, Color foregroundColor, Color foregroundBrigthColor, Color backgroundColor) { _text = text; _foregroundColor = foregroundColor; + _foregroundBrightColor = foregroundBrigthColor; _backgroundColor = backgroundColor; _customAnsiSeq = string.Empty; } @@ -323,10 +335,10 @@ public string Text set { _text = value ?? string.Empty; } } - public Color ForegroundColor + public string CustomAnsiSeq { - get { return _foregroundColor; } - set { _foregroundColor = value ?? new Color(); } + get { return _customAnsiSeq; } + set { _customAnsiSeq = value ?? string.Empty; } } public Color BackgroundColor @@ -335,17 +347,28 @@ public Color BackgroundColor set { _backgroundColor = value ?? new Color(); } } - public string CustomAnsiSeq + public Color ForegroundColor { - get { return _customAnsiSeq; } - set { _customAnsiSeq = value ?? string.Empty; } + get { return _foregroundColor; } + set { _foregroundColor = value ?? new Color(); } + } + + public Color ForegroundBrightColor + { + get { return _foregroundBrightColor; } + set { _foregroundBrightColor = value ?? new Color(); } } public override string ToString() { if (String.IsNullOrWhiteSpace(_customAnsiSeq)) { - return String.Format("'{0}', fg:{1}, bg:{2}", _text, _foregroundColor, _backgroundColor); + if (_foregroundBrightColor.ColorMode == ColorMode.DefaultColor) + { + return String.Format("'{0}', fg:{1}, bg:{2}", _text, _foregroundColor, _backgroundColor); + } + + return String.Format("'{0}', fg:{1}, fgB:{2}, bg:{3}", _text, _foregroundColor, _foregroundBrightColor, _backgroundColor); } else { diff --git a/src/GitPrompt.ps1 b/src/GitPrompt.ps1 index d8f83b08b..9ed6fbd5a 100644 --- a/src/GitPrompt.ps1 +++ b/src/GitPrompt.ps1 @@ -15,19 +15,9 @@ $global:GitPromptSettings = [pscustomobject]@{ FileRemovedText = '-' FileConflictedText = '!' - LocalDefaultStatusSymbol = $null - LocalDefaultStatusForegroundColor = [ConsoleColor]::DarkGreen - LocalDefaultStatusForegroundBrightColor = [ConsoleColor]::Green - LocalDefaultStatusBackgroundColor = $Host.UI.RawUI.BackgroundColor - - LocalWorkingStatusSymbol = '!' - LocalWorkingStatusForegroundColor = [ConsoleColor]::DarkRed - LocalWorkingStatusForegroundBrightColor = [ConsoleColor]::Red - LocalWorkingStatusBackgroundColor = $Host.UI.RawUI.BackgroundColor - - LocalStagedStatusSymbol = '~' - LocalStagedStatusForegroundColor = [ConsoleColor]::Cyan - LocalStagedStatusBackgroundColor = $Host.UI.RawUI.BackgroundColor + LocalDefaultStatusSymbol = New-Object PoshGit.TextSpan -Arg '', DarkGreen, Green, $defColor + LocalWorkingStatusSymbol = New-Object PoshGit.TextSpan -Arg '!', DarkRed, Red, $defColor + LocalStagedStatusSymbol = New-Object PoshGit.TextSpan -Arg '~', Cyan, $defColor BranchUntrackedSymbol = $null BranchForegroundColor = [ConsoleColor]::Cyan @@ -298,23 +288,19 @@ function Write-GitStatus($status, [System.Text.StringBuilder]$StringBuilder) { if ($status.HasWorking) { # We have un-staged files in the working tree - $localStatusSymbol = $s.LocalWorkingStatusSymbol - $localStatusBackgroundColor = $s.LocalWorkingStatusBackgroundColor - $localStatusForegroundColor = $s.LocalWorkingStatusForegroundColor - } elseif ($status.HasIndex) { + $localStatusSymbol = $s.LocalWorkingStatusSymbol + } + elseif ($status.HasIndex) { # We have staged but uncommited files - $localStatusSymbol = $s.LocalStagedStatusSymbol - $localStatusBackgroundColor = $s.LocalStagedStatusBackgroundColor - $localStatusForegroundColor = $s.LocalStagedStatusForegroundColor - } else { + $localStatusSymbol = $s.LocalStagedStatusSymbol + } + else { # No uncommited changes - $localStatusSymbol = $s.LocalDefaultStatusSymbol - $localStatusBackgroundColor = $s.LocalDefaultStatusBackgroundColor - $localStatusForegroundColor = $s.LocalDefaultStatusForegroundColor + $localStatusSymbol = $s.LocalDefaultStatusSymbol } if ($localStatusSymbol) { - Write-Prompt (" {0}" -f $localStatusSymbol) -BackgroundColor $localStatusBackgroundColor -ForegroundColor $localStatusForegroundColor -StringBuilder $strBld + Write-Prompt $localStatusSymbol -StringBuilder $strBld } if ($s.EnableStashStatus -and ($status.StashCount -gt 0)) { @@ -354,8 +340,8 @@ $s = $global:GitPromptSettings # Override some of the normal colors if the background color is set to the default DarkMagenta. if ($Host.UI.RawUI.BackgroundColor -eq [ConsoleColor]::DarkMagenta) { - $s.LocalDefaultStatusForegroundColor = $s.LocalDefaultStatusForegroundBrightColor - $s.LocalWorkingStatusForegroundColor = $s.LocalWorkingStatusForegroundBrightColor + $s.LocalDefaultStatusSymbol.ForegroundColor = $s.LocalDefaultStatusSymbol.ForegroundBrightColor + $s.LocalWorkingStatusSymbol.ForegroundColor = $s.LocalWorkingStatusSymbol.ForegroundBrightColor $s.BeforeIndexForegroundColor = $s.BeforeIndexForegroundBrightColor $s.IndexForegroundColor = $s.IndexForegroundBrightColor From a1945f7a4fd6821fe4312818537ebd7724cf6b9e Mon Sep 17 00:00:00 2001 From: Keith Hill Date: Sun, 5 Mar 2017 19:08:47 -0700 Subject: [PATCH 09/19] Finished up GitPromptSetting reductions. Went from 79 to 42. Yay! Also works on PSCore on Windows. Need to test on Linux and on 24bit color Windows. --- src/GitPrompt.ps1 | 248 ++++++++++++++++++++++++---------------------- 1 file changed, 127 insertions(+), 121 deletions(-) diff --git a/src/GitPrompt.ps1 b/src/GitPrompt.ps1 index 9ed6fbd5a..6255e3a10 100644 --- a/src/GitPrompt.ps1 +++ b/src/GitPrompt.ps1 @@ -4,82 +4,69 @@ $defColor = New-Object PoshGit.Color $global:GitPromptSettings = [pscustomobject]@{ - DefaultForegroundColor = $Host.UI.RawUI.ForegroundColor + DefaultForegroundColor = $defColor - BeforeText = New-Object PoshGit.TextSpan -Arg ' [', Yellow, $defColor - DelimText = New-Object PoshGit.TextSpan -Arg ' |', Yellow, $defColor - AfterText = New-Object PoshGit.TextSpan -Arg ']', Yellow, $defColor + BeforeText = New-Object PoshGit.TextSpan -Arg ' [', Yellow, $defColor + DelimText = New-Object PoshGit.TextSpan -Arg ' |', Yellow, $defColor + AfterText = New-Object PoshGit.TextSpan -Arg ']', Yellow, $defColor - FileAddedText = '+' - FileModifiedText = '~' - FileRemovedText = '-' - FileConflictedText = '!' + FileAddedText = '+' + FileModifiedText = '~' + FileRemovedText = '-' + FileConflictedText = '!' - LocalDefaultStatusSymbol = New-Object PoshGit.TextSpan -Arg '', DarkGreen, Green, $defColor - LocalWorkingStatusSymbol = New-Object PoshGit.TextSpan -Arg '!', DarkRed, Red, $defColor - LocalStagedStatusSymbol = New-Object PoshGit.TextSpan -Arg '~', Cyan, $defColor + LocalDefaultStatusSymbol = New-Object PoshGit.TextSpan -Arg '', DarkGreen, Green, $defColor + LocalWorkingStatusSymbol = New-Object PoshGit.TextSpan -Arg '!', DarkRed, Red, $defColor + LocalStagedStatusSymbol = New-Object PoshGit.TextSpan -Arg '~', Cyan, $defColor - BranchUntrackedSymbol = $null - BranchForegroundColor = [ConsoleColor]::Cyan - BranchBackgroundColor = $Host.UI.RawUI.BackgroundColor - - BranchGoneStatusSymbol = [char]0x00D7 # × Multiplication sign - BranchGoneStatusForegroundColor = [ConsoleColor]::DarkCyan - BranchGoneStatusBackgroundColor = $Host.UI.RawUI.BackgroundColor + BranchColor = New-Object PoshGit.TextSpan -Arg '', Cyan, $defColor + BranchUntrackedSymbol = New-Object PoshGit.TextSpan -Arg '', $defColor, $defColor + # × Multiplication sign + BranchGoneStatusSymbol = New-Object PoshGit.TextSpan -Arg ([char]0x00D7), DarkCyan, $defColor # ≡ Three horizontal lines - BranchIdenticalStatusSymbol = New-Object PoshGit.TextSpan -Arg ([char]0x2261), Cyan, $defColor - + BranchIdenticalStatusSymbol = New-Object PoshGit.TextSpan -Arg ([char]0x2261), Cyan, $defColor # ↑ Up arrow - BranchAheadStatusSymbol = New-Object PoshGit.TextSpan -Arg ([char]0x2191), Green, $defColor - + BranchAheadStatusSymbol = New-Object PoshGit.TextSpan -Arg ([char]0x2191), Green, $defColor # ↓ Down arrow - BranchBehindStatusSymbol = New-Object PoshGit.TextSpan -Arg ([char]0x2193), Red, $defColor - + BranchBehindStatusSymbol = New-Object PoshGit.TextSpan -Arg ([char]0x2193), Red, $defColor # ↕ Up & Down arrow - BranchBehindAndAheadStatusSymbol = New-Object PoshGit.TextSpan -Arg ([char]0x2195), Yellow, $defColor + BranchBehindAndAheadStatusSymbol = New-Object PoshGit.TextSpan -Arg ([char]0x2195), Yellow, $defColor - BeforeIndexText = New-Object PoshGit.TextSpan -Arg '', DarkGreen, $defColor - BeforeIndexForegroundBrightColor = [ConsoleColor]::Green + BeforeIndexText = New-Object PoshGit.TextSpan -Arg '', DarkGreen, Green, $defColor + IndexColor = New-Object PoshGit.TextSpan -Arg '', DarkGreen, Green, $defColor + WorkingColor = New-Object PoshGit.TextSpan -Arg '', DarkRed, Red, $defColor - IndexForegroundColor = [ConsoleColor]::DarkGreen - IndexForegroundBrightColor = [ConsoleColor]::Green - IndexBackgroundColor = $Host.UI.RawUI.BackgroundColor + EnableStashStatus = $false + BeforeStashText = New-Object PoshGit.TextSpan -Arg ' (', Red, $defColor + AfterStashText = New-Object PoshGit.TextSpan -Arg ')', Red, $defColor + StashTextColor = New-Object PoshGit.TextSpan -Arg '', Red, $defColor - WorkingForegroundColor = [ConsoleColor]::DarkRed - WorkingForegroundBrightColor = [ConsoleColor]::Red - WorkingBackgroundColor = $Host.UI.RawUI.BackgroundColor + ShowStatusWhenZero = $true - EnableStashStatus = $false - BeforeStashText = New-Object PoshGit.TextSpan -Arg ' (', Red, $defColor - AfterStashText = New-Object PoshGit.TextSpan -Arg ')', Red, $defColor - StashTextColor = New-Object PoshGit.TextSpan -Arg '', Red, $defColor - - ShowStatusWhenZero = $true - - AutoRefreshIndex = $true + AutoRefreshIndex = $true # Valid values are "Full", "Compact", and "Minimal" - BranchBehindAndAheadDisplay = "Full" + BranchBehindAndAheadDisplay = "Full" - EnablePromptStatus = !$Global:GitMissing - EnableFileStatus = $true - EnableFileStatusFromCache = $null - RepositoriesInWhichToDisableFileStatus = @( ) # Array of repository paths - DescribeStyle = '' + EnablePromptStatus = !$Global:GitMissing + EnableFileStatus = $true + EnableFileStatusFromCache = $null + RepositoriesInWhichToDisableFileStatus = @( ) # Array of repository paths + DescribeStyle = '' - EnableWindowTitle = 'posh~git ~ ' + EnableWindowTitle = 'posh~git ~ ' - DefaultPromptPrefix = '' - DefaultPromptSuffix = '$(''>'' * ($nestedPromptLevel + 1))' - DefaultPromptDebugSuffix = ' [DBG]$(''>'' * ($nestedPromptLevel + 1))' - DefaultPromptEnableTiming = $false - DefaultPromptAbbreviateHomeDirectory = $false + DefaultPromptPrefix = '' + DefaultPromptSuffix = '$(''>'' * ($nestedPromptLevel + 1))' + DefaultPromptDebugSuffix = ' [DBG]$(''>'' * ($nestedPromptLevel + 1))' + DefaultPromptEnableTiming = $false + DefaultPromptAbbreviateHomeDirectory = $false - Debug = $false + Debug = $false - BranchNameLimit = 0 - TruncatedBranchSuffix = '...' + BranchNameLimit = 0 + TruncatedBranchSuffix = '...' } # Make this a function for mocking and user may not want to use ANSI even on a system that supports ANSI. @@ -192,77 +179,93 @@ function Write-GitStatus($status, [System.Text.StringBuilder]$StringBuilder) { Write-Prompt $s.BeforeText -StringBuilder $strBld - $branchStatusText = $null - $branchStatusBackgroundColor = $s.BranchBackgroundColor - $branchStatusForegroundColor = $s.BranchForegroundColor + $branchStatusTextSpan = New-Object PoshGit.TextSpan -ArgumentList $s.BranchColor if (!$status.Upstream) { - $branchStatusText = $s.BranchUntrackedSymbol - } elseif ($status.UpstreamGone -eq $true) { + $branchStatusTextSpan.Text = $s.BranchUntrackedSymbol.Text + } + elseif ($status.UpstreamGone -eq $true) { # Upstream branch is gone - $branchStatusText = $s.BranchGoneStatusSymbol - $branchStatusBackgroundColor = $s.BranchGoneStatusBackgroundColor - $branchStatusForegroundColor = $s.BranchGoneStatusForegroundColor - } elseif ($status.BehindBy -eq 0 -and $status.AheadBy -eq 0) { + $branchStatusTextSpan = $s.BranchGoneStatusSymbol + } + elseif ($status.BehindBy -eq 0 -and $status.AheadBy -eq 0) { # We are aligned with remote - $branchStatusText = $s.BranchIdenticalStatusSymbol.Text - $branchStatusBackgroundColor = $s.BranchIdenticalStatusSymbol.BackgroundColor - $branchStatusForegroundColor = $s.BranchIdenticalStatusSymbol.ForegroundColor - } elseif ($status.BehindBy -ge 1 -and $status.AheadBy -ge 1) { + $branchStatusTextSpan = $s.BranchIdenticalStatusSymbol + } + elseif ($status.BehindBy -ge 1 -and $status.AheadBy -ge 1) { + $branchStatusTextSpan.ForegroundColor = $s.BranchBehindAndAheadStatusSymbol.ForegroundColor + $branchStatusTextSpan.BackgroundColor = $s.BranchBehindAndAheadStatusSymbol.BackgroundColor + # We are both behind and ahead of remote if ($s.BranchBehindAndAheadDisplay -eq "Full") { - $branchStatusText = ("{0}{1} {2}{3}" -f $s.BranchBehindStatusSymbol.Text, $status.BehindBy, $s.BranchAheadStatusSymbol.Text, $status.AheadBy) - } elseif ($s.BranchBehindAndAheadDisplay -eq "Compact") { - $branchStatusText = ("{0}{1}{2}" -f $status.BehindBy, $s.BranchBehindAndAheadStatusSymbol.Text, $status.AheadBy) - } else { - $branchStatusText = $s.BranchBehindAndAheadStatusSymbol.Text + $branchStatusTextSpan.Text = "{0}{1} {2}{3}" -f $s.BranchBehindStatusSymbol.Text, $status.BehindBy, $s.BranchAheadStatusSymbol.Text, $status.AheadBy } - $branchStatusBackgroundColor = $s.BranchBehindAndAheadStatusSymbol.BackgroundColor - $branchStatusForegroundColor = $s.BranchBehindAndAheadStatusSymbol.ForegroundColor - } elseif ($status.BehindBy -ge 1) { + elseif ($s.BranchBehindAndAheadDisplay -eq "Compact") { + $branchStatusTextSpan.Text = "{0}{1}{2}" -f $status.BehindBy, $s.BranchBehindAndAheadStatusSymbol.Text, $status.AheadBy + } + else { + $branchStatusTextSpan.Text = $s.BranchBehindAndAheadStatusSymbol.Text + } + } + elseif ($status.BehindBy -ge 1) { + $branchStatusTextSpan.ForegroundColor = $s.BranchBehindStatusSymbol.ForegroundColor + $branchStatusTextSpan.BackgroundColor = $s.BranchBehindStatusSymbol.BackgroundColor + # We are behind remote if ($s.BranchBehindAndAheadDisplay -eq "Full" -Or $s.BranchBehindAndAheadDisplay -eq "Compact") { - $branchStatusText = ("{0}{1}" -f $s.BranchBehindStatusSymbol.Text, $status.BehindBy) - } else { - $branchStatusText = $s.BranchBehindStatusSymbol.Text + $branchStatusTextSpan.Text = "{0}{1}" -f $s.BranchBehindStatusSymbol.Text, $status.BehindBy } - $branchStatusBackgroundColor = $s.BranchBehindStatusSymbol.BackgroundColor - $branchStatusForegroundColor = $s.BranchBehindStatusSymbol.ForegroundColor - } elseif ($status.AheadBy -ge 1) { + else { + $branchStatusTextSpan.Text = $s.BranchBehindStatusSymbol.Text + } + } + elseif ($status.AheadBy -ge 1) { + $branchStatusTextSpan.ForegroundColor = $s.BranchAheadStatusSymbol.ForegroundColor + $branchStatusTextSpan.BackgroundColor = $s.BranchAheadStatusSymbol.BackgroundColor + # We are ahead of remote if ($s.BranchBehindAndAheadDisplay -eq "Full" -Or $s.BranchBehindAndAheadDisplay -eq "Compact") { - $branchStatusText = ("{0}{1}" -f $s.BranchAheadStatusSymbol.Text, $status.AheadBy) - } else { - $branchStatusText = $s.BranchAheadStatusSymbol.Text + $branchStatusTextSpan.Text = "{0}{1}" -f $s.BranchAheadStatusSymbol.Text, $status.AheadBy } - $branchStatusBackgroundColor = $s.BranchAheadStatusSymbol.BackgroundColor - $branchStatusForegroundColor = $s.BranchAheadStatusSymbol.ForegroundColor - } else { + else { + $branchStatusTextSpan.Text = $s.BranchAheadStatusSymbol.Text + } + } + else { # This condition should not be possible but defaulting the variables to be safe - $branchStatusText = "?" + $branchStatusTextSpan.Text = "?" } - Write-Prompt (Format-BranchName($status.Branch)) -BackgroundColor $branchStatusBackgroundColor -ForegroundColor $branchStatusForegroundColor -StringBuilder $strBld + $branchNameTextSpan = New-Object PoshGit.TextSpan -ArgumentList $branchStatusTextSpan + $branchNameTextSpan.Text = Format-BranchName($status.Branch) + Write-Prompt $branchNameTextSpan -StringBuilder $strBld - if ($branchStatusText) { - Write-Prompt (" {0}" -f $branchStatusText) -BackgroundColor $branchStatusBackgroundColor -ForegroundColor $branchStatusForegroundColor -StringBuilder $strBld + if ($branchStatusTextSpan.Text) { + Write-Prompt $branchStatusTextSpan.Text -StringBuilder $strBld } - if($s.EnableFileStatus -and $status.HasIndex) { + if ($s.EnableFileStatus -and $status.HasIndex) { Write-Prompt $s.BeforeIndexText -StringBuilder $strBld - if($s.ShowStatusWhenZero -or $status.Index.Added) { - Write-Prompt (" $($s.FileAddedText)$($status.Index.Added.Count)") -BackgroundColor $s.IndexBackgroundColor -ForegroundColor $s.IndexForegroundColor -StringBuilder $strBld + $textSpan = New-Object PoshGit.TextSpan -ArgumentList $s.IndexColor + if ($s.ShowStatusWhenZero -or $status.Index.Added) { + $textSpan.Text = " $($s.FileAddedText)$($status.Index.Added.Count)" + Write-Prompt $textSpan -StringBuilder $strBld } - if($s.ShowStatusWhenZero -or $status.Index.Modified) { - Write-Prompt (" $($s.FileModifiedText)$($status.Index.Modified.Count)") -BackgroundColor $s.IndexBackgroundColor -ForegroundColor $s.IndexForegroundColor -StringBuilder $strBld + + if ($s.ShowStatusWhenZero -or $status.Index.Modified) { + $textSpan.Text = " $($s.FileModifiedText)$($status.Index.Modified.Count)" + Write-Prompt $textSpan -StringBuilder $strBld } - if($s.ShowStatusWhenZero -or $status.Index.Deleted) { - Write-Prompt (" $($s.FileRemovedText)$($status.Index.Deleted.Count)") -BackgroundColor $s.IndexBackgroundColor -ForegroundColor $s.IndexForegroundColor -StringBuilder $strBld + + if ($s.ShowStatusWhenZero -or $status.Index.Deleted) { + $textSpan.Text = " $($s.FileRemovedText)$($status.Index.Deleted.Count)" + Write-Prompt $textSpan -StringBuilder $strBld } if ($status.Index.Unmerged) { - Write-Prompt (" $($s.FileConflictedText)$($status.Index.Unmerged.Count)") -BackgroundColor $s.IndexBackgroundColor -ForegroundColor $s.IndexForegroundColor -StringBuilder $strBld + $textSpan.Text = " $($s.FileConflictedText)$($status.Index.Unmerged.Count)" + Write-Prompt $textSpan -StringBuilder $strBld } if ($status.HasWorking) { @@ -270,33 +273,38 @@ function Write-GitStatus($status, [System.Text.StringBuilder]$StringBuilder) { } } - if($s.EnableFileStatus -and $status.HasWorking) { - if($s.ShowStatusWhenZero -or $status.Working.Added) { - Write-Prompt (" $($s.FileAddedText)$($status.Working.Added.Count)") -BackgroundColor $s.WorkingBackgroundColor -ForegroundColor $s.WorkingForegroundColor -StringBuilder $strBld + if ($s.EnableFileStatus -and $status.HasWorking) { + $textSpan = New-Object PoshGit.TextSpan -ArgumentList $s.WorkingColor + + if ($s.ShowStatusWhenZero -or $status.Working.Added) { + $textSpan.Text = " $($s.FileAddedText)$($status.Working.Added.Count)" + Write-Prompt $textSpan -StringBuilder $strBld } - if($s.ShowStatusWhenZero -or $status.Working.Modified) { - Write-Prompt (" $($s.FileModifiedText)$($status.Working.Modified.Count)") -BackgroundColor $s.WorkingBackgroundColor -ForegroundColor $s.WorkingForegroundColor -StringBuilder $strBld + + if ($s.ShowStatusWhenZero -or $status.Working.Modified) { + $textSpan.Text = " $($s.FileModifiedText)$($status.Working.Modified.Count)" + Write-Prompt $textSpan -StringBuilder $strBld } - if($s.ShowStatusWhenZero -or $status.Working.Deleted) { - Write-Prompt (" $($s.FileRemovedText)$($status.Working.Deleted.Count)") -BackgroundColor $s.WorkingBackgroundColor -ForegroundColor $s.WorkingForegroundColor -StringBuilder $strBld + + if ($s.ShowStatusWhenZero -or $status.Working.Deleted) { + $textSpan.Text = " $($s.FileRemovedText)$($status.Working.Deleted.Count)" + Write-Prompt $textSpan -StringBuilder $strBld } if ($status.Working.Unmerged) { - Write-Prompt (" $($s.FileConflictedText)$($status.Working.Unmerged.Count)") -BackgroundColor $s.WorkingBackgroundColor -ForegroundColor $s.WorkingForegroundColor -StringBuilder $strBld + $textSpan.Text = " $($s.FileConflictedText)$($status.Working.Unmerged.Count)" + Write-Prompt $textSpan -StringBuilder $strBld } } if ($status.HasWorking) { - # We have un-staged files in the working tree - $localStatusSymbol = $s.LocalWorkingStatusSymbol + $localStatusSymbol = $s.LocalWorkingStatusSymbol # We have un-staged files in the working tree } elseif ($status.HasIndex) { - # We have staged but uncommited files - $localStatusSymbol = $s.LocalStagedStatusSymbol + $localStatusSymbol = $s.LocalStagedStatusSymbol # We have staged but uncommited files } else { - # No uncommited changes - $localStatusSymbol = $s.LocalDefaultStatusSymbol + $localStatusSymbol = $s.LocalDefaultStatusSymbol # No uncommited changes } if ($localStatusSymbol) { @@ -327,7 +335,7 @@ function Write-GitStatus($status, [System.Text.StringBuilder]$StringBuilder) { return $strBld.ToString() } } - elseif ( $Global:PreviousWindowTitle ) { + elseif ($Global:PreviousWindowTitle) { $Host.UI.RawUI.WindowTitle = $Global:PreviousWindowTitle return "" } @@ -342,11 +350,9 @@ $s = $global:GitPromptSettings if ($Host.UI.RawUI.BackgroundColor -eq [ConsoleColor]::DarkMagenta) { $s.LocalDefaultStatusSymbol.ForegroundColor = $s.LocalDefaultStatusSymbol.ForegroundBrightColor $s.LocalWorkingStatusSymbol.ForegroundColor = $s.LocalWorkingStatusSymbol.ForegroundBrightColor - - $s.BeforeIndexForegroundColor = $s.BeforeIndexForegroundBrightColor - $s.IndexForegroundColor = $s.IndexForegroundBrightColor - - $s.WorkingForegroundColor = $s.WorkingForegroundBrightColor + $s.BeforeIndexText.ForegroundColor = $s.BeforeIndexText.ForegroundBrightColor + $s.IndexColor.ForegroundColor = $s.IndexColor.ForegroundBrightColor + $s.WorkingColor.ForegroundColor = $s.WorkingColor.ForegroundBrightColor } function Global:Write-VcsStatus([System.Text.StringBuilder]$StringBuilder) { From c5ebb5595a5c5403ee943fd205a3954e1ba2aa5a Mon Sep 17 00:00:00 2001 From: Keith Hill Date: Sun, 5 Mar 2017 19:21:54 -0700 Subject: [PATCH 10/19] Put back space before branch status. --- src/GitPrompt.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/GitPrompt.ps1 b/src/GitPrompt.ps1 index 6255e3a10..09bdf5696 100644 --- a/src/GitPrompt.ps1 +++ b/src/GitPrompt.ps1 @@ -241,7 +241,7 @@ function Write-GitStatus($status, [System.Text.StringBuilder]$StringBuilder) { Write-Prompt $branchNameTextSpan -StringBuilder $strBld if ($branchStatusTextSpan.Text) { - Write-Prompt $branchStatusTextSpan.Text -StringBuilder $strBld + Write-Prompt " $($branchStatusTextSpan.Text)" -StringBuilder $strBld } if ($s.EnableFileStatus -and $status.HasIndex) { From 1a8bf921d2adde98d4529bdfbf9e9cf9efc5fc7f Mon Sep 17 00:00:00 2001 From: Keith Hill Date: Sun, 5 Mar 2017 20:13:16 -0700 Subject: [PATCH 11/19] Fix branch not showing w/correct color. Also tweak to get 24-bit color to work on my Windows 10 Insider build. --- src/Ansi.cs | 23 ++++++++++++++++++++--- src/GitPrompt.ps1 | 6 +++--- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/src/Ansi.cs b/src/Ansi.cs index bc3fe540f..bcf869f26 100644 --- a/src/Ansi.cs +++ b/src/Ansi.cs @@ -77,7 +77,7 @@ public static class Ansi { public static string GetAnsiSequence(Color color, bool isForeground) { - var extended = isForeground ? AnsiTextOption.FgExtended : AnsiTextOption.BgExtended; + var extended = (int)(isForeground ? AnsiTextOption.FgExtended : AnsiTextOption.BgExtended); var sgrSubSeq256 = "5"; var sgrSubSeqRgb = "2"; @@ -119,12 +119,29 @@ public static string GetAnsiSequence(TextSpan textSpan) public static string GetAnsiSequence(string text, Color foregroundColor, Color backgroundColor) { + string ansiSeq; string fgSeq = GetAnsiSequence(foregroundColor, true); string bgSeq = GetAnsiSequence(backgroundColor, false); - var ansiSeq = String.Format("\x1b[{0};{1}m{2}\x1b[{3}m", fgSeq, bgSeq, text, (int)AnsiTextOption.Default); - return ansiSeq; + if ((foregroundColor.ColorMode == ColorMode.DefaultColor) && + (backgroundColor.ColorMode == ColorMode.DefaultColor)) + { + ansiSeq = text; + } + else if (foregroundColor.ColorMode == ColorMode.DefaultColor) + { + ansiSeq = String.Format("\x1b[{0}m{1}\x1b[{2}m", bgSeq, text, (int)AnsiTextOption.Default); + } + else if (backgroundColor.ColorMode == ColorMode.DefaultColor) + { + ansiSeq = String.Format("\x1b[{0}m{1}\x1b[{2}m", fgSeq, text, (int)AnsiTextOption.Default); + } + else + { + ansiSeq = String.Format("\x1b[{0}m\x1b[{1}m{2}\x1b[{3}m", fgSeq, bgSeq, text, (int)AnsiTextOption.Default); + } + return ansiSeq; } } diff --git a/src/GitPrompt.ps1 b/src/GitPrompt.ps1 index 09bdf5696..f95831adc 100644 --- a/src/GitPrompt.ps1 +++ b/src/GitPrompt.ps1 @@ -233,15 +233,15 @@ function Write-GitStatus($status, [System.Text.StringBuilder]$StringBuilder) { } else { # This condition should not be possible but defaulting the variables to be safe - $branchStatusTextSpan.Text = "?" + $branchStatusTextSpan.Text = "?" } $branchNameTextSpan = New-Object PoshGit.TextSpan -ArgumentList $branchStatusTextSpan - $branchNameTextSpan.Text = Format-BranchName($status.Branch) + $branchNameTextSpan.Text = "$(Format-BranchName($status.Branch)) " Write-Prompt $branchNameTextSpan -StringBuilder $strBld if ($branchStatusTextSpan.Text) { - Write-Prompt " $($branchStatusTextSpan.Text)" -StringBuilder $strBld + Write-Prompt $branchStatusTextSpan -StringBuilder $strBld } if ($s.EnableFileStatus -and $status.HasIndex) { From 01652c256d85a0868e950d3786d0764d087f90ee Mon Sep 17 00:00:00 2001 From: Keith Hill Date: Sun, 5 Mar 2017 20:47:42 -0700 Subject: [PATCH 12/19] Use path dir sep that works for Linux for ref assm --- src/posh-git.psm1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/posh-git.psm1 b/src/posh-git.psm1 index 1fc661515..fac7e7e67 100644 --- a/src/posh-git.psm1 +++ b/src/posh-git.psm1 @@ -4,8 +4,8 @@ param([switch]$NoVersionWarn, [switch]$ForcePoshGitPrompt) if ($PSVersionTable.PSEdition -eq 'Core') { Add-Type -Path $PSScriptRoot\Ansi.cs -ReferencedAssemblies @( - "$PSHome\System.Console.dll", - "$PSHome\System.Management.Automation.dll") + "$PSHome/System.Console.dll", + "$PSHome/System.Management.Automation.dll") } else { Add-Type -Path $PSScriptRoot\Ansi.cs, $PSScriptRoot\ConsoleMode.cs From 4b5e514e4957d431fddb81923aa5f31ca1f458bf Mon Sep 17 00:00:00 2001 From: Keith Hill Date: Mon, 6 Mar 2017 21:32:03 -0700 Subject: [PATCH 13/19] Don't add extra space when no branch name. --- src/GitPrompt.ps1 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/GitPrompt.ps1 b/src/GitPrompt.ps1 index f95831adc..f98cf8d46 100644 --- a/src/GitPrompt.ps1 +++ b/src/GitPrompt.ps1 @@ -237,10 +237,11 @@ function Write-GitStatus($status, [System.Text.StringBuilder]$StringBuilder) { } $branchNameTextSpan = New-Object PoshGit.TextSpan -ArgumentList $branchStatusTextSpan - $branchNameTextSpan.Text = "$(Format-BranchName($status.Branch)) " + $branchNameTextSpan.Text = Format-BranchName($status.Branch) Write-Prompt $branchNameTextSpan -StringBuilder $strBld if ($branchStatusTextSpan.Text) { + $branchStatusTextSpan.Text = " " + $branchStatusTextSpan.Text Write-Prompt $branchStatusTextSpan -StringBuilder $strBld } From bb4647ffafa51ec69c3dc2004b06f0079af628ec Mon Sep 17 00:00:00 2001 From: Keith Hill Date: Mon, 6 Mar 2017 21:33:48 -0700 Subject: [PATCH 14/19] Remove unnecessary parens. --- src/GitPrompt.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/GitPrompt.ps1 b/src/GitPrompt.ps1 index f98cf8d46..c7bcb00d3 100644 --- a/src/GitPrompt.ps1 +++ b/src/GitPrompt.ps1 @@ -237,7 +237,7 @@ function Write-GitStatus($status, [System.Text.StringBuilder]$StringBuilder) { } $branchNameTextSpan = New-Object PoshGit.TextSpan -ArgumentList $branchStatusTextSpan - $branchNameTextSpan.Text = Format-BranchName($status.Branch) + $branchNameTextSpan.Text = Format-BranchName $status.Branch Write-Prompt $branchNameTextSpan -StringBuilder $strBld if ($branchStatusTextSpan.Text) { From dcceea11c25e95a152fb23b707614a0a984e083a Mon Sep 17 00:00:00 2001 From: Keith Hill Date: Mon, 6 Mar 2017 22:41:05 -0700 Subject: [PATCH 15/19] Add IEquatable<>, hashcode support to Color. --- src/Ansi.cs | 79 ++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 54 insertions(+), 25 deletions(-) diff --git a/src/Ansi.cs b/src/Ansi.cs index bcf869f26..8a4b9069d 100644 --- a/src/Ansi.cs +++ b/src/Ansi.cs @@ -145,7 +145,9 @@ public static string GetAnsiSequence(string text, Color foregroundColor, Color b } } - public class Color + // Thinking about renaming this to AnsiColor to prevent collisions with System.Drawing.Color, as I would + // like to add a ctor that takes a System.Drawing.Color.AntiqueWhite, etc. + public class Color : IEquatable { private ColorMode _mode; private int _value; @@ -166,7 +168,7 @@ public Color(Color color) public Color(ConsoleColor consoleColor) { _mode = ColorMode.ConsoleColor; - _value = (int)consoleColor; + _value = (int)consoleColor << 24; } public Color(string consoleColorName) @@ -175,7 +177,7 @@ public Color(string consoleColorName) if (Enum.TryParse(consoleColorName, true, out consoleColor)) { _mode = ColorMode.ConsoleColor; - _value = (int)consoleColor; + _value = (int)consoleColor << 24; } else { @@ -186,7 +188,7 @@ public Color(string consoleColorName) public Color(byte xterm256Index) { _mode = ColorMode.XTerm256; - _value = xterm256Index; + _value = xterm256Index << 24; } public Color(byte red, byte green, byte blue) @@ -209,12 +211,8 @@ public ConsoleColor ConsoleColor { get { - if (_mode != ColorMode.ConsoleColor) - { - throw new InvalidOperationException("ConsoleColor is only valid when ColorMode is set to ConsoleColor."); - } - - return (ConsoleColor)_value; + VerifyColorMode(ColorMode.ConsoleColor, "ConsoleColor"); + return (ConsoleColor)(_value >> 24); } } @@ -222,12 +220,8 @@ public byte XTerm256Index { get { - if (_mode != ColorMode.XTerm256) - { - throw new InvalidOperationException("XTerm256Index is only valid when ColorMode is set to XTerm256."); - } - - return (byte)_value; + VerifyColorMode(ColorMode.XTerm256, "XTerm256Index"); + return (byte)(_value >> 24); } } @@ -235,7 +229,7 @@ public int Rgb { get { - VerifyColorModeRgb(); + VerifyColorMode(ColorMode.Rgb, "Rgb"); return _value; } } @@ -244,7 +238,7 @@ public byte Red { get { - VerifyColorModeRgb(); + VerifyColorMode(ColorMode.Rgb, "Red"); return (byte)((_value & 0x00FF0000) >> 16); } } @@ -253,7 +247,7 @@ public byte Green { get { - VerifyColorModeRgb(); + VerifyColorMode(ColorMode.Rgb, "Green"); return (byte)((_value & 0x0000FF00) >> 8); } } @@ -262,7 +256,7 @@ public byte Blue { get { - VerifyColorModeRgb(); + VerifyColorMode(ColorMode.Rgb, "Blue"); return (byte)(_value & 0x000000FF); } } @@ -270,10 +264,10 @@ public byte Blue public override string ToString() { switch (_mode) { case ColorMode.ConsoleColor: - return Enum.GetName(typeof(ConsoleColor), _value); + return Enum.GetName(typeof(ConsoleColor), this.ConsoleColor); case ColorMode.XTerm256: - return "XTerm256: " + _value.ToString(); + return "XTerm256: " + this.XTerm256Index.ToString(); case ColorMode.Rgb: return String.Format("RGB: 0x{0:X8}", _value); @@ -285,11 +279,46 @@ public override string ToString() { return base.ToString(); } - private void VerifyColorModeRgb() + public static bool operator ==(Color left, Color right) + { + return (left._value == right._value) && (left._mode == right._mode); + } + + public static bool operator !=(Color left, Color right) + { + return !(left == right); + } + + public override bool Equals(object obj) + { + return (obj is Color) && Equals((Color)obj); + } + + public bool Equals(Color other) + { + return this == other; + } + + public override int GetHashCode() + { + // For RGB and DefaultColor (-1 / 0xFFFFFFFF) _value is unique + if (_mode == ColorMode.Rgb || _mode == ColorMode.DefaultColor) + { + return _value; + } + + // For ConsoleColor / XTerm256, where value is pushed up to 0xFF000000, we just combine + // the value with the mode in the lower nibble 0xFF0000FF to get a unique number. + return (int)(_value & 0xFF000000) | (int)_mode; + } + + private void VerifyColorMode(ColorMode colorMode, string methodName) { - if (_mode != ColorMode.Rgb) + if (_mode != colorMode) { - throw new InvalidOperationException("Rgb is only valid when ColorMode is set to Rgb."); + string colorModeName = Enum.GetName(typeof(ColorMode), colorMode); + var msg = String.Format("{0} is on valid when the ColorMode is {1}.", methodName, colorModeName); + throw new InvalidOperationException(msg); } } } From 648d42f7209a94e64ef3a03a08dc695e9477f24e Mon Sep 17 00:00:00 2001 From: Keith Hill Date: Mon, 6 Mar 2017 22:55:09 -0700 Subject: [PATCH 16/19] Remove ForegroundBrightColor - yay! The handling for DarkMagenta happens now by just assigning former FgBrightColors to the corresponding FgColor *if* the bg is DarkMagenta. This isn't dynamic anymore but the user can always change the fg colors. --- src/Ansi.cs | 29 +++------------------------- src/GitPrompt.ps1 | 48 +++++++++++++++++++++++------------------------ 2 files changed, 27 insertions(+), 50 deletions(-) diff --git a/src/Ansi.cs b/src/Ansi.cs index 8a4b9069d..b3c88a40f 100644 --- a/src/Ansi.cs +++ b/src/Ansi.cs @@ -328,10 +328,9 @@ public class TextSpan { private string _customAnsiSeq; private Color _backgroundColor; private Color _foregroundColor; - private Color _foregroundBrightColor; public TextSpan(TextSpan textSpan) - : this(textSpan.Text, textSpan.ForegroundColor, textSpan.ForegroundBrightColor, textSpan.BackgroundColor) + : this(textSpan.Text, textSpan.ForegroundColor, textSpan.BackgroundColor) { } @@ -350,21 +349,10 @@ public TextSpan(string text, string foregroundConsoleColorName, Color background { } - public TextSpan(string text, string foregroundConsoleColorName, string foregroundBrightConsoleColorName, Color backgroundColor) - : this(text, new Color(foregroundConsoleColorName), new Color(foregroundBrightConsoleColorName), backgroundColor) - { - } - - public TextSpan(string text, Color foregroundColor, Color backgroundColor) - : this(text, foregroundColor, new Color(), backgroundColor) - { - } - - public TextSpan(string text, Color foregroundColor, Color foregroundBrigthColor, Color backgroundColor) + public TextSpan(string text, Color foregroundColor,Color backgroundColor) { _text = text; _foregroundColor = foregroundColor; - _foregroundBrightColor = foregroundBrigthColor; _backgroundColor = backgroundColor; _customAnsiSeq = string.Empty; } @@ -399,22 +387,11 @@ public Color ForegroundColor set { _foregroundColor = value ?? new Color(); } } - public Color ForegroundBrightColor - { - get { return _foregroundBrightColor; } - set { _foregroundBrightColor = value ?? new Color(); } - } - public override string ToString() { if (String.IsNullOrWhiteSpace(_customAnsiSeq)) { - if (_foregroundBrightColor.ColorMode == ColorMode.DefaultColor) - { - return String.Format("'{0}', fg:{1}, bg:{2}", _text, _foregroundColor, _backgroundColor); - } - - return String.Format("'{0}', fg:{1}, fgB:{2}, bg:{3}", _text, _foregroundColor, _foregroundBrightColor, _backgroundColor); + return String.Format("'{0}', fg:{1}, bg:{2}", _text, _foregroundColor, _backgroundColor); } else { diff --git a/src/GitPrompt.ps1 b/src/GitPrompt.ps1 index c7bcb00d3..841412b6b 100644 --- a/src/GitPrompt.ps1 +++ b/src/GitPrompt.ps1 @@ -15,27 +15,27 @@ $global:GitPromptSettings = [pscustomobject]@{ FileRemovedText = '-' FileConflictedText = '!' - LocalDefaultStatusSymbol = New-Object PoshGit.TextSpan -Arg '', DarkGreen, Green, $defColor - LocalWorkingStatusSymbol = New-Object PoshGit.TextSpan -Arg '!', DarkRed, Red, $defColor - LocalStagedStatusSymbol = New-Object PoshGit.TextSpan -Arg '~', Cyan, $defColor + LocalDefaultStatusSymbol = New-Object PoshGit.TextSpan -Arg '', DarkGreen, $defColor + LocalWorkingStatusSymbol = New-Object PoshGit.TextSpan -Arg '!', DarkRed, $defColor + LocalStagedStatusSymbol = New-Object PoshGit.TextSpan -Arg '~', Cyan, $defColor - BranchColor = New-Object PoshGit.TextSpan -Arg '', Cyan, $defColor - BranchUntrackedSymbol = New-Object PoshGit.TextSpan -Arg '', $defColor, $defColor + BranchColor = New-Object PoshGit.TextSpan -Arg '', Cyan, $defColor + BranchUntrackedSymbol = New-Object PoshGit.TextSpan -Arg '', $defColor, $defColor # × Multiplication sign BranchGoneStatusSymbol = New-Object PoshGit.TextSpan -Arg ([char]0x00D7), DarkCyan, $defColor # ≡ Three horizontal lines - BranchIdenticalStatusSymbol = New-Object PoshGit.TextSpan -Arg ([char]0x2261), Cyan, $defColor + BranchIdenticalStatusSymbol = New-Object PoshGit.TextSpan -Arg ([char]0x2261), Cyan, $defColor # ↑ Up arrow - BranchAheadStatusSymbol = New-Object PoshGit.TextSpan -Arg ([char]0x2191), Green, $defColor + BranchAheadStatusSymbol = New-Object PoshGit.TextSpan -Arg ([char]0x2191), Green, $defColor # ↓ Down arrow - BranchBehindStatusSymbol = New-Object PoshGit.TextSpan -Arg ([char]0x2193), Red, $defColor + BranchBehindStatusSymbol = New-Object PoshGit.TextSpan -Arg ([char]0x2193), Red, $defColor # ↕ Up & Down arrow - BranchBehindAndAheadStatusSymbol = New-Object PoshGit.TextSpan -Arg ([char]0x2195), Yellow, $defColor + BranchBehindAndAheadStatusSymbol = New-Object PoshGit.TextSpan -Arg ([char]0x2195), Yellow, $defColor - BeforeIndexText = New-Object PoshGit.TextSpan -Arg '', DarkGreen, Green, $defColor - IndexColor = New-Object PoshGit.TextSpan -Arg '', DarkGreen, Green, $defColor - WorkingColor = New-Object PoshGit.TextSpan -Arg '', DarkRed, Red, $defColor + BeforeIndexText = New-Object PoshGit.TextSpan -Arg '', DarkGreen, $defColor + IndexColor = New-Object PoshGit.TextSpan -Arg '', DarkGreen, $defColor + WorkingColor = New-Object PoshGit.TextSpan -Arg '', DarkRed, $defColor EnableStashStatus = $false BeforeStashText = New-Object PoshGit.TextSpan -Arg ' (', Red, $defColor @@ -69,6 +69,16 @@ $global:GitPromptSettings = [pscustomobject]@{ TruncatedBranchSuffix = '...' } +# Override some of the normal colors if the background color is set to the default DarkMagenta. +$s = $global:GitPromptSettings +if ($true -or $Host.UI.RawUI.BackgroundColor -eq [ConsoleColor]::DarkMagenta) { + $s.LocalDefaultStatusSymbol.ForegroundColor = New-Object PoshGit.Color -ArgumentList Green + $s.LocalWorkingStatusSymbol.ForegroundColor = New-Object PoshGit.Color -ArgumentList Red + $s.BeforeIndexText.ForegroundColor = New-Object PoshGit.Color -ArgumentList Green + $s.IndexColor.ForegroundColor = New-Object PoshGit.Color -ArgumentList Green + $s.WorkingColor.ForegroundColor = New-Object PoshGit.Color -ArgumentList Red +} + # Make this a function for mocking and user may not want to use ANSI even on a system that supports ANSI. function UseAnsi { $global:Host.UI.SupportsVirtualTerminal @@ -160,9 +170,9 @@ function Write-Prompt { function Format-BranchName($branchName){ $s = $global:GitPromptSettings - if($s.BranchNameLimit -gt 0 -and $branchName.Length -gt $s.BranchNameLimit) + if ($s.BranchNameLimit -gt 0 -and $branchName.Length -gt $s.BranchNameLimit) { - $branchName = "{0}{1}" -f $branchName.Substring(0,$s.BranchNameLimit), $s.TruncatedBranchSuffix + $branchName = "{0}{1}" -f $branchName.Substring(0, $s.BranchNameLimit), $s.TruncatedBranchSuffix } return $branchName @@ -345,16 +355,6 @@ function Write-GitStatus($status, [System.Text.StringBuilder]$StringBuilder) { if (!(Test-Path Variable:Global:VcsPromptStatuses)) { $Global:VcsPromptStatuses = @() } -$s = $global:GitPromptSettings - -# Override some of the normal colors if the background color is set to the default DarkMagenta. -if ($Host.UI.RawUI.BackgroundColor -eq [ConsoleColor]::DarkMagenta) { - $s.LocalDefaultStatusSymbol.ForegroundColor = $s.LocalDefaultStatusSymbol.ForegroundBrightColor - $s.LocalWorkingStatusSymbol.ForegroundColor = $s.LocalWorkingStatusSymbol.ForegroundBrightColor - $s.BeforeIndexText.ForegroundColor = $s.BeforeIndexText.ForegroundBrightColor - $s.IndexColor.ForegroundColor = $s.IndexColor.ForegroundBrightColor - $s.WorkingColor.ForegroundColor = $s.WorkingColor.ForegroundBrightColor -} function Global:Write-VcsStatus([System.Text.StringBuilder]$StringBuilder) { # Is this the right place for this call? If someone uses Write-GitStatus (Get-GitStatus) they lose. From 502e7ef8eb1e6e94fc322e1bbd49716e3984a5c5 Mon Sep 17 00:00:00 2001 From: Keith Hill Date: Sun, 12 Mar 2017 20:19:44 -0600 Subject: [PATCH 17/19] Fix issue with extra space after branch name. --- src/GitPrompt.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/GitPrompt.ps1 b/src/GitPrompt.ps1 index 841412b6b..9301ceae4 100644 --- a/src/GitPrompt.ps1 +++ b/src/GitPrompt.ps1 @@ -251,7 +251,7 @@ function Write-GitStatus($status, [System.Text.StringBuilder]$StringBuilder) { Write-Prompt $branchNameTextSpan -StringBuilder $strBld if ($branchStatusTextSpan.Text) { - $branchStatusTextSpan.Text = " " + $branchStatusTextSpan.Text + Write-Prompt ' ' -StringBuilder $strBld Write-Prompt $branchStatusTextSpan -StringBuilder $strBld } From 556267a608eead227119395cdd8da318ca4bf8ff Mon Sep 17 00:00:00 2001 From: Keith Hill Date: Tue, 4 Apr 2017 22:47:58 -0600 Subject: [PATCH 18/19] Fix Color equals methods/operators. The updates work for a "class" instead of a "struct" where you don't have to worry about comparing with null or an instance being null. --- src/Ansi.cs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/Ansi.cs b/src/Ansi.cs index b3c88a40f..d5add4352 100644 --- a/src/Ansi.cs +++ b/src/Ansi.cs @@ -281,7 +281,12 @@ public override string ToString() { public static bool operator ==(Color left, Color right) { - return (left._value == right._value) && (left._mode == right._mode); + if (object.ReferenceEquals(left, null)) + { + return object.ReferenceEquals(right, null); + } + + return left.Equals(right); } public static bool operator !=(Color left, Color right) @@ -291,12 +296,14 @@ public override string ToString() { public override bool Equals(object obj) { - return (obj is Color) && Equals((Color)obj); + return Equals(obj as Color); } - public bool Equals(Color other) + public bool Equals(Color obj) { - return this == other; + return object.ReferenceEquals(obj, this) || + ((!object.ReferenceEquals(obj, null) && + _value == obj._value && _mode == obj._mode)); } public override int GetHashCode() From cd794efcdb3a4f1a6aff348c170a59830c7d84fd Mon Sep 17 00:00:00 2001 From: Keith Hill Date: Mon, 15 May 2017 19:58:40 -0600 Subject: [PATCH 19/19] Make work w/.NET Core 2.0 preview1. --- src/posh-git.psm1 | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/posh-git.psm1 b/src/posh-git.psm1 index fac7e7e67..0632c8107 100644 --- a/src/posh-git.psm1 +++ b/src/posh-git.psm1 @@ -3,9 +3,7 @@ param([switch]$NoVersionWarn, [switch]$ForcePoshGitPrompt) & $PSScriptRoot\CheckRequirements.ps1 > $null if ($PSVersionTable.PSEdition -eq 'Core') { - Add-Type -Path $PSScriptRoot\Ansi.cs -ReferencedAssemblies @( - "$PSHome/System.Console.dll", - "$PSHome/System.Management.Automation.dll") + Add-Type -Path $PSScriptRoot\Ansi.cs } else { Add-Type -Path $PSScriptRoot\Ansi.cs, $PSScriptRoot\ConsoleMode.cs