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:
@@ -0,0 +1,160 @@
|
||||
#include <bshell/line-editor/line-editor.h>
|
||||
#include <bshell/line-editor/plugin.h>
|
||||
#include <bshell/line-editor/shell-integration.h>
|
||||
#include <bshell/runtime/cmdcall.h>
|
||||
#include <bshell/runtime/pipeline.h>
|
||||
#include <bshell/runtime/runtime.h>
|
||||
#include <bshell/runtime/scope.h>
|
||||
#include <fx/term/tty.h>
|
||||
|
||||
#define DEFAULT_PROMPT "Bsh> "
|
||||
|
||||
struct bshell_line_ed_shell_integration_p {
|
||||
struct bshell_runtime *si_rt;
|
||||
fx_hashtable *si_data;
|
||||
fx_string *si_default_prompt;
|
||||
bshell_pipeline *si_pipeline;
|
||||
bshell_cmdcall *si_prompt;
|
||||
};
|
||||
|
||||
static void init(fx_object *obj, void *priv)
|
||||
{
|
||||
struct bshell_line_ed_shell_integration_p *p = priv;
|
||||
p->si_default_prompt = fx_string_create_from_cstr(DEFAULT_PROMPT);
|
||||
p->si_pipeline = bshell_pipeline_create();
|
||||
p->si_prompt = bshell_cmdcall_create();
|
||||
bshell_cmdcall_push_arg(p->si_prompt, FX_CSTR("prompt"));
|
||||
bshell_pipeline_add_cmdcall(p->si_pipeline, p->si_prompt);
|
||||
}
|
||||
|
||||
static void fini(fx_object *obj, void *priv)
|
||||
{
|
||||
struct bshell_line_ed_shell_integration_p *p = priv;
|
||||
bshell_pipeline_unref(p->si_pipeline);
|
||||
bshell_cmdcall_unref(p->si_prompt);
|
||||
fx_string_unref(p->si_default_prompt);
|
||||
}
|
||||
|
||||
bshell_line_ed_shell_integration *bshell_line_ed_shell_integration_create(
|
||||
struct bshell_runtime *rt)
|
||||
{
|
||||
bshell_line_ed_shell_integration *out
|
||||
= fx_object_create(BSHELL_TYPE_LINE_EDITOR_SHELL_INTEGRATION);
|
||||
if (!out) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct bshell_line_ed_shell_integration_p *p = fx_object_get_private(
|
||||
out,
|
||||
BSHELL_TYPE_LINE_EDITOR_SHELL_INTEGRATION);
|
||||
p->si_rt = rt;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
static enum bshell_status load(
|
||||
bshell_line_ed_plugin *plugin,
|
||||
bshell_line_ed *ed)
|
||||
{
|
||||
struct bshell_line_ed_shell_integration_p *shell
|
||||
= fx_object_get_private(
|
||||
plugin,
|
||||
BSHELL_TYPE_LINE_EDITOR_SHELL_INTEGRATION);
|
||||
shell->si_data = fx_hashtable_create();
|
||||
struct bshell_runtime_scope *global
|
||||
= bshell_runtime_get_global_scope(shell->si_rt);
|
||||
|
||||
bshell_variable *item
|
||||
= bshell_runtime_scope_define_variable(global, "line_editor");
|
||||
bshell_variable_set_value(item, FX_VALUE_OBJECT(shell->si_data));
|
||||
printf("shell integration loaded\n");
|
||||
return BSHELL_SUCCESS;
|
||||
}
|
||||
|
||||
static enum bshell_status get_prompt(
|
||||
struct bshell_line_ed_shell_integration_p *shell,
|
||||
bshell_line_ed *ed,
|
||||
fx_value *out)
|
||||
{
|
||||
enum bshell_status status
|
||||
= bshell_cmdcall_resolve(shell->si_prompt, shell->si_rt);
|
||||
if (status != BSHELL_SUCCESS) {
|
||||
*out = fx_value_copy_return(
|
||||
FX_VALUE_OBJECT(shell->si_default_prompt));
|
||||
return BSHELL_SUCCESS;
|
||||
}
|
||||
|
||||
int eof = 0;
|
||||
status = bshell_pipeline_pump_record(
|
||||
shell->si_pipeline,
|
||||
shell->si_rt,
|
||||
out,
|
||||
&eof);
|
||||
return status;
|
||||
}
|
||||
|
||||
static enum bshell_status get_data(
|
||||
bshell_line_ed_plugin *plugin,
|
||||
bshell_line_ed *ed,
|
||||
const char *key,
|
||||
fx_value *out)
|
||||
{
|
||||
struct bshell_line_ed_shell_integration_p *shell
|
||||
= fx_object_get_private(
|
||||
plugin,
|
||||
BSHELL_TYPE_LINE_EDITOR_SHELL_INTEGRATION);
|
||||
if (!strcmp(key, "prompt")) {
|
||||
return get_prompt(shell, ed, out);
|
||||
}
|
||||
|
||||
const fx_value *v = fx_hashtable_get(shell->si_data, &FX_CSTR(key));
|
||||
if (v) {
|
||||
fx_value_copy(out, v);
|
||||
} else {
|
||||
*out = FX_VALUE_EMPTY;
|
||||
}
|
||||
|
||||
return BSHELL_SUCCESS;
|
||||
}
|
||||
|
||||
static enum bshell_status put_data(
|
||||
bshell_line_ed_plugin *plugin,
|
||||
bshell_line_ed *ed,
|
||||
const char *key,
|
||||
const fx_value *value)
|
||||
{
|
||||
if (!strcmp(key, "prompt")) {
|
||||
return BSHELL_SUCCESS;
|
||||
}
|
||||
|
||||
struct bshell_line_ed_shell_integration_p *shell
|
||||
= fx_object_get_private(
|
||||
plugin,
|
||||
BSHELL_TYPE_LINE_EDITOR_SHELL_INTEGRATION);
|
||||
fx_hashtable_put(shell->si_data, &FX_CSTR(key), (fx_value *)value);
|
||||
return BSHELL_SUCCESS;
|
||||
}
|
||||
|
||||
FX_TYPE_CLASS_BEGIN(bshell_line_ed_shell_integration)
|
||||
FX_TYPE_VTABLE_INTERFACE_BEGIN(fx_object, FX_TYPE_OBJECT)
|
||||
FX_INTERFACE_ENTRY(to_string) = NULL;
|
||||
FX_TYPE_VTABLE_INTERFACE_END(fx_object, FX_TYPE_OBJECT)
|
||||
|
||||
FX_TYPE_VTABLE_INTERFACE_BEGIN(
|
||||
bshell_line_ed_plugin,
|
||||
BSHELL_TYPE_LINE_EDITOR_PLUGIN)
|
||||
FX_INTERFACE_ENTRY(p_load) = load;
|
||||
FX_INTERFACE_ENTRY(p_get_data) = get_data;
|
||||
FX_INTERFACE_ENTRY(p_put_data) = put_data;
|
||||
FX_TYPE_VTABLE_INTERFACE_END(bshell_command, BSHELL_TYPE_COMMAND)
|
||||
FX_TYPE_CLASS_END(bshell_line_ed_shell_integration)
|
||||
|
||||
FX_TYPE_DEFINITION_BEGIN(bshell_line_ed_shell_integration)
|
||||
FX_TYPE_ID(0x8e89c8d0, 0xbc82, 0x4153, 0x9607, 0xa7a710486596);
|
||||
FX_TYPE_NAME("bshell.runtime.line_editor.plugins.shell_integration");
|
||||
FX_TYPE_EXTENDS(BSHELL_TYPE_LINE_EDITOR_PLUGIN);
|
||||
FX_TYPE_CLASS(bshell_line_ed_shell_integration_class);
|
||||
FX_TYPE_INSTANCE_INIT(init);
|
||||
FX_TYPE_INSTANCE_FINI(fini);
|
||||
FX_TYPE_INSTANCE_PRIVATE(struct bshell_line_ed_shell_integration_p);
|
||||
FX_TYPE_DEFINITION_END(bshell_line_ed_shell_integration)
|
||||
Reference in New Issue
Block a user