From 7ce97f4abe02c145f44e59b8da81f2a760ad6dbe Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sat, 19 Apr 2025 18:32:14 +0300 Subject: [PATCH] extract code editor implementation from console.xml --- res/layouts/code_editor.xml | 69 ++++++++ res/layouts/code_editor.xml.lua | 270 +++++++++++++++++++++++++++++++ res/layouts/console.xml | 73 +-------- res/layouts/console.xml.lua | 271 -------------------------------- 4 files changed, 342 insertions(+), 341 deletions(-) create mode 100644 res/layouts/code_editor.xml create mode 100644 res/layouts/code_editor.xml.lua diff --git a/res/layouts/code_editor.xml b/res/layouts/code_editor.xml new file mode 100644 index 00000000..906ff070 --- /dev/null +++ b/res/layouts/code_editor.xml @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/res/layouts/code_editor.xml.lua b/res/layouts/code_editor.xml.lua new file mode 100644 index 00000000..e936aa71 --- /dev/null +++ b/res/layouts/code_editor.xml.lua @@ -0,0 +1,270 @@ +local writeables = {} +local registry = require "core:internal/scripts_registry" +local filenames + +local current_file = { + filename = "", + mutable = nil +} + +local warnings_all = {} +local errors_all = {} + +local warning_id = 0 +local error_id = 0 + +events.on("core:warning", function (wtype, text, traceback) + local full = wtype..": "..text + if table.has(warnings_all, full) then + return + end + local encoded = base64.encode(bjson.tobytes({frames=traceback})) + document.problemsLog:add(gui.template("problem", { + type="warning", + text=full, + traceback=encoded, + id=tostring(warning_id) + })) + warning_id = warning_id + 1 + table.insert(warnings_all, full) +end) + +events.on("core:error", function (msg, traceback) + local _, endindex = string.find(msg, ": ") + local full = "" + for i,frame in ipairs(traceback) do + full = full..frame.source..tostring(frame.currentline) + end + if table.has(errors_all, full) then + return + end + local encoded = base64.encode(bjson.tobytes({frames=traceback})) + document.problemsLog:add(gui.template("problem", { + type="error", + text=msg:sub(endindex), + traceback=encoded, + id=tostring(error_id) + })) + error_id = error_id + 1 + table.insert(errors_all, full) +end) + +local function find_mutable(filename) + local packid = file.prefix(filename) + if packid == "core" then + return + end + local saved = writeables[packid] + if saved then + return saved..":"..file.path(filename) + end + local packinfo = pack.get_info(packid) + if not packinfo then + return + end + local path = packinfo.path + if file.is_writeable(path) then + return file.join(path, file.path(filename)) + end +end + +local function refresh_file_title() + if current_file.filename == "" then + document.title.text = "" + return + end + local edited = document.editor.edited + current_file.modified = edited + document.saveIcon.enabled = edited + document.title.text = gui.str('File')..' - '..current_file.filename + ..(edited and ' *' or '') +end + +function filter_files(text) + local filtered = {} + for _, filename in ipairs(filenames) do + if filename:find(text) then + table.insert(filtered, filename) + end + end + build_files_list(filtered, text) +end + +function on_control_combination(keycode) + if keycode == input.keycode("s") then + save_current_file() + elseif keycode == input.keycode("r") then + run_current_file() + end +end + +function unlock_access() + if current_file.filename == "" then + return + end + pack.request_writeable(file.prefix(current_file.filename), + function(token) + writeables[file.prefix(current_file.filename)] = token + current_file.mutable = token..":"..file.path(current_file.filename) + open_file_in_editor(current_file.filename, 0, current_file.mutable) + end + ) +end + +function run_current_file() + if not current_file.filename then + return + end + local chunk, err = loadstring(document.editor.text, current_file.filename) + clear_output() + if not chunk then + local line, message = err:match(".*:(%d*): (.*)") + document.output:paste( + string.format( + "\n[#FF3030]%s: %s[#FFFFFF]", + gui.str("Error at line %{0}"):gsub("%%{0}", line), message) + ) + return + end + local info = registry.get_info(current_file.filename) + local script_type = info and info.type or "file" + local unit = info and info.unit + save_current_file() + + local func = function() + local stack_size = debug.count_frames() + xpcall(chunk, function(msg) __vc__error(msg, 1, 1, stack_size) end) + end + + local funcs = { + block = block.reload_script, + item = item.reload_script, + world = world.reload_script, + hud = hud.reload_script, + component = entities.reload_component, + module = reload_module, + } + func = funcs[script_type] or func + local output = core.capture_output(function() func(unit) end) + document.output:paste(string.format("\n%s", output)) +end + +function clear_traceback() + local tb_list = document.traceback + tb_list:clear() + tb_list:add("") +end + +function clear_output() + local output = document.output + output.text = "" + output:paste("[#FFFFFF80]"..gui.str("devtools.output").."[#FFFFFF]") +end + +events.on("core:open_traceback", function(traceback_b64) + local traceback = bjson.frombytes(base64.decode(traceback_b64)) + modes:set('debug') + + clear_traceback() + + local tb_list = document.traceback + local srcsize = tb_list.size + for _, frame in ipairs(traceback.frames) do + local callback = "" + local framestr = "" + if frame.what == "C" then + framestr = "C/C++ " + else + framestr = frame.source..":"..tostring(frame.currentline).." " + if file.exists(frame.source) then + callback = string.format( + "open_file_in_editor('%s', %s)", + frame.source, frame.currentline-1 + ) + else + callback = "document.editor.text = 'Could not open source file'" + end + end + if frame.name then + framestr = framestr.."("..tostring(frame.name)..")" + end + local color = "#FFFFFF" + tb_list:add(gui.template("stack_frame", { + location=framestr, + color=color, + callback=callback, + enabled=file.exists(frame.source) + })) + end + tb_list.size = srcsize +end) + +function save_current_file() + if not current_file.mutable then + return + end + file.write(current_file.mutable, document.editor.text) + current_file.modified = false + document.saveIcon.enabled = false + document.title.text = gui.str('File')..' - '..current_file.filename + document.editor.edited = false +end + +function open_file_in_editor(filename, line, mutable) + local editor = document.editor + local source = file.read(filename):gsub('\t', ' ') + editor.scroll = 0 + editor.text = source + editor.focused = true + editor.syntax = file.ext(filename) + if line then + time.post_runnable(function() + editor.caret = editor:linePos(line) + end) + end + document.title.text = gui.str('File') .. ' - ' .. filename + current_file.filename = filename + current_file.mutable = mutable or find_mutable(filename) + document.lockIcon.visible = current_file.mutable == nil + document.editor.editable = current_file.mutable ~= nil + document.saveIcon.enabled = current_file.modified +end + +function build_files_list(filenames, selected) + local files_list = document.filesList + files_list.scroll = 0 + files_list:clear() + + for _, actual_filename in ipairs(filenames) do + local filename = actual_filename + if selected then + filename = filename:gsub(selected, "**"..selected.."**") + end + local parent = file.parent(filename) + local info = registry.get_info(actual_filename) + local icon = "file" + if info then + icon = info.type == "component" and "entity" or info.type + end + files_list:add(gui.template("script_file", { + path = parent .. (parent[#parent] == ':' and '' or '/'), + name = file.name(filename), + icon = icon, + unit = info and info.unit or '', + filename = actual_filename + })) + end +end + +function on_open(mode) + local files_list = document.filesList + + filenames = registry.filenames + table.sort(filenames) + build_files_list(filenames) + + document.editorContainer:setInterval(200, refresh_file_title) + + clear_traceback() + clear_output() +end diff --git a/res/layouts/console.xml b/res/layouts/console.xml index bbfdcf7a..16fbbb0b 100644 --- a/res/layouts/console.xml +++ b/res/layouts/console.xml @@ -22,76 +22,9 @@ markup="md" > - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @devtools.traceback") -end - -function clear_output() - local output = document.output - output.text = "" - output:paste("[#FFFFFF80]"..gui.str("devtools.output").."[#FFFFFF]") -end - -events.on("core:open_traceback", function(traceback_b64) - local traceback = bjson.frombytes(base64.decode(traceback_b64)) - modes:set('debug') - - clear_traceback() - - local tb_list = document.traceback - local srcsize = tb_list.size - for _, frame in ipairs(traceback.frames) do - local callback = "" - local framestr = "" - if frame.what == "C" then - framestr = "C/C++ " - else - framestr = frame.source..":"..tostring(frame.currentline).." " - if file.exists(frame.source) then - callback = string.format( - "open_file_in_editor('%s', %s)", - frame.source, frame.currentline-1 - ) - else - callback = "document.editor.text = 'Could not open source file'" - end - end - if frame.name then - framestr = framestr.."("..tostring(frame.name)..")" - end - local color = "#FFFFFF" - tb_list:add(gui.template("stack_frame", { - location=framestr, - color=color, - callback=callback, - enabled=file.exists(frame.source) - })) - end - tb_list.size = srcsize -end) - function setup_variables() local pid = hud.get_player() local x,y,z = player.get_pos(pid) @@ -351,9 +93,7 @@ end function set_mode(mode) local show_prompt = mode == 'chat' or mode == 'console' - document.lockIcon.visible = false document.editorRoot.visible = mode == 'debug' - document.editorContainer.visible = mode == 'debug' document.logContainer.visible = mode ~= 'debug' if mode == 'debug' then @@ -378,17 +118,6 @@ function on_open(mode) }, function (mode) set_mode(mode) end, mode or "console") - - local files_list = document.filesList - - filenames = registry.filenames - table.sort(filenames) - build_files_list(filenames) - - document.editorContainer:setInterval(200, refresh_file_title) - - clear_traceback() - clear_output() elseif mode then modes:set(mode) end