Programming environment for editing various of my live apps without restarting them.
-- the communication channel with the app
-- commands are processed on the other end, in the app

function live.send_definition_to_app(State)
  local current_buffer = live.definition_to_string(State)
  State.saved = true
  live.send_to_app(current_buffer)
end

function live.send_to_app(msg)
  Load_time_error = nil
  Run_time_error = nil
  -- first clear the response buffer from any previous commands
  local clear = io.open(love.filesystem.getAppdataDirectory()..'/_love_akkartik_app_driver', 'w')
  clear:close()
  -- send a fresh command
  local f = io.open(love.filesystem.getAppdataDirectory()..'/_love_akkartik_driver_app', 'w')
  if f == nil then return end
  f:write(msg)
  f:close()
  print('$'..color_escape(--[[bold]]1, --[[blue]]4))
  print(msg)
  print(reset_terminal())
end

-- look for a message from outside, and return nil if there's nothing
-- if there's an error, save it and return ''
function live.receive_from_app()
  local f = io.open(love.filesystem.getAppdataDirectory()..'/_love_akkartik_app_driver')
  if f == nil then return nil end
  local result = f:read('*a')
  f:close()
  if result == '' then return nil end  -- empty file == no message
  print('=>'..color_escape(0, --[[green]]2))
  print(result)
  print(reset_terminal())
  if result:match('^ERROR ') then
    Load_time_error = result:sub(#('ERROR '))
    result = '{}'
  end
  os.remove(love.filesystem.getAppdataDirectory()..'/_love_akkartik_app_driver')
  return result
end

function live.receive_run_time_error_from_app()
  local f = io.open(love.filesystem.getAppdataDirectory()..'/_love_akkartik_app_driver_run_time_error')
  if f == nil then return nil end
  local result = f:read('*a')
  f:close()
  if result == '' then return nil end  -- empty file == no message
  print('=>'..color_escape(0, --[[red]]1))
  print(result)
  print(reset_terminal())
  Run_time_error = result
  os.remove(love.filesystem.getAppdataDirectory()..'/_love_akkartik_app_driver_run_time_error')
  return result
end

function live.definition_to_string(State)
  return table.concat(map(State.lines, function(line) return line.data end), '\n')
end

function map(arr, f)
  local result = {}
  for _, x in ipairs(arr) do
    table.insert(result, f(x))
  end
  return result
end