aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--toys/pending/vi.c1347
1 files changed, 791 insertions, 556 deletions
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+i<end; i++)
+ if (s->node->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 ; start<end; start++, str++) *str = text_byte(start);
+ *str = 0;
- 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;
-
- 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;
}
-