about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorKartik K. Agaram <vc@akkartik.com>2024-10-29 15:43:35 -0700
committerKartik K. Agaram <vc@akkartik.com>2024-10-29 15:58:00 -0700
commitc1a3616964bfdeb11144c0e7289fc16ba563e84a (patch)
tree18a6b93c13b61070de189527a4357bfb4c2d9893
parent1609d79516b2e8f790648fd67614d9edb9c2e24c (diff)
downloadview.love-c1a3616964bfdeb11144c0e7289fc16ba563e84a.tar.gz
bugfix in search UI
This one is ancient and it affects every single one of my forks,
including the whole lines2 lineage. The corner case: searching for empty
string.

In the process I've also cleaned up edit.check_locs on initialization to
only modify cursor if it can find a legal place for it.

In general I should be more careful about mutating the cursor. Just
adding 1 to it is irresponsible.
-rw-r--r--app.lua2
-rw-r--r--edit.lua33
-rw-r--r--source_edit.lua33
-rw-r--r--source_text_tests.lua14
-rw-r--r--text_tests.lua14
5 files changed, 83 insertions, 13 deletions
diff --git a/app.lua b/app.lua
index 263518c..1bec1f0 100644
--- a/app.lua
+++ b/app.lua
@@ -131,7 +131,7 @@ function App.run_tests()
   end
   table.sort(sorted_names)
 --?   App.initialize_for_test() -- debug: run a single test at a time like these 2 lines
---?   test_click_below_all_lines()
+--?   test_search()
   for _,name in ipairs(sorted_names) do
     App.initialize_for_test()
 --?     print('=== '..name)
diff --git a/edit.lua b/edit.lua
index 551db10..9e80cc3 100644
--- a/edit.lua
+++ b/edit.lua
@@ -140,15 +140,36 @@ function edit.cursor_on_text(State)
 end
 
 function edit.put_cursor_on_next_text_line(State)
-  while true do
-    if State.cursor1.line >= #State.lines then
+  local line = State.cursor1.line
+  while line < #State.lines do
+    line = line+1
+    if State.lines[line].mode == 'text' then
+      State.cursor1.line = line
+      State.cursor1.pos = 1
       break
     end
-    if State.lines[State.cursor1.line].mode == 'text' then
+  end
+end
+
+function edit.put_cursor_on_next_text_line_wrapping_around_if_necessary(State)
+  local line = State.cursor1.line
+  local max = #State.lines
+  for _ = 1, max-1 do
+    line = (line+1) % max
+    if State.lines[line].mode == 'text' then
+      State.cursor1.line = line
+      State.cursor1.pos = 1
       break
     end
-    State.cursor1.line = State.cursor1.line+1
-    State.cursor1.pos = 1
+  end
+end
+
+function edit.put_cursor_on_next_text_loc_wrapping_around_if_necessary(State)
+  local cursor_line = State.lines[State.cursor1.line].data
+  if State.cursor1.pos <= utf8.len(cursor_line) then
+    State.cursor1.pos = State.cursor1.pos + 1
+  else
+    edit.put_cursor_on_next_text_line_wrapping_around_if_necessary(State)
   end
 end
 
@@ -405,7 +426,7 @@ function edit.keychord_press(State, chord, key)
       State.screen_top = deepcopy(State.search_backup.screen_top)
       Text.search_next(State)
     elseif chord == 'down' then
-      State.cursor1.pos = State.cursor1.pos+1
+      edit.put_cursor_on_next_text_loc_wrapping_around_if_necessary(State)
       Text.search_next(State)
     elseif chord == 'up' then
       Text.search_previous(State)
diff --git a/source_edit.lua b/source_edit.lua
index 3c0f794..90c1171 100644
--- a/source_edit.lua
+++ b/source_edit.lua
@@ -142,15 +142,36 @@ function edit.cursor_on_text(State)
 end
 
 function edit.put_cursor_on_next_text_line(State)
-  while true do
-    if State.cursor1.line >= #State.lines then
+  local line = State.cursor1.line
+  while line < #State.lines do
+    line = line+1
+    if State.lines[line].mode == 'text' then
+      State.cursor1.line = line
+      State.cursor1.pos = 1
       break
     end
-    if State.lines[State.cursor1.line].mode == 'text' then
+  end
+end
+
+function edit.put_cursor_on_next_text_line_wrapping_around_if_necessary(State)
+  local line = State.cursor1.line
+  local max = #State.lines
+  for _ = 1, max-1 do
+    line = (line+1) % max
+    if State.lines[line].mode == 'text' then
+      State.cursor1.line = line
+      State.cursor1.pos = 1
       break
     end
-    State.cursor1.line = State.cursor1.line+1
-    State.cursor1.pos = 1
+  end
+end
+
+function edit.put_cursor_on_next_text_loc_wrapping_around_if_necessary(State)
+  local cursor_line = State.lines[State.cursor1.line].data
+  if State.cursor1.pos <= utf8.len(cursor_line) then
+    State.cursor1.pos = State.cursor1.pos + 1
+  else
+    edit.put_cursor_on_next_text_line_wrapping_around_if_necessary(State)
   end
 end
 
@@ -411,7 +432,7 @@ function edit.keychord_press(State, chord, key)
       State.screen_top = deepcopy(State.search_backup.screen_top)
       Text.search_next(State)
     elseif chord == 'down' then
-      State.cursor1.pos = State.cursor1.pos+1
+      edit.put_cursor_on_next_text_loc_wrapping_around_if_necessary(State)
       Text.search_next(State)
     elseif chord == 'up' then
       Text.search_previous(State)
diff --git a/source_text_tests.lua b/source_text_tests.lua
index 11cc823..db3ae44 100644
--- a/source_text_tests.lua
+++ b/source_text_tests.lua
@@ -2073,3 +2073,17 @@ function test_search_wrap_upwards()
   check_eq(Editor_state.cursor1.line, 1, '1/cursor:line')
   check_eq(Editor_state.cursor1.pos, 6, '1/cursor:pos')
 end
+
+function test_search_downwards_from_end_of_line()
+  App.screen.init{width=120, height=60}
+  Editor_state = edit.initialize_test_state()
+  Editor_state.lines = load_array{'abc', 'def', 'ghi'}
+  Text.redraw_all(Editor_state)
+  Editor_state.cursor1 = {line=1, pos=4}
+  Editor_state.screen_top1 = {line=1, pos=1}
+  edit.draw(Editor_state)
+  -- search for empty string
+  edit.run_after_keychord(Editor_state, 'C-f', 'f')
+  edit.run_after_keychord(Editor_state, 'down', 'down')
+  -- no crash
+end
diff --git a/text_tests.lua b/text_tests.lua
index 9b34a24..aebade8 100644
--- a/text_tests.lua
+++ b/text_tests.lua
@@ -2073,3 +2073,17 @@ function test_search_wrap_upwards()
   check_eq(Editor_state.cursor1.line, 1, '1/cursor:line')
   check_eq(Editor_state.cursor1.pos, 6, '1/cursor:pos')
 end
+
+function test_search_downwards_from_end_of_line()
+  App.screen.init{width=120, height=60}
+  Editor_state = edit.initialize_test_state()
+  Editor_state.lines = load_array{'abc', 'def', 'ghi'}
+  Text.redraw_all(Editor_state)
+  Editor_state.cursor1 = {line=1, pos=4}
+  Editor_state.screen_top1 = {line=1, pos=1}
+  edit.draw(Editor_state)
+  -- search for empty string
+  edit.run_after_keychord(Editor_state, 'C-f', 'f')
+  edit.run_after_keychord(Editor_state, 'down', 'down')
+  -- no crash
+end