app running again
[?]
Nov 27, 2022, 10:06 PM
LRDM35CEK3OHXOTB7TEFJRL7P6PQWO5ZG3F2BVA7DIDFHBPJQ7KACDependencies
- [2]
YJ6ASFBGyet another fork trying to integrate my live framework with everything else - [3]
6LJZN727handle chords - [4]
D4B52CQ2Merge lines.love - [5]
TVCPXAAUrename - [6]
32V6ZHQBMerge lines.love - [7]
VBU5YHLRMerge lines.love - [8]
TXDMRA5Jbugfix: alt-tab shouldn't emit keypress events - [9]
RXNR3U5EMerge text.love - [10]
73OCE2MCafter much struggle, a brute-force undo - [11]
4YDBYBA4clean up memory leak experiments - [12]
PP2IIHL6stop putting button state in a global - [13]
2CTN2IEFMerge lines.love - [14]
VXORMHMEdelete experimental REPL - [15]
MD3W5IRAnew fork: rip out drawing support - [16]
VHQCNMARseveral more modules - [17]
KMSL74GAsupport selections in the source editor - [18]
UH4YWHW5button framework is at the app level - [19]
2WGHUWE6self-documenting 0 Test_right_margin - [20]
HAZLW5K7Merge text.love - [21]
RPGTBMMMMerge lines.love - [22]
UHB4GARJleft/right margin -> left/right coordinates - [23]
OTIBCAUJlove2d scaffold - [24]
3QNOKBFMbeginnings of a test harness - [25]
JOPVPUSAediting source code from within the app - [26]
VP5KC4XZMerge lines.love - [27]
VO2ZVTWKMerge lines.love - [28]
KKMFQDR4editing source code from within the app - [29]
36Z442IVback to commit 8123959e52f without code editing - [30]
PTDO2SOTadd state arg to schedule_save - [31]
ATQO62TFMerge lines.love - [32]
JCXL74WVbring back everything from commit a68647ae22 - [33]
OGUV4HSAremove some memory leaks from rendered fragments - [34]
LXTTOB33extract a couple of files - [35]
TLOAPLBJadd a license - [36]
BJ5X5O4Alet's prevent the text cursor from ever getting on a drawing - [37]
JCSLDGAHbeginnings of support for multiple shapes - [38]
BPWFKBXTnew test: dragging and dropping a file on lines.love - [39]
FS2ITYYHrecord a known issue - [40]
D2GCFTTTclean up repl functionality - [41]
LNUHQOGHstart passing in Editor_state explicitly - [42]
V7LATJC7bugfix: resize - [43]
XX7G2FFJintermingle freehand line drawings with text - [44]
3PSFWAILMerge lines.love - [45]
ZNLTRNNKhighlight another global - [46]
YIQYNVD2rip out the line-width slider - [47]
LF7BWEG4group all editor globals - [48]
SDRXK4X5move - [49]
OL7ZCZWDMerge text.love - [50]
66X36NZNa little more prose describing manual_tests - [51]
MUJTM6REbring back a level of wrapping - [52]
BLWAYPKVextract a module - [53]
CNCYMM6Amake test initializations a little more obvious - [54]
RSZD5A7Gforgot to add json.lua - [55]
R5QXEHUIsomebody stop me - [56]
BULPIBEGbeginnings of a module for the text editor - [57]
VHUNJHXBMerge lines.love - [58]
CE4LZV4Tdrop last couple of manual tests - [59]
2L5MEZV3experiment: new edit namespace - [60]
AVTNUQYRbasic test-enabled framework - [61]
T4FRZSYLdelete an ancient, unused file - [62]
5F54FYKIMerge lines.love - [63]
BZRRUIFQcorrect location of the line width slider - [64]
GUOQRUL7Merge lines.love - [65]
ETXNVRPTMerge lines.love - [66]
K2X6G75Zstart writing some tests for drawings - [67]
YT5P6TO6bugfix: save previous file when dropping a new one on - [68]
H3KWPK3Gregression: dropping files on the window - [69]
SCOXD4EOMerge lines.love - [*]
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