Merge lines.love
This commit is contained in:
commit
aa592d25c4
28
commands.lua
28
commands.lua
|
@ -163,7 +163,7 @@ function draw_menu_bar()
|
||||||
if pane.cursor_x == nil then
|
if pane.cursor_x == nil then
|
||||||
add_panning_hotkeys_to_menu()
|
add_panning_hotkeys_to_menu()
|
||||||
else
|
else
|
||||||
assert(pane.cursor_y)
|
assert(pane.cursor_y, 'cursor fell off viewport')
|
||||||
add_hotkey_to_menu('ctrl+e: stop editing')
|
add_hotkey_to_menu('ctrl+e: stop editing')
|
||||||
add_hotkey_to_menu('ctrl+h on drawing: help')
|
add_hotkey_to_menu('ctrl+h on drawing: help')
|
||||||
add_hotkey_to_menu('ctrl+f: find')
|
add_hotkey_to_menu('ctrl+f: find')
|
||||||
|
@ -671,7 +671,7 @@ function command.edit_note()
|
||||||
add_error('no current note')
|
add_error('no current note')
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
assert(not pane.editable)
|
assert(not pane.editable, 'pane already editable')
|
||||||
stop_editing_all()
|
stop_editing_all()
|
||||||
pane.recent_updated = false
|
pane.recent_updated = false
|
||||||
pane.editable = true
|
pane.editable = true
|
||||||
|
@ -681,10 +681,10 @@ function command.edit_note()
|
||||||
end
|
end
|
||||||
|
|
||||||
function command.exit_editing()
|
function command.exit_editing()
|
||||||
assert(Cursor_pane.col >= 1)
|
assert(Cursor_pane.col >= 1, 'no current pane')
|
||||||
local pane = Surface[Cursor_pane.col][Cursor_pane.row]
|
local pane = Surface[Cursor_pane.col][Cursor_pane.row]
|
||||||
assert(pane)
|
assert(pane, 'no current pane')
|
||||||
assert(pane.editable)
|
assert(pane.editable, 'current pane not editable')
|
||||||
stop_editing(pane)
|
stop_editing(pane)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -853,7 +853,7 @@ end
|
||||||
function finalize_search_all_pane()
|
function finalize_search_all_pane()
|
||||||
if Display_settings.search_all_query:sub(1,1) == '"' then
|
if Display_settings.search_all_query:sub(1,1) == '"' then
|
||||||
-- support only a single quoted phrase by itself
|
-- support only a single quoted phrase by itself
|
||||||
assert(Display_settings.search_all_query:sub(#Display_settings.search_all_query) == '"')
|
assert(Display_settings.search_all_query:sub(#Display_settings.search_all_query) == '"', 'you can search for strings in quotes, but only one of them by itself')
|
||||||
Display_settings.search_all_terms = {Display_settings.search_all_query:sub(2, #Display_settings.search_all_query-1)}
|
Display_settings.search_all_terms = {Display_settings.search_all_query:sub(2, #Display_settings.search_all_query-1)}
|
||||||
else
|
else
|
||||||
Display_settings.search_all_terms = split(Display_settings.search_all_query)
|
Display_settings.search_all_terms = split(Display_settings.search_all_query)
|
||||||
|
@ -1168,17 +1168,17 @@ function remove_link(src, rel, target)
|
||||||
if array.find(Non_unique_links, rel) then
|
if array.find(Non_unique_links, rel) then
|
||||||
--? print(('%s is non-unique'):format(rel))
|
--? print(('%s is non-unique'):format(rel))
|
||||||
local arr = Links[src][rel]
|
local arr = Links[src][rel]
|
||||||
assert(arr)
|
assert(arr, 'found no link')
|
||||||
assert(type(arr) == 'table')
|
assert(type(arr) == 'table', 'links not arranged in a table')
|
||||||
local pos = array.find(arr, target)
|
local pos = array.find(arr, target)
|
||||||
--? print(('contains %s at index %s'):format(target, pos))
|
--? print(('contains %s at index %s'):format(target, pos))
|
||||||
assert(pos)
|
assert(pos, "couldn't find link to remove in links table")
|
||||||
table.remove(arr, pos)
|
table.remove(arr, pos)
|
||||||
if #arr == 0 then
|
if #arr == 0 then
|
||||||
Links[src][rel] = nil
|
Links[src][rel] = nil
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
assert(Links[src][rel] == target)
|
assert(Links[src][rel] == target, 'link at this rel is not the target; giving up')
|
||||||
Links[src][rel] = nil
|
Links[src][rel] = nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -1345,7 +1345,7 @@ function command.unroll(rel)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
if #Surface[Cursor_pane.col] == 1 then
|
if #Surface[Cursor_pane.col] == 1 then
|
||||||
assert(Cursor_pane.row == 1)
|
assert(Cursor_pane.row == 1, "couldn't set current pane after unrolling")
|
||||||
stop_editing(pane) -- save any edits before we blow it away
|
stop_editing(pane) -- save any edits before we blow it away
|
||||||
Surface[Cursor_pane.col] = column
|
Surface[Cursor_pane.col] = column
|
||||||
else
|
else
|
||||||
|
@ -1471,7 +1471,7 @@ function command.neighbors()
|
||||||
local column = {name=('neighbors of %s'):format(pane.id)}
|
local column = {name=('neighbors of %s'):format(pane.id)}
|
||||||
populate_neighbors_column(column, pane.id)
|
populate_neighbors_column(column, pane.id)
|
||||||
if #Surface[Cursor_pane.col] == 1 then
|
if #Surface[Cursor_pane.col] == 1 then
|
||||||
assert(Cursor_pane.row == 1)
|
assert(Cursor_pane.row == 1, "couldn't repurpose column for neighbors")
|
||||||
Surface[Cursor_pane.col] = column
|
Surface[Cursor_pane.col] = column
|
||||||
else
|
else
|
||||||
table.insert(Surface, Cursor_pane.col+1, column)
|
table.insert(Surface, Cursor_pane.col+1, column)
|
||||||
|
@ -1561,7 +1561,7 @@ function command.delete_note()
|
||||||
Cursor_pane.row = 1
|
Cursor_pane.row = 1
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
assert(not delete_cursor_column)
|
assert(not delete_cursor_column, "failed to delete note's column")
|
||||||
end
|
end
|
||||||
--
|
--
|
||||||
while Cursor_pane.row > #Surface[Cursor_pane.col] do
|
while Cursor_pane.row > #Surface[Cursor_pane.col] do
|
||||||
|
@ -1605,7 +1605,7 @@ function new_pane()
|
||||||
local id = os.date('%Y/%m/%d/%H-%M-%S', t)
|
local id = os.date('%Y/%m/%d/%H-%M-%S', t)
|
||||||
print_and_log('new_pane: creating directory '..Directory..dirname(id))
|
print_and_log('new_pane: creating directory '..Directory..dirname(id))
|
||||||
local status = App.mkdir(Directory..dirname(id))
|
local status = App.mkdir(Directory..dirname(id))
|
||||||
assert(status)
|
assert(status, "failed to create directory for note")
|
||||||
Links[id] = {}
|
Links[id] = {}
|
||||||
local pane = load_pane(id)
|
local pane = load_pane(id)
|
||||||
if not file_exists(pane.filename) then
|
if not file_exists(pane.filename) then
|
||||||
|
|
28
drawing.lua
28
drawing.lua
|
@ -34,7 +34,6 @@ function Drawing.draw(State, line_index, y)
|
||||||
|
|
||||||
--? print(State.left, State.right)
|
--? print(State.left, State.right)
|
||||||
for _,shape in ipairs(line.shapes) do
|
for _,shape in ipairs(line.shapes) do
|
||||||
assert(shape)
|
|
||||||
if State.editable and geom.on_shape(mx,my, line, shape) then
|
if State.editable and geom.on_shape(mx,my, line, shape) then
|
||||||
App.color(Focus_stroke_color)
|
App.color(Focus_stroke_color)
|
||||||
else
|
else
|
||||||
|
@ -114,8 +113,7 @@ function Drawing.draw_shape(drawing, shape, top, left,right)
|
||||||
elseif shape.mode == 'deleted' then
|
elseif shape.mode == 'deleted' then
|
||||||
-- ignore
|
-- ignore
|
||||||
else
|
else
|
||||||
print(shape.mode)
|
assert(false, ('unknown drawing mode %s'):format(shape.mode))
|
||||||
assert(false)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -208,8 +206,7 @@ function Drawing.draw_pending_shape(drawing, top, left,right)
|
||||||
elseif shape.mode == 'name' then
|
elseif shape.mode == 'name' then
|
||||||
-- nothing pending; changes are immediately committed
|
-- nothing pending; changes are immediately committed
|
||||||
else
|
else
|
||||||
print(shape.mode)
|
assert(false, ('unknown drawing mode %s'):format(shape.mode))
|
||||||
assert(false)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -240,8 +237,7 @@ function Drawing.mouse_press(State, drawing_index, x,y, mouse_button)
|
||||||
elseif State.current_drawing_mode == 'name' then
|
elseif State.current_drawing_mode == 'name' then
|
||||||
-- nothing
|
-- nothing
|
||||||
else
|
else
|
||||||
print(State.current_drawing_mode)
|
assert(false, ('unknown drawing mode %s'):format(State.current_drawing_mode))
|
||||||
assert(false)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -256,7 +252,7 @@ function Drawing.update(State)
|
||||||
-- just skip this frame
|
-- just skip this frame
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
assert(drawing.mode == 'drawing')
|
assert(drawing.mode == 'drawing', 'Drawing.update: line is not a drawing')
|
||||||
local pmx, pmy = App.mouse_x(), App.mouse_y()
|
local pmx, pmy = App.mouse_x(), App.mouse_y()
|
||||||
local mx = Drawing.coord(pmx-State.left, State.width)
|
local mx = Drawing.coord(pmx-State.left, State.width)
|
||||||
local my = Drawing.coord(pmy-line_cache.starty, State.width)
|
local my = Drawing.coord(pmy-line_cache.starty, State.width)
|
||||||
|
@ -343,7 +339,7 @@ function Drawing.mouse_release(State, x,y, mouse_button)
|
||||||
table.insert(drawing.shapes, drawing.pending)
|
table.insert(drawing.shapes, drawing.pending)
|
||||||
end
|
end
|
||||||
elseif drawing.pending.mode == 'rectangle' then
|
elseif drawing.pending.mode == 'rectangle' then
|
||||||
assert(#drawing.pending.vertices <= 2)
|
assert(#drawing.pending.vertices <= 2, 'Drawing.mouse_release: rectangle has too many pending vertices')
|
||||||
if #drawing.pending.vertices == 2 then
|
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-line_cache.starty, State.width)
|
||||||
if mx >= 0 and mx < 256 and my >= 0 and my < drawing.h then
|
if mx >= 0 and mx < 256 and my >= 0 and my < drawing.h then
|
||||||
|
@ -358,7 +354,7 @@ function Drawing.mouse_release(State, x,y, mouse_button)
|
||||||
-- too few points; draw nothing
|
-- too few points; draw nothing
|
||||||
end
|
end
|
||||||
elseif drawing.pending.mode == 'square' then
|
elseif drawing.pending.mode == 'square' then
|
||||||
assert(#drawing.pending.vertices <= 2)
|
assert(#drawing.pending.vertices <= 2, 'Drawing.mouse_release: square has too many pending vertices')
|
||||||
if #drawing.pending.vertices == 2 then
|
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-line_cache.starty, State.width)
|
||||||
if mx >= 0 and mx < 256 and my >= 0 and my < drawing.h then
|
if mx >= 0 and mx < 256 and my >= 0 and my < drawing.h then
|
||||||
|
@ -387,8 +383,7 @@ function Drawing.mouse_release(State, x,y, mouse_button)
|
||||||
elseif drawing.pending.mode == 'name' then
|
elseif drawing.pending.mode == 'name' then
|
||||||
-- drop it
|
-- drop it
|
||||||
else
|
else
|
||||||
print(drawing.pending.mode)
|
assert(false, ('unknown drawing mode %s'):format(drawing.pending.mode))
|
||||||
assert(false)
|
|
||||||
end
|
end
|
||||||
State.lines.current_drawing.pending = {}
|
State.lines.current_drawing.pending = {}
|
||||||
State.lines.current_drawing = nil
|
State.lines.current_drawing = nil
|
||||||
|
@ -546,7 +541,7 @@ function Drawing.keychord_press(State, chord)
|
||||||
if Drawing.contains_point(shape, i) then
|
if Drawing.contains_point(shape, i) then
|
||||||
if shape.mode == 'polygon' then
|
if shape.mode == 'polygon' then
|
||||||
local idx = table.find(shape.vertices, i)
|
local idx = table.find(shape.vertices, i)
|
||||||
assert(idx)
|
assert(idx, 'point to delete is not in vertices')
|
||||||
table.remove(shape.vertices, idx)
|
table.remove(shape.vertices, idx)
|
||||||
if #shape.vertices < 3 then
|
if #shape.vertices < 3 then
|
||||||
shape.mode = 'deleted'
|
shape.mode = 'deleted'
|
||||||
|
@ -642,7 +637,6 @@ function Drawing.select_shape_at_mouse(State)
|
||||||
if Drawing.in_drawing(drawing, line_cache, x,y, State.left,State.right) then
|
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 mx,my = Drawing.coord(x-State.left, State.width), Drawing.coord(y-line_cache.starty, State.width)
|
||||||
for i,shape in ipairs(drawing.shapes) do
|
for i,shape in ipairs(drawing.shapes) do
|
||||||
assert(shape)
|
|
||||||
if geom.on_shape(mx,my, drawing, shape) then
|
if geom.on_shape(mx,my, drawing, shape) then
|
||||||
return drawing,line_cache,i,shape
|
return drawing,line_cache,i,shape
|
||||||
end
|
end
|
||||||
|
@ -660,7 +654,6 @@ function Drawing.select_point_at_mouse(State)
|
||||||
if Drawing.in_drawing(drawing, line_cache, x,y, State.left,State.right) then
|
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 mx,my = Drawing.coord(x-State.left, State.width), Drawing.coord(y-line_cache.starty, State.width)
|
||||||
for i,point in ipairs(drawing.points) do
|
for i,point in ipairs(drawing.points) do
|
||||||
assert(point)
|
|
||||||
if Drawing.near(point, mx,my, State.width) then
|
if Drawing.near(point, mx,my, State.width) then
|
||||||
return drawing_index,drawing,line_cache,i,point
|
return drawing_index,drawing,line_cache,i,point
|
||||||
end
|
end
|
||||||
|
@ -697,13 +690,12 @@ function Drawing.contains_point(shape, p)
|
||||||
elseif shape.mode == 'deleted' then
|
elseif shape.mode == 'deleted' then
|
||||||
-- already done
|
-- already done
|
||||||
else
|
else
|
||||||
print(shape.mode)
|
assert(false, ('unknown drawing mode %s'):format(shape.mode))
|
||||||
assert(false)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function Drawing.smoothen(shape)
|
function Drawing.smoothen(shape)
|
||||||
assert(shape.mode == 'freehand')
|
assert(shape.mode == 'freehand', 'can only smoothen freehand shapes')
|
||||||
for _=1,7 do
|
for _=1,7 do
|
||||||
for i=2,#shape.points-1 do
|
for i=2,#shape.points-1 do
|
||||||
local a = shape.points[i-1]
|
local a = shape.points[i-1]
|
||||||
|
|
10
edit.lua
10
edit.lua
|
@ -82,7 +82,7 @@ function edit.initialize_state(top, left, right, font_height, line_height) -- c
|
||||||
cursor_x = nil,
|
cursor_x = nil,
|
||||||
cursor_y = nil,
|
cursor_y = nil,
|
||||||
|
|
||||||
current_drawing_mode = 'line',
|
current_drawing_mode = 'line', -- one of the available shape modes
|
||||||
previous_drawing_mode = nil, -- extra state for some ephemeral modes like moving/deleting/naming points
|
previous_drawing_mode = nil, -- extra state for some ephemeral modes like moving/deleting/naming points
|
||||||
|
|
||||||
font_height = font_height,
|
font_height = font_height,
|
||||||
|
@ -169,10 +169,7 @@ function edit.draw(State)
|
||||||
--? print_and_log(('edit.draw %s %d %d,%d'):format(State.id, State.top, State.left,State.right))
|
--? print_and_log(('edit.draw %s %d %d,%d'):format(State.id, State.top, State.left,State.right))
|
||||||
State.button_handlers = {}
|
State.button_handlers = {}
|
||||||
App.color(Text_color)
|
App.color(Text_color)
|
||||||
if #State.lines ~= #State.line_cache then
|
assert(#State.lines == #State.line_cache, ('line_cache is out of date; %d elements when it should be %d'):format(#State.line_cache, #State.lines))
|
||||||
log(2, os.date('%Y/%m/%d/%H-%M-%S')..(' line_cache is out of date; %d when it should be %d'):format(#State.line_cache, #State.lines))
|
|
||||||
assert(false)
|
|
||||||
end
|
|
||||||
State.cursor_x = nil
|
State.cursor_x = nil
|
||||||
State.cursor_y = nil
|
State.cursor_y = nil
|
||||||
local y = State.top
|
local y = State.top
|
||||||
|
@ -215,8 +212,7 @@ function edit.draw(State)
|
||||||
Drawing.draw(State, line_index, y)
|
Drawing.draw(State, line_index, y)
|
||||||
y = y + Drawing.pixels(line.h, State.width) + Drawing_padding_bottom
|
y = y + Drawing.pixels(line.h, State.width) + Drawing_padding_bottom
|
||||||
else
|
else
|
||||||
print(line.mode)
|
assert(false, ('unknown line mode %s'):format(line.mode))
|
||||||
assert(false)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
State.bottom = y
|
State.bottom = y
|
||||||
|
|
17
file.lua
17
file.lua
|
@ -66,7 +66,7 @@ function save_to_disk(State)
|
||||||
if not f then
|
if not f then
|
||||||
error(err)
|
error(err)
|
||||||
end
|
end
|
||||||
assert(not err)
|
assert(not err, 'failed to save recent')
|
||||||
f:write(State.id, '\n')
|
f:write(State.id, '\n')
|
||||||
f:close()
|
f:close()
|
||||||
end
|
end
|
||||||
|
@ -79,7 +79,7 @@ function load_drawing(infile_next_line)
|
||||||
local drawing = {mode='drawing', h=256/2, points={}, shapes={}, pending={}}
|
local drawing = {mode='drawing', h=256/2, points={}, shapes={}, pending={}}
|
||||||
while true do
|
while true do
|
||||||
local line = infile_next_line()
|
local line = infile_next_line()
|
||||||
assert(line)
|
assert(line, 'drawing in file is incomplete')
|
||||||
if line == '```' then break end
|
if line == '```' then break end
|
||||||
local shape = json.decode(line)
|
local shape = json.decode(line)
|
||||||
if shape.mode == 'freehand' then
|
if shape.mode == 'freehand' then
|
||||||
|
@ -104,8 +104,7 @@ function load_drawing(infile_next_line)
|
||||||
elseif shape.mode == 'deleted' then
|
elseif shape.mode == 'deleted' then
|
||||||
-- ignore
|
-- ignore
|
||||||
else
|
else
|
||||||
print_and_log('load_drawing: '..shape.mode)
|
assert(false, ('unknown drawing mode %s'):format(shape.mode))
|
||||||
assert(false)
|
|
||||||
end
|
end
|
||||||
table.insert(drawing.shapes, shape)
|
table.insert(drawing.shapes, shape)
|
||||||
end
|
end
|
||||||
|
@ -139,8 +138,7 @@ function store_drawing(outfile, drawing)
|
||||||
elseif shape.mode == 'deleted' then
|
elseif shape.mode == 'deleted' then
|
||||||
-- ignore
|
-- ignore
|
||||||
else
|
else
|
||||||
print_and_log('store_drawing: '..shape.mode)
|
assert(false, ('unknown drawing mode %s'):format(shape.mode))
|
||||||
assert(false)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
outfile:write('```\n')
|
outfile:write('```\n')
|
||||||
|
@ -158,7 +156,7 @@ end
|
||||||
function save_links(id)
|
function save_links(id)
|
||||||
local links_filename = Directory..id..'.json'
|
local links_filename = Directory..id..'.json'
|
||||||
local status = App.mkdir(dirname(links_filename))
|
local status = App.mkdir(dirname(links_filename))
|
||||||
assert(status)
|
assert(status, 'failed to create directory for links')
|
||||||
log(2, 'save_links: '..id)
|
log(2, 'save_links: '..id)
|
||||||
if empty(Links[id]) then
|
if empty(Links[id]) then
|
||||||
print_and_log('save_links: no links; getting rid of .json if it exists')
|
print_and_log('save_links: no links; getting rid of .json if it exists')
|
||||||
|
@ -206,7 +204,7 @@ function load_drawing_from_array(iter, a, i)
|
||||||
local line
|
local line
|
||||||
while true do
|
while true do
|
||||||
i, line = iter(a, i)
|
i, line = iter(a, i)
|
||||||
assert(i)
|
assert(i, 'drawing in array is incomplete')
|
||||||
--? print(i)
|
--? print(i)
|
||||||
if line == '```' then break end
|
if line == '```' then break end
|
||||||
local shape = json.decode(line)
|
local shape = json.decode(line)
|
||||||
|
@ -232,8 +230,7 @@ function load_drawing_from_array(iter, a, i)
|
||||||
elseif shape.mode == 'deleted' then
|
elseif shape.mode == 'deleted' then
|
||||||
-- ignore
|
-- ignore
|
||||||
else
|
else
|
||||||
print_and_log('load_drawing_from_array: '..shape.mode)
|
assert(false, ('unknown drawing mode %s'):format(shape.mode))
|
||||||
assert(false)
|
|
||||||
end
|
end
|
||||||
table.insert(drawing.shapes, shape)
|
table.insert(drawing.shapes, shape)
|
||||||
end
|
end
|
||||||
|
|
3
geom.lua
3
geom.lua
|
@ -38,8 +38,7 @@ function geom.on_shape(x,y, drawing, shape)
|
||||||
return geom.angle_between(center.x,center.y, x,y, shape.start_angle,shape.end_angle)
|
return geom.angle_between(center.x,center.y, x,y, shape.start_angle,shape.end_angle)
|
||||||
elseif shape.mode == 'deleted' then
|
elseif shape.mode == 'deleted' then
|
||||||
else
|
else
|
||||||
print(shape.mode)
|
assert(false, ('unknown drawing mode %s'):format(shape.mode))
|
||||||
assert(false)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -75,7 +75,7 @@ function table.shallowcopy(x)
|
||||||
end
|
end
|
||||||
|
|
||||||
function log_browser.draw(State, hide_cursor)
|
function log_browser.draw(State, hide_cursor)
|
||||||
assert(#State.lines == #State.line_cache)
|
assert(#State.lines == #State.line_cache, ('line_cache is out of date; %d elements when it should be %d'):format(#State.line_cache, #State.lines))
|
||||||
local mouse_line_index = log_browser.line_index(State, App.mouse_x(), App.mouse_y())
|
local mouse_line_index = log_browser.line_index(State, App.mouse_x(), App.mouse_y())
|
||||||
local y = State.top
|
local y = State.top
|
||||||
for line_index = State.screen_top1.line,#State.lines do
|
for line_index = State.screen_top1.line,#State.lines do
|
||||||
|
@ -95,7 +95,7 @@ function log_browser.draw(State, hide_cursor)
|
||||||
love.graphics.line(xleft,sectiony, xleft+50-2,sectiony)
|
love.graphics.line(xleft,sectiony, xleft+50-2,sectiony)
|
||||||
love.graphics.print(line.section_name, xleft+50,y)
|
love.graphics.print(line.section_name, xleft+50,y)
|
||||||
love.graphics.line(xleft+50+App.width(line.section_name)+2,sectiony, xright,sectiony)
|
love.graphics.line(xleft+50+App.width(line.section_name)+2,sectiony, xright,sectiony)
|
||||||
else assert(line.section_end)
|
else assert(line.section_end, "log line has a section name, but it's neither the start nor end of a section")
|
||||||
local sectiony = y+State.line_height-Section_border_padding_vertical
|
local sectiony = y+State.line_height-Section_border_padding_vertical
|
||||||
love.graphics.line(xleft,y, xleft,sectiony)
|
love.graphics.line(xleft,y, xleft,sectiony)
|
||||||
love.graphics.line(xright,y, xright,sectiony)
|
love.graphics.line(xright,y, xright,sectiony)
|
||||||
|
|
46
run.lua
46
run.lua
|
@ -176,7 +176,7 @@ function run.initialize(arg)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
assert(Directory)
|
assert(Directory, 'no directory to browse notes in')
|
||||||
print('Directory initialized to '..Directory)
|
print('Directory initialized to '..Directory)
|
||||||
Error_log.filename = Directory..'errors'
|
Error_log.filename = Directory..'errors'
|
||||||
|
|
||||||
|
@ -332,8 +332,7 @@ function refresh_pane_height(pane)
|
||||||
-- nothing
|
-- nothing
|
||||||
y = y + Drawing.pixels(line.h, Display_settings.column_width) + Drawing_padding_height
|
y = y + Drawing.pixels(line.h, Display_settings.column_width) + Drawing_padding_height
|
||||||
else
|
else
|
||||||
print_and_log('refresh_pane_height: '..line.mode)
|
assert(false, ('unknown line mode %s'):format(line.mode))
|
||||||
assert(false)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if Links[pane.id] and not empty(Links[pane.id]) then
|
if Links[pane.id] and not empty(Links[pane.id]) then
|
||||||
|
@ -436,8 +435,7 @@ function run.draw()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
print_and_log('run.draw: '..Display_settings.mode)
|
assert(false, ('pensieve is in an unknown mode %s'):format(Display_settings.mode))
|
||||||
assert(false)
|
|
||||||
end
|
end
|
||||||
if Grab_pane then
|
if Grab_pane then
|
||||||
local old_top, old_left, old_right = Grab_pane.top, Grab_pane.left, Grab_pane.right
|
local old_top, old_left, old_right = Grab_pane.top, Grab_pane.left, Grab_pane.right
|
||||||
|
@ -467,11 +465,11 @@ function run.draw()
|
||||||
end
|
end
|
||||||
|
|
||||||
function draw_normal_mode()
|
function draw_normal_mode()
|
||||||
assert(Cursor_pane.col)
|
assert(Cursor_pane.col, 'no current column')
|
||||||
assert(Cursor_pane.row)
|
assert(Cursor_pane.row, 'no current row')
|
||||||
--? print('draw', Display_settings.x, Display_settings.y)
|
--? print('draw', Display_settings.x, Display_settings.y)
|
||||||
for _,pane in ipairs(Panes_to_draw) do
|
for _,pane in ipairs(Panes_to_draw) do
|
||||||
assert(pane.top)
|
assert(pane.top, "pane has no top coordinate; there's likely a problem in plan_draw")
|
||||||
--? if pane.column_index == 1 and pane.pane_index == 1 then
|
--? if pane.column_index == 1 and pane.pane_index == 1 then
|
||||||
--? print('draw', pane.id, 'from y', pane.top, 'down to screen height', App.screen.height)
|
--? print('draw', pane.id, 'from y', pane.top, 'down to screen height', App.screen.height)
|
||||||
--? print('screen top', pane.screen_top1.line, pane.screen_top1.pos)
|
--? print('screen top', pane.screen_top1.line, pane.screen_top1.pos)
|
||||||
|
@ -517,7 +515,7 @@ function should_show_pane(pane, sy)
|
||||||
end
|
end
|
||||||
|
|
||||||
function draw_title(pane)
|
function draw_title(pane)
|
||||||
assert(pane.title)
|
assert(pane.title, 'pane has no title')
|
||||||
App.color(Pane_title_color)
|
App.color(Pane_title_color)
|
||||||
App.screen.print(pane.title, pane.left, pane.top-Margin_above -5-Line_height)
|
App.screen.print(pane.title, pane.left, pane.top-Margin_above -5-Line_height)
|
||||||
App.color(Pane_title_background_color)
|
App.color(Pane_title_background_color)
|
||||||
|
@ -675,7 +673,9 @@ function run.quit()
|
||||||
end
|
end
|
||||||
|
|
||||||
function run.settings()
|
function run.settings()
|
||||||
assert(Directory_error == nil) -- this will go into an infinite loop if handle_error ever tries to save settings
|
-- avoid an infinite loop if handle_error ever tries to save settings
|
||||||
|
assert(Directory_error == nil,
|
||||||
|
"tried to save settings when we couldn't determine the directory to browse notes in")
|
||||||
-- side effect: save notes-related settings inside Directory
|
-- side effect: save notes-related settings inside Directory
|
||||||
local column_names = {}
|
local column_names = {}
|
||||||
for _,column in ipairs(Surface) do
|
for _,column in ipairs(Surface) do
|
||||||
|
@ -690,7 +690,7 @@ function run.settings()
|
||||||
surface_x=Display_settings.x,
|
surface_x=Display_settings.x,
|
||||||
surface_y=Display_settings.y,
|
surface_y=Display_settings.y,
|
||||||
}))
|
}))
|
||||||
assert(status)
|
assert(status, 'failed to write settings')
|
||||||
|
|
||||||
if Settings == nil then Settings = {} end
|
if Settings == nil then Settings = {} end
|
||||||
Settings.x, Settings.y, Settings.displayindex = App.screen.position()
|
Settings.x, Settings.y, Settings.displayindex = App.screen.position()
|
||||||
|
@ -717,8 +717,7 @@ function run.mouse_press(x,y, mouse_button)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
print_and_log('run.mouse_press: '..Display_settings.mode)
|
assert(false, ('pensieve is in an unknown mode %s'):format(Display_settings.mode))
|
||||||
assert(false)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -874,8 +873,7 @@ function run.text_input(t)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
print_and_log('run.text_input: '..Display_settings.mode)
|
assert(false, ('pensieve is in an unknown mode %s'):format(Display_settings.mode))
|
||||||
assert(false)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -911,7 +909,7 @@ function run.keychord_press(chord, key)
|
||||||
command.exit_editing()
|
command.exit_editing()
|
||||||
elseif pane.cursor_x == nil then
|
elseif pane.cursor_x == nil then
|
||||||
-- ignore if cursor is not visible on screen
|
-- ignore if cursor is not visible on screen
|
||||||
assert(pane.cursor_y == nil)
|
assert(pane.cursor_y == nil, 'cursor x is not set but y is set')
|
||||||
panning_keychord_press(chord, key)
|
panning_keychord_press(chord, key)
|
||||||
plan_draw()
|
plan_draw()
|
||||||
else
|
else
|
||||||
|
@ -949,8 +947,7 @@ function run.keychord_press(chord, key)
|
||||||
keychord_press_in_maximize_mode(chord, key)
|
keychord_press_in_maximize_mode(chord, key)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
print_and_log('run.keychord_press: '..Display_settings.mode)
|
assert(false, ('pensieve is in an unknown mode %s'):format(Display_settings.mode))
|
||||||
assert(false)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1203,8 +1200,7 @@ function bring_cursor_of_cursor_pane_in_view(dir)
|
||||||
if not horizontally_ok then
|
if not horizontally_ok then
|
||||||
Display_settings.y = cursor_sy - 3*Line_height
|
Display_settings.y = cursor_sy - 3*Line_height
|
||||||
end
|
end
|
||||||
else
|
elseif dir == 'down' then
|
||||||
assert(dir == 'down')
|
|
||||||
if not vertically_ok then
|
if not vertically_ok then
|
||||||
Display_settings.x = left_edge_sx + Display_settings.column_width + Margin_right + Padding_horizontal - App.screen.width
|
Display_settings.x = left_edge_sx + Display_settings.column_width + Margin_right + Padding_horizontal - App.screen.width
|
||||||
end
|
end
|
||||||
|
@ -1213,7 +1209,9 @@ function bring_cursor_of_cursor_pane_in_view(dir)
|
||||||
Display_settings.y = cursor_sy + Text.search_bar_height(pane) - (App.screen.height - Header_height)
|
Display_settings.y = cursor_sy + Text.search_bar_height(pane) - (App.screen.height - Header_height)
|
||||||
-- Bah, temporarily giving up on debugging.
|
-- Bah, temporarily giving up on debugging.
|
||||||
Display_settings.y = Display_settings.y + Line_height
|
Display_settings.y = Display_settings.y + Line_height
|
||||||
assert(App.screen.height - (cursor_sy-Display_settings.y) > 1.5*Line_height)
|
assert(App.screen.height - (cursor_sy-Display_settings.y) > 1.5*Line_height, 'ugh, ancient bug is back: panning the viewport when cursor falls off')
|
||||||
|
else
|
||||||
|
assert(false, ('unknown dir %s'):format(dir))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
Display_settings.x = math.max(Display_settings.x, 0)
|
Display_settings.x = math.max(Display_settings.x, 0)
|
||||||
|
@ -1414,7 +1412,7 @@ end
|
||||||
-- - let loc, y_offset = schema1_of_y(pane, y)
|
-- - let loc, y_offset = schema1_of_y(pane, y)
|
||||||
-- y - y_offset == y_of_schema1(pane, loc)
|
-- y - y_offset == y_of_schema1(pane, loc)
|
||||||
function schema1_of_y(pane, y)
|
function schema1_of_y(pane, y)
|
||||||
assert(y >= 0)
|
assert(y >= 0, 'something is at negative y on the surface')
|
||||||
local y_offset = y
|
local y_offset = y
|
||||||
for i=1,#pane.lines do
|
for i=1,#pane.lines do
|
||||||
--? print('--', y_offset)
|
--? print('--', y_offset)
|
||||||
|
@ -1427,7 +1425,7 @@ function schema1_of_y(pane, y)
|
||||||
else
|
else
|
||||||
local nlines = math.floor(y_offset/pane.line_height)
|
local nlines = math.floor(y_offset/pane.line_height)
|
||||||
--? print(y_offset, pane.line_height, nlines)
|
--? print(y_offset, pane.line_height, nlines)
|
||||||
assert(nlines >= 0 and nlines < #pane.line_cache[i].screen_line_starting_pos)
|
assert(nlines >= 0 and nlines < #pane.line_cache[i].screen_line_starting_pos, 'error in mapping y coordinate to schema-1')
|
||||||
local pos = pane.line_cache[i].screen_line_starting_pos[nlines+1] -- switch to 1-indexing
|
local pos = pane.line_cache[i].screen_line_starting_pos[nlines+1] -- switch to 1-indexing
|
||||||
y_offset = y_offset - nlines*pane.line_height
|
y_offset = y_offset - nlines*pane.line_height
|
||||||
return {line=i, pos=pos}, y_offset
|
return {line=i, pos=pos}, y_offset
|
||||||
|
@ -1459,7 +1457,7 @@ function stop_editing_all()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
assert(edit_count <= 1)
|
assert(edit_count <= 1, 'multiple panes were editable')
|
||||||
end
|
end
|
||||||
|
|
||||||
function stop_editing(pane)
|
function stop_editing(pane)
|
||||||
|
|
|
@ -151,7 +151,7 @@ function rfind(s, pat, i, plain)
|
||||||
local rendpos = rs:find(rpat, ri, plain)
|
local rendpos = rs:find(rpat, ri, plain)
|
||||||
if rendpos == nil then return nil end
|
if rendpos == nil then return nil end
|
||||||
local endpos = #s - rendpos + 1
|
local endpos = #s - rendpos + 1
|
||||||
assert (endpos >= #pat)
|
assert (endpos >= #pat, ('rfind: endpos %d should be >= #pat %d at this point'):format(endpos, #pat))
|
||||||
return endpos-#pat+1
|
return endpos-#pat+1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
10
select.lua
10
select.lua
|
@ -33,13 +33,13 @@ function Text.clip_selection(State, line_index, apos, bpos)
|
||||||
-- fully contained
|
-- fully contained
|
||||||
return apos,bpos
|
return apos,bpos
|
||||||
elseif a_ge then
|
elseif a_ge then
|
||||||
assert(maxl == line_index)
|
assert(maxl == line_index, ('maxl %d not equal to line_index %d'):format(maxl, line_index))
|
||||||
return apos,maxp
|
return apos,maxp
|
||||||
elseif b_lt then
|
elseif b_lt then
|
||||||
assert(minl == line_index)
|
assert(minl == line_index, ('minl %d not equal to line_index %d'):format(minl, line_index))
|
||||||
return minp,bpos
|
return minp,bpos
|
||||||
else
|
else
|
||||||
assert(minl == maxl and minl == line_index)
|
assert(minl == maxl and minl == line_index, ('minl %d, maxl %d and line_index %d are not all equal'):format(minl, maxl, line_index))
|
||||||
return minp,maxp
|
return minp,maxp
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -127,7 +127,7 @@ function Text.delete_selection_without_undo(State)
|
||||||
State.lines[minl].data = State.lines[minl].data:sub(1, min_offset-1)..State.lines[minl].data:sub(max_offset)
|
State.lines[minl].data = State.lines[minl].data:sub(1, min_offset-1)..State.lines[minl].data:sub(max_offset)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
assert(minl < maxl)
|
assert(minl < maxl, ('minl %d not < maxl %d'):format(minl, maxl))
|
||||||
local rhs = State.lines[maxl].data:sub(max_offset)
|
local rhs = State.lines[maxl].data:sub(max_offset)
|
||||||
for i=maxl,minl+1,-1 do
|
for i=maxl,minl+1,-1 do
|
||||||
table.remove(State.lines, i)
|
table.remove(State.lines, i)
|
||||||
|
@ -154,7 +154,7 @@ function Text.selection(State)
|
||||||
if minl == maxl then
|
if minl == maxl then
|
||||||
return State.lines[minl].data:sub(min_offset, max_offset-1)
|
return State.lines[minl].data:sub(min_offset, max_offset-1)
|
||||||
end
|
end
|
||||||
assert(minl < maxl)
|
assert(minl < maxl, ('minl %d not < maxl %d'):format(minl, maxl))
|
||||||
local result = {State.lines[minl].data:sub(min_offset)}
|
local result = {State.lines[minl].data:sub(min_offset)}
|
||||||
for i=minl+1,maxl-1 do
|
for i=minl+1,maxl-1 do
|
||||||
if State.lines[i].mode == 'text' then
|
if State.lines[i].mode == 'text' then
|
||||||
|
|
|
@ -136,7 +136,7 @@ end
|
||||||
|
|
||||||
function move_candidate_to_front(s)
|
function move_candidate_to_front(s)
|
||||||
local index = array.find(File_navigation.all_candidates, s)
|
local index = array.find(File_navigation.all_candidates, s)
|
||||||
assert(index)
|
assert(index, 'file missing from manifest')
|
||||||
table.remove(File_navigation.all_candidates, index)
|
table.remove(File_navigation.all_candidates, index)
|
||||||
table.insert(File_navigation.all_candidates, 1, s)
|
table.insert(File_navigation.all_candidates, 1, s)
|
||||||
end
|
end
|
||||||
|
|
|
@ -158,14 +158,8 @@ end
|
||||||
function edit.draw(State, hide_cursor, show_line_numbers)
|
function edit.draw(State, hide_cursor, show_line_numbers)
|
||||||
State.button_handlers = {}
|
State.button_handlers = {}
|
||||||
App.color(Text_color)
|
App.color(Text_color)
|
||||||
if #State.lines ~= #State.line_cache then
|
assert(#State.lines == #State.line_cache, ('line_cache is out of date; %d elements when it should be %d'):format(#State.line_cache, #State.lines))
|
||||||
print(('line_cache is out of date; %d when it should be %d'):format(#State.line_cache, #State.lines))
|
assert(Text.le1(State.screen_top1, State.cursor1), ('screen_top (line=%d,pos=%d) is below cursor (line=%d,pos=%d)'):format(State.screen_top1.line, State.screen_top1.pos, State.cursor1.line, State.cursor1.pos))
|
||||||
assert(false)
|
|
||||||
end
|
|
||||||
if not Text.le1(State.screen_top1, State.cursor1) then
|
|
||||||
print(State.screen_top1.line, State.screen_top1.pos, State.cursor1.line, State.cursor1.pos)
|
|
||||||
assert(false)
|
|
||||||
end
|
|
||||||
State.cursor_x = nil
|
State.cursor_x = nil
|
||||||
State.cursor_y = nil
|
State.cursor_y = nil
|
||||||
local y = State.top
|
local y = State.top
|
||||||
|
@ -209,8 +203,7 @@ function edit.draw(State, hide_cursor, show_line_numbers)
|
||||||
Drawing.draw(State, line_index, y)
|
Drawing.draw(State, line_index, y)
|
||||||
y = y + Drawing.pixels(line.h, State.width) + Drawing_padding_bottom
|
y = y + Drawing.pixels(line.h, State.width) + Drawing_padding_bottom
|
||||||
else
|
else
|
||||||
print(line.mode)
|
assert(false, ('unknown line mode %s'):format(line.mode))
|
||||||
assert(false)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
State.screen_bottom1 = screen_bottom1
|
State.screen_bottom1 = screen_bottom1
|
||||||
|
|
|
@ -63,7 +63,7 @@ function load_drawing(infile_next_line)
|
||||||
local drawing = {mode='drawing', h=256/2, points={}, shapes={}, pending={}}
|
local drawing = {mode='drawing', h=256/2, points={}, shapes={}, pending={}}
|
||||||
while true do
|
while true do
|
||||||
local line = infile_next_line()
|
local line = infile_next_line()
|
||||||
assert(line)
|
assert(line, 'drawing in file is incomplete')
|
||||||
if line == '```' then break end
|
if line == '```' then break end
|
||||||
local shape = json.decode(line)
|
local shape = json.decode(line)
|
||||||
if shape.mode == 'freehand' then
|
if shape.mode == 'freehand' then
|
||||||
|
@ -88,8 +88,7 @@ function load_drawing(infile_next_line)
|
||||||
elseif shape.mode == 'deleted' then
|
elseif shape.mode == 'deleted' then
|
||||||
-- ignore
|
-- ignore
|
||||||
else
|
else
|
||||||
print(shape.mode)
|
assert(false, ('unknown drawing mode %s'):format(shape.mode))
|
||||||
assert(false)
|
|
||||||
end
|
end
|
||||||
table.insert(drawing.shapes, shape)
|
table.insert(drawing.shapes, shape)
|
||||||
end
|
end
|
||||||
|
@ -123,8 +122,7 @@ function store_drawing(outfile, drawing)
|
||||||
elseif shape.mode == 'deleted' then
|
elseif shape.mode == 'deleted' then
|
||||||
-- ignore
|
-- ignore
|
||||||
else
|
else
|
||||||
print(shape.mode)
|
assert(false, ('unknown drawing mode %s'):format(shape.mode))
|
||||||
assert(false)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
outfile:write('```\n')
|
outfile:write('```\n')
|
||||||
|
@ -162,7 +160,7 @@ function load_drawing_from_array(iter, a, i)
|
||||||
local line
|
local line
|
||||||
while true do
|
while true do
|
||||||
i, line = iter(a, i)
|
i, line = iter(a, i)
|
||||||
assert(i)
|
assert(i, 'drawing in array is incomplete')
|
||||||
--? print(i)
|
--? print(i)
|
||||||
if line == '```' then break end
|
if line == '```' then break end
|
||||||
local shape = json.decode(line)
|
local shape = json.decode(line)
|
||||||
|
@ -188,8 +186,7 @@ function load_drawing_from_array(iter, a, i)
|
||||||
elseif shape.mode == 'deleted' then
|
elseif shape.mode == 'deleted' then
|
||||||
-- ignore
|
-- ignore
|
||||||
else
|
else
|
||||||
print(shape.mode)
|
assert(false, ('unknown drawing mode %s'):format(shape.mode))
|
||||||
assert(false)
|
|
||||||
end
|
end
|
||||||
table.insert(drawing.shapes, shape)
|
table.insert(drawing.shapes, shape)
|
||||||
end
|
end
|
||||||
|
|
|
@ -33,13 +33,13 @@ function Text.clip_selection(State, line_index, apos, bpos)
|
||||||
-- fully contained
|
-- fully contained
|
||||||
return apos,bpos
|
return apos,bpos
|
||||||
elseif a_ge then
|
elseif a_ge then
|
||||||
assert(maxl == line_index)
|
assert(maxl == line_index, ('maxl %d not equal to line_index %d'):format(maxl, line_index))
|
||||||
return apos,maxp
|
return apos,maxp
|
||||||
elseif b_lt then
|
elseif b_lt then
|
||||||
assert(minl == line_index)
|
assert(minl == line_index, ('minl %d not equal to line_index %d'):format(minl, line_index))
|
||||||
return minp,bpos
|
return minp,bpos
|
||||||
else
|
else
|
||||||
assert(minl == maxl and minl == line_index)
|
assert(minl == maxl and minl == line_index, ('minl %d, maxl %d and line_index %d are not all equal'):format(minl, maxl, line_index))
|
||||||
return minp,maxp
|
return minp,maxp
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -127,7 +127,7 @@ function Text.delete_selection_without_undo(State)
|
||||||
State.lines[minl].data = State.lines[minl].data:sub(1, min_offset-1)..State.lines[minl].data:sub(max_offset)
|
State.lines[minl].data = State.lines[minl].data:sub(1, min_offset-1)..State.lines[minl].data:sub(max_offset)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
assert(minl < maxl)
|
assert(minl < maxl, ('minl %d not < maxl %d'):format(minl, maxl))
|
||||||
local rhs = State.lines[maxl].data:sub(max_offset)
|
local rhs = State.lines[maxl].data:sub(max_offset)
|
||||||
for i=maxl,minl+1,-1 do
|
for i=maxl,minl+1,-1 do
|
||||||
table.remove(State.lines, i)
|
table.remove(State.lines, i)
|
||||||
|
@ -154,7 +154,7 @@ function Text.selection(State)
|
||||||
if minl == maxl then
|
if minl == maxl then
|
||||||
return State.lines[minl].data:sub(min_offset, max_offset-1)
|
return State.lines[minl].data:sub(min_offset, max_offset-1)
|
||||||
end
|
end
|
||||||
assert(minl < maxl)
|
assert(minl < maxl, ('minl %d not < maxl %d'):format(minl, maxl))
|
||||||
local result = {State.lines[minl].data:sub(min_offset)}
|
local result = {State.lines[minl].data:sub(min_offset)}
|
||||||
for i=minl+1,maxl-1 do
|
for i=minl+1,maxl-1 do
|
||||||
if State.lines[i].mode == 'text' then
|
if State.lines[i].mode == 'text' then
|
||||||
|
|
|
@ -17,7 +17,7 @@ function Text.draw(State, line_index, y, startpos, hide_cursor, show_line_number
|
||||||
love.graphics.print(line_index, State.left-Line_number_width*App.width('m')+10,y)
|
love.graphics.print(line_index, State.left-Line_number_width*App.width('m')+10,y)
|
||||||
end
|
end
|
||||||
initialize_color()
|
initialize_color()
|
||||||
assert(#line_cache.screen_line_starting_pos >= 1)
|
assert(#line_cache.screen_line_starting_pos >= 1, 'line cache missing screen line info')
|
||||||
for i=1,#line_cache.screen_line_starting_pos do
|
for i=1,#line_cache.screen_line_starting_pos do
|
||||||
local pos = line_cache.screen_line_starting_pos[i]
|
local pos = line_cache.screen_line_starting_pos[i]
|
||||||
if pos < startpos then
|
if pos < startpos then
|
||||||
|
@ -209,7 +209,7 @@ function Text.text_input(State, t)
|
||||||
end
|
end
|
||||||
|
|
||||||
function Text.insert_at_cursor(State, t)
|
function Text.insert_at_cursor(State, t)
|
||||||
assert(State.lines[State.cursor1.line].mode == 'text')
|
assert(State.lines[State.cursor1.line].mode == 'text', 'line is not text')
|
||||||
local byte_offset = Text.offset(State.lines[State.cursor1.line].data, State.cursor1.pos)
|
local byte_offset = Text.offset(State.lines[State.cursor1.line].data, State.cursor1.pos)
|
||||||
State.lines[State.cursor1.line].data = string.sub(State.lines[State.cursor1.line].data, 1, byte_offset-1)..t..string.sub(State.lines[State.cursor1.line].data, byte_offset)
|
State.lines[State.cursor1.line].data = string.sub(State.lines[State.cursor1.line].data, 1, byte_offset-1)..t..string.sub(State.lines[State.cursor1.line].data, byte_offset)
|
||||||
Text.clear_screen_line_cache(State, State.cursor1.line)
|
Text.clear_screen_line_cache(State, State.cursor1.line)
|
||||||
|
@ -286,7 +286,7 @@ function Text.keychord_press(State, chord)
|
||||||
Text.redraw_all(State) -- if we're scrolling, reclaim all fragments to avoid memory leaks
|
Text.redraw_all(State) -- if we're scrolling, reclaim all fragments to avoid memory leaks
|
||||||
end
|
end
|
||||||
Text.clear_screen_line_cache(State, State.cursor1.line)
|
Text.clear_screen_line_cache(State, State.cursor1.line)
|
||||||
assert(Text.le1(State.screen_top1, State.cursor1))
|
assert(Text.le1(State.screen_top1, State.cursor1), ('screen_top (line=%d,pos=%d) is below cursor (line=%d,pos=%d)'):format(State.screen_top1.line, State.screen_top1.pos, State.cursor1.line, State.cursor1.pos))
|
||||||
schedule_save(State)
|
schedule_save(State)
|
||||||
record_undo_event(State, {before=before, after=snapshot(State, State.cursor1.line)})
|
record_undo_event(State, {before=before, after=snapshot(State, State.cursor1.line)})
|
||||||
elseif chord == 'delete' then
|
elseif chord == 'delete' then
|
||||||
|
@ -452,7 +452,7 @@ function Text.pagedown(State)
|
||||||
end
|
end
|
||||||
|
|
||||||
function Text.up(State)
|
function Text.up(State)
|
||||||
assert(State.lines[State.cursor1.line].mode == 'text')
|
assert(State.lines[State.cursor1.line].mode == 'text', 'line is not text')
|
||||||
--? print('up', State.cursor1.line, State.cursor1.pos, State.screen_top1.line, State.screen_top1.pos)
|
--? print('up', State.cursor1.line, State.cursor1.pos, State.screen_top1.line, State.screen_top1.pos)
|
||||||
local screen_line_starting_pos, screen_line_index = Text.pos_at_start_of_screen_line(State, State.cursor1)
|
local screen_line_starting_pos, screen_line_index = Text.pos_at_start_of_screen_line(State, State.cursor1)
|
||||||
if screen_line_starting_pos == 1 then
|
if screen_line_starting_pos == 1 then
|
||||||
|
@ -478,7 +478,7 @@ function Text.up(State)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
-- move up one screen line in current line
|
-- move up one screen line in current line
|
||||||
assert(screen_line_index > 1)
|
assert(screen_line_index > 1, 'bumped up against top screen line in line')
|
||||||
local new_screen_line_starting_pos = State.line_cache[State.cursor1.line].screen_line_starting_pos[screen_line_index-1]
|
local new_screen_line_starting_pos = State.line_cache[State.cursor1.line].screen_line_starting_pos[screen_line_index-1]
|
||||||
local new_screen_line_starting_byte_offset = Text.offset(State.lines[State.cursor1.line].data, new_screen_line_starting_pos)
|
local new_screen_line_starting_byte_offset = Text.offset(State.lines[State.cursor1.line].data, new_screen_line_starting_pos)
|
||||||
local s = string.sub(State.lines[State.cursor1.line].data, new_screen_line_starting_byte_offset)
|
local s = string.sub(State.lines[State.cursor1.line].data, new_screen_line_starting_byte_offset)
|
||||||
|
@ -495,9 +495,9 @@ function Text.up(State)
|
||||||
end
|
end
|
||||||
|
|
||||||
function Text.down(State)
|
function Text.down(State)
|
||||||
assert(State.lines[State.cursor1.line].mode == 'text')
|
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, State.screen_bottom1.line, State.screen_bottom1.pos)
|
||||||
assert(State.cursor1.pos)
|
assert(State.cursor1.pos, 'cursor has no pos')
|
||||||
if Text.cursor_at_final_screen_line(State) then
|
if Text.cursor_at_final_screen_line(State) then
|
||||||
-- line is done, skip to next text line
|
-- line is done, skip to next text line
|
||||||
--? print('cursor at final screen line of its line')
|
--? print('cursor at final screen line of its line')
|
||||||
|
@ -571,7 +571,7 @@ function Text.word_left(State)
|
||||||
if State.cursor1.pos == 1 then
|
if State.cursor1.pos == 1 then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
assert(State.cursor1.pos > 1)
|
assert(State.cursor1.pos > 1, 'bumped up against start of line')
|
||||||
if Text.match(State.lines[State.cursor1.line].data, State.cursor1.pos-1, '%s') then
|
if Text.match(State.lines[State.cursor1.line].data, State.cursor1.pos-1, '%s') then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
|
@ -605,15 +605,14 @@ end
|
||||||
|
|
||||||
function Text.match(s, pos, pat)
|
function Text.match(s, pos, pat)
|
||||||
local start_offset = Text.offset(s, pos)
|
local start_offset = Text.offset(s, pos)
|
||||||
assert(start_offset)
|
|
||||||
local end_offset = Text.offset(s, pos+1)
|
local end_offset = Text.offset(s, pos+1)
|
||||||
assert(end_offset > start_offset)
|
assert(end_offset > start_offset, ('end_offset %d not > start_offset %d'):format(end_offset, start_offset))
|
||||||
local curr = s:sub(start_offset, end_offset-1)
|
local curr = s:sub(start_offset, end_offset-1)
|
||||||
return curr:match(pat)
|
return curr:match(pat)
|
||||||
end
|
end
|
||||||
|
|
||||||
function Text.left(State)
|
function Text.left(State)
|
||||||
assert(State.lines[State.cursor1.line].mode == 'text')
|
assert(State.lines[State.cursor1.line].mode == 'text', 'line is not text')
|
||||||
if State.cursor1.pos > 1 then
|
if State.cursor1.pos > 1 then
|
||||||
State.cursor1.pos = State.cursor1.pos-1
|
State.cursor1.pos = State.cursor1.pos-1
|
||||||
else
|
else
|
||||||
|
@ -646,7 +645,7 @@ function Text.right(State)
|
||||||
end
|
end
|
||||||
|
|
||||||
function Text.right_without_scroll(State)
|
function Text.right_without_scroll(State)
|
||||||
assert(State.lines[State.cursor1.line].mode == 'text')
|
assert(State.lines[State.cursor1.line].mode == 'text', 'line is not text')
|
||||||
if State.cursor1.pos <= utf8.len(State.lines[State.cursor1.line].data) then
|
if State.cursor1.pos <= utf8.len(State.lines[State.cursor1.line].data) then
|
||||||
State.cursor1.pos = State.cursor1.pos+1
|
State.cursor1.pos = State.cursor1.pos+1
|
||||||
else
|
else
|
||||||
|
@ -671,7 +670,7 @@ function Text.pos_at_start_of_screen_line(State, loc1)
|
||||||
return spos,i
|
return spos,i
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
assert(false)
|
assert(false, ('invalid pos %d'):format(loc1.pos))
|
||||||
end
|
end
|
||||||
|
|
||||||
function Text.pos_at_end_of_screen_line(State, loc1)
|
function Text.pos_at_end_of_screen_line(State, loc1)
|
||||||
|
@ -685,7 +684,7 @@ function Text.pos_at_end_of_screen_line(State, loc1)
|
||||||
end
|
end
|
||||||
most_recent_final_pos = spos-1
|
most_recent_final_pos = spos-1
|
||||||
end
|
end
|
||||||
assert(false)
|
assert(false, ('invalid pos %d'):format(loc1.pos))
|
||||||
end
|
end
|
||||||
|
|
||||||
function Text.cursor_at_final_screen_line(State)
|
function Text.cursor_at_final_screen_line(State)
|
||||||
|
@ -710,7 +709,7 @@ function Text.move_cursor_down_to_next_text_line_while_scrolling_again_if_necess
|
||||||
end
|
end
|
||||||
-- hack: insert a text line at bottom of file if necessary
|
-- hack: insert a text line at bottom of file if necessary
|
||||||
if State.cursor1.line > #State.lines then
|
if State.cursor1.line > #State.lines then
|
||||||
assert(State.cursor1.line == #State.lines+1)
|
assert(State.cursor1.line == #State.lines+1, 'tried to ensure bottom line of file is text, but failed')
|
||||||
table.insert(State.lines, {mode='text', data=''})
|
table.insert(State.lines, {mode='text', data=''})
|
||||||
table.insert(State.line_cache, {})
|
table.insert(State.line_cache, {})
|
||||||
end
|
end
|
||||||
|
@ -742,8 +741,8 @@ function Text.snap_cursor_to_bottom_of_screen(State)
|
||||||
end
|
end
|
||||||
y = y - h
|
y = y - h
|
||||||
else
|
else
|
||||||
assert(top2.line > 1)
|
assert(top2.line > 1, 'tried to snap cursor to buttom of screen but failed')
|
||||||
assert(State.lines[top2.line-1].mode == 'drawing')
|
assert(State.lines[top2.line-1].mode == 'drawing', "expected a drawing but it's not")
|
||||||
-- We currently can't draw partial drawings, so either skip it entirely
|
-- We currently can't draw partial drawings, so either skip it entirely
|
||||||
-- or not at all.
|
-- or not at all.
|
||||||
local h = Drawing_padding_height + Drawing.pixels(State.lines[top2.line-1].h, State.width)
|
local h = Drawing_padding_height + Drawing.pixels(State.lines[top2.line-1].h, State.width)
|
||||||
|
@ -775,7 +774,7 @@ end
|
||||||
function Text.to_pos_on_line(State, line_index, mx, my)
|
function Text.to_pos_on_line(State, line_index, mx, my)
|
||||||
local line = State.lines[line_index]
|
local line = State.lines[line_index]
|
||||||
local line_cache = State.line_cache[line_index]
|
local line_cache = State.line_cache[line_index]
|
||||||
assert(my >= line_cache.starty)
|
assert(my >= line_cache.starty, 'failed to map y pixel to line')
|
||||||
-- duplicate some logic from Text.draw
|
-- duplicate some logic from Text.draw
|
||||||
local y = line_cache.starty
|
local y = line_cache.starty
|
||||||
local start_screen_line_index = Text.screen_line_index(line_cache.screen_line_starting_pos, line_cache.startpos)
|
local start_screen_line_index = Text.screen_line_index(line_cache.screen_line_starting_pos, line_cache.startpos)
|
||||||
|
@ -798,7 +797,7 @@ function Text.to_pos_on_line(State, line_index, mx, my)
|
||||||
end
|
end
|
||||||
y = nexty
|
y = nexty
|
||||||
end
|
end
|
||||||
assert(false)
|
assert(false, 'failed to map y pixel to line')
|
||||||
end
|
end
|
||||||
|
|
||||||
function Text.screen_line_width(State, line_index, i)
|
function Text.screen_line_width(State, line_index, i)
|
||||||
|
@ -864,7 +863,7 @@ function Text.nearest_cursor_pos(line, x, left)
|
||||||
leftpos = curr
|
leftpos = curr
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
assert(false)
|
assert(false, 'failed to map x pixel to pos')
|
||||||
end
|
end
|
||||||
|
|
||||||
-- return the nearest index of line (in utf8 code points) which lies entirely
|
-- return the nearest index of line (in utf8 code points) which lies entirely
|
||||||
|
@ -895,7 +894,7 @@ function Text.nearest_pos_less_than(line, x)
|
||||||
left = curr
|
left = curr
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
assert(false)
|
assert(false, 'failed to map x pixel to pos')
|
||||||
end
|
end
|
||||||
|
|
||||||
function Text.x_after(s, pos)
|
function Text.x_after(s, pos)
|
||||||
|
@ -926,7 +925,7 @@ function Text.to2(State, loc1)
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
assert(result.screen_pos)
|
assert(result.screen_pos, 'failed to convert schema-1 coordinate to schema-2')
|
||||||
return result
|
return result
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -968,7 +967,7 @@ function Text.offset(s, pos1)
|
||||||
if result == nil then
|
if result == nil then
|
||||||
print(pos1, #s, s)
|
print(pos1, #s, s)
|
||||||
end
|
end
|
||||||
assert(result)
|
assert(result, "Text.offset returned nil; this is likely a failure to handle utf8")
|
||||||
return result
|
return result
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -36,11 +36,11 @@ end
|
||||||
-- Make copies of objects; the rest of the app may mutate them in place, but undo requires immutable histories.
|
-- Make copies of objects; the rest of the app may mutate them in place, but undo requires immutable histories.
|
||||||
function snapshot(State, s,e)
|
function snapshot(State, s,e)
|
||||||
-- Snapshot everything by default, but subset if requested.
|
-- Snapshot everything by default, but subset if requested.
|
||||||
assert(s)
|
assert(s, 'failed to snapshot operation for undo history')
|
||||||
if e == nil then
|
if e == nil then
|
||||||
e = s
|
e = s
|
||||||
end
|
end
|
||||||
assert(#State.lines > 0)
|
assert(#State.lines > 0, 'failed to snapshot operation for undo history')
|
||||||
if s < 1 then s = 1 end
|
if s < 1 then s = 1 end
|
||||||
if s > #State.lines then s = #State.lines end
|
if s > #State.lines then s = #State.lines end
|
||||||
if e < 1 then e = 1 end
|
if e < 1 then e = 1 end
|
||||||
|
@ -65,8 +65,7 @@ function snapshot(State, s,e)
|
||||||
elseif line.mode == 'drawing' then
|
elseif line.mode == 'drawing' then
|
||||||
table.insert(event.lines, {mode='drawing', h=line.h, points=deepcopy(line.points), shapes=deepcopy(line.shapes), pending={}})
|
table.insert(event.lines, {mode='drawing', h=line.h, points=deepcopy(line.points), shapes=deepcopy(line.shapes), pending={}})
|
||||||
else
|
else
|
||||||
print(line.mode)
|
assert(false, ('unknown line mode %s'):format(line.mode))
|
||||||
assert(false)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return event
|
return event
|
||||||
|
@ -80,22 +79,22 @@ function patch(lines, from, to)
|
||||||
--? lines[from.start_line] = to.lines[1]
|
--? lines[from.start_line] = to.lines[1]
|
||||||
--? return
|
--? return
|
||||||
--? end
|
--? end
|
||||||
assert(from.start_line == to.start_line)
|
assert(from.start_line == to.start_line, 'failed to patch undo operation')
|
||||||
for i=from.end_line,from.start_line,-1 do
|
for i=from.end_line,from.start_line,-1 do
|
||||||
table.remove(lines, i)
|
table.remove(lines, i)
|
||||||
end
|
end
|
||||||
assert(#to.lines == to.end_line-to.start_line+1)
|
assert(#to.lines == to.end_line-to.start_line+1, 'failed to patch undo operation')
|
||||||
for i=1,#to.lines do
|
for i=1,#to.lines do
|
||||||
table.insert(lines, to.start_line+i-1, to.lines[i])
|
table.insert(lines, to.start_line+i-1, to.lines[i])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function patch_placeholders(line_cache, from, to)
|
function patch_placeholders(line_cache, from, to)
|
||||||
assert(from.start_line == to.start_line)
|
assert(from.start_line == to.start_line, 'failed to patch undo operation')
|
||||||
for i=from.end_line,from.start_line,-1 do
|
for i=from.end_line,from.start_line,-1 do
|
||||||
table.remove(line_cache, i)
|
table.remove(line_cache, i)
|
||||||
end
|
end
|
||||||
assert(#to.lines == to.end_line-to.start_line+1)
|
assert(#to.lines == to.end_line-to.start_line+1, 'failed to patch undo operation')
|
||||||
for i=1,#to.lines do
|
for i=1,#to.lines do
|
||||||
table.insert(line_cache, to.start_line+i-1, {})
|
table.insert(line_cache, to.start_line+i-1, {})
|
||||||
end
|
end
|
||||||
|
|
47
text.lua
47
text.lua
|
@ -13,7 +13,7 @@ function Text.draw(State, line_index, y, startpos)
|
||||||
local final_screen_line_starting_pos = startpos -- track value to return
|
local final_screen_line_starting_pos = startpos -- track value to return
|
||||||
Text.populate_screen_line_starting_pos(State, line_index)
|
Text.populate_screen_line_starting_pos(State, line_index)
|
||||||
Text.populate_link_offsets(State, line_index)
|
Text.populate_link_offsets(State, line_index)
|
||||||
assert(#line_cache.screen_line_starting_pos >= 1)
|
assert(#line_cache.screen_line_starting_pos >= 1, 'line cache missing screen line info')
|
||||||
for i=1,#line_cache.screen_line_starting_pos do
|
for i=1,#line_cache.screen_line_starting_pos do
|
||||||
local pos = line_cache.screen_line_starting_pos[i]
|
local pos = line_cache.screen_line_starting_pos[i]
|
||||||
if pos < startpos then
|
if pos < startpos then
|
||||||
|
@ -205,7 +205,7 @@ function Text.text_input(State, t)
|
||||||
end
|
end
|
||||||
|
|
||||||
function Text.insert_at_cursor(State, t)
|
function Text.insert_at_cursor(State, t)
|
||||||
assert(State.lines[State.cursor1.line].mode == 'text')
|
assert(State.lines[State.cursor1.line].mode == 'text', 'line is not text')
|
||||||
local byte_offset = Text.offset(State.lines[State.cursor1.line].data, State.cursor1.pos)
|
local byte_offset = Text.offset(State.lines[State.cursor1.line].data, State.cursor1.pos)
|
||||||
State.lines[State.cursor1.line].data = string.sub(State.lines[State.cursor1.line].data, 1, byte_offset-1)..t..string.sub(State.lines[State.cursor1.line].data, byte_offset)
|
State.lines[State.cursor1.line].data = string.sub(State.lines[State.cursor1.line].data, 1, byte_offset-1)..t..string.sub(State.lines[State.cursor1.line].data, byte_offset)
|
||||||
Text.clear_screen_line_cache(State, State.cursor1.line)
|
Text.clear_screen_line_cache(State, State.cursor1.line)
|
||||||
|
@ -282,7 +282,7 @@ function Text.keychord_press(State, chord)
|
||||||
Text.redraw_all(State) -- if we're scrolling, reclaim all fragments to avoid memory leaks
|
Text.redraw_all(State) -- if we're scrolling, reclaim all fragments to avoid memory leaks
|
||||||
end
|
end
|
||||||
Text.clear_screen_line_cache(State, State.cursor1.line)
|
Text.clear_screen_line_cache(State, State.cursor1.line)
|
||||||
assert(Text.le1(State.screen_top1, State.cursor1))
|
assert(Text.le1(State.screen_top1, State.cursor1), ('screen_top (line=%d,pos=%d) is below cursor (line=%d,pos=%d)'):format(State.screen_top1.line, State.screen_top1.pos, State.cursor1.line, State.cursor1.pos))
|
||||||
schedule_save(State)
|
schedule_save(State)
|
||||||
record_undo_event(State, {before=before, after=snapshot(State, State.cursor1.line)})
|
record_undo_event(State, {before=before, after=snapshot(State, State.cursor1.line)})
|
||||||
elseif chord == 'delete' then
|
elseif chord == 'delete' then
|
||||||
|
@ -459,7 +459,7 @@ function Text.pagedown(State)
|
||||||
end
|
end
|
||||||
|
|
||||||
function Text.up(State)
|
function Text.up(State)
|
||||||
assert(State.lines[State.cursor1.line].mode == 'text')
|
assert(State.lines[State.cursor1.line].mode == 'text', 'line is not text')
|
||||||
--? print('up', State.cursor1.line, State.cursor1.pos, State.screen_top1.line, State.screen_top1.pos)
|
--? print('up', State.cursor1.line, State.cursor1.pos, State.screen_top1.line, State.screen_top1.pos)
|
||||||
local screen_line_starting_pos, screen_line_index = Text.pos_at_start_of_screen_line(State, State.cursor1)
|
local screen_line_starting_pos, screen_line_index = Text.pos_at_start_of_screen_line(State, State.cursor1)
|
||||||
if screen_line_starting_pos == 1 then
|
if screen_line_starting_pos == 1 then
|
||||||
|
@ -485,7 +485,7 @@ function Text.up(State)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
-- move up one screen line in current line
|
-- move up one screen line in current line
|
||||||
assert(screen_line_index > 1)
|
assert(screen_line_index > 1, 'bumped up against top screen line in line')
|
||||||
local new_screen_line_starting_pos = State.line_cache[State.cursor1.line].screen_line_starting_pos[screen_line_index-1]
|
local new_screen_line_starting_pos = State.line_cache[State.cursor1.line].screen_line_starting_pos[screen_line_index-1]
|
||||||
local new_screen_line_starting_byte_offset = Text.offset(State.lines[State.cursor1.line].data, new_screen_line_starting_pos)
|
local new_screen_line_starting_byte_offset = Text.offset(State.lines[State.cursor1.line].data, new_screen_line_starting_pos)
|
||||||
local s = string.sub(State.lines[State.cursor1.line].data, new_screen_line_starting_byte_offset)
|
local s = string.sub(State.lines[State.cursor1.line].data, new_screen_line_starting_byte_offset)
|
||||||
|
@ -502,9 +502,9 @@ function Text.up(State)
|
||||||
end
|
end
|
||||||
|
|
||||||
function Text.down(State)
|
function Text.down(State)
|
||||||
assert(State.lines[State.cursor1.line].mode == 'text')
|
assert(State.lines[State.cursor1.line].mode == 'text', 'line is not text')
|
||||||
--? print('down', State.screen_top1.line, State.screen_top1.pos, State.cursor1.line, State.cursor1.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, State.screen_bottom1.line, State.screen_bottom1.pos)
|
||||||
assert(State.cursor1.pos)
|
assert(State.cursor1.pos, 'cursor has no pos')
|
||||||
if Text.cursor_at_final_screen_line(State) then
|
if Text.cursor_at_final_screen_line(State) then
|
||||||
-- line is done, skip to next text line
|
-- line is done, skip to next text line
|
||||||
--? print('cursor at final screen line of its line')
|
--? print('cursor at final screen line of its line')
|
||||||
|
@ -579,7 +579,7 @@ function Text.word_left(State)
|
||||||
if State.cursor1.pos == 1 then
|
if State.cursor1.pos == 1 then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
assert(State.cursor1.pos > 1)
|
assert(State.cursor1.pos > 1, 'bumped up against start of line')
|
||||||
if Text.match(State.lines[State.cursor1.line].data, State.cursor1.pos-1, '%s') then
|
if Text.match(State.lines[State.cursor1.line].data, State.cursor1.pos-1, '%s') then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
|
@ -613,15 +613,14 @@ end
|
||||||
|
|
||||||
function Text.match(s, pos, pat)
|
function Text.match(s, pos, pat)
|
||||||
local start_offset = Text.offset(s, pos)
|
local start_offset = Text.offset(s, pos)
|
||||||
assert(start_offset)
|
|
||||||
local end_offset = Text.offset(s, pos+1)
|
local end_offset = Text.offset(s, pos+1)
|
||||||
assert(end_offset > start_offset)
|
assert(end_offset > start_offset, ('end_offset %d not > start_offset %d'):format(end_offset, start_offset))
|
||||||
local curr = s:sub(start_offset, end_offset-1)
|
local curr = s:sub(start_offset, end_offset-1)
|
||||||
return curr:match(pat)
|
return curr:match(pat)
|
||||||
end
|
end
|
||||||
|
|
||||||
function Text.left(State)
|
function Text.left(State)
|
||||||
assert(State.lines[State.cursor1.line].mode == 'text')
|
assert(State.lines[State.cursor1.line].mode == 'text', 'line is not text')
|
||||||
if State.cursor1.pos > 1 then
|
if State.cursor1.pos > 1 then
|
||||||
State.cursor1.pos = State.cursor1.pos-1
|
State.cursor1.pos = State.cursor1.pos-1
|
||||||
else
|
else
|
||||||
|
@ -654,7 +653,7 @@ function Text.right(State)
|
||||||
end
|
end
|
||||||
|
|
||||||
function Text.right_without_scroll(State)
|
function Text.right_without_scroll(State)
|
||||||
assert(State.lines[State.cursor1.line].mode == 'text')
|
assert(State.lines[State.cursor1.line].mode == 'text', 'line is not text')
|
||||||
if State.cursor1.pos <= utf8.len(State.lines[State.cursor1.line].data) then
|
if State.cursor1.pos <= utf8.len(State.lines[State.cursor1.line].data) then
|
||||||
State.cursor1.pos = State.cursor1.pos+1
|
State.cursor1.pos = State.cursor1.pos+1
|
||||||
else
|
else
|
||||||
|
@ -679,7 +678,7 @@ function Text.pos_at_start_of_screen_line(State, loc1)
|
||||||
return spos,i
|
return spos,i
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
assert(false)
|
assert(false, ('invalid pos %d'):format(loc1.pos))
|
||||||
end
|
end
|
||||||
|
|
||||||
function Text.pos_at_end_of_screen_line(State, loc1)
|
function Text.pos_at_end_of_screen_line(State, loc1)
|
||||||
|
@ -693,7 +692,7 @@ function Text.pos_at_end_of_screen_line(State, loc1)
|
||||||
end
|
end
|
||||||
most_recent_final_pos = spos-1
|
most_recent_final_pos = spos-1
|
||||||
end
|
end
|
||||||
assert(false)
|
assert(false, ('invalid pos %d'):format(loc1.pos))
|
||||||
end
|
end
|
||||||
|
|
||||||
function Text.cursor_at_final_screen_line(State)
|
function Text.cursor_at_final_screen_line(State)
|
||||||
|
@ -718,7 +717,7 @@ function Text.move_cursor_down_to_next_text_line_while_scrolling_again_if_necess
|
||||||
end
|
end
|
||||||
-- hack: insert a text line at bottom of file if necessary
|
-- hack: insert a text line at bottom of file if necessary
|
||||||
if State.cursor1.line > #State.lines then
|
if State.cursor1.line > #State.lines then
|
||||||
assert(State.cursor1.line == #State.lines+1)
|
assert(State.cursor1.line == #State.lines+1, 'tried to ensure bottom line of file is text, but failed')
|
||||||
table.insert(State.lines, {mode='text', data=''})
|
table.insert(State.lines, {mode='text', data=''})
|
||||||
table.insert(State.line_cache, {})
|
table.insert(State.line_cache, {})
|
||||||
end
|
end
|
||||||
|
@ -759,8 +758,8 @@ function Text.snap_cursor_to_bottom_of_screen(State)
|
||||||
end
|
end
|
||||||
y = y - h
|
y = y - h
|
||||||
else
|
else
|
||||||
assert(top2.line > 1)
|
assert(top2.line > 1, 'tried to snap cursor to buttom of screen but failed')
|
||||||
assert(State.lines[top2.line-1].mode == 'drawing')
|
assert(State.lines[top2.line-1].mode == 'drawing', "expected a drawing but it's not")
|
||||||
-- We currently can't draw partial drawings, so either skip it entirely
|
-- We currently can't draw partial drawings, so either skip it entirely
|
||||||
-- or not at all.
|
-- or not at all.
|
||||||
local h = Drawing_padding_height + Drawing.pixels(State.lines[top2.line-1].h, State.width)
|
local h = Drawing_padding_height + Drawing.pixels(State.lines[top2.line-1].h, State.width)
|
||||||
|
@ -793,7 +792,7 @@ function Text.to_pos_on_line(State, line_index, mx, my)
|
||||||
--? print('Text.to_pos_on_line', State.left, State.right, App.screen.width, mx)
|
--? print('Text.to_pos_on_line', State.left, State.right, App.screen.width, mx)
|
||||||
local line = State.lines[line_index]
|
local line = State.lines[line_index]
|
||||||
local line_cache = State.line_cache[line_index]
|
local line_cache = State.line_cache[line_index]
|
||||||
assert(my >= line_cache.starty)
|
assert(my >= line_cache.starty, 'failed to map y pixel to line')
|
||||||
-- duplicate some logic from Text.draw
|
-- duplicate some logic from Text.draw
|
||||||
local y = line_cache.starty
|
local y = line_cache.starty
|
||||||
local start_screen_line_index = Text.screen_line_index(line_cache.screen_line_starting_pos, line_cache.startpos)
|
local start_screen_line_index = Text.screen_line_index(line_cache.screen_line_starting_pos, line_cache.startpos)
|
||||||
|
@ -816,7 +815,7 @@ function Text.to_pos_on_line(State, line_index, mx, my)
|
||||||
end
|
end
|
||||||
y = nexty
|
y = nexty
|
||||||
end
|
end
|
||||||
assert(false)
|
assert(false, 'failed to map y pixel to line')
|
||||||
end
|
end
|
||||||
|
|
||||||
function Text.screen_line_width(State, line_index, i)
|
function Text.screen_line_width(State, line_index, i)
|
||||||
|
@ -894,7 +893,7 @@ function Text.nearest_cursor_pos(line, x, left)
|
||||||
leftpos = curr
|
leftpos = curr
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
assert(false)
|
assert(false, 'failed to map x pixel to pos')
|
||||||
end
|
end
|
||||||
|
|
||||||
-- return the nearest index of line (in utf8 code points) which lies entirely
|
-- return the nearest index of line (in utf8 code points) which lies entirely
|
||||||
|
@ -925,7 +924,7 @@ function Text.nearest_pos_less_than(line, x)
|
||||||
left = curr
|
left = curr
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
assert(false)
|
assert(false, 'failed to map x pixel to pos')
|
||||||
end
|
end
|
||||||
|
|
||||||
function Text.x_after(s, pos)
|
function Text.x_after(s, pos)
|
||||||
|
@ -956,7 +955,7 @@ function Text.to2(State, loc1)
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
assert(result.screen_pos)
|
assert(result.screen_pos, 'failed to convert schema-1 coordinate to schema-2')
|
||||||
return result
|
return result
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1004,7 +1003,7 @@ function Text.offset(s, pos1)
|
||||||
if result == nil then
|
if result == nil then
|
||||||
print(pos1, #s, s)
|
print(pos1, #s, s)
|
||||||
end
|
end
|
||||||
assert(result)
|
assert(result, "Text.offset returned nil; this is likely a failure to handle utf8")
|
||||||
return result
|
return result
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
14
undo.lua
14
undo.lua
|
@ -36,11 +36,11 @@ end
|
||||||
-- Make copies of objects; the rest of the app may mutate them in place, but undo requires immutable histories.
|
-- Make copies of objects; the rest of the app may mutate them in place, but undo requires immutable histories.
|
||||||
function snapshot(State, s,e)
|
function snapshot(State, s,e)
|
||||||
-- Snapshot everything by default, but subset if requested.
|
-- Snapshot everything by default, but subset if requested.
|
||||||
assert(s)
|
assert(s, 'failed to snapshot operation for undo history')
|
||||||
if e == nil then
|
if e == nil then
|
||||||
e = s
|
e = s
|
||||||
end
|
end
|
||||||
assert(#State.lines > 0)
|
assert(#State.lines > 0, 'failed to snapshot operation for undo history')
|
||||||
if s < 1 then s = 1 end
|
if s < 1 then s = 1 end
|
||||||
if s > #State.lines then s = #State.lines end
|
if s > #State.lines then s = #State.lines end
|
||||||
if e < 1 then e = 1 end
|
if e < 1 then e = 1 end
|
||||||
|
@ -66,7 +66,7 @@ function snapshot(State, s,e)
|
||||||
table.insert(event.lines, {mode='drawing', h=line.h, points=deepcopy(line.points), shapes=deepcopy(line.shapes), pending={}})
|
table.insert(event.lines, {mode='drawing', h=line.h, points=deepcopy(line.points), shapes=deepcopy(line.shapes), pending={}})
|
||||||
else
|
else
|
||||||
print(line.mode)
|
print(line.mode)
|
||||||
assert(false)
|
assert(false, ('unknown line mode %s'):format(line.mode))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return event
|
return event
|
||||||
|
@ -80,22 +80,22 @@ function patch(lines, from, to)
|
||||||
--? lines[from.start_line] = to.lines[1]
|
--? lines[from.start_line] = to.lines[1]
|
||||||
--? return
|
--? return
|
||||||
--? end
|
--? end
|
||||||
assert(from.start_line == to.start_line)
|
assert(from.start_line == to.start_line, 'failed to patch undo operation')
|
||||||
for i=from.end_line,from.start_line,-1 do
|
for i=from.end_line,from.start_line,-1 do
|
||||||
table.remove(lines, i)
|
table.remove(lines, i)
|
||||||
end
|
end
|
||||||
assert(#to.lines == to.end_line-to.start_line+1)
|
assert(#to.lines == to.end_line-to.start_line+1, 'failed to patch undo operation')
|
||||||
for i=1,#to.lines do
|
for i=1,#to.lines do
|
||||||
table.insert(lines, to.start_line+i-1, to.lines[i])
|
table.insert(lines, to.start_line+i-1, to.lines[i])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function patch_placeholders(line_cache, from, to)
|
function patch_placeholders(line_cache, from, to)
|
||||||
assert(from.start_line == to.start_line)
|
assert(from.start_line == to.start_line, 'failed to patch undo operation')
|
||||||
for i=from.end_line,from.start_line,-1 do
|
for i=from.end_line,from.start_line,-1 do
|
||||||
table.remove(line_cache, i)
|
table.remove(line_cache, i)
|
||||||
end
|
end
|
||||||
assert(#to.lines == to.end_line-to.start_line+1)
|
assert(#to.lines == to.end_line-to.start_line+1, 'failed to patch undo operation')
|
||||||
for i=1,#to.lines do
|
for i=1,#to.lines do
|
||||||
table.insert(line_cache, to.start_line+i-1, {})
|
table.insert(line_cache, to.start_line+i-1, {})
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue