aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--toys/pending/vi.c472
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;