diff options
author | Jarno Mäkipää <jmakip87@gmail.com> | 2020-02-15 23:55:06 +0200 |
---|---|---|
committer | Rob Landley <rob@landley.net> | 2020-02-28 17:59:05 -0600 |
commit | 6f0f61ad4430f3df72b118371a8b1643ddb69429 (patch) | |
tree | def1d11a1ef59de7d7cf106db58cf3c655481b7e /toys/pending | |
parent | 436d18dfea8e01bca368ac65970b57201d485b97 (diff) | |
download | toybox-6f0f61ad4430f3df72b118371a8b1643ddb69429.tar.gz |
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
Diffstat (limited to 'toys/pending')
-rw-r--r-- | toys/pending/vi.c | 986 |
1 files changed, 475 insertions, 511 deletions
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,6 +565,217 @@ static void write_file(char *filename) } +//jump into valid offset index +//and valid utf8 codepoint +static void check_cursor_bounds() +{ + char buf[8] = {0}; + int len, width = 0; + if (!TT.filesize) TT.cursor = 0; + + 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 + } +} + +// 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; + +} + +//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 ; start<end; start++, str++) *str = text_byte(start); + + *str = 0; + + return 1; +} + +static int vi_delete(char reg, size_t from, int flags) +{ + 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) { + //TODO + } + //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.vi_mov_flag |= 0x30000000; + + return 1; +} + +static int vi_change(char reg, size_t to, int flags) +{ + vi_delete(reg, to, flags); + TT.vi_mode = 2; + return 1; +} + +static int cur_left(int count0, int count1, char *unused) +{ + int count = count0*count1; + 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; + } + + if (utf8_lnw(&width, buf, len) && width) break; + else TT.cursor += len; + } + } + 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); + + 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; @@ -592,7 +793,7 @@ static int vi_yy(char reg, int count0, int count1) 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 |= 0x4; + TT.vi_mov_flag |= 0x30000000; for (;count0; count0--) TT.cursor = text_nsol(TT.cursor); @@ -620,7 +821,7 @@ static int vi_x(char reg, int count0, int count1) return 1; } -static int vi_movw(int count0, int count1, char* unused) +static int vi_movw(int count0, int count1, char *unused) { int count = count0*count1; while (count--) { @@ -653,7 +854,7 @@ static int vi_movw(int count0, int count1, char* unused) return 1; } -static int vi_movb(int count0, int count1, char* unused) +static int vi_movb(int count0, int count1, char *unused) { int count = count0*count1; int type = 0; @@ -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 ; start<end; start++, str++) *str = text_byte(start); - - *str = 0; - - return 1; -} - //NOTES //vi-mode cmd syntax is //("[REG])[COUNT0]CMD[COUNT1](MOV) @@ -942,13 +1092,16 @@ struct vi_special_param { }; struct vi_special_param vi_special[] = { - {"dd", &vi_dd}, - {"yy", &vi_yy}, {"D", &vi_D}, + {"I", &vi_I}, {"J", &vi_join}, + {"O", &vi_O}, {"n", &vi_find_next}, + {"o", &vi_o}, + {"p", &vi_push}, {"x", &vi_x}, - {"p", &vi_push} + {"dd", &vi_dd}, + {"yy", &vi_yy}, }; //there is around ~47 vi moves //some of them need extra params @@ -1039,19 +1192,6 @@ static int run_vi_cmd(char *cmd) return 0; } -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 run_ex_cmd(char *cmd) { @@ -1088,196 +1228,7 @@ static int run_ex_cmd(char *cmd) } -void vi_main(void) -{ - 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.il = xzalloc(sizeof(struct str_line)); - TT.il->data = xzalloc(80); - TT.yank.data = xzalloc(128); - - TT.il->alloc = 80, TT.yank.alloc = 128; - - 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; - - terminal_size(&TT.screen_width, &TT.screen_height); - TT.screen_height -= 1; - - 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); - - - 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 (key == -1) goto cleanup_vi; - - terminal_size(&TT.screen_width, &TT.screen_height); - TT.screen_height -= 1; //TODO this is hack fix visual alignment - - // 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; - } - - 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) +static int vi_crunch(FILE *out, int cols, int wc) { int ret = 0; if (wc < 32 && TT.list) { @@ -1329,7 +1280,6 @@ static int crunch_nstr(char **str, int width, int n, FILE *out, char *escmore, return columns; } -//BUGBUG cursor at eol static void draw_page() { unsigned y = 0; @@ -1485,179 +1435,193 @@ static void draw_page() xflush(1); } -//jump into valid offset index -//and valid utf8 codepoint -static void check_cursor_bounds() -{ - char buf[8] = {0}; - int len, width = 0; - if (!TT.filesize) TT.cursor = 0; - - 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 - } -} -//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; -} |