Skip to content

Commit fccc682

Browse files
committed
feat(compatibility): ✨ Enhance input handling and add HasPendingInput method
* Improved input handling by checking for pending input before draining. * Introduced `HasPendingInput` method to encapsulate input availability logic. * Refactored `RenderToString` method in `SpectreRenderBridge` for better error handling and clarity. * Updated `Writer` class to use consistent parameter naming conventions. * Fixed window rectangle calculations in test helper for accurate buffer retrieval.
1 parent 310f744 commit fccc682

4 files changed

Lines changed: 40 additions & 21 deletions

File tree

src/PSTextMate/Sixel/Compatibility.cs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,9 @@ internal static string GetControlSequenceResponse(string controlSequence) {
3434
const int maxRetries = 2;
3535

3636
lock (s_controlSequenceLock) {
37-
// Drain any stale bytes that may have leaked from prior VT interactions.
38-
DrainPendingInput();
37+
if (HasPendingInput()) {
38+
return string.Empty;
39+
}
3940

4041
for (int retry = 0; retry < maxRetries; retry++) {
4142
try {
@@ -89,6 +90,19 @@ internal static string GetControlSequenceResponse(string controlSequence) {
8990
return string.Empty;
9091
}
9192

93+
private static bool HasPendingInput() {
94+
if (Console.IsOutputRedirected || Console.IsInputRedirected) {
95+
return false;
96+
}
97+
98+
try {
99+
return Console.KeyAvailable;
100+
}
101+
catch {
102+
return false;
103+
}
104+
}
105+
92106
/// <summary>
93107
/// Attempts to read a key if one is available.
94108
/// </summary>

src/PSTextMate/Utilities/SpectreRenderBridge.cs

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,18 @@ public static class SpectreRenderBridge {
1818
public static string RenderToString(object renderableObject, bool escapeAnsi = false, int? width = null) {
1919
ArgumentNullException.ThrowIfNull(renderableObject);
2020

21-
string rendered = renderableObject is IRenderable localRenderable
22-
? RenderLocal(localRenderable, width)
23-
: RenderForeign(renderableObject, width);
24-
25-
return rendered.Length != 0
26-
? escapeAnsi ? VTHelpers.StripAnsi(rendered) : rendered
27-
: throw new ArgumentException(
21+
string rendered;
22+
if (renderableObject is IRenderable localRenderable) {
23+
rendered = RenderLocal(localRenderable, width);
24+
}
25+
else if (!TryRenderForeign(renderableObject, width, out rendered)) {
26+
throw new ArgumentException(
2827
$"Object of type '{renderableObject.GetType().FullName}' does not implement a supported Spectre IRenderable shape.",
2928
nameof(renderableObject)
3029
);
30+
}
31+
32+
return escapeAnsi ? VTHelpers.StripAnsi(rendered) : rendered;
3133
}
3234

3335
/// <summary>
@@ -87,7 +89,8 @@ private static string RenderLocal(IRenderable renderable, int? width) {
8789
return writer.ToString();
8890
}
8991

90-
private static string RenderForeign(object renderableObject, int? width) {
92+
private static bool TryRenderForeign(object renderableObject, int? width, out string rendered) {
93+
rendered = string.Empty;
9194
Type valueType = renderableObject.GetType();
9295
Assembly assembly = valueType.Assembly;
9396

@@ -101,15 +104,15 @@ private static string RenderForeign(object renderableObject, int? width) {
101104
|| ansiConsoleSettingsType is null
102105
|| ansiConsoleOutputType is null
103106
|| foreignRenderableType?.IsInstanceOfType(renderableObject) != true) {
104-
return string.Empty;
107+
return false;
105108
}
106109

107110
using StringWriter writer = new(new StringBuilder(1024), CultureInfo.InvariantCulture);
108111
object? output = Activator.CreateInstance(ansiConsoleOutputType, writer);
109112
object? settings = Activator.CreateInstance(ansiConsoleSettingsType);
110113
PropertyInfo? outProperty = ansiConsoleSettingsType.GetProperty("Out");
111114
if (output is null || settings is null || outProperty is null || !outProperty.CanWrite) {
112-
return string.Empty;
115+
return false;
113116
}
114117

115118
outProperty.SetValue(settings, output);
@@ -121,7 +124,7 @@ private static string RenderForeign(object renderableObject, int? width) {
121124
&& parameters[0].ParameterType == ansiConsoleSettingsType);
122125
object? console = createMethod?.Invoke(null, [settings]);
123126
if (console is null) {
124-
return string.Empty;
127+
return false;
125128
}
126129

127130
if (width is int targetWidth && targetWidth > 0) {
@@ -136,7 +139,8 @@ private static string RenderForeign(object renderableObject, int? width) {
136139
MethodInfo? writeMethod = console.GetType().GetMethod("Write", [foreignRenderableType]);
137140
if (writeMethod is not null) {
138141
_ = writeMethod.Invoke(console, [renderableObject]);
139-
return writer.ToString();
142+
rendered = writer.ToString();
143+
return true;
140144
}
141145

142146
Type? extType = assembly.GetType("Spectre.Console.AnsiConsoleExtensions");
@@ -146,11 +150,12 @@ private static string RenderForeign(object renderableObject, int? width) {
146150
&& method.GetParameters() is { Length: 2 } parameters
147151
&& parameters[1].ParameterType == foreignRenderableType);
148152
if (extWriteMethod is null) {
149-
return string.Empty;
153+
return false;
150154
}
151155

152156
_ = extWriteMethod.Invoke(null, [console, renderableObject]);
153-
return writer.ToString();
157+
rendered = writer.ToString();
158+
return true;
154159
}
155160

156161
private static CallSite<Func<CallSite, object, IRenderable>> CreateConvertToRenderableCallSite() {

src/PSTextMate/Utilities/Writer.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,21 +32,21 @@ public static string Write(IRenderable renderable) {
3232
/// Renders highlighted text to string.
3333
/// </summary>
3434
[MethodImpl(MethodImplOptions.AggressiveInlining)]
35-
public static string? Write(HighlightedText highlightedText, bool autoPage = false, bool FromFormat = false) {
35+
public static string? Write(HighlightedText highlightedText, bool autoPage = false, bool fromFormat = false) {
3636
ArgumentNullException.ThrowIfNull(highlightedText);
3737

3838
if (highlightedText.Page || (autoPage && ShouldPage(highlightedText))) {
3939
var pager = new Pager(highlightedText);
4040
pager.Show();
41-
if (FromFormat) VTHelpers.MoveCursorRowUp(2);
41+
if (fromFormat) VTHelpers.MoveCursorRowUp(2);
4242
return null;
4343
}
4444

4545
// Sixel payload must be written as raw control sequences. Converting to a string
4646
// and flowing through host formatting can strip DCS wrappers and print payload text.
4747
if (ContainsImageRenderables(highlightedText.Renderables)) {
4848
AnsiConsole.Write(highlightedText);
49-
if (FromFormat) VTHelpers.MoveCursorRowUp(2);
49+
if (fromFormat) VTHelpers.MoveCursorRowUp(2);
5050
return null;
5151
}
5252

tests/testhelper.psm1

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ function Get-HostBuffer {
2929
$windowRect = [System.Management.Automation.Host.Rectangle]::new(
3030
$windowPosition.X,
3131
$windowPosition.Y,
32-
($windowPosition.X + $windowWidth),
33-
($windowPosition.Y + $windowHeight))
32+
($windowPosition.X + $windowWidth - 1),
33+
($windowPosition.Y + $windowHeight - 1))
3434
$windowBuffer = $host.UI.RawUI.GetBufferContents($windowRect)
3535
foreach ($x in 0..($windowHeight - 1)) {
3636
$row = foreach ($y in 0..($windowWidth - 1)) {

0 commit comments

Comments
 (0)