about summary refs log tree commit diff stats
path: root/zet.tlv
diff options
context:
space:
mode:
authorKartik K. Agaram <vc@akkartik.com>2022-02-08 00:28:20 -0800
committerKartik K. Agaram <vc@akkartik.com>2022-02-08 00:28:20 -0800
commitfb251c2819c15d2fd58ca434de6b9636b8dfdce0 (patch)
treea92328069407a63132083359f1ee5391f86da456 /zet.tlv
parenta8fb6d40023a735219d779efdd0434b90f2f45bf (diff)
downloadteliva-fb251c2819c15d2fd58ca434de6b9636b8dfdce0.tar.gz
zet.tlv: editing a single zettel
Diffstat (limited to 'zet.tlv')
-rw-r--r--zet.tlv314
1 files changed, 307 insertions, 7 deletions
diff --git a/zet.tlv b/zet.tlv
index 32282db..f5106cb 100644
--- a/zet.tlv
+++ b/zet.tlv
@@ -160,13 +160,9 @@
   menu:
     >-- To show app-specific hotkeys in the menu bar, add hotkey/command
     >-- arrays of strings to the menu array.
-    >menu = {}
-- __teliva_timestamp: original
-  update:
-    >function update(window)
-    >  local key = curses.getch()
-    >  -- process key here
-    >end
+    >menu = {
+    >  {'^e', 'edit'},
+    >}
 - __teliva_timestamp: original
   init_colors:
     >function init_colors()
@@ -323,6 +319,11 @@
     >    if render_state.wh2id[render_state.curr_w + 1] and render_state.wh2id[render_state.curr_w + 1][render_state.curr_h] then
     >      current_zettel_id = render_state.wh2id[render_state.curr_w + 1][render_state.curr_h]
     >    end
+    >  --
+    >  elseif key == 5 then  -- ctrl-e
+    >    local old_menu = menu
+    >    editz(window)
+    >    menu = old_menu
     >  end
     >end
 - __teliva_timestamp: original
@@ -388,3 +389,302 @@
     >  indent=2,  -- how children of a zettel are indicated
     >  current_zettel_bg=3,  -- color pair index initialized in init_colors
     >}
+- __teliva_timestamp: original
+  editz:
+    >function editz()
+    >  menu = { {'^e', 'back to browsing'},}
+    >  local top = (render_state.curr_h - 1) * (view_settings.height + view_settings.vmargin)
+    >  local bottom = top + view_settings.height
+    >  local left = (render_state.curr_w - 1) * (view_settings.width + view_settings.hmargin)
+    >  local right = left + view_settings.width
+    >  local cursor = 1
+    >  curses.curs_set(0)
+    >  local quit = false
+    >  while not quit do
+    >    editz_render(window, zettels[current_zettel_id].data, cursor, top, bottom, left, right)
+    >    quit, zettels[current_zettel_id].data, cursor = editz_update(window, zettels[current_zettel_id].data, cursor)
+    >  end
+    >  curses.curs_set(1)
+    >end
+- __teliva_timestamp: original
+  editz_render:
+    >function editz_render(window, s, cursor, top, bottom, left, right)
+    >  window:attrset(curses.color_pair(view_settings.current_zettel_bg))
+    >  for y=top,bottom-1 do
+    >    for x=left,right-1 do
+    >      window:mvaddch(y, x, ' ')
+    >    end
+    >  end
+    >  left = left + 1  -- left padding; TODO: indent
+    >  local y, x = top, left
+    >  window:mvaddstr(y, x, '')
+    >  for i=1,string.len(s) do
+    >    -- render character
+    >    if i == cursor then
+    >      if s[i] == '\n' then
+    >        -- newline at cursor = render extra space in reverse video before jumping to new line
+    >        window:attron(curses.A_REVERSE)
+    >        window:addch(' ')
+    >        window:attroff(curses.A_REVERSE)
+    >      else
+    >        -- most characters at cursor = render in reverse video
+    >        window:attron(curses.A_REVERSE)
+    >        window:addstr(s[i])
+    >        window:attroff(curses.A_REVERSE)
+    >      end
+    >    else
+    >      if s[i] ~= '\n' then
+    >        window:addstr(s[i])
+    >      end
+    >    end
+    >    -- update cursor position
+    >    if s[i] == '\n' then
+    >      x = left
+    >      y = y + 1
+    >      if y >= bottom then return end
+    >      window:mvaddstr(y, x, '')
+    >    else
+    >      x = x + 1
+    >      if x >= right then
+    >        y = y + 1
+    >        if y >= bottom then return end
+    >        x = left
+    >        window:mvaddstr(y, x, '')
+    >      end
+    >    end
+    >  end
+    >  if cursor > string.len(s) then
+    >    window:attron(curses.A_REVERSE)
+    >    window:addch(' ')
+    >    window:attroff(curses.A_REVERSE)
+    >  else
+    >    window:addch(' ')
+    >  end
+    >end
+- __teliva_timestamp: original
+  editz_update:
+    >function editz_update(window, prose, cursor)
+    >  local key = curses.getch()
+    >  local h, w = window:getmaxyx()
+    >  if key == curses.KEY_LEFT then
+    >    if cursor > 1 then
+    >      cursor = cursor-1
+    >    end
+    >  elseif key == curses.KEY_RIGHT then
+    >    if cursor <= #prose then
+    >      cursor = cursor+1
+    >    end
+    >  elseif key == curses.KEY_DOWN then
+    >    cursor = cursor_down(prose, cursor, w)
+    >  elseif key == curses.KEY_UP then
+    >    cursor = cursor_up(prose, cursor, w)
+    >  elseif key == curses.KEY_BACKSPACE or key == 8 or key == 127 then  -- ctrl-h, ctrl-?, delete
+    >    if cursor > 1 then
+    >      cursor = cursor-1
+    >      prose = prose:remove(cursor)
+    >    end
+    >  elseif key == 5 then  -- ctrl-e
+    >    return true, prose, cursor
+    >  elseif key == 10 or (key >= 32 and key < 127) then
+    >    prose = prose:insert(string.char(key), cursor-1)
+    >    cursor = cursor+1
+    >  end
+    >  return false, prose, cursor
+    >end
+- __teliva_timestamp: original
+  cursor_down:
+    >function cursor_down(s, old_idx, width)
+    >  local max = string.len(s)
+    >  local i = 1
+    >  -- compute oldcol, the screen column of old_idx
+    >  local oldcol = 0
+    >  local col = 0
+    >  while true do
+    >    if i > max then
+    >      -- abnormal old_idx
+    >      return old_idx
+    >    end
+    >    if i == old_idx then
+    >      oldcol = col
+    >      break
+    >    end
+    >    if s[i] == '\n' then
+    >      col = 0
+    >    else
+    >      col = col+1
+    >    end
+    >    i = i+1
+    >  end
+    >  -- skip rest of line
+    >  while true do
+    >    if i > max then
+    >      -- current line is at bottom
+    >      if col >= width then
+    >        return i
+    >      end
+    >      return old_idx
+    >    end
+    >    if s[i] == '\n' then
+    >      break
+    >    end
+    >    if i - old_idx >= width then
+    >      return i
+    >    end
+    >    col = col+1
+    >    i = i+1
+    >  end
+    >  -- compute index at same column on next line
+    >  -- i is at a newline
+    >  i = i+1
+    >  col = 0
+    >  while true do
+    >    if i > max then
+    >      -- next line is at bottom and is too short; position at end of it
+    >      return i
+    >    end
+    >    if s[i] == '\n' then
+    >      -- next line is too short; position at end of it
+    >      return i
+    >    end
+    >    if col == oldcol then
+    >      return i
+    >    end
+    >    col = col+1
+    >    i = i+1
+    >  end
+    >end
+    >
+    >function test_cursor_down()
+    >  -- lines that don't wrap
+    >  check_eq(cursor_down('abc\ndef', 1, 5), 5, 'cursor_down: non-bottom line first char')
+    >  check_eq(cursor_down('abc\ndef', 2, 5), 6, 'cursor_down: non-bottom line mid char')
+    >  check_eq(cursor_down('abc\ndef', 3, 5), 7, 'cursor_down: non-bottom line final char')
+    >  check_eq(cursor_down('abc\ndef', 4, 5), 8, 'cursor_down: non-bottom line end')
+    >  check_eq(cursor_down('abc\ndef', 5, 5), 5, 'cursor_down: bottom line first char')
+    >  check_eq(cursor_down('abc\ndef', 6, 5), 6, 'cursor_down: bottom line mid char')
+    >  check_eq(cursor_down('abc\ndef', 7, 5), 7, 'cursor_down: bottom line final char')
+    >  check_eq(cursor_down('abc\n\ndef', 2, 5), 5, 'cursor_down: to shorter line')
+    >
+    >  -- within a single wrapping line
+    >  --   |abcde|  <-- wrap, no newline
+    >  --   |fgh  |
+    >  check_eq(cursor_down('abcdefgh', 1, 5), 6, 'cursor_down from wrapping line: first char')
+    >  check_eq(cursor_down('abcdefgh', 2, 5), 7, 'cursor_down from wrapping line: mid char')
+    >  check_eq(cursor_down('abcdefgh', 5, 5), 9, 'cursor_down from wrapping line: to shorter line')
+    >
+    >  -- within a single very long wrapping line
+    >  --   |abcde|  <-- wrap, no newline
+    >  --   |fghij|  <-- wrap, no newline
+    >  --   |klm  |
+    >  check_eq(cursor_down('abcdefghijklm', 1, 5), 6, 'cursor_down within wrapping line: first char')
+    >  check_eq(cursor_down('abcdefghijklm', 2, 5), 7, 'cursor_down within wrapping line: mid char')
+    >  check_eq(cursor_down('abcdefghijklm', 5, 5), 10, 'cursor_down within wrapping line: final char')
+    >end
+- __teliva_timestamp: original
+  cursor_up:
+    >function cursor_up(s, old_idx, width)
+    >  local max = string.len(s)
+    >  local i = 1
+    >  -- compute oldcol, the screen column of old_idx
+    >  local oldcol = 0
+    >  local col = 0
+    >  local newline_before_current_line = 0
+    >  while true do
+    >    if i > max or i == old_idx then
+    >      oldcol = col
+    >      break
+    >    end
+    >    if s[i] == '\n' then
+    >      col = 0
+    >      newline_before_current_line = i
+    >    else
+    >      col = col+1
+    >      if col == width then
+    >        col = 0
+    >      end
+    >    end
+    >    i = i+1
+    >  end
+    >  -- find previous newline
+    >  i = i-col-1
+    >  if old_idx - newline_before_current_line > width then
+    >    -- we're in a wrapped line
+    >    return old_idx - width
+    >  end
+    >  -- scan back to start of previous line
+    >  if s[i] == '\n' then
+    >    i = i-1
+    >  end
+    >  while true do
+    >    if i < 1 then
+    >      -- current line is at top
+    >      break
+    >    end
+    >    if s[i] == '\n' then
+    >      break
+    >    end
+    >    i = i-1
+    >  end
+    >  -- i is at a newline
+    >  i = i+1
+    >  -- skip whole screen lines within previous line
+    >  while newline_before_current_line - i > width do
+    >    i = i + width
+    >  end
+    >  -- compute index at same column on previous screen line
+    >  col = 0
+    >  while true do
+    >    if i > max then
+    >      -- next line is at bottom and is too short; position at end of it
+    >      return i
+    >    end
+    >    if s[i] == '\n' then
+    >      -- next line is too short; position at end of it
+    >      return i
+    >    end
+    >    if col == oldcol then
+    >      return i
+    >    end
+    >    col = col+1
+    >    i = i+1
+    >  end
+    >end
+    >
+    >function test_cursor_up()
+    >  -- lines that don't wrap
+    >  check_eq(cursor_up('abc\ndef', 1, 5), 1, 'cursor_up: top line first char')
+    >  check_eq(cursor_up('abc\ndef', 2, 5), 2, 'cursor_up: top line mid char')
+    >  check_eq(cursor_up('abc\ndef', 3, 5), 3, 'cursor_up: top line final char')
+    >  check_eq(cursor_up('abc\ndef', 4, 5), 4, 'cursor_up: top line end')
+    >  check_eq(cursor_up('abc\ndef', 5, 5), 1, 'cursor_up: non-top line first char')
+    >  check_eq(cursor_up('abc\ndef', 6, 5), 2, 'cursor_up: non-top line mid char')
+    >  check_eq(cursor_up('abc\ndef', 7, 5), 3, 'cursor_up: non-top line final char')
+    >  check_eq(cursor_up('abc\ndef\n', 8, 5), 4, 'cursor_up: non-top line end')
+    >  check_eq(cursor_up('ab\ndef\n', 7, 5), 3, 'cursor_up: to shorter line')
+    >
+    >  -- within a single wrapping line
+    >  --   |abcde|  <-- wrap, no newline
+    >  --   |fgh  |
+    >  check_eq(cursor_up('abcdefgh', 6, 5), 1, 'cursor_up from wrapping line: first char')
+    >  check_eq(cursor_up('abcdefgh', 7, 5), 2, 'cursor_up from wrapping line: mid char')
+    >  check_eq(cursor_up('abcdefgh', 8, 5), 3, 'cursor_up from wrapping line: final char')
+    >  check_eq(cursor_up('abcdefgh', 9, 5), 4, 'cursor_up from wrapping line: wrapped line end')
+    >
+    >  -- within a single very long wrapping line
+    >  --   |abcde|  <-- wrap, no newline
+    >  --   |fghij|  <-- wrap, no newline
+    >  --   |klm  |
+    >  check_eq(cursor_up('abcdefghijklm', 11, 5), 6, 'cursor_up within wrapping line: first char')
+    >  check_eq(cursor_up('abcdefghijklm', 12, 5), 7, 'cursor_up within wrapping line: mid char')
+    >  check_eq(cursor_up('abcdefghijklm', 13, 5), 8, 'cursor_up within wrapping line: final char')
+    >  check_eq(cursor_up('abcdefghijklm', 14, 5), 9, 'cursor_up within wrapping line: wrapped line end')
+    >
+    >  -- from below to (the bottom of) a wrapping line
+    >  --   |abcde|  <-- wrap, no newline
+    >  --   |fg   |
+    >  --   |hij  |
+    >  check_eq(cursor_up('abcdefg\nhij', 9, 5), 6, 'cursor_up to wrapping line: first char')
+    >  check_eq(cursor_up('abcdefg\nhij', 10, 5), 7, 'cursor_up to wrapping line: mid char')
+    >  check_eq(cursor_up('abcdefg\nhij', 11, 5), 8, 'cursor_up to wrapping line: final char')
+    >  check_eq(cursor_up('abcdefg\nhij', 12, 5), 8, 'cursor_up to wrapping line: to shorter line')
+    >end