305 lines
5.3 KiB
C
305 lines
5.3 KiB
C
#include "line-ed.h"
|
|
#include "history.h"
|
|
#include "hook.h"
|
|
#include "input.h"
|
|
#include "prompt.h"
|
|
|
|
#include <ctype.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <wchar.h>
|
|
#include <wctype.h>
|
|
#include <blue/term/tty.h>
|
|
|
|
#define LINE_ED_FROM_LEX_SOURCE(p) \
|
|
((struct line_ed *)((char *)p - offsetof(struct line_ed, l_line_source)))
|
|
|
|
static enum ivy_status readline(
|
|
struct ivy_line_source *src, char *out, size_t max, size_t *read,
|
|
const char *scope_type)
|
|
{
|
|
struct line_ed *ed = LINE_ED_FROM_LEX_SOURCE(src);
|
|
|
|
line_ed_set_scope_type(ed, scope_type);
|
|
long r = line_ed_readline(ed, out, max);
|
|
line_ed_set_scope_type(ed, NULL);
|
|
|
|
if (r < 0) {
|
|
return IVY_ERR_EOF;
|
|
}
|
|
|
|
*read = r;
|
|
return IVY_OK;
|
|
}
|
|
|
|
struct line_ed *line_ed_create(void)
|
|
{
|
|
struct line_ed *out = malloc(sizeof *out);
|
|
if (!out) {
|
|
return NULL;
|
|
}
|
|
|
|
memset(out, 0x0, sizeof *out);
|
|
|
|
out->l_buf = malloc(LINE_MAX);
|
|
if (!out->l_buf) {
|
|
free(out);
|
|
return NULL;
|
|
}
|
|
|
|
out->l_history = b_array_create();
|
|
if (!out->l_history) {
|
|
free(out->l_buf);
|
|
free(out);
|
|
return NULL;
|
|
}
|
|
|
|
out->l_tty = b_stdtty;
|
|
out->l_buf_end = out->l_buf + LINE_MAX;
|
|
out->l_buf_ptr = out->l_buf;
|
|
out->l_line_end = out->l_buf;
|
|
|
|
out->l_prompt[PROMPT_MAIN] = ">>> ";
|
|
out->l_prompt[PROMPT_CONT] = "> ";
|
|
|
|
out->l_line_source.s_readline = readline;
|
|
|
|
return out;
|
|
}
|
|
|
|
void line_ed_destroy(struct line_ed *ed)
|
|
{
|
|
b_array_release(ed->l_history);
|
|
line_ed_clear_highlights(ed);
|
|
free(ed->l_buf);
|
|
free(ed);
|
|
}
|
|
|
|
void line_ed_set_flags(struct line_ed *ed, enum line_ed_flags flags)
|
|
{
|
|
ed->l_flags |= flags;
|
|
}
|
|
|
|
void line_ed_set_scope_type(struct line_ed *ed, const char *scope_type)
|
|
{
|
|
ed->l_scope_type = scope_type;
|
|
}
|
|
|
|
static void clear_buffer(struct line_ed *ed)
|
|
{
|
|
memset(ed->l_buf, 0x0, ed->l_buf_end - ed->l_buf);
|
|
|
|
ed->l_buf_ptr = ed->l_buf;
|
|
ed->l_line_end = ed->l_buf;
|
|
|
|
ed->l_cursor_x = 0;
|
|
ed->l_cursor_y = 0;
|
|
|
|
ed->l_continuations = 0;
|
|
}
|
|
|
|
static void convert_continuation_feeds(char *s, size_t max)
|
|
{
|
|
char *end = s + max;
|
|
unsigned int len = strlen(s);
|
|
|
|
while (1) {
|
|
unsigned int r = strcspn(s, "\\");
|
|
if (s + r > end) {
|
|
break;
|
|
}
|
|
|
|
s += r;
|
|
len -= r;
|
|
|
|
if (*s == '\0') {
|
|
break;
|
|
}
|
|
|
|
if (*(s + 1) != '\n') {
|
|
s++;
|
|
continue;
|
|
}
|
|
|
|
memmove(s, s + 1, len);
|
|
s += 1;
|
|
}
|
|
}
|
|
|
|
static void remove_continuation_feeds(char *s, size_t max)
|
|
{
|
|
char *end = s + max;
|
|
unsigned int len = strlen(s);
|
|
|
|
while (1) {
|
|
unsigned int r = strcspn(s, "\\");
|
|
if (s + r > end) {
|
|
break;
|
|
}
|
|
|
|
s += r;
|
|
len -= r;
|
|
|
|
if (*s == '\0') {
|
|
break;
|
|
}
|
|
|
|
if (*(s + 1) != '\n') {
|
|
s++;
|
|
continue;
|
|
}
|
|
|
|
memmove(s, s + 2, len);
|
|
s += 2;
|
|
}
|
|
}
|
|
|
|
static bool input_is_empty(struct line_ed *ed)
|
|
{
|
|
const char *p = ed->l_buf;
|
|
while (p < ed->l_line_end) {
|
|
if (!isspace(*p)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
long line_ed_readline(struct line_ed *ed, char *out, size_t max)
|
|
{
|
|
clear_buffer(ed);
|
|
|
|
bool append_history = false;
|
|
unsigned int append_to_index = (unsigned int)-1;
|
|
if (!ed->l_scope_type) {
|
|
alloc_empty_history_entry(ed);
|
|
} else {
|
|
append_history = true;
|
|
append_to_index = ed->l_history_pos;
|
|
}
|
|
|
|
b_tty *tty = ed->l_tty;
|
|
b_tty_set_mode(tty, B_TTY_RAW);
|
|
show_prompt(ed);
|
|
|
|
for (int i = 0; ed->l_buf[i]; i++) {
|
|
if (ed->l_buf[i] == '\n') {
|
|
fputc('\r', stdout);
|
|
}
|
|
|
|
fputc(ed->l_buf[i], stdout);
|
|
}
|
|
fflush(stdout);
|
|
|
|
bool end = false;
|
|
bool eof = false;
|
|
|
|
while (!end) {
|
|
b_keycode key = b_tty_read_key(tty);
|
|
hook_keypress(ed, key);
|
|
|
|
if (key == B_TTY_CTRL_KEY('d')) {
|
|
if (!input_is_empty(ed)) {
|
|
continue;
|
|
}
|
|
|
|
eof = true;
|
|
break;
|
|
}
|
|
|
|
if (key & B_MOD_CTRL) {
|
|
continue;
|
|
}
|
|
|
|
switch (key) {
|
|
case B_KEY_RETURN:
|
|
b_tty_reset_vmode(tty);
|
|
if (ed->l_line_end > ed->l_buf
|
|
&& *(ed->l_line_end - 1) != '\\') {
|
|
end = true;
|
|
break;
|
|
}
|
|
|
|
if (input_is_empty(ed)) {
|
|
fputc('\r', stdout);
|
|
fputc('\n', stdout);
|
|
clear_buffer(ed);
|
|
show_prompt(ed);
|
|
break;
|
|
}
|
|
|
|
*ed->l_line_end = '\n';
|
|
ed->l_line_end++;
|
|
ed->l_buf_ptr = ed->l_line_end;
|
|
|
|
ed->l_cursor_x = 0;
|
|
ed->l_cursor_y++;
|
|
ed->l_continuations++;
|
|
fputc('\r', stdout);
|
|
fputc('\n', stdout);
|
|
// fputs("\033[G\n", stdout);
|
|
show_prompt(ed);
|
|
break;
|
|
case B_KEY_BACKSPACE:
|
|
backspace(ed);
|
|
break;
|
|
case B_KEY_ARROW_LEFT:
|
|
cursor_left(ed);
|
|
break;
|
|
case B_KEY_ARROW_RIGHT:
|
|
cursor_right(ed);
|
|
break;
|
|
case B_KEY_ARROW_UP:
|
|
arrow_up(ed);
|
|
break;
|
|
case B_KEY_ARROW_DOWN:
|
|
arrow_down(ed);
|
|
break;
|
|
default:
|
|
if (iswgraph(key) || key == ' ') {
|
|
put_char(ed, key);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
b_tty_set_mode(tty, B_TTY_CANONICAL);
|
|
fputc('\n', stdout);
|
|
|
|
if (*ed->l_buf == '\0') {
|
|
return eof ? -1 : 0;
|
|
}
|
|
|
|
if (append_history) {
|
|
ed->l_history_pos = append_to_index;
|
|
append_buf_to_history(ed);
|
|
} else {
|
|
ed->l_history_pos = b_array_size(ed->l_history) - 1;
|
|
|
|
const char *last = last_history_line(ed);
|
|
if (!last || strcmp(last, ed->l_buf) != 0) {
|
|
save_buf_to_history(ed);
|
|
}
|
|
}
|
|
|
|
size_t sz = ed->l_line_end - ed->l_buf + 1;
|
|
sz = MIN(max - 1, sz);
|
|
memcpy(out, ed->l_buf, sz);
|
|
out[sz - 1] = '\n';
|
|
out[sz] = '\0';
|
|
|
|
if ((ed->l_flags & LINE_ED_REMOVE_CONTINUATIONS)
|
|
== LINE_ED_REMOVE_CONTINUATIONS) {
|
|
remove_continuation_feeds(out, max);
|
|
} else if (
|
|
(ed->l_flags & LINE_ED_CONVERT_CONTINUATIONS)
|
|
== LINE_ED_CONVERT_CONTINUATIONS) {
|
|
convert_continuation_feeds(out, max);
|
|
}
|
|
sz = strlen(out);
|
|
|
|
return sz;
|
|
}
|