aboutsummaryrefslogtreecommitdiff
path: root/toys
diff options
context:
space:
mode:
Diffstat (limited to 'toys')
-rw-r--r--toys/pending/vi.c986
1 files changed, 475 insertions, 511 deletions
diff --git a/toys/pending/vi.c b/toys/pending/vi.c
index c6f44ef6..b5f34e21 100644
--- a/toys/pending/vi.c
+++ b/toys/pending/vi.c
@@ -21,37 +21,28 @@ config VI
#include "toys.h"
GLOBALS(
- char *s;
- int cur_col;
- int cur_row;
- int scr_row;
- int drawn_row;
- int drawn_col;
- unsigned screen_height;
- unsigned screen_width;
- int vi_mode;
- int count0;
- int count1;
- int vi_mov_flag;
- int modified;
- char vi_reg;
- char *last_search;
- int tabstop;
- int list;
- struct str_line {
- int alloc;
- int len;
- char *data;
- } *il;
- 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;
-
+ char *s;
+ int vi_mode, tabstop, list;
+ int cur_col, cur_row, scr_row;
+ int drawn_row, drawn_col;
+ int count0, count1, vi_mov_flag;
+ unsigned screen_height, screen_width;
+ char vi_reg, *last_search;
+ struct str_line {
+ int alloc;
+ int len;
+ char *data;
+ } *il;
+ size_t screen, cursor; //offsets
+ //yank buffer
+ struct yank_buf {
+ char reg;
+ int alloc;
+ char* data;
+ } yank;
+
+ int modified, fd;
+ size_t filesize;
// mem_block contains RO data that is either original file as mmap
// or heap allocated inserted data
//
@@ -85,47 +76,58 @@ GLOBALS(
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
+//get utf8 length and width at same time
+static int utf8_lnw(int *width, char *s, int bytes)
+{
+ wchar_t wc;
+ int length = 1;
+ if (*s == '\t') *width = TT.tabstop;
+ else {
+ length = utf8towc(&wc, s, bytes);
+ if (length < 1) length = 0, *width = 0;
+ else *width = wcwidth(wc);
+ }
+ return length;
+}
-static void draw_page();
+static int utf8_dec(char key, char *utf8_scratch, int *sta_p)
+{
+ int len = 0;
+ char *c = utf8_scratch;
+ c[*sta_p] = key;
+ if (!(*sta_p)) *c = key;
+ if (*c < 0x7F) { *sta_p = 1; return 1; }
+ if ((*c & 0xE0) == 0xc0) len = 2;
+ else if ((*c & 0xF0) == 0xE0 ) len = 3;
+ else if ((*c & 0xF8) == 0xF0 ) len = 4;
+ else {*sta_p = 0; return 0; }
-//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 char* utf8_last(char* str, int size);
+ (*sta_p)++;
+ if (*sta_p == 1) return 0;
+ if ((c[*sta_p-1] & 0xc0) != 0x80) {*sta_p = 0; return 0; }
-static int cur_left(int count0, int count1, char* unused);
-static int cur_right(int count0, int count1, char* unused);
-static int cur_up(int count0, int count1, char* unused);
-static int cur_down(int count0, int count1, char* unused);
-static void check_cursor_bounds();
-static void adjust_screen_buffer();
-static int search_str(char *s);
+ if (*sta_p == len) { c[(*sta_p)] = 0; return 1; }
-//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);
+ return 0;
+}
+static char* utf8_last(char* str, int size)
+{
+ char* end = str+size;
+ int pos = size, len, width = 0;
+ for (;pos >= 0; end--, pos--) {
+ len = utf8_lnw(&width, end, size-pos);
+ if (len && width) return end;
+ }
+ return 0;
+}
struct double_list *dlist_add_before(struct double_list **head,
struct double_list **list, char *data)
@@ -254,27 +256,21 @@ static int cut_str(size_t offset, size_t len)
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)) {
+ } 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) {
+ } else if (spos == offset && s == e) {
//cut begin
size_t clip = end - offset;
s->node->len -= clip;
s->node->data += clip;
break;
- }
-
- else {
+ } else {
//cut middle
struct slice *tail = xmalloc(sizeof(struct slice));
size_t clip = end-offset;
@@ -410,7 +406,7 @@ static int text_codepoint(char *dest, size_t offset)
if (!state) return -1;
if (!finished && !state) return -1;
- if (dest) memcpy(dest,scratch,8);
+ if (dest) memcpy(dest, scratch, 8);
return strlen(scratch);
}
@@ -512,15 +508,9 @@ static void block_list_free(void *node)
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;
- }
+ if (TT.fd) xclose(TT.fd);
+ TT.slices = 0, TT.text = 0, TT.fd = 0;
}
static int linelist_load(char *filename)
@@ -575,6 +565,217 @@ static void write_file(char *filename)
}
+//jump into valid offset index
+//and valid utf8 codepoint
+static void check_cursor_bounds()
+{
+ char buf[8] = {0};
+ int len, width = 0;
+ if (!TT.filesize) TT.cursor = 0;
+
+ for (;;) {
+ if (TT.cursor < 1) {
+ TT.cursor = 0;
+ return;
+ } else if (TT.cursor >= TT.filesize-1) {
+ TT.cursor = TT.filesize-1;
+ return;
+ }
+ if ((len = text_codepoint(buf, TT.cursor)) < 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
+ }
+}
+
+// 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
+
+//TODO rewrite the logic, difficulties counting lines
+//and with big files scroll should not rely in knowing
+//absoluteline numbers
+static void adjust_screen_buffer()
+{
+ 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;
+ }
+
+ 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 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.scr_row = s;
+ TT.cur_row = c;
+
+}
+
+//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, size_t from, int flags)
+{
+ 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.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;
+ }
+
+ //this is naive copy
+ for (str = TT.yank.data ; start<end; start++, str++) *str = text_byte(start);
+
+ *str = 0;
+
+ return 1;
+}
+
+static int vi_delete(char reg, size_t from, int flags)
+{
+ size_t start = from, end = TT.cursor;
+
+ vi_yank(reg, from, flags);
+
+ 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) {
+ //TODO
+ }
+ //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?
+ TT.vi_mov_flag |= 0x30000000;
+
+ return 1;
+}
+
+static int vi_change(char reg, size_t to, int flags)
+{
+ vi_delete(reg, to, flags);
+ TT.vi_mode = 2;
+ return 1;
+}
+
+static int cur_left(int count0, int count1, char *unused)
+{
+ int count = count0*count1;
+ TT.vi_mov_flag |= 0x80000000;
+ for (;count && TT.cursor; count--) {
+ TT.cursor--;
+ if (text_byte(TT.cursor) == '\n') TT.cursor++;
+ check_cursor_bounds();
+ }
+ return 1;
+}
+
+static int cur_right(int count0, int count1, char *unused)
+{
+ int count = count0*count1, len, width = 0;
+ char buf[8] = {0};
+
+ for (;count; count--) {
+ len = text_codepoint(buf, TT.cursor);
+
+ if (*buf == '\n') break;
+ else if (len > 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;
+ }
+ }
+ 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.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.cursor = text_nsol(TT.cursor);
+
+ check_cursor_bounds();
+ return 1;
+}
+
+static int search_str(char *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 (pos != SIZE_MAX) TT.cursor = pos;
+ check_cursor_bounds();
+ return 0;
+}
+
static int vi_yy(char reg, int count0, int count1)
{
size_t history = TT.cursor;
@@ -592,7 +793,7 @@ static int vi_yy(char reg, int count0, int count1)
static int vi_dd(char reg, int count0, int count1)
{
size_t pos = text_sol(TT.cursor); //go left to first char on line
- TT.vi_mov_flag |= 0x4;
+ TT.vi_mov_flag |= 0x30000000;
for (;count0; count0--) TT.cursor = text_nsol(TT.cursor);
@@ -620,7 +821,7 @@ static int vi_x(char reg, int count0, int count1)
return 1;
}
-static int vi_movw(int count0, int count1, char* unused)
+static int vi_movw(int count0, int count1, char *unused)
{
int count = count0*count1;
while (count--) {
@@ -653,7 +854,7 @@ static int vi_movw(int count0, int count1, char* unused)
return 1;
}
-static int vi_movb(int count0, int count1, char* unused)
+static int vi_movb(int count0, int count1, char *unused)
{
int count = count0*count1;
int type = 0;
@@ -717,7 +918,7 @@ static int vi_move(int count0, int count1, char *unused)
}
-static void i_insert(char* str, int len)
+static void i_insert(char *str, int len)
{
if (!str || !len) return;
@@ -807,41 +1008,20 @@ static int vi_go(int count0, int count1, char *symbol)
return 1;
}
-static int vi_delete(char reg, size_t from, int flags)
+static int vi_o(char reg, int count0, int count1)
{
- size_t start = from, end = TT.cursor;
-
- vi_yank(reg, from, flags);
-
- 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;
- //}
- }
- //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?
-
+ TT.cursor = text_eol(TT.cursor);
+ insert_str(xstrdup("\n"), TT.cursor++, 1, 1, HEAP);
+ TT.vi_mov_flag |= 0x30000000;
+ TT.vi_mode = 2;
return 1;
}
+static int vi_O(char reg, int count0, int count1)
+{
+ TT.cursor = text_psol(TT.cursor);
+ return vi_o(reg, count0, count1);
+}
static int vi_D(char reg, int count0, int count1)
{
@@ -849,14 +1029,19 @@ static int vi_D(char reg, int count0, int count1)
if (!count0) return 1;
vi_eol(1, 1, 0);
vi_delete(reg, pos, 0);
- count0--;
- if (count0) {
- vi_dd(reg, count0, 1);
- }
+ if (--count0) vi_dd(reg, count0, 1);
+
check_cursor_bounds();
return 1;
}
+static int vi_I(char reg, int count0, int count1)
+{
+ TT.cursor = text_sol(TT.cursor);
+ TT.vi_mode = 2;
+ return 1;
+}
+
static int vi_join(char reg, int count0, int count1)
{
size_t next;
@@ -875,41 +1060,6 @@ static int vi_find_next(char reg, int count0, int count1)
return 1;
}
-static int vi_change(char reg, size_t to, int 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, size_t from, int flags)
-{
- 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.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;
- }
-
- //this is naive copy
- for (str = TT.yank.data ; start<end; start++, str++) *str = text_byte(start);
-
- *str = 0;
-
- return 1;
-}
-
//NOTES
//vi-mode cmd syntax is
//("[REG])[COUNT0]CMD[COUNT1](MOV)
@@ -942,13 +1092,16 @@ struct vi_special_param {
};
struct vi_special_param vi_special[] =
{
- {"dd", &vi_dd},
- {"yy", &vi_yy},
{"D", &vi_D},
+ {"I", &vi_I},
{"J", &vi_join},
+ {"O", &vi_O},
{"n", &vi_find_next},
+ {"o", &vi_o},
+ {"p", &vi_push},
{"x", &vi_x},
- {"p", &vi_push}
+ {"dd", &vi_dd},
+ {"yy", &vi_yy},
};
//there is around ~47 vi moves
//some of them need extra params
@@ -1039,19 +1192,6 @@ static int run_vi_cmd(char *cmd)
return 0;
}
-static int search_str(char *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 (pos != SIZE_MAX) TT.cursor = pos;
- check_cursor_bounds();
- return 0;
-}
static int run_ex_cmd(char *cmd)
{
@@ -1088,196 +1228,7 @@ static int run_ex_cmd(char *cmd)
}
-void vi_main(void)
-{
- char keybuf[16] = {0};
- char vi_buf[16] = {0};
- char utf8_code[8] = {0};
- int utf8_dec_p = 0, vi_buf_pos = 0;
- FILE *script = 0;
- if (FLAG(s)) script = fopen(TT.s, "r");
-
- TT.il = xzalloc(sizeof(struct str_line));
- TT.il->data = xzalloc(80);
- TT.yank.data = xzalloc(128);
-
- TT.il->alloc = 80, TT.yank.alloc = 128;
-
- linelist_load(0);
- TT.screen = TT.cursor = 0;
-
- TT.vi_mov_flag = 0x20000000;
- TT.vi_mode = 1, TT.tabstop = 8;
- TT.screen_width = 80, TT.screen_height = 24;
-
- terminal_size(&TT.screen_width, &TT.screen_height);
- TT.screen_height -= 1;
-
- set_terminal(0, 1, 0, 0);
- //writes stdout into different xterm buffer so when we exit
- //we dont get scroll log full of junk
- tty_esc("?1049h");
- tty_esc("H");
- xflush(1);
-
-
- draw_page();
- for (;;) {
- int key = 0;
- if (script) {
- key = fgetc(script);
- if (key == EOF) {
- fclose(script);
- script = 0;
- key = scan_key(keybuf, -1);
- }
- } else key = scan_key(keybuf, -1);
-
- if (key == -1) goto cleanup_vi;
-
- terminal_size(&TT.screen_width, &TT.screen_height);
- TT.screen_height -= 1; //TODO this is hack fix visual alignment
-
- // TODO: support cursor keys in ex mode too.
- if (TT.vi_mode && key>=256) {
- key -= 256;
- if (key==KEY_UP) cur_up(1, 1, 0);
- else if (key==KEY_DOWN) cur_down(1, 1, 0);
- else if (key==KEY_LEFT) cur_left(1, 1, 0);
- else if (key==KEY_RIGHT) cur_right(1, 1, 0);
- draw_page();
- continue;
- }
-
- if (TT.vi_mode == 1) { //NORMAL
- switch (key) {
- case '/':
- case '?':
- case ':':
- TT.vi_mode = 0;
- TT.il->data[0]=key;
- TT.il->len++;
- break;
- case 'A':
- vi_eol(1, 1, 0);
- TT.vi_mode = 2;
- break;
- case 'a':
- cur_right(1, 1, 0);
- // FALLTHROUGH
- case 'i':
- TT.vi_mode = 2;
- break;
- case 27:
- vi_buf[0] = 0;
- vi_buf_pos = 0;
- break;
- default:
- if (key > 0x20 && key < 0x7B) {
- vi_buf[vi_buf_pos] = key;//TODO handle input better
- vi_buf_pos++;
- if (run_vi_cmd(vi_buf)) {
- memset(vi_buf, 0, 16);
- vi_buf_pos = 0;
- }
- else if (vi_buf_pos == 16) {
- vi_buf_pos = 0;
- memset(vi_buf, 0, 16);
- }
-
- }
-
- break;
- }
- } else if (TT.vi_mode == 0) { //EX MODE
- switch (key) {
- case 0x7F:
- case 0x08:
- if (TT.il->len > 1) {
- TT.il->data[--TT.il->len] = 0;
- break;
- }
- // FALLTHROUGH
- case 27:
- TT.vi_mode = 1;
- TT.il->len = 0;
- memset(TT.il->data, 0, TT.il->alloc);
- break;
- case 0x0A:
- case 0x0D:
- if (run_ex_cmd(TT.il->data) == -1)
- goto cleanup_vi;
- TT.vi_mode = 1;
- TT.il->len = 0;
- memset(TT.il->data, 0, TT.il->alloc);
- break;
- default: //add chars to ex command until ENTER
- if (key >= 0x20 && key < 0x7F) { //might be utf?
- if (TT.il->len == TT.il->alloc) {
- TT.il->data = realloc(TT.il->data, TT.il->alloc*2);
- TT.il->alloc *= 2;
- }
- TT.il->data[TT.il->len] = key;
- TT.il->len++;
- }
- break;
- }
- } else if (TT.vi_mode == 2) {//INSERT MODE
- 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);
- break;
- case 0x7F:
- case 0x08:
- if (TT.il->len) {
- char *last = utf8_last(TT.il->data, TT.il->len);
- int shrink = strlen(last);
- memset(last, 0, shrink);
- TT.il->len -= shrink;
- }
- break;
- case 0x0A:
- 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);
- break;
- default:
- if ((key >= 0x20 || key == 0x09) &&
- utf8_dec(key, utf8_code, &utf8_dec_p)) {
-
- if (TT.il->len+utf8_dec_p+1 >= TT.il->alloc) {
- TT.il->data = realloc(TT.il->data, TT.il->alloc*2);
- TT.il->alloc *= 2;
- }
- strcpy(TT.il->data+TT.il->len, utf8_code);
- TT.il->len += utf8_dec_p;
- utf8_dec_p = 0;
- *utf8_code = 0;
-
- }
- break;
- }
- }
-
- draw_page();
-
- }
-cleanup_vi:
- linelist_unload();
- free(TT.il->data), free(TT.il), free(TT.yank.data);
- tty_reset();
- tty_esc("?1049l");
-}
-
-static 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) {
@@ -1329,7 +1280,6 @@ static int crunch_nstr(char **str, int width, int n, FILE *out, char *escmore,
return columns;
}
-//BUGBUG cursor at eol
static void draw_page()
{
unsigned y = 0;
@@ -1485,179 +1435,193 @@ static void draw_page()
xflush(1);
}
-//jump into valid offset index
-//and valid utf8 codepoint
-static void check_cursor_bounds()
-{
- char buf[8] = {0};
- int len, width = 0;
- if (!TT.filesize) TT.cursor = 0;
-
- for (;;) {
- if (TT.cursor < 1) {
- TT.cursor = 0;
- return;
- } else if (TT.cursor >= TT.filesize-1) {
- TT.cursor = TT.filesize-1;
- return;
- }
- if ((len = text_codepoint(buf, TT.cursor)) < 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()
+void vi_main(void)
{
- 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
+ char keybuf[16] = {0};
+ char vi_buf[16] = {0};
+ char utf8_code[8] = {0};
+ int utf8_dec_p = 0, vi_buf_pos = 0;
+ FILE *script = 0;
+ if (FLAG(s)) script = fopen(TT.s, "r");
- TT.screen = text_strrchr(TT.cursor-1, '\n')+1;
- TT.vi_mov_flag = 0x20000000;
- return;
- }
+ TT.il = xzalloc(sizeof(struct str_line));
+ TT.il->data = xzalloc(80);
+ TT.yank.data = xzalloc(128);
- 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 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.il->alloc = 80, TT.yank.alloc = 128;
- TT.scr_row = s;
- TT.cur_row = c;
+ linelist_load(0);
+ TT.screen = TT.cursor = 0;
-}
+ TT.vi_mov_flag = 0x20000000;
+ TT.vi_mode = 1, TT.tabstop = 8;
+ TT.screen_width = 80, TT.screen_height = 24;
-//get utf8 length and width at same time
-static int utf8_lnw(int* width, char* s, int bytes)
-{
- wchar_t wc;
- int length;
+ terminal_size(&TT.screen_width, &TT.screen_height);
+ TT.screen_height -= 1;
- *width = 0;
- if (*s == '\t') {
- *width = TT.tabstop;
- return 1;
- }
- length = utf8towc(&wc, s, bytes);
- if (length < 1) return 0;
- *width = wcwidth(wc);
- return length;
-}
+ set_terminal(0, 1, 0, 0);
+ //writes stdout into different xterm buffer so when we exit
+ //we dont get scroll log full of junk
+ tty_esc("?1049h");
+ tty_esc("H");
+ xflush(1);
-static int utf8_dec(char key, char *utf8_scratch, int *sta_p)
-{
- int len = 0;
- char *c = utf8_scratch;
- c[*sta_p] = key;
- if (!(*sta_p)) *c = key;
- if (*c < 0x7F) { *sta_p = 1; return 1; }
- if ((*c & 0xE0) == 0xc0) len = 2;
- else if ((*c & 0xF0) == 0xE0 ) len = 3;
- else if ((*c & 0xF8) == 0xF0 ) len = 4;
- else {*sta_p = 0; return 0; }
- (*sta_p)++;
+ draw_page();
+ for (;;) {
+ int key = 0;
+ if (script) {
+ key = fgetc(script);
+ if (key == EOF) {
+ fclose(script);
+ script = 0;
+ key = scan_key(keybuf, -1);
+ }
+ } else key = scan_key(keybuf, -1);
- if (*sta_p == 1) return 0;
- if ((c[*sta_p-1] & 0xc0) != 0x80) {*sta_p = 0; return 0; }
+ if (key == -1) goto cleanup_vi;
- if (*sta_p == len) { c[(*sta_p)] = 0; return 1; }
+ terminal_size(&TT.screen_width, &TT.screen_height);
+ TT.screen_height -= 1; //TODO this is hack fix visual alignment
- return 0;
-}
+ // TODO: support cursor keys in ex mode too.
+ if (TT.vi_mode && key>=256) {
+ key -= 256;
+ if (key==KEY_UP) cur_up(1, 1, 0);
+ else if (key==KEY_DOWN) cur_down(1, 1, 0);
+ else if (key==KEY_LEFT) cur_left(1, 1, 0);
+ else if (key==KEY_RIGHT) cur_right(1, 1, 0);
+ draw_page();
+ continue;
+ }
-static char* utf8_last(char* str, int size)
-{
- char* end = str+size;
- int pos = size;
- int len = 0;
- int width = 0;
- while (pos >= 0) {
- len = utf8_lnw(&width, end, size-pos);
- if (len && width) return end;
- end--; pos--;
- }
- return 0;
-}
+ if (TT.vi_mode == 1) { //NORMAL
+ switch (key) {
+ case '/':
+ case '?':
+ case ':':
+ TT.vi_mode = 0;
+ TT.il->data[0]=key;
+ TT.il->len++;
+ break;
+ case 'A':
+ vi_eol(1, 1, 0);
+ TT.vi_mode = 2;
+ break;
+ case 'a':
+ cur_right(1, 1, 0);
+ // FALLTHROUGH
+ case 'i':
+ TT.vi_mode = 2;
+ break;
+ case 27:
+ vi_buf[0] = 0;
+ vi_buf_pos = 0;
+ break;
+ default:
+ if (key > 0x20 && key < 0x7B) {
+ vi_buf[vi_buf_pos] = key;//TODO handle input better
+ vi_buf_pos++;
+ if (run_vi_cmd(vi_buf)) {
+ memset(vi_buf, 0, 16);
+ vi_buf_pos = 0;
+ }
+ else if (vi_buf_pos == 16) {
+ vi_buf_pos = 0;
+ memset(vi_buf, 0, 16);
+ }
-static int cur_left(int count0, int count1, char* unused)
-{
- int count = count0*count1;
- TT.vi_mov_flag |= 0x80000000;
- for (;count--;) {
- if (!TT.cursor) return 1;
+ }
- TT.cursor--;
- check_cursor_bounds();
- }
- return 1;
-}
+ break;
+ }
+ } else if (TT.vi_mode == 0) { //EX MODE
+ switch (key) {
+ case 0x7F:
+ case 0x08:
+ if (TT.il->len > 1) {
+ TT.il->data[--TT.il->len] = 0;
+ break;
+ }
+ // FALLTHROUGH
+ case 27:
+ TT.vi_mode = 1;
+ TT.il->len = 0;
+ memset(TT.il->data, 0, TT.il->alloc);
+ break;
+ case 0x0A:
+ case 0x0D:
+ if (run_ex_cmd(TT.il->data) == -1)
+ goto cleanup_vi;
+ TT.vi_mode = 1;
+ TT.il->len = 0;
+ memset(TT.il->data, 0, TT.il->alloc);
+ break;
+ default: //add chars to ex command until ENTER
+ if (key >= 0x20 && key < 0x7F) { //might be utf?
+ if (TT.il->len == TT.il->alloc) {
+ TT.il->data = realloc(TT.il->data, TT.il->alloc*2);
+ TT.il->alloc *= 2;
+ }
+ TT.il->data[TT.il->len] = key;
+ TT.il->len++;
+ }
+ break;
+ }
+ } else if (TT.vi_mode == 2) {//INSERT MODE
+ 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);
+ break;
+ case 0x7F:
+ case 0x08:
+ if (TT.il->len) {
+ char *last = utf8_last(TT.il->data, TT.il->len);
+ int shrink = strlen(last);
+ memset(last, 0, shrink);
+ TT.il->len -= shrink;
+ }
+ break;
+ case 0x0A:
+ 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);
+ break;
+ default:
+ if ((key >= 0x20 || key == 0x09) &&
+ utf8_dec(key, utf8_code, &utf8_dec_p)) {
-static int cur_right(int count0, int count1, char* unused)
-{
- int count = count0*count1;
- 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++;
+ if (TT.il->len+utf8_dec_p+1 >= TT.il->alloc) {
+ TT.il->data = realloc(TT.il->data, TT.il->alloc*2);
+ TT.il->alloc *= 2;
+ }
+ strcpy(TT.il->data+TT.il->len, utf8_code);
+ TT.il->len += utf8_dec_p;
+ utf8_dec_p = 0;
+ *utf8_code = 0;
- 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;
+ }
+ break;
}
-
- if (utf8_lnw(&width, buf, len) && width) break;
- else TT.cursor += len;
}
- 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.cursor = text_psol(TT.cursor);
+ draw_page();
- TT.vi_mov_flag |= 0x80000000;
- check_cursor_bounds();
- return 1;
+ }
+cleanup_vi:
+ linelist_unload();
+ free(TT.il->data), free(TT.il), free(TT.yank.data);
+ tty_reset();
+ tty_esc("?1049l");
}
-//TODO column shift
-static int cur_down(int count0, int count1, char* unused)
-{
- int count = count0*count1;
- for (;count--;) TT.cursor = text_nsol(TT.cursor);
-
- check_cursor_bounds();
- return 1;
-}