diff options
Diffstat (limited to 'src/user/libterm/readline.c')
-rw-r--r-- | src/user/libterm/readline.c | 122 |
1 files changed, 119 insertions, 3 deletions
diff --git a/src/user/libterm/readline.c b/src/user/libterm/readline.c index fdb61a1..c59a60b 100644 --- a/src/user/libterm/readline.c +++ b/src/user/libterm/readline.c @@ -1,13 +1,82 @@ #include <libterm/terminal.h> #include <knob/block.h> +#include <knob/heap.h> #include <knob/key.h> #include <stdint.h> -//returns length of string without null terminator -//max_length doesn't include null terminator -uint32_t read_line(char *sz, uint32_t max_length, const char *prompt) { +struct he { + struct he *prev; + struct he *next; + const char *data; +}; + +struct history { + uint32_t entries_left; + struct he *newest_entry; + struct he *oldest_entry; +}; + +static void move_entry_to_front(struct history *h, struct he *e) { + if (!e || !h || (e == h->newest_entry)) + return; + if (e->prev) + e->prev->next = e->next; + if (e->next) + e->next->prev = e->prev; + if (e == h->oldest_entry) + h->oldest_entry = e->next; + e->prev = h->newest_entry; + e->next = 0; + if (h->newest_entry) + h->newest_entry->next = e; + h->newest_entry = e; +} + +static void add_to_history(struct history *h, const char *from) { + const uint32_t from_size = strlen(from) + 1; + + char *const copy = get_block(from_size); + blockcpy(copy, from, from_size); + + struct he *entry; + + if (h->entries_left) { + --h->entries_left; + entry = get_block(sizeof(struct he)); + } + + else { + entry = h->oldest_entry; + h->oldest_entry = entry->next; + entry->next->prev = 0; + free_block(entry->data); + } + + entry->data = copy; + entry->prev = h->newest_entry; + entry->next = 0; + if (h->newest_entry) + h->newest_entry->next = entry; + else + h->oldest_entry = entry; + h->newest_entry = entry; +} + +struct history *new_history(uint32_t max_entries) { + if (max_entries < 2) + return 0; + struct history *h = get_block(sizeof(struct history)); + if (!h) + return 0; + h->newest_entry = 0; + h->oldest_entry = 0; + h->entries_left = max_entries; + return h; +} + +uint32_t read_line(char *sz, uint32_t max_length, const char *prompt, struct history *hist) { term_add_sz(prompt); term_add_char(' '); term_paint(); @@ -15,6 +84,9 @@ uint32_t read_line(char *sz, uint32_t max_length, const char *prompt) { uint32_t l = 0; uint32_t c = 0; + struct he *h_on = 0; + bool he_modified = false; + while (1) { struct key_packet kp = term_get_key_blocking(); switch (kp.key_id) { @@ -54,6 +126,7 @@ uint32_t read_line(char *sz, uint32_t max_length, const char *prompt) { case KEY_BSPACE: if (!c) continue; + he_modified = true; --c; --l; for (uint32_t i = c; i < l; ++i) @@ -71,16 +144,59 @@ uint32_t read_line(char *sz, uint32_t max_length, const char *prompt) { term_cursor_right(); } sz[l] = '\0'; + if (hist) { + if (he_modified) + add_to_history(hist, sz); + else if (h_on) + move_entry_to_front(hist, h_on); + } term_add_char('\n'); term_paint(); _yield_task(); return l; + case KEY_UP_ARROW: + if (!hist || !hist->newest_entry || (h_on == hist->oldest_entry)) + continue; + he_modified = false; + h_on = h_on ? h_on->prev : hist->newest_entry; + for (uint32_t i = 0; i < c; ++i) + term_cursor_left(); + for (uint32_t i = 0; i < l; ++i) + term_add_char(' '); + for (uint32_t i = 0; i < l; ++i) + term_cursor_left(); + l = strcpy(sz, h_on->data); + term_add_sn_no_ww(sz, l); + c = l; + term_paint(); + continue; + case KEY_DOWN_ARROW: + if (!h_on) + continue; + he_modified = false; + h_on = h_on->next; + for (uint32_t i = 0; i < c; ++i) + term_cursor_left(); + for (uint32_t i = 0; i < l; ++i) + term_add_char(' '); + for (uint32_t i = 0; i < l; ++i) + term_cursor_left(); + if (h_on) { + l = strcpy(sz, h_on->data); + term_add_sn_no_ww(sz, l); + } + else + l = 0; + c = l; + term_paint(); + continue; default: if (l == max_length) continue; char ch = key_to_char(kp); if (!ch) continue; + he_modified = true; if (c == l) { ++l; term_add_char(sz[c++] = ch); |