#include #include #include #include #include #include "buf.h" struct mark { struct line *line; int x; }; struct mark mark[256]; static void remove_line_marks(struct line *l); static int cmd_end(struct cmd *c, struct buf *b) { struct line *l; int i; l = curr_line; c->mstart = cur; for (i = 0; i < c->count - 1; i++) { l = line_next(l); if (l == NULL) return (1); } c->new_pos.y = cur.y + c->count - 1; c->new_pos.x = l->len; /* Will get fixed up */ if (c->count > 1) { c->mend.x = 0; c->mend.y = c->new_pos.y + 1; } else c->mend = c->new_pos; return (0); } static int cmd_begin(struct cmd *c, struct buf *b) { c->mend = cur; c->mstart.x = c->new_pos.x = 0; c->mstart.y = c->new_pos.y = cur.y; return (0); } static int cmd_begin_nonwhite(struct cmd *c, struct buf *b) { int i; c->mstart.y = c->mend.y = c->new_pos.y = cur.y; for (i = 0; i < curr_line->len; i++) { if (!isblank(curr_line->line[i])) break; } c->new_pos.x = i; if (i < cur.x) { c->mend = cur; c->mstart.x = i; } else { c->mstart = cur; c->mend.x = i; } return (0); } static int cmd_line(struct cmd *c, struct buf *b) { if (c->count == 0) { c->mstart = cur; c->mend.x = c->new_pos.x = 0; c->mend.y = c->new_pos.y = b->num_lines - 1; if (c->mend.y < 0) c->mend.y = 0; return (0); } if (c->count > b->num_lines) return (1); c->new_pos.x = 0; c->new_pos.y = c->count - 1; if (cur.y > c->count - 1) { c->mstart = c->new_pos; c->mend = cur; } else { c->mend = c->new_pos; c->mstart = cur; } return (0); } static int cmd_join(struct cmd *c, struct buf *b) { struct line *l; struct pos p; char *t; int count, i, len, len1; count = c->count; if (count >= 2) count--; for (i = 0; i < count; i++) { p.x = 0; l = line_next(curr_line); if (l == NULL) return (0); skipblank(l, &p); len1 = curr_line->len; len = len1 + l->len - p.x + 1; t = malloc(len); if (t == NULL) abort(); memcpy(t, curr_line->line, len1); t[len1] = ' '; memcpy(t + curr_line->len + 1, l->line + p.x, l->len - p.x); line_replace_undo(curr_line, t, len, cur.y); line_remove_undo(b, l, cur.y + 1); } c->new_pos.x = len1; c->new_pos.y = cur.y; return (0); } int delete(struct buf *b, struct buf *yank, struct pos start, struct pos end, int del) { struct line *l, *n; struct pos p; char *t; int dels, e, len; if (yank) buf_clear(yank); p = start; dels = 0; l = n = buf_get_line(b, p.y); while ((p.y < end.y) || ((p.y == end.y) && p.x < end.x)) { l = n; if (p.y < end.y) e = l->len; else e = end.x; if (yank) line_add_partial(yank, l, p.x, e, yank_buf.num_lines); /* Whole line */ if (end.y > p.y && p.x == 0) { if (del) { line_remove_undo(b, l, p.y - dels); remove_line_marks(l); dels++; p.y++; n = line_next(l); line_free(l); /* End of file. */ /* XXX also for !del ? */ if (n == NULL) { if (b->num_lines == 0) { l = line_alloc(NULL, 0); line_add_undo(b, l, 0); } return (1); } l = n; continue; } } t = NULL; len = l->len - (e - p.x); if (len != 0) { t = malloc(len); if (t == NULL) abort(); memcpy(t, l->line, p.x); memcpy(t + p.x, l->line + e, l->len - e); } if (del) line_replace_undo(l, t, len, p.y - dels); p.y++; p.x = 0; n = line_next(l); if (n == NULL) break; } return (0); } static int cmd_del(struct cmd *c, struct buf *b) { int del; if (c->key == 'd') del = 1; else del = 0; if (delete(b, &yank_buf, c->mstart, c->mend, del)) return (1); if (del) { c->new_pos = c->mstart; if (c->mstart.y != c->mend.y) c->new_pos.x = 0; } return (0); } static int cmd_left(struct cmd *c, struct buf *b) { int i; c->mend = c->new_pos = cur; for (i = 0; i < c->count; i++) if (c->new_pos.x > 0) c->new_pos.x--; c->mstart = c->new_pos; return (0); } static int cmd_down(struct cmd *c, struct buf *b) { struct line *l; int i; l = curr_line; c->mstart = c->new_pos = cur; for (i = 0; i < c->count; i++) { l = line_next(l); if (l == NULL) return (1); c->new_pos.y++; } c->mend = c->new_pos; return (0); } static int cmd_up(struct cmd *c, struct buf *b) { struct line *l; int i; c->mend = c->new_pos = cur; l = curr_line; for (i = 0; i < c->count; i++) { l = line_prev(l); if (l == NULL) return (1); c->new_pos.y--; } c->mstart = c->new_pos; return (0); } static int cmd_right(struct cmd *c, struct buf *b) { int i; c->mstart = c->new_pos = cur; for (i = 0; i < c->count; i++) if (c->new_pos.x < curr_line->len) c->new_pos.x++; c->mend = c->new_pos; return (0); } static int cmd_nextpage(struct cmd *c, struct buf *b) { /* XXX new cursor should be on top of screen */ c->mstart = cur; c->mend.x = 0; c->mend.y = cur.y + c->count * maxy; if (c->mend.y > b->num_lines - 1) c->mend.y = b->num_lines - 1; c->new_pos = c->mend; return (0); } static int cmd_prevpage(struct cmd *c, struct buf *b) { /* XXX new cursor should be on bottom of screen */ c->mend = cur; c->mstart.x = 0; c->mstart.y = cur.y - c->count * maxy; if (c->mstart.y < 0) c->mstart.y = 0; c->new_pos = c->mstart; return (0); } static int isregchar(int c) { if (isalnum(c) || (c == '_')) return (1); return (0); } void skipblank(struct line *l, struct pos *p) { for (; p->x < l->len; p->x++) if (!isblank(*(l->line + p->x))) break; } int nextchar(struct buf *b, struct pos *p) { struct line *l; l = buf_get_line(b, p->y); if (l == NULL) abort(); p->x++; while (p->x >= l->len) { l = line_next(l); if (l == NULL) return (-1); p->x = 0; p->y++; } return (*(l->line + p->x)); } int prevchar(struct buf *b, struct pos *p) { struct line *l; l = buf_get_line(b, p->y); if (l == NULL) abort(); p->x--; while (p->x < 0) { l = line_prev(l); if (l == NULL) return (-1); p->x = l->len - 1; p->y--; } return (*(l->line + p->x)); } static int cmd_word(struct cmd *c, struct buf *b) { struct line *l; struct pos p; char t; int i, s; p = c->mstart = cur; l = curr_line; i = c->count; /* XXX use nextchar? */ t = *(l->line + p.x); if (isblank(t)) { skipblank(l, &p); i--; } while (i--) { /* * We count non-alphanumeric (or underscore) characters and * blank as word delimiters. * Non-blank characters count as part of a word, so "3w" * on "a#b_b d" should put you on the 'd' (when starting at * 'a'). */ s = isregchar(t); for (; p.x < l->len; p.x++) { t = *(l->line + p.x); if (isblank(t)) break; if (s == 0 && isregchar(t)) break; else if (s == 1 && !isregchar(t)) break; } if (isblank(t)) skipblank(l, &p); while (p.x >= l->len) { /* Ran out of words on this line. */ p.x = 0; l = line_next(l); if (l == NULL) break; p.y++; skipblank(l, &p); } t = *(l->line + p.x); } c->mend = c->new_pos = p; return (0); } static void skipblankb(struct line *l, struct pos *p) { for (; p->x >= 0; p->x--) if (!isblank(*(l->line + p->x))) break; } static int cmd_wordb(struct cmd *c, struct buf *b) { struct line *l; struct pos p; char t; int i, s; p = c->mend = cur; l = curr_line; i = c->count + 1; /* XXX use prevchar? */ t = *(l->line + p.x); if (isblank(t)) { skipblankb(l, &p); i--; } while (i--) { /* See comment in cmd_word. */ s = isregchar(t); for (; p.x >= 0; p.x--) { t = *(l->line + p.x); if (isblank(t)) break; if (s == 0 && isregchar(t)) break; else if (s == 1 && !isregchar(t)) break; } if (i == 0) { p.x++; break; } if (isblank(t)) skipblankb(l, &p); while (p.x < 0) { /* Ran out of words on this line. */ l = line_prev(l); if (l == NULL) break; p.x = l->len - 1; p.y--; skipblankb(l, &p); } t = *(l->line + p.x); } c->mstart = c->new_pos = p; return (0); } static int cmd_del_char(struct cmd *c, struct buf *b) { char *t; int end, len; /* XXX save to yank buffer */ if (curr_line->len == 0) return (1); end = cur.x + c->count; if (end > curr_line->len - 1) end = curr_line->len; len = curr_line->len - (end - cur.x); t = malloc(len); if (t == NULL) abort(); memcpy(t, curr_line->line, cur.x); memcpy(t + cur.x, curr_line->line + end, len - cur.x); line_replace_undo(curr_line, t, len, cur.y); cur_fixup(); return (0); } static int cmd_put(struct cmd *c, struct buf *b) { struct line *l, *n; int c_y, i, init_y; c_y = cur.y; if (c->key == 'p') c_y++; init_y = c_y; for (i = 0; i < c->count; i++) { TAILQ_FOREACH(l, &yank_buf.lines, list) { n = line_dup(l); if (n == NULL) abort(); line_add_undo(b, n, c_y++); } } c->new_pos.y = init_y; return (0); } static void remove_line_marks(struct line *l) { int i; for (i = 0; i < 256; i++) { if (mark[i].line == l) { mark[i].line = NULL; mark[i].x = 0; } } } static int cmd_mark(struct cmd *c, struct buf *b) { struct mark *m; int k; k = getch(); if (k == ESC) return (1); m = &mark[k]; m->x = cur.x; m->line = curr_line; return (0); } static int cmd_gotomark(struct cmd *c, struct buf *b) { struct mark *m; int k, y; k = getch(); if (k == ESC) return (1); m = &mark[k]; if (m->line == NULL) return (1); y = buf_find_line(b, m->line); if (y == -1) abort(); c->new_pos.y = y; c->new_pos.x = 0; if ((c->key == '`') && (m->x < m->line->len)) c->new_pos.x = m->x; c->mstart = cur; c->mend = c->new_pos; return (0); } static int cmd_info(struct cmd *c, struct buf *b) { printd("%s: %smodified: line %d of %d", file.path, b->dirty ? "" : "un", cur.y + 1, b->num_lines); return (0); } static int matching_char(char t) { switch (t) { case '(': return (')'); case ')': return (-'('); case '{': return ('}'); case '}': return (-'{'); case '[': return (']'); case ']': return (-'['); case '<': return ('>'); case '>': return (-'<'); default: return (0); } } int findmatch(struct buf *b, char o, struct pos *p, int _depth) { char t, m; int back, depth; depth = _depth; m = matching_char(o); if (m == 0) return (-1); else if (m < 0) { m = -m; back = 1; } else back = 0; while (1) { if (back) t = prevchar(b, p); else t = nextchar(b, p); if (t == -1) break; if (t == o) depth++; else if (t == m) { if (depth == 0) return (0); depth--; } } return (depth + 1); } static int cmd_findmatch(struct cmd *c, struct buf *b) { struct pos p; char o, m; p = c->mstart = cur; for (; p.x < curr_line->len; p.x++) { o = *(curr_line->line + p.x); m = matching_char(o); if (m != 0) break; } if (findmatch(b, o, &p, 0) == 0) { c->mend = c->new_pos = p; return (0); } return (1); } struct commands cmds[NUM_COMMANDS] = { ['$'] = { CMD_COUNT | CMD_ISMOTION, cmd_end }, ['0'] = { CMD_ISMOTION, cmd_begin }, [':'] = { CMD_MODE, cmd_ex }, ['G'] = { CMD_COUNT | CMD_COUNT0 | CMD_ISMOTION | CMD_MLINE, cmd_line }, ['J'] = { CMD_COUNT | CMD_EFFECT, cmd_join }, ['O'] = { CMD_MODE | CMD_EFFECT, cmd_insert }, /* XXX should take count */ ['^'] = { CMD_ISMOTION, cmd_begin_nonwhite }, ['a'] = { CMD_MODE | CMD_EFFECT, cmd_insert }, /* XXX should take count */ ['b'] = { CMD_COUNT | CMD_ISMOTION, cmd_wordb }, ['c'] = { CMD_MODE | CMD_MOTION | CMD_EFFECT, cmd_change }, ['d'] = { CMD_COUNT | CMD_MOTION | CMD_EFFECT, cmd_del }, ['i'] = { CMD_MODE | CMD_EFFECT, cmd_insert }, /* XXX should take count */ ['h'] = { CMD_COUNT | CMD_ISMOTION, cmd_left }, ['j'] = { CMD_COUNT | CMD_ISMOTION | CMD_MLINE, cmd_down }, ['k'] = { CMD_COUNT | CMD_ISMOTION | CMD_MLINE, cmd_up }, ['l'] = { CMD_COUNT | CMD_ISMOTION, cmd_right }, ['m'] = { 0, cmd_mark }, ['n'] = { 0, cmd_search_next }, ['N'] = { 0, cmd_search_next }, ['o'] = { CMD_MODE | CMD_EFFECT, cmd_insert }, /* XXX should take count */ ['p'] = { CMD_COUNT | CMD_EFFECT, cmd_put }, ['P'] = { CMD_COUNT | CMD_EFFECT, cmd_put }, ['r'] = { CMD_COUNT | CMD_EFFECT, cmd_replace }, ['u'] = { CMD_EFFECT, cmd_undo }, ['w'] = { CMD_COUNT | CMD_ISMOTION, cmd_word }, ['x'] = { CMD_COUNT | CMD_EFFECT, cmd_del_char }, ['y'] = { CMD_COUNT | CMD_MOTION, cmd_del }, ['.'] = { 0, NULL }, /* XXX should take count */ ['/'] = { 0, cmd_search }, ['?'] = { 0, cmd_search }, ['\''] = { CMD_ISMOTION, cmd_gotomark }, ['`'] = { CMD_ISMOTION, cmd_gotomark }, ['%'] = { CMD_ISMOTION, cmd_findmatch }, [CTRL_G] = { 0, cmd_info }, [KEY_NPAGE] = { CMD_COUNT | CMD_ISMOTION, cmd_nextpage }, [KEY_PPAGE] = { CMD_COUNT | CMD_ISMOTION, cmd_prevpage }, [KEY_LEFT] = { CMD_COUNT | CMD_ISMOTION, cmd_left }, [KEY_DOWN] = { CMD_COUNT | CMD_ISMOTION, cmd_down }, [KEY_UP] = { CMD_COUNT | CMD_ISMOTION, cmd_up }, [KEY_RIGHT] = { CMD_COUNT | CMD_ISMOTION, cmd_right }, };