diff --git a/Terminal.Gui/ConsoleDrivers/AnsiResponseParser.cs b/Terminal.Gui/ConsoleDrivers/AnsiResponseParser.cs
index 31aed18ee..3ac5b4d68 100644
--- a/Terminal.Gui/ConsoleDrivers/AnsiResponseParser.cs
+++ b/Terminal.Gui/ConsoleDrivers/AnsiResponseParser.cs
@@ -1,5 +1,7 @@
#nullable enable
+using System.Diagnostics;
+
namespace Terminal.Gui;
class AnsiResponseParser
{
@@ -23,18 +25,69 @@ class AnsiResponseParser
private bool inResponse = false;
+ private StringBuilder held = new StringBuilder();
- public bool ConsumeInput (char character, out string? released)
+ ///
+ ///
+ /// Processes input which may be a single character or multiple.
+ /// Returns what should be passed on to any downstream input processing
+ /// (i.e. removes expected Ansi responses from the input stream
+ ///
+ ///
+ /// This method is designed to be called iteratively and as such may
+ /// return more characters than were passed in depending on previous
+ /// calls (e.g. if it was in the middle of an unrelated ANSI response.
+ ///
+ ///
+ ///
+ public string ProcessInput (string input)
{
+
+ if (inResponse)
+ {
+ if (currentTerminator != null && input.StartsWith (currentTerminator))
+ {
+ // Consume terminator and release the event
+ held.Append (currentTerminator);
+ currentResponse?.Invoke (held.ToString());
+
+ // clear the state
+ held.Clear ();
+ currentResponse = null;
+
+ // recurse
+ return ProcessInput (input.Substring (currentTerminator.Length));
+ }
+
+ // we are in a response but have not reached terminator yet
+ held.Append (input [0]);
+ return ProcessInput (input.Substring (1));
+ }
+
+
// if character is escape
+ if (input.StartsWith ('\x1B'))
+ {
+ // We shouldn't get an escape in the middle of a response - TODO: figure out how to handle that
+ Debug.Assert (!inResponse);
- // start consuming till we see terminator
- released = null;
- return false;
+ // consume the escape
+ held.Append (input [0]);
+ inResponse = true;
+ return ProcessInput (input.Substring (1));
+ }
+
+ return input[0] + ProcessInput (input.Substring (1));
}
- public void ExpectResponse (char terminator, Action response)
+ private string? currentTerminator = null;
+ private Action? currentResponse = null;
+
+ public void ExpectResponse (string terminator, Action response)
{
+ currentTerminator = terminator;
+ currentResponse = response;
+
}
}
diff --git a/UnitTests/ConsoleDrivers/AnsiResponseParserTests.cs b/UnitTests/ConsoleDrivers/AnsiResponseParserTests.cs
index 79f0bbba4..ab72288b1 100644
--- a/UnitTests/ConsoleDrivers/AnsiResponseParserTests.cs
+++ b/UnitTests/ConsoleDrivers/AnsiResponseParserTests.cs
@@ -16,7 +16,7 @@ public class AnsiResponseParserTests
int i = 0;
// Imagine that we are expecting a DAR
- _parser.ExpectResponse ('c',(s)=> response = s);
+ _parser.ExpectResponse ("c",(s)=> response = s);
// First char is Escape which we must consume incase what follows is the DAR
AssertConsumed (ansiStream, ref i); // Esc
@@ -50,27 +50,27 @@ public class AnsiResponseParserTests
private void AssertIgnored (string ansiStream, ref int i)
{
+ var c = NextChar (ansiStream, ref i);
+
// Parser does not grab this key (i.e. driver can continue with regular operations)
- Assert.False (_parser.ConsumeInput (NextChar (ansiStream, ref i), out var released));
- Assert.Null (released);
+ Assert.Equal ( c,_parser.ProcessInput (c));
}
private void AssertConsumed (string ansiStream, ref int i)
{
// Parser grabs this key
- Assert.True (_parser.ConsumeInput( NextChar (ansiStream, ref i), out var released));
- Assert.Null (released);
+ var c = NextChar (ansiStream, ref i);
+ Assert.Empty (_parser.ProcessInput(c));
}
private void AssertReleased (string ansiStream, ref int i, string expectedRelease)
{
+ var c = NextChar (ansiStream, ref i);
+
// Parser realizes it has grabbed content that does not belong to an outstanding request
// Parser returns false to indicate to continue
- Assert.False(_parser.ConsumeInput (NextChar (ansiStream,ref i), out var released));
-
- // Parser releases all the grabbed content back to the driver
- Assert.Equal ( released,expectedRelease);
+ Assert.Equal(expectedRelease,_parser.ProcessInput (c));
}
- private char NextChar (string ansiStream, ref int i)
+ private string NextChar (string ansiStream, ref int i)
{
- return ansiStream [i++];
+ return ansiStream [i++].ToString();
}
}