Skip to content

Commit bc7b714

Browse files
committed
- Fixed : Cursor visibilit,hide on linux system and Color conversion methods. Improved : AnsiDetector has been improved and Check requires a terminal/console environment at startup.
1 parent ec1080d commit bc7b714

12 files changed

Lines changed: 164 additions & 143 deletions

File tree

README.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,22 @@
3333
----
3434
## What's new in the latest version
3535

36+
### V5.0.3
37+
[**Top**](#table-of-contents)
38+
39+
- Improved : AnsiDetector has been improved.
40+
- Improved : Check requires a terminal/console environment at startup.
41+
- System.Console.IsInputRedirected or System.Console.IsOutputRedirected not supported.
42+
- Fixed : Cursor visibility/hide on linux system.
43+
- Fixed : Color conversion methods.
44+
- Stackoverflow exception when use ToConsoleColor Extensions. (the Extensions methods were calling themselves recursively).
45+
- ForegroundColor/BackgroundColor conversion.
46+
47+
### V5.0.2
48+
[**Top**](#table-of-contents)
49+
50+
- Fixed : Incorrect PageSize value for widgets.Fixed: ProgressBar control with custom range not updating percentage.
51+
-
3652
### V5.0.1
3753
[**Top**](#table-of-contents)
3854

docs/api/assemblies/PromptPlus.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
| enum [ChartBarType](./PromptPlusLibrary/ChartBarType.md) | Represents the Kinds Progress Bar |
2121
| class [ChartItem](./PromptPlusLibrary/ChartItem.md) | Represents a chart item. |
2222
| struct [Color](./PromptPlusLibrary/Color.md) | Represents an RGB color (optionally mapped to an indexed palette entry). |
23-
| static class [ColorExtensions](./PromptPlusLibrary/ColorExtensions.md) | Extensions for the Color class. |
2423
| enum [ColorSystem](./PromptPlusLibrary/ColorSystem.md) | Represents the capacity of console color system. |
2524
| enum [ComponentStyles](./PromptPlusLibrary/ComponentStyles.md) | Represents the different styles that can be applied to components in the PromptPlus library. |
2625
| enum [DashOptions](./PromptPlusLibrary/DashOptions.md) | Represents a boder when write line with SingleDash/DoubleDash. |

docs/api/assemblies/PromptPlusLibrary/ColorExtensions.md

Lines changed: 0 additions & 23 deletions
This file was deleted.

docs/api/assemblies/PromptPlusLibrary/ColorExtensions/ToConsoleColor.md

Lines changed: 0 additions & 27 deletions
This file was deleted.

src/Core/Ansi/AnsiDetector.cs

Lines changed: 98 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -40,36 +40,95 @@ public static bool IsValidTerminal(string term)
4040
return !string.IsNullOrWhiteSpace(term) && _regexes.Any(regex => regex.IsMatch(term));
4141
}
4242

43+
/// <summary>
44+
/// Detect whether current environment supports ANSI sequences.
45+
/// Returns tuple (SupportsAnsi, LegacyConsole).
46+
/// - SupportsAnsi: true when we can emit ANSI sequences and they will be interpreted.
47+
/// - LegacyConsole: true when environment is a legacy Windows console that doesn't support ANSI.
48+
/// </summary>
4349
public static (bool SupportsAnsi, bool LegacyConsole) Detect()
4450
{
4551
// Running on Windows?
4652
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
4753
{
48-
// Running under ConEmu?
54+
// ConEmu explicit opt-in
4955
string? conEmu = Environment.GetEnvironmentVariable("ConEmuANSI");
5056
if (!string.IsNullOrEmpty(conEmu) && conEmu.Equals("On", StringComparison.OrdinalIgnoreCase))
5157
{
5258
return (true, false);
5359
}
5460

55-
bool supportsAnsi = Windows.SupportsAnsi(out bool legacyConsole);
56-
return (supportsAnsi, legacyConsole);
61+
// ANSICON (older ANSI emulator), Windows Terminal (WT_SESSION), and similar env hints
62+
if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("ANSICON")) ||
63+
!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("WT_SESSION")) ||
64+
IsKnownTerminalProgram())
65+
{
66+
// Even if these env vars are present, we should still try to enable console mode if possible.
67+
bool supports = Windows.SupportsAnsi(out bool legacyConsole);
68+
return (supports, legacyConsole);
69+
}
70+
71+
// Otherwise query Windows console directly (stdout preferred).
72+
bool supportsAnsi = Windows.SupportsAnsi(out bool legacy);
73+
return (supportsAnsi, legacy);
5774
}
75+
// Non-Windows platforms: decide from TERM/COLORTERM
5876
return DetectFromTerm();
5977
}
6078

6179
private static (bool SupportsAnsi, bool LegacyConsole) DetectFromTerm()
6280
{
63-
// Check if the terminal is of type ANSI/VT100/xterm compatible.
81+
// If output is redirected, ANSI sequences probably won't be useful.
82+
if (Console.IsOutputRedirected && Console.IsErrorRedirected)
83+
{
84+
return (false, true);
85+
}
86+
87+
// Common variable that indicates color support (truecolor/24bit or general color)
88+
string? colorterm = Environment.GetEnvironmentVariable("COLORTERM");
89+
if (!string.IsNullOrWhiteSpace(colorterm))
90+
{
91+
// If COLORTERM is present, assume ANSI/VT sequences are supported on non-Windows
92+
return (true, false);
93+
}
94+
95+
// TERM-based detection (xterm, screen, linux, cygwin, etc).
6496
string? term = Environment.GetEnvironmentVariable("TERM");
6597
if (!string.IsNullOrWhiteSpace(term) && _regexes.Any(regex => regex.IsMatch(term)))
6698
{
6799
return (true, false);
68100
}
69101

102+
// Fallback: if TERM_PROGRAM suggests a modern terminal (vscode, iTerm, Apple_Terminal)
103+
if (IsKnownTerminalProgram())
104+
{
105+
return (true, false);
106+
}
107+
70108
return (false, true);
71109
}
72110

111+
private static bool IsKnownTerminalProgram()
112+
{
113+
string? termProgram = Environment.GetEnvironmentVariable("TERM_PROGRAM");
114+
if (string.IsNullOrWhiteSpace(termProgram))
115+
{
116+
return false;
117+
}
118+
119+
// Some known modern terminal identifiers
120+
string[] known =
121+
[
122+
"vscode", // VS Code integrated terminal
123+
"vscode-insiders",
124+
"iTerm.app", // iTerm2 on macOS
125+
"Apple_Terminal",
126+
"WindowsTerminal",
127+
];
128+
129+
return known.Any(k => termProgram.Contains(k, StringComparison.OrdinalIgnoreCase));
130+
}
131+
73132
[GeneratedRegex("^xterm")]
74133
private static partial Regex XtermRegex();
75134

@@ -121,12 +180,12 @@ private static (bool SupportsAnsi, bool LegacyConsole) DetectFromTerm()
121180
[GeneratedRegex("alacritty")]
122181
private static partial Regex AlacrittyRegex();
123182

124-
unsafe private static partial class Windows
183+
private static partial class Windows
125184
{
185+
private const int STD_OUTPUT_HANDLE = -11;
126186
private const int STD_ERROR_HANDLE = -12;
127187

128188
private const uint ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004;
129-
130189
private const uint DISABLE_NEWLINE_AUTO_RETURN = 0x0008;
131190

132191
[LibraryImport("kernel32.dll")]
@@ -149,39 +208,54 @@ public static bool SupportsAnsi(out bool isLegacy)
149208

150209
try
151210
{
152-
nint @out = GetStdHandle(STD_ERROR_HANDLE);
153-
if (!GetConsoleMode(@out, out uint mode))
211+
// If output is redirected to a file/pipe, the console APIs will fail. Respect redirection.
212+
if (Console.IsOutputRedirected && Console.IsErrorRedirected)
154213
{
155-
// Could not get console mode, try TERM (set in cygwin, WSL-Shell).
156-
(bool ansiFromTerm, bool legacyFromTerm) = DetectFromTerm();
157-
158-
isLegacy = ansiFromTerm ? legacyFromTerm : isLegacy;
159-
return ansiFromTerm;
214+
isLegacy = false;
215+
return false;
160216
}
161217

162-
if ((mode & ENABLE_VIRTUAL_TERMINAL_PROCESSING) == 0)
218+
// Prefer stdout handle, then stderr.
219+
nint handle = GetStdHandle(STD_OUTPUT_HANDLE);
220+
if (handle == nint.Zero || !GetConsoleMode(handle, out uint mode))
163221
{
164-
isLegacy = true;
165-
166-
// Try enable ANSI support.
167-
mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING | DISABLE_NEWLINE_AUTO_RETURN;
168-
if (!SetConsoleMode(@out, mode))
222+
// try stderr as fallback
223+
handle = GetStdHandle(STD_ERROR_HANDLE);
224+
if (handle == nint.Zero || !GetConsoleMode(handle, out mode))
169225
{
170-
// Enabling failed.
171-
return false;
226+
// Could not get console mode. Fall back to TERM detection (WSL, MSYS, Cygwin)
227+
(bool ansiFromTerm, bool legacyFromTerm) = DetectFromTerm();
228+
isLegacy = ansiFromTerm ? legacyFromTerm : isLegacy;
229+
return ansiFromTerm;
172230
}
231+
}
173232

174-
isLegacy = false;
233+
// If virtual terminal processing already enabled, we support ANSI
234+
if ((mode & ENABLE_VIRTUAL_TERMINAL_PROCESSING) != 0)
235+
{
236+
return true;
237+
}
238+
239+
// Attempt to enable virtual terminal processing.
240+
uint newMode = mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING | DISABLE_NEWLINE_AUTO_RETURN;
241+
if (!SetConsoleMode(handle, newMode))
242+
{
243+
// Enabling failed: treat as legacy Windows console.
244+
isLegacy = true;
245+
return false;
175246
}
176247

248+
// Successfully enabled.
249+
isLegacy = false;
177250
return true;
178251
}
179252
catch
180253
{
181-
// All we know here is that we don't support ANSI.
254+
// Unknown failure; assume no ANSI support.
255+
isLegacy = true;
182256
return false;
183257
}
184258
}
185259
}
186260
}
187-
}
261+
}

src/Drivers/ConsoleDriveLinux.cs

Lines changed: 0 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -3,42 +3,9 @@
33
// The maintenance and evolution is maintained by the PromptPlus project under MIT license
44
// ***************************************************************************************
55

6-
using System;
7-
86
namespace PromptPlusLibrary.Drivers
97
{
108
internal sealed class ConsoleDriveLinux(IProfileDrive profile) : ConsoleDriveWindows(profile)
119
{
12-
private bool _cursorvisible = true;
13-
14-
public override bool CursorVisible
15-
{
16-
get
17-
{
18-
return _cursorvisible;
19-
}
20-
set
21-
{
22-
_cursorvisible = value;
23-
}
24-
}
25-
26-
public override void HideCursor()
27-
{
28-
_cursorvisible = false;
29-
if (ProfilePlus.SupportsAnsi)
30-
{
31-
Console.Write("\x1b[?25l");
32-
}
33-
}
34-
35-
public override void ShowCursor()
36-
{
37-
_cursorvisible = true;
38-
if (ProfilePlus.SupportsAnsi)
39-
{
40-
Console.Write("\x1b[?25h");
41-
}
42-
}
4310
}
4411
}

src/Drivers/ConsoleDriveWindows.cs

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ internal class ConsoleDriveWindows : IConsole, IConsoleExtend
2727
private CancellationTokenSource _tokenCancelPress;
2828
private bool _isExistDefaultCancel;
2929
private bool _isAbortCtrlC;
30+
private bool _cursorvisible = true;
3031

3132
public ConsoleDriveWindows(IProfileDrive profile)
3233
{
@@ -142,7 +143,7 @@ public Color ForegroundColor
142143
}
143144
else
144145
{
145-
Console.ForegroundColor = value.ToConsoleColor();
146+
Console.ForegroundColor = Color.ToConsoleColor(value);
146147
}
147148
}
148149
}
@@ -162,7 +163,7 @@ public Color BackgroundColor
162163
}
163164
else
164165
{
165-
Console.BackgroundColor = value.ToConsoleColor();
166+
Console.BackgroundColor = Color.ToConsoleColor(value);
166167
}
167168
}
168169
}
@@ -171,11 +172,7 @@ public virtual bool CursorVisible
171172
{
172173
get
173174
{
174-
#pragma warning disable IDE0079 // Remove unnecessary suppression
175-
#pragma warning disable CA1416 // Validate platform compatibility
176-
return Console.CursorVisible;
177-
#pragma warning restore CA1416 // Validate platform compatibility
178-
#pragma warning restore IDE0079 // Remove unnecessary suppression
175+
return _cursorvisible;
179176
}
180177
set
181178
{
@@ -195,10 +192,19 @@ private void ShowCusor(bool value)
195192
{
196193
Console.Write(AnsiSequences.RM());
197194
}
195+
_cursorvisible = value;
198196
}
199197
else
200198
{
201-
Console.CursorVisible = value;
199+
try
200+
{
201+
Console.CursorVisible = value;
202+
}
203+
catch
204+
{
205+
//ignore exception for non supported
206+
}
207+
_cursorvisible = value;
202208
}
203209
}
204210

src/NugetREADME.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,20 @@ PromptPlus **Supports 4/8/24-bit colors** in the terminal with auto-detection of
1010

1111
#### [Visit the official page for complete documentation of PromptPlus](https://github.com/FRACerqueira/PromptPlus)
1212

13+
## What's new in V5.0.3
14+
15+
- Improved : AnsiDetector has been improved.
16+
- Improved : Check requires a terminal/console environment at startup.
17+
- System.Console.IsInputRedirected or System.Console.IsOutputRedirected not supported.
18+
- Fixed : Cursor visibility/hide on linux system.
19+
- Fixed : Color conversion methods.
20+
- Stackoverflow exception when use ToConsoleColor Extensions. (the Extensions methods were calling themselves recursively).
21+
- ForegroundColor/BackgroundColor conversion.
22+
23+
## What's new in V5.0.2
24+
25+
- Fixed : Incorrect PageSize value for widgets.Fixed: ProgressBar control with custom range not updating percentage.
26+
1327
## What's new in V5.0.1
1428

1529
- Removed: Feature multithreading (Incompatible when another process uses the same output stream).

0 commit comments

Comments
 (0)