Compare commits

...

9 Commits

Author SHA1 Message Date
Kartik K. Agaram 2eb71d7a80 Merge lines.love 2024-06-11 11:46:36 -07:00
Kartik K. Agaram 55f5c2d696 crap, fix some final changes in the source editor 2024-06-11 11:10:34 -07:00
Kartik K. Agaram 7aa43d1d2d comment 2024-06-11 10:49:56 -07:00
Kartik K. Agaram 85b09772ba whitespace 2024-06-11 10:49:34 -07:00
Kartik K. Agaram 69c88da98c stop caching starty
This is quite useful because I used to have a long list of places in
which to invalidate the cache.
2024-06-11 10:37:58 -07:00
Kartik K. Agaram f2299cb422 stop caching screen_bottom1
I'm not sure this is very useful. I had an initial idea to stop using
screen_bottom1 in final_text_loc_on_screen, by starting from screen_top1
rather than screen_bottom1. But that changes the direction in which we
scan for the text line in situations where there is somehow no text on
screen (something that should never happen but I have zero confidence in
that).

Still, it doesn't seem like a bad thing to drastically reduce the
lifetime of some derived state.

Really what I need to do is throw this whole UX out and allow the cursor
to be on a drawing as a whole. So up arrow or left arrow below a drawing
would focus the whole drawing in a red border, and another up arrow and
left arrow would skip the drawing and continue upward. I think that
change to the UX will eliminate a whole class of special cases in the
code.
2024-06-11 07:02:46 -07:00
Kartik K. Agaram 19615eade0 bugfix in source editor: don't clear selection on M-arrow 2024-06-09 20:38:53 -07:00
Kartik K. Agaram 9b5a78d3c5 bugfix in source editor 2024-06-09 20:35:50 -07:00
Kartik K. Agaram 9501f01ca0 fix a crash involving mouse and drawings
Thanks Alex Schroeder for reporting this crash. The scenario:
  * Edit a file like say this repo's Readme.
  * The second line is empty and there's a '+' to insert a drawing.
    Click on that.
  * Resize the window so just the first line of text and the drawing are
    visible.
  * Close the window.
  * Reopen lines.love, it will reopen the same file.
  * Click on the left margin to the left of the drawing.

Before this commit these steps yielded the following crash:

  Error: bad argument #1 to 'len' (string expected, got nil)
  text.lua:626: in function 'pos_at_end_of_screen_line'
  edit.lua:298: in function 'mouse_press'

There were two distinct problems here:

1. State.screen_bottom1 is not required to point to a text line, it
   could just as well be a drawing. I have been sloppy in handling that.
2. The bug was partially masked (the need to close and reopen the
   window) by a second bug: inserting a drawing was not invalidating the
   cache I save of starty coordinates for each line. (I've inserted and
   deleted starty invalidations a few times in the past, but it looks
   like I'd never had one in this particular location edit.draw before.)

How did these issues get missed for years?
  - Even though I use lines.love on a daily basis, it turns out I don't
    actually create line drawings all that often.
  - When I do, I'm still living in files that are mostly text with only
    an occasional drawing.
  - I keep my windows fairly large.

Between these 3 patterns, the odds of running into a drawing as the
first or bottom-most line on the screen were fairly small. And then I
had to interact with it. I suspect I tend to interact with drawings
after centering them vertically.

---

Bug #1 in particular has some interesting past history.

* Near the start of the project, when I implemented line-wrapping I
  started saving screen_bottom, the bottom-most line displayed on
  screen. I did this so I could scroll down easily just by assigning
  `screen_top = screen_bottom`. (On the other hand, scrolling up still
  required some work. I should perhaps get rid of it and just compute
  scrolls from scratch each time.)

* Also near the start of the project, I supported selecting text by a
  complex state machine spanning keypress, mouse press and mouse
  release:
    mouse click (press and immediate release) moves cursor
    mouse drag (press and much later release) creates selection
    shift-click selects from current cursor to click location
    shift-movement creates/grows a selection

* On 2023-06-01, inscript reported a bug. Opening a window with just a
  little bit of text (lots of unused space in the window), selecting all
  the text and then clicking below all the text would crash the editor.

  To fix this I added code at the bottom of edit.mouse_press which
  computed the final visible line+pos location and used that in the
  cursor-move/text-selection state machine. It did this computation
  based on.. screen_bottom. But I didn't notice that screen_bottom could
  be a drawing (which has no pos). This commit's bug/regression was
  created.

* On 2023-09-20, Matt Wynne encountered a crash which got me to realize
  I need code at the bottom of edit.mouse_release symmetric to the code
  at the bottom of edit.mouse_press. I still didn't notice that
  screen_bottom could be a drawing.

So in fixing inscript's bug report, I introduced (at least) 2
regressions, because I either had no idea or quickly forgot that
screen_bottom could point at a drawing.

While I created regressions, the underlying mental bug feels new. I just
never focused on the fact that screen_bottom could point at a drawing.

This past history makes me suspicious of my mouse_press/mouse_release
code. I think I'm going to get rid of screen_bottom entirely as a
concept. I'll still have to be careful though about the remaining
locations and which of them are allowed to point at drawings:

  - cursor and selection are not allowed to point at drawings
  - screen_top and screen_bottom are allowed to point at drawings

I sometimes copy between these 4 location variables. Auditing shows no
gaps where cursor could ever end up pointing at a drawing. It's just
when I started using screen_bottom for a whole new purpose (in
the mouse_press/release state machine) that I went wrong.

I should also try getting rid of starty entirely. Is it _really_ needed
for a responsive editor? I think I introduced it back when I didn't know
what I was doing with LÖVE and was profligately creating text objects
willy-nilly just to compute widths.

Getting rid of these two fairly global bits of mutable state will
hopefully make lines much more robust when the next person tries it out
in 6 months :-/ X-(

Thanks everyone for the conversation around this bug:
  https://merveilles.town/@akkartik/112567862542495637

---

Bug #2 has some complexity as well, and might lead to some follow-on
cleanup.

When I click on the button to insert a new drawing, the mouse_release
hook triggers and moves the cursor below the new drawing. This is
desirable, but I'd never noticed this happy accident. It stops working
when I invalidate starty for all lines (which gets recomputed and cached
for all visible lines on every frame).

Fixing this caused a couple of unit tests start crashing for 2 reasons
that required their own minor fixes:

  - My emulated mouse press and release didn't have an intervening
    frame and so mouse_release no longer receives starty. Now I've added
    a call to edit.draw() between press and release.

    This might actually bite someone for real someday, if they're
    running on a slow computer or something like that. I've tried to
    click really fast but I can't seem to put mouse_press and release in
    the same frame (assuming 30 frames per second)

  - My tests' window dimensions often violate my constraint that the
    screen always have one line of text for showing the cursor. They're
    unrealistically small or have a really wide aspect ratio (width 2x
    of height). I suspect lines.love will itself crash in those
    situations, but hopefully they're unrealistic. Hmm, I wonder what
    would happen if someone maximized in a 16:9 screen, that's almost
    2x.. Anyways, I've cleaned a couple of tests up, but might need to
    fix up others at some point. I'd have to rejigger all my brittle
    line-wrapping tests if I modify the screen width :-/ X-(
2024-06-09 13:17:55 -07:00
15 changed files with 387 additions and 433 deletions

View File

@ -73,6 +73,11 @@ found anything amiss: http://akkartik.name/contact
* No clipping yet for drawings. In particular, circles/squares/rectangles and
point labels can overflow a drawing.
* If you ever see a crash when clicking on the mouse, it might be because a
mouse press and release need to happen in separate frames. Try pressing and
releasing more slowly and let me know if that helps or not. This is klunky,
sorry.
* Touchpads can drag the mouse pointer using a light touch or a heavy click.
On Linux, drags using the light touch get interrupted when a key is pressed.
You'll have to press down to drag.

View File

@ -130,6 +130,8 @@ function App.run_tests()
end
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()
for _,name in ipairs(sorted_names) do
App.initialize_for_test()
--? print('=== '..name)
@ -404,9 +406,10 @@ end
-- prepend file/line/test
function prepend_debug_info_to_test_failure(test_name, err)
local err_without_line_number = err:gsub('^[^:]*:[^:]*: ', '')
local stack_trace = debug.traceback('', --[[stack frame]]5)
local stack_trace = debug.traceback('', --[[stack frame]]5) -- most likely to be useful, but set to 0 for a complete stack trace
local file_and_line_number = stack_trace:gsub('stack traceback:\n', ''):gsub(': .*', '')
local full_error = file_and_line_number..':'..test_name..' -- '..err_without_line_number
-- uncomment this line for a complete stack trace
--? local full_error = file_and_line_number..':'..test_name..' -- '..err_without_line_number..'\t\t'..stack_trace:gsub('\n', '\n\t\t')
table.insert(Test_errors, full_error)
end

View File

@ -6,16 +6,15 @@ require 'drawing_tests'
-- into 256 parts.
function Drawing.draw(State, line_index, y)
local line = State.lines[line_index]
local line_cache = State.line_cache[line_index]
line_cache.starty = y
local pmx,pmy = App.mouse_x(), App.mouse_y()
if pmx < State.right and pmy > line_cache.starty and pmy < line_cache.starty+Drawing.pixels(line.h, State.width) then
local starty = Text.starty(State, line_index)
if pmx < State.right and pmy > starty and pmy < starty+Drawing.pixels(line.h, State.width) then
App.color(Icon_color)
love.graphics.rectangle('line', State.left,line_cache.starty, State.width,Drawing.pixels(line.h, State.width))
love.graphics.rectangle('line', State.left,starty, State.width,Drawing.pixels(line.h, State.width))
if icon[State.current_drawing_mode] then
icon[State.current_drawing_mode](State.right-22, line_cache.starty+4)
icon[State.current_drawing_mode](State.right-22, starty+4)
else
icon[State.previous_drawing_mode](State.right-22, line_cache.starty+4)
icon[State.previous_drawing_mode](State.right-22, starty+4)
end
if App.mouse_down(1) and love.keyboard.isDown('h') then
@ -30,7 +29,7 @@ function Drawing.draw(State, line_index, y)
end
local mx = Drawing.coord(pmx-State.left, State.width)
local my = Drawing.coord(pmy-line_cache.starty, State.width)
local my = Drawing.coord(pmy-starty, State.width)
for _,shape in ipairs(line.shapes) do
if geom.on_shape(mx,my, line, shape) then
@ -38,11 +37,11 @@ function Drawing.draw(State, line_index, y)
else
App.color(Stroke_color)
end
Drawing.draw_shape(line, shape, line_cache.starty, State.left,State.right)
Drawing.draw_shape(line, shape, starty, State.left,State.right)
end
local function px(x) return Drawing.pixels(x, State.width)+State.left end
local function py(y) return Drawing.pixels(y, State.width)+line_cache.starty end
local function py(y) return Drawing.pixels(y, State.width)+starty end
for i,p in ipairs(line.points) do
if p.deleted == nil then
if Drawing.near(p, mx,my, State.width) then
@ -71,7 +70,7 @@ function Drawing.draw(State, line_index, y)
end
end
App.color(Current_stroke_color)
Drawing.draw_pending_shape(line, line_cache.starty, State.left,State.right)
Drawing.draw_pending_shape(line, starty, State.left,State.right)
end
function Drawing.draw_shape(drawing, shape, top, left,right)
@ -209,17 +208,24 @@ function Drawing.draw_pending_shape(drawing, top, left,right)
end
end
function Drawing.in_drawing(drawing, line_cache, x,y, left,right)
if line_cache.starty == nil then return false end -- outside current page
function Drawing.in_current_drawing(State, x,y, left,right)
return Drawing.in_drawing(State, State.lines.current_drawing_index, x,y, left,right)
end
function Drawing.in_drawing(State, line_index, x,y, left,right)
assert(State.lines[line_index].mode == 'drawing')
local starty = Text.starty(State, line_index)
if starty == nil then return false end -- outside current page
local drawing = State.lines[line_index]
local width = right-left
return y >= line_cache.starty and y < line_cache.starty + Drawing.pixels(drawing.h, width) and x >= left and x < right
return y >= starty and y < starty + Drawing.pixels(drawing.h, width) and x >= left and x < right
end
function Drawing.mouse_press(State, drawing_index, x,y, mouse_button)
local drawing = State.lines[drawing_index]
local line_cache = State.line_cache[drawing_index]
local starty = Text.starty(State, drawing_index)
local cx = Drawing.coord(x-State.left, State.width)
local cy = Drawing.coord(y-line_cache.starty, State.width)
local cy = Drawing.coord(y-starty, State.width)
if State.current_drawing_mode == 'freehand' then
drawing.pending = {mode=State.current_drawing_mode, points={{x=cx, y=cy}}}
elseif State.current_drawing_mode == 'line' or State.current_drawing_mode == 'manhattan' then
@ -244,8 +250,8 @@ end
function Drawing.update(State)
if State.lines.current_drawing == nil then return end
local drawing = State.lines.current_drawing
local line_cache = State.line_cache[State.lines.current_drawing_index]
if line_cache.starty == nil then
local starty = Text.starty(State, State.lines.current_drawing_index)
if starty == nil then
-- some event cleared starty just this frame
-- draw in this frame will soon set starty
-- just skip this frame
@ -254,9 +260,9 @@ function Drawing.update(State)
assert(drawing.mode == 'drawing', 'Drawing.update: line is not a drawing')
local pmx, pmy = App.mouse_x(), App.mouse_y()
local mx = Drawing.coord(pmx-State.left, State.width)
local my = Drawing.coord(pmy-line_cache.starty, State.width)
local my = Drawing.coord(pmy-starty, State.width)
if App.mouse_down(1) then
if Drawing.in_drawing(drawing, line_cache, pmx,pmy, State.left,State.right) then
if Drawing.in_current_drawing(State, pmx,pmy, State.left,State.right) then
if drawing.pending.mode == 'freehand' then
table.insert(drawing.pending.points, {x=mx, y=my})
elseif drawing.pending.mode == 'move' then
@ -266,7 +272,7 @@ function Drawing.update(State)
end
end
elseif State.current_drawing_mode == 'move' then
if Drawing.in_drawing(drawing, line_cache, pmx, pmy, State.left,State.right) then
if Drawing.in_current_drawing(State, pmx, pmy, State.left,State.right) then
drawing.pending.target_point.x = mx
drawing.pending.target_point.y = my
Drawing.relax_constraints(drawing, drawing.pending.target_point_index)
@ -304,7 +310,7 @@ function Drawing.mouse_release(State, x,y, mouse_button)
end
elseif State.lines.current_drawing then
local drawing = State.lines.current_drawing
local line_cache = State.line_cache[State.lines.current_drawing_index]
local starty = Text.starty(State, State.lines.current_drawing_index)
if drawing.pending then
if drawing.pending.mode == nil then
-- nothing pending
@ -313,14 +319,14 @@ function Drawing.mouse_release(State, x,y, mouse_button)
Drawing.smoothen(drawing.pending)
table.insert(drawing.shapes, drawing.pending)
elseif drawing.pending.mode == 'line' then
local mx,my = Drawing.coord(x-State.left, State.width), Drawing.coord(y-line_cache.starty, State.width)
local mx,my = Drawing.coord(x-State.left, State.width), Drawing.coord(y-starty, State.width)
if mx >= 0 and mx < 256 and my >= 0 and my < drawing.h then
drawing.pending.p2 = Drawing.find_or_insert_point(drawing.points, mx,my, State.width)
table.insert(drawing.shapes, drawing.pending)
end
elseif drawing.pending.mode == 'manhattan' then
local p1 = drawing.points[drawing.pending.p1]
local mx,my = Drawing.coord(x-State.left, State.width), Drawing.coord(y-line_cache.starty, State.width)
local mx,my = Drawing.coord(x-State.left, State.width), Drawing.coord(y-starty, State.width)
if mx >= 0 and mx < 256 and my >= 0 and my < drawing.h then
if math.abs(mx-p1.x) > math.abs(my-p1.y) then
drawing.pending.p2 = Drawing.find_or_insert_point(drawing.points, mx, p1.y, State.width)
@ -328,11 +334,11 @@ function Drawing.mouse_release(State, x,y, mouse_button)
drawing.pending.p2 = Drawing.find_or_insert_point(drawing.points, p1.x, my, State.width)
end
local p2 = drawing.points[drawing.pending.p2]
App.mouse_move(State.left+Drawing.pixels(p2.x, State.width), line_cache.starty+Drawing.pixels(p2.y, State.width))
App.mouse_move(State.left+Drawing.pixels(p2.x, State.width), starty+Drawing.pixels(p2.y, State.width))
table.insert(drawing.shapes, drawing.pending)
end
elseif drawing.pending.mode == 'polygon' then
local mx,my = Drawing.coord(x-State.left, State.width), Drawing.coord(y-line_cache.starty, State.width)
local mx,my = Drawing.coord(x-State.left, State.width), Drawing.coord(y-starty, State.width)
if mx >= 0 and mx < 256 and my >= 0 and my < drawing.h then
table.insert(drawing.pending.vertices, Drawing.find_or_insert_point(drawing.points, mx,my, State.width))
table.insert(drawing.shapes, drawing.pending)
@ -340,7 +346,7 @@ function Drawing.mouse_release(State, x,y, mouse_button)
elseif drawing.pending.mode == 'rectangle' then
assert(#drawing.pending.vertices <= 2, 'Drawing.mouse_release: rectangle has too many pending vertices')
if #drawing.pending.vertices == 2 then
local mx,my = Drawing.coord(x-State.left, State.width), Drawing.coord(y-line_cache.starty, State.width)
local mx,my = Drawing.coord(x-State.left, State.width), Drawing.coord(y-starty, State.width)
if mx >= 0 and mx < 256 and my >= 0 and my < drawing.h then
local first = drawing.points[drawing.pending.vertices[1]]
local second = drawing.points[drawing.pending.vertices[2]]
@ -355,7 +361,7 @@ function Drawing.mouse_release(State, x,y, mouse_button)
elseif drawing.pending.mode == 'square' then
assert(#drawing.pending.vertices <= 2, 'Drawing.mouse_release: square has too many pending vertices')
if #drawing.pending.vertices == 2 then
local mx,my = Drawing.coord(x-State.left, State.width), Drawing.coord(y-line_cache.starty, State.width)
local mx,my = Drawing.coord(x-State.left, State.width), Drawing.coord(y-starty, State.width)
if mx >= 0 and mx < 256 and my >= 0 and my < drawing.h then
local first = drawing.points[drawing.pending.vertices[1]]
local second = drawing.points[drawing.pending.vertices[2]]
@ -366,14 +372,14 @@ function Drawing.mouse_release(State, x,y, mouse_button)
end
end
elseif drawing.pending.mode == 'circle' then
local mx,my = Drawing.coord(x-State.left, State.width), Drawing.coord(y-line_cache.starty, State.width)
local mx,my = Drawing.coord(x-State.left, State.width), Drawing.coord(y-starty, State.width)
if mx >= 0 and mx < 256 and my >= 0 and my < drawing.h then
local center = drawing.points[drawing.pending.center]
drawing.pending.radius = round(geom.dist(center.x,center.y, mx,my))
table.insert(drawing.shapes, drawing.pending)
end
elseif drawing.pending.mode == 'arc' then
local mx,my = Drawing.coord(x-State.left, State.width), Drawing.coord(y-line_cache.starty, State.width)
local mx,my = Drawing.coord(x-State.left, State.width), Drawing.coord(y-starty, State.width)
if mx >= 0 and mx < 256 and my >= 0 and my < drawing.h then
local center = drawing.points[drawing.pending.center]
drawing.pending.end_angle = geom.angle_with_hint(center.x,center.y, mx,my, drawing.pending.end_angle)
@ -477,13 +483,15 @@ function Drawing.keychord_press(State, chord)
end
drawing.pending.mode = 'square'
elseif App.mouse_down(1) and chord == 'p' and State.current_drawing_mode == 'polygon' then
local _,drawing,line_cache = Drawing.current_drawing(State)
local mx,my = Drawing.coord(App.mouse_x()-State.left, State.width), Drawing.coord(App.mouse_y()-line_cache.starty, State.width)
local drawing_index,drawing = Drawing.current_drawing(State)
local starty = Text.starty(State, drawing_index)
local mx,my = Drawing.coord(App.mouse_x()-State.left, State.width), Drawing.coord(App.mouse_y()-starty, State.width)
local j = Drawing.find_or_insert_point(drawing.points, mx,my, State.width)
table.insert(drawing.pending.vertices, j)
elseif App.mouse_down(1) and chord == 'p' and (State.current_drawing_mode == 'rectangle' or State.current_drawing_mode == 'square') then
local _,drawing,line_cache = Drawing.current_drawing(State)
local mx,my = Drawing.coord(App.mouse_x()-State.left, State.width), Drawing.coord(App.mouse_y()-line_cache.starty, State.width)
local drawing_index,drawing = Drawing.current_drawing(State)
local starty = Text.starty(State, drawing_index)
local mx,my = Drawing.coord(App.mouse_x()-State.left, State.width), Drawing.coord(App.mouse_y()-starty, State.width)
local j = Drawing.find_or_insert_point(drawing.points, mx,my, State.width)
while #drawing.pending.vertices >= 2 do
table.remove(drawing.pending.vertices)
@ -492,9 +500,10 @@ function Drawing.keychord_press(State, chord)
elseif chord == 'C-o' and not App.mouse_down(1) then
State.current_drawing_mode = 'circle'
elseif App.mouse_down(1) and chord == 'a' and State.current_drawing_mode == 'circle' then
local _,drawing,line_cache = Drawing.current_drawing(State)
local drawing_index,drawing = Drawing.current_drawing(State)
local starty = Text.starty(State, drawing_index)
drawing.pending.mode = 'arc'
local mx,my = Drawing.coord(App.mouse_x()-State.left, State.width), Drawing.coord(App.mouse_y()-line_cache.starty, State.width)
local mx,my = Drawing.coord(App.mouse_x()-State.left, State.width), Drawing.coord(App.mouse_y()-starty, State.width)
local center = drawing.points[drawing.pending.center]
drawing.pending.radius = round(geom.dist(center.x,center.y, mx,my))
drawing.pending.start_angle = geom.angle(center.x,center.y, mx,my)
@ -510,7 +519,7 @@ function Drawing.keychord_press(State, chord)
end
drawing.pending.mode = 'circle'
elseif chord == 'C-u' and not App.mouse_down(1) then
local drawing_index,drawing,line_cache,i,p = Drawing.select_point_at_mouse(State)
local drawing_index,drawing,_,i,p = Drawing.select_point_at_mouse(State)
if drawing then
if State.previous_drawing_mode == nil then
State.previous_drawing_mode = State.current_drawing_mode
@ -521,7 +530,7 @@ function Drawing.keychord_press(State, chord)
State.lines.current_drawing = drawing
end
elseif chord == 'C-n' and not App.mouse_down(1) then
local drawing_index,drawing,line_cache,point_index,p = Drawing.select_point_at_mouse(State)
local drawing_index,drawing,_,point_index,p = Drawing.select_point_at_mouse(State)
if drawing then
if State.previous_drawing_mode == nil then
-- don't clobber
@ -619,9 +628,8 @@ function Drawing.current_drawing(State)
local x, y = App.mouse_x(), App.mouse_y()
for drawing_index,drawing in ipairs(State.lines) do
if drawing.mode == 'drawing' then
local line_cache = State.line_cache[drawing_index]
if Drawing.in_drawing(drawing, line_cache, x,y, State.left,State.right) then
return drawing_index,drawing,line_cache
if Drawing.in_drawing(State, drawing_index, x,y, State.left,State.right) then
return drawing_index,drawing
end
end
end
@ -632,12 +640,12 @@ function Drawing.select_shape_at_mouse(State)
for drawing_index,drawing in ipairs(State.lines) do
if drawing.mode == 'drawing' then
local x, y = App.mouse_x(), App.mouse_y()
local line_cache = State.line_cache[drawing_index]
if Drawing.in_drawing(drawing, line_cache, x,y, State.left,State.right) then
local mx,my = Drawing.coord(x-State.left, State.width), Drawing.coord(y-line_cache.starty, State.width)
local starty = Text.starty(State, drawing_index)
if Drawing.in_drawing(State, drawing_index, x,y, State.left,State.right) then
local mx,my = Drawing.coord(x-State.left, State.width), Drawing.coord(y-starty, State.width)
for i,shape in ipairs(drawing.shapes) do
if geom.on_shape(mx,my, drawing, shape) then
return drawing,line_cache,i,shape
return drawing,starty,i,shape
end
end
end
@ -649,12 +657,12 @@ function Drawing.select_point_at_mouse(State)
for drawing_index,drawing in ipairs(State.lines) do
if drawing.mode == 'drawing' then
local x, y = App.mouse_x(), App.mouse_y()
local line_cache = State.line_cache[drawing_index]
if Drawing.in_drawing(drawing, line_cache, x,y, State.left,State.right) then
local mx,my = Drawing.coord(x-State.left, State.width), Drawing.coord(y-line_cache.starty, State.width)
local starty = Text.starty(State, drawing_index)
if Drawing.in_drawing(State, drawing_index, x,y, State.left,State.right) then
local mx,my = Drawing.coord(x-State.left, State.width), Drawing.coord(y-starty, State.width)
for i,point in ipairs(drawing.points) do
if Drawing.near(point, mx,my, State.width) then
return drawing_index,drawing,line_cache,i,point
return drawing_index,drawing,starty,i,point
end
end
end
@ -666,8 +674,7 @@ function Drawing.select_drawing_at_mouse(State)
for drawing_index,drawing in ipairs(State.lines) do
if drawing.mode == 'drawing' then
local x, y = App.mouse_x(), App.mouse_y()
local line_cache = State.line_cache[drawing_index]
if Drawing.in_drawing(drawing, line_cache, x,y, State.left,State.right) then
if Drawing.in_drawing(State, drawing_index, x,y, State.left,State.right) then
return drawing
end
end

View File

@ -3,7 +3,7 @@
-- of specific shapes. In particular, no tests of freehand drawings.
function test_creating_drawing_saves()
App.screen.init{width=120, height=60}
App.screen.init{width=800, height=600}
Editor_state = edit.initialize_test_state()
Editor_state.filename = 'foo'
Editor_state.lines = load_array{}
@ -32,7 +32,7 @@ function test_draw_line()
edit.draw(Editor_state)
check_eq(#Editor_state.lines, 2, 'baseline/#lines')
check_eq(Editor_state.lines[1].mode, 'drawing', 'baseline/mode')
check_eq(Editor_state.line_cache[1].starty, Editor_state.top+Drawing_padding_top, 'baseline/y')
check_eq(Text.starty(Editor_state, 1), Editor_state.top+Drawing_padding_top, 'baseline/y')
check_eq(Editor_state.lines[1].h, 128, 'baseline/y')
check_eq(#Editor_state.lines[1].shapes, 0, 'baseline/#shapes')
-- draw a line
@ -77,7 +77,7 @@ function test_draw_horizontal_line()
edit.draw(Editor_state)
check_eq(#Editor_state.lines, 2, 'baseline/#lines')
check_eq(Editor_state.lines[1].mode, 'drawing', 'baseline/mode')
check_eq(Editor_state.line_cache[1].starty, Editor_state.top+Drawing_padding_top, 'baseline/y')
check_eq(Text.starty(Editor_state, 1), Editor_state.top+Drawing_padding_top, 'baseline/y')
check_eq(Editor_state.lines[1].h, 128, 'baseline/y')
check_eq(#Editor_state.lines[1].shapes, 0, 'baseline/#shapes')
-- draw a line that is more horizontal than vertical
@ -105,7 +105,7 @@ function test_draw_circle()
edit.draw(Editor_state)
check_eq(#Editor_state.lines, 2, 'baseline/#lines')
check_eq(Editor_state.lines[1].mode, 'drawing', 'baseline/mode')
check_eq(Editor_state.line_cache[1].starty, Editor_state.top+Drawing_padding_top, 'baseline/y')
check_eq(Text.starty(Editor_state, 1), Editor_state.top+Drawing_padding_top, 'baseline/y')
check_eq(Editor_state.lines[1].h, 128, 'baseline/y')
check_eq(#Editor_state.lines[1].shapes, 0, 'baseline/#shapes')
-- draw a circle
@ -134,7 +134,7 @@ function test_cancel_stroke()
edit.draw(Editor_state)
check_eq(#Editor_state.lines, 2, 'baseline/#lines')
check_eq(Editor_state.lines[1].mode, 'drawing', 'baseline/mode')
check_eq(Editor_state.line_cache[1].starty, Editor_state.top+Drawing_padding_top, 'baseline/y')
check_eq(Text.starty(Editor_state, 1), Editor_state.top+Drawing_padding_top, 'baseline/y')
check_eq(Editor_state.lines[1].h, 128, 'baseline/y')
check_eq(#Editor_state.lines[1].shapes, 0, 'baseline/#shapes')
-- start drawing a line
@ -172,7 +172,7 @@ function test_draw_circle_mid_stroke()
edit.draw(Editor_state)
check_eq(#Editor_state.lines, 2, 'baseline/#lines')
check_eq(Editor_state.lines[1].mode, 'drawing', 'baseline/mode')
check_eq(Editor_state.line_cache[1].starty, Editor_state.top+Drawing_padding_top, 'baseline/y')
check_eq(Text.starty(Editor_state, 1), Editor_state.top+Drawing_padding_top, 'baseline/y')
check_eq(Editor_state.lines[1].h, 128, 'baseline/y')
check_eq(#Editor_state.lines[1].shapes, 0, 'baseline/#shapes')
-- draw a circle
@ -200,7 +200,7 @@ function test_draw_arc()
edit.draw(Editor_state)
check_eq(#Editor_state.lines, 2, 'baseline/#lines')
check_eq(Editor_state.lines[1].mode, 'drawing', 'baseline/mode')
check_eq(Editor_state.line_cache[1].starty, Editor_state.top+Drawing_padding_top, 'baseline/y')
check_eq(Text.starty(Editor_state, 1), Editor_state.top+Drawing_padding_top, 'baseline/y')
check_eq(Editor_state.lines[1].h, 128, 'baseline/y')
check_eq(#Editor_state.lines[1].shapes, 0, 'baseline/#shapes')
-- draw an arc
@ -231,7 +231,7 @@ function test_draw_polygon()
check_eq(Editor_state.current_drawing_mode, 'line', 'baseline/drawing_mode')
check_eq(#Editor_state.lines, 2, 'baseline/#lines')
check_eq(Editor_state.lines[1].mode, 'drawing', 'baseline/mode')
check_eq(Editor_state.line_cache[1].starty, Editor_state.top+Drawing_padding_top, 'baseline/y')
check_eq(Text.starty(Editor_state, 1), Editor_state.top+Drawing_padding_top, 'baseline/y')
check_eq(Editor_state.lines[1].h, 128, 'baseline/y')
check_eq(#Editor_state.lines[1].shapes, 0, 'baseline/#shapes')
-- first point
@ -269,7 +269,7 @@ function test_draw_rectangle()
check_eq(Editor_state.current_drawing_mode, 'line', 'baseline/drawing_mode')
check_eq(#Editor_state.lines, 2, 'baseline/#lines')
check_eq(Editor_state.lines[1].mode, 'drawing', 'baseline/mode')
check_eq(Editor_state.line_cache[1].starty, Editor_state.top+Drawing_padding_top, 'baseline/y')
check_eq(Text.starty(Editor_state, 1), Editor_state.top+Drawing_padding_top, 'baseline/y')
check_eq(Editor_state.lines[1].h, 128, 'baseline/y')
check_eq(#Editor_state.lines[1].shapes, 0, 'baseline/#shapes')
-- first point
@ -313,7 +313,7 @@ function test_draw_rectangle_intermediate()
check_eq(Editor_state.current_drawing_mode, 'line', 'baseline/drawing_mode')
check_eq(#Editor_state.lines, 2, 'baseline/#lines')
check_eq(Editor_state.lines[1].mode, 'drawing', 'baseline/mode')
check_eq(Editor_state.line_cache[1].starty, Editor_state.top+Drawing_padding_top, 'baseline/y')
check_eq(Text.starty(Editor_state, 1), Editor_state.top+Drawing_padding_top, 'baseline/y')
check_eq(Editor_state.lines[1].h, 128, 'baseline/y')
check_eq(#Editor_state.lines[1].shapes, 0, 'baseline/#shapes')
-- first point
@ -349,7 +349,7 @@ function test_draw_square()
check_eq(Editor_state.current_drawing_mode, 'line', 'baseline/drawing_mode')
check_eq(#Editor_state.lines, 2, 'baseline/#lines')
check_eq(Editor_state.lines[1].mode, 'drawing', 'baseline/mode')
check_eq(Editor_state.line_cache[1].starty, Editor_state.top+Drawing_padding_top, 'baseline/y')
check_eq(Text.starty(Editor_state, 1), Editor_state.top+Drawing_padding_top, 'baseline/y')
check_eq(Editor_state.lines[1].h, 128, 'baseline/y')
check_eq(#Editor_state.lines[1].shapes, 0, 'baseline/#shapes')
-- first point

View File

@ -31,7 +31,6 @@ function edit.initialize_state(top, left, right, font, font_height, line_height)
-- string data,
-- a drawing is a table with:
-- mode = 'drawing'
-- a (y) coord in pixels (updated while painting screen),
-- a (h)eight,
-- an array of points, and
-- an array of shapes
@ -52,7 +51,6 @@ function edit.initialize_state(top, left, right, font, font_height, line_height)
-- rendering wrapped text lines needs some additional short-lived data per line:
-- startpos, the index of data the line starts rendering from, can only be >1 for topmost line on screen
-- starty, the y coord in pixels the line starts rendering from
-- fragments: snippets of the line guaranteed to not straddle screen lines
-- screen_line_starting_pos: optional array of grapheme indices if it wraps over more than one screen line
line_cache = {},
@ -66,9 +64,10 @@ function edit.initialize_state(top, left, right, font, font_height, line_height)
--
-- Make sure these coordinates are never aliased, so that changing one causes
-- action at a distance.
--
-- On lines that are drawings, pos will be nil.
screen_top1 = {line=1, pos=1}, -- position of start of screen line at top of screen
cursor1 = {line=1, pos=1}, -- position of cursor
screen_bottom1 = {line=1, pos=1}, -- position of start of screen line at bottom of screen
cursor1 = {line=1, pos=1}, -- position of cursor; must be on a text line
selection1 = {},
-- some extra state to compute selection between mouse press and release
@ -166,13 +165,11 @@ function edit.draw(State)
State.cursor_x = nil
State.cursor_y = nil
local y = State.top
local screen_bottom1 = {line=nil, pos=nil}
--? print('== draw')
for line_index = State.screen_top1.line,#State.lines do
local line = State.lines[line_index]
--? print('draw:', y, line_index, line)
if y + State.line_height > App.screen.height then break end
screen_bottom1.line = line_index
if line.mode == 'text' then
--? print('text.draw', y, line_index)
local startpos = 1
@ -195,7 +192,7 @@ function edit.draw(State)
end,
})
end
y, screen_bottom1.pos = Text.draw(State, line_index, y, startpos)
y = Text.draw(State, line_index, y, startpos)
--? print('=> y', y)
elseif line.mode == 'drawing' then
y = y+Drawing_padding_top
@ -205,7 +202,6 @@ function edit.draw(State)
assert(false, ('unknown line mode %s'):format(line.mode))
end
end
State.screen_bottom1 = screen_bottom1
if State.search_term then
Text.draw_search_bar(State)
end
@ -279,8 +275,7 @@ function edit.mouse_press(State, x,y, mouse_button)
return
end
elseif line.mode == 'drawing' then
local line_cache = State.line_cache[line_index]
if Drawing.in_drawing(line, line_cache, x, y, State.left,State.right) then
if Drawing.in_drawing(State, line_index, x, y, State.left,State.right) then
State.lines.current_drawing_index = line_index
State.lines.current_drawing = line
Drawing.before = snapshot(State, line_index)
@ -294,10 +289,7 @@ function edit.mouse_press(State, x,y, mouse_button)
State.old_cursor1 = State.cursor1
State.old_selection1 = State.selection1
State.mousepress_shift = App.shift_down()
State.selection1 = {
line=State.screen_bottom1.line,
pos=Text.pos_at_end_of_screen_line(State, State.screen_bottom1),
}
State.selection1 = Text.final_text_loc_on_screen(State)
end
function edit.mouse_release(State, x,y, mouse_button)
@ -335,7 +327,7 @@ function edit.mouse_release(State, x,y, mouse_button)
end
-- still here? mouse release is below all screen lines
State.cursor1.line, State.cursor1.pos = State.screen_bottom1.line, Text.pos_at_end_of_screen_line(State, State.screen_bottom1)
State.cursor1 = Text.final_text_loc_on_screen(State)
edit.clean_up_mouse_press(State)
--? print_and_log(('edit.mouse_release: finally selection %s,%s cursor %d,%d'):format(tostring(State.selection1.line), tostring(State.selection1.pos), State.cursor1.line, State.cursor1.pos))
end
@ -363,7 +355,7 @@ function edit.mouse_wheel_move(State, dx,dy)
Text.up(State)
end
elseif dy < 0 then
State.cursor1 = {line=State.screen_bottom1.line, pos=State.screen_bottom1.pos}
State.cursor1 = Text.screen_bottom1(State)
edit.put_cursor_on_next_text_line(State)
for i=1,math.floor(-dy) do
Text.down(State)
@ -401,7 +393,6 @@ function edit.keychord_press(State, chord, key)
Text.delete_selection(State, State.left, State.right)
end
if State.search_term then
for _,line_cache in ipairs(State.line_cache) do line_cache.starty = nil end -- just in case we scroll
if chord == 'escape' then
State.search_term = nil
State.cursor1 = State.search_backup.cursor
@ -506,7 +497,6 @@ function edit.keychord_press(State, chord, key)
record_undo_event(State, {before=before, after=snapshot(State, before_line, State.cursor1.line)})
-- dispatch to drawing or text
elseif App.mouse_down(1) or chord:sub(1,2) == 'C-' then
-- DON'T reset line_cache.starty here
local drawing_index, drawing = Drawing.current_drawing(State)
if drawing_index then
local before = snapshot(State, drawing_index)
@ -543,7 +533,6 @@ function edit.keychord_press(State, chord, key)
end
schedule_save(State)
else
for _,line_cache in ipairs(State.line_cache) do line_cache.starty = nil end -- just in case we scroll
Text.keychord_press(State, chord)
end
end
@ -598,6 +587,7 @@ end
function edit.run_after_mouse_click(State, x,y, mouse_button)
App.fake_mouse_press(x,y, mouse_button)
edit.mouse_press(State, x,y, mouse_button)
edit.draw(State)
App.fake_mouse_release(x,y, mouse_button)
edit.mouse_release(State, x,y, mouse_button)
App.screen.contents = {}

View File

@ -1,8 +1,8 @@
function draw_help_without_mouse_pressed(State, drawing_index)
local drawing = State.lines[drawing_index]
local line_cache = State.line_cache[drawing_index]
local starty = Text.starty(State, drawing_index)
App.color(Help_color)
local y = line_cache.starty+10
local y = starty+10
love.graphics.print("Things you can do:", State.left+30,y)
y = y + State.line_height
love.graphics.print("* Press the mouse button to start drawing a "..current_shape(State), State.left+30,y)
@ -48,14 +48,14 @@ function draw_help_without_mouse_pressed(State, drawing_index)
love.graphics.print("Press 'esc' now to hide this message", State.left+30,y)
y = y + State.line_height
App.color(Help_background_color)
love.graphics.rectangle('fill', State.left,line_cache.starty, State.width, math.max(Drawing.pixels(drawing.h, State.width),y-line_cache.starty))
love.graphics.rectangle('fill', State.left,starty, State.width, math.max(Drawing.pixels(drawing.h, State.width),y-starty))
end
function draw_help_with_mouse_pressed(State, drawing_index)
local drawing = State.lines[drawing_index]
local line_cache = State.line_cache[drawing_index]
local starty = Text.starty(State, drawing_index)
App.color(Help_color)
local y = line_cache.starty+10
local y = starty+10
love.graphics.print("You're currently drawing a "..current_shape(State, drawing.pending), State.left+30,y)
y = y + State.line_height
love.graphics.print('Things you can do now:', State.left+30,y)
@ -129,7 +129,7 @@ function draw_help_with_mouse_pressed(State, drawing_index)
y = y + State.line_height
end
App.color(Help_background_color)
love.graphics.rectangle('fill', State.left,line_cache.starty, State.width, math.max(Drawing.pixels(drawing.h, State.width),y-line_cache.starty))
love.graphics.rectangle('fill', State.left,starty, State.width, math.max(Drawing.pixels(drawing.h, State.width),y-starty))
end
function current_shape(State, shape)

View File

@ -62,7 +62,8 @@ function Text.search_next(State)
State.screen_top1.line = State.search_backup.screen_top.line
State.screen_top1.pos = State.search_backup.screen_top.pos
end
if Text.lt1(State.cursor1, State.screen_top1) or Text.lt1(State.screen_bottom1, State.cursor1) then
local screen_bottom1 = Text.screen_bottom1(State)
if Text.lt1(State.cursor1, State.screen_top1) or Text.lt1(screen_bottom1, State.cursor1) then
State.screen_top1.line = State.cursor1.line
local pos = Text.pos_at_start_of_screen_line(State, State.cursor1)
State.screen_top1.pos = pos
@ -115,7 +116,8 @@ function Text.search_previous(State)
State.screen_top1.line = State.search_backup.screen_top.line
State.screen_top1.pos = State.search_backup.screen_top.pos
end
if Text.lt1(State.cursor1, State.screen_top1) or Text.lt1(State.screen_bottom1, State.cursor1) then
local screen_bottom1 = Text.screen_bottom1(State)
if Text.lt1(State.cursor1, State.screen_top1) or Text.lt1(screen_bottom1, State.cursor1) then
State.screen_top1.line = State.cursor1.line
local pos = Text.pos_at_start_of_screen_line(State, State.cursor1)
State.screen_top1.pos = pos

View File

@ -69,7 +69,7 @@ end
function Text.mouse_pos(State)
local x,y = App.mouse_x(), App.mouse_y()
if y < State.line_cache[State.screen_top1.line].starty then
if y < State.top then
return State.screen_top1.line, State.screen_top1.pos
end
for line_index,line in ipairs(State.lines) do
@ -79,7 +79,8 @@ function Text.mouse_pos(State)
end
end
end
return State.screen_bottom1.line, Text.pos_at_end_of_screen_line(State, State.screen_bottom1)
local screen_bottom1 = Text.screen_bottom1(State)
return screen_bottom1.line, Text.pos_at_end_of_screen_line(State, screen_bottom1)
end
function Text.cut_selection(State)

View File

@ -307,7 +307,6 @@ function source.mouse_press(x,y, mouse_button)
return
end
log_browser.mouse_press(Log_browser_state, x,y, mouse_button)
for _,line_cache in ipairs(Editor_state.line_cache) do line_cache.starty = nil end -- just in case we scroll
end
end

View File

@ -33,7 +33,6 @@ function edit.initialize_state(top, left, right, font, font_height, line_height)
-- string data,
-- a drawing is a table with:
-- mode = 'drawing'
-- a (y) coord in pixels (updated while painting screen),
-- a (h)eight,
-- an array of points, and
-- an array of shapes
@ -54,7 +53,6 @@ function edit.initialize_state(top, left, right, font, font_height, line_height)
-- rendering wrapped text lines needs some additional short-lived data per line:
-- startpos, the index of data the line starts rendering from, can only be >1 for topmost line on screen
-- starty, the y coord in pixels the line starts rendering from
-- fragments: snippets of the line guaranteed to not straddle screen lines
-- screen_line_starting_pos: optional array of grapheme indices if it wraps over more than one screen line
line_cache = {},
@ -68,9 +66,10 @@ function edit.initialize_state(top, left, right, font, font_height, line_height)
--
-- Make sure these coordinates are never aliased, so that changing one causes
-- action at a distance.
--
-- On lines that are drawings, pos will be nil.
screen_top1 = {line=1, pos=1}, -- position of start of screen line at top of screen
cursor1 = {line=1, pos=1}, -- position of cursor
screen_bottom1 = {line=1, pos=1}, -- position of start of screen line at bottom of screen
cursor1 = {line=1, pos=1}, -- position of cursor; must be on a text line
selection1 = {},
-- some extra state to compute selection between mouse press and release
@ -165,13 +164,11 @@ function edit.draw(State, hide_cursor, show_line_numbers)
State.cursor_x = nil
State.cursor_y = nil
local y = State.top
local screen_bottom1 = {line=nil, pos=nil}
--? print('== draw')
for line_index = State.screen_top1.line,#State.lines do
local line = State.lines[line_index]
--? print('draw:', y, line_index, line)
if y + State.line_height > App.screen.height then break end
screen_bottom1.line = line_index
if line.mode == 'text' then
--? print('text.draw', y, line_index)
local startpos = 1
@ -198,7 +195,7 @@ function edit.draw(State, hide_cursor, show_line_numbers)
end,
})
end
y, screen_bottom1.pos = Text.draw(State, line_index, y, startpos, hide_cursor, show_line_numbers)
y = Text.draw(State, line_index, y, startpos, hide_cursor, show_line_numbers)
--? print('=> y', y)
elseif line.mode == 'drawing' then
y = y+Drawing_padding_top
@ -208,7 +205,6 @@ function edit.draw(State, hide_cursor, show_line_numbers)
assert(false, ('unknown line mode %s'):format(line.mode))
end
end
State.screen_bottom1 = screen_bottom1
if State.search_term then
Text.draw_search_bar(State)
end
@ -281,8 +277,7 @@ function edit.mouse_press(State, x,y, mouse_button)
return
end
elseif line.mode == 'drawing' then
local line_cache = State.line_cache[line_index]
if Drawing.in_drawing(line, line_cache, x, y, State.left,State.right) then
if Drawing.in_drawing(State, line_index, x, y, State.left,State.right) then
State.lines.current_drawing_index = line_index
State.lines.current_drawing = line
Drawing.before = snapshot(State, line_index)
@ -296,10 +291,7 @@ function edit.mouse_press(State, x,y, mouse_button)
State.old_cursor1 = State.cursor1
State.old_selection1 = State.selection1
State.mousepress_shift = App.shift_down()
State.selection1 = {
line=State.screen_bottom1.line,
pos=Text.pos_at_end_of_screen_line(State, State.screen_bottom1),
}
State.selection1 = Text.final_text_loc_on_screen(State)
end
function edit.mouse_release(State, x,y, mouse_button)
@ -337,7 +329,7 @@ function edit.mouse_release(State, x,y, mouse_button)
end
-- still here? mouse release is below all screen lines
State.cursor1.line, State.cursor1.pos = State.screen_bottom1.line, Text.pos_at_end_of_screen_line(State, State.screen_bottom1)
State.cursor1 = Text.final_text_loc_on_screen(State)
edit.clean_up_mouse_press(State)
--? print_and_log(('edit.mouse_release: finally selection %s,%s cursor %d,%d'):format(tostring(State.selection1.line), tostring(State.selection1.pos), State.cursor1.line, State.cursor1.pos))
end
@ -365,7 +357,7 @@ function edit.mouse_wheel_move(State, dx,dy)
Text.up(State)
end
elseif dy < 0 then
State.cursor1 = {line=State.screen_bottom1.line, pos=State.screen_bottom1.pos}
State.cursor1 = Text.screen_bottom1(State)
edit.put_cursor_on_next_text_line(State)
for i=1,math.floor(-dy) do
Text.down(State)
@ -399,11 +391,10 @@ function edit.keychord_press(State, chord, key)
-- printable character created using shift key => delete selection
-- (we're not creating any ctrl-shift- or alt-shift- combinations using regular/printable keys)
(not App.shift_down() or utf8.len(key) == 1) and
chord ~= 'C-a' and chord ~= 'C-c' and chord ~= 'C-x' and chord ~= 'backspace' and chord ~= 'delete' and chord ~= 'C-z' and chord ~= 'C-y' and not App.is_cursor_movement(chord) then
chord ~= 'C-a' and chord ~= 'C-c' and chord ~= 'C-x' and chord ~= 'backspace' and chord ~= 'delete' and chord ~= 'C-z' and chord ~= 'C-y' and not App.is_cursor_movement(key) then
Text.delete_selection(State, State.left, State.right)
end
if State.search_term then
for _,line_cache in ipairs(State.line_cache) do line_cache.starty = nil end -- just in case we scroll
if chord == 'escape' then
State.search_term = nil
State.cursor1 = State.search_backup.cursor
@ -508,7 +499,6 @@ function edit.keychord_press(State, chord, key)
record_undo_event(State, {before=before, after=snapshot(State, before_line, State.cursor1.line)})
-- dispatch to drawing or text
elseif App.mouse_down(1) or chord:sub(1,2) == 'C-' then
-- DON'T reset line_cache.starty here
local drawing_index, drawing = Drawing.current_drawing(State)
if drawing_index then
local before = snapshot(State, drawing_index)
@ -545,7 +535,6 @@ function edit.keychord_press(State, chord, key)
end
schedule_save(State)
else
for _,line_cache in ipairs(State.line_cache) do line_cache.starty = nil end -- just in case we scroll
Text.keychord_press(State, chord)
end
end
@ -600,6 +589,7 @@ end
function edit.run_after_mouse_click(State, x,y, mouse_button)
App.fake_mouse_press(x,y, mouse_button)
edit.mouse_press(State, x,y, mouse_button)
edit.draw(State)
App.fake_mouse_release(x,y, mouse_button)
edit.mouse_release(State, x,y, mouse_button)
App.screen.contents = {}

View File

@ -69,7 +69,7 @@ end
function Text.mouse_pos(State)
local x,y = App.mouse_x(), App.mouse_y()
if y < State.line_cache[State.screen_top1.line].starty then
if y < State.top then
return State.screen_top1.line, State.screen_top1.pos
end
for line_index,line in ipairs(State.lines) do
@ -79,7 +79,8 @@ function Text.mouse_pos(State)
end
end
end
return State.screen_bottom1.line, Text.pos_at_end_of_screen_line(State, State.screen_bottom1)
local screen_bottom1 = Text.screen_bottom1(State)
return screen_bottom1.line, Text.pos_at_end_of_screen_line(State, screen_bottom1)
end
function Text.cut_selection(State)

View File

@ -2,14 +2,12 @@
Text = {}
-- draw a line starting from startpos to screen at y between State.left and State.right
-- return y for the next line, and position of start of final screen line drawn
-- return y for the next line
function Text.draw(State, line_index, y, startpos, hide_cursor, show_line_numbers)
local line = State.lines[line_index]
local line_cache = State.line_cache[line_index]
line_cache.starty = y
line_cache.startpos = startpos
-- wrap long lines
local final_screen_line_starting_pos = startpos -- track value to return
Text.populate_screen_line_starting_pos(State, line_index)
Text.populate_link_offsets(State, line_index)
if show_line_numbers then
@ -24,7 +22,6 @@ function Text.draw(State, line_index, y, startpos, hide_cursor, show_line_number
-- render nothing
--? print('skipping', screen_line)
else
final_screen_line_starting_pos = pos
local screen_line = Text.screen_line(line, line_cache, i)
--? print('text.draw:', screen_line, 'at', line_index,pos, 'after', x,y)
local frag_len = utf8.len(screen_line)
@ -84,7 +81,7 @@ function Text.draw(State, line_index, y, startpos, hide_cursor, show_line_number
end
end
end
return y, final_screen_line_starting_pos
return y
end
function Text.screen_line(line, line_cache, i)
@ -208,7 +205,7 @@ function Text.text_input(State, t)
end
end
local before = snapshot(State, State.cursor1.line)
--? print(State.screen_top1.line, State.screen_top1.pos, State.cursor1.line, State.cursor1.pos, State.screen_bottom1.line, State.screen_bottom1.pos)
--? print(State.screen_top1.line, State.screen_top1.pos, State.cursor1.line, State.cursor1.pos)
Text.insert_at_cursor(State, t)
if State.cursor_y > App.screen.height - State.line_height then
Text.populate_screen_line_starting_pos(State, State.cursor1.line)
@ -241,12 +238,12 @@ function Text.keychord_press(State, chord)
record_undo_event(State, {before=before, after=snapshot(State, before_line, State.cursor1.line)})
elseif chord == 'tab' then
local before = snapshot(State, State.cursor1.line)
--? print(State.screen_top1.line, State.screen_top1.pos, State.cursor1.line, State.cursor1.pos, State.screen_bottom1.line, State.screen_bottom1.pos)
--? print(State.screen_top1.line, State.screen_top1.pos, State.cursor1.line, State.cursor1.pos)
Text.insert_at_cursor(State, '\t')
if State.cursor_y > App.screen.height - State.line_height then
Text.populate_screen_line_starting_pos(State, State.cursor1.line)
Text.snap_cursor_to_bottom_of_screen(State, State.left, State.right)
--? print('=>', State.screen_top1.line, State.screen_top1.pos, State.cursor1.line, State.cursor1.pos, State.screen_bottom1.line, State.screen_bottom1.pos)
--? print('=>', State.screen_top1.line, State.screen_top1.pos, State.cursor1.line, State.cursor1.pos)
end
schedule_save(State)
record_undo_event(State, {before=before, after=snapshot(State, State.cursor1.line)})
@ -427,37 +424,79 @@ function Text.insert_return(State)
end
function Text.pageup(State)
--? print('pageup')
-- duplicate some logic from love.draw
local top2 = Text.to2(State, State.screen_top1)
--? print(App.screen.height)
local y = App.screen.height - State.line_height
while y >= State.top do
--? print(y, top2.line, top2.screen_line, top2.screen_pos)
if State.screen_top1.line == 1 and State.screen_top1.pos == 1 then break end
if State.lines[State.screen_top1.line].mode == 'text' then
y = y - State.line_height
elseif State.lines[State.screen_top1.line].mode == 'drawing' then
y = y - Drawing_padding_height - Drawing.pixels(State.lines[State.screen_top1.line].h, State.width)
end
top2 = Text.previous_screen_line(State, top2)
end
State.screen_top1 = Text.to1(State, top2)
State.screen_top1 = Text.previous_screen_top1(State)
State.cursor1 = {line=State.screen_top1.line, pos=State.screen_top1.pos}
Text.move_cursor_down_to_next_text_line_while_scrolling_again_if_necessary(State)
--? print(State.cursor1.line, State.cursor1.pos, State.screen_top1.line, State.screen_top1.pos)
--? print('pageup end')
Text.redraw_all(State) -- if we're scrolling, reclaim all fragments to avoid memory leaks
end
-- return the top y coordinate of a given line_index,
-- or nil if no part of it is on screen
function Text.starty(State, line_index)
-- duplicate some logic from love.draw
-- does not modify State (except to populate line_cache)
if line_index < State.screen_top1.line then return end
local loc2 = Text.to2(State, State.screen_top1)
local y = State.top
while true do
if State.lines[loc2.line].mode == 'drawing' then
y = y + Drawing_padding_top
end
if loc2.line == line_index then return y end
if State.lines[loc2.line].mode == 'text' then
y = y + State.line_height
elseif State.lines[loc2.line].mode == 'drawing' then
y = y + Drawing.pixels(State.lines[loc2.line].h, State.width) + Drawing_padding_bottom
end
if y + State.line_height > App.screen.height then break end
local next_loc2 = Text.next_screen_line(State, loc2)
if Text.eq2(next_loc2, loc2) then break end -- end of file
loc2 = next_loc2
end
end
function Text.previous_screen_top1(State)
-- duplicate some logic from love.draw
-- does not modify State (except to populate line_cache)
local loc2 = Text.to2(State, State.screen_top1)
local y = App.screen.height - State.line_height
while y >= State.top do
if loc2.line == 1 and loc2.screen_line == 1 and loc2.screen_pos == 1 then break end
if State.lines[loc2.line].mode == 'text' then
y = y - State.line_height
elseif State.lines[loc2.line].mode == 'drawing' then
y = y - Drawing_padding_height - Drawing.pixels(State.lines[loc2.line].h, State.width)
end
loc2 = Text.previous_screen_line(State, loc2)
end
return Text.to1(State, loc2)
end
function Text.pagedown(State)
--? print('pagedown')
State.screen_top1 = {line=State.screen_bottom1.line, pos=State.screen_bottom1.pos}
--? print('setting top to', State.screen_top1.line, State.screen_top1.pos)
State.screen_top1 = Text.screen_bottom1(State)
State.cursor1 = {line=State.screen_top1.line, pos=State.screen_top1.pos}
Text.move_cursor_down_to_next_text_line_while_scrolling_again_if_necessary(State)
--? print('top now', State.screen_top1.line)
Text.redraw_all(State) -- if we're scrolling, reclaim all fragments to avoid memory leaks
--? print('pagedown end')
end
-- return the location of the start of the bottom-most line on screen
function Text.screen_bottom1(State)
-- duplicate some logic from love.draw
-- does not modify State (except to populate line_cache)
local loc2 = Text.to2(State, State.screen_top1)
local y = State.top
while true do
if State.lines[loc2.line].mode == 'text' then
y = y + State.line_height
elseif State.lines[loc2.line].mode == 'drawing' then
y = y + Drawing_padding_height + Drawing.pixels(State.lines[loc2.line].h, State.width)
end
if y + State.line_height > App.screen.height then break end
local next_loc2 = Text.next_screen_line(State, loc2)
if Text.eq2(next_loc2, loc2) then break end
loc2 = next_loc2
end
return Text.to1(State, loc2)
end
function Text.up(State)
@ -505,7 +544,7 @@ end
function Text.down(State)
assert(State.lines[State.cursor1.line].mode == 'text', 'line is not text')
--? print('down', State.cursor1.line, State.cursor1.pos, State.screen_top1.line, State.screen_top1.pos, State.screen_bottom1.line, State.screen_bottom1.pos)
--? print('down', State.cursor1.line, State.cursor1.pos, State.screen_top1.line, State.screen_top1.pos)
assert(State.cursor1.pos, 'cursor has no pos')
if Text.cursor_at_final_screen_line(State) then
-- line is done, skip to next text line
@ -522,7 +561,9 @@ function Text.down(State)
break
end
end
if State.cursor1.line > State.screen_bottom1.line then
local screen_bottom1 = Text.screen_bottom1(State)
--? print('down 2', State.cursor1.line, State.cursor1.pos, State.screen_top1.line, State.screen_top1.pos, screen_bottom1.line, screen_bottom1.pos)
if State.cursor1.line > screen_bottom1.line then
--? print('screen top before:', State.screen_top1.line, State.screen_top1.pos)
--? print('scroll up preserving cursor')
Text.snap_cursor_to_bottom_of_screen(State)
@ -530,7 +571,8 @@ function Text.down(State)
end
else
-- move down one screen line in current line
local scroll_down = Text.le1(State.screen_bottom1, State.cursor1)
local screen_bottom1 = Text.screen_bottom1(State)
local scroll_down = Text.le1(screen_bottom1, State.cursor1)
--? print('cursor is NOT at final screen line of its line')
local screen_line_starting_pos, screen_line_index = Text.pos_at_start_of_screen_line(State, State.cursor1)
Text.populate_screen_line_starting_pos(State, State.cursor1.line)
@ -546,7 +588,7 @@ function Text.down(State)
--? print('screen top after:', State.screen_top1.line, State.screen_top1.pos)
end
end
--? print('=>', State.cursor1.line, State.cursor1.pos, State.screen_top1.line, State.screen_top1.pos, State.screen_bottom1.line, State.screen_bottom1.pos)
--? print('=>', State.cursor1.line, State.cursor1.pos, State.screen_top1.line, State.screen_top1.pos)
end
function Text.start_of_line(State)
@ -683,6 +725,7 @@ function Text.pos_at_start_of_screen_line(State, loc1)
end
function Text.pos_at_end_of_screen_line(State, loc1)
assert(State.lines[loc1.line].mode == 'text')
Text.populate_screen_line_starting_pos(State, loc1.line)
local line_cache = State.line_cache[loc1.line]
local most_recent_final_pos = utf8.len(State.lines[loc1.line].data)+1
@ -696,6 +739,25 @@ function Text.pos_at_end_of_screen_line(State, loc1)
assert(false, ('invalid pos %d'):format(loc1.pos))
end
function Text.final_text_loc_on_screen(State)
local screen_bottom1 = Text.screen_bottom1(State)
if State.lines[screen_bottom1.line].mode == 'text' then
return {
line=screen_bottom1.line,
pos=Text.pos_at_end_of_screen_line(State, screen_bottom1),
}
end
local loc2 = Text.to2(State, screen_bottom1)
while true do
if State.lines[loc2.line].mode == 'text' then break end
assert(loc2.line > 1 or loc2.screen_line > 1 and loc2.screen_pos > 1) -- elsewhere we're making sure there's always at least one text line on screen
loc2 = Text.previous_screen_line(State, loc2)
end
local result = Text.to1(State, loc2)
result.pos = Text.pos_at_end_of_screen_line(State, result)
return result
end
function Text.cursor_at_final_screen_line(State)
Text.populate_screen_line_starting_pos(State, State.cursor1.line)
local screen_lines = State.line_cache[State.cursor1.line].screen_line_starting_pos
@ -736,7 +798,7 @@ function Text.snap_cursor_to_bottom_of_screen(State)
--? print('to2: =>', top2.line, top2.screen_line, top2.screen_pos)
-- slide to start of screen line
top2.screen_pos = 1 -- start of screen line
--? print('snap', State.screen_top1.line, State.screen_top1.pos, State.cursor1.line, State.cursor1.pos, State.screen_bottom1.line, State.screen_bottom1.pos)
--? print('snap', State.screen_top1.line, State.screen_top1.pos, State.cursor1.line, State.cursor1.pos)
--? print('cursor pos '..tostring(State.cursor1.pos)..' is on the #'..tostring(top2.screen_line)..' screen line down')
local y = App.screen.height - State.line_height
-- duplicate some logic from love.draw
@ -766,26 +828,28 @@ function Text.snap_cursor_to_bottom_of_screen(State)
--? print('top2 finally:', top2.line, top2.screen_line, top2.screen_pos)
State.screen_top1 = Text.to1(State, top2)
--? print('top1 finally:', State.screen_top1.line, State.screen_top1.pos)
--? print('snap =>', State.screen_top1.line, State.screen_top1.pos, State.cursor1.line, State.cursor1.pos, State.screen_bottom1.line, State.screen_bottom1.pos)
--? print('snap =>', State.screen_top1.line, State.screen_top1.pos, State.cursor1.line, State.cursor1.pos)
Text.redraw_all(State) -- if we're scrolling, reclaim all fragments to avoid memory leaks
end
function Text.in_line(State, line_index, x,y)
local line = State.lines[line_index]
local line_cache = State.line_cache[line_index]
if line_cache.starty == nil then return false end -- outside current page
if y < line_cache.starty then return false end
local starty = Text.starty(State, line_index)
if starty == nil then return false end -- outside current page
if y < starty then return false end
Text.populate_screen_line_starting_pos(State, line_index)
return y < line_cache.starty + State.line_height*(#line_cache.screen_line_starting_pos - Text.screen_line_index(line_cache.screen_line_starting_pos, line_cache.startpos) + 1)
return y < starty + State.line_height*(#line_cache.screen_line_starting_pos - Text.screen_line_index(line_cache.screen_line_starting_pos, line_cache.startpos) + 1)
end
-- convert mx,my in pixels to schema-1 coordinates
function Text.to_pos_on_line(State, line_index, mx, my)
local line = State.lines[line_index]
local line_cache = State.line_cache[line_index]
assert(my >= line_cache.starty, 'failed to map y pixel to line')
local starty = Text.starty(State, line_index)
assert(my >= starty, 'failed to map y pixel to line')
-- duplicate some logic from Text.draw
local y = line_cache.starty
local y = starty
local start_screen_line_index = Text.screen_line_index(line_cache.screen_line_starting_pos, line_cache.startpos)
for screen_line_index = start_screen_line_index,#line_cache.screen_line_starting_pos do
local screen_line_starting_pos = line_cache.screen_line_starting_pos[screen_line_index]
@ -971,6 +1035,10 @@ function Text.le1(a, b)
return a.pos <= b.pos
end
function Text.eq2(a, b)
return a.line == b.line and a.screen_line == b.screen_line and a.screen_pos == b.screen_pos
end
function Text.offset(s, pos1)
if pos1 == 1 then return 1 end
local result = utf8.offset(s, pos1)
@ -994,6 +1062,22 @@ function Text.previous_screen_line(State, loc2)
end
end
function Text.next_screen_line(State, loc2)
if State.lines[loc2.line].mode == 'drawing' then
return {line=loc2.line+1, screen_line=1, screen_pos=1}
end
Text.populate_screen_line_starting_pos(State, loc2.line)
if loc2.screen_line >= #State.line_cache[loc2.line].screen_line_starting_pos then
if loc2.line < #State.lines then
return {line=loc2.line+1, screen_line=1, screen_pos=1}
else
return loc2
end
else
return {line=loc2.line, screen_line=loc2.screen_line+1, screen_pos=1}
end
end
-- resize helper
function Text.tweak_screen_top_and_cursor(State)
if State.screen_top1.pos == 1 then return end
@ -1017,16 +1101,12 @@ function Text.tweak_screen_top_and_cursor(State)
end
end
-- make sure cursor is on screen
local screen_bottom1 = Text.screen_bottom1(State)
if Text.lt1(State.cursor1, State.screen_top1) then
State.cursor1 = {line=State.screen_top1.line, pos=State.screen_top1.pos}
elseif State.cursor1.line >= State.screen_bottom1.line then
--? print('too low')
elseif State.cursor1.line >= screen_bottom1.line then
if Text.cursor_out_of_screen(State) then
--? print('tweak')
State.cursor1 = {
line=State.screen_bottom1.line,
pos=Text.to_pos_on_line(State, State.screen_bottom1.line, State.right-5, App.screen.height-5),
}
State.cursor1 = Text.final_text_loc_on_screen(State)
end
end
end
@ -1035,11 +1115,6 @@ end
function Text.cursor_out_of_screen(State)
edit.draw(State)
return State.cursor_y == nil
-- this approach is cheaper and almost works, except on the final screen
-- where file ends above bottom of screen
--? local botpos = Text.pos_at_start_of_screen_line(State, State.cursor1)
--? local botline1 = {line=State.cursor1.line, pos=botpos}
--? return Text.lt1(State.screen_bottom1, botline1)
end
function Text.redraw_all(State)

View File

@ -16,7 +16,7 @@ function test_initial_state()
end
function test_click_to_create_drawing()
App.screen.init{width=120, height=60}
App.screen.init{width=800, height=600}
Editor_state = edit.initialize_test_state()
Editor_state.lines = load_array{}
Text.redraw_all(Editor_state)
@ -75,7 +75,6 @@ function test_press_ctrl()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
edit.run_after_keychord(Editor_state, 'C-m', 'm')
end
@ -232,7 +231,7 @@ function test_skip_multiple_spaces_to_next_word()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=4} -- at the start of second word
edit.draw(Editor_state)
edit.run_after_keychord(Editor_state, 'M-right', 'right')
edit.run_after_keychord(Editor_state, 'M-right', 'right')
check_eq(Editor_state.cursor1.pos, 9, 'check')
end
@ -243,7 +242,7 @@ function test_move_past_end_of_word_on_next_line()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=8}
edit.draw(Editor_state)
edit.run_after_keychord(Editor_state, 'M-right', 'right')
edit.run_after_keychord(Editor_state, 'M-right', 'right')
check_eq(Editor_state.cursor1.line, 2, 'line')
check_eq(Editor_state.cursor1.pos, 4, 'pos')
end
@ -255,9 +254,8 @@ function test_click_moves_cursor()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
Editor_state.selection1 = {}
edit.draw(Editor_state) -- populate line_cache.starty for each line Editor_state.line_cache
edit.draw(Editor_state) -- populate line_cache.startpos for each line
edit.run_after_mouse_release(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)
check_eq(Editor_state.cursor1.line, 1, 'cursor:line')
check_eq(Editor_state.cursor1.pos, 2, 'cursor:pos')
@ -274,7 +272,6 @@ function test_click_to_left_of_line()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=3}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
Editor_state.selection1 = {}
-- click to the left of the line
edit.draw(Editor_state)
@ -294,7 +291,6 @@ function test_click_takes_margins_into_account()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=2, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
Editor_state.selection1 = {}
-- click on the other line
edit.draw(Editor_state)
@ -313,7 +309,6 @@ function test_click_on_empty_line()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=2, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
Editor_state.selection1 = {}
-- click on the empty line
edit.draw(Editor_state)
@ -332,7 +327,6 @@ function test_click_below_all_lines()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
Editor_state.selection1 = {}
-- click below first line
edit.draw(Editor_state)
@ -350,7 +344,6 @@ function test_draw_text()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'abc', 'screen:1')
@ -367,7 +360,6 @@ function test_draw_wrapping_text()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'abc', 'screen:1')
@ -384,7 +376,6 @@ function test_draw_word_wrapping_text()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'abc ', 'screen:1')
@ -402,7 +393,6 @@ function test_click_on_wrapping_line()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=20}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
-- click on the other line
edit.draw(Editor_state)
edit.run_after_mouse_click(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)
@ -421,7 +411,6 @@ function test_click_on_wrapping_line_takes_margins_into_account()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=20}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
-- click on the other line
edit.draw(Editor_state)
edit.run_after_mouse_click(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)
@ -439,7 +428,6 @@ function test_draw_text_wrapping_within_word()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'abcd ', 'screen:1')
@ -457,7 +445,6 @@ function test_draw_wrapping_text_containing_non_ascii()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'mad', 'screen:1')
@ -476,7 +463,6 @@ function test_click_past_end_of_screen_line()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'madam ', 'baseline/screen:1')
@ -499,7 +485,6 @@ function test_click_on_wrapping_line_rendered_from_partway_at_top_of_screen()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=8}
Editor_state.screen_top1 = {line=1, pos=7}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, "I'm ad", 'baseline/screen:2')
@ -520,7 +505,6 @@ function test_click_past_end_of_wrapping_line()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'madam ', 'baseline/screen:1')
@ -544,7 +528,6 @@ function test_click_past_end_of_wrapping_line_containing_non_ascii()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'madam ', 'baseline/screen:1')
@ -569,7 +552,6 @@ function test_click_past_end_of_word_wrapping_line()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'the quick brown fox ', 'baseline/screen:1')
@ -588,11 +570,10 @@ function test_select_text()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
-- select a letter
App.fake_key_press('lshift')
edit.run_after_keychord(Editor_state, 'S-right', 'right')
edit.run_after_keychord(Editor_state, 'S-right', 'right')
App.fake_key_release('lshift')
edit.key_release(Editor_state, 'lshift')
-- selection persists even after shift is released
@ -611,10 +592,9 @@ function test_cursor_movement_without_shift_resets_selection()
Editor_state.cursor1 = {line=1, pos=1}
Editor_state.selection1 = {line=1, pos=2}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
-- press an arrow key without shift
edit.run_after_keychord(Editor_state, 'right', 'right')
edit.run_after_keychord(Editor_state, 'right', 'right')
-- no change to data, selection is reset
check_nil(Editor_state.selection1.line, 'check')
check_eq(Editor_state.lines[1].data, 'abc', 'data')
@ -629,7 +609,6 @@ function test_edit_deletes_selection()
Editor_state.cursor1 = {line=1, pos=1}
Editor_state.selection1 = {line=1, pos=2}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
-- press a key
edit.run_after_text_input(Editor_state, 'x')
@ -646,7 +625,6 @@ function test_edit_with_shift_key_deletes_selection()
Editor_state.cursor1 = {line=1, pos=1}
Editor_state.selection1 = {line=1, pos=2}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
-- mimic precise keypresses for a capital letter
App.fake_key_press('lshift')
@ -668,7 +646,6 @@ function test_copy_does_not_reset_selection()
Editor_state.cursor1 = {line=1, pos=1}
Editor_state.selection1 = {line=1, pos=2}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
-- copy selection
edit.run_after_keychord(Editor_state, 'C-c', 'c')
@ -686,7 +663,6 @@ function test_cut()
Editor_state.cursor1 = {line=1, pos=1}
Editor_state.selection1 = {line=1, pos=2}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
-- press a key
edit.run_after_keychord(Editor_state, 'C-x', 'x')
@ -704,7 +680,6 @@ function test_paste_replaces_selection()
Editor_state.cursor1 = {line=2, pos=1}
Editor_state.selection1 = {line=1, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
-- set clipboard
App.clipboard = 'xyz'
@ -723,7 +698,6 @@ function test_deleting_selection_may_scroll()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=3, pos=2}
Editor_state.screen_top1 = {line=2, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'def', 'baseline/screen:1')
@ -747,7 +721,6 @@ function test_edit_wrapping_text()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=2, pos=4}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
edit.run_after_text_input(Editor_state, 'g')
local y = Editor_state.top
@ -766,7 +739,6 @@ function test_insert_newline()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=2}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'abc', 'baseline/screen:1')
@ -795,7 +767,6 @@ function test_insert_newline_at_start_of_line()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
-- hitting the enter key splits the line
edit.run_after_keychord(Editor_state, 'return', 'return')
check_eq(Editor_state.cursor1.line, 2, 'cursor:line')
@ -812,7 +783,6 @@ function test_insert_from_clipboard()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=2}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'abc', 'baseline/screen:1')
@ -841,9 +811,8 @@ function test_select_text_using_mouse()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
Editor_state.selection1 = {}
edit.draw(Editor_state) -- populate line_cache.starty for each line Editor_state.line_cache
edit.draw(Editor_state) -- populate line_cache.startpos for each line
-- press and hold on first location
edit.run_after_mouse_press(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)
-- drag and release somewhere else
@ -861,9 +830,8 @@ function test_select_text_using_mouse_starting_above_text()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
Editor_state.selection1 = {}
edit.draw(Editor_state) -- populate line_cache.starty for each line Editor_state.line_cache
edit.draw(Editor_state) -- populate line_cache.startpos for each line
-- press mouse above first line of text
edit.run_after_mouse_press(Editor_state, Editor_state.left+8,5, 1)
check(Editor_state.selection1.line ~= nil, 'selection:line-not-nil')
@ -879,7 +847,6 @@ function test_select_text_using_mouse_starting_above_text_wrapping_line()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=2, pos=5}
Editor_state.screen_top1 = {line=2, pos=3}
Editor_state.screen_bottom1 = {}
-- press mouse above first line of text
edit.draw(Editor_state)
edit.run_after_mouse_press(Editor_state, Editor_state.left+8,5, 1)
@ -902,7 +869,6 @@ function test_select_text_using_mouse_starting_below_text()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'ab', 'baseline:screen:1')
@ -923,9 +889,8 @@ function test_select_text_using_mouse_and_shift()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
Editor_state.selection1 = {}
edit.draw(Editor_state) -- populate line_cache.starty for each line Editor_state.line_cache
edit.draw(Editor_state) -- populate line_cache.startpos for each line
-- click on first location
edit.run_after_mouse_press(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)
edit.run_after_mouse_release(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)
@ -948,9 +913,8 @@ function test_select_text_repeatedly_using_mouse_and_shift()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
Editor_state.selection1 = {}
edit.draw(Editor_state) -- populate line_cache.starty for each line Editor_state.line_cache
edit.draw(Editor_state) -- populate line_cache.startpos for each line
-- click on first location
edit.run_after_mouse_press(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)
edit.run_after_mouse_release(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)
@ -978,7 +942,6 @@ function test_select_all_text()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
-- select all
App.fake_key_press('lctrl')
@ -1000,7 +963,6 @@ function test_cut_without_selection()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=2}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
Editor_state.selection1 = {}
edit.draw(Editor_state)
-- try to cut without selecting text
@ -1016,7 +978,6 @@ function test_pagedown()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
-- initially the first two lines are displayed
edit.draw(Editor_state)
local y = Editor_state.top
@ -1046,7 +1007,6 @@ function test_pagedown_skips_drawings()
check_eq(Editor_state.lines[2].mode, 'drawing', 'baseline/lines')
Editor_state.cursor1 = {line=1, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
local drawing_height = Drawing_padding_height + drawing_width/2 -- default
-- initially the screen displays the first line and the drawing
-- 15px margin + 15px line1 + 10px margin + 25px drawing + 10px margin = 75px < screen height 80px
@ -1070,7 +1030,6 @@ function test_pagedown_can_start_from_middle_of_long_wrapping_line()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=2}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'abc ', 'baseline/screen:1')
@ -1105,7 +1064,6 @@ function test_pagedown_never_moves_up()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=9}
Editor_state.screen_top1 = {line=1, pos=9}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
-- pagedown makes no change
edit.run_after_keychord(Editor_state, 'pagedown', 'pagedown')
@ -1120,7 +1078,6 @@ function test_down_arrow_moves_cursor()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
-- initially the first three lines are displayed
edit.draw(Editor_state)
local y = Editor_state.top
@ -1153,7 +1110,6 @@ function test_down_arrow_skips_drawing()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'abc', 'baseline/screen:1')
@ -1175,7 +1131,6 @@ function test_down_arrow_scrolls_down_by_one_line()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=3, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'abc', 'baseline/screen:1')
@ -1203,7 +1158,6 @@ function test_down_arrow_scrolls_down_by_one_screen_line()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=3, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'abc', 'baseline/screen:1')
@ -1232,7 +1186,6 @@ function test_down_arrow_scrolls_down_by_one_screen_line_after_splitting_within_
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=3, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'abc', 'baseline/screen:1')
@ -1260,7 +1213,6 @@ function test_pagedown_followed_by_down_arrow_does_not_scroll_screen_up()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=3, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'abc', 'baseline/screen:1')
@ -1294,7 +1246,6 @@ function test_up_arrow_moves_cursor()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=3, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'abc', 'baseline/screen:1')
@ -1326,7 +1277,6 @@ function test_up_arrow_skips_drawing()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=3, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'abc', 'baseline/screen:1')
@ -1348,7 +1298,6 @@ function test_up_arrow_scrolls_up_by_one_line()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=2, pos=1}
Editor_state.screen_top1 = {line=2, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'def', 'baseline/screen:1')
@ -1376,7 +1325,6 @@ function test_up_arrow_scrolls_up_by_one_line_skipping_drawing()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=3, pos=1}
Editor_state.screen_top1 = {line=3, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'def', 'baseline/screen:1')
@ -1398,7 +1346,6 @@ function test_up_arrow_scrolls_up_by_one_screen_line()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=3, pos=6}
Editor_state.screen_top1 = {line=3, pos=5}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'jkl', 'baseline/screen:1')
@ -1426,7 +1373,6 @@ function test_up_arrow_scrolls_up_to_final_screen_line()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=2, pos=1}
Editor_state.screen_top1 = {line=2, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'ghi', 'baseline/screen:1')
@ -1456,7 +1402,6 @@ function test_up_arrow_scrolls_up_to_empty_line()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=2, pos=1}
Editor_state.screen_top1 = {line=2, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'abc', 'baseline/screen:1')
@ -1483,7 +1428,6 @@ function test_pageup()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=2, pos=1}
Editor_state.screen_top1 = {line=2, pos=1}
Editor_state.screen_bottom1 = {}
-- initially the last two lines are displayed
edit.draw(Editor_state)
local y = Editor_state.top
@ -1508,7 +1452,6 @@ function test_pageup_scrolls_up_by_screen_line()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=2, pos=1}
Editor_state.screen_top1 = {line=2, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'ghi', 'baseline/screen:1')
@ -1537,7 +1480,6 @@ function test_pageup_scrolls_up_from_middle_screen_line()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=2, pos=5}
Editor_state.screen_top1 = {line=2, pos=5}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'jkl', 'baseline/screen:2')
@ -1564,7 +1506,6 @@ function test_enter_on_bottom_line_scrolls_down()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=3, pos=2}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'abc', 'baseline/screen:1')
@ -1593,7 +1534,6 @@ function test_enter_on_final_line_avoids_scrolling_down_when_not_at_bottom()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=4, pos=2}
Editor_state.screen_top1 = {line=4, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'jkl', 'baseline/screen:1')
@ -1616,7 +1556,6 @@ function test_inserting_text_on_final_line_avoids_scrolling_down_when_not_at_bot
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=2, pos=1}
Editor_state.screen_top1 = {line=2, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
-- after hitting the inserting_text key the screen does not scroll down
edit.run_after_text_input(Editor_state, 'a')
@ -1635,7 +1574,6 @@ function test_typing_on_bottom_line_scrolls_down()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=3, pos=4}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'abc', 'baseline/screen:1')
@ -1665,7 +1603,6 @@ function test_left_arrow_scrolls_up_in_wrapped_line()
Editor_state.lines = load_array{'abc', 'def', 'ghi jkl', 'mno'}
Text.redraw_all(Editor_state)
Editor_state.screen_top1 = {line=3, pos=5}
Editor_state.screen_bottom1 = {}
-- cursor is at top of screen
Editor_state.cursor1 = {line=3, pos=5}
edit.draw(Editor_state)
@ -1694,7 +1631,6 @@ function test_right_arrow_scrolls_down_in_wrapped_line()
Editor_state.lines = load_array{'abc', 'def', 'ghi jkl', 'mno'}
Text.redraw_all(Editor_state)
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
-- cursor is at bottom right of screen
Editor_state.cursor1 = {line=3, pos=5}
edit.draw(Editor_state)
@ -1705,7 +1641,7 @@ function test_right_arrow_scrolls_down_in_wrapped_line()
y = y + Editor_state.line_height
App.screen.check(y, 'ghi ', 'baseline/screen:3') -- line wrapping includes trailing whitespace
-- after hitting the right arrow the screen scrolls down by one line
edit.run_after_keychord(Editor_state, 'right', 'right')
edit.run_after_keychord(Editor_state, 'right', 'right')
check_eq(Editor_state.screen_top1.line, 2, 'screen_top')
check_eq(Editor_state.cursor1.line, 3, 'cursor:line')
check_eq(Editor_state.cursor1.pos, 6, 'cursor:pos')
@ -1724,7 +1660,6 @@ function test_home_scrolls_up_in_wrapped_line()
Editor_state.lines = load_array{'abc', 'def', 'ghi jkl', 'mno'}
Text.redraw_all(Editor_state)
Editor_state.screen_top1 = {line=3, pos=5}
Editor_state.screen_bottom1 = {}
-- cursor is at top of screen
Editor_state.cursor1 = {line=3, pos=5}
edit.draw(Editor_state)
@ -1753,7 +1688,6 @@ function test_end_scrolls_down_in_wrapped_line()
Editor_state.lines = load_array{'abc', 'def', 'ghi jkl', 'mno'}
Text.redraw_all(Editor_state)
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
-- cursor is at bottom right of screen
Editor_state.cursor1 = {line=3, pos=5}
edit.draw(Editor_state)
@ -1784,7 +1718,6 @@ function test_position_cursor_on_recently_edited_wrapping_line()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=25}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'abc def ghi ', 'baseline1/screen:1')
@ -1818,7 +1751,6 @@ function test_backspace_can_scroll_up()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=2, pos=1}
Editor_state.screen_top1 = {line=2, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'def', 'baseline/screen:1')
@ -1846,7 +1778,6 @@ function test_backspace_can_scroll_up_screen_line()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=3, pos=5}
Editor_state.screen_top1 = {line=3, pos=5}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'jkl', 'baseline/screen:1')
@ -1981,7 +1912,6 @@ function test_undo_insert_text()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=2, pos=4}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
-- insert a character
edit.draw(Editor_state)
edit.run_after_text_input(Editor_state, 'g')
@ -2016,7 +1946,6 @@ function test_undo_delete_text()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=2, pos=5}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
-- delete a character
edit.run_after_keychord(Editor_state, 'backspace', 'backspace')
check_eq(Editor_state.cursor1.line, 2, 'baseline/cursor:line')
@ -2055,7 +1984,6 @@ function test_undo_restores_selection()
Editor_state.cursor1 = {line=1, pos=1}
Editor_state.selection1 = {line=1, pos=2}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
-- delete selected text
edit.run_after_text_input(Editor_state, 'x')
@ -2076,7 +2004,6 @@ function test_search()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
-- search for a string
edit.run_after_keychord(Editor_state, 'C-f', 'f')
@ -2103,7 +2030,6 @@ function test_search_upwards()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=2, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
-- search for a string
edit.run_after_keychord(Editor_state, 'C-f', 'f')
@ -2121,7 +2047,6 @@ function test_search_wrap()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=2, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
-- search for a string
edit.run_after_keychord(Editor_state, 'C-f', 'f')
@ -2139,7 +2064,6 @@ function test_search_wrap_upwards()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
-- search upwards for a string
edit.run_after_keychord(Editor_state, 'C-f', 'f')

195
text.lua
View File

@ -2,15 +2,13 @@
Text = {}
-- draw a line starting from startpos to screen at y between State.left and State.right
-- return y for the next line, and position of start of final screen line drawn
-- return y for the next line
function Text.draw(State, line_index, y, startpos)
--? print('text.draw', line_index, y)
local line = State.lines[line_index]
local line_cache = State.line_cache[line_index]
line_cache.starty = y
line_cache.startpos = startpos
-- wrap long lines
local final_screen_line_starting_pos = startpos -- track value to return
Text.populate_screen_line_starting_pos(State, line_index)
assert(#line_cache.screen_line_starting_pos >= 1, 'line cache missing screen line info')
for i=1,#line_cache.screen_line_starting_pos do
@ -18,7 +16,6 @@ function Text.draw(State, line_index, y, startpos)
if pos < startpos then
-- render nothing
else
final_screen_line_starting_pos = pos
local screen_line = Text.screen_line(line, line_cache, i)
--? print('text.draw:', screen_line, 'at', line_index,pos, 'after', x,y)
local frag_len = utf8.len(screen_line)
@ -59,7 +56,7 @@ function Text.draw(State, line_index, y, startpos)
end
end
end
return y, final_screen_line_starting_pos
return y
end
function Text.screen_line(line, line_cache, i)
@ -134,7 +131,7 @@ function Text.text_input(State, t)
end
end
local before = snapshot(State, State.cursor1.line)
--? print(State.screen_top1.line, State.screen_top1.pos, State.cursor1.line, State.cursor1.pos, State.screen_bottom1.line, State.screen_bottom1.pos)
--? print(State.screen_top1.line, State.screen_top1.pos, State.cursor1.line, State.cursor1.pos)
Text.insert_at_cursor(State, t)
if State.cursor_y > App.screen.height - State.line_height then
Text.populate_screen_line_starting_pos(State, State.cursor1.line)
@ -167,12 +164,12 @@ function Text.keychord_press(State, chord)
record_undo_event(State, {before=before, after=snapshot(State, before_line, State.cursor1.line)})
elseif chord == 'tab' then
local before = snapshot(State, State.cursor1.line)
--? print(State.screen_top1.line, State.screen_top1.pos, State.cursor1.line, State.cursor1.pos, State.screen_bottom1.line, State.screen_bottom1.pos)
--? print(State.screen_top1.line, State.screen_top1.pos, State.cursor1.line, State.cursor1.pos)
Text.insert_at_cursor(State, '\t')
if State.cursor_y > App.screen.height - State.line_height then
Text.populate_screen_line_starting_pos(State, State.cursor1.line)
Text.snap_cursor_to_bottom_of_screen(State, State.left, State.right)
--? print('=>', State.screen_top1.line, State.screen_top1.pos, State.cursor1.line, State.cursor1.pos, State.screen_bottom1.line, State.screen_bottom1.pos)
--? print('=>', State.screen_top1.line, State.screen_top1.pos, State.cursor1.line, State.cursor1.pos)
end
schedule_save(State)
record_undo_event(State, {before=before, after=snapshot(State, State.cursor1.line)})
@ -353,49 +350,79 @@ function Text.insert_return(State)
end
function Text.pageup(State)
--? print('pageup')
-- duplicate some logic from love.draw
local top2 = Text.to2(State, State.screen_top1)
--? print(App.screen.height)
local y = App.screen.height - State.line_height
while y >= State.top do
--? print(y, top2.line, top2.screen_line, top2.screen_pos)
if State.screen_top1.line == 1 and State.screen_top1.pos == 1 then break end
if State.lines[State.screen_top1.line].mode == 'text' then
y = y - State.line_height
elseif State.lines[State.screen_top1.line].mode == 'drawing' then
y = y - Drawing_padding_height - Drawing.pixels(State.lines[State.screen_top1.line].h, State.width)
end
top2 = Text.previous_screen_line(State, top2)
end
State.screen_top1 = Text.to1(State, top2)
State.screen_top1 = Text.previous_screen_top1(State)
State.cursor1 = {line=State.screen_top1.line, pos=State.screen_top1.pos}
Text.move_cursor_down_to_next_text_line_while_scrolling_again_if_necessary(State)
--? print(State.cursor1.line, State.cursor1.pos, State.screen_top1.line, State.screen_top1.pos)
--? print('pageup end')
Text.redraw_all(State) -- if we're scrolling, reclaim all fragments to avoid memory leaks
end
-- return the top y coordinate of a given line_index,
-- or nil if no part of it is on screen
function Text.starty(State, line_index)
-- duplicate some logic from love.draw
-- does not modify State (except to populate line_cache)
if line_index < State.screen_top1.line then return end
local loc2 = Text.to2(State, State.screen_top1)
local y = State.top
while true do
if State.lines[loc2.line].mode == 'drawing' then
y = y + Drawing_padding_top
end
if loc2.line == line_index then return y end
if State.lines[loc2.line].mode == 'text' then
y = y + State.line_height
elseif State.lines[loc2.line].mode == 'drawing' then
y = y + Drawing.pixels(State.lines[loc2.line].h, State.width) + Drawing_padding_bottom
end
if y + State.line_height > App.screen.height then break end
local next_loc2 = Text.next_screen_line(State, loc2)
if Text.eq2(next_loc2, loc2) then break end -- end of file
loc2 = next_loc2
end
end
function Text.previous_screen_top1(State)
-- duplicate some logic from love.draw
-- does not modify State (except to populate line_cache)
local loc2 = Text.to2(State, State.screen_top1)
local y = App.screen.height - State.line_height
while y >= State.top do
if loc2.line == 1 and loc2.screen_line == 1 and loc2.screen_pos == 1 then break end
if State.lines[loc2.line].mode == 'text' then
y = y - State.line_height
elseif State.lines[loc2.line].mode == 'drawing' then
y = y - Drawing_padding_height - Drawing.pixels(State.lines[loc2.line].h, State.width)
end
loc2 = Text.previous_screen_line(State, loc2)
end
return Text.to1(State, loc2)
end
function Text.pagedown(State)
--? print('pagedown')
-- If a line/paragraph gets to a page boundary, I often want to scroll
-- before I get to the bottom.
-- However, only do this if it makes forward progress.
local bot2 = Text.to2(State, State.screen_bottom1)
if bot2.screen_line > 1 then
bot2.screen_line = math.max(bot2.screen_line-10, 1)
end
local new_top1 = Text.to1(State, bot2)
if Text.lt1(State.screen_top1, new_top1) then
State.screen_top1 = new_top1
else
State.screen_top1 = {line=State.screen_bottom1.line, pos=State.screen_bottom1.pos}
end
--? print('setting top to', State.screen_top1.line, State.screen_top1.pos)
State.screen_top1 = Text.screen_bottom1(State)
State.cursor1 = {line=State.screen_top1.line, pos=State.screen_top1.pos}
Text.move_cursor_down_to_next_text_line_while_scrolling_again_if_necessary(State)
--? print('top now', State.screen_top1.line)
Text.redraw_all(State) -- if we're scrolling, reclaim all fragments to avoid memory leaks
--? print('pagedown end')
end
-- return the location of the start of the bottom-most line on screen
function Text.screen_bottom1(State)
-- duplicate some logic from love.draw
-- does not modify State (except to populate line_cache)
local loc2 = Text.to2(State, State.screen_top1)
local y = State.top
while true do
if State.lines[loc2.line].mode == 'text' then
y = y + State.line_height
elseif State.lines[loc2.line].mode == 'drawing' then
y = y + Drawing_padding_height + Drawing.pixels(State.lines[loc2.line].h, State.width)
end
if y + State.line_height > App.screen.height then break end
local next_loc2 = Text.next_screen_line(State, loc2)
if Text.eq2(next_loc2, loc2) then break end
loc2 = next_loc2
end
return Text.to1(State, loc2)
end
function Text.up(State)
@ -443,7 +470,7 @@ end
function Text.down(State)
assert(State.lines[State.cursor1.line].mode == 'text', 'line is not text')
--? print('down', State.cursor1.line, State.cursor1.pos, State.screen_top1.line, State.screen_top1.pos, State.screen_bottom1.line, State.screen_bottom1.pos)
--? print('down', State.cursor1.line, State.cursor1.pos, State.screen_top1.line, State.screen_top1.pos)
assert(State.cursor1.pos, 'cursor has no pos')
if Text.cursor_at_final_screen_line(State) then
-- line is done, skip to next text line
@ -460,7 +487,9 @@ function Text.down(State)
break
end
end
if State.cursor1.line > State.screen_bottom1.line then
local screen_bottom1 = Text.screen_bottom1(State)
--? print('down 2', State.cursor1.line, State.cursor1.pos, State.screen_top1.line, State.screen_top1.pos, screen_bottom1.line, screen_bottom1.pos)
if State.cursor1.line > screen_bottom1.line then
--? print('screen top before:', State.screen_top1.line, State.screen_top1.pos)
--? print('scroll up preserving cursor')
Text.snap_cursor_to_bottom_of_screen(State)
@ -468,7 +497,8 @@ function Text.down(State)
end
else
-- move down one screen line in current line
local scroll_down = Text.le1(State.screen_bottom1, State.cursor1)
local screen_bottom1 = Text.screen_bottom1(State)
local scroll_down = Text.le1(screen_bottom1, State.cursor1)
--? print('cursor is NOT at final screen line of its line')
local screen_line_starting_pos, screen_line_index = Text.pos_at_start_of_screen_line(State, State.cursor1)
Text.populate_screen_line_starting_pos(State, State.cursor1.line)
@ -484,7 +514,7 @@ function Text.down(State)
--? print('screen top after:', State.screen_top1.line, State.screen_top1.pos)
end
end
--? print('=>', State.cursor1.line, State.cursor1.pos, State.screen_top1.line, State.screen_top1.pos, State.screen_bottom1.line, State.screen_bottom1.pos)
--? print('=>', State.cursor1.line, State.cursor1.pos, State.screen_top1.line, State.screen_top1.pos)
end
function Text.start_of_line(State)
@ -621,6 +651,7 @@ function Text.pos_at_start_of_screen_line(State, loc1)
end
function Text.pos_at_end_of_screen_line(State, loc1)
assert(State.lines[loc1.line].mode == 'text')
Text.populate_screen_line_starting_pos(State, loc1.line)
local line_cache = State.line_cache[loc1.line]
local most_recent_final_pos = utf8.len(State.lines[loc1.line].data)+1
@ -634,6 +665,25 @@ function Text.pos_at_end_of_screen_line(State, loc1)
assert(false, ('invalid pos %d'):format(loc1.pos))
end
function Text.final_text_loc_on_screen(State)
local screen_bottom1 = Text.screen_bottom1(State)
if State.lines[screen_bottom1.line].mode == 'text' then
return {
line=screen_bottom1.line,
pos=Text.pos_at_end_of_screen_line(State, screen_bottom1),
}
end
local loc2 = Text.to2(State, screen_bottom1)
while true do
if State.lines[loc2.line].mode == 'text' then break end
assert(loc2.line > 1 or loc2.screen_line > 1 and loc2.screen_pos > 1) -- elsewhere we're making sure there's always at least one text line on screen
loc2 = Text.previous_screen_line(State, loc2)
end
local result = Text.to1(State, loc2)
result.pos = Text.pos_at_end_of_screen_line(State, result)
return result
end
function Text.cursor_at_final_screen_line(State)
Text.populate_screen_line_starting_pos(State, State.cursor1.line)
local screen_lines = State.line_cache[State.cursor1.line].screen_line_starting_pos
@ -674,7 +724,7 @@ function Text.snap_cursor_to_bottom_of_screen(State)
--? print('to2: =>', top2.line, top2.screen_line, top2.screen_pos)
-- slide to start of screen line
top2.screen_pos = 1 -- start of screen line
--? print('snap', State.screen_top1.line, State.screen_top1.pos, State.cursor1.line, State.cursor1.pos, State.screen_bottom1.line, State.screen_bottom1.pos)
--? print('snap', State.screen_top1.line, State.screen_top1.pos, State.cursor1.line, State.cursor1.pos)
--? print('cursor pos '..tostring(State.cursor1.pos)..' is on the #'..tostring(top2.screen_line)..' screen line down')
local y = App.screen.height - State.line_height
-- duplicate some logic from love.draw
@ -704,26 +754,28 @@ function Text.snap_cursor_to_bottom_of_screen(State)
--? print('top2 finally:', top2.line, top2.screen_line, top2.screen_pos)
State.screen_top1 = Text.to1(State, top2)
--? print('top1 finally:', State.screen_top1.line, State.screen_top1.pos)
--? print('snap =>', State.screen_top1.line, State.screen_top1.pos, State.cursor1.line, State.cursor1.pos, State.screen_bottom1.line, State.screen_bottom1.pos)
--? print('snap =>', State.screen_top1.line, State.screen_top1.pos, State.cursor1.line, State.cursor1.pos)
Text.redraw_all(State) -- if we're scrolling, reclaim all fragments to avoid memory leaks
end
function Text.in_line(State, line_index, x,y)
local line = State.lines[line_index]
local line_cache = State.line_cache[line_index]
if line_cache.starty == nil then return false end -- outside current page
if y < line_cache.starty then return false end
local starty = Text.starty(State, line_index)
if starty == nil then return false end -- outside current page
if y < starty then return false end
Text.populate_screen_line_starting_pos(State, line_index)
return y < line_cache.starty + State.line_height*(#line_cache.screen_line_starting_pos - Text.screen_line_index(line_cache.screen_line_starting_pos, line_cache.startpos) + 1)
return y < starty + State.line_height*(#line_cache.screen_line_starting_pos - Text.screen_line_index(line_cache.screen_line_starting_pos, line_cache.startpos) + 1)
end
-- convert mx,my in pixels to schema-1 coordinates
function Text.to_pos_on_line(State, line_index, mx, my)
local line = State.lines[line_index]
local line_cache = State.line_cache[line_index]
assert(my >= line_cache.starty, 'failed to map y pixel to line')
local starty = Text.starty(State, line_index)
assert(my >= starty, 'failed to map y pixel to line')
-- duplicate some logic from Text.draw
local y = line_cache.starty
local y = starty
local start_screen_line_index = Text.screen_line_index(line_cache.screen_line_starting_pos, line_cache.startpos)
for screen_line_index = start_screen_line_index,#line_cache.screen_line_starting_pos do
local screen_line_starting_pos = line_cache.screen_line_starting_pos[screen_line_index]
@ -909,6 +961,10 @@ function Text.le1(a, b)
return a.pos <= b.pos
end
function Text.eq2(a, b)
return a.line == b.line and a.screen_line == b.screen_line and a.screen_pos == b.screen_pos
end
function Text.offset(s, pos1)
if pos1 == 1 then return 1 end
local result = utf8.offset(s, pos1)
@ -932,6 +988,22 @@ function Text.previous_screen_line(State, loc2)
end
end
function Text.next_screen_line(State, loc2)
if State.lines[loc2.line].mode == 'drawing' then
return {line=loc2.line+1, screen_line=1, screen_pos=1}
end
Text.populate_screen_line_starting_pos(State, loc2.line)
if loc2.screen_line >= #State.line_cache[loc2.line].screen_line_starting_pos then
if loc2.line < #State.lines then
return {line=loc2.line+1, screen_line=1, screen_pos=1}
else
return loc2
end
else
return {line=loc2.line, screen_line=loc2.screen_line+1, screen_pos=1}
end
end
-- resize helper
function Text.tweak_screen_top_and_cursor(State)
if State.screen_top1.pos == 1 then return end
@ -955,16 +1027,12 @@ function Text.tweak_screen_top_and_cursor(State)
end
end
-- make sure cursor is on screen
local screen_bottom1 = Text.screen_bottom1(State)
if Text.lt1(State.cursor1, State.screen_top1) then
State.cursor1 = {line=State.screen_top1.line, pos=State.screen_top1.pos}
elseif State.cursor1.line >= State.screen_bottom1.line then
--? print('too low')
elseif State.cursor1.line >= screen_bottom1.line then
if Text.cursor_out_of_screen(State) then
--? print('tweak')
State.cursor1 = {
line=State.screen_bottom1.line,
pos=Text.to_pos_on_line(State, State.screen_bottom1.line, State.right-5, App.screen.height-5),
}
State.cursor1 = Text.final_text_loc_on_screen(State)
end
end
end
@ -973,11 +1041,6 @@ end
function Text.cursor_out_of_screen(State)
edit.draw(State)
return State.cursor_y == nil
-- this approach is cheaper and almost works, except on the final screen
-- where file ends above bottom of screen
--? local botpos = Text.pos_at_start_of_screen_line(State, State.cursor1)
--? local botline1 = {line=State.cursor1.line, pos=botpos}
--? return Text.lt1(State.screen_bottom1, botline1)
end
function Text.redraw_all(State)

View File

@ -16,7 +16,7 @@ function test_initial_state()
end
function test_click_to_create_drawing()
App.screen.init{width=120, height=60}
App.screen.init{width=800, height=600}
Editor_state = edit.initialize_test_state()
Editor_state.lines = load_array{}
Text.redraw_all(Editor_state)
@ -75,7 +75,6 @@ function test_press_ctrl()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
edit.run_after_keychord(Editor_state, 'C-m', 'm')
end
@ -255,9 +254,8 @@ function test_click_moves_cursor()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
Editor_state.selection1 = {}
edit.draw(Editor_state) -- populate line_cache.starty for each line Editor_state.line_cache
edit.draw(Editor_state) -- populate line_cache.startpos for each line
edit.run_after_mouse_release(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)
check_eq(Editor_state.cursor1.line, 1, 'cursor:line')
check_eq(Editor_state.cursor1.pos, 2, 'cursor:pos')
@ -274,7 +272,6 @@ function test_click_to_left_of_line()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=3}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
Editor_state.selection1 = {}
-- click to the left of the line
edit.draw(Editor_state)
@ -294,7 +291,6 @@ function test_click_takes_margins_into_account()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=2, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
Editor_state.selection1 = {}
-- click on the other line
edit.draw(Editor_state)
@ -313,7 +309,6 @@ function test_click_on_empty_line()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=2, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
Editor_state.selection1 = {}
-- click on the empty line
edit.draw(Editor_state)
@ -332,7 +327,6 @@ function test_click_below_all_lines()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
Editor_state.selection1 = {}
-- click below first line
edit.draw(Editor_state)
@ -350,7 +344,6 @@ function test_draw_text()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'abc', 'screen:1')
@ -367,7 +360,6 @@ function test_draw_wrapping_text()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'abc', 'screen:1')
@ -384,7 +376,6 @@ function test_draw_word_wrapping_text()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'abc ', 'screen:1')
@ -402,7 +393,6 @@ function test_click_on_wrapping_line()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=20}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
-- click on the other line
edit.draw(Editor_state)
edit.run_after_mouse_click(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)
@ -421,7 +411,6 @@ function test_click_on_wrapping_line_takes_margins_into_account()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=20}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
-- click on the other line
edit.draw(Editor_state)
edit.run_after_mouse_click(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)
@ -439,7 +428,6 @@ function test_draw_text_wrapping_within_word()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'abcd ', 'screen:1')
@ -457,7 +445,6 @@ function test_draw_wrapping_text_containing_non_ascii()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'mad', 'screen:1')
@ -476,7 +463,6 @@ function test_click_past_end_of_screen_line()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'madam ', 'baseline/screen:1')
@ -499,7 +485,6 @@ function test_click_on_wrapping_line_rendered_from_partway_at_top_of_screen()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=8}
Editor_state.screen_top1 = {line=1, pos=7}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, "I'm ad", 'baseline/screen:2')
@ -520,7 +505,6 @@ function test_click_past_end_of_wrapping_line()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'madam ', 'baseline/screen:1')
@ -544,7 +528,6 @@ function test_click_past_end_of_wrapping_line_containing_non_ascii()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'madam ', 'baseline/screen:1')
@ -569,7 +552,6 @@ function test_click_past_end_of_word_wrapping_line()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'the quick brown fox ', 'baseline/screen:1')
@ -588,7 +570,6 @@ function test_select_text()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
-- select a letter
App.fake_key_press('lshift')
@ -611,7 +592,6 @@ function test_cursor_movement_without_shift_resets_selection()
Editor_state.cursor1 = {line=1, pos=1}
Editor_state.selection1 = {line=1, pos=2}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
-- press an arrow key without shift
edit.run_after_keychord(Editor_state, 'right', 'right')
@ -629,7 +609,6 @@ function test_edit_deletes_selection()
Editor_state.cursor1 = {line=1, pos=1}
Editor_state.selection1 = {line=1, pos=2}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
-- press a key
edit.run_after_text_input(Editor_state, 'x')
@ -646,7 +625,6 @@ function test_edit_with_shift_key_deletes_selection()
Editor_state.cursor1 = {line=1, pos=1}
Editor_state.selection1 = {line=1, pos=2}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
-- mimic precise keypresses for a capital letter
App.fake_key_press('lshift')
@ -668,7 +646,6 @@ function test_copy_does_not_reset_selection()
Editor_state.cursor1 = {line=1, pos=1}
Editor_state.selection1 = {line=1, pos=2}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
-- copy selection
edit.run_after_keychord(Editor_state, 'C-c', 'c')
@ -686,7 +663,6 @@ function test_cut()
Editor_state.cursor1 = {line=1, pos=1}
Editor_state.selection1 = {line=1, pos=2}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
-- press a key
edit.run_after_keychord(Editor_state, 'C-x', 'x')
@ -704,7 +680,6 @@ function test_paste_replaces_selection()
Editor_state.cursor1 = {line=2, pos=1}
Editor_state.selection1 = {line=1, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
-- set clipboard
App.clipboard = 'xyz'
@ -723,7 +698,6 @@ function test_deleting_selection_may_scroll()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=3, pos=2}
Editor_state.screen_top1 = {line=2, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'def', 'baseline/screen:1')
@ -747,7 +721,6 @@ function test_edit_wrapping_text()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=2, pos=4}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
edit.run_after_text_input(Editor_state, 'g')
local y = Editor_state.top
@ -766,7 +739,6 @@ function test_insert_newline()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=2}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'abc', 'baseline/screen:1')
@ -795,7 +767,6 @@ function test_insert_newline_at_start_of_line()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
-- hitting the enter key splits the line
edit.run_after_keychord(Editor_state, 'return', 'return')
check_eq(Editor_state.cursor1.line, 2, 'cursor:line')
@ -812,7 +783,6 @@ function test_insert_from_clipboard()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=2}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'abc', 'baseline/screen:1')
@ -841,9 +811,8 @@ function test_select_text_using_mouse()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
Editor_state.selection1 = {}
edit.draw(Editor_state) -- populate line_cache.starty for each line Editor_state.line_cache
edit.draw(Editor_state) -- populate line_cache.startpos for each line
-- press and hold on first location
edit.run_after_mouse_press(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)
-- drag and release somewhere else
@ -861,9 +830,8 @@ function test_select_text_using_mouse_starting_above_text()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
Editor_state.selection1 = {}
edit.draw(Editor_state) -- populate line_cache.starty for each line Editor_state.line_cache
edit.draw(Editor_state) -- populate line_cache.startpos for each line
-- press mouse above first line of text
edit.run_after_mouse_press(Editor_state, Editor_state.left+8,5, 1)
check(Editor_state.selection1.line ~= nil, 'selection:line-not-nil')
@ -879,7 +847,6 @@ function test_select_text_using_mouse_starting_above_text_wrapping_line()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=2, pos=5}
Editor_state.screen_top1 = {line=2, pos=3}
Editor_state.screen_bottom1 = {}
-- press mouse above first line of text
edit.draw(Editor_state)
edit.run_after_mouse_press(Editor_state, Editor_state.left+8,5, 1)
@ -902,7 +869,6 @@ function test_select_text_using_mouse_starting_below_text()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'ab', 'baseline:screen:1')
@ -923,9 +889,8 @@ function test_select_text_using_mouse_and_shift()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
Editor_state.selection1 = {}
edit.draw(Editor_state) -- populate line_cache.starty for each line Editor_state.line_cache
edit.draw(Editor_state) -- populate line_cache.startpos for each line
-- click on first location
edit.run_after_mouse_press(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)
edit.run_after_mouse_release(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)
@ -948,9 +913,8 @@ function test_select_text_repeatedly_using_mouse_and_shift()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
Editor_state.selection1 = {}
edit.draw(Editor_state) -- populate line_cache.starty for each line Editor_state.line_cache
edit.draw(Editor_state) -- populate line_cache.startpos for each line
-- click on first location
edit.run_after_mouse_press(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)
edit.run_after_mouse_release(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)
@ -978,7 +942,6 @@ function test_select_all_text()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
-- select all
App.fake_key_press('lctrl')
@ -1000,7 +963,6 @@ function test_cut_without_selection()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=2}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
Editor_state.selection1 = {}
edit.draw(Editor_state)
-- try to cut without selecting text
@ -1016,7 +978,6 @@ function test_pagedown()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
-- initially the first two lines are displayed
edit.draw(Editor_state)
local y = Editor_state.top
@ -1046,7 +1007,6 @@ function test_pagedown_skips_drawings()
check_eq(Editor_state.lines[2].mode, 'drawing', 'baseline/lines')
Editor_state.cursor1 = {line=1, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
local drawing_height = Drawing_padding_height + drawing_width/2 -- default
-- initially the screen displays the first line and the drawing
-- 15px margin + 15px line1 + 10px margin + 25px drawing + 10px margin = 75px < screen height 80px
@ -1062,36 +1022,6 @@ function test_pagedown_skips_drawings()
App.screen.check(y, 'def', 'screen:1')
end
function test_pagedown_often_shows_start_of_wrapping_line()
-- draw a few lines ending in part of a wrapping line
App.screen.init{width=50, height=60}
Editor_state = edit.initialize_test_state()
Editor_state.lines = load_array{'abc', 'def ghi jkl', 'mno'}
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'abc', 'baseline/screen:1')
y = y + Editor_state.line_height
App.screen.check(y, 'def ', 'baseline/screen:2')
y = y + Editor_state.line_height
App.screen.check(y, 'ghi ', 'baseline/screen:3')
-- after pagedown we start drawing from the bottom _line_ (multiple screen lines)
edit.run_after_keychord(Editor_state, 'pagedown', 'pagedown')
check_eq(Editor_state.screen_top1.line, 2, 'screen_top:line')
check_eq(Editor_state.screen_top1.pos, 1, 'screen_top:pos')
check_eq(Editor_state.cursor1.line, 2, 'cursor:line')
check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')
y = Editor_state.top
App.screen.check(y, 'def ', 'screen:1')
y = y + Editor_state.line_height
App.screen.check(y, 'ghi ', 'screen:2')
y = y + Editor_state.line_height
App.screen.check(y, 'jkl', 'screen:3')
end
function test_pagedown_can_start_from_middle_of_long_wrapping_line()
-- draw a few lines starting from a very long wrapping line
App.screen.init{width=Editor_state.left+30, height=60}
@ -1100,7 +1030,6 @@ function test_pagedown_can_start_from_middle_of_long_wrapping_line()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=2}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'abc ', 'baseline/screen:1')
@ -1135,7 +1064,6 @@ function test_pagedown_never_moves_up()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=9}
Editor_state.screen_top1 = {line=1, pos=9}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
-- pagedown makes no change
edit.run_after_keychord(Editor_state, 'pagedown', 'pagedown')
@ -1150,7 +1078,6 @@ function test_down_arrow_moves_cursor()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
-- initially the first three lines are displayed
edit.draw(Editor_state)
local y = Editor_state.top
@ -1183,7 +1110,6 @@ function test_down_arrow_skips_drawing()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'abc', 'baseline/screen:1')
@ -1205,7 +1131,6 @@ function test_down_arrow_scrolls_down_by_one_line()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=3, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'abc', 'baseline/screen:1')
@ -1233,7 +1158,6 @@ function test_down_arrow_scrolls_down_by_one_screen_line()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=3, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'abc', 'baseline/screen:1')
@ -1262,7 +1186,6 @@ function test_down_arrow_scrolls_down_by_one_screen_line_after_splitting_within_
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=3, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'abc', 'baseline/screen:1')
@ -1290,7 +1213,6 @@ function test_pagedown_followed_by_down_arrow_does_not_scroll_screen_up()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=3, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'abc', 'baseline/screen:1')
@ -1324,7 +1246,6 @@ function test_up_arrow_moves_cursor()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=3, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'abc', 'baseline/screen:1')
@ -1356,7 +1277,6 @@ function test_up_arrow_skips_drawing()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=3, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'abc', 'baseline/screen:1')
@ -1378,7 +1298,6 @@ function test_up_arrow_scrolls_up_by_one_line()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=2, pos=1}
Editor_state.screen_top1 = {line=2, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'def', 'baseline/screen:1')
@ -1406,7 +1325,6 @@ function test_up_arrow_scrolls_up_by_one_line_skipping_drawing()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=3, pos=1}
Editor_state.screen_top1 = {line=3, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'def', 'baseline/screen:1')
@ -1428,7 +1346,6 @@ function test_up_arrow_scrolls_up_by_one_screen_line()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=3, pos=6}
Editor_state.screen_top1 = {line=3, pos=5}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'jkl', 'baseline/screen:1')
@ -1456,7 +1373,6 @@ function test_up_arrow_scrolls_up_to_final_screen_line()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=2, pos=1}
Editor_state.screen_top1 = {line=2, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'ghi', 'baseline/screen:1')
@ -1486,7 +1402,6 @@ function test_up_arrow_scrolls_up_to_empty_line()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=2, pos=1}
Editor_state.screen_top1 = {line=2, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'abc', 'baseline/screen:1')
@ -1513,7 +1428,6 @@ function test_pageup()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=2, pos=1}
Editor_state.screen_top1 = {line=2, pos=1}
Editor_state.screen_bottom1 = {}
-- initially the last two lines are displayed
edit.draw(Editor_state)
local y = Editor_state.top
@ -1538,7 +1452,6 @@ function test_pageup_scrolls_up_by_screen_line()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=2, pos=1}
Editor_state.screen_top1 = {line=2, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'ghi', 'baseline/screen:1')
@ -1567,7 +1480,6 @@ function test_pageup_scrolls_up_from_middle_screen_line()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=2, pos=5}
Editor_state.screen_top1 = {line=2, pos=5}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'jkl', 'baseline/screen:2')
@ -1594,7 +1506,6 @@ function test_enter_on_bottom_line_scrolls_down()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=3, pos=2}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'abc', 'baseline/screen:1')
@ -1623,7 +1534,6 @@ function test_enter_on_final_line_avoids_scrolling_down_when_not_at_bottom()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=4, pos=2}
Editor_state.screen_top1 = {line=4, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'jkl', 'baseline/screen:1')
@ -1646,7 +1556,6 @@ function test_inserting_text_on_final_line_avoids_scrolling_down_when_not_at_bot
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=2, pos=1}
Editor_state.screen_top1 = {line=2, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
-- after hitting the inserting_text key the screen does not scroll down
edit.run_after_text_input(Editor_state, 'a')
@ -1665,7 +1574,6 @@ function test_typing_on_bottom_line_scrolls_down()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=3, pos=4}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'abc', 'baseline/screen:1')
@ -1695,7 +1603,6 @@ function test_left_arrow_scrolls_up_in_wrapped_line()
Editor_state.lines = load_array{'abc', 'def', 'ghi jkl', 'mno'}
Text.redraw_all(Editor_state)
Editor_state.screen_top1 = {line=3, pos=5}
Editor_state.screen_bottom1 = {}
-- cursor is at top of screen
Editor_state.cursor1 = {line=3, pos=5}
edit.draw(Editor_state)
@ -1724,7 +1631,6 @@ function test_right_arrow_scrolls_down_in_wrapped_line()
Editor_state.lines = load_array{'abc', 'def', 'ghi jkl', 'mno'}
Text.redraw_all(Editor_state)
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
-- cursor is at bottom right of screen
Editor_state.cursor1 = {line=3, pos=5}
edit.draw(Editor_state)
@ -1754,7 +1660,6 @@ function test_home_scrolls_up_in_wrapped_line()
Editor_state.lines = load_array{'abc', 'def', 'ghi jkl', 'mno'}
Text.redraw_all(Editor_state)
Editor_state.screen_top1 = {line=3, pos=5}
Editor_state.screen_bottom1 = {}
-- cursor is at top of screen
Editor_state.cursor1 = {line=3, pos=5}
edit.draw(Editor_state)
@ -1783,7 +1688,6 @@ function test_end_scrolls_down_in_wrapped_line()
Editor_state.lines = load_array{'abc', 'def', 'ghi jkl', 'mno'}
Text.redraw_all(Editor_state)
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
-- cursor is at bottom right of screen
Editor_state.cursor1 = {line=3, pos=5}
edit.draw(Editor_state)
@ -1814,7 +1718,6 @@ function test_position_cursor_on_recently_edited_wrapping_line()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=25}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'abc def ghi ', 'baseline1/screen:1')
@ -1848,7 +1751,6 @@ function test_backspace_can_scroll_up()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=2, pos=1}
Editor_state.screen_top1 = {line=2, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'def', 'baseline/screen:1')
@ -1876,7 +1778,6 @@ function test_backspace_can_scroll_up_screen_line()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=3, pos=5}
Editor_state.screen_top1 = {line=3, pos=5}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
local y = Editor_state.top
App.screen.check(y, 'jkl', 'baseline/screen:1')
@ -2011,7 +1912,6 @@ function test_undo_insert_text()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=2, pos=4}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
-- insert a character
edit.draw(Editor_state)
edit.run_after_text_input(Editor_state, 'g')
@ -2046,7 +1946,6 @@ function test_undo_delete_text()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=2, pos=5}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
-- delete a character
edit.run_after_keychord(Editor_state, 'backspace', 'backspace')
check_eq(Editor_state.cursor1.line, 2, 'baseline/cursor:line')
@ -2085,7 +1984,6 @@ function test_undo_restores_selection()
Editor_state.cursor1 = {line=1, pos=1}
Editor_state.selection1 = {line=1, pos=2}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
-- delete selected text
edit.run_after_text_input(Editor_state, 'x')
@ -2106,7 +2004,6 @@ function test_search()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
-- search for a string
edit.run_after_keychord(Editor_state, 'C-f', 'f')
@ -2133,7 +2030,6 @@ function test_search_upwards()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=2, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
-- search for a string
edit.run_after_keychord(Editor_state, 'C-f', 'f')
@ -2151,7 +2047,6 @@ function test_search_wrap()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=2, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
-- search for a string
edit.run_after_keychord(Editor_state, 'C-f', 'f')
@ -2169,7 +2064,6 @@ function test_search_wrap_upwards()
Text.redraw_all(Editor_state)
Editor_state.cursor1 = {line=1, pos=1}
Editor_state.screen_top1 = {line=1, pos=1}
Editor_state.screen_bottom1 = {}
edit.draw(Editor_state)
-- search upwards for a string
edit.run_after_keychord(Editor_state, 'C-f', 'f')