Place function calls in order so that there is no unneeded
declarations, clear some whitespace stuff. Add few commands
that are commonly used.

cleanup: reorganize functions
cleanup: some whitespace stuff
add: vi_o vi_O vi_I
fix: stop at edges when h and l
fix: fix dd not updating screen
fix: render after all delete moves
---
 toys/pending/vi.c | 1158 ++++++++++++++++++++++-----------------------
 1 file changed, 561 insertions(+), 597 deletions(-)
From 0947603c8687903df206ea0d5094d2ae2217c538 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jarno=20M=C3=A4kip=C3=A4=C3=A4?= <jmaki...@gmail.com>
Date: Sat, 15 Feb 2020 23:55:06 +0200
Subject: [PATCH] vi: Rearrange functions, add o, O, I

Place function calls in order so that there is no unneeded
declarations, clear some whitespace stuff. Add few commands
that are commonly used.

cleanup: reorganize functions
cleanup: some whitespace stuff
add: vi_o vi_O vi_I
fix: stop at edges when h and l
fix: fix dd not updating screen
fix: render after all delete moves
---
 toys/pending/vi.c | 1158 ++++++++++++++++++++++-----------------------
 1 file changed, 561 insertions(+), 597 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,95 +565,306 @@ static void write_file(char *filename)
 
 }
 
-static int vi_yy(char reg, int count0, int count1)
+//jump into valid offset index
+//and valid utf8 codepoint
+static void check_cursor_bounds()
 {
-  size_t history = TT.cursor;
-  size_t pos = text_sol(TT.cursor); //go left to first char on line
-  TT.vi_mov_flag |= 0x4;
+  char buf[8] = {0};
+  int len, width = 0;
+  if (!TT.filesize) TT.cursor = 0;
 
-  for (;count0; count0--) TT.cursor = text_nsol(TT.cursor);
+  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
+  }
+}
 
-  vi_yank(reg, pos, 0);
+// 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;
 
-  TT.cursor = history;
-  return 1;
 }
 
-static int vi_dd(char reg, int count0, int count1)
+//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 pos = text_sol(TT.cursor); //go left to first char on line
-  TT.vi_mov_flag |= 0x4;
+  size_t start = from, end = TT.cursor;
+  char *str;
 
-  for (;count0; count0--) TT.cursor = text_nsol(TT.cursor);
+  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;
 
-  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)
+static int vi_delete(char reg, size_t from, int flags)
 {
-  size_t from = TT.cursor;
+  size_t start = from, end = TT.cursor;
 
-  if (text_byte(TT.cursor) == '\n') {
-    cur_left(count0-1, 1, 0);
-  }
-  else {
-    cur_right(count0-1, 1, 0);
-    if (text_byte(TT.cursor) == '\n') TT.vi_mov_flag |= 2;
-    else cur_right(1, 1, 0);
+  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);
 
-  vi_delete(reg, from, 0);
-  check_cursor_bounds();
+  //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 vi_movw(int count0, int count1, char* unused)
+static int cur_left(int count0, int count1, char *unused)
 {
   int count = count0*count1;
-  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;
+  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;
-      } 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
+      if (utf8_lnw(&width, buf, len) && width) break;
+      else TT.cursor += len;
+    }
   }
   check_cursor_bounds();
   return 1;
 }
 
-static int vi_movb(int count0, int count1, char* unused)
+//TODO column shift
+static int cur_up(int count0, int count1, char *unused)
 {
   int count = count0*count1;
-  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 {
+  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;
+  size_t pos = text_sol(TT.cursor); //go left to first char on line
+  TT.vi_mov_flag |= 0x4;
+
+  for (;count0; count0--) TT.cursor = text_nsol(TT.cursor);
+
+  vi_yank(reg, pos, 0);
+
+  TT.cursor = history;
+  return 1;
+}
+
+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 |= 0x30000000;
+
+  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)
+{
+  size_t from = TT.cursor;
+
+  if (text_byte(TT.cursor) == '\n') {
+    cur_left(count0-1, 1, 0);
+  }
+  else {
+    cur_right(count0-1, 1, 0);
+    if (text_byte(TT.cursor) == '\n') TT.vi_mov_flag |= 2;
+    else cur_right(1, 1, 0);
+  }
+
+  vi_delete(reg, from, 0);
+  check_cursor_bounds();
+  return 1;
+}
+
+static int vi_movw(int count0, int count1, char *unused)
+{
+  int count = count0*count1;
+  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
+  }
+  check_cursor_bounds();
+  return 1;
+}
+
+static int vi_movb(int count0, int count1, char *unused)
+{
+  int count = count0*count1;
+  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));
@@ -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,231 +1228,42 @@ static int run_ex_cmd(char *cmd)
 
 }
 
-void vi_main(void)
+static int vi_crunch(FILE *out, int cols, int wc)
 {
-  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);
+  int ret = 0;
+  if (wc < 32 && TT.list) {
+    tty_esc("1m");
+    ret = crunch_escape(out,cols,wc);
+    tty_esc("m");
+  } else if (wc == 0x09) {
+    if (out) {
+      int i = TT.tabstop;
+      for (;i--;) fputs(" ", out);
+    }
+    ret = TT.tabstop;
+  } else if (wc == '\n') return 0;
+  return ret;
+}
 
-  TT.il->alloc = 80, TT.yank.alloc = 128;
+//crunch_str with n bytes restriction for printing substrings or
+//non null terminated strings
+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;
+  char *start, *end;
 
-  linelist_load(0);
-  TT.screen = TT.cursor = 0;
+  for (end = start = *str; *end && n>0; columns += col, end += bytes, n -= bytes) {
+    wchar_t wc;
 
-  TT.vi_mov_flag = 0x20000000;
-  TT.vi_mode = 1, TT.tabstop = 8;
-  TT.screen_width = 80, TT.screen_height = 24;
+    if ((bytes = utf8towc(&wc, end, 4))>0 && (col = wcwidth(wc))>=0) {
+      if (!escmore || wc>255 || !strchr(escmore, wc)) {
+        if (width-columns<col) break;
+        if (out) fwrite(end, bytes, 1, out);
 
-  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)
-{
-  int ret = 0;
-  if (wc < 32 && TT.list) {
-    tty_esc("1m");
-    ret = crunch_escape(out,cols,wc);
-    tty_esc("m");
-  } else if (wc == 0x09) {
-    if (out) {
-      int i = TT.tabstop;
-      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
-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;
-  char *start, *end;
-
-  for (end = start = *str; *end && n>0; columns += col, end += bytes, n -= bytes) {
-    wchar_t wc;
-
-    if ((bytes = utf8towc(&wc, end, 4))>0 && (col = wcwidth(wc))>=0) {
-      if (!escmore || wc>255 || !strchr(escmore, wc)) {
-        if (width-columns<col) break;
-        if (out) fwrite(end, bytes, 1, out);
-
-        continue;
-      }
-    }
+        continue;
+      }
+    }
 
     if (bytes<1) {
       bytes = 1;
@@ -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;
-}
-- 
2.20.1

_______________________________________________
Toybox mailing list
Toybox@lists.landley.net
http://lists.landley.net/listinfo.cgi/toybox-landley.net

Reply via email to