runtime: implement an extensible line editor

the line editor supports custom keybinds and data storage, allowing for
a wide range of functionality to be implemented via plugins.

included plugins include:
* core: basic input and cursor movement.
* history: input history storage and retrieval (unfinished)
* highlight: support for colour-highlighting the line buffer (unfinished)
* syntax: parses the line buffer and uses `highlight` to apply syntax highlighting (unfinished).
* shell-integration:
  - allows a `prompt` function to be defined, which will be called to get the text to
    be used for the prompt
  - exposes key properties of the line editor to the shell environment, allowing line editor
    functionality to be extended via shell scripts.
This commit is contained in:
2026-06-20 15:13:34 +01:00
parent f622c2eba3
commit ad8b284869
21 changed files with 1857 additions and 1 deletions
@@ -0,0 +1,21 @@
#ifndef BSHELL_LINE_EDITOR_CORE_H_
#define BSHELL_LINE_EDITOR_CORE_H_
#include <fx/macros.h>
FX_DECLS_BEGIN;
#define BSHELL_TYPE_LINE_EDITOR_CORE (bshell_line_ed_core_get_type())
FX_DECLARE_TYPE(bshell_line_ed_core);
FX_TYPE_CLASS_DECLARATION_BEGIN(bshell_line_ed_core)
FX_TYPE_CLASS_DECLARATION_END(bshell_line_ed_core)
extern fx_type_id bshell_line_ed_core_get_type(void);
FX_TYPE_DEFAULT_CONSTRUCTOR(bshell_line_ed_core, BSHELL_TYPE_LINE_EDITOR_CORE);
FX_DECLS_END;
#endif
@@ -0,0 +1,23 @@
#ifndef BSHELL_LINE_EDITOR_HIGHLIGHT_H_
#define BSHELL_LINE_EDITOR_HIGHLIGHT_H_
#include <fx/macros.h>
FX_DECLS_BEGIN;
#define BSHELL_TYPE_LINE_EDITOR_HIGHLIGHT (bshell_line_ed_highlight_get_type())
FX_DECLARE_TYPE(bshell_line_ed_highlight);
FX_TYPE_CLASS_DECLARATION_BEGIN(bshell_line_ed_highlight)
FX_TYPE_CLASS_DECLARATION_END(bshell_line_ed_highlight)
extern fx_type_id bshell_line_ed_highlight_get_type(void);
FX_TYPE_DEFAULT_CONSTRUCTOR(
bshell_line_ed_highlight,
BSHELL_TYPE_LINE_EDITOR_HIGHLIGHT);
FX_DECLS_END;
#endif
@@ -0,0 +1,23 @@
#ifndef BSHELL_LINE_EDITOR_HISTORY_H_
#define BSHELL_LINE_EDITOR_HISTORY_H_
#include <fx/macros.h>
FX_DECLS_BEGIN;
#define BSHELL_TYPE_LINE_EDITOR_HISTORY (bshell_line_ed_history_get_type())
FX_DECLARE_TYPE(bshell_line_ed_history);
FX_TYPE_CLASS_DECLARATION_BEGIN(bshell_line_ed_history)
FX_TYPE_CLASS_DECLARATION_END(bshell_line_ed_history)
extern fx_type_id bshell_line_ed_history_get_type(void);
FX_TYPE_DEFAULT_CONSTRUCTOR(
bshell_line_ed_history,
BSHELL_TYPE_LINE_EDITOR_HISTORY);
FX_DECLS_END;
#endif
@@ -0,0 +1,44 @@
#ifndef BSHELL_LINE_EDITOR_KEYBIND_H_
#define BSHELL_LINE_EDITOR_KEYBIND_H_
#include <bshell/status.h>
#include <fx/macros.h>
#include <fx/term/tty.h>
FX_DECLS_BEGIN;
#define BSHELL_LINE_EDITOR_KEYBIND_RESULT(status, action) \
((struct bshell_line_ed_keybind_result) { \
.r_status = (status), \
.r_action = (action), \
})
typedef FX_TYPE_FWDREF(bshell_line_ed) bshell_line_ed;
struct bshell_line_ed_keybind_result {
enum bshell_status r_status;
enum {
BSHELL_LINE_EDITOR_KEYBIND_CONTINUE = 0,
BSHELL_LINE_EDITOR_KEYBIND_STOP,
} r_action;
};
typedef struct bshell_line_ed_keybind_result (
*bshell_line_ed_keybind_callback_t)(bshell_line_ed *, fx_keycode);
#define BSHELL_TYPE_LINE_EDITOR_KEYBIND (bshell_line_ed_keybind_get_type())
FX_DECLARE_TYPE(bshell_line_ed_keybind);
FX_TYPE_CLASS_DECLARATION_BEGIN(bshell_line_ed_keybind)
FX_TYPE_CLASS_DECLARATION_END(bshell_line_ed_keybind)
extern fx_type_id bshell_line_ed_keybind_get_type(void);
FX_TYPE_DEFAULT_CONSTRUCTOR(
bshell_line_ed_keybind,
BSHELL_TYPE_LINE_EDITOR_KEYBIND);
FX_DECLS_END;
#endif
@@ -0,0 +1,68 @@
#ifndef BSHELL_LINE_EDITOR_H_
#define BSHELL_LINE_EDITOR_H_
#include <bshell/line-editor/keybind.h>
#include <bshell/line-editor/plugin.h>
#include <fx/macros.h>
#include <fx/string.h>
#include <fx/term/tty.h>
FX_DECLS_BEGIN;
#define BSHELL_TYPE_LINE_EDITOR (bshell_line_ed_get_type())
FX_DECLARE_TYPE(bshell_line_ed);
FX_TYPE_CLASS_DECLARATION_BEGIN(bshell_line_ed)
FX_TYPE_CLASS_DECLARATION_END(bshell_line_ed)
extern fx_type_id bshell_line_ed_get_type(void);
FX_TYPE_DEFAULT_CONSTRUCTOR(bshell_line_ed, BSHELL_TYPE_LINE_EDITOR);
extern enum bshell_status bshell_line_ed_readline(
bshell_line_ed *ed,
fx_string *out);
extern struct bshell_line_source *bshell_line_ed_get_line_source(
bshell_line_ed *ed);
extern bshell_line_ed_keybind *bshell_line_ed_bind_key(
bshell_line_ed *ed,
const char *key,
bshell_line_ed_keybind_callback_t callback,
unsigned long priority);
extern enum bshell_status bshell_line_ed_unbind_key(
bshell_line_ed *ed,
bshell_line_ed_keybind *bind);
extern unsigned long bshell_line_ed_get_cursor(const bshell_line_ed *ed);
extern void bshell_line_ed_set_cursor(bshell_line_ed *ed, unsigned long cursor);
extern unsigned long bshell_line_ed_move_cursor(
bshell_line_ed *ed,
long move_amount);
extern enum bshell_status bshell_line_ed_set_done(bshell_line_ed *ed);
extern enum bshell_status bshell_line_ed_set_eof(bshell_line_ed *ed);
extern bool bshell_line_ed_is_done(const bshell_line_ed *ed);
extern bool bshell_line_ed_is_eof(const bshell_line_ed *ed);
extern fx_string *bshell_line_ed_get_prompt(bshell_line_ed *ed);
extern fx_string *bshell_line_ed_get_buffer(bshell_line_ed *ed);
extern fx_tty *bshell_line_ed_get_tty(bshell_line_ed *ed);
extern fx_value bshell_line_ed_get_data(
const bshell_line_ed *ed,
const char *key);
extern void bshell_line_ed_put_data(
bshell_line_ed *ed,
const char *key,
fx_value value);
extern void bshell_line_ed_buffer_changed(bshell_line_ed *ed);
extern enum bshell_status bshell_line_ed_add_plugin(
bshell_line_ed *ed,
bshell_line_ed_plugin *plugin);
FX_DECLS_END;
#endif
@@ -0,0 +1,42 @@
#ifndef BSHELL_LINE_EDITOR_PLUGIN_H_
#define BSHELL_LINE_EDITOR_PLUGIN_H_
#include <bshell/status.h>
#include <fx/macros.h>
#include <fx/value.h>
FX_DECLS_BEGIN;
typedef FX_TYPE_FWDREF(bshell_line_ed) bshell_line_ed;
#define BSHELL_TYPE_LINE_EDITOR_PLUGIN (bshell_line_ed_plugin_get_type())
FX_DECLARE_TYPE(bshell_line_ed_plugin);
FX_TYPE_CLASS_DECLARATION_BEGIN(bshell_line_ed_plugin)
enum bshell_status (*p_load)(bshell_line_ed_plugin *, bshell_line_ed *);
enum bshell_status (
*p_unload)(bshell_line_ed_plugin *, bshell_line_ed *);
enum bshell_status (
*p_render_prompt)(bshell_line_ed_plugin *, bshell_line_ed *);
enum bshell_status (
*p_render_buffer)(bshell_line_ed_plugin *, bshell_line_ed *);
enum bshell_status (
*p_buffer_changed)(bshell_line_ed_plugin *, bshell_line_ed *);
enum bshell_status (*p_get_data)(
bshell_line_ed_plugin *,
bshell_line_ed *,
const char *,
fx_value *);
enum bshell_status (*p_put_data)(
bshell_line_ed_plugin *,
bshell_line_ed *,
const char *,
const fx_value *);
FX_TYPE_CLASS_DECLARATION_END(bshell_line_ed_plugin)
extern fx_type_id bshell_line_ed_plugin_get_type(void);
FX_DECLS_END;
#endif
@@ -0,0 +1,25 @@
#ifndef BSHELL_LINE_EDITOR_SHELL_INTEGRATION_H_
#define BSHELL_LINE_EDITOR_SHELL_INTEGRATION_H_
#include <fx/macros.h>
FX_DECLS_BEGIN;
struct bshell_runtime;
#define BSHELL_TYPE_LINE_EDITOR_SHELL_INTEGRATION \
(bshell_line_ed_shell_integration_get_type())
FX_DECLARE_TYPE(bshell_line_ed_shell_integration);
FX_TYPE_CLASS_DECLARATION_BEGIN(bshell_line_ed_shell_integration)
FX_TYPE_CLASS_DECLARATION_END(bshell_line_ed_shell_integration)
extern fx_type_id bshell_line_ed_shell_integration_get_type(void);
extern bshell_line_ed_shell_integration *bshell_line_ed_shell_integration_create(
struct bshell_runtime *rt);
FX_DECLS_END;
#endif
@@ -0,0 +1,23 @@
#ifndef BSHELL_LINE_EDITOR_SYNTAX_H_
#define BSHELL_LINE_EDITOR_SYNTAX_H_
#include <fx/macros.h>
FX_DECLS_BEGIN;
#define BSHELL_TYPE_LINE_EDITOR_SYNTAX (bshell_line_ed_syntax_get_type())
FX_DECLARE_TYPE(bshell_line_ed_syntax);
FX_TYPE_CLASS_DECLARATION_BEGIN(bshell_line_ed_syntax)
FX_TYPE_CLASS_DECLARATION_END(bshell_line_ed_syntax)
extern fx_type_id bshell_line_ed_syntax_get_type(void);
FX_TYPE_DEFAULT_CONSTRUCTOR(
bshell_line_ed_syntax,
BSHELL_TYPE_LINE_EDITOR_SYNTAX);
FX_DECLS_END;
#endif