From 34688fdd8c3f79df41e3621823a92b0fe6fab761 Mon Sep 17 00:00:00 2001
From: Arthur Sonzogni <sonzogniarthur@gmail.com>
Date: Sat, 24 Jun 2023 17:15:23 +0200
Subject: [PATCH] Support F1-F5 from OS terminal (#687)

Bug:https://github.com/ArthurSonzogni/FTXUI/issues/685
---
 CHANGELOG.md                                     |  1 +
 src/ftxui/component/terminal_input_parser.cpp    | 16 +++++++++++++++-
 .../component/terminal_input_parser_test.cpp     |  7 +++++++
 3 files changed, 23 insertions(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 427b8165..200b7a1a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,6 +12,7 @@ current (development)
 - Feature: `ResizeableSplit` now support arbitrary element as a separator.
 - Feature: `input` is now supporting multiple lines.
 - Feature: `input` style is now customizeable.
+- Bugfix: Support F1-F5 from OS terminal.
 
 ### Dom
 - Feature: Add `hyperlink` decorator. For instance:
diff --git a/src/ftxui/component/terminal_input_parser.cpp b/src/ftxui/component/terminal_input_parser.cpp
index 9fad1ba1..cbf125e2 100644
--- a/src/ftxui/component/terminal_input_parser.cpp
+++ b/src/ftxui/component/terminal_input_parser.cpp
@@ -44,6 +44,13 @@ const std::map<std::string, std::string> g_uniformize = {
     {"\x1BOH", "\x1B[H"},  // HOME
     {"\x1BOF", "\x1B[F"},  // END
 
+    // Variations around the FN keys.
+    // See: https://github.com/ArthurSonzogni/FTXUI/issues/685
+    {"\x1B[[A", "\x1BOP"},    // F1
+    {"\x1B[[B", "\x1BOQ"},    // F2
+    {"\x1B[[C", "\x1BOR"},    // F3
+    {"\x1B[[D", "\x1BOS"},    // F4
+    {"\x1B[[E", "\x1B[15~"},  // F5
 };
 
 TerminalInputParser::TerminalInputParser(Sender<Task> out)
@@ -291,9 +298,16 @@ TerminalInputParser::Output TerminalInputParser::ParseCSI() {
       continue;
     }
 
-    if (Current() >= ' ' && Current() <= '~' && Current() != '<') {
+    // CSI is terminated by a character in the range 0x40–0x7E
+    // (ASCII @A–Z[\]^_`a–z{|}~),
+    if (Current() >= '@' && Current() <= '~' &&
+        // Note: I don't remember why we exclude '<'
+        Current() != '<' &&
+        // To handle F1-F4, we exclude '['.
+        Current() != '[') {
       arguments.push_back(argument);
       argument = 0;  // NOLINT
+
       switch (Current()) {
         case 'M':
           return ParseMouse(altered, true, std::move(arguments));
diff --git a/src/ftxui/component/terminal_input_parser_test.cpp b/src/ftxui/component/terminal_input_parser_test.cpp
index 79ca86d9..95fd4234 100644
--- a/src/ftxui/component/terminal_input_parser_test.cpp
+++ b/src/ftxui/component/terminal_input_parser_test.cpp
@@ -385,6 +385,13 @@ TEST(Event, Special) {
       {str("\x1B[23~"), Event::F11},
       {str("\x1B[24~"), Event::F12},
 
+      // Function keys for virtual terminal:
+      {str("\x1B[[A"), Event::F1},
+      {str("\x1B[[B"), Event::F2},
+      {str("\x1B[[C"), Event::F3},
+      {str("\x1B[[D"), Event::F4},
+      {str("\x1B[[E"), Event::F5},
+
       // Page up and down:
       {str("\x1B[5~"), Event::PageUp},
       {str("\x1B[6~"), Event::PageDown},
-- 
GitLab