[Concept,11/19] cli: Add a Kconfig for enhanced editing

Message ID 20260130035849.3580212-12-simon.glass@canonical.com
State New
Headers
Series Enhanced command-line editing with undo/redo support |

Commit Message

Simon Glass Jan. 30, 2026, 3:58 a.m. UTC
  Add a new Kconfig option CONFIG_CMDLINE_EDITOR to enable enhanced
command-line editing features. This replaces CLI_READLINE_CALLBACK and
is enabled by default when EXPO is enabled.

Create struct cli_editor_state to hold state for enhanced editing
features, initially containing:
- putch: callback for character output redirection
- line_nav: callback for multi-line navigation (Ctrl-P/N)
- multiline: flag for multi-line input mode

This struct is embedded in cli_line_state when CMDLINE_EDITOR is enabled.
Add cli_editor() accessor function that returns a pointer to the editor
state, or NULL if CMDLINE_EDITOR is not enabled.

Update cli_readline.c and scene_txtin.c to use the new accessor.

Co-developed-by: Claude Opus 4.5 <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
---

 boot/scene_txtin.c    | 10 ++++---
 cmd/Kconfig           | 16 +++++++----
 common/cli_readline.c | 12 +++++---
 include/cli.h         | 66 +++++++++++++++++++++++++++++++++++++------
 4 files changed, 81 insertions(+), 23 deletions(-)
  

Patch

diff --git a/boot/scene_txtin.c b/boot/scene_txtin.c
index cde9fdb8ccf..79814891cdc 100644
--- a/boot/scene_txtin.c
+++ b/boot/scene_txtin.c
@@ -112,6 +112,7 @@  int scene_txtin_render_deps(struct scene *scn, struct scene_obj *obj,
 			    struct scene_txtin *tin)
 {
 	struct cli_line_state *cls = &tin->cls;
+	struct cli_editor_state *ed = cli_editor(cls);
 	const bool open = obj->flags & SCENEOF_OPEN;
 	struct udevice *cons = scn->expo->cons;
 	void *ctx = tin->ctx;
@@ -121,7 +122,7 @@  int scene_txtin_render_deps(struct scene *scn, struct scene_obj *obj,
 	if (open) {
 		scene_render_obj(scn, tin->edit_id, ctx);
 
-		if (cls->multiline) {
+		if (ed->multiline) {
 			/* for multiline, set cursor position directly */
 			struct scene_obj_txt *txt;
 
@@ -260,6 +261,7 @@  int scene_txtin_open(struct scene *scn, struct scene_obj *obj,
 		     struct scene_txtin *tin)
 {
 	struct cli_line_state *cls = &tin->cls;
+	struct cli_editor_state *ed = cli_editor(cls);
 	struct udevice *cons = scn->expo->cons;
 	struct scene_obj_txt *txt;
 	void *ctx;
@@ -286,11 +288,11 @@  int scene_txtin_open(struct scene *scn, struct scene_obj *obj,
 	vidconsole_entry_start(cons, ctx);
 	cli_cread_init(cls, abuf_data(&tin->buf), abuf_size(&tin->buf));
 	cls->insert = true;
-	cls->putch = scene_txtin_putch;
+	ed->putch = scene_txtin_putch;
 	cls->priv = scn;
 	if (obj->type == SCENEOBJT_TEXTEDIT) {
-		cls->multiline = true;
-		cls->line_nav = scene_txtin_line_nav;
+		ed->multiline = true;
+		ed->line_nav = scene_txtin_line_nav;
 	}
 	cli_cread_add_initial(cls);
 
diff --git a/cmd/Kconfig b/cmd/Kconfig
index d8f57c446a5..448a6e9fe39 100644
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -54,13 +54,17 @@  config CMDLINE_EDITING
 	  Enable editing and History functions for interactive command line
 	  input operations
 
-config CLI_READLINE_CALLBACK
-	bool "Support a callback for character output"
+config CMDLINE_EDITOR
+	bool "Enhanced command-line editing features"
 	depends on CMDLINE_EDITING
-	help
-	  Enable a callback for character output during line editing. This
-	  allows redirection of output to a different destination, such as
-	  a vidconsole. This is used by expo to support textline editing.
+	default y if EXPO
+	help
+	  Enable enhanced editing features for the command line, including:
+	  - Character-output callback for redirection to vidconsole
+	  - Multi-line navigation callback (Ctrl-P/N)
+	  - Ctrl+Left/Right arrow keys to move by words
+	  - Undo/redo support (Ctrl+Z / Ctrl+Shift+Z)
+	  - Yank/paste of killed text (Ctrl+Y)
 
 config CMDLINE_PS_SUPPORT
 	bool "Enable support for changing the command prompt string at run-time"
diff --git a/common/cli_readline.c b/common/cli_readline.c
index 38f825184df..d2b02933fd7 100644
--- a/common/cli_readline.c
+++ b/common/cli_readline.c
@@ -79,8 +79,10 @@  static char *delete_char (char *buffer, char *p, int *colp, int *np, int plen)
  */
 static void cls_putch(struct cli_line_state *cls, int ch)
 {
-	if (CONFIG_IS_ENABLED(CLI_READLINE_CALLBACK) && cls->putch)
-		cls->putch(cls, ch);
+	struct cli_editor_state *ed = cli_editor(cls);
+
+	if (ed && ed->putch)
+		ed->putch(cls, ch);
 	else
 		putc(ch);
 }
@@ -299,6 +301,7 @@  static void cread_add_str(struct cli_line_state *cls, char *str, int strsize,
 
 int cread_line_process_ch(struct cli_line_state *cls, char ichar)
 {
+	struct cli_editor_state *ed;
 	char *buf = cls->buf;
 
 	/* ichar=0x0 when error occurs in U-Boot getc */
@@ -405,10 +408,11 @@  int cread_line_process_ch(struct cli_line_state *cls, char ichar)
 		break;
 	case CTL_CH('p'):
 	case CTL_CH('n'):
-		if (cls->multiline && cls->line_nav) {
+		ed = cli_editor(cls);
+		if (ed && ed->multiline && ed->line_nav) {
 			int new_num;
 
-			new_num = cls->line_nav(cls, ichar == CTL_CH('p'));
+			new_num = ed->line_nav(cls, ichar == CTL_CH('p'));
 			if (new_num < 0) {
 				getcmd_cbeep(cls);
 				break;
diff --git a/include/cli.h b/include/cli.h
index f1e5887fa56..b6a8a6be1dd 100644
--- a/include/cli.h
+++ b/include/cli.h
@@ -25,6 +25,43 @@  struct cli_ch_state {
 	bool emitting;
 };
 
+struct cli_line_state;
+
+/**
+ * struct cli_editor_state - state for enhanced editing features
+ *
+ * This is only available when CONFIG_CMDLINE_EDITOR is enabled.
+ *
+ * @putch: Output a character (NULL to use putc())
+ * @line_nav: Handle multi-line navigation (Ctrl-P/N)
+ * @multiline: true if input may contain multiple lines (enables
+ *	Ctrl-P/N for line navigation instead of history)
+ */
+struct cli_editor_state {
+	/**
+	 * @putch: Output a character (NULL to use putc())
+	 *
+	 * @cls: CLI line state
+	 * @ch: Character to output
+	 */
+	void (*putch)(struct cli_line_state *cls, int ch);
+
+	/**
+	 * @line_nav: Handle multi-line navigation (Ctrl-P/N)
+	 *
+	 * @cls: CLI line state
+	 * @up: true for previous line, false for next
+	 * Return: new cursor position, or -ve if at boundary
+	 */
+	int (*line_nav)(struct cli_line_state *cls, bool up);
+
+	/**
+	 * @multiline: true if input may contain multiple lines (enables
+	 * Ctrl-P/N for line navigation instead of history)
+	 */
+	bool multiline;
+};
+
 /**
  * struct cli_line_state - state of the line editor
  *
@@ -34,15 +71,10 @@  struct cli_ch_state {
  * @history: true if history should be accessible
  * @cmd_complete: true if tab completion should be enabled (requires @prompt to
  *	be set)
- * @multiline: true if input may contain multiple lines (enables Ctrl-P/N for
- *	line navigation instead of history)
  * @buf: Buffer containing line
  * @prompt: Prompt for the line
- * @putch: Function to call to output a character (NULL to use putc())
- * @line_nav: Function to call for multi-line navigation (Ctrl-P/N). Called with
- *	@up true for previous line, false for next. Returns new cursor position,
- *	or -ve if at boundary
  * @priv: Private data for callbacks
+ * @ed: Editor state for enhanced features (if CONFIG_CMDLINE_EDITOR)
  */
 struct cli_line_state {
 	uint num;
@@ -51,14 +83,30 @@  struct cli_line_state {
 	bool insert;
 	bool history;
 	bool cmd_complete;
-	bool multiline;
 	char *buf;
 	const char *prompt;
-	void (*putch)(struct cli_line_state *cls, int ch);
-	int (*line_nav)(struct cli_line_state *cls, bool up);
 	void *priv;
+#if CONFIG_IS_ENABLED(CMDLINE_EDITOR)
+	struct cli_editor_state ed;
+#endif
 };
 
+/**
+ * cli_editor() - Get the editor state from a line state
+ *
+ * @cls: CLI line state
+ * Return: Pointer to editor state, or NULL if CONFIG_CMDLINE_EDITOR is not
+ * enabled
+ */
+static inline struct cli_editor_state *cli_editor(struct cli_line_state *cls)
+{
+#if CONFIG_IS_ENABLED(CMDLINE_EDITOR)
+	return &cls->ed;
+#else
+	return NULL;
+#endif
+}
+
 /**
  * Go into the command loop
  *