From 6f0f61ad4430f3df72b118371a8b1643ddb69429 Mon Sep 17 00:00:00 2001 From: Jarno Mäkipää Date: Sat, 15 Feb 2020 23:55:06 +0200 Subject: vi: Rearrange functions, add o, O, I Place function calls in order so that there is no unneeded declarations, clear some whitespace stuff. Add few commands that are commonly used. cleanup: reorganize functions cleanup: some whitespace stuff add: vi_o vi_O vi_I fix: stop at edges when h and l fix: fix dd not updating screen fix: render after all delete moves --- toys/pending/vi.c | 1158 ++++++++++++++++++++++++++--------------------------- 1 file changed, 561 insertions(+), 597 deletions(-) (limited to 'toys/pending/vi.c') diff --git a/toys/pending/vi.c b/toys/pending/vi.c index c6f44ef6..b5f34e21 100644 --- a/toys/pending/vi.c +++ b/toys/pending/vi.c @@ -21,37 +21,28 @@ config VI #include "toys.h" GLOBALS( - char *s; - int cur_col; - int cur_row; - int scr_row; - int drawn_row; - int drawn_col; - unsigned screen_height; - unsigned screen_width; - int vi_mode; - int count0; - int count1; - int vi_mov_flag; - int modified; - char vi_reg; - char *last_search; - int tabstop; - int list; - struct str_line { - int alloc; - int len; - char *data; - } *il; - size_t screen; //offset in slices must be higher than cursor - size_t cursor; //offset in slices - //yank buffer - struct yank_buf { - char reg; - int alloc; - char* data; - } yank; - + char *s; + int vi_mode, tabstop, list; + int cur_col, cur_row, scr_row; + int drawn_row, drawn_col; + int count0, count1, vi_mov_flag; + unsigned screen_height, screen_width; + char vi_reg, *last_search; + struct str_line { + int alloc; + int len; + char *data; + } *il; + size_t screen, cursor; //offsets + //yank buffer + struct yank_buf { + char reg; + int alloc; + char* data; + } yank; + + int modified, fd; + size_t filesize; // mem_block contains RO data that is either original file as mmap // or heap allocated inserted data // @@ -85,47 +76,58 @@ GLOBALS( const char *data; } *node; } *slices; - - size_t filesize; - int fd; //file_handle - ) static const char *blank = " \n\r\t"; static const char *specials = ",.:;=-+*/(){}<>[]!@#$%^&|\\?\"\'"; -// TT.vi_mov_flag is used for special cases when certain move -// acts differently depending is there DELETE/YANK or NOP -// Also commands such as G does not default to count0=1 -// 0x1 = Command needs argument (f,F,r...) -// 0x2 = Move 1 right on yank/delete/insert (e, $...) -// 0x4 = yank/delete last line fully -// 0x10000000 = redraw after cursor needed -// 0x20000000 = full redraw needed -// 0x40000000 = count0 not given -// 0x80000000 = move was reverse +//get utf8 length and width at same time +static int utf8_lnw(int *width, char *s, int bytes) +{ + wchar_t wc; + int length = 1; + if (*s == '\t') *width = TT.tabstop; + else { + length = utf8towc(&wc, s, bytes); + if (length < 1) length = 0, *width = 0; + else *width = wcwidth(wc); + } + return length; +} -static void draw_page(); +static int utf8_dec(char key, char *utf8_scratch, int *sta_p) +{ + int len = 0; + char *c = utf8_scratch; + c[*sta_p] = key; + if (!(*sta_p)) *c = key; + if (*c < 0x7F) { *sta_p = 1; return 1; } + if ((*c & 0xE0) == 0xc0) len = 2; + else if ((*c & 0xF0) == 0xE0 ) len = 3; + else if ((*c & 0xF8) == 0xF0 ) len = 4; + else {*sta_p = 0; return 0; } -//utf8 support -static int utf8_lnw(int* width, char* str, int bytes); -static int utf8_dec(char key, char *utf8_scratch, int *sta_p); -static char* utf8_last(char* str, int size); + (*sta_p)++; + if (*sta_p == 1) return 0; + if ((c[*sta_p-1] & 0xc0) != 0x80) {*sta_p = 0; return 0; } -static int cur_left(int count0, int count1, char* unused); -static int cur_right(int count0, int count1, char* unused); -static int cur_up(int count0, int count1, char* unused); -static int cur_down(int count0, int count1, char* unused); -static void check_cursor_bounds(); -static void adjust_screen_buffer(); -static int search_str(char *s); + if (*sta_p == len) { c[(*sta_p)] = 0; return 1; } -//from TT.cursor to -static int vi_yank(char reg, size_t from, int flags); -static int vi_delete(char reg, size_t from, int flags); + return 0; +} +static char* utf8_last(char* str, int size) +{ + char* end = str+size; + int pos = size, len, width = 0; + for (;pos >= 0; end--, pos--) { + len = utf8_lnw(&width, end, size-pos); + if (len && width) return end; + } + return 0; +} struct double_list *dlist_add_before(struct double_list **head, struct double_list **list, char *data) @@ -254,27 +256,21 @@ static int cut_str(size_t offset, size_t len) spos += s->node->len; offset += s->node->len; s = dlist_pop(&s); - if (s == TT.slices) TT.slices = s->next; - } - else if (spos < offset && ( end >= spos+s->node->len)) { + } else if (spos < offset && ( end >= spos+s->node->len)) { //cut end size_t clip = s->node->len - (offset - spos); offset = spos+s->node->len; spos += s->node->len; s->node->len -= clip; - } - - else if (spos == offset && s == e) { + } else if (spos == offset && s == e) { //cut begin size_t clip = end - offset; s->node->len -= clip; s->node->data += clip; break; - } - - else { + } else { //cut middle struct slice *tail = xmalloc(sizeof(struct slice)); size_t clip = end-offset; @@ -410,7 +406,7 @@ static int text_codepoint(char *dest, size_t offset) if (!state) return -1; if (!finished && !state) return -1; - if (dest) memcpy(dest,scratch,8); + if (dest) memcpy(dest, scratch, 8); return strlen(scratch); } @@ -512,15 +508,9 @@ static void block_list_free(void *node) static void linelist_unload() { llist_traverse((void *)TT.slices, llist_free_double); - TT.slices = 0; - llist_traverse((void *)TT.text, block_list_free); - TT.text = 0; - - if (TT.fd) { - xclose(TT.fd); - TT.fd = 0; - } + if (TT.fd) xclose(TT.fd); + TT.slices = 0, TT.text = 0, TT.fd = 0; } static int linelist_load(char *filename) @@ -575,95 +565,306 @@ static void write_file(char *filename) } -static int vi_yy(char reg, int count0, int count1) +//jump into valid offset index +//and valid utf8 codepoint +static void check_cursor_bounds() { - size_t history = TT.cursor; - size_t pos = text_sol(TT.cursor); //go left to first char on line - TT.vi_mov_flag |= 0x4; + char buf[8] = {0}; + int len, width = 0; + if (!TT.filesize) TT.cursor = 0; - for (;count0; count0--) TT.cursor = text_nsol(TT.cursor); + for (;;) { + if (TT.cursor < 1) { + TT.cursor = 0; + return; + } else if (TT.cursor >= TT.filesize-1) { + TT.cursor = TT.filesize-1; + return; + } + if ((len = text_codepoint(buf, TT.cursor)) < 1) { + TT.cursor--; //we are not in valid data try jump over + continue; + } + if (utf8_lnw(&width, buf, len) && width) break; + else TT.cursor--; //combine char jump over + } +} - vi_yank(reg, pos, 0); +// TT.vi_mov_flag is used for special cases when certain move +// acts differently depending is there DELETE/YANK or NOP +// Also commands such as G does not default to count0=1 +// 0x1 = Command needs argument (f,F,r...) +// 0x2 = Move 1 right on yank/delete/insert (e, $...) +// 0x4 = yank/delete last line fully +// 0x10000000 = redraw after cursor needed +// 0x20000000 = full redraw needed +// 0x40000000 = count0 not given +// 0x80000000 = move was reverse + +//TODO rewrite the logic, difficulties counting lines +//and with big files scroll should not rely in knowing +//absoluteline numbers +static void adjust_screen_buffer() +{ + size_t c, s; + TT.cur_row = 0, TT.scr_row = 0; + if (!TT.cursor) { + TT.screen = 0; + TT.vi_mov_flag = 0x20000000; + return; + } else if (TT.screen > (1<<18) || TT.cursor > (1<<18)) { + //give up, file is big, do full redraw + + TT.screen = text_strrchr(TT.cursor-1, '\n')+1; + TT.vi_mov_flag = 0x20000000; + return; + } + + s = text_count(0, TT.screen, '\n'); + c = text_count(0, TT.cursor, '\n'); + if (s >= c) { + TT.screen = text_strrchr(TT.cursor-1, '\n')+1; + s = c; + TT.vi_mov_flag = 0x20000000; //TODO I disabled scroll + } else { + int distance = c-s+1; + if (distance > (int)TT.screen_height) { + int n, adj = distance-TT.screen_height; + TT.vi_mov_flag = 0x20000000; //TODO I disabled scroll + for (;adj; adj--, s++) + if ((n = text_strchr(TT.screen, '\n'))+1 > TT.screen) + TT.screen = n+1; + } + } + + TT.scr_row = s; + TT.cur_row = c; - TT.cursor = history; - return 1; } -static int vi_dd(char reg, int count0, int count1) +//TODO search yank buffer by register +//TODO yanks could be separate slices so no need to copy data +//now only supports default register +static int vi_yank(char reg, size_t from, int flags) { - size_t pos = text_sol(TT.cursor); //go left to first char on line - TT.vi_mov_flag |= 0x4; + size_t start = from, end = TT.cursor; + char *str; - for (;count0; count0--) TT.cursor = text_nsol(TT.cursor); + memset(TT.yank.data, 0, TT.yank.alloc); + if (TT.vi_mov_flag&0x80000000) start = TT.cursor, end = from; + else TT.cursor = start; //yank moves cursor to left pos always? + + if (TT.yank.alloc < end-from) { + size_t new_bounds = (1+end-from)/1024; + new_bounds += ((1+end-from)%1024) ? 1 : 0; + new_bounds *= 1024; + TT.yank.data = xrealloc(TT.yank.data, new_bounds); + TT.yank.alloc = new_bounds; + } + + //this is naive copy + for (str = TT.yank.data ; start TT.filesize-1) break; - //if at empty jump to non empty - if (c == '\n') { - if (++TT.cursor > TT.filesize-1) break; - if ((c = text_byte(TT.cursor)) == '\n') break; + TT.vi_mov_flag |= 0x80000000; + for (;count && TT.cursor; count--) { + TT.cursor--; + if (text_byte(TT.cursor) == '\n') TT.cursor++; + check_cursor_bounds(); + } + return 1; +} + +static int cur_right(int count0, int count1, char *unused) +{ + int count = count0*count1, len, width = 0; + char buf[8] = {0}; + + for (;count; count--) { + len = text_codepoint(buf, TT.cursor); + + if (*buf == '\n') break; + else if (len > 0) TT.cursor += len; + else TT.cursor++; + + for (;TT.cursor < TT.filesize;) { + if ((len = text_codepoint(buf, TT.cursor)) < 1) { + TT.cursor++; //we are not in valid data try jump over continue; - } else if (strchr(blank, c)) do { - if (++TT.cursor > TT.filesize-1) break; - c = text_byte(TT.cursor); - } while (strchr(blank, c)); - //if at special jump to non special - else if (strchr(specials, c)) do { - if (++TT.cursor > TT.filesize-1) break; - c = text_byte(TT.cursor); - } while (strchr(specials, c)); - //else jump to empty or spesial - else do { - if (++TT.cursor > TT.filesize-1) break; - c = text_byte(TT.cursor); - } while (c && !strchr(blank, c) && !strchr(specials, c)); + } - } while (strchr(blank, c) && c != '\n'); //never stop at empty + if (utf8_lnw(&width, buf, len) && width) break; + else TT.cursor += len; + } } check_cursor_bounds(); return 1; } -static int vi_movb(int count0, int count1, char* unused) +//TODO column shift +static int cur_up(int count0, int count1, char *unused) { int count = count0*count1; - int type = 0; - char c; - while (count--) { - c = text_byte(TT.cursor); - do { - if (!TT.cursor) break; - //if at empty jump to non empty - if (strchr(blank, c)) do { + for (;count--;) TT.cursor = text_psol(TT.cursor); + + TT.vi_mov_flag |= 0x80000000; + check_cursor_bounds(); + return 1; +} + +//TODO column shift +static int cur_down(int count0, int count1, char *unused) +{ + int count = count0*count1; + for (;count--;) TT.cursor = text_nsol(TT.cursor); + + check_cursor_bounds(); + return 1; +} + +static int search_str(char *s) +{ + size_t pos = text_strstr(TT.cursor+1, s); + + if (TT.last_search != s) { + free(TT.last_search); + TT.last_search = xstrdup(s); + } + + if (pos != SIZE_MAX) TT.cursor = pos; + check_cursor_bounds(); + return 0; +} + +static int vi_yy(char reg, int count0, int count1) +{ + size_t history = TT.cursor; + size_t pos = text_sol(TT.cursor); //go left to first char on line + TT.vi_mov_flag |= 0x4; + + for (;count0; count0--) TT.cursor = text_nsol(TT.cursor); + + vi_yank(reg, pos, 0); + + TT.cursor = history; + return 1; +} + +static int vi_dd(char reg, int count0, int count1) +{ + size_t pos = text_sol(TT.cursor); //go left to first char on line + TT.vi_mov_flag |= 0x30000000; + + for (;count0; count0--) TT.cursor = text_nsol(TT.cursor); + + if (pos == TT.cursor && TT.filesize) pos--; + vi_delete(reg, pos, 0); + check_cursor_bounds(); + return 1; +} + +static int vi_x(char reg, int count0, int count1) +{ + size_t from = TT.cursor; + + if (text_byte(TT.cursor) == '\n') { + cur_left(count0-1, 1, 0); + } + else { + cur_right(count0-1, 1, 0); + if (text_byte(TT.cursor) == '\n') TT.vi_mov_flag |= 2; + else cur_right(1, 1, 0); + } + + vi_delete(reg, from, 0); + check_cursor_bounds(); + return 1; +} + +static int vi_movw(int count0, int count1, char *unused) +{ + int count = count0*count1; + while (count--) { + char c = text_byte(TT.cursor); + do { + if (TT.cursor > TT.filesize-1) break; + //if at empty jump to non empty + if (c == '\n') { + if (++TT.cursor > TT.filesize-1) break; + if ((c = text_byte(TT.cursor)) == '\n') break; + continue; + } else if (strchr(blank, c)) do { + if (++TT.cursor > TT.filesize-1) break; + c = text_byte(TT.cursor); + } while (strchr(blank, c)); + //if at special jump to non special + else if (strchr(specials, c)) do { + if (++TT.cursor > TT.filesize-1) break; + c = text_byte(TT.cursor); + } while (strchr(specials, c)); + //else jump to empty or spesial + else do { + if (++TT.cursor > TT.filesize-1) break; + c = text_byte(TT.cursor); + } while (c && !strchr(blank, c) && !strchr(specials, c)); + + } while (strchr(blank, c) && c != '\n'); //never stop at empty + } + check_cursor_bounds(); + return 1; +} + +static int vi_movb(int count0, int count1, char *unused) +{ + int count = count0*count1; + int type = 0; + char c; + while (count--) { + c = text_byte(TT.cursor); + do { + if (!TT.cursor) break; + //if at empty jump to non empty + if (strchr(blank, c)) do { if (!--TT.cursor) break; c = text_byte(TT.cursor); } while (strchr(blank, c)); @@ -717,7 +918,7 @@ static int vi_move(int count0, int count1, char *unused) } -static void i_insert(char* str, int len) +static void i_insert(char *str, int len) { if (!str || !len) return; @@ -807,41 +1008,20 @@ static int vi_go(int count0, int count1, char *symbol) return 1; } -static int vi_delete(char reg, size_t from, int flags) +static int vi_o(char reg, int count0, int count1) { - size_t start = from, end = TT.cursor; - - vi_yank(reg, from, flags); - - if (TT.vi_mov_flag&0x80000000) - start = TT.cursor, end = from; - - //pre adjust cursor move one right until at next valid rune - if (TT.vi_mov_flag&2) { - //int len, width; - //char *s = end->line->data; - //len = utf8_lnw(&width, s+col_e, strlen(s+col_e)); - //for (;;) { - //col_e += len; - //len = utf8_lnw(&width, s+col_e, strlen(s+col_e)); - //if (len<1 || width || !(*(s+col_e))) break; - //} - } - //find if range contains atleast single /n - //if so set TT.vi_mov_flag |= 0x10000000; - - //do slice cut - cut_str(start, end-start); - - //cursor is at start at after delete - TT.cursor = start; - TT.filesize = text_filesize(); - //find line start by strrchr(/n) ++ - //set cur_col with crunch_n_str maybe? - + TT.cursor = text_eol(TT.cursor); + insert_str(xstrdup("\n"), TT.cursor++, 1, 1, HEAP); + TT.vi_mov_flag |= 0x30000000; + TT.vi_mode = 2; return 1; } +static int vi_O(char reg, int count0, int count1) +{ + TT.cursor = text_psol(TT.cursor); + return vi_o(reg, count0, count1); +} static int vi_D(char reg, int count0, int count1) { @@ -849,14 +1029,19 @@ static int vi_D(char reg, int count0, int count1) if (!count0) return 1; vi_eol(1, 1, 0); vi_delete(reg, pos, 0); - count0--; - if (count0) { - vi_dd(reg, count0, 1); - } + if (--count0) vi_dd(reg, count0, 1); + check_cursor_bounds(); return 1; } +static int vi_I(char reg, int count0, int count1) +{ + TT.cursor = text_sol(TT.cursor); + TT.vi_mode = 2; + return 1; +} + static int vi_join(char reg, int count0, int count1) { size_t next; @@ -875,41 +1060,6 @@ static int vi_find_next(char reg, int count0, int count1) return 1; } -static int vi_change(char reg, size_t to, int flags) -{ - vi_delete(reg, to, flags); - TT.vi_mode = 2; - return 1; -} - -//TODO search yank buffer by register -//TODO yanks could be separate slices so no need to copy data -//now only supports default register -static int vi_yank(char reg, size_t from, int flags) -{ - size_t start = from, end = TT.cursor; - char *str; - - memset(TT.yank.data, 0, TT.yank.alloc); - if (TT.vi_mov_flag&0x80000000) start = TT.cursor, end = from; - else TT.cursor = start; //yank moves cursor to left pos always? - - if (TT.yank.alloc < end-from) { - size_t new_bounds = (1+end-from)/1024; - new_bounds += ((1+end-from)%1024) ? 1 : 0; - new_bounds *= 1024; - TT.yank.data = xrealloc(TT.yank.data, new_bounds); - TT.yank.alloc = new_bounds; - } - - //this is naive copy - for (str = TT.yank.data ; startdata = xzalloc(80); - TT.yank.data = xzalloc(128); + int ret = 0; + if (wc < 32 && TT.list) { + tty_esc("1m"); + ret = crunch_escape(out,cols,wc); + tty_esc("m"); + } else if (wc == 0x09) { + if (out) { + int i = TT.tabstop; + for (;i--;) fputs(" ", out); + } + ret = TT.tabstop; + } else if (wc == '\n') return 0; + return ret; +} - TT.il->alloc = 80, TT.yank.alloc = 128; +//crunch_str with n bytes restriction for printing substrings or +//non null terminated strings +static int crunch_nstr(char **str, int width, int n, FILE *out, char *escmore, + int (*escout)(FILE *out, int cols, int wc)) +{ + int columns = 0, col, bytes; + char *start, *end; - linelist_load(0); - TT.screen = TT.cursor = 0; + for (end = start = *str; *end && n>0; columns += col, end += bytes, n -= bytes) { + wchar_t wc; - TT.vi_mov_flag = 0x20000000; - TT.vi_mode = 1, TT.tabstop = 8; - TT.screen_width = 80, TT.screen_height = 24; + if ((bytes = utf8towc(&wc, end, 4))>0 && (col = wcwidth(wc))>=0) { + if (!escmore || wc>255 || !strchr(escmore, wc)) { + if (width-columns=256) { - key -= 256; - if (key==KEY_UP) cur_up(1, 1, 0); - else if (key==KEY_DOWN) cur_down(1, 1, 0); - else if (key==KEY_LEFT) cur_left(1, 1, 0); - else if (key==KEY_RIGHT) cur_right(1, 1, 0); - draw_page(); - continue; - } - - if (TT.vi_mode == 1) { //NORMAL - switch (key) { - case '/': - case '?': - case ':': - TT.vi_mode = 0; - TT.il->data[0]=key; - TT.il->len++; - break; - case 'A': - vi_eol(1, 1, 0); - TT.vi_mode = 2; - break; - case 'a': - cur_right(1, 1, 0); - // FALLTHROUGH - case 'i': - TT.vi_mode = 2; - break; - case 27: - vi_buf[0] = 0; - vi_buf_pos = 0; - break; - default: - if (key > 0x20 && key < 0x7B) { - vi_buf[vi_buf_pos] = key;//TODO handle input better - vi_buf_pos++; - if (run_vi_cmd(vi_buf)) { - memset(vi_buf, 0, 16); - vi_buf_pos = 0; - } - else if (vi_buf_pos == 16) { - vi_buf_pos = 0; - memset(vi_buf, 0, 16); - } - - } - - break; - } - } else if (TT.vi_mode == 0) { //EX MODE - switch (key) { - case 0x7F: - case 0x08: - if (TT.il->len > 1) { - TT.il->data[--TT.il->len] = 0; - break; - } - // FALLTHROUGH - case 27: - TT.vi_mode = 1; - TT.il->len = 0; - memset(TT.il->data, 0, TT.il->alloc); - break; - case 0x0A: - case 0x0D: - if (run_ex_cmd(TT.il->data) == -1) - goto cleanup_vi; - TT.vi_mode = 1; - TT.il->len = 0; - memset(TT.il->data, 0, TT.il->alloc); - break; - default: //add chars to ex command until ENTER - if (key >= 0x20 && key < 0x7F) { //might be utf? - if (TT.il->len == TT.il->alloc) { - TT.il->data = realloc(TT.il->data, TT.il->alloc*2); - TT.il->alloc *= 2; - } - TT.il->data[TT.il->len] = key; - TT.il->len++; - } - break; - } - } else if (TT.vi_mode == 2) {//INSERT MODE - switch (key) { - case 27: - i_insert(TT.il->data, TT.il->len); - cur_left(1, 1, 0); - TT.vi_mode = 1; - TT.il->len = 0; - memset(TT.il->data, 0, TT.il->alloc); - break; - case 0x7F: - case 0x08: - if (TT.il->len) { - char *last = utf8_last(TT.il->data, TT.il->len); - int shrink = strlen(last); - memset(last, 0, shrink); - TT.il->len -= shrink; - } - break; - case 0x0A: - case 0x0D: - //insert newline - // - TT.il->data[TT.il->len++] = '\n'; - i_insert(TT.il->data, TT.il->len); - TT.il->len = 0; - memset(TT.il->data, 0, TT.il->alloc); - break; - default: - if ((key >= 0x20 || key == 0x09) && - utf8_dec(key, utf8_code, &utf8_dec_p)) { - - if (TT.il->len+utf8_dec_p+1 >= TT.il->alloc) { - TT.il->data = realloc(TT.il->data, TT.il->alloc*2); - TT.il->alloc *= 2; - } - strcpy(TT.il->data+TT.il->len, utf8_code); - TT.il->len += utf8_dec_p; - utf8_dec_p = 0; - *utf8_code = 0; - - } - break; - } - } - - draw_page(); - - } -cleanup_vi: - linelist_unload(); - free(TT.il->data), free(TT.il), free(TT.yank.data); - tty_reset(); - tty_esc("?1049l"); -} - -static int vi_crunch(FILE* out, int cols, int wc) -{ - int ret = 0; - if (wc < 32 && TT.list) { - tty_esc("1m"); - ret = crunch_escape(out,cols,wc); - tty_esc("m"); - } else if (wc == 0x09) { - if (out) { - int i = TT.tabstop; - for (;i--;) fputs(" ", out); - } - ret = TT.tabstop; - } else if (wc == '\n') return 0; - return ret; -} - -//crunch_str with n bytes restriction for printing substrings or -//non null terminated strings -static int crunch_nstr(char **str, int width, int n, FILE *out, char *escmore, - int (*escout)(FILE *out, int cols, int wc)) -{ - int columns = 0, col, bytes; - char *start, *end; - - for (end = start = *str; *end && n>0; columns += col, end += bytes, n -= bytes) { - wchar_t wc; - - if ((bytes = utf8towc(&wc, end, 4))>0 && (col = wcwidth(wc))>=0) { - if (!escmore || wc>255 || !strchr(escmore, wc)) { - if (width-columns= TT.filesize-1) { - TT.cursor = TT.filesize-1; - return; - } - if ((len = text_codepoint(buf, TT.cursor)) < 1) { - TT.cursor--; //we are not in valid data try jump over - continue; - } - if (utf8_lnw(&width, buf, len) && width) break; - else TT.cursor--; //combine char jump over - } -} - -//TODO rewrite the logic, difficulties counting lines -//and with big files scroll should not rely in knowing -//absoluteline numbers -static void adjust_screen_buffer() +void vi_main(void) { - size_t c, s; - TT.cur_row = 0, TT.scr_row = 0; - if (!TT.cursor) { - TT.screen = 0; - TT.vi_mov_flag = 0x20000000; - return; - } else if (TT.screen > (1<<18) || TT.cursor > (1<<18)) { - //give up, file is big, do full redraw + char keybuf[16] = {0}; + char vi_buf[16] = {0}; + char utf8_code[8] = {0}; + int utf8_dec_p = 0, vi_buf_pos = 0; + FILE *script = 0; + if (FLAG(s)) script = fopen(TT.s, "r"); - TT.screen = text_strrchr(TT.cursor-1, '\n')+1; - TT.vi_mov_flag = 0x20000000; - return; - } + TT.il = xzalloc(sizeof(struct str_line)); + TT.il->data = xzalloc(80); + TT.yank.data = xzalloc(128); - s = text_count(0, TT.screen, '\n'); - c = text_count(0, TT.cursor, '\n'); - if (s >= c) { - TT.screen = text_strrchr(TT.cursor-1, '\n')+1; - s = c; - TT.vi_mov_flag = 0x20000000; //TODO I disabled scroll - } else { - int distance = c-s+1; - if (distance > (int)TT.screen_height) { - int n, adj = distance-TT.screen_height; - TT.vi_mov_flag = 0x20000000; //TODO I disabled scroll - for (;adj; adj--, s++) - if ((n = text_strchr(TT.screen, '\n'))+1 > TT.screen) - TT.screen = n+1; - } - } + TT.il->alloc = 80, TT.yank.alloc = 128; - TT.scr_row = s; - TT.cur_row = c; + linelist_load(0); + TT.screen = TT.cursor = 0; -} + TT.vi_mov_flag = 0x20000000; + TT.vi_mode = 1, TT.tabstop = 8; + TT.screen_width = 80, TT.screen_height = 24; -//get utf8 length and width at same time -static int utf8_lnw(int* width, char* s, int bytes) -{ - wchar_t wc; - int length; + terminal_size(&TT.screen_width, &TT.screen_height); + TT.screen_height -= 1; - *width = 0; - if (*s == '\t') { - *width = TT.tabstop; - return 1; - } - length = utf8towc(&wc, s, bytes); - if (length < 1) return 0; - *width = wcwidth(wc); - return length; -} + set_terminal(0, 1, 0, 0); + //writes stdout into different xterm buffer so when we exit + //we dont get scroll log full of junk + tty_esc("?1049h"); + tty_esc("H"); + xflush(1); -static int utf8_dec(char key, char *utf8_scratch, int *sta_p) -{ - int len = 0; - char *c = utf8_scratch; - c[*sta_p] = key; - if (!(*sta_p)) *c = key; - if (*c < 0x7F) { *sta_p = 1; return 1; } - if ((*c & 0xE0) == 0xc0) len = 2; - else if ((*c & 0xF0) == 0xE0 ) len = 3; - else if ((*c & 0xF8) == 0xF0 ) len = 4; - else {*sta_p = 0; return 0; } - (*sta_p)++; + draw_page(); + for (;;) { + int key = 0; + if (script) { + key = fgetc(script); + if (key == EOF) { + fclose(script); + script = 0; + key = scan_key(keybuf, -1); + } + } else key = scan_key(keybuf, -1); - if (*sta_p == 1) return 0; - if ((c[*sta_p-1] & 0xc0) != 0x80) {*sta_p = 0; return 0; } + if (key == -1) goto cleanup_vi; - if (*sta_p == len) { c[(*sta_p)] = 0; return 1; } + terminal_size(&TT.screen_width, &TT.screen_height); + TT.screen_height -= 1; //TODO this is hack fix visual alignment - return 0; -} + // TODO: support cursor keys in ex mode too. + if (TT.vi_mode && key>=256) { + key -= 256; + if (key==KEY_UP) cur_up(1, 1, 0); + else if (key==KEY_DOWN) cur_down(1, 1, 0); + else if (key==KEY_LEFT) cur_left(1, 1, 0); + else if (key==KEY_RIGHT) cur_right(1, 1, 0); + draw_page(); + continue; + } -static char* utf8_last(char* str, int size) -{ - char* end = str+size; - int pos = size; - int len = 0; - int width = 0; - while (pos >= 0) { - len = utf8_lnw(&width, end, size-pos); - if (len && width) return end; - end--; pos--; - } - return 0; -} + if (TT.vi_mode == 1) { //NORMAL + switch (key) { + case '/': + case '?': + case ':': + TT.vi_mode = 0; + TT.il->data[0]=key; + TT.il->len++; + break; + case 'A': + vi_eol(1, 1, 0); + TT.vi_mode = 2; + break; + case 'a': + cur_right(1, 1, 0); + // FALLTHROUGH + case 'i': + TT.vi_mode = 2; + break; + case 27: + vi_buf[0] = 0; + vi_buf_pos = 0; + break; + default: + if (key > 0x20 && key < 0x7B) { + vi_buf[vi_buf_pos] = key;//TODO handle input better + vi_buf_pos++; + if (run_vi_cmd(vi_buf)) { + memset(vi_buf, 0, 16); + vi_buf_pos = 0; + } + else if (vi_buf_pos == 16) { + vi_buf_pos = 0; + memset(vi_buf, 0, 16); + } -static int cur_left(int count0, int count1, char* unused) -{ - int count = count0*count1; - TT.vi_mov_flag |= 0x80000000; - for (;count--;) { - if (!TT.cursor) return 1; + } - TT.cursor--; - check_cursor_bounds(); - } - return 1; -} + break; + } + } else if (TT.vi_mode == 0) { //EX MODE + switch (key) { + case 0x7F: + case 0x08: + if (TT.il->len > 1) { + TT.il->data[--TT.il->len] = 0; + break; + } + // FALLTHROUGH + case 27: + TT.vi_mode = 1; + TT.il->len = 0; + memset(TT.il->data, 0, TT.il->alloc); + break; + case 0x0A: + case 0x0D: + if (run_ex_cmd(TT.il->data) == -1) + goto cleanup_vi; + TT.vi_mode = 1; + TT.il->len = 0; + memset(TT.il->data, 0, TT.il->alloc); + break; + default: //add chars to ex command until ENTER + if (key >= 0x20 && key < 0x7F) { //might be utf? + if (TT.il->len == TT.il->alloc) { + TT.il->data = realloc(TT.il->data, TT.il->alloc*2); + TT.il->alloc *= 2; + } + TT.il->data[TT.il->len] = key; + TT.il->len++; + } + break; + } + } else if (TT.vi_mode == 2) {//INSERT MODE + switch (key) { + case 27: + i_insert(TT.il->data, TT.il->len); + cur_left(1, 1, 0); + TT.vi_mode = 1; + TT.il->len = 0; + memset(TT.il->data, 0, TT.il->alloc); + break; + case 0x7F: + case 0x08: + if (TT.il->len) { + char *last = utf8_last(TT.il->data, TT.il->len); + int shrink = strlen(last); + memset(last, 0, shrink); + TT.il->len -= shrink; + } + break; + case 0x0A: + case 0x0D: + //insert newline + // + TT.il->data[TT.il->len++] = '\n'; + i_insert(TT.il->data, TT.il->len); + TT.il->len = 0; + memset(TT.il->data, 0, TT.il->alloc); + break; + default: + if ((key >= 0x20 || key == 0x09) && + utf8_dec(key, utf8_code, &utf8_dec_p)) { -static int cur_right(int count0, int count1, char* unused) -{ - int count = count0*count1; - char buf[8] = {0}; - int len, width = 0; - for (;count; count--) { - if ((len = text_codepoint(buf, TT.cursor)) > 0) TT.cursor += len; - else TT.cursor++; + if (TT.il->len+utf8_dec_p+1 >= TT.il->alloc) { + TT.il->data = realloc(TT.il->data, TT.il->alloc*2); + TT.il->alloc *= 2; + } + strcpy(TT.il->data+TT.il->len, utf8_code); + TT.il->len += utf8_dec_p; + utf8_dec_p = 0; + *utf8_code = 0; - for (;TT.cursor < TT.filesize;) { - if ((len = text_codepoint(buf, TT.cursor)) < 1) { - TT.cursor++; //we are not in valid data try jump over - continue; + } + break; } - - if (utf8_lnw(&width, buf, len) && width) break; - else TT.cursor += len; } - if (*buf == '\n') break; - } - check_cursor_bounds(); - return 1; -} -//TODO column shift -static int cur_up(int count0, int count1, char* unused) -{ - int count = count0*count1; - for (;count--;) TT.cursor = text_psol(TT.cursor); + draw_page(); - TT.vi_mov_flag |= 0x80000000; - check_cursor_bounds(); - return 1; + } +cleanup_vi: + linelist_unload(); + free(TT.il->data), free(TT.il), free(TT.yank.data); + tty_reset(); + tty_esc("?1049l"); } -//TODO column shift -static int cur_down(int count0, int count1, char* unused) -{ - int count = count0*count1; - for (;count--;) TT.cursor = text_nsol(TT.cursor); - - check_cursor_bounds(); - return 1; -} -- cgit v1.2.3