230 lines
6.1 KiB
C
230 lines
6.1 KiB
C
|
|
#include <bshell/line-editor/core.h>
|
||
|
|
#include <bshell/line-editor/line-editor.h>
|
||
|
|
#include <bshell/line-editor/plugin.h>
|
||
|
|
#include <fx/collections/hashtable.h>
|
||
|
|
#include <fx/term/tty.h>
|
||
|
|
|
||
|
|
struct bshell_line_ed_core_p {
|
||
|
|
bshell_line_ed_keybind *c_key_cursor_left;
|
||
|
|
bshell_line_ed_keybind *c_key_cursor_right;
|
||
|
|
bshell_line_ed_keybind *c_key_return;
|
||
|
|
bshell_line_ed_keybind *c_key_eof;
|
||
|
|
bshell_line_ed_keybind *c_key_generic;
|
||
|
|
bshell_line_ed_keybind *c_key_backspace;
|
||
|
|
|
||
|
|
fx_hashtable *c_data;
|
||
|
|
};
|
||
|
|
|
||
|
|
static void init(fx_object *obj, void *priv)
|
||
|
|
{
|
||
|
|
struct bshell_line_ed_core_p *core = priv;
|
||
|
|
core->c_data = fx_hashtable_create();
|
||
|
|
}
|
||
|
|
|
||
|
|
static void fini(fx_object *obj, void *priv)
|
||
|
|
{
|
||
|
|
struct bshell_line_ed_core_p *core = priv;
|
||
|
|
fx_hashtable_unref(core->c_data);
|
||
|
|
}
|
||
|
|
|
||
|
|
static struct bshell_line_ed_keybind_result delete_backward(
|
||
|
|
bshell_line_ed *ed,
|
||
|
|
fx_keycode key)
|
||
|
|
{
|
||
|
|
return BSHELL_LINE_EDITOR_KEYBIND_RESULT(
|
||
|
|
BSHELL_SUCCESS,
|
||
|
|
BSHELL_LINE_EDITOR_KEYBIND_CONTINUE);
|
||
|
|
}
|
||
|
|
|
||
|
|
static struct bshell_line_ed_keybind_result cursor_left(
|
||
|
|
bshell_line_ed *ed,
|
||
|
|
fx_keycode key)
|
||
|
|
{
|
||
|
|
bshell_line_ed_move_cursor(ed, -1);
|
||
|
|
return BSHELL_LINE_EDITOR_KEYBIND_RESULT(
|
||
|
|
BSHELL_SUCCESS,
|
||
|
|
BSHELL_LINE_EDITOR_KEYBIND_CONTINUE);
|
||
|
|
}
|
||
|
|
|
||
|
|
static struct bshell_line_ed_keybind_result cursor_right(
|
||
|
|
bshell_line_ed *ed,
|
||
|
|
fx_keycode key)
|
||
|
|
{
|
||
|
|
bshell_line_ed_move_cursor(ed, 1);
|
||
|
|
return BSHELL_LINE_EDITOR_KEYBIND_RESULT(
|
||
|
|
BSHELL_SUCCESS,
|
||
|
|
BSHELL_LINE_EDITOR_KEYBIND_CONTINUE);
|
||
|
|
}
|
||
|
|
|
||
|
|
static struct bshell_line_ed_keybind_result eof_key(
|
||
|
|
bshell_line_ed *ed,
|
||
|
|
fx_keycode key)
|
||
|
|
{
|
||
|
|
bshell_line_ed_set_eof(ed);
|
||
|
|
return BSHELL_LINE_EDITOR_KEYBIND_RESULT(
|
||
|
|
BSHELL_SUCCESS,
|
||
|
|
BSHELL_LINE_EDITOR_KEYBIND_CONTINUE);
|
||
|
|
}
|
||
|
|
|
||
|
|
static struct bshell_line_ed_keybind_result return_key(
|
||
|
|
bshell_line_ed *ed,
|
||
|
|
fx_keycode key)
|
||
|
|
{
|
||
|
|
bshell_line_ed_set_done(ed);
|
||
|
|
fx_string *buf = bshell_line_ed_get_buffer(ed);
|
||
|
|
fx_string_append_c(buf, '\n');
|
||
|
|
return BSHELL_LINE_EDITOR_KEYBIND_RESULT(
|
||
|
|
BSHELL_SUCCESS,
|
||
|
|
BSHELL_LINE_EDITOR_KEYBIND_CONTINUE);
|
||
|
|
}
|
||
|
|
|
||
|
|
static struct bshell_line_ed_keybind_result backspace(
|
||
|
|
bshell_line_ed *ed,
|
||
|
|
fx_keycode key)
|
||
|
|
{
|
||
|
|
fx_string *buf = bshell_line_ed_get_buffer(ed);
|
||
|
|
unsigned long cursor = bshell_line_ed_get_cursor(ed);
|
||
|
|
if (cursor > 0) {
|
||
|
|
fx_string_remove(buf, cursor - 1, 1);
|
||
|
|
bshell_line_ed_move_cursor(ed, -1);
|
||
|
|
bshell_line_ed_buffer_changed(ed);
|
||
|
|
}
|
||
|
|
|
||
|
|
return BSHELL_LINE_EDITOR_KEYBIND_RESULT(
|
||
|
|
BSHELL_SUCCESS,
|
||
|
|
BSHELL_LINE_EDITOR_KEYBIND_CONTINUE);
|
||
|
|
}
|
||
|
|
|
||
|
|
static struct bshell_line_ed_keybind_result generic_key(
|
||
|
|
bshell_line_ed *ed,
|
||
|
|
fx_keycode key)
|
||
|
|
{
|
||
|
|
fx_wchar c = key;
|
||
|
|
bool valid = fx_wchar_utf8_is_valid_scalar(c) && fx_wchar_is_graph(c);
|
||
|
|
|
||
|
|
if (valid) {
|
||
|
|
fx_string *buf = bshell_line_ed_get_buffer(ed);
|
||
|
|
unsigned long cursor = bshell_line_ed_get_cursor(ed);
|
||
|
|
char cstr[5] = {0};
|
||
|
|
fx_string_insert_wc(buf, c, cursor);
|
||
|
|
bshell_line_ed_move_cursor(ed, 1);
|
||
|
|
bshell_line_ed_buffer_changed(ed);
|
||
|
|
}
|
||
|
|
|
||
|
|
return BSHELL_LINE_EDITOR_KEYBIND_RESULT(
|
||
|
|
BSHELL_SUCCESS,
|
||
|
|
BSHELL_LINE_EDITOR_KEYBIND_CONTINUE);
|
||
|
|
}
|
||
|
|
|
||
|
|
static enum bshell_status load(
|
||
|
|
bshell_line_ed_plugin *plugin,
|
||
|
|
bshell_line_ed *ed)
|
||
|
|
{
|
||
|
|
struct bshell_line_ed_core_p *core
|
||
|
|
= fx_object_get_private(plugin, BSHELL_TYPE_LINE_EDITOR_CORE);
|
||
|
|
core->c_key_eof = bshell_line_ed_bind_key(ed, "^d", eof_key, 100);
|
||
|
|
core->c_key_cursor_left
|
||
|
|
= bshell_line_ed_bind_key(ed, "left", cursor_left, 100);
|
||
|
|
core->c_key_cursor_right
|
||
|
|
= bshell_line_ed_bind_key(ed, "right", cursor_right, 100);
|
||
|
|
core->c_key_cursor_right
|
||
|
|
= bshell_line_ed_bind_key(ed, "return", return_key, 100);
|
||
|
|
core->c_key_generic
|
||
|
|
= bshell_line_ed_bind_key(ed, "*", generic_key, 100);
|
||
|
|
core->c_key_backspace
|
||
|
|
= bshell_line_ed_bind_key(ed, "backspace", backspace, 100);
|
||
|
|
|
||
|
|
return BSHELL_SUCCESS;
|
||
|
|
}
|
||
|
|
|
||
|
|
static enum bshell_status render_prompt(
|
||
|
|
bshell_line_ed_plugin *plugin,
|
||
|
|
bshell_line_ed *ed)
|
||
|
|
{
|
||
|
|
fx_string *prompt = bshell_line_ed_get_prompt(ed);
|
||
|
|
fx_tty *tty = bshell_line_ed_get_tty(ed);
|
||
|
|
const char *s = fx_string_get_cstr(prompt);
|
||
|
|
fx_tty_puts(tty, 0, s);
|
||
|
|
return BSHELL_SUCCESS;
|
||
|
|
}
|
||
|
|
|
||
|
|
static enum bshell_status render_buffer(
|
||
|
|
bshell_line_ed_plugin *plugin,
|
||
|
|
bshell_line_ed *ed)
|
||
|
|
{
|
||
|
|
fx_string *buf = bshell_line_ed_get_buffer(ed);
|
||
|
|
fx_tty *tty = bshell_line_ed_get_tty(ed);
|
||
|
|
const char *s = fx_string_get_cstr(buf);
|
||
|
|
for (unsigned long i = 0; *s; i++) {
|
||
|
|
fx_wchar c = fx_wchar_utf8_codepoint_decode(s);
|
||
|
|
unsigned int stride = fx_wchar_utf8_codepoint_size(c);
|
||
|
|
if (stride == 0) {
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
char cs[5] = {0};
|
||
|
|
fx_wchar_utf8_codepoint_encode(c, cs);
|
||
|
|
|
||
|
|
fx_tty_puts(tty, 0, cs);
|
||
|
|
s += stride;
|
||
|
|
}
|
||
|
|
|
||
|
|
return BSHELL_SUCCESS;
|
||
|
|
}
|
||
|
|
|
||
|
|
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_core_p *core
|
||
|
|
= fx_object_get_private(plugin, BSHELL_TYPE_LINE_EDITOR_CORE);
|
||
|
|
const fx_value *v = fx_hashtable_get(core->c_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)
|
||
|
|
{
|
||
|
|
struct bshell_line_ed_core_p *core
|
||
|
|
= fx_object_get_private(plugin, BSHELL_TYPE_LINE_EDITOR_CORE);
|
||
|
|
fx_hashtable_put(core->c_data, &FX_CSTR(key), (fx_value *)value);
|
||
|
|
return BSHELL_SUCCESS;
|
||
|
|
}
|
||
|
|
|
||
|
|
FX_TYPE_CLASS_BEGIN(bshell_line_ed_core)
|
||
|
|
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_render_prompt) = render_prompt;
|
||
|
|
FX_INTERFACE_ENTRY(p_render_buffer) = render_buffer;
|
||
|
|
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_core)
|
||
|
|
|
||
|
|
FX_TYPE_DEFINITION_BEGIN(bshell_line_ed_core)
|
||
|
|
FX_TYPE_ID(0xf5174a63, 0x8fe4, 0x4720, 0xa861, 0x4ebb0d170dd3);
|
||
|
|
FX_TYPE_NAME("bshell.runtime.line_editor.plugins.core");
|
||
|
|
FX_TYPE_EXTENDS(BSHELL_TYPE_LINE_EDITOR_PLUGIN);
|
||
|
|
FX_TYPE_CLASS(bshell_line_ed_core_class);
|
||
|
|
FX_TYPE_INSTANCE_INIT(init);
|
||
|
|
FX_TYPE_INSTANCE_FINI(fini);
|
||
|
|
FX_TYPE_INSTANCE_PRIVATE(struct bshell_line_ed_core_p);
|
||
|
|
FX_TYPE_DEFINITION_END(bshell_line_ed_core)
|