Browse Source

Support for late responses collection

tznind 9 months ago
parent
commit
d3ac6e108d

+ 1 - 2
Terminal.Gui/ConsoleDrivers/AnsiRequestScheduler.cs

@@ -1,5 +1,6 @@
 #nullable enable
 using System.Collections.Concurrent;
+using System.Diagnostics;
 
 namespace Terminal.Gui;
 
@@ -73,8 +74,6 @@ public class AnsiRequestScheduler(IAnsiResponseParser parser)
     {
         if (_lastSend.TryGetValue (withTerminator, out var dt))
         {
-            // TODO: If debugging this can cause problem becuase we stop expecting response but one comes in anyway
-            // causing parser to ignore and it to fall through to default console iteration which typically crashes.
             if (DateTime.Now - dt > _staleTimeout)
             {
                 parser.StopExpecting (withTerminator);

+ 41 - 7
Terminal.Gui/ConsoleDrivers/AnsiResponseParser.cs

@@ -6,7 +6,16 @@ namespace Terminal.Gui;
 
 internal abstract class AnsiResponseParserBase : IAnsiResponseParser
 {
+    /// <summary>
+    /// Responses we are expecting to come in.
+    /// </summary>
     protected readonly List<(string terminator, Action<string> response)> expectedResponses = new ();
+
+    /// <summary>
+    /// Collection of responses that we <see cref="StopExpecting"/>.
+    /// </summary>
+    protected readonly List<(string terminator, Action<string> response)> lateResponses = new ();
+
     private AnsiResponseParserState _state = AnsiResponseParserState.Normal;
 
     // Current state of the parser
@@ -198,17 +207,20 @@ internal abstract class AnsiResponseParserBase : IAnsiResponseParser
     {
         string cur = HeldToString ();
 
-        // Check for expected responses
-        (string terminator, Action<string> response) matchingResponse = expectedResponses.FirstOrDefault (r => cur.EndsWith (r.terminator));
-
-        if (matchingResponse.response != null)
+        // Look for an expected response for what is accumulated so far (since Esc)
+        if (MatchResponse (cur, expectedResponses))
         {
-            DispatchResponse (matchingResponse.response);
-            expectedResponses.Remove (matchingResponse);
+            return false;
+        }
 
+        // Also try looking for late requests
+        if (MatchResponse (cur, lateResponses))
+        {
             return false;
         }
 
+        // Finally if it is a valid ansi response but not one we are expect (e.g. its mouse activity)
+        // then we can release it back to input processing stream
         if (_knownTerminators.Contains (cur.Last ()) && cur.StartsWith (EscSeqUtils.CSI))
         {
             // Detected a response that was not expected
@@ -218,6 +230,22 @@ internal abstract class AnsiResponseParserBase : IAnsiResponseParser
         return false; // Continue accumulating
     }
 
+    private bool MatchResponse (string cur, List<(string terminator, Action<string> response)> valueTuples)
+    {
+        // Check for expected responses
+        var matchingResponse = valueTuples.FirstOrDefault (r => cur.EndsWith (r.terminator));
+
+        if (matchingResponse.response != null)
+        {
+            DispatchResponse (matchingResponse.response);
+            expectedResponses.Remove (matchingResponse);
+
+            return true;
+        }
+
+        return false;
+    }
+
     protected void DispatchResponse (Action<string> response)
     {
         response?.Invoke (HeldToString ());
@@ -237,7 +265,13 @@ internal abstract class AnsiResponseParserBase : IAnsiResponseParser
     /// <inheritdoc />
     public void StopExpecting (string requestTerminator)
     {
-        expectedResponses.RemoveAll (r => r.terminator == requestTerminator);
+        var removed = expectedResponses.Where (r => r.terminator == requestTerminator).ToArray ();
+
+        foreach (var r in removed)
+        {
+            expectedResponses.Remove (r);
+            lateResponses.Add (r);
+        }
     }
 }