#nullable enable using System.Diagnostics; namespace Terminal.Gui; class AnsiResponseParser { /* * ANSI Input Sequences * * \x1B[A // Up Arrow key pressed * \x1B[B // Down Arrow key pressed * \x1B[C // Right Arrow key pressed * \x1B[D // Left Arrow key pressed * \x1B[3~ // Delete key pressed * \x1B[2~ // Insert key pressed * \x1B[5~ // Page Up key pressed * \x1B[6~ // Page Down key pressed * \x1B[1;5D // Ctrl + Left Arrow * \x1B[1;5C // Ctrl + Right Arrow * \x1B[0;10;20M // Mouse button pressed at position (10, 20) * \x1B[0c // Device Attributes Response (e.g., terminal identification) */ private bool inResponse = false; private StringBuilder held = new StringBuilder(); /// /// /// 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); // consume the escape held.Append (input [0]); inResponse = true; return ProcessInput (input.Substring (1)); } return input[0] + ProcessInput (input.Substring (1)); } private string? currentTerminator = null; private Action? currentResponse = null; public void ExpectResponse (string terminator, Action response) { currentTerminator = terminator; currentResponse = response; } }