app running again
[?]
Nov 27, 2022, 10:06 PM
LRDM35CEK3OHXOTB7TEFJRL7P6PQWO5ZG3F2BVA7DIDFHBPJQ7KACDependencies
- [2]
YJ6ASFBGyet another fork trying to integrate my live framework with everything else - [3]
VO2ZVTWKMerge lines.love - [4]
UH4YWHW5button framework is at the app level - [5]
TXDMRA5Jbugfix: alt-tab shouldn't emit keypress events - [6]
PP2IIHL6stop putting button state in a global - [7]
VP5KC4XZMerge lines.love - [8]
MD3W5IRAnew fork: rip out drawing support - [9]
KMSL74GAsupport selections in the source editor - [10]
H3KWPK3Gregression: dropping files on the window - [11]
ZNLTRNNKhighlight another global - [12]
6LJZN727handle chords - [13]
XX7G2FFJintermingle freehand line drawings with text - [14]
D2GCFTTTclean up repl functionality - [15]
HAZLW5K7Merge text.love - [16]
3QNOKBFMbeginnings of a test harness - [17]
2CTN2IEFMerge lines.love - [18]
66X36NZNa little more prose describing manual_tests - [19]
RSZD5A7Gforgot to add json.lua - [20]
K2X6G75Zstart writing some tests for drawings - [21]
YIQYNVD2rip out the line-width slider - [22]
4YDBYBA4clean up memory leak experiments - [23]
BULPIBEGbeginnings of a module for the text editor - [24]
V7LATJC7bugfix: resize - [25]
AVTNUQYRbasic test-enabled framework - [26]
OTIBCAUJlove2d scaffold - [27]
PTDO2SOTadd state arg to schedule_save - [28]
BZRRUIFQcorrect location of the line width slider - [29]
5F54FYKIMerge lines.love - [30]
CNCYMM6Amake test initializations a little more obvious - [31]
OL7ZCZWDMerge text.love - [32]
VHUNJHXBMerge lines.love - [33]
TVCPXAAUrename - [34]
LXTTOB33extract a couple of files - [35]
32V6ZHQBMerge lines.love - [36]
KKMFQDR4editing source code from within the app - [37]
ATQO62TFMerge lines.love - [38]
TLOAPLBJadd a license - [39]
VXORMHMEdelete experimental REPL - [40]
UHB4GARJleft/right margin -> left/right coordinates - [41]
MUJTM6REbring back a level of wrapping - [42]
T4FRZSYLdelete an ancient, unused file - [43]
RPGTBMMMMerge lines.love - [44]
BPWFKBXTnew test: dragging and dropping a file on lines.love - [45]
2WGHUWE6self-documenting 0 Test_right_margin - [46]
73OCE2MCafter much struggle, a brute-force undo - [47]
JOPVPUSAediting source code from within the app - [48]
D4B52CQ2Merge lines.love - [49]
LF7BWEG4group all editor globals - [50]
RXNR3U5EMerge text.love - [51]
2L5MEZV3experiment: new edit namespace - [52]
ETXNVRPTMerge lines.love - [53]
JCXL74WVbring back everything from commit a68647ae22 - [54]
YT5P6TO6bugfix: save previous file when dropping a new one on - [55]
BJ5X5O4Alet's prevent the text cursor from ever getting on a drawing - [56]
LNUHQOGHstart passing in Editor_state explicitly - [57]
GUOQRUL7Merge lines.love - [58]
3PSFWAILMerge lines.love - [59]
VBU5YHLRMerge lines.love - [60]
FS2ITYYHrecord a known issue - [61]
VHQCNMARseveral more modules - [62]
BLWAYPKVextract a module - [63]
SDRXK4X5move - [64]
36Z442IVback to commit 8123959e52f without code editing - [65]
JCSLDGAHbeginnings of support for multiple shapes - [66]
R5QXEHUIsomebody stop me - [67]
SCOXD4EOMerge lines.love - [68]
OGUV4HSAremove some memory leaks from rendered fragments - [69]
CE4LZV4Tdrop last couple of manual tests - [*]
KVHUFUFVreorg
Change contents
- file deletion: main_tests.lua run_tests.lua run_tests.lua[3.2]→[3.1125:1163](∅→∅),[3.1163]→[3.7:7](∅→∅),[3.2]→[3.178005:178042](∅→∅),[3.178042]→[3.7:7](∅→∅),[3.2]→[3.178068:178105](∅→∅),[3.178105]→[3.7:7](∅→∅)
function test_resize_window()io.write('\ntest_resize_window')check_eq(App.screen.height, 300, 'F - test_resize_window/baseline/height')check_eq(Editor_state.left, Test_margin_left, 'F - test_resize_window/baseline/left_margin')check_eq(Editor_state.right, 300 - Test_margin_right, 'F - test_resize_window/baseline/left_margin')App.resize(200, 400)-- ugly; resize switches to real, non-test marginscheck_eq(App.screen.width, 200, 'F - test_resize_window/width')check_eq(App.screen.height, 400, 'F - test_resize_window/height')check_eq(Editor_state.right, 200-Margin_right, 'F - test_resize_window/right_margin')check_eq(Editor_state.width, 200-Margin_left-Margin_right, 'F - test_resize_window/drawing_width')check_eq(Editor_state.left, Margin_left, 'F - test_resize_window/left_margin')-- TODO: how to make assertions about when App.update got past the early exit?endfunction test_drop_file()io.write('\ntest_drop_file')App.filesystem['foo'] = 'abc\ndef\nghi\n'local fake_dropped_file = {opened = false,getFilename = function(self)return 'foo'end,open = function(self)self.opened = trueend,lines = function(self)assert(self.opened)return App.filesystem['foo']:gmatch('[^\n]+')end,close = function(self)self.opened = falseend,}App.filedropped(fake_dropped_file)endfunction test_drop_file_saves_previous()io.write('\ntest_drop_file_saves_previous')-- initially editing a file called foo that hasn't been saved to filesystem yet-- now drag a new file bar from the filesystemApp.filesystem['bar'] = 'abc\ndef\nghi\n'local fake_dropped_file = {opened = false,getFilename = function(self)return 'bar'end,open = function(self)self.opened = trueend,lines = function(self)assert(self.opened)return App.filesystem['bar']:gmatch('[^\n]+')end,close = function(self)self.opened = falseend,}App.filedropped(fake_dropped_file)-- filesystem now contains a file called foocheck_eq(App.filesystem['foo'], 'abc\ndef\n', 'F - test_drop_file_saves_previous')endEditor_state.lines = load_array{'abc', 'def'}Editor_state.filename = 'foo'schedule_save(Editor_state)App.screen.init{width=Editor_state.left+300, height=300}check_eq(#Editor_state.lines, 3, 'F - test_drop_file/#lines')edit.draw(Editor_state)check_eq(Editor_state.lines[1].data, 'abc', 'F - test_drop_file/lines:1')check_eq(Editor_state.lines[2].data, 'def', 'F - test_drop_file/lines:2')check_eq(Editor_state.lines[3].data, 'ghi', 'F - test_drop_file/lines:3')App.screen.init{width=Editor_state.left+300, height=300}Editor_state = edit.initialize_test_state()Editor_state.filename = 'foo'check_eq(App.screen.width, 300, 'F - test_resize_window/baseline/width')App.screen.init{width=300, height=300}Editor_state = edit.initialize_test_state() - edit in main.lua at line 6
require 'live' - edit in main.lua at line 11
require 'main_tests' - edit in main.lua at line 35
Editor_state = nil -- not used outside editor tests - edit in main.lua at line 39
live.initialize(arg) - edit in main.lua at line 82
if on.resize then on.resize(w,h) end - replacement in main.lua at line 86[3.5643]→[3.5643:5763](∅→∅),[3.5763]→[3.188315:188321](∅→∅),[3.188315]→[3.188315:188321](∅→∅),[3.188321]→[3.5764:6044](∅→∅)
-- first make sure to save edits on any existing fileif Editor_state.next_save thensave_to_disk(Editor_state)end-- clear the slate for the new fileApp.initialize_globals()Editor_state.filename = file:getFilename()file:open('r')Editor_state.lines = load_from_file(file)file:close()Text.redraw_all(Editor_state)love.window.setTitle('text.love - '..Editor_state.filename)if on.filedropped then on.filedropped() end - replacement in main.lua at line 90
edit.draw(Editor_state)if on.draw then on.draw() end - replacement in main.lua at line 100
edit.update(Editor_state, dt)if on.update then on.update(dt) end - replacement in main.lua at line 104
edit.quit(Editor_state)if on.quit then on.quit() end - replacement in main.lua at line 110
return edit.mouse_pressed(Editor_state, x,y, mouse_button)if on.mouse_pressed then on.mouse_pressed(x,y, mouse_button) end - replacement in main.lua at line 115
return edit.mouse_released(Editor_state, x,y, mouse_button)if on.mouse_released then on.mouse_released(x,y, mouse_button) end - edit in main.lua at line 122[3.188672][71.3]
if on.focus then on.focus(in_focus) end - edit in main.lua at line 125[71.8][3.188134]
-- App.keypressed is defined in keychord.lua - replacement in main.lua at line 133
return edit.keychord_pressed(Editor_state, chord, key)if on.keychord_pressed then on.keychord_pressed(key, scancode, isrepeat) end - replacement in main.lua at line 142
return edit.textinput(Editor_state, t)if on.textinput then on.textinput(t) end - replacement in main.lua at line 151
return edit.key_released(Editor_state, key, scancode)if on.key_released then on.key_released(key, scancode, isrepeat) end - file addition: live.lua[3.2]
-- A general architecture for free-wheeling, live programs:-- on startup:-- scan both the app directory and the save directory for files with numeric prefixes-- from the largest numeric prefix found, obtain a manifest-- load all files (which must start with a numeric prefix) from the manifest)---- then start drawing frames on screen and reacting to events---- events from keyboard and mouse are handled as the app desires---- on incoming messages to a specific file, however, the app must:-- save the message's value to a specific, unused numeric prefix-- execute the value---- if a game encounters an error:-- find the previous version of the definition in the 'head' numeric prefix-- decrement 'head'-- namespace for these functionslive = {}-- state for these functionsLive = {}-- a namespace of frameworky callbacks-- these will be modified live-- TODO: how to make them discoverable?on = {}-- ========= on startup, load the version at headfunction live.initialize(arg)-- version controlLive.Head = 0Live.Next_version = 1Live.History = {} -- array of filename roots corresponding to each numeric prefixLive.Manifest = {} -- mapping from roots to numeric prefixes as of version Live.Headlive.load_files_so_far()if on.load then on.load() endendfunction live.load_files_so_far()print('new edits will go to ' .. love.filesystem.getSaveDirectory())local files = {}live.append_files_with_numeric_prefix('', files)table.sort(files)live.check_integrity(files)live.append_files_with_numeric_prefix(love.filesystem.getSaveDirectory(), files)table.sort(files)live.check_integrity(files)Live.History = live.load_history(files)Live.Next_version = #Live.History + 1local head_string = love.filesystem.read('head')Live.Head = tonumber(head_string)if Live.Head > 0 thenLive.Manifest = json.decode(love.filesystem.read(live.versioned_manifest(Live.Head)))endlive.load_everything_in_manifest()endfunction live.append_files_with_numeric_prefix(dir, files)for _,file in ipairs(love.filesystem.getDirectoryItems(dir)) dotable.insert(files, file)endendfunction live.check_integrity(files)local manifest_found, file_found = false, falselocal expected_index = 1for _,file in ipairs(files) dofor numeric_prefix, root in file:gmatch('(%d+)-(.+)') do-- only runs oncelocal index = tonumber(numeric_prefix)-- skip files without numeric prefixesif index ~= nil thenif index < expected_index thenprint(index, expected_index)endassert(index >= expected_index)if index > expected_index thenassert(index == expected_index+1)assert(manifest_found and file_found)expected_index = indexmanifest_found, file_found = false, falseendassert(index == expected_index)if root == 'manifest' thenassert(not manifest_found)manifest_found = trueelseassert(not file_found)file_found = trueendendendendendfunction live.load_history(files)local result = {}for _,file in ipairs(files) dofor numeric_prefix, root in file:gmatch('(%d+)-(.+)') do-- only runs oncelocal index = tonumber(numeric_prefix)-- skipif index ~= nil thenif root ~= 'manifest' thenassert(index == #result+1)table.insert(result, root)endendendendreturn resultendfunction live.load_everything_in_manifest()for k,v in pairs(Live.Manifest) doif k ~= 'parent' thenlocal root, index = k, vlocal filename = live.versioned_filename(index, root)local buf = love.filesystem.read(filename)assert(buf and buf ~= '')live.eval(buf)endendendfunction live.versioned_filename(index, root)return ('%04d-%s'):format(index, root)endfunction live.versioned_manifest(index)return ('%04d-manifest'):format(index)end-- ========= on each frame, check for messages and alter the app as neededfunction live.update(dt)if Current_time - Live.Previous_read > 0.1 thenlocal buf = live.receive()if buf thenlive.run(buf)endLive.Previous_read = Current_timeendend-- look for a message from outside, and return nil if there's nothingfunction live.receive()local f = io.open(love.filesystem.getUserDirectory()..'/_love_akkartik_app')if f == nil then return nil endlocal result = f:read('*a')f:close()if result == '' then return nil end -- empty file == no messageprint('<='..color(--[[bold]]1, --[[blue]]4))print(result)print(reset_terminal())-- we can't unlink files, so just clear themlocal clear = io.open(love.filesystem.getUserDirectory()..'/_love_akkartik_app', 'w')clear:close()return resultendfunction live.send(msg)local f = io.open(love.filesystem.getUserDirectory()..'/_love_akkartik_driver', 'w')if f == nil then return endf:write(msg)f:close()print('=>'..color(0, --[[green]]2))print(msg)print(reset_terminal())end-- args:-- format: 0 for normal, 1 for bold-- color: 0-15function color(format, color)return ('\027[%d;%dm'):format(format, 30+color)endfunction reset_terminal()return '\027[m'end-- define or undefine top-level bindingsfunction live.run(buf)local cmd = buf:match('^%S+')assert(cmd)print('command is '..cmd)if cmd == 'QUIT' thenlove.event.quit(1)elseif cmd == 'MANIFEST' thenlive.send(json.encode(Live.Manifest))elseif cmd == 'DELETE' thenlocal binding = buf:match('^%S+%s+(%S+)')Live.Manifest[binding] = nillive.eval(binding..' = nil')local next_filename = live.versioned_filename(Live.Next_version, binding)love.filesystem.write(next_filename, '')table.insert(Live.History, binding)Live.Manifest.parent = Live.Headlocal manifest_filename = live.versioned_manifest(Live.Next_version)love.filesystem.write(manifest_filename, json.encode(Live.Manifest))Live.Head = Live.Next_versionlove.filesystem.write('head', tostring(Live.Head))Live.Next_version = Live.Next_version + 1elseif cmd == 'GET' thenlocal binding = buf:match('^%S+%s+(%S+)')live.send(live.get_binding(binding))-- other commands go hereelselocal binding = cmdlocal next_filename = live.versioned_filename(Live.Next_version, binding)love.filesystem.write(next_filename, buf)table.insert(Live.History, binding)Live.Manifest[binding] = Live.Next_versionLive.Manifest.parent = Live.Headlocal manifest_filename = live.versioned_manifest(Live.Next_version)love.filesystem.write(manifest_filename, json.encode(Live.Manifest))Live.Head = Live.Next_versionlove.filesystem.write('head', tostring(Live.Head))Live.Next_version = Live.Next_version + 1local status, err = live.eval(buf)if not status then-- roll backLive.Head = Live.Manifest.parentlocal previous_manifest_filename = live.versioned_manifest(Live.Head)Live.Manifest = json.decode(love.filesystem.read(previous_manifest_filename))-- throw an errorlive.send('ERROR '..tostring(err))endendendfunction live.get_binding(name)if Live.Manifest[name] thenreturn love.filesystem.read(live.versioned_filename(Live.Manifest[name], name))endend-- Wrapper for Lua's weird evaluation model.-- Lua is persnickety about expressions vs statements, so we need to do some-- extra work to get the result of an evaluation.-- return values:-- all well -> true, ...-- load failed -> nil, error message-- run failed -> false, error messagefunction live.eval(buf)-- We assume a program is either correct with 'return' prefixed xor not.-- Is this correct? Who knows! But the Lua REPL does this as well.local f = load('return '..buf, 'REPL')if f thenreturn pcall(f)endlocal f, err = load(buf, 'REPL')if f thenreturn pcall(f)elsereturn nil, errendend-- ========= on error, pause the app and wait for messagesfunction live.run()love.load(love.arg.parseGameArguments(arg), arg)love.timer.step()local dt = 0return function()local status, result = xpcall(live.try_run, live.handle_error)return resultendend-- one iteration of the event loop-- return nil to continue the event loop, non-nil to quit-- from https://love2d.org/wiki/love.runfunction live.try_run()if love.event thenlove.event.pump()for name, a,b,c,d,e,f in love.event.poll() doif name == 'quit' thenif not love.quit() thenreturn a or 0endendlove.handlers[name](a,b,c,d,e,f)endend-- updatedt = love.timer.step()love.update(dt)-- draw before update to give it a chance to mutate statelove.graphics.origin()love.graphics.clear(love.graphics.getBackgroundColor())love.draw()love.graphics.present()love.timer.sleep(0.001)-- returning nil continues the loopend-- return nil to continue the event loop, non-nil to quitfunction live.handle_error(err)local msg = tostring(err)-- draw a pause indicator on screenlove.graphics.setColor(1,0,0)love.graphics.rectangle('fill', 10,10, 3,10)love.graphics.rectangle('fill', 16,10, 3,10)love.graphics.present()-- print stack trace here just in case we ran the app through a terminallocal stack_trace = debug.traceback('Error: ' .. tostring(msg), --[[stack frame]]2):gsub('\n[^\n]+$', '')print(stack_trace)print('Look in the driver for options to investigate further.')print("(You probably can't close the app window at this point. If you don't have the driver set up, you might need to force-quit.)")-- send stack trace to driver and wait for a responselive.send('ERROR '..stack_trace)local bufrepeatbuf = live.receive()until bufif buf == 'QUIT' thenreturn trueendlive.run(buf)end - file addition: head[3.2]
0