From 4b9fece9f48615f2e8068054c60763e844415a7c Mon Sep 17 00:00:00 2001 From: Kurtis Rader Date: Thu, 14 Jan 2016 21:46:53 -0800 Subject: allow configuring the escape delay timeout Introduce a "fish_escape_delay_ms" variable to allow the user to configure the delay used when seeing a bare escape before assuming no other characters will be received that might match a bound character sequence. This is primarily useful for vi mode so that a bare escape character (i.e., keystroke) can switch to vi "insert" to "normal" mode in a timely fashion. --- src/env.cpp | 6 ++- src/input_common.cpp | 59 +++++++++++++++--------- src/input_common.h | 3 ++ tests/bind.expect | 121 ++++++++++++++++++++++++++++++++++++++------------ tests/bind.expect.out | 12 +++-- 5 files changed, 146 insertions(+), 55 deletions(-) diff --git a/src/env.cpp b/src/env.cpp index 3d7023db..527ed253 100644 --- a/src/env.cpp +++ b/src/env.cpp @@ -303,7 +303,7 @@ static void handle_locale() } -/** React to modifying hte given variable */ +/** React to modifying the given variable */ static void react_to_variable_change(const wcstring &key) { if (var_is_locale(key)) @@ -319,6 +319,10 @@ static void react_to_variable_change(const wcstring &key) { reader_react_to_color_change(); } + else if (key == L"fish_escape_delay_ms") + { + update_wait_on_escape_ms(); + } } /** diff --git a/src/input_common.cpp b/src/input_common.cpp index cda4ca11..291a37a7 100644 --- a/src/input_common.cpp +++ b/src/input_common.cpp @@ -33,9 +33,11 @@ Implementation file for the low level input library reading after \\x1b is read before assuming that escape key was pressed, and not an escape sequence. - This is the value used by the readline library. + This is the value used by the readline library. It can be overridden by + setting the fish_escape_delay_ms variable. */ -#define WAIT_ON_ESCAPE 500 +#define WAIT_ON_ESCAPE_DEFAULT 500 +static int wait_on_escape_ms = WAIT_ON_ESCAPE_DEFAULT; /** Characters that have been read and returned by the sequence matching code */ static std::deque lookahead_list; @@ -79,6 +81,7 @@ static int (*interrupt_handler)(); void input_common_init(int (*ih)()) { interrupt_handler = ih; + update_wait_on_escape_ms(); } void input_common_destroy() @@ -214,36 +217,48 @@ static wint_t readb() return arr[0]; } +// Update the wait_on_escape_ms value in response to the fish_escape_delay_ms +// user variable being set. +void update_wait_on_escape_ms() +{ + env_var_t escape_time_ms = env_get_string(L"fish_escape_delay_ms"); + if (escape_time_ms.missing_or_empty()) + { + wait_on_escape_ms = WAIT_ON_ESCAPE_DEFAULT; + return; + } + + wchar_t *endptr; + long tmp = wcstol(escape_time_ms.c_str(), &endptr, 10); + + if (*endptr != '\0' || tmp < 10 || tmp >= 5000) + { + fwprintf(stderr, L"ignoring fish_escape_delay_ms: value '%ls' " + "is not an integer or is < 10 or >= 5000 ms\n", + escape_time_ms.c_str()); + } + else + { + wait_on_escape_ms = (int)tmp; + } +} + + wchar_t input_common_readch(int timed) { if (! has_lookahead()) { if (timed) { - int count; fd_set fds; - struct timeval tm= - { - 0, - 1000 * WAIT_ON_ESCAPE - } - ; - FD_ZERO(&fds); FD_SET(0, &fds); - count = select(1, &fds, 0, 0, &tm); - - switch (count) + struct timeval tm = {wait_on_escape_ms / 1000, + 1000 * (wait_on_escape_ms % 1000)}; + int count = select(1, &fds, 0, 0, &tm); + if (count <= 0) { - case 0: - return WEOF; - - case -1: - return WEOF; - break; - default: - break; - + return WEOF; } } diff --git a/src/input_common.h b/src/input_common.h index 5ec34b39..72ccc073 100644 --- a/src/input_common.h +++ b/src/input_common.h @@ -35,6 +35,9 @@ void input_common_init(int (*ih)()); */ void input_common_destroy(); +// Adjust the escape timeout. +void update_wait_on_escape_ms(); + /** Function used by input_readch to read bytes from stdin until enough bytes have been read to convert them to a wchar_t. Conversion is diff --git a/tests/bind.expect b/tests/bind.expect index 6c80b8f1..9bde0689 100644 --- a/tests/bind.expect +++ b/tests/bind.expect @@ -2,59 +2,124 @@ spawn $fish expect_prompt -# Test switching key bindings to vi mode. -# This should leave the mode in the appropriate state (i.e., insert mode). +# Test switching key bindings to vi mode. This should leave the mode in the +# appropriate state (i.e., insert mode). These initial tests assume the +# default escape timeout of 500ms is in effect. send "set -g fish_key_bindings fish_vi_key_bindings\r" expect_prompt -send -h "echo fail\033" +send "echo fail: default escape timeout" +send "\033" # Delay needed to allow fish to transition to vi "normal" mode. sleep 0.510 -send -h "ddiecho success\r" -expect_prompt -re {\r\nsuccess\r\n} { - puts "vi replace line success" +send "ddi" +send "echo success: default escape timeout\r" +expect_prompt -re {\r\nsuccess: default escape timeout\r\n} { + puts "vi replace line: default escape timeout" } -nounmatched -re {\r\nfail} { - puts stderr "vi replace line fail" + puts stderr "vi replace line fail: default escape timeout" } unmatched { - puts stderr "Couldn't find expected output 'success'" + puts stderr "couldn't find expected output: replace line, default escape timeout" } # Verify that a human can transpose words using \et (which is an emacs default # binding but should be valid while in vi insert mode). -send "echo abc def\033" -# Fish should still be in vi "insert" mode after this delay. +send "echo abc def" +send "\033" +# Fish should still be in vi insert mode after this delay to simulate a slow +# typist. sleep 0.400 send "t\r" expect_prompt -re {\r\ndef abc\r\n} { - puts "vi transpose words success" + puts "vi transpose words: default escape timeout" } unmatched { - puts stderr "vi transpose words fail" + puts stderr "vi transpose words fail: default escape timeout" } # Test replacing a single character. -send -h "echo TEXT\033" +send "echo TEXT" +send "\033" # Delay needed to allow fish to transition to vi "normal" mode. sleep 0.510 -send -h "hhrAi\r" +send "hhrAi\r" expect_prompt -re {\r\nTAXT\r\n} { - puts "vi mode replace success" + puts "vi mode replace: default escape timeout" } -nounmatched -re {\r\nfail} { - puts stderr "vi mode replace fail" + puts stderr "vi mode replace fail: default escape timeout" } unmatched { - puts stderr "Couldn't find expected output 'TAXT'" + puts stderr "couldn't find expected output 'TAXT': default escape timeout" +} + +# Verify that changing the escape timeout has an effect. The vi key bindings +# should still be in effect. +send "set -g fish_escape_delay_ms 100\r" +expect_prompt +send "echo fail: shortened escape timeout" +send "\033" +sleep 0.110 +send "ddi" +send "echo success: shortened escape timeout\r" +expect_prompt -re {\r\nsuccess: shortened escape timeout\r\n} { + puts "vi replace line: shortened escape timeout" +} -nounmatched -re {\r\nfail} { + puts stderr "vi replace line fail: shortened escape timeout" +} unmatched { + puts stderr "couldn't find expected output: replace_line, shortened escape timeout" +} + +# Verify that we don't switch to vi normal mode if we don't wait long enough +# after sending escape. The vi key bindings should still be in effect. +send "echo fail: no normal mode" +send "\033" +sleep 0.050 +send "ddi" +send "inserted\r" +expect_prompt -re {\r\nfail: no normal modediinserted\r\n} { + puts "vi normal mode: shortened escape timeout" +} -nounmatched -re {\r\nfail} { + puts stderr "vi normal mode fail: shortened escape timeout" +} unmatched { + puts stderr "couldn't find expected output: no normal mode" } # Switch back to regular (emacs mode) key bindings. -send -h "set -g fish_key_bindings fish_default_key_bindings\r" +send "set -g fish_key_bindings fish_default_key_bindings\r" +expect_prompt + +# Verify the emacs transpose word (\et) behavior using various delays, +# including none, after the escape character. + +# Start by testing with no delay. This should transpose the words. +send "echo abc def" +send "\033t\r" +expect_prompt -re {\r\ndef abc\r\n} { + puts "emacs transpose words: no escape delay" +} unmatched { + puts stderr "emacs transpose words fail: no escape delay" +} + +# Now test with a delay > 0 and < the escape timeout. This should transpose +# the words. +send "set -g fish_escape_delay_ms 100\r" expect_prompt -send "echo success\r" -expect_prompt -re {\r\nsuccess\r\n} { - puts "emacs success" -} unmatched { - puts stderr "Couldn't find expected output 'success'" -} timeout { - set msg "" - append msg "Timeout after setting fish_key_bindings to fish_default_key_bindings\n" \ - "\$fish_bind_mode is most likely still set to 'insert'" - abort $msg +send "echo ghi jkl" +send "\033" +sleep 0.050 +send "t\r" +expect_prompt -re {\r\njkl ghi\r\n} { + puts "emacs transpose words: short escape delay" +} unmatched { + puts stderr "emacs transpose words fail: short escape delay" +} + +# Now test with a delay > the escape timeout. The transposition should not +# occur and the "t" should become part of the text that is echoed. +send "echo mno pqr" +send "\033" +sleep 0.110 +send "t\r" +expect_prompt -re {\r\nmno pqrt\r\n} { + puts "emacs transpose words: long escape delay" +} unmatched { + puts stderr "emacs transpose words fail: long escape delay" } diff --git a/tests/bind.expect.out b/tests/bind.expect.out index f82eec23..f4682d2c 100644 --- a/tests/bind.expect.out +++ b/tests/bind.expect.out @@ -1,4 +1,8 @@ -vi replace line success -vi transpose words success -vi mode replace success -emacs success +vi replace line: default escape timeout +vi transpose words: default escape timeout +vi mode replace: default escape timeout +vi replace line: shortened escape timeout +vi normal mode: shortened escape timeout +emacs transpose words: no escape delay +emacs transpose words: short escape delay +emacs transpose words: long escape delay -- cgit v1.2.3