From a43497b523ae7db08908d3113f003c6df5e83aea Mon Sep 17 00:00:00 2001 From: Jarno Mäkipää Date: Fri, 13 Sep 2019 20:14:32 +0300 Subject: vi: changes to buffer drawing Replaced: draw_str_until with lib/crunch_str() where possible Removed: Unused char draw functions. Implemented: crunch_nstr() which is crunch_str with additional check for byte length, this can be used to draw substrings or non null terminated strings. (This can be moved to lib/ if its useful for others) Reimplemented: Buffer drawing without line wrapping. Now too long lines are drawn with @ in end. And cursor line scrolls left and right when hitting right margin point. This will simplify buffer handling alot. Linewrapping can be reimplemented later if needed but will add complexity Implemented: set list and set nolist ex commands, set list will show escape codes such as tabs Fix: Bug on splitting 2 lines, split was 1 byte off. --- toys/pending/vi.c | 314 ++++++++++++++++++++++++++---------------------------- 1 file changed, 153 insertions(+), 161 deletions(-) diff --git a/toys/pending/vi.c b/toys/pending/vi.c index b861a667..80be7e80 100644 --- a/toys/pending/vi.c +++ b/toys/pending/vi.c @@ -32,6 +32,7 @@ GLOBALS( char vi_reg; char *last_search; int tabstop; + int list; ) /* @@ -59,14 +60,11 @@ struct linestack_show { }; static void draw_page(); -static int draw_str_until(int *drawn, char *str, int width, int bytes); -static void draw_char(char c, int x, int y, int highlight); //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 int utf8_len(char *str); static int utf8_width(char *str, int bytes); -static int draw_rune(char *c, int x, int y, int highlight); static char* utf8_last(char* str, int size); @@ -437,13 +435,15 @@ void i_split() { struct str_line *l = xmalloc(sizeof(struct str_line)); int l_a = c_r->line->alloc_len; - int l_len = c_r->line->str_len-TT.cur_col; + int l_len = c_r->line->str_len-TT.cur_col-1; + l_len = (l_len >= 0) ? l_len : 0; l->str_data = xzalloc(l_a); l->alloc_len = l_a; l->str_len = l_len; - strncpy(l->str_data, &c_r->line->str_data[TT.cur_col], l_len); + strncpy(l->str_data, &c_r->line->str_data[TT.cur_col+1], l_len); l->str_data[l_len] = 0; c_r->line->str_len -= l_len; + if (c_r->line->str_len <= 0) c_r->line->str_len = 0; c_r->line->str_data[c_r->line->str_len] = 0; c_r = (struct linelist*)dlist_insert((struct double_list**)&c_r, (char*)l); c_r->line = l; @@ -517,7 +517,7 @@ static int vi_delete(char reg, struct linelist *row, int col, int flags) int distance = col - TT.cur_col; if (distance > 0) vi_x(reg, distance, 1); } - if (TT.vi_mov_flag&2) + if (TT.vi_mov_flag&2) vi_x(reg, 1, 1); } return 1; @@ -765,6 +765,14 @@ int run_ex_cmd(char *cmd) write_file(0); return 1; } + else if (strstr(&cmd[1], "set list")) { + TT.list = 1; + return 1; + } + else if (strstr(&cmd[1], "set nolist")) { + TT.list = 0; + return 1; + } } return 0; @@ -804,6 +812,8 @@ void vi_main(void) while(1) { int key = scan_key(keybuf, -1); + terminal_size(&TT.screen_width, &TT.screen_height); + TT.screen_height -= 2; //TODO this is hack fix visual alignment // TODO: support cursor keys in ex mode too. if (TT.vi_mode && key>=256) { key -= 256; @@ -942,6 +952,58 @@ cleanup_vi: tty_esc("?1049l"); } +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; + } + return ret; +} + +//crunch_str with n bytes restriction for printing substrings or +//non null terminated strings +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-columnsline->str_data && scr_buf->line->str_len) { - if (scr_buf == c_r) - break; - line = scr_buf->line->str_data; - bytes = scr_buf->line->str_len; - scr_buf = scr_buf->down; - } else { - if (scr_buf == c_r) - break; - y++; - tty_jump(0, y); - //printf(" \n"); - if (scr_buf) scr_buf = scr_buf->down; + for (; y < TT.screen_height; y++ ) { + tty_jump(0, y); + if (scr_buf == c_r) + break; + if (scr_buf) { + if (scr_buf->line->str_data && scr_buf->line->str_len) { + line = scr_buf->line->str_data; + bytes = scr_buf->line->str_len; + scr_buf = scr_buf->down; + crunch_str(&line, TT.screen_width-1, stdout, "\t", vi_crunch); + if ( *line ) printf("@"); + } else scr_buf = scr_buf->down; } - } - //draw cursor row until cursor - //this is to calculate cursor position on screen and possible insert + + //draw cursor row + ///////////////////////////////////////////////////////////// + //for long lines line starts to scroll when cursor hits margin line = scr_buf->line->str_data; bytes = TT.cur_col; - for (; y < TT.screen_height; ) { - if (bytes) { - x = draw_str_until(&drawn, line, TT.screen_width, bytes); - bytes = drawn ? (bytes-drawn) : 0; - line = bytes ? (line+drawn) : 0; - } - if (!bytes) break; - y++; - tty_jump(0, y); - } + end = line; + + //find cursor position + aw = crunch_nstr(&end, 1024, bytes, 0, "\t", vi_crunch); + + + //if we need to render text that is not inserted to buffer yet if (TT.vi_mode == 2 && il->str_len) { - line = il->str_data; - bytes = il->str_len; - cx_scr = x; - cy_scr = y; - x = draw_str_until(&drawn, line, TT.screen_width-x, bytes); - bytes = drawn ? (bytes-drawn) : 0; - line = bytes ? (line+drawn) : 0; - cx_scr += x; - for (; y < TT.screen_height; ) { - if (bytes) { - x = draw_str_until(&drawn, line, TT.screen_width, bytes); - bytes = drawn ? (bytes-drawn) : 0; - line = bytes ? (line+drawn) : 0; - cx_scr = x; - } - if (!bytes) break; - y++; - cy_scr = y; - tty_jump(0, y); + char* iend = il->str_data; //input end + x = 0; + //find insert end position + iw = crunch_str(&iend, 1024, 0, "\t", vi_crunch); + clip = (aw+iw) - TT.screen_width+margin; + + //if clipped area is bigger than text before insert + if (clip > aw) { + clip -= aw; + iend = il->str_data; + + iw -= crunch_str(&iend, clip, 0, "\t", vi_crunch); + x = crunch_str(&iend, iw, stdout, "\t", vi_crunch); + } else { + iend = il->str_data; + end = line; + + //if clipped area is substring from cursor row start + aw -= crunch_nstr(&end, clip, bytes, 0, "\t", vi_crunch); + x = crunch_str(&end, aw, stdout, "\t", vi_crunch); + x += crunch_str(&iend, iw, stdout, "\t", vi_crunch); } - } else { - cy_scr = y; - cx_scr = x; } - line = scr_buf->line->str_data+TT.cur_col; - bytes = scr_buf->line->str_len-TT.cur_col; - scr_buf = scr_buf->down; - x = draw_str_until(&drawn,line, TT.screen_width-x, bytes); - bytes = drawn ? (bytes-drawn) : 0; - line = bytes ? (line+drawn) : 0; - y++; - tty_jump(0, y); + //when not inserting but still need to keep cursor inside screen + //margin area + else if ( aw+margin > TT.screen_width) { + clip = aw-TT.screen_width+margin; + end = line; + aw -= crunch_nstr(&end, clip, bytes, 0, "\t", vi_crunch); + x = crunch_str(&end, aw, stdout, "\t", vi_crunch); + } + else { + end = line; + x = crunch_nstr(&end, aw, bytes, stdout, "\t", vi_crunch); + } + cx_scr = x; + cy_scr = y; + if (scr_buf->line->str_len > bytes) { + x += crunch_str(&end, TT.screen_width-x, stdout, "\t", vi_crunch); + } -//draw until end - for (; y < TT.screen_height; ) { - if (line && bytes) { - draw_str_until(&drawn, line, TT.screen_width, bytes); - bytes = drawn ? (bytes-drawn) : 0; - line = bytes ? (line+drawn) : 0; - y++; - tty_jump(0, y); - } else if (scr_buf && scr_buf->line->str_data && scr_buf->line->str_len) { - line = scr_buf->line->str_data; - bytes = scr_buf->line->str_len; - scr_buf = scr_buf->down; - } else { - y++; - tty_jump(0, y); - if (scr_buf) scr_buf = scr_buf->down; - } + tty_jump(0, ++y); + if (scr_buf) scr_buf = scr_buf->down; + // drawing cursor row ends + /////////////////////////////////////////////////////////////////// +//draw until end + for (; y < TT.screen_height; y++ ) { + tty_jump(0, y); + if (scr_buf) { + if (scr_buf->line->str_data && scr_buf->line->str_len) { + line = scr_buf->line->str_data; + bytes = scr_buf->line->str_len; + scr_buf = scr_buf->down; + crunch_str(&line, TT.screen_width-1, stdout, "\t", vi_crunch); + if ( *line ) printf("@"); + } else scr_buf = scr_buf->down; + } else printf("~"); } tty_jump(0, TT.screen_height); @@ -1092,35 +1156,6 @@ static void draw_page() } -static void draw_char(char c, int x, int y, int highlight) -{ - tty_jump(x, y); - if (highlight) { - tty_esc("30m"); //foreground black - tty_esc("47m"); //background white - } - printf("%c", c); -} - -//utf rune draw -//printf and useless copy could be replaced by direct write() to stdout -static int draw_rune(char *c, int x, int y, int highlight) -{ - int l = utf8_len(c); - char t[5] = {0, 0, 0, 0, 0}; - if (!l) return 0; - tty_jump(x, y); - tty_esc("0m"); - if (highlight) { - tty_esc("30m"); //foreground black - tty_esc("47m"); //background white - } - strncpy(t, c, 5); - printf("%s", t); - tty_esc("0m"); - return l; -} - static void check_cursor_bounds() { if (c_r->line->str_len == 0) TT.cur_col = 0; @@ -1268,49 +1303,6 @@ static char* utf8_last(char* str, int size) return 0; } -static int draw_str_until(int *drawn, char *str, int width, int bytes) -{ - int len = 0; - int rune_width = 0; - int rune_bytes = 0; - int max_bytes = bytes; - int max_width = width; - char* end = str; - for (;width && bytes;) { - if (*end == 0x09) { - rune_bytes = 1; - rune_width = TT.tabstop; - } else rune_bytes = utf8_lnw(&rune_width, end, 4); - - if (!rune_bytes) break; - if (width - rune_width < 0) goto write_bytes; - width -= rune_width; - bytes -= rune_bytes; - end += rune_bytes; - } - for (;bytes;) { - if (*end == 0x09) { - rune_bytes = 1; - rune_width = TT.tabstop; - } else rune_bytes = utf8_lnw(&rune_width, end, 4); - - if (!rune_bytes) break; - if (rune_width) break; - bytes -= rune_bytes; - end += rune_bytes; - } -write_bytes: - len = max_bytes-bytes; - for (;len--; str++) { - if (*str == 0x09) { - int i = 8; - for (;i--;) fwrite(" ", 1, 1, stdout); - } else fwrite(str, 1, 1, stdout); - } - *drawn = max_bytes-bytes; - return max_width-width; -} - static int cur_left(int count0, int count1, char* unused) { int count = count0*count1; -- cgit v1.2.3