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(+)
@@ -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 */
+ }
}
}
@@ -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;
@@ -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"));