diff options
author | Jarno Mäkipää <jmakip87@gmail.com> | 2019-09-21 22:20:36 +0300 |
---|---|---|
committer | Rob Landley <rob@landley.net> | 2019-09-22 09:25:06 -0500 |
commit | 49f5ac02c55426b8f7dd1ba22da2d02b0074366d (patch) | |
tree | a1954ba7b09e038595e12e70b56c7b0ea87f3cb7 | |
parent | 1c0d96520032b0ee22442cb3b6ad74c798b0af78 (diff) | |
download | toybox-49f5ac02c55426b8f7dd1ba22da2d02b0074366d.tar.gz |
vi: Added yank
Added: yank and push
Rewrote: delete operations
Minor cleanups:
Rewrote delete operations to use one delete function instead of having
separate behavior here and there.
Now delete and yank both always move cursor and then clip the whole
cursor area into yank register. For example x is just ld or jd depeding
are we right edge or not, and dd is jd with some special flags etc.
Now only default yank register is implemented, but implemeting
yank register list should be trivial since cmd execution already passes
register char.
-rw-r--r-- | toys/pending/vi.c | 472 |
1 files changed, 271 insertions, 201 deletions
diff --git a/toys/pending/vi.c b/toys/pending/vi.c index 80be7e80..50b8f0ab 100644 --- a/toys/pending/vi.c +++ b/toys/pending/vi.c @@ -35,31 +35,29 @@ GLOBALS( int list; ) -/* - * - * TODO: - * BUGS: screen pos adjust does not cover "widelines" - * - * - * REFACTOR: use dllist functions where possible. - * draw_page dont draw full page at time if nothing changed... - * ex callbacks - * - * FEATURE: ex: / ? % //atleast easy cases - * ex: r - * ex: !external programs - * ex: w filename //only writes to same file now - * big file support? - */ - - -struct linestack_show { - struct linestack_show *next; - long top, left; - int x, width, y, height; +struct str_line { + int alloc_len; + int str_len; + char *str_data; +}; +//yank buffer +struct yank_buf { + char reg; + int alloc; + char* data; +}; + + +//lib dllist uses next and prev kinda opposite what im used to so I just +//renamed both ends to up and down +struct linelist { + struct linelist *up;//next + struct linelist *down;//prev + struct str_line *line; }; static void draw_page(); + //utf8 support static int utf8_lnw(int* width, char* str, int bytes); static int utf8_dec(char key, char *utf8_scratch, int *sta_p); @@ -76,25 +74,25 @@ static void check_cursor_bounds(); static void adjust_screen_buffer(); static int search_str(char *s); -struct str_line { - int alloc_len; - int str_len; - char *str_data; -}; +static int vi_yank(char reg, struct linelist *row, int col, int flags); +static int vi_delete(char reg, struct linelist *row, int col, int flags); -//lib dllist uses next and prev kinda opposite what im used to so I just -//renamed both ends to up and down -struct linelist { - struct linelist *up;//next - struct linelist *down;//prev - struct str_line *line; -}; //inserted line not yet pushed to buffer struct str_line *il; struct linelist *text; //file loaded into buffer struct linelist *scr_r;//current screen coord 0 row struct linelist *c_r;//cursor position row +struct yank_buf yank; //single yank + +// 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 +// 0x40000000 = count0 not given +// 0x80000000 = move was reverse void dlist_insert_nomalloc(struct double_list **list, struct double_list *new) { @@ -179,14 +177,11 @@ int linelist_load(char *filename) lst->line->str_data[len-1] = 0; lst->line->str_len--; } - if (text == 0) { - text = lst; - } - - } - if (text) { - dlist_terminate(text->up); + if (text == 0) text = lst; } + + if (text) dlist_terminate(text->up); + fclose(fp); return 1; @@ -194,116 +189,58 @@ int linelist_load(char *filename) int vi_yy(char reg, int count0, int count1) { + struct linelist *pos = c_r; + int col = TT.cur_col; + TT.cur_col = 0; + TT.vi_mov_flag |= 0x4; + + if (count0>1) cur_down(count0-1, 1, 0); + + vi_yank(reg, pos, 0, 0); + + TT.cur_col = col, c_r = pos; return 1; } -//TODO this is overly complicated refactor with lib dllist int vi_dd(char reg, int count0, int count1) { - int count = count0*count1; - struct linelist *lst = c_r; - if (c_r == text && text == scr_r) { - if (!text->down && !text->up && text->line) { - text->line->str_len = 1; - sprintf(text->line->str_data, " "); - goto success_exit; - } - if (text->down) { - text = text->down; - text->up = 0; - c_r = text; - scr_r = text; - free(lst->line->str_data); - free(lst->line); - free(lst); - } - goto recursion_exit; - } - //TODO use lib dllist stuff - if (lst) - { - if (lst->down) { - lst->down->up = lst->up; - } - if (lst->up) { - lst->up->down = lst->down; - } - if (scr_r == c_r) { - scr_r = c_r->down ? c_r->down : c_r->up; - } - if (c_r->down) - c_r = c_r->down; - else { - c_r = c_r->up; - count = 1; - } - free(lst->line->str_data); - free(lst->line); - free(lst); - } + struct linelist *pos = c_r; + TT.cur_col = 0; + TT.vi_mov_flag |= 0x4; + if (count0>1) cur_down(count0-1, 1, 0); -recursion_exit: - count--; - //make this recursive - if (count>0) - return vi_dd(reg, count, 1); -success_exit: + vi_delete(reg, pos, 0, 0); check_cursor_bounds(); adjust_screen_buffer(); return 1; } -//TODO i think this thing has bug when removing >40 chars from 80 wide line + static int vi_x(char reg, int count0, int count1) { - int count = count0; - char *s; - char *last; - int *l; - int length = 0; - int width = 0; - int remaining = 0; - char *end; - char *start; - if (!c_r) - return 0; - s = c_r->line->str_data; - l = &c_r->line->str_len; + char *last = 0, *cpos = 0, *start = 0; + int len = 0; + struct linelist *pos = c_r; + int col = TT.cur_col; + if (!c_r) return 0; - last = utf8_last(s,*l); - if (last == s+TT.cur_col) { - memset(last, 0, (*l)-TT.cur_col); - *l = TT.cur_col; - if (!TT.cur_col) return 1; - last = utf8_last(s, TT.cur_col); - TT.cur_col = last-s; - return 1; - } + start = c_r->line->str_data; + len = c_r->line->str_len; - start = s+TT.cur_col; - end = start; - remaining = (*l)-TT.cur_col; - for (;remaining;) { - int next = utf8_lnw(&width, end, remaining); - if (next && width) { - if (!count) break; - count--; - } if (!next) break; - length += next; - end += next; - remaining -= next; + last = utf8_last(start, len); + cpos = start+TT.cur_col; + if (cpos == last) { + cur_left(count0-1, 1, 0); + col = strlen(start); } - if (remaining) { - memmove(start, end, remaining); - memset(start+remaining,0,end-start); - } else { - memset(start,0,(*l)-TT.cur_col); - } - *l -= end-start; - if (!TT.cur_col) return 1; - if (TT.cur_col == (*l)) { - last = utf8_last(s, TT.cur_col); - TT.cur_col = last-s; + else { + cur_right(count0-1, 1, 0); + cpos = start+TT.cur_col; + if (cpos == last) TT.vi_mov_flag |= 2; + else cur_right(1, 1, 0); } + + vi_delete(reg, pos, col, 0); + check_cursor_bounds(); return 1; } @@ -380,6 +317,7 @@ exit_function: count--; if (count>1) return vi_movb(count, 1, 0); + TT.vi_mov_flag |= 0x80000000; check_cursor_bounds(); adjust_screen_buffer(); return 1; @@ -404,28 +342,29 @@ static int vi_move(int count0, int count1, char *unused) return 1; } -void i_insert() + +static void i_insert(char* str, int len) { char *t = xzalloc(c_r->line->alloc_len); char *s = c_r->line->str_data; int sel = c_r->line->str_len-TT.cur_col; strncpy(t, &s[TT.cur_col], sel); t[sel+1] = 0; - if (c_r->line->alloc_len < c_r->line->str_len+il->str_len+5) { + if (c_r->line->alloc_len < c_r->line->str_len+len+5) { c_r->line->str_data = xrealloc(c_r->line->str_data, - c_r->line->alloc_len*2+il->alloc_len*2); + (c_r->line->alloc_len+len)<<1); - c_r->line->alloc_len = c_r->line->alloc_len*2+2*il->alloc_len; + c_r->line->alloc_len = (c_r->line->alloc_len+len)<<1; memset(&c_r->line->str_data[c_r->line->str_len], 0, c_r->line->alloc_len-c_r->line->str_len); s = c_r->line->str_data; } - strcpy(&s[TT.cur_col], il->str_data); - strcpy(&s[TT.cur_col+il->str_len], t); - TT.cur_col += il->str_len; + strncpy(&s[TT.cur_col], str, len); + strcpy(&s[TT.cur_col+len], t); + TT.cur_col += len; if (TT.cur_col) TT.cur_col--; - c_r->line->str_len += il->str_len; + c_r->line->str_len += len; free(t); } @@ -452,9 +391,11 @@ void i_split() adjust_screen_buffer(); } + static int vi_zero(int count0, int count1, char *unused) { TT.cur_col = 0; + TT.vi_mov_flag |= 0x80000000; return 1; } @@ -471,6 +412,37 @@ static int vi_eol(int count0, int count1, char *unused) return 1; } +//TODO check register where to push from +static int vi_push(char reg, int count0, int count1) +{ + char *start = yank.data; + char *end = yank.data+strlen(yank.data); + //insert into new lines + if (*(end-1) == '\n') for (;start != end;) { + char *next = strchr(start, '\n'); + vi_eol(1, 1, 0); + i_split(); + if (next) { + i_insert(start, next-start); + start = next+1; + } else start = end; //?? + } + + //insert into cursor + else for (;start != end;) { + char *next = strchr(start, '\n'); + if (next) { + i_insert(start, next-start); + i_split(); + start = next+1; + } else { + i_insert(start, strlen(start)); + start = end; + } + } + return 1; +} + static int vi_find_c(int count0, int count1, char *symbol) { int count = count0*count1; @@ -495,31 +467,83 @@ static int vi_find_cb(int count0, int count1, char *symbol) //if count is not spesified should go to last line static int vi_go(int count0, int count1, char *symbol) { + int prev_row = TT.cur_row; c_r = text; - while(--count0) { - if (c_r && c_r->down) c_r = c_r->down; - } + + if (TT.vi_mov_flag&0x40000000) for (;c_r && c_r->down; c_r = c_r->down); + else for (;c_r && c_r->down && --count0; c_r = c_r->down); + TT.cur_col = 0; - check_cursor_bounds(); - adjust_screen_buffer(); + check_cursor_bounds(); //adjusts cursor column + adjust_screen_buffer(); //adjusts screen buffer + if (prev_row>TT.cur_row) TT.vi_mov_flag |= 0x80000000; + return 1; } //need to refactor when implementing yank buffers static int vi_delete(char reg, struct linelist *row, int col, int flags) { - if (row == c_r) { - if (col < TT.cur_col) { - int distance = TT.cur_col - col; - TT.cur_col = col; - vi_x(reg, distance, 1); - } else { - int distance = col - TT.cur_col; - if (distance > 0) vi_x(reg, distance, 1); + struct linelist *start = 0, *end = 0; + int col_s = 0, col_e = 0, bytes = 0; + + vi_yank(reg, row, col, flags); + + if (TT.vi_mov_flag&0x80000000) { + start = c_r, end = row; + col_s = TT.cur_col, col_e = col; + } else { + start = row, end = c_r; + col_s = col, col_e = TT.cur_col; + } + if (start == end) goto last_line_delete; + if (!col_s) goto full_line_delete; + + memset(start->line->str_data+col_s, 0, start->line->str_len-col_s); + row->line->str_len = col_s; + col_s = 0; + start = start->down; + +full_line_delete: + for (;start != end;) { + struct linelist* lst = start; + //struct linelist *lst = dlist_pop(&start); + start = start->down; + if (lst->down) lst->down->up = lst->up; + if (lst->up) lst->up->down = lst->down; + if (scr_r == lst) scr_r = lst->down ? lst->down : lst->up; + if (text == lst) text = lst->down; + free(lst->line->str_data); + free(lst->line); + free(lst); + } +last_line_delete: + if (TT.vi_mov_flag&2) col_e = start->line->str_len; + if (TT.vi_mov_flag&4) { + if (!end->down && !end->up) + col_e = start->line->str_len; + else { + col_e = 0, col_s = 0; + if (end->down) end->down->up = end->up; + if (end->up) end->up->down = end->down; + if (scr_r == end) scr_r = end->down ? end->down : end->up; + //if (text == end) text = end->down; + start = end->down ? end->down : end->up; + free(end->line->str_data); + free(end->line); + free(end); + } - if (TT.vi_mov_flag&2) - vi_x(reg, 1, 1); } + if (col_s < col_e) { + bytes = col_s + start->line->str_len - col_e; + memmove(start->line->str_data+col_s, start->line->str_data+col_e, + start->line->str_len-col_e); + memset(start->line->str_data+bytes, 0, start->line->str_len-bytes); + start->line->str_len = bytes; + } + c_r = start; + TT.cur_col = col_s; return 1; } @@ -535,6 +559,7 @@ static int vi_D(char reg, int count0, int count1) c_r = c_r->down; vi_dd(reg, count0, 1); } + check_cursor_bounds(); return 1; } @@ -588,8 +613,53 @@ static int vi_change(char reg, struct linelist *row, int col, int flags) return 1; } +//TODO search yank buffer by register +//now only supports default register static int vi_yank(char reg, struct linelist *row, int col, int flags) { + struct linelist *start = 0, *end = 0; + int col_s = 0, col_e = 0, bytes = 0; + + memset(yank.data, 0, yank.alloc); + if (TT.vi_mov_flag&0x80000000) { + start = c_r, end = row; + col_s = TT.cur_col, col_e = col; + } else { + start = row, end = c_r; + col_s = col, col_e = TT.cur_col; + } + if (start == end) goto last_line_yank; + if (!col_s) goto full_line_yank; + + if (yank.alloc < start->line->alloc_len) { + yank.data = xrealloc(yank.data, start->line->alloc_len*2); + yank.alloc = start->line->alloc_len*2; + } + + sprintf(yank.data, "%s\n", start->line->str_data+col_s); + col_s = 0; + start = start->down; + +full_line_yank: + for (;start != end;) { + while (yank.alloc-1 < strlen(yank.data)+start->line->str_len) + yank.data = xrealloc(yank.data, yank.alloc*2), yank.alloc *= 2; + + + sprintf(yank.data+strlen(yank.data), "%s\n", start->line->str_data); + start = start->down; + } +last_line_yank: + while (yank.alloc-1 < strlen(yank.data)+end->line->str_len) + yank.data = xrealloc(yank.data, yank.alloc*2), yank.alloc *= 2; + + if (TT.vi_mov_flag & 0x4) + sprintf(yank.data+strlen(yank.data), "%s\n", start->line->str_data); + else { + bytes = strlen(yank.data)+col_e-col_s; + strncpy(yank.data+strlen(yank.data), end->line->str_data+col_s, col_e-col_s); + yank.data[bytes] = 0; + } return 1; } @@ -631,6 +701,7 @@ struct vi_special_param vi_special[] = {"J", &vi_join}, {"n", &vi_find_next}, {"x", &vi_x}, + {"p", &vi_push} }; //there is around ~47 vi moves //some of them need extra params @@ -664,22 +735,22 @@ struct vi_cmd_param vi_cmds[] = int run_vi_cmd(char *cmd) { - int i = 0; - int val = 0; + int i = 0, val = 0; char *cmd_e; int (*vi_cmd)(char, struct linelist*, int, int) = 0; int (*vi_mov)(int, int, char*) = 0; - TT.count0 = 0; - TT.count1 = 0; + + TT.count0 = 0, TT.count1 = 0, TT.vi_mov_flag = 0; TT.vi_reg = '"'; - TT.vi_mov_flag = 0; + if (*cmd == '"') { cmd++; TT.vi_reg = *cmd; //TODO check validity cmd++; } + errno = 0; val = strtol(cmd, &cmd_e, 10); - if (errno || val == 0) val = 1; + if (errno || val == 0) val = 1, TT.vi_mov_flag |= 0x40000000; else cmd = cmd_e; TT.count0 = val; @@ -696,6 +767,7 @@ int run_vi_cmd(char *cmd) break; } } + errno = 0; val = strtol(cmd, &cmd_e, 10); if (errno || val == 0) val = 1; else cmd = cmd_e; @@ -704,7 +776,7 @@ int run_vi_cmd(char *cmd) for (i = 0; i < ARRAY_LEN(vi_movs); i++) { if (!strncmp(cmd, vi_movs[i].mov, strlen(vi_movs[i].mov))) { vi_mov = vi_movs[i].vi_mov; - TT.vi_mov_flag = vi_movs[i].flags; + TT.vi_mov_flag |= vi_movs[i].flags; cmd++; if (TT.vi_mov_flag&1 && !(*cmd)) return 0; break; @@ -800,6 +872,8 @@ void vi_main(void) TT.screen_height = 24; TT.vi_mode = 1; TT.tabstop = 8; + yank.data = xzalloc(128); + yank.alloc = 128; terminal_size(&TT.screen_width, &TT.screen_height); TT.screen_height -= 2; //TODO this is hack fix visual alignment set_terminal(0, 1, 0, 0); @@ -905,7 +979,7 @@ void vi_main(void) } else if (TT.vi_mode == 2) {//INSERT MODE switch (key) { case 27: - i_insert(); + i_insert(il->str_data, il->str_len); TT.vi_mode = 1; il->str_len = 0; memset(il->str_data, 0, il->alloc_len); @@ -922,7 +996,7 @@ void vi_main(void) case 0x0D: //insert newline // - i_insert(); + i_insert(il->str_data, il->str_len); il->str_len = 0; memset(il->str_data, 0, il->alloc_len); i_split(); @@ -1006,22 +1080,18 @@ int crunch_nstr(char **str, int width, int n, FILE *out, char *escmore, static void draw_page() { + struct linelist *scr_buf = scr_r; unsigned y = 0; - int cy_scr = 0; - int cx_scr = 0; - int utf_l = 0; - - char* line = 0; - int bytes = 0; int x = 0; - struct linelist *scr_buf= scr_r; + + char *line = 0, *end = 0; + int utf_l = 0, bytes = 0; + + //screen coordinates for cursor + int cy_scr = 0, cx_scr = 0; //variables used only for cursor handling - int aw = 0; - int iw = 0; - int clip = 0; - char *end = 0; - int margin = 8; + int aw = 0, iw = 0, clip = 0, margin = 8; //clear screen tty_esc("2J"); @@ -1158,42 +1228,40 @@ static void draw_page() static void check_cursor_bounds() { - if (c_r->line->str_len == 0) TT.cur_col = 0; - else if (c_r->line->str_len-1 < TT.cur_col) TT.cur_col = c_r->line->str_len-1; + if (c_r->line->str_len == 0) { + TT.cur_col = 0; + return; + } else if (c_r->line->str_len-1 < TT.cur_col) TT.cur_col = c_r->line->str_len-1; + if (utf8_width(&c_r->line->str_data[TT.cur_col], c_r->line->str_len-TT.cur_col) <= 0) - cur_left(1, 1, 0); + TT.cur_col--, check_cursor_bounds(); } static void adjust_screen_buffer() { - //search cursor and screen TODO move this perhaps + //search cursor and screen struct linelist *t = text; - int c = -1; - int s = -1; - int i = 0; + int c = -1, s = -1, i = 0; + //searching cursor and screen line numbers for (;;) { i++; - if (t == c_r) - c = i; - if (t == scr_r) - s = i; + if (t == c_r) c = i; + if (t == scr_r) s = i; + t = t->down; if ( ((c != -1) && (s != -1)) || t == 0) break; } - if (c <= s) { - scr_r = c_r; - } - else if ( c > s ) { - //should count multiline long strings! + //adjust screen buffer so cursor is on drawing area + if (c <= s) scr_r = c_r; //scroll up + else if (c > s) { + //drawing does not have wrapping so no need to check width int distance = c - s + 1; - //TODO instead iterate scr_r up and check strlen%screen_width - //for each iteration + if (distance >= (int)TT.screen_height) { int adj = distance - TT.screen_height; - while (adj--) { - scr_r = scr_r->down; - } + while (adj--) scr_r = scr_r->down; //scroll down + } } TT.cur_row = c; @@ -1306,11 +1374,12 @@ static char* utf8_last(char* str, int size) static int cur_left(int count0, int count1, char* unused) { int count = count0*count1; + TT.vi_mov_flag |= 0x80000000; for (;count--;) { if (!TT.cur_col) return 1; TT.cur_col--; - check_cursor_bounds();//has bit ugly recursion hidden here + check_cursor_bounds(); } return 1; } @@ -1339,6 +1408,7 @@ static int cur_up(int count0, int count1, char* unused) for (;count-- && c_r->up;) c_r = c_r->up; + TT.vi_mov_flag |= 0x80000000; check_cursor_bounds(); adjust_screen_buffer(); return 1; |