Replaced dlist linelist with continuous memory blocks. This will allow editing huge files without billion mallocs. File is first opened with mmap() and mapped region is fully described in block_list as one block.
Currently "valid" data is described as slices, when first loading file there is only one slice that points to memory existing in block_list. When cutting text, block_list is not freed or modified, but instead slice_list is modified to have "hole" between 2 slices. when inserting new mem_block is added, previos slices are cut in cursor position and new slice is added... Implementation is not as finished as old linelist, but most of the heavy lifting is done so wanted to share this at this state. Added functions to handling data inside block_list+slice_list insert_str(), cut_str() are used for all delete and add operations text_strrchr(), text_strchr() are used for searching lineendings text_byte(), text_codepoint(), text_getline() are for simple data access Implemented: yank,delete,insert,push and most of the previos moves Implemented partly: saving of file, since current file is still mmaped cant save on top of it, so now saves into "filename".swp, later need to move .swp into original file after munmap Not Implemented: search, linenumber counting, etc.. Removed: Some unused functions br -Jarno --- toys/pending/vi.c | 1237 +++++++++++++++++++++++++-------------------- 1 file changed, 692 insertions(+), 545 deletions(-)
From ecc4485700c4db93820513d94e632910543b24d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jarno=20M=C3=A4kip=C3=A4=C3=A4?= <[email protected]> Date: Sat, 11 Jan 2020 00:36:02 +0800 Subject: [PATCH] vi: Replace linelist with mem_block based design Replaced dlist linelist with continuous memory blocks. This will allow editing huge files without billion mallocs. File is first opened with mmap() and mapped region is fully described in block_list as one block. Currently "valid" data is described as slices, when first loading file there is only one slice that points to memory existing in block_list. When cutting text, block_list is not freed or modified, but instead slice_list is modified to have "hole" between 2 slices. when inserting new mem_block is added, previos slices are cut in cursor position and new slice is added... Implementation is not as finished as old linelist, but most of the heavy lifting is done so wanted to share this at this state. Added functions to handling data inside block_list+slice_list insert_str(), cut_str() are used for all delete and add operations text_strrchr(), text_strchr() are used for searching lineendings text_byte(), text_codepoint(), text_getline() are for simple data access Implemented: yank,delete,insert,push and most of the previos moves Implemented partly: saving of file, since current file is still mmaped cant save on top of it, so now saves into "filename".swp, later need to move .swp into original file after munmap Not Implemented: search, linenumber counting, etc.. Removed: Some unused functions --- toys/pending/vi.c | 1237 +++++++++++++++++++++++++-------------------- 1 file changed, 692 insertions(+), 545 deletions(-) diff --git a/toys/pending/vi.c b/toys/pending/vi.c index 9c47a4c5..07413c9b 100644 --- a/toys/pending/vi.c +++ b/toys/pending/vi.c @@ -41,20 +41,64 @@ GLOBALS( int len; char *data; } *il; - struct linelist { - struct linelist *up;//next - struct linelist *down;//prev - struct str_line *line; - } *text, *screen, *c_r; + 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; + +// mem_block contains RO data that is either original file as mmap +// or heap allocated inserted data +// +// +// + struct block_list { + struct block_list *next, *prev; + struct mem_block { + size_t size; + size_t len; + enum alloc_flag { + MMAP, //can be munmap() before exit() + HEAP, //can be free() before exit() + STACK, //global or stack perhaps toybuf + } alloc; + const char *data; + } *node; + } *text; + +// slices do not contain actual allocated data but slices of data in mem_block +// when file is first opened it has only one slice. +// after inserting data into middle new mem_block is allocated for insert data +// and 3 slices are created, where first and last slice are pointing to original +// mem_block with offsets, and middle slice is pointing to newly allocated block +// When deleting, data is not freed but mem_blocks are sliced more such way that +// deleted data left between 2 slices + struct slice_list { + struct slice_list *next, *prev; + struct slice { + size_t len; + const char *data; + } *node; + } *slices; + + size_t filesize; + ) +// 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 static void draw_page(); @@ -62,7 +106,6 @@ 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); -static int utf8_width(char *str, int bytes); static char* utf8_last(char* str, int size); @@ -74,221 +117,464 @@ static void check_cursor_bounds(); static void adjust_screen_buffer(); static int search_str(char *s); -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); +//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); +struct double_list *dlist_add_before(struct double_list **head, + struct double_list **list, char *data) +{ + struct double_list *new = xmalloc(sizeof(struct double_list)); + new->data = data; + if (*list == *head) *head = new; -// 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 + dlist_add_nomalloc(list, new); + return new; +} -void dlist_insert_nomalloc(struct double_list **list, struct double_list *new) +struct double_list *dlist_add_after(struct double_list **head, + struct double_list **list, char *data) { + struct double_list *new = xmalloc(sizeof(struct double_list)); + new->data = data; + if (*list) { - new->next = *list; - new->prev = (*list)->prev; - if ((*list)->prev) (*list)->prev->next = new; - (*list)->prev = new; - } else *list = new->next = new->prev = new; + new->prev = *list; + new->next = (*list)->next; + (*list)->next->prev = new; + (*list)->next = new; + } else *head = *list = new->next = new->prev = new; + return new; } +// str must be already allocated +// ownership of allocated data is moved +// data, pre allocated data +// offset, offset in whole text +// size, data allocation size of given data +// len, length of the string +// type, define allocation type for cleanup purposes at app exit +int insert_str(const char *data, size_t offset, size_t size, size_t len, + enum alloc_flag type) +{ + struct mem_block *b = xmalloc(sizeof(struct mem_block)); + struct slice *next = xmalloc(sizeof(struct slice)); + struct slice_list *s = TT.slices; + b->size = size; + b->len = len; + b->alloc = type; + b->data = data; + next->len = len; + next->data = data; + + //mem blocks can be just added unordered + TT.text = (struct block_list *)dlist_add((struct double_list **)&TT.text, + (char *)b); + + if (!s) { + TT.slices = (struct slice_list *)dlist_add( + (struct double_list **)&TT.slices, + (char *)next); + } else { + size_t pos = 0; + //search insertation point for slice + do { + if (pos<=offset && pos+s->node->len>offset) break; + pos += s->node->len; + s = s->next; + if (s == TT.slices) return -1; //error out of bounds + } while (1); + //need to cut previous slice into 2 since insert is in middle + if (pos+s->node->len>offset && pos!=offset) { + struct slice *tail = xmalloc(sizeof(struct slice)); + tail->len = s->node->len-(offset-pos); + tail->data = s->node->data+(offset-pos); + s->node->len = offset-pos; + //pos = offset; + s = (struct slice_list *)dlist_add_after( + (struct double_list **)&TT.slices, + (struct double_list **)&s, + (char *)tail); + + s = (struct slice_list *)dlist_add_before( + (struct double_list **)&TT.slices, + (struct double_list **)&s, + (char *)next); + } else if (pos==offset) { + // insert before + s = (struct slice_list *)dlist_add_before( + (struct double_list **)&TT.slices, + (struct double_list **)&s, + (char *)next); + } else { + // insert after + s = (struct slice_list *)dlist_add_after((struct double_list **)&TT.slices, + (struct double_list **)&s, + (char *)next); + } + } + return 0; +} -// Add an entry to the end of a doubly linked list -struct double_list *dlist_insert(struct double_list **list, char *data) +// this will not free any memory +// will only create more slices depending on position +int cut_str(size_t offset, size_t len) { - struct double_list *new = xmalloc(sizeof(struct double_list)); - new->data = data; - dlist_insert_nomalloc(list, new); + struct slice_list *e, *s = TT.slices; + size_t end = offset+len; + size_t epos, spos = 0; + if (!s) return -1; - return new; + //find start and end slices + for (;;) { + if (spos<=offset && spos+s->node->len>offset) break; + spos += s->node->len; + s = s->next; + + if (s == TT.slices) return -1; //error out of bounds + } + + for (e = s, epos = spos; ; ) { + if (epos<=end && epos+e->node->len>end) break; + epos += e->node->len; + e = e->next; + + if (e == TT.slices) return -1; //error out of bounds + } + + for (;;) { + if (spos == offset && ( end >= spos+s->node->len)) { + //cut full + 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)) { + //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) { + //cut begin + size_t clip = end - offset; + s->node->len -= clip; + s->node->data += clip; + break; + } + + else { + //cut middle + struct slice *tail = xmalloc(sizeof(struct slice)); + size_t clip = end-offset; + tail->len = s->node->len-(offset-spos)-clip; + tail->data = s->node->data+(offset-spos)+clip; + s->node->len = offset-spos; //wrong? + s = (struct slice_list *)dlist_add_after( + (struct double_list **)&TT.slices, + (struct double_list **)&s, + (char *)tail); + break; + } + if (s == e) break; + + s = s->next; + } + + return 0; } -void linelist_free(void *node) +size_t text_strchr(size_t offset, char c) { - struct linelist *lst = (struct linelist *)node; - free(lst->line->data), free(lst->line), free(lst); + struct slice_list *s = TT.slices; + size_t epos, spos = 0; + int i = 0; + + if (!s) return SIZE_MAX; + + //find start + for (;;) { + if (spos<=offset && spos+s->node->len>offset) break; + + spos += s->node->len; + s = s->next; + + if (s == TT.slices) return SIZE_MAX; //error out of bounds + } + + i = offset-spos; + epos = spos+i; + do { + for (; i < s->node->len; i++, epos++) + if (s->node->data[i] == c) return epos; + s = s->next; + i = 0; + } while(s != TT.slices); + + return SIZE_MAX; + +} + +size_t text_strrchr(size_t offset, char c) +{ + struct slice_list *s = TT.slices; + size_t epos, spos = 0; + int i = 0; + + if (!s) return SIZE_MAX; + + //find start + for (;;) { + if (spos<=offset && spos+s->node->len>offset) break; + + spos += s->node->len; + s = s->next; + + if (s == TT.slices) return SIZE_MAX; //error out of bounds + } + + i = offset-spos; + epos = spos+i; + do { + for (; i >= 0; i--, epos--) + if (s->node->data[i] == c) return epos; + s = s->prev; + i = s->node->len-1; + } while(s != TT.slices->prev); //tail + + return SIZE_MAX; + +} + +size_t text_filesize() +{ + struct slice_list *s = TT.slices; + size_t pos = 0; + if (s) do { + + pos += s->node->len; + s = s->next; + + } while (s != TT.slices); + + return pos; +} + +char text_byte(size_t offset) +{ + struct slice_list *s = TT.slices; + size_t spos = 0; + //find start + for (;;) { + if (spos<=offset && spos+s->node->len>offset) break; + + spos += s->node->len; + s = s->next; + + if (s == TT.slices) return 0; //error out of bounds + } + return s->node->data[offset-spos]; } +//utf-8 codepoint -1 if not valid, 0 if out_of_bounds, len if valid +//copies data to dest if dest is not 0 +int text_codepoint(char *dest, size_t offset) +{ + char scratch[8] = {0}; + int state = 0, finished = 0; + + for (;!(finished = utf8_dec(text_byte(offset), scratch, &state)); offset++) + if (!state) return -1; + + if (!finished && !state) return -1; + if (dest) memcpy(dest,scratch,8); + + return state; +} + +size_t text_getline(char *dest, size_t offset, size_t max_len) +{ + struct slice_list *s = TT.slices; + size_t end, spos = 0; + int i, j = 0; + + if (dest) *dest = 0; + + if (!s) return 0; + if ((end = text_strchr(offset, '\n')) == SIZE_MAX) + if ((end = TT.filesize) > offset+max_len) return 0; + + //find start + for (;;) { + if (spos<=offset && spos+s->node->len>offset) break; + + spos += s->node->len; + s = s->next; + + if (s == TT.slices) return 0; //error out of bounds + } + + i = offset-spos; + j = end-offset+1; + if (dest) do { + for (; i < s->node->len && j; i++, j--, dest++) + *dest = s->node->data[i]; + s = s->next; + i = 0; + } while(s != TT.slices && j); + + if (dest) *dest = 0; + + return end - offset; +} + + void linelist_unload() { - void* list = 0; - for (;TT.text->down; TT.text = TT.text->down); - list = (void*)TT.text; - TT.text = TT.screen = TT.c_r = 0; - llist_traverse(list, linelist_free); +// void* list = 0; +// for (;TT.text->down; TT.text = TT.text->down); +// list = (void*)TT.text; +// TT.text = TT.screen = TT.c_r = 0; +// llist_traverse(list, linelist_free); } +//TODO copy into original file void write_file(char *filename) { - struct linelist *lst = TT.text; + struct slice_list *s = TT.slices; FILE *fp = 0; + if (!s) return; + if (!filename) filename = (char*)*toys.optargs; - if (!(fp = fopen(filename, "w")) ) return; - for (;lst; lst = lst->down) - fprintf(fp, "%s\n", lst->line->data); + sprintf(toybuf, "%s.swp", filename); + + if (!(fp = fopen(toybuf, "w")) ) return; + + do { + fwrite(s->node->data, s->node->len, 1, fp); + s = s->next; + } while(s != TT.slices); fclose(fp); } int linelist_load(char *filename) { - struct linelist *lst = TT.c_r;//cursor position or 0 - FILE *fp = 0; - if (!filename) - filename = (char*)*toys.optargs; - - fp = fopen(filename, "r"); - if (!fp) { - char *line = xzalloc(80); - ssize_t alc = 80; - lst = (struct linelist*)dlist_add((struct double_list**)&lst, - xzalloc(sizeof(struct str_line))); - lst->line->alloc = alc; - lst->line->len = 0; - lst->line->data = line; - TT.text = lst; - dlist_terminate(TT.text->up); - return 1; - } + if (!filename) filename = (char*)*toys.optargs; - for (;;) { - char *line = xzalloc(80); - ssize_t alc = 80; - ssize_t len; - if ((len = getline(&line, (void *)&alc, fp)) == -1) { - if (errno == EINVAL || errno == ENOMEM) { - printf("error %d\n", errno); - } - free(line); - break; - } - lst = (struct linelist*)dlist_add((struct double_list**)&lst, - xzalloc(sizeof(struct str_line))); - lst->line->alloc = alc; - lst->line->len = len; - lst->line->data = line; - - if (lst->line->data[len-1] == '\n') { - lst->line->data[len-1] = 0; - lst->line->len--; - } - if (TT.text == 0) TT.text = lst; + if (filename) { + int fd; + struct stat sb; + size_t len; + char *data; + if ( (fd = open(filename, O_RDONLY)) <0) return 0; + if (fstat(fd, &sb) < 0) return 0; + + len = sb.st_size; + data = mmap(NULL, len, PROT_READ, MAP_SHARED, fd, 0); + if (data == MAP_FAILED) return 0; + insert_str(data, 0, len, len, MMAP); + TT.filesize = text_filesize(); } - if (TT.text) dlist_terminate(TT.text->up); - - fclose(fp); return 1; - } +//TODO int vi_yy(char reg, int count0, int count1) { - struct linelist *pos = TT.c_r; - int col = TT.cur_col; - TT.cur_col = 0; + size_t history = TT.cursor; + size_t pos = 0; TT.vi_mov_flag |= 0x4; - if (count0>1) cur_down(count0-1, 1, 0); + //go left to first char on line + if ((pos = text_strrchr(TT.cursor, '\n')) == SIZE_MAX ) pos = 0; - vi_yank(reg, pos, 0, 0); + for (;count0; count0--) { + if ((TT.cursor = text_strchr(TT.cursor+1, '\n')) == SIZE_MAX) { + TT.cursor = TT.filesize; + break; + } + } + + vi_yank(reg, pos, 0); - TT.cur_col = col, TT.c_r = pos; + TT.cursor = history; return 1; } int vi_dd(char reg, int count0, int count1) { - struct linelist *pos = TT.c_r; - TT.cur_col = 0; + size_t pos = 0; TT.vi_mov_flag |= 0x4; - if (count0>1) cur_down(count0-1, 1, 0); - vi_delete(reg, pos, 0, 0); + //go left to first char on line + if ((pos = text_strrchr(TT.cursor, '\n')) == SIZE_MAX) pos = 0; + + for (;count0; count0--) { + if ((TT.cursor = text_strchr(TT.cursor+1, '\n')) == SIZE_MAX) { + TT.cursor = TT.filesize; + break; + } + } + + vi_delete(reg, pos, 0); check_cursor_bounds(); return 1; } static int vi_x(char reg, int count0, int count1) { - char *last = 0, *cpos = 0, *start = 0; - int len = 0; - struct linelist *pos = TT.c_r; - int col = TT.cur_col; - if (!TT.c_r) return 0; - - start = TT.c_r->line->data; - len = TT.c_r->line->len; + size_t from = TT.cursor; - last = utf8_last(start, len); - cpos = start+TT.cur_col; - if (cpos == last) { + if (text_byte(TT.cursor) == '\n') { cur_left(count0-1, 1, 0); - col = strlen(start); } else { cur_right(count0-1, 1, 0); - cpos = start+TT.cur_col; - if (cpos == last) TT.vi_mov_flag |= 2; + if (text_byte(TT.cursor) == '\n') TT.vi_mov_flag |= 2; else cur_right(1, 1, 0); } - vi_delete(reg, pos, col, 0); + vi_delete(reg, from, 0); check_cursor_bounds(); return 1; } -//move commands does not behave correct way yet. +//TODO check EOF int vi_movw(int count0, int count1, char* unused) { int count = count0*count1; const char *empties = " \t\n\r"; const char *specials = ",.=-+*/(){}<>[]"; -// char *current = 0; - if (!TT.c_r) - return 0; - if (TT.cur_col == TT.c_r->line->len-1 || !TT.c_r->line->len) - goto next_line; - if (strchr(empties, TT.c_r->line->data[TT.cur_col])) - goto find_non_empty; - if (strchr(specials, TT.c_r->line->data[TT.cur_col])) { - for (;strchr(specials, TT.c_r->line->data[TT.cur_col]); ) { - TT.cur_col++; - if (TT.cur_col == TT.c_r->line->len-1) - goto next_line; - } - } else for (;!strchr(specials, TT.c_r->line->data[TT.cur_col]) && - !strchr(empties, TT.c_r->line->data[TT.cur_col]);) { - TT.cur_col++; - if (TT.cur_col == TT.c_r->line->len-1) - goto next_line; - } - - for (;strchr(empties, TT.c_r->line->data[TT.cur_col]); ) { - TT.cur_col++; -find_non_empty: - if (TT.cur_col == TT.c_r->line->len-1) { -next_line: - //we could call j and g0 - if (!TT.c_r->down) return 0; - TT.c_r = TT.c_r->down; - TT.cur_col = 0; - if (!TT.c_r->line->len) break; - } + while (count--) { + char c = text_byte(TT.cursor); + do { + //if at empty jump to non empty + if (strchr(empties, c)) do { + TT.cursor++; + c = text_byte(TT.cursor); + } while(strchr(empties, c)); + //if at special jump to non special + else if (strchr(specials, c)) do { + TT.cursor++; + c = text_byte(TT.cursor); + } while(strchr(specials, c)); + //else jump to empty or spesial + else do { + TT.cursor++; + c = text_byte(TT.cursor); + } while(!strchr(empties, c) && !strchr(specials, c)); + + } while(strchr(empties, c)); //never stop at empty } - count--; - if (count>0) - return vi_movw(count, 1, 0); - check_cursor_bounds(); return 1; } @@ -296,47 +582,44 @@ next_line: static int vi_movb(int count0, int count1, char* unused) { int count = count0*count1; - if (!TT.c_r) - return 0; - if (!TT.cur_col) { - if (!TT.c_r->up) return 0; - TT.c_r = TT.c_r->up; - TT.cur_col = (TT.c_r->line->len) ? TT.c_r->line->len-1 : 0; - goto exit_function; - } - if (TT.cur_col) - TT.cur_col--; - while (TT.c_r->line->data[TT.cur_col] <= ' ') { - if (TT.cur_col) TT.cur_col--; - else goto exit_function; - } - while (TT.c_r->line->data[TT.cur_col] > ' ') { - if (TT.cur_col)TT.cur_col--; - else goto exit_function; + const char *empties = " \t\n\r"; + const char *specials = ",.=-+*/(){}<>[]"; + while (count--) { + char c = text_byte(TT.cursor); + do { + //if at empty jump to non empty + if (strchr(empties, c)) do { + TT.cursor--; + c = text_byte(TT.cursor); + } while(strchr(empties, c)); + //if at special jump to non special + else if (strchr(specials, c)) do { + TT.cursor--; + c = text_byte(TT.cursor); + } while(strchr(specials, c)); + //else jump to empty or spesial + else do { + TT.cursor--; + c = text_byte(TT.cursor); + } while(!strchr(empties, c) && !strchr(specials, c)); + + } while(strchr(empties, c)); //never stop at empty } - TT.cur_col++; -exit_function: - count--; - if (count>1) - return vi_movb(count, 1, 0); TT.vi_mov_flag |= 0x80000000; check_cursor_bounds(); return 1; } +//TODO static int vi_move(int count0, int count1, char *unused) { int count = count0*count1; - if (!TT.c_r) - return 0; - if (TT.cur_col < TT.c_r->line->len) - TT.cur_col++; - if (TT.c_r->line->data[TT.cur_col] <= ' ' || count > 1) - vi_movw(count, 1, 0); //find next word; - while (TT.c_r->line->data[TT.cur_col] > ' ') - TT.cur_col++; - if (TT.cur_col) TT.cur_col--; + while (count--) { +//TODO if (count>1 || slice_char(TT.cursor) <= ' ') +//TODO vi_movw(count, 1, 0); //find next word; +//TODO while (slice_char(TT.cursor+1)>' ') TT.cursor++; + } TT.vi_mov_flag |= 2; check_cursor_bounds(); return 1; @@ -345,65 +628,18 @@ static int vi_move(int count0, int count1, char *unused) static void i_insert(char* str, int len) { - char *t = xzalloc(TT.c_r->line->alloc); - char *s = TT.c_r->line->data; - int sel = TT.c_r->line->len-TT.cur_col; - strncpy(t, &s[TT.cur_col], sel); - t[sel+1] = 0; - if (TT.c_r->line->alloc < TT.c_r->line->len+len+5) { - TT.c_r->line->data = xrealloc(TT.c_r->line->data, - (TT.c_r->line->alloc+len)<<1); - - TT.c_r->line->alloc = (TT.c_r->line->alloc+len)<<1; - memset(&TT.c_r->line->data[TT.c_r->line->len], 0, - TT.c_r->line->alloc-TT.c_r->line->len); - - s = TT.c_r->line->data; - } - 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--; - - TT.c_r->line->len += len; - free(t); + char *s = xstrdup(str); + insert_str(s, TT.cursor, len, len, HEAP); + TT.cursor += len; + TT.filesize = text_filesize(); TT.vi_mov_flag |= 0x30000000; } -//new line at split pos; -void i_split() -{ - int alloc = 0, len = 0, idx = 0; - struct str_line *l = xmalloc(sizeof(struct str_line)); - alloc = TT.c_r->line->alloc; - - if (TT.cur_col) len = TT.c_r->line->len-TT.cur_col-1; - else len = TT.c_r->line->len; - if (len < 0) len = 0; - - l->data = xzalloc(alloc); - l->alloc = alloc; - l->len = len; - idx = TT.c_r->line->len - len; - - strncpy(l->data, &TT.c_r->line->data[idx], len); - memset(&l->data[len], 0, alloc-len); - - TT.c_r->line->len -= len; - if (TT.c_r->line->len <= 0) TT.c_r->line->len = 0; - - len = TT.c_r->line->len; - - memset(&TT.c_r->line->data[len], 0, alloc-len); - TT.c_r = (struct linelist*)dlist_insert((struct double_list**)&TT.c_r, (char*)l); - TT.c_r->line = l; - TT.cur_col = 0; -} - - static int vi_zero(int count0, int count1, char *unused) { + //backward find /n + TT.cursor = text_strrchr(TT.cursor, '\n')+1; TT.cur_col = 0; TT.vi_mov_flag |= 0x80000000; return 1; @@ -411,12 +647,8 @@ static int vi_zero(int count0, int count1, char *unused) static int vi_eol(int count0, int count1, char *unused) { - int count = count0*count1; - for (;count > 1 && TT.c_r->down; count--) - TT.c_r = TT.c_r->down; - - if (TT.c_r && TT.c_r->line->len) - TT.cur_col = TT.c_r->line->len-1; + //forward find /n + TT.cursor = text_strchr(TT.cursor, '\n'); TT.vi_mov_flag |= 2; check_cursor_bounds(); return 1; @@ -425,53 +657,31 @@ static int vi_eol(int count0, int count1, char *unused) //TODO check register where to push from static int vi_push(char reg, int count0, int count1) { - char *start = TT.yank.data, *end = TT.yank.data+strlen(TT.yank.data); - struct linelist *cursor = TT.c_r; - int col = TT.cur_col; - //insert into new lines - if (*(end-1) == '\n') for (;start != end;) { - TT.vi_mov_flag |= 0x10000000; - char *next = strchr(start, '\n'); - TT.cur_col = (TT.c_r->line->len) ? TT.c_r->line->len-1: 0; - i_split(); - if (next) { - i_insert(start, next-start); - start = next+1; - } else start = end; //?? + size_t history = TT.cursor; + char *start = TT.yank.data; + char *eol = strchr(start, '\n'); + + if (*start == '\n') { + if ((TT.cursor = text_strchr(TT.cursor, '\n')) == SIZE_MAX) + TT.cursor = TT.filesize; + //else start++; } - //insert into cursor - else for (;start != end;) { - char *next = strchr(start, '\n'); - if (next) { - TT.vi_mov_flag |= 0x10000000; - i_insert(start, next-start); - i_split(); - start = next+1; - } else { - i_insert(start, strlen(start)); - start = end; - } + i_insert(start, strlen(start)); + if (eol) { + //if row changes during push original cursor position is kept + //vi inconsistancy + TT.vi_mov_flag |= 0x10000000; + TT.cursor = history; } - //if row changes during push original cursor position is kept - //vi inconsistancy - if (TT.c_r != cursor) TT.c_r = cursor, TT.cur_col = col; return 1; } +//TODO static int vi_find_c(int count0, int count1, char *symbol) { - int count = count0*count1; - if (TT.c_r && TT.c_r->line->len) { - while (count--) { - char* pos = strstr(&TT.c_r->line->data[TT.cur_col], symbol); - if (pos) { - TT.cur_col = pos-TT.c_r->line->data; - return 1; - } - } - } +//// int count = count0*count1; return 0; } @@ -484,107 +694,69 @@ 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; - TT.c_r = TT.text; + size_t prev_cursor = TT.cursor; + int count = count0*count1; - if (TT.vi_mov_flag&0x40000000) for (;TT.c_r && TT.c_r->down; TT.c_r = TT.c_r->down); - else for (;TT.c_r && TT.c_r->down && --count0; TT.c_r = TT.c_r->down); + if (TT.vi_mov_flag&0x40000000 && (TT.cursor = TT.filesize) > 0) + TT.cursor--; + else { + size_t next = 0; + TT.cursor = 0; + for ( ;count && (next = text_strchr(next+1, '\n')) != SIZE_MAX; count--) + TT.cursor = next; + TT.cursor++; + } - TT.cur_col = 0; check_cursor_bounds(); //adjusts cursor column - if (prev_row>TT.cur_row) TT.vi_mov_flag |= 0x80000000; + if (prev_cursor>TT.cursor) TT.vi_mov_flag |= 0x80000000; return 1; } -static int vi_delete(char reg, struct linelist *row, int col, int flags) +static int vi_delete(char reg, size_t from, int flags) { - struct linelist *start = 0, *end = 0; - int col_s = 0, col_e = 0, bytes = 0; + size_t start = from, end = TT.cursor; - vi_yank(reg, row, col, flags); + vi_yank(reg, from, flags); - if (TT.vi_mov_flag&0x80000000) { - start = TT.c_r, end = row; - col_s = TT.cur_col, col_e = col; - } else { - start = row, end = TT.c_r; - col_s = col, col_e = TT.cur_col; - } + 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; - } - } - if (start != end) { //goto last_line_delete; - if (col_s) { - memset(start->line->data+col_s, 0, start->line->len-col_s); - row->line->len = col_s; - col_s = 0; - start = start->down; - } - //full_line_delete: - TT.vi_mov_flag |= 0x10000000; - for (;start != end;) { - struct linelist *lst = start; - start = start->down; - lst = dlist_pop(&lst); - if (TT.screen == lst) TT.screen = start; - if (TT.text == lst) TT.text = start; - free(lst->line->data); - free(lst->line); - free(lst); - } - } -//last_line_delete: - TT.vi_mov_flag |= 0x10000000; - //if (TT.vi_mov_flag&2) col_e = start->line->len; - if (TT.vi_mov_flag&4) { - if (!end->down && !end->up) - col_e = start->line->len; - else { - struct linelist *lst = 0; - col_e = 0, col_s = 0; - if (!start->down) lst = dlist_pop(&start); - else { - lst = start; - start = start->down; - lst = dlist_pop(&lst); - } - if (TT.screen == lst) TT.screen = start; - if (TT.text == lst) TT.text = start; - free(lst->line->data); - free(lst->line); - free(lst); - } + //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; + //} } - if (col_s < col_e) { - bytes = col_s + start->line->len - col_e; - memmove(start->line->data+col_s, start->line->data+col_e, - start->line->len-col_e); - memset(start->line->data+bytes, 0, start->line->len-bytes); - start->line->len = bytes; - } - TT.c_r = start; - TT.cur_col = col_s; + //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? + return 1; } + static int vi_D(char reg, int count0, int count1) { - int prev_col = TT.cur_col; - struct linelist *pos = TT.c_r; + size_t pos = TT.cursor; if (!count0) return 1; vi_eol(1, 1, 0); - vi_delete(reg, pos, prev_col, 0); + vi_delete(reg, pos, 0); count0--; - if (count0 && TT.c_r->down) { - TT.c_r = TT.c_r->down; + if (count0) { vi_dd(reg, count0, 1); } check_cursor_bounds(); @@ -593,37 +765,12 @@ static int vi_D(char reg, int count0, int count1) static int vi_join(char reg, int count0, int count1) { + size_t next; while (count0--) { - if (TT.c_r && TT.c_r->down) { - int size = TT.c_r->line->len+TT.c_r->down->line->len; - if (size > TT.c_r->line->alloc) { - if (size > TT.c_r->down->line->alloc) { - TT.c_r->line->data = xrealloc(TT.c_r->line->data, - TT.c_r->line->alloc*2+TT.il->alloc*2); - memmove(&TT.c_r->line->data[TT.c_r->line->len], - TT.c_r->down->line->data,TT.c_r->down->line->len); - TT.c_r->line->len = size; - TT.c_r = TT.c_r->down; - TT.c_r->line->alloc = TT.c_r->line->alloc*2+2*TT.il->alloc; - vi_dd(0,1,1); - } else { - memmove(&TT.c_r->down->line->data[TT.c_r->line->len], - TT.c_r->down->line->data,TT.c_r->down->line->len); - memmove(TT.c_r->down->line->data,TT.c_r->line->data, - TT.c_r->line->len); - TT.c_r->down->line->len = size; - vi_dd(0,1,1); - } - } else { - memmove(&TT.c_r->line->data[TT.c_r->line->len], - TT.c_r->down->line->data,TT.c_r->down->line->len); - TT.c_r->line->len = size; - TT.c_r = TT.c_r->down; - vi_dd(0,1,1); - } - TT.c_r = TT.c_r->up; - - } + //just strchr(/n) and cut_str(pos, 1); + if ((next = text_strchr(TT.cursor, '\n')) == SIZE_MAX) break; + TT.cursor = next+1; + vi_delete(reg, TT.cursor-1, 0); } return 1; } @@ -634,60 +781,39 @@ static int vi_find_next(char reg, int count0, int count1) return 1; } -static int vi_change(char reg, struct linelist *row, int col, int flags) +static int vi_change(char reg, size_t to, int flags) { - vi_delete(reg, row, col, 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, struct linelist *row, int col, int flags) +static int vi_yank(char reg, size_t from, int flags) { - struct linelist *start = 0, *end = 0; - int col_s = 0, col_e = 0, bytes = 0; + 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.c_r, end = row; - col_s = TT.cur_col, col_e = col; - } else { - start = row, end = TT.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 (TT.vi_mov_flag&0x80000000) + start = TT.cursor, end = from; - if (TT.yank.alloc < start->line->alloc) { - TT.yank.data = xrealloc(TT.yank.data, start->line->alloc*2); - TT.yank.alloc = start->line->alloc*2; - } - - sprintf(TT.yank.data, "%s\n", start->line->data+col_s); - col_s = 0; - start = start->down; -full_line_yank: - for (;start != end;) { - while (TT.yank.alloc-1 < strlen(TT.yank.data)+start->line->len) - TT.yank.data = xrealloc(TT.yank.data, TT.yank.alloc*2), TT.yank.alloc *= 2; + 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); - sprintf(TT.yank.data+strlen(TT.yank.data), "%s\n", start->line->data); - start = start->down; - } -last_line_yank: - while (TT.yank.alloc-1 < strlen(TT.yank.data)+end->line->len) - TT.yank.data = xrealloc(TT.yank.data, TT.yank.alloc*2), TT.yank.alloc *= 2; + *str = 0; - if (TT.vi_mov_flag & 0x4) - sprintf(TT.yank.data+strlen(TT.yank.data), "%s\n", start->line->data); - else { - bytes = strlen(TT.yank.data)+col_e-col_s; - strncpy(TT.yank.data+strlen(TT.yank.data), end->line->data+col_s, col_e-col_s); - TT.yank.data[bytes] = 0; - } return 1; } @@ -709,7 +835,7 @@ last_line_yank: struct vi_cmd_param { const char* cmd; unsigned flags; - int (*vi_cmd)(char, struct linelist*, int, int);//REG,row,col,FLAGS + int (*vi_cmd)(char, size_t, int);//REG,from,FLAGS }; struct vi_mov_param { const char* mov; @@ -765,7 +891,7 @@ int run_vi_cmd(char *cmd) { int i = 0, val = 0; char *cmd_e; - int (*vi_cmd)(char, struct linelist*, int, int) = 0; + int (*vi_cmd)(char, size_t, int) = 0; int (*vi_mov)(int, int, char*) = 0; TT.count0 = 0, TT.count1 = 0, TT.vi_mov_flag = 0; @@ -811,35 +937,29 @@ int run_vi_cmd(char *cmd) } } if (vi_mov) { - int prev_col = TT.cur_col; - struct linelist *pos = TT.c_r; + int prev_cursor = TT.cursor; if (vi_mov(TT.count0, TT.count1, cmd)) { - if (vi_cmd) return (vi_cmd(TT.vi_reg, pos, prev_col, TT.vi_mov_flag)); + if (vi_cmd) return (vi_cmd(TT.vi_reg, prev_cursor, TT.vi_mov_flag)); else return 1; } else return 0; //return some error } return 0; } +//TODO rewrite with slices static int search_str(char *s) { - struct linelist *lst = TT.c_r; - char *c = strstr(&TT.c_r->line->data[TT.cur_col+1], s); + //TODO may need to write + //size_t slice_strstr(size_t at, s) + //size_t slice_strrstr(size_t at, s) //reverse if (TT.last_search != s) { free(TT.last_search); TT.last_search = xstrdup(s); } - if (c) { - TT.cur_col = c-TT.c_r->line->data; - } else for (; !c;) { - lst = lst->down; - if (!lst) return 1; - c = strstr(lst->line->data, s); - } - TT.c_r = lst; - TT.cur_col = c-TT.c_r->line->data; + TT.cursor = 0; //TODO + TT.cur_col = 0; //TODO check_cursor_bounds(); return 0; } @@ -893,7 +1013,7 @@ void vi_main(void) TT.il->alloc = 80, TT.yank.alloc = 128; linelist_load(0); - TT.screen = TT.c_r = TT.text; + TT.screen = TT.cursor = 0; TT.vi_mov_flag = 0x20000000; TT.vi_mode = 1, TT.tabstop = 8; @@ -943,7 +1063,7 @@ void vi_main(void) vi_eol(1, 1, 0); // FALLTHROUGH case 'a': - if (TT.c_r && TT.c_r->line->len) TT.cur_col++; + //TODO cur_right(); // FALLTHROUGH case 'i': TT.vi_mode = 2; @@ -1021,10 +1141,10 @@ void vi_main(void) 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); - i_split(); break; default: if ((key >= 0x20 || key == 0x09) && @@ -1067,7 +1187,7 @@ int vi_crunch(FILE* out, int cols, int wc) for (;i--;) fputs(" ", out); } ret = TT.tabstop; - } + } else if (wc == '\n') return 0; return ret; } @@ -1108,7 +1228,6 @@ int crunch_nstr(char **str, int width, int n, FILE *out, char *escmore, static void draw_page() { - struct linelist *scr_buf = 0; unsigned y = 0; int x = 0; @@ -1123,9 +1242,13 @@ static void draw_page() int scroll = 0, redraw = 0; + int SSOL, SOL; + + adjust_screen_buffer(); - scr_buf = TT.screen; - redraw = (TT.vi_mov_flag & 0x30000000)>>28; + + //redraw = (TT.vi_mov_flag & 0x30000000)>>28; + redraw = 3; //TODO count line numbers and remove this line scroll = TT.drawn_row-TT.scr_row; if (TT.drawn_row<0 || TT.cur_row<0 || TT.scr_row<0) redraw = 3; @@ -1136,30 +1259,34 @@ static void draw_page() else if (scroll>0) printf("\033[%dL", scroll); //scroll up else if (scroll<0) printf("\033[%dM", -scroll); //scroll down - //jump until cursor - for (; y < TT.screen_height; y++ ) { - if (scr_buf == TT.c_r) break; - scr_buf = scr_buf->down; - } + SOL = text_strrchr(TT.cursor-1, '\n')+1; + bytes = text_getline(toybuf, SOL, ARRAY_LEN(toybuf)); + line = toybuf; + + SSOL = text_strrchr(TT.screen, '\n')+1; + for (y = 0; SSOL < SOL; y++) + SSOL += text_getline(0, SSOL, ARRAY_LEN(toybuf))+1; + + cy_scr = y; + //draw cursor row ///////////////////////////////////////////////////////////// //for long lines line starts to scroll when cursor hits margin - line = scr_buf->line->data; - bytes = TT.cur_col; + bytes = TT.cursor-SOL; // TT.cur_col; end = line; tty_jump(0, y); tty_esc("2K"); //find cursor position - aw = crunch_nstr(&end, 1024, bytes, 0, "\t", vi_crunch); + aw = crunch_nstr(&end, 1024, bytes, 0, "\t\n", vi_crunch); //if we need to render text that is not inserted to buffer yet if (TT.vi_mode == 2 && TT.il->len) { char* iend = TT.il->data; //input end x = 0; //find insert end position - iw = crunch_str(&iend, 1024, 0, "\t", vi_crunch); + iw = crunch_str(&iend, 1024, 0, "\t\n", vi_crunch); clip = (aw+iw) - TT.screen_width+margin; //if clipped area is bigger than text before insert @@ -1167,16 +1294,16 @@ static void draw_page() clip -= aw; iend = TT.il->data; - iw -= crunch_str(&iend, clip, 0, "\t", vi_crunch); - x = crunch_str(&iend, iw, stdout, "\t", vi_crunch); + iw -= crunch_str(&iend, clip, 0, "\t\n", vi_crunch); + x = crunch_str(&iend, iw, stdout, "\t\n", vi_crunch); } else { iend = TT.il->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); + aw -= crunch_nstr(&end, clip, bytes, 0, "\t\n", vi_crunch); + x = crunch_str(&end, aw, stdout, "\t\n", vi_crunch); + x += crunch_str(&iend, iw, stdout, "\t\n", vi_crunch); } } //when not inserting but still need to keep cursor inside screen @@ -1184,34 +1311,32 @@ static void draw_page() 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); + aw -= crunch_nstr(&end, clip, bytes, 0, "\t\n", vi_crunch); + x = crunch_str(&end, aw, stdout, "\t\n", vi_crunch); } else { end = line; - x = crunch_nstr(&end, aw, bytes, stdout, "\t", vi_crunch); + x = crunch_nstr(&end, aw, bytes, stdout, "\t\n", vi_crunch); } cx_scr = x; cy_scr = y; - if (scr_buf->line->len > bytes) { - x += crunch_str(&end, TT.screen_width-x, stdout, "\t", vi_crunch); - } - - if (scr_buf) scr_buf = scr_buf->down; - // drawing cursor row ends - /////////////////////////////////////////////////////////////////// + x += crunch_str(&end, TT.screen_width-x, stdout, "\t\n", vi_crunch); //start drawing all other rows that needs update /////////////////////////////////////////////////////////////////// - y = 0, scr_buf = TT.screen; - + y = 0; + SSOL = text_strrchr(TT.screen, '\n')+1; + bytes = text_getline(toybuf, SSOL, ARRAY_LEN(toybuf)); + line = toybuf; //if we moved around in long line might need to redraw everything if (clip != TT.drawn_col) redraw = 3; for (; y < TT.screen_height; y++ ) { int draw_line = 0; - if (scr_buf == TT.c_r) { - scr_buf = scr_buf->down; + if (SSOL == SOL) { + line = toybuf; + SSOL += bytes+1; + bytes = text_getline(line, SSOL, ARRAY_LEN(toybuf)); continue; } else if (redraw) draw_line++; else if (scroll<0 && TT.screen_height-y-1<-scroll) @@ -1222,75 +1347,97 @@ static void draw_page() if (draw_line) { tty_esc("2K"); - if (scr_buf) { - if (draw_line && scr_buf->line->data && scr_buf->line->len) { - line = scr_buf->line->data; - bytes = scr_buf->line->len; + if (line) { + if (draw_line && line && strlen(line)) { - aw = crunch_nstr(&line, clip, bytes, 0, "\t", vi_crunch); - crunch_str(&line, TT.screen_width-1, stdout, "\t", vi_crunch); + aw = crunch_nstr(&line, clip, bytes, 0, "\t\n", vi_crunch); + crunch_str(&line, TT.screen_width-1, stdout, "\t\n", vi_crunch); if ( *line ) printf("@"); } } else if (draw_line) printf("~"); } - if (scr_buf) scr_buf = scr_buf->down; + if (SSOL+bytes < TT.filesize) { + line = toybuf; + SSOL += bytes+1; + bytes = text_getline(line, SSOL, ARRAY_LEN(toybuf)); + } else line = 0; } TT.drawn_row = TT.scr_row, TT.drawn_col = clip; //finished updating visual area - tty_jump(0, TT.screen_height); tty_esc("2K"); if (TT.vi_mode == 2) printf("\x1b[1m-- INSERT --\x1b[m"); if (!TT.vi_mode) printf("\x1b[1m%s \x1b[m",TT.il->data); - tty_jump(TT.screen_width-12, TT.screen_height); - printf("%d,%d", TT.cur_row+1, TT.cur_col+1); - if (TT.cur_col != cx_scr) printf("-%d", cx_scr+1); + sprintf(toybuf, "%ld / %ld,%d,%d", TT.cursor, TT.filesize, + TT.cur_row+1, TT.cur_col+1); + + if (TT.cur_col != cx_scr) sprintf(toybuf+strlen(toybuf),"-%d", cx_scr+1); + + tty_jump(TT.screen_width-strlen(toybuf), TT.screen_height); + printf("%s", toybuf); if (TT.vi_mode) tty_jump(cx_scr, cy_scr); xflush(1); } - +//jump into valid offset index +//and valid utf8 codepoint static void check_cursor_bounds() { - if (TT.c_r->line->len == 0) { - TT.cur_col = 0; - return; - } else if (TT.c_r->line->len-1 < TT.cur_col) TT.cur_col = TT.c_r->line->len-1; - - if (TT.cur_col && utf8_width(&TT.c_r->line->data[TT.cur_col], - TT.c_r->line->len-TT.cur_col) <= 0) - TT.cur_col--, 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))) { + 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 update cursor and screen line numbers static void adjust_screen_buffer() { - //search cursor and screen - struct linelist *t = TT.text; - int c = -1, s = -1, i = 0; - //searching cursor and screen line numbers - for (;((c == -1) || (s == -1)) && t != 0; i++, t = t->down) { - if (t == TT.c_r) c = i; - if (t == TT.screen) s = i; - } - //adjust screen buffer so cursor is on drawing area - if (c <= s) TT.screen = TT.c_r, s = c; //scroll up - else { - //drawing does not have wrapping so no need to check width - int distance = c-s+1; - - if (distance > (int)TT.screen_height) { - int adj = distance-TT.screen_height; - for (;adj; adj--) TT.screen = TT.screen->down, s++; //scroll down - + if (!TT.cursor) TT.screen = 0; + else if (TT.screen > TT.cursor) { + if (text_byte(TT.cursor) == '\n') TT.screen = TT.cursor; + else TT.screen = text_strrchr(TT.cursor, '\n')+1; + } else { + size_t pos = TT.screen; + int n = 0; + for (;pos < TT.cursor; n++) { + pos = text_strchr(pos+1, '\n'); + if (n > TT.screen_height*2) { + if (text_byte(TT.cursor) == '\n') TT.screen = TT.cursor; + else TT.screen = text_strrchr(TT.cursor, '\n')+1; + return; + } + } + if (n > TT.screen_height) { + do { + TT.screen = text_strchr(TT.screen, '\n'); + } while (--n > TT.screen_height); + TT.screen++; } } - TT.cur_row = c, TT.scr_row = s; + + //TODO + TT.cur_row = 0, TT.scr_row = 0; } @@ -1311,19 +1458,6 @@ static int utf8_lnw(int* width, char* s, int bytes) return length; } -//try to estimate width of next "glyph" in terminal buffer -//combining chars 0x300-0x36F shall be zero width -static int utf8_width(char *s, int bytes) -{ - wchar_t wc; - int length; - - if (*s == '\t') return TT.tabstop; - length = utf8towc(&wc, s, bytes); - if (length < 1) return -1; - return wcwidth(wc); -} - static int utf8_dec(char key, char *utf8_scratch, int *sta_p) { int len = 0; @@ -1365,50 +1499,63 @@ 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; + if (!TT.cursor) return 1; - TT.cur_col--; + TT.cursor--; check_cursor_bounds(); } return 1; } +//TODO does not jump over combine char text?! static int cur_right(int count0, int count1, char* unused) { int count = count0*count1; + char buf[8] = {0}; + int len, width = 0; for (;count--;) { - if (TT.c_r->line->len <= 1) return 1; - if (TT.cur_col >= TT.c_r->line->len-1) { - TT.cur_col = utf8_last(TT.c_r->line->data, TT.c_r->line->len) - - TT.c_r->line->data; - return 1; + for (;TT.cursor < TT.filesize;) { + if ((len = text_codepoint(buf, TT.cursor)) > 0) TT.cursor += len; + else { + TT.cursor++; //we are not in valid data try jump over + continue; + } + + if (utf8_lnw(&width, buf, len) && width) break; } - TT.cur_col++; - if (utf8_width(&TT.c_r->line->data[TT.cur_col], - TT.c_r->line->len-TT.cur_col) <= 0) - cur_right(1, 1, 0); + //if (TT.cursor == filesize) return 1; + if (*buf == '\n') break; //if (TT.cursor == '/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.c_r->up;) - TT.c_r = TT.c_r->up; - + for (;count--;) { + if (TT.cursor) TT.cursor = text_strrchr(TT.cursor-1, '\n'); + if (TT.cursor) TT.cursor = text_strrchr(TT.cursor-1, '\n'); + } + TT.cursor++; + //TODO + column shift 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.c_r->down;) - TT.c_r = TT.c_r->down; + size_t n; + for (;count--;) + if ((n = text_strchr(TT.cursor, '\n'))+1 > TT.cursor) + TT.cursor = n+1; + + //TODO + column shift check_cursor_bounds(); return 1; } - -- 2.19.1
_______________________________________________ Toybox mailing list [email protected] http://lists.landley.net/listinfo.cgi/toybox-landley.net
