aboutsummaryrefslogtreecommitdiff
path: root/main.c
diff options
context:
space:
mode:
authorMarc André Tanner <mat@brain-dump.org>2014-09-09 19:20:07 +0200
committerMarc André Tanner <mat@brain-dump.org>2014-09-09 19:20:07 +0200
commit98646851ade5386e3f72bfda4d94db20b65fde78 (patch)
treec80c77ef8243edaf6760fc5c44e900d14e1d734a /main.c
parent9e2755fefda4383913375521a717defc6cedd241 (diff)
downloadvis-98646851ade5386e3f72bfda4d94db20b65fde78.tar.gz
vis-98646851ade5386e3f72bfda4d94db20b65fde78.tar.xz
Unclutter config.def.h by moving stuff to main.c
Diffstat (limited to 'main.c')
-rw-r--r--main.c693
1 files changed, 688 insertions, 5 deletions
diff --git a/main.c b/main.c
index 13b5da1..2a4e08d 100644
--- a/main.c
+++ b/main.c
@@ -112,13 +112,436 @@ typedef struct {
regex_t regex;
} Command;
-static Key getkey(void);
+/* global variables */
+static Vis *vis; /* global editor instance, keeps track of all windows etc. */
+static Mode *mode; /* currently active mode, used to search for keybindings */
+static Mode *mode_prev; /* mode which was active previously */
+static Action action; /* current action which is in progress */
+static Action action_prev; /* last operator action used by the repeat '.' key */
+
+/* movements which can be used besides the one in text-motions.h and window.h */
+static size_t search_forward(const Arg *arg);
+static size_t search_backward(const Arg *arg);
+static size_t mark_goto(const Arg *arg);
+static size_t mark_line_goto(const Arg *arg);
+static size_t to(const Arg *arg);
+static size_t till(const Arg *arg);
+static size_t to_left(const Arg *arg);
+static size_t till_left(const Arg *arg);
+static size_t line(const Arg *arg);
+static size_t column(const Arg *arg);
+
+enum {
+ MOVE_LINE_UP,
+ MOVE_LINE_DOWN,
+ MOVE_LINE_BEGIN,
+ MOVE_LINE_START,
+ MOVE_LINE_FINISH,
+ MOVE_LINE_END,
+ MOVE_LINE,
+ MOVE_COLUMN,
+ MOVE_CHAR_PREV,
+ MOVE_CHAR_NEXT,
+ MOVE_WORD_START_PREV,
+ MOVE_WORD_START_NEXT,
+ MOVE_WORD_END_PREV,
+ MOVE_WORD_END_NEXT,
+ MOVE_SENTENCE_PREV,
+ MOVE_SENTENCE_NEXT,
+ MOVE_PARAGRAPH_PREV,
+ MOVE_PARAGRAPH_NEXT,
+ MOVE_BRACKET_MATCH,
+ MOVE_LEFT_TO,
+ MOVE_RIGHT_TO,
+ MOVE_LEFT_TILL,
+ MOVE_RIGHT_TILL,
+ MOVE_FILE_BEGIN,
+ MOVE_FILE_END,
+ MOVE_MARK,
+ MOVE_MARK_LINE,
+ MOVE_SEARCH_FORWARD,
+ MOVE_SEARCH_BACKWARD,
+};
+
+static Movement moves[] = {
+ [MOVE_LINE_UP] = { .win = window_line_up },
+ [MOVE_LINE_DOWN] = { .win = window_line_down },
+ [MOVE_LINE_BEGIN] = { .txt = text_line_begin, .type = LINEWISE },
+ [MOVE_LINE_START] = { .txt = text_line_start, .type = LINEWISE },
+ [MOVE_LINE_FINISH] = { .txt = text_line_finish, .type = LINEWISE },
+ [MOVE_LINE_END] = { .txt = text_line_end, .type = LINEWISE },
+ [MOVE_LINE] = { .cmd = line, .type = LINEWISE },
+ [MOVE_COLUMN] = { .cmd = column, .type = CHARWISE },
+ [MOVE_CHAR_PREV] = { .win = window_char_prev },
+ [MOVE_CHAR_NEXT] = { .win = window_char_next },
+ [MOVE_WORD_START_PREV] = { .txt = text_word_start_prev, .type = CHARWISE },
+ [MOVE_WORD_START_NEXT] = { .txt = text_word_start_next, .type = CHARWISE },
+ [MOVE_WORD_END_PREV] = { .txt = text_word_end_prev, .type = CHARWISE|INCLUSIVE },
+ [MOVE_WORD_END_NEXT] = { .txt = text_word_end_next, .type = CHARWISE|INCLUSIVE },
+ [MOVE_SENTENCE_PREV] = { .txt = text_sentence_prev, .type = LINEWISE },
+ [MOVE_SENTENCE_NEXT] = { .txt = text_sentence_next, .type = LINEWISE },
+ [MOVE_PARAGRAPH_PREV] = { .txt = text_paragraph_prev, .type = LINEWISE },
+ [MOVE_PARAGRAPH_NEXT] = { .txt = text_paragraph_next, .type = LINEWISE },
+ [MOVE_BRACKET_MATCH] = { .txt = text_bracket_match, .type = LINEWISE|INCLUSIVE },
+ [MOVE_FILE_BEGIN] = { .txt = text_begin, .type = LINEWISE },
+ [MOVE_FILE_END] = { .txt = text_end, .type = LINEWISE },
+ [MOVE_LEFT_TO] = { .cmd = to_left, .type = LINEWISE },
+ [MOVE_RIGHT_TO] = { .cmd = to, .type = LINEWISE },
+ [MOVE_LEFT_TILL] = { .cmd = till_left, .type = LINEWISE },
+ [MOVE_RIGHT_TILL] = { .cmd = till, .type = LINEWISE },
+ [MOVE_MARK] = { .cmd = mark_goto, .type = LINEWISE },
+ [MOVE_MARK_LINE] = { .cmd = mark_line_goto, .type = LINEWISE },
+ [MOVE_SEARCH_FORWARD] = { .cmd = search_forward, .type = LINEWISE },
+ [MOVE_SEARCH_BACKWARD] = { .cmd = search_backward, .type = LINEWISE },
+};
+
+/* available text objects */
+enum {
+ TEXT_OBJ_WORD,
+ TEXT_OBJ_LINE_UP,
+ TEXT_OBJ_LINE_DOWN,
+ TEXT_OBJ_SENTENCE,
+ TEXT_OBJ_PARAGRAPH,
+ TEXT_OBJ_OUTER_SQUARE_BRACKET,
+ TEXT_OBJ_INNER_SQUARE_BRACKET,
+ TEXT_OBJ_OUTER_CURLY_BRACKET,
+ TEXT_OBJ_INNER_CURLY_BRACKET,
+ TEXT_OBJ_OUTER_ANGLE_BRACKET,
+ TEXT_OBJ_INNER_ANGLE_BRACKET,
+ TEXT_OBJ_OUTER_PARANTHESE,
+ TEXT_OBJ_INNER_PARANTHESE,
+ TEXT_OBJ_OUTER_QUOTE,
+ TEXT_OBJ_INNER_QUOTE,
+ TEXT_OBJ_OUTER_SINGLE_QUOTE,
+ TEXT_OBJ_INNER_SINGLE_QUOTE,
+ TEXT_OBJ_OUTER_BACKTICK,
+ TEXT_OBJ_INNER_BACKTICK,
+};
+
+static TextObject textobjs[] = {
+ [TEXT_OBJ_WORD] = { text_object_word },
+ [TEXT_OBJ_LINE_UP] = { text_object_line },
+ [TEXT_OBJ_LINE_DOWN] = { text_object_line },
+ [TEXT_OBJ_SENTENCE] = { text_object_sentence },
+ [TEXT_OBJ_PARAGRAPH] = { text_object_paragraph },
+ [TEXT_OBJ_OUTER_SQUARE_BRACKET] = { text_object_square_bracket, OUTER },
+ [TEXT_OBJ_INNER_SQUARE_BRACKET] = { text_object_square_bracket, INNER },
+ [TEXT_OBJ_OUTER_CURLY_BRACKET] = { text_object_curly_bracket, OUTER },
+ [TEXT_OBJ_INNER_CURLY_BRACKET] = { text_object_curly_bracket, INNER },
+ [TEXT_OBJ_OUTER_ANGLE_BRACKET] = { text_object_angle_bracket, OUTER },
+ [TEXT_OBJ_INNER_ANGLE_BRACKET] = { text_object_angle_bracket, INNER },
+ [TEXT_OBJ_OUTER_PARANTHESE] = { text_object_paranthese, OUTER },
+ [TEXT_OBJ_INNER_PARANTHESE] = { text_object_paranthese, INNER },
+ [TEXT_OBJ_OUTER_QUOTE] = { text_object_quote, OUTER },
+ [TEXT_OBJ_INNER_QUOTE] = { text_object_quote, INNER },
+ [TEXT_OBJ_OUTER_SINGLE_QUOTE] = { text_object_single_quote, OUTER },
+ [TEXT_OBJ_INNER_SINGLE_QUOTE] = { text_object_single_quote, INNER },
+ [TEXT_OBJ_OUTER_BACKTICK] = { text_object_backtick, OUTER },
+ [TEXT_OBJ_INNER_BACKTICK] = { text_object_backtick, INNER },
+};
+
+/* if some movements are forced to be linewise, they are translated to text objects */
+static TextObject *moves_linewise[] = {
+ [MOVE_LINE_UP] = &textobjs[TEXT_OBJ_LINE_UP],
+ [MOVE_LINE_DOWN] = &textobjs[TEXT_OBJ_LINE_DOWN],
+};
+
+/* functions to be called from keybindings */
+static void switchmode(const Arg *arg);
+static void mark_set(const Arg *arg);
+static void insert(const Arg *arg);
+static void insert_tab(const Arg *arg);
+static void insert_newline(const Arg *arg);
+static void split(const Arg *arg);
+static void quit(const Arg *arg);
+static void repeat(const Arg *arg);
+static void count(const Arg *arg);
+static void linewise(const Arg *arg);
+static void operator(const Arg *arg);
+static void movement_key(const Arg *arg);
+static void movement(const Arg *arg);
+static void textobj(const Arg *arg);
+static void reg(const Arg *arg);
+static void mark(const Arg *arg);
+static void mark_line(const Arg *arg);
+static void undo(const Arg *arg);
+static void redo(const Arg *arg);
+static void zero(const Arg *arg);
+static void delete_word(const Arg *arg);
+static void insert_register(const Arg *arg);
+static void prompt(const Arg *arg);
+static void prompt_enter(const Arg *arg);
+static void prompt_up(const Arg *arg);
+static void prompt_down(const Arg *arg);
+static void insert_verbatim(const Arg *arg);
static void cursor(const Arg *arg);
static void call(const Arg *arg);
+/* commands to enter at the ':'-prompt */
+static bool cmd_gotoline(const char *argv[]);
+static bool cmd_open(const char *argv[]);
+static bool cmd_quit(const char *argv[]);
+static bool cmd_read(const char *argv[]);
+static bool cmd_substitute(const char *argv[]);
+static bool cmd_split(const char *argv[]);
+static bool cmd_vsplit(const char *argv[]);
+static bool cmd_wq(const char *argv[]);
+static bool cmd_write(const char *argv[]);
+
+static void action_reset(Action *a);
+static void switchmode_to(Mode *new_mode);
+
#include "config.h"
-static Config *config = &editors[0];
+static Key getkey(void);
+static void action_do(Action *a);
+static bool exec_command(char *cmdline);
+
+/* operator implementations of type: void (*op)(OperatorContext*) */
+
+static void op_delete(OperatorContext *c) {
+ size_t len = c->range.end - c->range.start;
+ c->reg->linewise = c->linewise;
+ register_put(c->reg, vis->win->text, &c->range);
+ vis_delete(vis, c->range.start, len);
+ window_cursor_to(vis->win->win, c->range.start);
+}
+
+static void op_change(OperatorContext *c) {
+ op_delete(c);
+ switchmode(&(const Arg){ .i = VIS_MODE_INSERT });
+}
+
+static void op_yank(OperatorContext *c) {
+ c->reg->linewise = c->linewise;
+ register_put(c->reg, vis->win->text, &c->range);
+}
+
+static void op_paste(OperatorContext *c) {
+ size_t pos = window_cursor_get(vis->win->win);
+ if (c->reg->linewise)
+ pos = text_line_next(vis->win->text, pos);
+ vis_insert(vis, pos, c->reg->data, c->reg->len);
+ window_cursor_to(vis->win->win, pos + c->reg->len);
+}
+
+/* movement implementations of type: size_t (*move)(const Arg*) */
+
+static size_t search_forward(const Arg *arg) {
+ size_t pos = window_cursor_get(vis->win->win);
+ return text_search_forward(vis->win->text, pos, vis->search_pattern);
+}
+
+static size_t search_backward(const Arg *arg) {
+ size_t pos = window_cursor_get(vis->win->win);
+ return text_search_backward(vis->win->text, pos, vis->search_pattern);
+}
+
+static void mark_set(const Arg *arg) {
+ text_mark_set(vis->win->text, arg->i, window_cursor_get(vis->win->win));
+}
+
+static size_t mark_goto(const Arg *arg) {
+ return text_mark_get(vis->win->text, action.mark);
+}
+
+static size_t mark_line_goto(const Arg *arg) {
+ return text_line_start(vis->win->text, mark_goto(arg));
+}
+
+static size_t to(const Arg *arg) {
+ return text_find_char_next(vis->win->text, window_cursor_get(vis->win->win) + 1,
+ action.key.str, strlen(action.key.str));
+}
+
+static size_t till(const Arg *arg) {
+ return text_char_prev(vis->win->text, to(arg));
+}
+
+static size_t to_left(const Arg *arg) {
+ return text_find_char_prev(vis->win->text, window_cursor_get(vis->win->win) - 1,
+ action.key.str, strlen(action.key.str));
+}
+
+static size_t till_left(const Arg *arg) {
+ return text_char_next(vis->win->text, to_left(arg));
+}
+
+static size_t line(const Arg *arg) {
+ if (action.count == 0)
+ return text_size(vis->win->text);
+ size_t pos = text_pos_by_lineno(vis->win->text, action.count);
+ action.count = 0;
+ return pos;
+}
+
+static size_t column(const Arg *arg) {
+ char c;
+ VisWin *win = vis->win;
+ size_t pos = window_cursor_get(win->win);
+ Iterator it = text_iterator_get(win->text, text_line_begin(win->text, pos));
+ while (action.count-- > 0 && text_iterator_byte_get(&it, &c) && c != '\n')
+ text_iterator_byte_next(&it, NULL);
+ action.count = 0;
+ return it.pos;
+}
+
+/* key bindings functions of type: void (*func)(const Arg*) */
+
+static void repeat(const Arg *arg) {
+ action = action_prev;
+ action_do(&action);
+}
+
+static void count(const Arg *arg) {
+ action.count = action.count * 10 + arg->i;
+}
+
+static void linewise(const Arg *arg) {
+ action.linewise = arg->b;
+}
+
+static void operator(const Arg *arg) {
+ Operator *op = &ops[arg->i];
+ if (mode == &vis_modes[VIS_MODE_VISUAL]) {
+ action.op = op;
+ action_do(&action);
+ return;
+ }
+
+ switchmode(&(const Arg){ .i = VIS_MODE_OPERATOR });
+ if (action.op == op) {
+ /* hacky way to handle double operators i.e. things like
+ * dd, yy etc where the second char isn't a movement */
+ action.linewise = true;
+ action.textobj = moves_linewise[MOVE_LINE_DOWN];
+ action_do(&action);
+ } else {
+ action.op = op;
+ if (op->selfcontained)
+ action_do(&action);
+ }
+}
+
+static void movement_key(const Arg *arg) {
+ Key k = getkey();
+ if (!k.str[0]) {
+ action_reset(&action);
+ return;
+ }
+ action.key = k;
+ action.movement = &moves[arg->i];
+ action_do(&action);
+}
+
+static void movement(const Arg *arg) {
+ if (action.linewise && arg->i < LENGTH(moves_linewise))
+ action.textobj = moves_linewise[arg->i];
+ else
+ action.movement = &moves[arg->i];
+ action_do(&action);
+}
+
+static void textobj(const Arg *arg) {
+ action.textobj = &textobjs[arg->i];
+ action_do(&action);
+}
+
+static void reg(const Arg *arg) {
+ action.reg = &vis->registers[arg->i];
+}
+
+static void mark(const Arg *arg) {
+ action.mark = arg->i;
+ action.movement = &moves[MOVE_MARK];
+ action_do(&action);
+}
+
+static void mark_line(const Arg *arg) {
+ action.mark = arg->i;
+ action.movement = &moves[MOVE_MARK_LINE];
+ action_do(&action);
+}
+
+static void undo(const Arg *arg) {
+ if (text_undo(vis->win->text))
+ vis_draw(vis);
+}
+
+static void redo(const Arg *arg) {
+ if (text_redo(vis->win->text))
+ vis_draw(vis);
+}
+
+static void zero(const Arg *arg) {
+ if (action.count == 0)
+ movement(&(const Arg){ .i = MOVE_LINE_BEGIN });
+ else
+ count(&(const Arg){ .i = 0 });
+}
+
+static void delete_word(const Arg *arg) {
+ operator(&(const Arg){ .i = OP_DELETE });
+ movement(&(const Arg){ .i = MOVE_WORD_START_PREV });
+}
+
+static void insert_register(const Arg *arg) {
+ Register *reg = &vis->registers[arg->i];
+ vis_insert(vis, window_cursor_get(vis->win->win), reg->data, reg->len);
+}
+
+static void prompt(const Arg *arg) {
+ vis_prompt_show(vis, arg->s);
+ switchmode(&(const Arg){ .i = VIS_MODE_PROMPT });
+}
+
+static void prompt_enter(const Arg *arg) {
+ char *s = vis_prompt_get(vis);
+ switchmode(&(const Arg){ .i = VIS_MODE_NORMAL });
+ switch (vis->prompt->title[0]) {
+ case '/':
+ case '?':
+ if (text_regex_compile(vis->search_pattern, s, REG_EXTENDED)) {
+ action_reset(&action);
+ } else {
+ movement(&(const Arg){ .i = vis->prompt->title[0] == '/' ?
+ MOVE_SEARCH_FORWARD : MOVE_SEARCH_BACKWARD });
+ }
+ break;
+ case ':':
+ exec_command(s);
+ break;
+ }
+ free(s);
+}
+
+static void prompt_up(const Arg *arg) {
+
+}
+
+static void prompt_down(const Arg *arg) {
+
+}
+
+static void insert_verbatim(const Arg *arg) {
+ int value = 0;
+ for (int i = 0; i < 3; i++) {
+ Key k = getkey();
+ if (k.str[0] < '0' || k.str[0] > '9')
+ return;
+ value = value * 10 + k.str[0] - '0';
+ }
+ char v = value;
+ vis_insert(vis, window_cursor_get(vis->win->win), &v, 1);
+}
+
+static void quit(const Arg *arg) {
+ vis->running = false;
+}
+
+static void split(const Arg *arg) {
+ vis_window_split(vis, arg->s);
+}
static void cursor(const Arg *arg) {
arg->m(vis->win->win);
@@ -128,6 +551,268 @@ static void call(const Arg *arg) {
arg->f(vis);
}
+static void insert(const Arg *arg) {
+ vis_insert_key(vis, arg->s, arg->s ? strlen(arg->s) : 0);
+}
+
+static void insert_tab(const Arg *arg) {
+ insert(&(const Arg){ .s = "\t" });
+}
+
+static void insert_newline(const Arg *arg) {
+ // TODO determine file type to insert \n\r or \n
+ insert(&(const Arg){ .s = "\n" });
+}
+
+static void switchmode(const Arg *arg) {
+ switchmode_to(&vis_modes[arg->i]);
+}
+
+/* action processing, executed the operator / movement / text object */
+
+static void action_do(Action *a) {
+ Text *txt = vis->win->text;
+ Win *win = vis->win->win;
+ size_t pos = window_cursor_get(win);
+ if (a->count == 0)
+ a->count = 1;
+ OperatorContext c = {
+ .count = a->count,
+ .pos = pos,
+ .reg = a->reg ? a->reg : &vis->registers[REG_DEFAULT],
+ .linewise = a->linewise,
+ };
+
+ if (a->movement) {
+ size_t start = pos;
+ for (int i = 0; i < a->count; i++) {
+ if (a->movement->txt)
+ pos = a->movement->txt(txt, pos);
+ else if (a->movement->win)
+ pos = a->movement->win(win);
+ else
+ pos = a->movement->cmd(&a->arg);
+ if (pos == (size_t)-1)
+ break;
+ }
+
+ if (pos == (size_t)-1) {
+ c.range.start = start;
+ c.range.end = start;
+ } else {
+ c.range.start = MIN(start, pos);
+ c.range.end = MAX(start, pos);
+ }
+
+ if (!a->op) {
+ if (a->movement->type & CHARWISE)
+ window_scroll_to(win, pos);
+ else
+ window_cursor_to(win, pos);
+ } else if (a->movement->type & INCLUSIVE) {
+ Iterator it = text_iterator_get(txt, c.range.end);
+ text_iterator_char_next(&it, NULL);
+ c.range.end = it.pos;
+ }
+ } else if (a->textobj) {
+ Filerange r;
+ c.range.start = c.range.end = pos;
+ for (int i = 0; i < a->count; i++) {
+ r = a->textobj->range(txt, pos);
+ // TODO range_valid?
+ if (r.start == (size_t)-1 || r.end == (size_t)-1)
+ break;
+ if (a->textobj->type == OUTER) {
+ r.start--;
+ r.end++;
+ }
+ // TODO c.range = range_union(&c.range, &r);
+ c.range.start = MIN(c.range.start, r.start);
+ c.range.end = MAX(c.range.end, r.end);
+ if (i < a->count - 1) {
+ if (a->textobj == &textobjs[TEXT_OBJ_LINE_UP]) {
+ pos = c.range.start - 1;
+ } else {
+ pos = c.range.end + 1;
+ }
+ }
+ }
+ } else if (mode == &vis_modes[VIS_MODE_VISUAL]) {
+ c.range = window_selection_get(win);
+ if (c.range.start == (size_t)-1 || c.range.end == (size_t)-1)
+ c.range.start = c.range.end = pos;
+ }
+
+ if (a->op) {
+ a->op->func(&c);
+ if (mode == &vis_modes[VIS_MODE_OPERATOR])
+ switchmode_to(mode_prev);
+ else if (mode == &vis_modes[VIS_MODE_VISUAL])
+ switchmode(&(const Arg){ .i = VIS_MODE_NORMAL });
+ text_snapshot(txt);
+ }
+
+ if (a != &action_prev) {
+ if (a->op)
+ action_prev = *a;
+ action_reset(a);
+ }
+}
+
+static void action_reset(Action *a) {
+ a->count = 0;
+ a->linewise = false;
+ a->op = NULL;
+ a->movement = NULL;
+ a->textobj = NULL;
+ a->reg = NULL;
+}
+
+static void switchmode_to(Mode *new_mode) {
+ if (mode == new_mode)
+ return;
+ if (mode->leave)
+ mode->leave(new_mode);
+ mode_prev = mode;
+ //fprintf(stderr, "%s -> %s\n", mode_prev->name, new_mode->name);
+ mode = new_mode;
+ if (mode->enter)
+ mode->enter(mode_prev);
+ // TODO display mode name somewhere?
+
+}
+
+
+
+/* ':'-command implementations */
+
+static bool cmd_gotoline(const char *argv[]) {
+ action.count = strtoul(argv[0], NULL, 10);
+ movement(&(const Arg){ .i = MOVE_LINE });
+ return true;
+}
+
+static bool cmd_open(const char *argv[]) {
+ for (const char **file = &argv[1]; *file; file++)
+ vis_window_new(vis, *file);
+ return true;
+}
+
+static bool cmd_quit(const char *argv[]) {
+ bool force = strchr(argv[0], '!') != NULL;
+ for (VisWin *win = vis->windows; win; win = win->next) {
+ if (text_modified(win->text) && !force)
+ return false;
+ }
+ vis->running = false;
+ return true;
+}
+
+static bool cmd_read(const char *argv[]) {
+ size_t pos = window_cursor_get(vis->win->win);
+ for (const char **file = &argv[1]; *file; file++) {
+ int fd = open(*file, O_RDONLY);
+ char *data = NULL;
+ struct stat info;
+ if (fd == -1)
+ goto err;
+ if (fstat(fd, &info) == -1)
+ goto err;
+ if (!S_ISREG(info.st_mode))
+ goto err;
+ // XXX: use lseek(fd, 0, SEEK_END); instead?
+ data = mmap(NULL, info.st_size, PROT_READ, MAP_SHARED, fd, 0);
+ if (data == MAP_FAILED)
+ goto err;
+
+ text_insert_raw(vis->win->text, pos, data, info.st_size);
+ pos += info.st_size;
+ err:
+ if (fd > 2)
+ close(fd);
+ if (data && data != MAP_FAILED)
+ munmap(data, info.st_size);
+ }
+ vis_draw(vis);
+ return true;
+}
+
+static bool cmd_substitute(const char *argv[]) {
+ // TODO
+ return true;
+}
+
+static bool cmd_split(const char *argv[]) {
+ vis_window_split(vis, argv[1]);
+ for (const char **file = &argv[2]; *file; file++)
+ vis_window_split(vis, *file);
+ return true;
+}
+
+static bool cmd_vsplit(const char *argv[]) {
+ vis_window_vsplit(vis, argv[1]);
+ for (const char **file = &argv[2]; *file; file++)
+ vis_window_vsplit(vis, *file);
+ return true;
+}
+
+static bool cmd_wq(const char *argv[]) {
+ if (cmd_write(argv))
+ return cmd_quit(argv);
+ return false;
+}
+
+static bool cmd_write(const char *argv[]) {
+ Text *text = vis->win->text;
+ if (!argv[1])
+ argv[1] = text_filename(text);
+ for (const char **file = &argv[1]; *file; file++) {
+ if (text_save(text, *file))
+ return false;
+ }
+ return true;
+}
+
+static bool exec_command(char *cmdline) {
+ static bool init = false;
+ if (!init) {
+ /* compile the regexes on first inovaction */
+ for (Command *c = cmds; c->name; c++)
+ regcomp(&c->regex, c->name, REG_EXTENDED);
+ init = true;
+ }
+
+ Command *cmd = NULL;
+ for (Command *c = cmds; c->name; c++) {
+ if (!regexec(&c->regex, cmdline, 0, NULL, 0)) {
+ cmd = c;
+ break;
+ }
+ }
+
+ if (!cmd)
+ return false;
+
+ const char *argv[32] = { cmdline };
+ char *s = cmdline;
+ for (int i = 1; i < LENGTH(argv); i++) {
+ if (s) {
+ if ((s = strchr(s, ' ')))
+ *s++ = '\0';
+ }
+ while (s && *s && *s == ' ')
+ s++;
+ argv[i] = s ? s : NULL;
+ }
+
+ cmd->cmd(argv);
+ return true;
+}
+
+/* default editor configuration to use */
+static Config *config = &editors[0];
+
+
typedef struct Screen Screen;
static struct Screen {
int w, h;
@@ -201,12 +886,10 @@ static KeyBinding *keybinding(Mode *mode, Key *key0, Key *key1) {
static Key getkey(void) {
Key key = { .str = "\0\0\0\0\0\0", .code = 0 };
- int keycode = getch();
+ int keycode = getch(), len = 0;
if (keycode == ERR)
return key;
- // TODO verbatim insert mode
- int len = 0;
if (keycode >= KEY_MIN) {
key.code = keycode;
} else {