Browse Source

Explore parser more

tznind 10 months ago
parent
commit
22e612e4ac

+ 58 - 5
Terminal.Gui/ConsoleDrivers/AnsiResponseParser.cs

@@ -1,5 +1,7 @@
 #nullable enable
 #nullable enable
 
 
+using System.Diagnostics;
+
 namespace Terminal.Gui;
 namespace Terminal.Gui;
 class AnsiResponseParser
 class AnsiResponseParser
 {
 {
@@ -23,18 +25,69 @@ class AnsiResponseParser
 
 
     private bool inResponse = false;
     private bool inResponse = false;
 
 
+    private StringBuilder held = new StringBuilder();
 
 
-    public bool ConsumeInput (char character, out string? released)
+    /// <summary>
+    /// <para>
+    /// 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
+    /// </para>
+    /// <para>
+    /// 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.</para>
+    /// </summary>
+    /// <param name="input"></param>
+    /// <returns></returns>
+    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 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<string> response)
+    private string? currentTerminator = null;
+    private Action<string>? currentResponse = null;
+
+    public void ExpectResponse (string terminator, Action<string> response)
     {
     {
+        currentTerminator = terminator;
+        currentResponse = response;
+        
     }
     }
 }
 }

+ 11 - 11
UnitTests/ConsoleDrivers/AnsiResponseParserTests.cs

@@ -16,7 +16,7 @@ public class AnsiResponseParserTests
         int i = 0;
         int i = 0;
 
 
         // Imagine that we are expecting a DAR
         // 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
         // First char is Escape which we must consume incase what follows is the DAR
         AssertConsumed (ansiStream, ref i); // Esc
         AssertConsumed (ansiStream, ref i); // Esc
@@ -50,27 +50,27 @@ public class AnsiResponseParserTests
 
 
     private void AssertIgnored (string ansiStream, ref int i)
     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)
         // 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)
     private void AssertConsumed (string ansiStream, ref int i)
     {
     {
         // Parser grabs this key
         // 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)
     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 realizes it has grabbed content that does not belong to an outstanding request
         // Parser returns false to indicate to continue
         // 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();
     }
     }
 }
 }