2022-08-02 19:35:42 +00:00
|
|
|
Recently_modified_lookback_window = 30
|
2022-07-26 17:04:07 +00:00
|
|
|
|
2022-07-31 05:44:37 +00:00
|
|
|
Opposite = {
|
|
|
|
next='previous',
|
|
|
|
previous='next',
|
|
|
|
child='parent',
|
|
|
|
parent='child',
|
|
|
|
}
|
|
|
|
|
2022-07-30 21:43:47 +00:00
|
|
|
-- Candidate commands to show in in the command palette in different contexts.
|
|
|
|
-- Ideally we'd have rules for:
|
|
|
|
-- contexts to show a command at
|
|
|
|
-- order in which to show commands for each context
|
|
|
|
-- But I don't want to design that, so I'm just going to embrace a
|
|
|
|
-- combinatorial explosion of duplication for a while.
|
2022-07-28 04:29:41 +00:00
|
|
|
Commands = {
|
2022-07-30 21:43:47 +00:00
|
|
|
normal={
|
|
|
|
'capture',
|
|
|
|
'edit note at cursor (ctrl+e)',
|
|
|
|
'maximize note',
|
|
|
|
'close column surrounding cursor',
|
2022-08-03 04:07:33 +00:00
|
|
|
'add (___) (create immediately link)',
|
|
|
|
'step (___) (open link in new column)',
|
2022-08-03 04:10:48 +00:00
|
|
|
'extract (open note in new column)',
|
2022-08-02 22:46:22 +00:00
|
|
|
'unroll (___) (repeatedly step from cursor)',
|
2022-08-03 04:07:33 +00:00
|
|
|
'append (___) (add link after repeatedly stepping from cursor)',
|
2022-07-30 21:43:47 +00:00
|
|
|
'down one pane (ctrl+down)',
|
|
|
|
'up one pane (ctrl+up)',
|
|
|
|
'top pane of column (ctrl+home)',
|
|
|
|
'bottom pane of column (ctrl+end)',
|
|
|
|
'wider columns (X)',
|
|
|
|
'narrower columns (x)',
|
|
|
|
'recently modified',
|
2022-07-31 05:10:32 +00:00
|
|
|
'open file ___',
|
2022-08-03 04:31:28 +00:00
|
|
|
'reload all',
|
2022-08-03 04:49:51 +00:00
|
|
|
'delete note at cursor from disk (if possible)',
|
2022-07-30 21:43:47 +00:00
|
|
|
'search (all notes)',
|
|
|
|
'/ (find on screen)',
|
|
|
|
},
|
|
|
|
editable={
|
|
|
|
'exit editing (ctrl+e)',
|
|
|
|
'capture',
|
|
|
|
'maximize note',
|
|
|
|
'close column surrounding cursor',
|
2022-08-03 04:07:33 +00:00
|
|
|
'add (___) (create immediately link)',
|
|
|
|
'step (___) (open link in new column)',
|
|
|
|
'unroll (___) (repeatedly step from cursor)',
|
|
|
|
'append (___) (add link after repeatedly stepping from cursor)',
|
2022-07-30 21:43:47 +00:00
|
|
|
'down one pane (ctrl+down)',
|
|
|
|
'up one pane (ctrl+up)',
|
|
|
|
'top pane of column (ctrl+home)',
|
|
|
|
'bottom pane of column (ctrl+end)',
|
|
|
|
'wider columns (X)',
|
|
|
|
'narrower columns (x)',
|
|
|
|
'recently modified',
|
2022-08-03 04:31:28 +00:00
|
|
|
'reload all',
|
2022-07-30 21:43:47 +00:00
|
|
|
'search (all notes)',
|
|
|
|
'/ (find on screen)',
|
|
|
|
},
|
|
|
|
maximized={
|
|
|
|
'back to surface',
|
|
|
|
'edit note at cursor (ctrl+e)',
|
|
|
|
'reload note at cursor from disk',
|
|
|
|
'/ (find on screen)',
|
|
|
|
},
|
|
|
|
maximized_editable={
|
|
|
|
'exit editing (ctrl+e)',
|
|
|
|
'back to surface',
|
|
|
|
'reload note at cursor from disk',
|
|
|
|
'search (all notes)',
|
|
|
|
'/ (find on screen)',
|
|
|
|
},
|
2022-07-28 04:29:41 +00:00
|
|
|
}
|
|
|
|
|
2022-08-04 06:21:54 +00:00
|
|
|
-- We incrementally create the menu based on context. Menu_cursor tracks how
|
|
|
|
-- far along the screen width we've gotten.
|
|
|
|
Menu_cursor = 0
|
|
|
|
Palette_cursor = {y=0, x=0}
|
|
|
|
|
|
|
|
function draw_menu_bar()
|
|
|
|
if App.run_tests then return end -- disable in tests
|
|
|
|
App.color(Command_palette_background_color)
|
|
|
|
love.graphics.rectangle('fill', 0,0, App.screen.width, Menu_status_bar_height)
|
|
|
|
App.color(Command_palette_border_color)
|
|
|
|
love.graphics.rectangle('line', 0,0, App.screen.width, Menu_status_bar_height)
|
|
|
|
if Display_settings.show_palette then
|
|
|
|
return
|
|
|
|
end
|
|
|
|
App.color(Command_palette_command_color)
|
|
|
|
love.graphics.print('ctrl+enter: search commands...', 5,5)
|
|
|
|
App.color(Command_palette_border_color)
|
|
|
|
Menu_cursor = 360
|
|
|
|
love.graphics.line(Menu_cursor-10,2, Menu_cursor-10,Menu_status_bar_height-2)
|
|
|
|
if Display_settings.mode == 'normal' then
|
|
|
|
local pane = Surface[Cursor_pane.col][Cursor_pane.row]
|
|
|
|
if not pane.editable then
|
|
|
|
local left_sx = left_edge_sx(Cursor_pane.col)
|
|
|
|
local up_sy = up_edge_sy(Cursor_pane.col, Cursor_pane.row)
|
|
|
|
if overlap(left_sx, left_sx+Display_settings.column_width, Display_settings.x, Display_settings.x+App.screen.width)
|
|
|
|
and overlap(up_sy, up_sy+pane.height, Display_settings.y, Display_settings.y+App.screen.height-Header_height) then
|
|
|
|
add_hotkey_to_menu('ctrl+e: edit')
|
|
|
|
add_panning_hotkeys_to_menu()
|
|
|
|
end
|
|
|
|
add_hotkey_to_menu('x/X: narrower/wider columns')
|
|
|
|
else
|
|
|
|
if pane.cursor_x >= 0 and pane.cursor_x < App.screen.width and pane.cursor_y >= Header_height and pane.cursor_y < App.screen.height then
|
|
|
|
add_hotkey_to_menu('ctrl+e: stop editing')
|
|
|
|
else
|
|
|
|
add_panning_hotkeys_to_menu()
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
add_hotkey_to_menu('ctrl+= ctrl+- ctrl+0: zoom')
|
|
|
|
end
|
|
|
|
|
|
|
|
function add_panning_hotkeys_to_menu()
|
|
|
|
add_hotkey_to_menu('arrows shift+arrows ctrl+up/down: pan')
|
|
|
|
end
|
|
|
|
|
|
|
|
function add_hotkey_to_menu(s)
|
|
|
|
if Menu_text_cache[s] == nil then
|
|
|
|
Menu_text_cache[s] = App.newText(love.graphics.getFont(), s)
|
|
|
|
end
|
|
|
|
local width = App.width(Menu_text_cache[s])
|
|
|
|
if Menu_cursor + width > App.screen.width - 5 then
|
|
|
|
return
|
|
|
|
end
|
|
|
|
App.color(Command_palette_command_color)
|
|
|
|
App.screen.draw(Menu_text_cache[s], Menu_cursor,5)
|
|
|
|
Menu_cursor = Menu_cursor + width + 30
|
|
|
|
end
|
|
|
|
|
|
|
|
-- unused
|
|
|
|
function draw_maximized_mode_menu_bar(left, right)
|
|
|
|
App.color(Command_palette_background_color)
|
|
|
|
love.graphics.rectangle('fill', left-Margin_left,0, Margin_left+right-left+Margin_right, Menu_status_bar_height)
|
|
|
|
App.color(Command_palette_border_color)
|
|
|
|
love.graphics.rectangle('line', left-Margin_left,0, Margin_left+right-left+Margin_right, Menu_status_bar_height)
|
|
|
|
end
|
|
|
|
|
2022-08-04 04:03:12 +00:00
|
|
|
function keychord_pressed_on_command_palette(chord, key)
|
2022-07-24 04:00:49 +00:00
|
|
|
if chord == 'escape' then
|
2022-07-29 03:36:39 +00:00
|
|
|
Display_settings.show_palette = false
|
2022-07-27 17:01:32 +00:00
|
|
|
-- don't forget text in case we want to continue it
|
2022-07-24 04:00:49 +00:00
|
|
|
elseif chord == 'backspace' then
|
|
|
|
local len = utf8.len(Display_settings.palette)
|
|
|
|
local byte_offset = Text.offset(Display_settings.palette, len)
|
|
|
|
Display_settings.palette = string.sub(Display_settings.palette, 1, byte_offset-1)
|
|
|
|
Display_settings.palette_text = App.newText(love.graphics.getFont(), Display_settings.palette)
|
2022-07-24 05:21:00 +00:00
|
|
|
elseif chord == 'tab' then
|
|
|
|
-- select top candidate, but don't submit
|
|
|
|
local candidates = candidates()
|
2022-07-30 22:40:22 +00:00
|
|
|
Display_settings.palette = command_string(candidates[1])
|
2022-07-24 05:21:00 +00:00
|
|
|
Display_settings.palette_text = App.newText(love.graphics.getFont(), Display_settings.palette)
|
2022-07-24 04:00:49 +00:00
|
|
|
elseif chord == 'return' then
|
2022-07-24 05:21:00 +00:00
|
|
|
-- select top candidate and submit
|
2022-07-24 04:00:49 +00:00
|
|
|
-- TODO: handle search command
|
|
|
|
local candidates = candidates()
|
|
|
|
if #candidates > 0 then
|
2022-07-29 18:32:53 +00:00
|
|
|
if file_exists(Directory..candidates[1]) then
|
2022-07-24 07:08:52 +00:00
|
|
|
command.open_file(candidates[1])
|
2022-07-24 04:26:35 +00:00
|
|
|
else
|
2022-07-30 22:40:22 +00:00
|
|
|
run_command(command_string(candidates[1]))
|
2022-07-24 04:26:35 +00:00
|
|
|
end
|
2022-07-30 22:32:19 +00:00
|
|
|
else
|
|
|
|
-- try to run the command as if it contains args
|
|
|
|
run_command_with_args(Display_settings.palette)
|
2022-07-24 04:00:49 +00:00
|
|
|
end
|
2022-07-27 17:01:32 +00:00
|
|
|
-- forget text for next command
|
2022-07-29 03:33:45 +00:00
|
|
|
Display_settings.palette = ''
|
|
|
|
Display_settings.palette_text = App.newText(love.graphics.getFont(), '')
|
2022-07-27 17:01:32 +00:00
|
|
|
--
|
2022-07-29 03:36:39 +00:00
|
|
|
Display_settings.show_palette = false
|
2022-07-24 04:00:49 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2022-07-30 22:40:22 +00:00
|
|
|
function command_string(s)
|
2022-07-31 05:44:37 +00:00
|
|
|
local result, _ = s:gsub(' %(.*', ''):gsub(' _.*', '')
|
|
|
|
return result
|
2022-07-30 22:40:22 +00:00
|
|
|
end
|
|
|
|
|
2022-07-24 04:00:49 +00:00
|
|
|
function draw_command_palette()
|
|
|
|
-- background
|
|
|
|
App.color(Command_palette_background_color)
|
2022-08-04 06:21:54 +00:00
|
|
|
love.graphics.rectangle('fill', 0,0, App.screen.width, Menu_status_bar_height)
|
2022-07-24 04:00:49 +00:00
|
|
|
App.color(Command_palette_border_color)
|
2022-08-04 06:21:54 +00:00
|
|
|
love.graphics.rectangle('line', 0,0, App.screen.width, Menu_status_bar_height)
|
2022-07-24 04:00:49 +00:00
|
|
|
-- input box
|
|
|
|
App.color(Command_palette_command_color)
|
2022-08-04 06:21:54 +00:00
|
|
|
draw_palette_input(5, 5)
|
2022-07-24 04:00:49 +00:00
|
|
|
-- alternatives
|
2022-08-04 06:21:54 +00:00
|
|
|
App.color(Command_palette_background_color)
|
|
|
|
love.graphics.rectangle('fill', 0, Menu_status_bar_height, App.screen.width, 5+5*Line_height+5)
|
|
|
|
App.color(Command_palette_border_color)
|
|
|
|
love.graphics.rectangle('line', 0, Menu_status_bar_height, App.screen.width, 5+5*Line_height+5)
|
2022-07-24 04:00:49 +00:00
|
|
|
App.color(Command_palette_alternatives_color)
|
2022-08-04 06:21:54 +00:00
|
|
|
Palette_cursor = {y=Menu_status_bar_height+5, x=5, nextx=5}
|
|
|
|
for i,cmd in ipairs(candidates()) do
|
|
|
|
add_command_to_palette(cmd)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function add_command_to_palette(s)
|
|
|
|
if Menu_text_cache[s] == nil then
|
|
|
|
Menu_text_cache[s] = App.newText(love.graphics.getFont(), s)
|
|
|
|
end
|
|
|
|
local width = App.width(Menu_text_cache[s])
|
|
|
|
if Palette_cursor.x + width/2 > App.screen.width - 5 then
|
|
|
|
return
|
|
|
|
end
|
|
|
|
App.screen.draw(Menu_text_cache[s], Palette_cursor.x, Palette_cursor.y)
|
|
|
|
Palette_cursor.nextx = math.max(Palette_cursor.nextx, Palette_cursor.x+width+10)
|
|
|
|
Palette_cursor.y = Palette_cursor.y + Line_height
|
|
|
|
if Palette_cursor.y >= Menu_status_bar_height + 5+5*Line_height then
|
|
|
|
love.graphics.line(Palette_cursor.nextx, Menu_status_bar_height+2, Palette_cursor.nextx, Menu_status_bar_height + 5+5*Line_height+5)
|
|
|
|
Palette_cursor.x = Palette_cursor.nextx + 5
|
|
|
|
Palette_cursor.y = Menu_status_bar_height+5
|
2022-07-24 04:00:49 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function draw_palette_input(x, y)
|
|
|
|
love.graphics.draw(Display_settings.palette_text, x,y)
|
|
|
|
x = x+App.width(Display_settings.palette_text)
|
|
|
|
draw_cursor(x, y)
|
|
|
|
end
|
|
|
|
|
|
|
|
function draw_cursor(x, y)
|
|
|
|
-- blink every 0.5s
|
|
|
|
if math.floor(Cursor_time*2)%2 == 0 then
|
|
|
|
App.color(Cursor_color)
|
|
|
|
love.graphics.rectangle('fill', x,y, 3,Line_height)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function candidates()
|
2022-07-29 05:08:59 +00:00
|
|
|
-- slight context-sensitive tweaks
|
2022-07-30 22:32:19 +00:00
|
|
|
local candidates = initial_candidates()
|
|
|
|
if Display_settings.palette == '' then
|
|
|
|
return candidates
|
|
|
|
elseif Display_settings.palette:sub(1,1) == '/' then
|
|
|
|
return {}
|
|
|
|
else
|
|
|
|
local results = filter_candidates(candidates, Display_settings.palette)
|
|
|
|
append(results, file_candidates(Display_settings.palette))
|
|
|
|
return results
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function initial_candidates()
|
2022-08-04 00:05:43 +00:00
|
|
|
if empty(Surface) then
|
|
|
|
return Commands.normal
|
|
|
|
end
|
2022-07-28 04:33:45 +00:00
|
|
|
local pane = Surface[Cursor_pane.col][Cursor_pane.row]
|
2022-07-30 21:43:47 +00:00
|
|
|
if Display_settings.mode == 'normal' then
|
|
|
|
if not pane.editable then
|
2022-07-30 22:32:19 +00:00
|
|
|
return Commands.normal
|
2022-07-30 21:43:47 +00:00
|
|
|
else
|
2022-07-30 22:32:19 +00:00
|
|
|
return Commands.editable
|
2022-07-30 21:43:47 +00:00
|
|
|
end
|
|
|
|
elseif Display_settings.mode == 'maximize' then
|
|
|
|
if not pane.editable then
|
2022-07-30 22:32:19 +00:00
|
|
|
return Commands.maximized
|
2022-07-30 21:43:47 +00:00
|
|
|
else
|
2022-07-30 22:32:19 +00:00
|
|
|
return Commands.maximized_editable
|
2022-07-30 21:43:47 +00:00
|
|
|
end
|
2022-07-28 04:33:45 +00:00
|
|
|
end
|
2022-07-24 04:00:49 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
function filter_candidates(candidates, prefix)
|
|
|
|
local result = {}
|
|
|
|
for _,cand in ipairs(candidates) do
|
2022-07-27 01:27:18 +00:00
|
|
|
if cand:find(prefix, 1, --[[literal pattern]] true) == 1 then
|
2022-07-24 04:00:49 +00:00
|
|
|
table.insert(result, cand)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
return result
|
|
|
|
end
|
|
|
|
|
|
|
|
function file_candidates(prefix)
|
2022-07-29 18:40:29 +00:00
|
|
|
if file_exists(Directory..prefix) then
|
2022-07-27 01:05:48 +00:00
|
|
|
return {prefix}
|
|
|
|
end
|
2022-07-24 06:27:44 +00:00
|
|
|
local path = Directory
|
2022-07-24 05:17:41 +00:00
|
|
|
local visible_dir = ''
|
|
|
|
if prefix:find('/') then
|
|
|
|
visible_dir = dirname(prefix)
|
|
|
|
path = path..dirname(prefix)
|
2022-07-24 04:00:49 +00:00
|
|
|
end
|
|
|
|
local files = love.filesystem.getDirectoryItems(path)
|
|
|
|
local base = basename(prefix)
|
|
|
|
return concat_all(visible_dir, filter_candidates(reorder(path, files), base))
|
|
|
|
end
|
|
|
|
|
|
|
|
function reorder(dir, files)
|
|
|
|
local result = {}
|
|
|
|
local info = {}
|
|
|
|
for _,file in ipairs(files) do
|
|
|
|
info[file] = love.filesystem.getInfo(dir..'/'..file)
|
|
|
|
end
|
|
|
|
-- files before directories
|
|
|
|
for _,file in ipairs(files) do
|
|
|
|
if info[file].type ~= 'directory' then
|
|
|
|
table.insert(result, file)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
for _,file in ipairs(files) do
|
|
|
|
if info[file].type == 'directory' then
|
|
|
|
table.insert(result, file..'/')
|
|
|
|
end
|
|
|
|
end
|
|
|
|
return result
|
|
|
|
end
|
2022-07-24 04:26:35 +00:00
|
|
|
|
2022-07-30 21:59:05 +00:00
|
|
|
function run_command(cmd, args)
|
|
|
|
if cmd == 'capture' then
|
|
|
|
command.capture()
|
|
|
|
elseif cmd == 'maximize note' then
|
|
|
|
command.maximize_note()
|
|
|
|
elseif cmd == 'back to surface' then
|
|
|
|
command.back_to_surface()
|
|
|
|
elseif cmd == 'edit note at cursor' then
|
|
|
|
command.edit_note_at_cursor()
|
|
|
|
elseif cmd == 'exit editing' then
|
|
|
|
command.exit_editing()
|
|
|
|
elseif cmd == 'close column surrounding cursor' then
|
|
|
|
command.close_column_surrounding_cursor()
|
2022-08-02 20:22:04 +00:00
|
|
|
elseif cmd == 'add' then
|
|
|
|
command.add_note(args)
|
2022-08-02 20:35:04 +00:00
|
|
|
elseif cmd == 'step' then
|
2022-08-02 22:40:13 +00:00
|
|
|
command.step(args)
|
2022-08-03 04:10:48 +00:00
|
|
|
elseif cmd == 'extract' then
|
|
|
|
command.extract()
|
2022-08-02 22:46:22 +00:00
|
|
|
elseif cmd == 'unroll' then
|
|
|
|
command.unroll(args)
|
2022-08-03 01:26:26 +00:00
|
|
|
elseif cmd == 'append' then
|
|
|
|
command.append_note(args)
|
2022-07-30 21:59:05 +00:00
|
|
|
elseif cmd == 'down one pane' then
|
|
|
|
command.down_one_pane()
|
|
|
|
elseif cmd == 'up one pane' then
|
|
|
|
command.up_one_pane()
|
2022-07-31 07:26:11 +00:00
|
|
|
elseif cmd == 'top pane of column' then
|
|
|
|
command.top_pane_of_column()
|
2022-07-30 21:59:05 +00:00
|
|
|
elseif cmd == 'bottom pane of column' then
|
|
|
|
command.bottom_pane_of_column()
|
|
|
|
elseif cmd == 'wider columns' then
|
|
|
|
command.wider_columns()
|
|
|
|
elseif cmd == 'narrower columns' then
|
|
|
|
command.narrower_columns()
|
|
|
|
elseif cmd == 'recently modified' then
|
|
|
|
command.recently_modified()
|
2022-07-30 22:32:19 +00:00
|
|
|
elseif cmd == 'open file' then
|
|
|
|
command.open_file(args)
|
2022-08-03 04:31:28 +00:00
|
|
|
elseif cmd == 'reload all' then
|
|
|
|
command.reload_all()
|
2022-08-04 11:32:45 +00:00
|
|
|
elseif cmd == 'reload note at cursor from disk' then
|
|
|
|
command.reload_note()
|
2022-08-03 04:49:51 +00:00
|
|
|
elseif cmd == 'delete note at cursor from disk' then
|
|
|
|
command.delete_note()
|
2022-07-30 21:59:05 +00:00
|
|
|
else
|
|
|
|
print(('not implemented yet: %s'):format(function_name))
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2022-07-30 22:32:19 +00:00
|
|
|
function run_command_with_args(cmd_with_args)
|
|
|
|
for _,cand in ipairs(initial_candidates()) do
|
2022-07-30 22:40:22 +00:00
|
|
|
cand = command_string(cand)
|
2022-07-30 22:32:19 +00:00
|
|
|
local found_offset = cmd_with_args:find(cand, 1, --[[literal pattern]] true)
|
|
|
|
if found_offset == 1 then
|
|
|
|
local pivot = #cand+1
|
|
|
|
if cmd_with_args:sub(pivot, pivot) == ' ' then
|
|
|
|
run_command(cand, trim(cmd_with_args:sub(pivot)))
|
|
|
|
end
|
|
|
|
return
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2022-07-24 04:26:35 +00:00
|
|
|
command = {}
|
|
|
|
|
2022-07-29 05:23:35 +00:00
|
|
|
function command.capture()
|
2022-07-24 04:26:35 +00:00
|
|
|
local filename = os.date('%Y/%m/%d/%H-%M-%S')
|
2022-07-26 19:04:20 +00:00
|
|
|
print('creating directory '..Directory..dirname(filename))
|
2022-07-24 06:27:44 +00:00
|
|
|
local status = love.filesystem.createDirectory(Directory..dirname(filename))
|
2022-07-24 04:26:35 +00:00
|
|
|
assert(status)
|
2022-07-31 07:26:42 +00:00
|
|
|
Cache[filename] = {lines={{mode='text', data=''}}, left=0, right=Display_settings.column_width, links={}}
|
2022-07-26 06:40:09 +00:00
|
|
|
initialize_file_metadata(Cache[filename])
|
2022-07-24 04:26:35 +00:00
|
|
|
local column = {name=filename}
|
2022-07-26 06:42:03 +00:00
|
|
|
local pane = load_pane_from_file(filename)
|
2022-07-24 04:26:35 +00:00
|
|
|
table.insert(column, pane)
|
|
|
|
table.insert(Surface, Cursor_pane.col+1, column)
|
|
|
|
Cursor_pane.col = Cursor_pane.col+1
|
|
|
|
Cursor_pane.row = 1
|
2022-07-24 05:01:13 +00:00
|
|
|
local col_sx = left_edge_sx(Cursor_pane.col)
|
|
|
|
if col_sx > Display_settings.x + App.screen.width - Display_settings.column_width then
|
|
|
|
Display_settings.x = math.max(0, col_sx + Display_settings.column_width + Margin_right + Padding_horizontal - App.screen.width)
|
|
|
|
Display_settings.y = 0
|
|
|
|
end
|
2022-07-24 04:26:35 +00:00
|
|
|
stop_editing_all()
|
2022-07-29 19:27:45 +00:00
|
|
|
pane.editable = true
|
2022-07-29 05:23:35 +00:00
|
|
|
Display_settings.mode = 'maximize'
|
2022-07-24 04:26:35 +00:00
|
|
|
end
|
2022-07-24 04:37:24 +00:00
|
|
|
|
2022-07-29 05:08:59 +00:00
|
|
|
function command.maximize_note()
|
2022-07-29 04:58:46 +00:00
|
|
|
Display_settings.mode = 'maximize'
|
|
|
|
end
|
|
|
|
|
2022-07-29 05:08:59 +00:00
|
|
|
function command.back_to_surface()
|
|
|
|
Display_settings.mode = 'normal'
|
2022-07-30 03:54:37 +00:00
|
|
|
local pane = Surface[Cursor_pane.col][Cursor_pane.row]
|
|
|
|
update_metadata(pane)
|
2022-07-29 05:23:35 +00:00
|
|
|
update_pane_bounds()
|
2022-07-29 05:08:59 +00:00
|
|
|
end
|
|
|
|
|
2022-07-24 04:37:24 +00:00
|
|
|
function command.close_column_surrounding_cursor()
|
|
|
|
stop_editing_all()
|
|
|
|
table.remove(Surface, Cursor_pane.col)
|
|
|
|
if Cursor_pane.col > 1 then
|
|
|
|
Cursor_pane.col = Cursor_pane.col - 1
|
|
|
|
Cursor_pane.row = 1
|
2022-08-03 01:48:08 +00:00
|
|
|
bring_cursor_column_on_screen()
|
2022-08-03 01:48:19 +00:00
|
|
|
update_pane_bounds()
|
2022-07-24 04:37:24 +00:00
|
|
|
end
|
|
|
|
end
|
2022-07-24 04:39:30 +00:00
|
|
|
|
|
|
|
function command.edit_note_at_cursor()
|
|
|
|
local pane = Surface[Cursor_pane.col][Cursor_pane.row]
|
2022-07-29 19:27:45 +00:00
|
|
|
assert(not pane.editable)
|
2022-07-29 04:55:01 +00:00
|
|
|
stop_editing_all()
|
|
|
|
pane.recent_updated = false
|
2022-07-29 19:27:45 +00:00
|
|
|
pane.editable = true
|
2022-07-29 19:34:21 +00:00
|
|
|
if pane.lines == Cache[pane.id].lines then
|
|
|
|
-- break an alias
|
|
|
|
pane.lines = deepcopy(pane.lines)
|
|
|
|
end
|
2022-07-29 04:55:01 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
function command.exit_editing()
|
|
|
|
local pane = Surface[Cursor_pane.col][Cursor_pane.row]
|
2022-07-29 19:27:45 +00:00
|
|
|
assert(pane.editable)
|
2022-07-29 04:55:01 +00:00
|
|
|
stop_editing(pane)
|
2022-07-24 04:39:30 +00:00
|
|
|
end
|
2022-07-24 05:38:19 +00:00
|
|
|
|
2022-07-30 05:17:40 +00:00
|
|
|
function command.down_one_pane()
|
|
|
|
if Cursor_pane.row < #Surface[Cursor_pane.col] then
|
|
|
|
Cursor_pane.row = Cursor_pane.row + 1
|
|
|
|
end
|
2022-07-30 15:34:01 +00:00
|
|
|
Display_settings.y = up_edge_sy(Cursor_pane.col, Cursor_pane.row) - Padding_vertical
|
|
|
|
update_pane_bounds()
|
2022-07-30 05:17:40 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
function command.up_one_pane()
|
|
|
|
if Cursor_pane.row > 1 then
|
|
|
|
Cursor_pane.row = Cursor_pane.row - 1
|
|
|
|
end
|
2022-07-30 15:34:01 +00:00
|
|
|
Display_settings.y = up_edge_sy(Cursor_pane.col, Cursor_pane.row) - Padding_vertical
|
|
|
|
update_pane_bounds()
|
2022-07-30 05:17:40 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
function command.bottom_pane_of_column()
|
|
|
|
if Cursor_pane.row < #Surface[Cursor_pane.col] then
|
|
|
|
Cursor_pane.row = #Surface[Cursor_pane.col]
|
|
|
|
end
|
2022-07-30 15:34:01 +00:00
|
|
|
Display_settings.y = up_edge_sy(Cursor_pane.col, Cursor_pane.row) - Padding_vertical
|
|
|
|
update_pane_bounds()
|
2022-07-30 05:17:40 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
function command.top_pane_of_column()
|
|
|
|
if Cursor_pane.row > 1 then
|
|
|
|
Cursor_pane.row = 1
|
|
|
|
end
|
2022-07-30 15:34:01 +00:00
|
|
|
Display_settings.y = up_edge_sy(Cursor_pane.col, Cursor_pane.row) - Padding_vertical
|
|
|
|
update_pane_bounds()
|
2022-07-30 05:17:40 +00:00
|
|
|
end
|
|
|
|
|
2022-07-30 21:13:30 +00:00
|
|
|
function command.wider_columns()
|
|
|
|
Display_settings.column_width = Display_settings.column_width + 5*App.width(Em)
|
|
|
|
redraw_all()
|
|
|
|
update_pane_bounds()
|
|
|
|
end
|
|
|
|
|
|
|
|
function command.narrower_columns()
|
|
|
|
Display_settings.column_width = Display_settings.column_width - 5*App.width(Em)
|
|
|
|
redraw_all()
|
|
|
|
update_pane_bounds()
|
|
|
|
end
|
|
|
|
|
2022-07-24 05:38:19 +00:00
|
|
|
function command.recently_modified()
|
2022-08-04 00:05:43 +00:00
|
|
|
if not file_exists(Directory..'recent') then
|
|
|
|
return
|
|
|
|
end
|
2022-07-26 17:02:27 +00:00
|
|
|
local filenames = {}
|
2022-07-24 06:27:44 +00:00
|
|
|
for line in love.filesystem.lines(Directory..'recent') do
|
2022-07-26 17:02:27 +00:00
|
|
|
table.insert(filenames, line)
|
2022-07-24 05:38:19 +00:00
|
|
|
end
|
2022-07-26 17:04:07 +00:00
|
|
|
local done, ndone = {}, 0
|
2022-07-24 05:38:19 +00:00
|
|
|
local column = {name='recently modified'}
|
2022-07-26 17:02:27 +00:00
|
|
|
for i=#filenames,1,-1 do
|
|
|
|
local filename = filenames[i]
|
2022-07-26 17:04:07 +00:00
|
|
|
if ndone >= Recently_modified_lookback_window then break end
|
2022-07-24 05:38:19 +00:00
|
|
|
if not done[filename] then
|
|
|
|
done[filename] = true
|
2022-07-26 17:04:07 +00:00
|
|
|
ndone = ndone+1
|
2022-07-24 06:23:59 +00:00
|
|
|
--? print('loading', filename)
|
|
|
|
local pane = load_pane_from_file(filename)
|
2022-07-24 05:38:19 +00:00
|
|
|
table.insert(column, pane)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
table.insert(Surface, Cursor_pane.col+1, column)
|
2022-07-24 06:23:59 +00:00
|
|
|
Cursor_pane.col = Cursor_pane.col+1
|
2022-07-24 05:38:19 +00:00
|
|
|
Cursor_pane.row = 1
|
|
|
|
local col_sx = left_edge_sx(Cursor_pane.col)
|
|
|
|
if col_sx > Display_settings.x + App.screen.width - Display_settings.column_width then
|
|
|
|
Display_settings.x = math.max(0, col_sx + Display_settings.column_width + Margin_right + Padding_horizontal - App.screen.width)
|
|
|
|
Display_settings.y = 0
|
|
|
|
end
|
2022-08-04 00:07:05 +00:00
|
|
|
update_pane_bounds()
|
2022-07-24 05:38:19 +00:00
|
|
|
end
|
2022-07-24 07:08:52 +00:00
|
|
|
|
|
|
|
function command.open_file(filename)
|
|
|
|
local column = {name=filename}
|
|
|
|
local pane = load_pane_from_file(filename)
|
|
|
|
table.insert(column, pane)
|
2022-08-03 01:48:08 +00:00
|
|
|
add_column_to_right_of_cursor(column)
|
2022-07-29 05:23:35 +00:00
|
|
|
update_pane_bounds()
|
2022-07-24 07:08:52 +00:00
|
|
|
end
|
2022-07-30 22:32:19 +00:00
|
|
|
|
2022-08-03 04:31:28 +00:00
|
|
|
function command.reload_all()
|
2022-08-03 04:35:22 +00:00
|
|
|
local column_names = {}
|
|
|
|
for _,column in ipairs(Surface) do
|
|
|
|
table.insert(column_names, column.name)
|
|
|
|
end
|
|
|
|
Surface = {}
|
|
|
|
Cache = {}
|
|
|
|
local old_cursor_pane = Cursor_pane
|
|
|
|
Cursor_pane = {col=0,row=1}
|
|
|
|
for _,column_name in ipairs(column_names) do
|
|
|
|
create_column(column_name)
|
|
|
|
end
|
|
|
|
Cursor_pane = old_cursor_pane
|
2022-08-03 05:16:01 +00:00
|
|
|
update_pane_bounds()
|
2022-08-03 04:31:28 +00:00
|
|
|
end
|
|
|
|
|
2022-08-02 20:22:04 +00:00
|
|
|
function command.add_note(rel)
|
2022-07-31 05:44:37 +00:00
|
|
|
if rel == nil then
|
|
|
|
rel = 'next'
|
|
|
|
end
|
|
|
|
local pane = Surface[Cursor_pane.col][Cursor_pane.row]
|
2022-08-02 20:30:43 +00:00
|
|
|
if Cache[pane.id].links[rel] then
|
|
|
|
print(('%s already has a %s note'):format(pane.id, rel))
|
|
|
|
return
|
|
|
|
end
|
2022-08-03 01:40:13 +00:00
|
|
|
local new_pane = new_pane()
|
|
|
|
stop_editing_all()
|
|
|
|
new_pane.editable = true
|
2022-07-31 05:44:37 +00:00
|
|
|
print('connecting up links')
|
2022-07-31 07:26:42 +00:00
|
|
|
Cache[pane.id].links[rel] = new_pane.id
|
|
|
|
Cache[new_pane.id].links[Opposite[rel]] = pane.id
|
2022-08-03 01:40:13 +00:00
|
|
|
local column = {name=new_pane.id}
|
2022-07-31 05:44:37 +00:00
|
|
|
table.insert(column, new_pane)
|
2022-08-03 01:48:08 +00:00
|
|
|
add_column_to_right_of_cursor(column)
|
2022-07-31 05:44:37 +00:00
|
|
|
update_pane_bounds()
|
|
|
|
end
|
|
|
|
|
2022-08-02 22:40:13 +00:00
|
|
|
function command.step(rel)
|
2022-08-02 20:35:04 +00:00
|
|
|
if rel == nil then
|
|
|
|
rel = 'next'
|
|
|
|
end
|
|
|
|
local pane = Surface[Cursor_pane.col][Cursor_pane.row]
|
|
|
|
if Cache[pane.id].links[rel] == nil then
|
|
|
|
print(('%s has no %s note'):format(pane.id, rel))
|
|
|
|
return
|
|
|
|
end
|
|
|
|
command.open_file(Cache[pane.id].links[rel])
|
|
|
|
end
|
|
|
|
|
2022-08-03 04:10:48 +00:00
|
|
|
function command.extract(rel)
|
|
|
|
local pane = Surface[Cursor_pane.col][Cursor_pane.row]
|
|
|
|
command.open_file(pane.id)
|
|
|
|
end
|
|
|
|
|
2022-08-02 22:46:22 +00:00
|
|
|
function command.unroll(rel)
|
|
|
|
if rel == nil then
|
|
|
|
rel = 'next'
|
|
|
|
end
|
|
|
|
local pane = Surface[Cursor_pane.col][Cursor_pane.row]
|
2022-08-02 23:00:18 +00:00
|
|
|
local column = unroll_column(pane.id, rel)
|
2022-08-03 17:55:32 +00:00
|
|
|
if column == nil then
|
|
|
|
return
|
|
|
|
end
|
2022-08-03 01:15:20 +00:00
|
|
|
if #Surface[Cursor_pane.col] == 1 then
|
|
|
|
assert(Cursor_pane.row == 1)
|
|
|
|
Surface[Cursor_pane.col] = column
|
|
|
|
else
|
|
|
|
table.insert(Surface, Cursor_pane.col+1, column)
|
|
|
|
Cursor_pane.col = Cursor_pane.col+1
|
|
|
|
Cursor_pane.row = 1
|
|
|
|
end
|
2022-08-03 01:48:08 +00:00
|
|
|
bring_cursor_column_on_screen()
|
2022-08-02 22:46:22 +00:00
|
|
|
update_pane_bounds()
|
|
|
|
end
|
|
|
|
|
2022-08-02 23:00:18 +00:00
|
|
|
function unroll_column(id, rel)
|
2022-08-03 22:45:45 +00:00
|
|
|
while Cache[id].links[Opposite[rel]] do
|
|
|
|
id = Cache[id].links[Opposite[rel]]
|
|
|
|
initialize_cache_if_necessary(id)
|
|
|
|
end
|
2022-08-02 23:00:18 +00:00
|
|
|
if Cache[id].links[rel] == nil then
|
|
|
|
print(('%s has no %s note'):format(id, rel))
|
|
|
|
return
|
|
|
|
end
|
|
|
|
local column = {name=('%s from %s'):format(rel, id)}
|
|
|
|
local curr = id
|
|
|
|
while curr do
|
|
|
|
local pane = load_pane_from_file(curr)
|
|
|
|
table.insert(column, pane)
|
|
|
|
curr = Cache[curr].links[rel]
|
|
|
|
end
|
|
|
|
return column
|
|
|
|
end
|
|
|
|
|
2022-08-03 01:26:26 +00:00
|
|
|
function command.append_note(rel)
|
|
|
|
if rel == nil then
|
|
|
|
rel = 'next'
|
|
|
|
end
|
|
|
|
local pane = Surface[Cursor_pane.col][Cursor_pane.row]
|
|
|
|
local curr_id = pane.id
|
|
|
|
while true do
|
|
|
|
initialize_cache_if_necessary(curr_id)
|
|
|
|
local next_id = Cache[curr_id].links[rel]
|
|
|
|
if next_id == nil then
|
|
|
|
break
|
|
|
|
end
|
|
|
|
curr_id = next_id
|
|
|
|
end
|
2022-08-03 01:40:13 +00:00
|
|
|
local new_pane = new_pane()
|
2022-08-03 01:30:40 +00:00
|
|
|
stop_editing_all()
|
|
|
|
new_pane.editable = true
|
2022-08-03 01:40:13 +00:00
|
|
|
print('connecting up links')
|
2022-08-03 01:26:26 +00:00
|
|
|
Cache[curr_id].links[rel] = new_pane.id
|
|
|
|
Cache[new_pane.id].links[Opposite[rel]] = curr_id
|
2022-08-03 01:40:13 +00:00
|
|
|
local column = {name=new_pane.id}
|
2022-08-03 01:26:26 +00:00
|
|
|
table.insert(column, new_pane)
|
|
|
|
table.insert(Surface, Cursor_pane.col+1, column)
|
|
|
|
Cursor_pane.col = Cursor_pane.col+1
|
|
|
|
Cursor_pane.row = 1
|
|
|
|
local col_sx = left_edge_sx(Cursor_pane.col)
|
|
|
|
if col_sx > Display_settings.x + App.screen.width - Display_settings.column_width then
|
|
|
|
Display_settings.x = math.max(0, col_sx + Display_settings.column_width + Margin_right + Padding_horizontal - App.screen.width)
|
|
|
|
Display_settings.y = 0
|
|
|
|
end
|
|
|
|
update_pane_bounds()
|
|
|
|
end
|
|
|
|
|
2022-08-04 11:32:45 +00:00
|
|
|
function command.reload_note()
|
|
|
|
local pane = Surface[Cursor_pane.col][Cursor_pane.row]
|
|
|
|
Cache[pane.id] = nil
|
|
|
|
for _,column in ipairs(Surface) do
|
|
|
|
for i,curr in ipairs(column) do
|
|
|
|
if curr.id == pane.id then
|
|
|
|
column[i] = load_pane_from_file(pane.id)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
redraw_all()
|
|
|
|
end
|
|
|
|
|
2022-08-03 04:49:51 +00:00
|
|
|
function command.delete_note()
|
|
|
|
local pane = Surface[Cursor_pane.col][Cursor_pane.row]
|
|
|
|
if #Cache[pane.id].links > 0 then
|
|
|
|
return
|
|
|
|
end
|
|
|
|
-- TODO: test harness support for file ops below
|
|
|
|
if App.run_tests then
|
|
|
|
return
|
|
|
|
end
|
|
|
|
-- delete from disk
|
|
|
|
love.filesystem.remove(Directory..pane.id)
|
|
|
|
-- delete from recently modified
|
|
|
|
local filenames = {}
|
|
|
|
for line in love.filesystem.lines(Directory..'recent') do
|
|
|
|
if line ~= pane.id then
|
|
|
|
table.insert(filenames, line)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
local f = love.filesystem.newFile(Directory..'recent')
|
|
|
|
f:open('w')
|
|
|
|
for _,filename in ipairs(filenames) do
|
|
|
|
f:write(filename)
|
|
|
|
f:write('\n')
|
|
|
|
end
|
|
|
|
f:close()
|
|
|
|
--
|
|
|
|
command.reload_all()
|
|
|
|
end
|
|
|
|
|
2022-08-03 01:40:13 +00:00
|
|
|
-- return a new pane with a unique filename
|
|
|
|
function new_pane()
|
|
|
|
local filename = os.date('%Y/%m/%d/%H-%M-%S')
|
|
|
|
print('creating directory '..Directory..dirname(filename))
|
|
|
|
local status = love.filesystem.createDirectory(Directory..dirname(filename))
|
|
|
|
assert(status)
|
|
|
|
Cache[filename] = {lines={{mode='text', data=''}}, left=0, right=Display_settings.column_width, links={}}
|
|
|
|
initialize_file_metadata(Cache[filename])
|
|
|
|
return load_pane_from_file(filename)
|
|
|
|
end
|
|
|
|
|
2022-08-03 01:48:08 +00:00
|
|
|
function add_column_to_right_of_cursor(column)
|
|
|
|
table.insert(Surface, Cursor_pane.col+1, column)
|
|
|
|
Cursor_pane.col = Cursor_pane.col+1
|
|
|
|
Cursor_pane.row = 1
|
|
|
|
bring_cursor_column_on_screen()
|
|
|
|
end
|
|
|
|
|
|
|
|
function bring_cursor_column_on_screen()
|
|
|
|
local col_sx = left_edge_sx(Cursor_pane.col)
|
|
|
|
if col_sx > Display_settings.x + App.screen.width - Display_settings.column_width then
|
|
|
|
Display_settings.x = math.max(0, col_sx + Display_settings.column_width + Margin_right + Padding_horizontal - App.screen.width)
|
|
|
|
Display_settings.y = 0
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2022-07-30 22:32:19 +00:00
|
|
|
function trim(s)
|
|
|
|
return s:gsub('^%s+', ''):gsub('%s+$', '')
|
|
|
|
end
|