From 055f07aab7bbf56c9ecd0ef404e38726bb5c2d2d Mon Sep 17 00:00:00 2001 From: Jarno Mäkipää Date: Sat, 11 Jan 2020 00:36:02 +0800 Subject: vi: Replace linelist with Piece table 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... 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: more or less all previous functionality Implemented more proper file write: file is saved to .swp, blocks are unloaded, file permissions are copied, and atomic rename is called, block is reloaded chmod some defaults(rw-rw-r--) if original file could not be fstat (does not exist) FIX make all tests pass Removed: Some unused functions --- toys/pending/vi.c | 1347 +++++++++++++++++++++++++++++++---------------------- 1 file changed, 791 insertions(+), 556 deletions(-) (limited to 'toys/pending') diff --git a/toys/pending/vi.c b/toys/pending/vi.c index a21867b2..551184c0 100644 --- a/toys/pending/vi.c +++ b/toys/pending/vi.c @@ -43,20 +43,67 @@ 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; + int fd; //file_handle + ) +static const char *blank = " \n\r\t"; +static const char *specials = ",.:;=-+*/(){}<>[]!@#$%^&|\\?\"\'"; +// TT.vi_mov_flag is used for special cases when certain move +// acts differently depending is there DELETE/YANK or NOP +// Also commands such as G does not default to count0=1 +// 0x1 = Command needs argument (f,F,r...) +// 0x2 = Move 1 right on yank/delete/insert (e, $...) +// 0x4 = yank/delete last line fully +// 0x10000000 = redraw after cursor needed +// 0x20000000 = full redraw needed +// 0x40000000 = count0 not given +// 0x80000000 = move was reverse static void draw_page(); @@ -64,7 +111,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); @@ -76,221 +122,533 @@ 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); -// 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 -void dlist_insert_nomalloc(struct double_list **list, struct double_list *new) +struct double_list *dlist_add_before(struct double_list **head, + struct double_list **list, char *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; -} + struct double_list *new = xmalloc(sizeof(struct double_list)); + new->data = data; + if (*list == *head) *head = new; + dlist_add_nomalloc(list, new); + return new; +} -// Add an entry to the end of a doubly linked list -struct double_list *dlist_insert(struct double_list **list, char *data) +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; - dlist_insert_nomalloc(list, new); + if (*list) { + new->prev = *list; + new->next = (*list)->next; + (*list)->next->prev = new; + (*list)->next = new; + } else *head = *list = new->next = new->prev = new; return new; } -void linelist_free(void *node) +// 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 +static int insert_str(const char *data, size_t offset, size_t size, size_t len, + enum alloc_flag type) { - struct linelist *lst = (struct linelist *)node; - free(lst->line->data), free(lst->line), free(lst); + 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; } -void linelist_unload() +// this will not free any memory +// will only create more slices depending on position +static int cut_str(size_t offset, size_t len) { - 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); -} + struct slice_list *e, *s = TT.slices; + size_t end = offset+len; + size_t epos, spos = 0; + if (!s) return -1; -void write_file(char *filename) -{ - struct linelist *lst = TT.text; - FILE *fp = 0; - if (!filename) filename = (char*)*toys.optargs; - if (!(fp = fopen(filename, "w")) ) return; + //find start and end slices + for (;;) { + if (spos<=offset && spos+s->node->len>offset) break; + spos += s->node->len; + s = s->next; - for (;lst; lst = lst->down) - fprintf(fp, "%s\n", lst->line->data); + if (s == TT.slices) return -1; //error out of bounds + } - fclose(fp); -} + for (e = s, epos = spos; ; ) { + if (epos<=end && epos+e->node->len>end) break; + epos += e->node->len; + e = e->next; -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 (e == TT.slices) return -1; //error out of bounds } 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); + 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; } - 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--; + + 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 (TT.text == 0) TT.text = lst; + if (s == e) break; + + s = s->next; } - if (TT.text) dlist_terminate(TT.text->up); + return 0; +} + +//find offset position in slices +static struct slice_list *slice_offset(size_t *start, size_t offset) +{ + struct slice_list *s = TT.slices; + size_t spos = 0; + + //find start + for ( ;s ; ) { + if (spos<=offset && spos+s->node->len>offset) break; + + spos += s->node->len; + s = s->next; + + if (s == TT.slices) s = 0; //error out of bounds + } + if (s) *start = spos; + return s; +} + +static size_t text_strchr(size_t offset, char c) +{ + struct slice_list *s = TT.slices; + size_t epos, spos = 0; + int i = 0; + + //find start + if (!(s = slice_offset(&spos, offset))) return SIZE_MAX; + + 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; +} + +static size_t text_strrchr(size_t offset, char c) +{ + struct slice_list *s = TT.slices; + size_t epos, spos = 0; + int i = 0; + + //find start + if (!(s = slice_offset(&spos, offset))) return SIZE_MAX; + + 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; +} + +static 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; +} + +static int text_count(size_t start, size_t end, char c) +{ + struct slice_list *s = TT.slices; + size_t i, count = 0, spos = 0; + if (!(s = slice_offset(&spos, start))) return 0; + i = start-spos; + if (s) do { + for (; i < s->node->len && spos+inode->data[i] == c) count++; + if (spos+i>=end) return count; + + spos += s->node->len; + i = 0; + s = s->next; + + } while (s != TT.slices); + + return count; +} + +static char text_byte(size_t offset) +{ + struct slice_list *s = TT.slices; + size_t spos = 0; + //find start + if (!(s = slice_offset(&spos, offset))) return 0; + 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 +static 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 strlen(scratch); +} + +static size_t text_sol(size_t offset) +{ + size_t pos; + if (!TT.filesize) return 0; + else if (TT.filesize <= offset) return TT.filesize-1; + else if ((pos = text_strrchr(offset, '\n')) == SIZE_MAX) return 0; + else if (pos < offset) return pos+1; + return offset; +} + +static size_t text_eol(size_t offset) +{ + if (!TT.filesize) offset = 1; + else if (TT.filesize <= offset) return TT.filesize-1; + else if ((offset = text_strchr(offset, '\n')) == SIZE_MAX) + return TT.filesize-1; + return offset; +} + +static size_t text_nsol(size_t offset) +{ + offset = text_eol(offset); + if (text_byte(offset) == '\n') offset++; + if (offset >= TT.filesize) offset--; + return offset; +} + +static size_t text_psol(size_t offset) +{ + offset = text_sol(offset); + if (offset) offset--; + if (offset && text_byte(offset-1) != '\n') offset = text_sol(offset-1); + return offset; +} + +static 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 + if (!(s = slice_offset(&spos, offset))) return 0; + + 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; +} + +//copying is needed when file has lot of inserts that are +//just few char long, but not always. Advanced search should +//check big slices directly and just copy edge cases. +//Also this is only line based search multiline +//and regexec should be done instead. +static size_t text_strstr(size_t offset, char *str) +{ + size_t bytes, pos = offset; + char *s = 0; + do { + bytes = text_getline(toybuf, pos, ARRAY_LEN(toybuf)); + if (!bytes) pos++; //empty line + else if ((s = strstr(toybuf, str))) return pos+(s-toybuf); + else pos += bytes; + } while (pos < TT.filesize); + + return SIZE_MAX; +} + +static void block_list_free(void *node) +{ + struct block_list *d = node; + + if (d->node->alloc == HEAP) free((void *)d->node->data); + else if (d->node->alloc == MMAP) munmap((void *)d->node->data, d->node->size); + + free(d->node); + free(d); +} + +static void linelist_unload() +{ + llist_traverse((void *)TT.slices, llist_free_double); + TT.slices = 0; + + llist_traverse((void *)TT.text, block_list_free); + TT.text = 0; + + if (TT.fd) { + xclose(TT.fd); + TT.fd = 0; + } +} + +static int linelist_load(char *filename) +{ + if (!filename) filename = (char*)*toys.optargs; + + if (filename) { + int fd; + size_t len, size; + char *data; + if ( (fd = open(filename, O_RDONLY)) <0) return 0; + + size = fdlength(fd); + if (!(len = lseek(fd, 0, SEEK_END))) len = size; + lseek(fd, 0, SEEK_SET); + + data = xmmap(0, size, PROT_READ, MAP_SHARED, fd, 0); + if (data == MAP_FAILED) return 0; + insert_str(data, 0, size, len, MMAP); + TT.filesize = text_filesize(); + TT.fd = fd; + } - fclose(fp); return 1; +} + +static void write_file(char *filename) +{ + struct slice_list *s = TT.slices; + struct stat st; + int fd = 0; + if (!s) return; + + if (!filename) filename = (char*)*toys.optargs; + + sprintf(toybuf, "%s.swp", filename); + + if ( (fd = xopen(toybuf, O_WRONLY | O_CREAT | O_TRUNC)) <0) return; + + do { + xwrite(fd, (void *)s->node->data, s->node->len ); + s = s->next; + } while (s != TT.slices); + + linelist_unload(); + + xclose(fd); + if (!stat(filename, &st)) chmod(toybuf, st.st_mode); + else chmod(toybuf, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); + xrename(toybuf, filename); + linelist_load(filename); } -int vi_yy(char reg, int count0, int count1) +static 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 = text_sol(TT.cursor); //go left to first char on line TT.vi_mov_flag |= 0x4; - if (count0>1) cur_down(count0-1, 1, 0); + for (;count0; count0--) TT.cursor = text_nsol(TT.cursor); - vi_yank(reg, pos, 0, 0); + 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) +static int vi_dd(char reg, int count0, int count1) { - struct linelist *pos = TT.c_r; - TT.cur_col = 0; + size_t pos = text_sol(TT.cursor); //go left to first char on line TT.vi_mov_flag |= 0x4; - if (count0>1) cur_down(count0-1, 1, 0); - vi_delete(reg, pos, 0, 0); + for (;count0; count0--) TT.cursor = text_nsol(TT.cursor); + + if (pos == TT.cursor && TT.filesize) pos--; + 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. -int vi_movw(int count0, int count1, char* unused) +static 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 (TT.cursor > TT.filesize-1) break; + //if at empty jump to non empty + if (c == '\n') { + if (++TT.cursor > TT.filesize-1) break; + if ((c = text_byte(TT.cursor)) == '\n') break; + continue; + } else if (strchr(blank, c)) do { + if (++TT.cursor > TT.filesize-1) break; + c = text_byte(TT.cursor); + } while (strchr(blank, c)); + //if at special jump to non special + else if (strchr(specials, c)) do { + if (++TT.cursor > TT.filesize-1) break; + c = text_byte(TT.cursor); + } while (strchr(specials, c)); + //else jump to empty or spesial + else do { + if (++TT.cursor > TT.filesize-1) break; + c = text_byte(TT.cursor); + } while (c && !strchr(blank, c) && !strchr(specials, c)); + + } while (strchr(blank, c) && c != '\n'); //never stop at empty } - count--; - if (count>0) - return vi_movw(count, 1, 0); - check_cursor_bounds(); return 1; } @@ -298,29 +656,39 @@ 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; + int type = 0; + char c; + while (count--) { + c = text_byte(TT.cursor); + do { + if (!TT.cursor) break; + //if at empty jump to non empty + if (strchr(blank, c)) do { + if (!--TT.cursor) break; + c = text_byte(TT.cursor); + } while (strchr(blank, c)); + //if at special jump to non special + else if (strchr(specials, c)) do { + if (!--TT.cursor) break; + type = 0; + c = text_byte(TT.cursor); + } while (strchr(specials, c)); + //else jump to empty or spesial + else do { + if (!--TT.cursor) break; + type = 1; + c = text_byte(TT.cursor); + } while (!strchr(blank, c) && !strchr(specials, c)); + + } while (strchr(blank, c)); //never stop at empty } - 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; + //find first + for (;TT.cursor; TT.cursor--) { + c = text_byte(TT.cursor-1); + if (type && !strchr(blank, c) && !strchr(specials, c)) break; + else if (!type && !strchr(specials, c)) break; } - while (TT.c_r->line->data[TT.cur_col] > ' ') { - if (TT.cur_col)TT.cur_col--; - else goto exit_function; - } - 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; @@ -329,15 +697,19 @@ exit_function: 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--; + int type = 0; + char c; + + if (count>1) vi_movw(count-1, 1, unused); + + c = text_byte(TT.cursor); + if (strchr(specials, c)) type = 1; + TT.cursor++; + for (;TT.cursor < TT.filesize-1; TT.cursor++) { + c = text_byte(TT.cursor+1); + if (!type && (strchr(blank, c) || strchr(specials, c))) break; + else if (type && !strchr(specials, c)) break; + } TT.vi_mov_flag |= 2; check_cursor_bounds(); @@ -347,65 +719,17 @@ 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); + if (!str || !len) return; + insert_str(xstrdup(str), 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) { + TT.cursor = text_sol(TT.cursor); TT.cur_col = 0; TT.vi_mov_flag |= 0x80000000; return 1; @@ -413,12 +737,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; @@ -427,166 +747,110 @@ 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; //?? - } - - //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; - } - } //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; + //if yank ends with \n push is linemode else push in place+1 + size_t history = TT.cursor; + char *start = TT.yank.data; + char *eol = strchr(start, '\n'); + + if (start[strlen(start)-1] == '\n') { + if ((TT.cursor = text_strchr(TT.cursor, '\n')) == SIZE_MAX) + TT.cursor = TT.filesize; + else TT.cursor = text_nsol(TT.cursor); + } else cur_right(1, 1, 0); + + i_insert(start, strlen(start)); + if (eol) { + TT.vi_mov_flag |= 0x10000000; + TT.cursor = history; + } return 1; } 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; - } - } - } - return 0; +//// int count = count0*count1; + size_t pos = text_strchr(TT.cursor, *symbol); + if (pos != SIZE_MAX) TT.cursor = pos; + return 1; } static int vi_find_cb(int count0, int count1, char *symbol) { //do backward search + size_t pos = text_strrchr(TT.cursor, *symbol); + if (pos != SIZE_MAX) TT.cursor = pos; return 1; } //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; - - 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); + size_t prev_cursor = TT.cursor; + int count = count0*count1-1; + TT.cursor = 0; + + if (TT.vi_mov_flag&0x40000000 && (TT.cursor = TT.filesize) > 0) + TT.cursor = text_sol(TT.cursor-1); + else if (count) { + size_t next = 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(); @@ -595,37 +859,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; } @@ -636,60 +875,38 @@ 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.yank.alloc < start->line->alloc) { - TT.yank.data = xrealloc(TT.yank.data, start->line->alloc*2); - TT.yank.alloc = start->line->alloc*2; + if (TT.vi_mov_flag&0x80000000) start = TT.cursor, end = from; + else TT.cursor = start; //yank moves cursor to left pos always? + + if (TT.yank.alloc < end-from) { + size_t new_bounds = (1+end-from)/1024; + new_bounds += ((1+end-from)%1024) ? 1 : 0; + new_bounds *= 1024; + TT.yank.data = xrealloc(TT.yank.data, new_bounds); + TT.yank.alloc = new_bounds; } - 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; + //this is naive copy + for (str = TT.yank.data ; startline->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; - - 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; } @@ -711,7 +928,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; @@ -763,11 +980,11 @@ struct vi_cmd_param vi_cmds[] = {"y", 1, &vi_yank}, }; -int run_vi_cmd(char *cmd) +static 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; @@ -813,10 +1030,9 @@ 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 } @@ -825,28 +1041,19 @@ int run_vi_cmd(char *cmd) 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); + size_t pos = text_strstr(TT.cursor+1, s); 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; + if (pos != SIZE_MAX) TT.cursor = pos; check_cursor_bounds(); return 0; } -int run_ex_cmd(char *cmd) +static int run_ex_cmd(char *cmd) { if (cmd[0] == '/') { search_str(&cmd[1]); @@ -897,7 +1104,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; @@ -953,9 +1160,10 @@ void vi_main(void) break; case 'A': vi_eol(1, 1, 0); - // FALLTHROUGH + TT.vi_mode = 2; + break; case 'a': - if (TT.c_r && TT.c_r->line->len) TT.cur_col++; + cur_right(1, 1, 0); // FALLTHROUGH case 'i': TT.vi_mode = 2; @@ -1018,6 +1226,7 @@ void vi_main(void) switch (key) { case 27: i_insert(TT.il->data, TT.il->len); + cur_left(1, 1, 0); TT.vi_mode = 1; TT.il->len = 0; memset(TT.il->data, 0, TT.il->alloc); @@ -1035,10 +1244,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) && @@ -1068,7 +1277,7 @@ cleanup_vi: tty_esc("?1049l"); } -int vi_crunch(FILE* out, int cols, int wc) +static int vi_crunch(FILE* out, int cols, int wc) { int ret = 0; if (wc < 32 && TT.list) { @@ -1081,13 +1290,13 @@ int vi_crunch(FILE* out, int cols, int wc) for (;i--;) fputs(" ", out); } ret = TT.tabstop; - } + } else if (wc == '\n') return 0; 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, +static 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; @@ -1120,9 +1329,9 @@ int crunch_nstr(char **str, int width, int n, FILE *out, char *escmore, return columns; } +//BUGBUG cursor at eol static void draw_page() { - struct linelist *scr_buf = 0; unsigned y = 0; int x = 0; @@ -1137,8 +1346,11 @@ static void draw_page() int scroll = 0, redraw = 0; + int SSOL, SOL; + + adjust_screen_buffer(); - scr_buf = TT.screen; + //redraw = 3; //force full redraw redraw = (TT.vi_mov_flag & 0x30000000)>>28; scroll = TT.drawn_row-TT.scr_row; @@ -1150,30 +1362,32 @@ 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_sol(TT.cursor); + bytes = text_getline(toybuf, SOL, ARRAY_LEN(toybuf)); + line = toybuf; + + for (SSOL = TT.screen, y = 0; SSOL < SOL; y++) SSOL = text_nsol(SSOL); + + 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 @@ -1181,16 +1395,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 @@ -1198,34 +1412,31 @@ 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 = TT.screen, line = toybuf; + bytes = text_getline(toybuf, SSOL, ARRAY_LEN(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) @@ -1236,75 +1447,107 @@ 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; + char buf[8] = {0}; + int len, width = 0; + if (!TT.filesize) TT.cursor = 0; - 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(); + for (;;) { + if (TT.cursor < 1) { + TT.cursor = 0; + return; + } else if (TT.cursor >= TT.filesize-1) { + TT.cursor = TT.filesize-1; + return; + } + if ((len = text_codepoint(buf, TT.cursor)) < 1) { + TT.cursor--; //we are not in valid data try jump over + continue; + } + if (utf8_lnw(&width, buf, len) && width) break; + else TT.cursor--; //combine char jump over + } } +//TODO rewrite the logic, difficulties counting lines +//and with big files scroll should not rely in knowing +//absoluteline numbers static void adjust_screen_buffer() { - //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; + size_t c, s; + TT.cur_row = 0, TT.scr_row = 0; + if (!TT.cursor) { + TT.screen = 0; + TT.vi_mov_flag = 0x20000000; + return; + } else if (TT.screen > (1<<18) || TT.cursor > (1<<18)) { + //give up, file is big, do full redraw + + TT.screen = text_strrchr(TT.cursor-1, '\n')+1; + TT.vi_mov_flag = 0x20000000; + return; } - //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; + s = text_count(0, TT.screen, '\n'); + c = text_count(0, TT.cursor, '\n'); + if (s >= c) { + TT.screen = text_strrchr(TT.cursor-1, '\n')+1; + s = c; + TT.vi_mov_flag = 0x20000000; //TODO I disabled scroll + } else { + int distance = c-s+1; if (distance > (int)TT.screen_height) { - int adj = distance-TT.screen_height; - for (;adj; adj--) TT.screen = TT.screen->down, s++; //scroll down - + int n, adj = distance-TT.screen_height; + TT.vi_mov_flag = 0x20000000; //TODO I disabled scroll + for (;adj; adj--, s++) + if ((n = text_strchr(TT.screen, '\n'))+1 > TT.screen) + TT.screen = n+1; } } - TT.cur_row = c, TT.scr_row = s; + + TT.scr_row = s; + TT.cur_row = c; } @@ -1325,19 +1568,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; @@ -1379,9 +1609,9 @@ 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; @@ -1390,39 +1620,44 @@ static int cur_left(int count0, int count1, char* unused) static int cur_right(int count0, int count1, char* unused) { int count = count0*count1; - 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; + char buf[8] = {0}; + int len, width = 0; + for (;count; count--) { + if ((len = text_codepoint(buf, TT.cursor)) > 0) TT.cursor += len; + else TT.cursor++; + + for (;TT.cursor < TT.filesize;) { + if ((len = text_codepoint(buf, TT.cursor)) < 1) { + TT.cursor++; //we are not in valid data try jump over + continue; + } + + if (utf8_lnw(&width, buf, len) && width) break; + else TT.cursor += len; } - 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 (*buf == '\n') break; } + check_cursor_bounds(); return 1; } +//TODO column shift static int cur_up(int count0, int count1, char* unused) { int count = count0*count1; - for (;count-- && TT.c_r->up;) - TT.c_r = TT.c_r->up; + for (;count--;) TT.cursor = text_psol(TT.cursor); TT.vi_mov_flag |= 0x80000000; check_cursor_bounds(); return 1; } +//TODO column shift static int cur_down(int count0, int count1, char* unused) { int count = count0*count1; - for (;count-- && TT.c_r->down;) - TT.c_r = TT.c_r->down; + for (;count--;) TT.cursor = text_nsol(TT.cursor); check_cursor_bounds(); return 1; } - -- cgit v1.2.3