Add support for Ctrl+Left and Ctrl+Right arrow keys to move the cursor by word in the command line editor. Ctrl+Left moves backward to the start of the previous word, and Ctrl+Right moves forward to the end of the next word. Decode the escape sequences (ESC[1;5D and ESC[1;5C) arein cli_getch.c and convert then to CTL_CH('r') and CTL_CH('t') respectively. Handle the actual word-movement logic is then handled in cli_readline.c, guarded by CONFIG_CMDLINE_EDITOR Co-developed-by: Claude Opus 4.5 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- common/cli_getch.c | 25 +++++++++++++++++++++++++ common/cli_readline.c | 31 +++++++++++++++++++++++++++++++ test/boot/editenv.c | 8 ++++++++ 3 files changed, 64 insertions(+) diff --git a/common/cli_getch.c b/common/cli_getch.c index a5ed6eb6fcf..8df1997911a 100644 --- a/common/cli_getch.c +++ b/common/cli_getch.c @@ -114,6 +114,12 @@ static int cli_ch_esc(struct cli_ch_state *cch, int ichar, if (cch->esc_save[2] == '2') act = ESC_SAVE; break; + case ';': + /* Ctrl+arrow: ESC [ 1 ; */ + if (CONFIG_IS_ENABLED(CMDLINE_EDITOR) && + cch->esc_save[2] == '1') + act = ESC_SAVE; + break; } break; case 4: @@ -122,12 +128,31 @@ static int cli_ch_esc(struct cli_ch_state *cch, int ichar, case '1': act = ESC_SAVE; break; /* bracketed paste */ + case '5': + /* Ctrl+arrow: ESC [ 1 ; 5 */ + if (CONFIG_IS_ENABLED(CMDLINE_EDITOR) && + cch->esc_save[3] == ';') + act = ESC_SAVE; + break; } break; case 5: if (ichar == '~') { /* bracketed paste */ ichar = 0; act = ESC_CONVERTED; + } else if (CONFIG_IS_ENABLED(CMDLINE_EDITOR) && + cch->esc_save[4] == '5') { + /* Ctrl+arrow: ESC [ 1 ; 5 D/C */ + switch (ichar) { + case 'D': /* Ctrl+<- key */ + ichar = CTL_CH('r'); + act = ESC_CONVERTED; + break; /* pass to backward-word handler */ + case 'C': /* Ctrl+-> key */ + ichar = CTL_CH('t'); + act = ESC_CONVERTED; + break; /* pass to forward-word handler */ + } } } diff --git a/common/cli_readline.c b/common/cli_readline.c index d2b02933fd7..4c25e9a04ba 100644 --- a/common/cli_readline.c +++ b/common/cli_readline.c @@ -333,6 +333,37 @@ int cread_line_process_ch(struct cli_line_state *cls, char ichar) cls->num--; } break; + case CTL_CH('r'): /* backward-word */ + if (CONFIG_IS_ENABLED(CMDLINE_EDITOR) && cls->num) { + uint pos = cls->num; + + /* skip spaces before word */ + while (pos > 0 && buf[pos - 1] == ' ') + pos--; + /* skip word characters */ + while (pos > 0 && buf[pos - 1] != ' ') + pos--; + cls_putchars(cls, cls->num - pos, CTL_BACKSPACE); + cls->num = pos; + } + break; + case CTL_CH('t'): /* forward-word */ + if (CONFIG_IS_ENABLED(CMDLINE_EDITOR) && cls->num < cls->eol_num) { + uint pos = cls->num; + + /* skip spaces after cursor */ + while (pos < cls->eol_num && buf[pos] == ' ') { + cls_putch(cls, buf[pos]); + pos++; + } + /* skip word characters */ + while (pos < cls->eol_num && buf[pos] != ' ') { + cls_putch(cls, buf[pos]); + pos++; + } + cls->num = pos; + } + break; case CTL_CH('d'): if (cls->num < cls->eol_num) { uint wlen; diff --git a/test/boot/editenv.c b/test/boot/editenv.c index 0f9db54474d..20273c53647 100644 --- a/test/boot/editenv.c +++ b/test/boot/editenv.c @@ -171,6 +171,14 @@ static int editenv_test_funcs(struct unit_test_state *uts) ut_assertok(expo_editenv_init("testvar", initial, &info)); ut_asserteq(16611, ut_check_video(uts, "init")); + /* Navigate up to previous line */ + ut_assertok(editenv_send(&info, BKEY_UP)); + ut_asserteq(16684, ut_check_video(uts, "up")); + + /* Navigate back down */ + ut_assertok(editenv_send(&info, BKEY_DOWN)); + ut_asserteq(16611, ut_check_video(uts, "down")); + /* Type a character and press Enter to accept */ ut_assertok(editenv_send(&info, '*')); ut_asserteq(16689, ut_check_video(uts, "insert")); -- 2.43.0