#include #include #include #include #include #include #include "buf.h" struct buf tmp_buf; struct pos ins_pos; /* Coordinates where we entered insert mode */ int last_chars, chars; int dirty; /* Have we written to the current line? */ int curr_start; /* Current line's start (after autoindent) */ static struct line * make_new_line(struct line *curr, int len) { struct line *l; struct pos p; char *text; int _len; text = NULL; _len = 0; if (opt_ai) { p.x = 0; skipblank(curr, &p); chars = _len = p.x; last_chars = -1; curr_start = _len; text = malloc(len + _len); if (text == NULL) abort(); memcpy(text, curr->line, _len); } else curr_start = 0; if (len == 0) dirty = 0; else dirty = 1; l = line_alloc(text, len + _len); return (l); } static void split_line(struct buf *b, struct line *l, int x, int skip, int y, int undo) { struct line *n; char *text; int len; len = l->len - x - skip; n = make_new_line(l, len); if (n == NULL) abort(); memcpy(n->line + curr_start, l->line + x + skip, len); text = malloc(x); if (text == NULL) abort(); memcpy(text, l->line, x); if (undo) { line_replace_undo(l, text, x, y); line_add_undo(b, n, y + 1); } else { line_replace(l, text, x); line_add(b, n, y + 1); } } static void update_display(struct buf *b) { struct line *l; int i, tmp_lines, y; tmp_lines = tmp_buf.num_lines - 1; for (y = 0, i = 0; y < maxy - 1; i++) { if ((disp_y + i >= ins_pos.y) && (i - (ins_pos.y - disp_y) < tmp_buf.num_lines)) l = buf_get_line(&tmp_buf, i - (ins_pos.y - disp_y)); else if (disp_y + i < ins_pos.y) l = buf_get_line(b, disp_y + i); else l = buf_get_line(b, disp_y + i - tmp_lines); if (disp_y + i == cur.y) cur_y = y; y += draw_line(l, y) + 1; } place_cursor(curr_line, cur.x, cur_y, 1); } static void add_to_tmp(int c) { struct line *l; char *text; int _dirty; if (c == '\n') { if (cur.x == curr_line->len) { _dirty = dirty; l = make_new_line(curr_line, 0); if (_dirty == 0) line_replace(curr_line, NULL, 0); /* XXX don't always add at the end? */ line_add(&tmp_buf, l, tmp_buf.num_lines); } else split_line(&tmp_buf, curr_line, cur.x, 0, tmp_buf.num_lines - 1, 0); chars++; return; } else if (c == BKSPC) { char *t; int len; if (chars == 0) { beep(); return; } chars--; cur.x--; /* Go to previous line */ if (cur.x < 0) { l = line_prev(curr_line); if (l == NULL) abort(); line_remove(&tmp_buf, curr_line); curr_line = l; cur.y--; cur.x = l->len; /* Force redraw */ last_chars = -1; return; } len = curr_line->len - 1; t = malloc(len); memcpy(t, curr_line->line, cur.x); memcpy(t + cur.x, curr_line->line + cur.x + 1, len - cur.x); line_replace(curr_line, t, len); place_cursor(curr_line, cur.x, cur_y, 1); return; } if (chars < last_chars) place_char(curr_line, cur.x, cur_y, c); chars++; text = malloc(curr_line->len + 1); if (text == NULL) abort(); memcpy(text, curr_line->line, cur.x); text[cur.x] = c; memcpy(text + cur.x + 1, curr_line->line + cur.x, curr_line->len - cur.x); line_replace(curr_line, text, curr_line->len + 1); } static void bounce_cursor(struct line *l, struct pos *p, int ins) { struct pollfd f; /* XXX use correct y coordinate reflecting linewrap */ if (ins) place_cursor(l, p->x, ins_pos.y + p->y - disp_y, 0); else place_cursor(l, p->x, p->y - disp_y, 0); f.fd = STDIN_FILENO; f.events = POLLIN | POLLERR; if (poll(&f, 1, SHOWMATCH_MS) == -1) printerr("poll"); place_cursor(curr_line, cur.x, cur_y, 1); } static void do_showmatch(struct buf *b, char k) { struct line *l; struct pos p; int depth; if (k != ')' && k != '}') return; p.x = cur.x; p.y = tmp_buf.num_lines - 1; depth = findmatch(&tmp_buf, k, &p, -1); if (depth == 0) { l = buf_get_line(&tmp_buf, p.y); bounce_cursor(l, &p, 1); } else if (depth > 0) { l = buf_get_line(b, ins_pos.y); if ((l = line_prev(l)) == NULL) return; p.y = ins_pos.y - 1; p.x = l->len; if (findmatch(b, k, &p, depth - 1) == 0) { l = buf_get_line(b, p.y); bounce_cursor(l, &p, 0); } } } static int insert_loop(struct buf *b) { int k; line_add(&tmp_buf, curr_line, 0); ins_pos = cur; k = 0; while (1) { if (chars >= last_chars) { last_chars = chars; update_display(b); } if (opt_sm) do_showmatch(b, k); k = getch(); if (k == ESC) { /* * Remove the autoindent if we haven't done anything. * * dirty == 0 and last_chars == 0 is possible if * we enter insert and exit without typing anything. */ if (dirty == 0 && last_chars != 0) line_replace(curr_line, NULL, 0); buf_union(b, &tmp_buf, ins_pos.y); buf_clear(&tmp_buf); /* XXX noop? */ curr_line = buf_get_line(b, cur.y); if (curr_line == NULL) abort(); cur_fixup(); return (0); } else if (k == 13) { add_to_tmp('\n'); y_inc(&cur); cur.x = curr_start; continue; } dirty = 1; add_to_tmp(k); if (k != BKSPC) cur.x++; } } int cmd_insert(struct cmd *c, struct buf *b) { struct line *l; chars = last_chars = 0; if ((c->key == 'a') && (curr_line->len != 0)) cur.x++; if (c->key == 'o' || c->key == 'O') { curr_line = make_new_line(curr_line, 0); cur.x = curr_line->len; l = line_alloc(NULL, 0); if (c->key == 'o') { cur.y++; if (cur.y > disp_y + maxy - 2) disp_y++; } line_add_undo(b, l, cur.y); } else { l = buf_get_line(b, cur.y); if (l == NULL) abort(); curr_line = line_dup(l); if (curr_line == NULL) abort(); } return (insert_loop(b)); } int cmd_replace(struct cmd *c, struct buf *b) { char *t; int i, k; if (cur.x + c->count > curr_line->len) return (1); k = getch(); if (k == ESC) return (1); if (k == 13) { split_line(b, curr_line, cur.x, 1, cur.y, 1); c->new_pos.y++; c->new_pos.x = curr_start; return (0); } t = malloc(curr_line->len); if (t == NULL) abort(); memcpy(t, curr_line->line, curr_line->len); for (i = 0; i < c->count; i++) t[cur.x + i] = k; line_replace_undo(curr_line, t, curr_line->len, cur.y); return (0); } int cmd_change(struct cmd *c, struct buf *b) { struct line *l; if (delete(b, &yank_buf, c->mstart, c->mend, 1)) return (1); l = buf_get_line(b, cur.y); if (l == NULL) abort(); curr_line = line_dup(l); if (curr_line == NULL) abort(); last_chars = chars = 0; if (c->mend.y == c->mstart.y) { place_char(curr_line, c->mend.x - 1, cur_y, '$'); last_chars = c->mend.x - c->mstart.x; } place_cursor(curr_line, cur.x, cur_y, 1); return (insert_loop(b)); }