local data = { -- window size width = 15, height = 10, } local form_esc = minetest.formspec_escape -- shorten the function local modstorage = core.get_mod_storage() local function create_tabs(selected) return "tabheader[0,0;_option_tabs_;" .. " LUA EDITOR ,FORMSPEC EDITOR, LUA CONSOLE , FILES , STARTUP , FUNCTIONS , HELP ;"..selected..";;]" end local function copy_table(table) local new = {} for i, v in pairs(table) do if type(v) == "table" then v = copy_table(v) end new[i] = v end return new end ---------- -- LOAD AND DEFINE STUFF - global stuff is accissible from the UI ---------- local split = function (str, splitter) -- a function to split a string into a list. "\" before the splitter makes it ignore it (usefull for minetests formspecs) local result = {""} for i=1, str:len() do char = string.sub(str, i, i) if char == splitter and string.sub(str, i-1, i-1) ~= "\\" then table.insert(result, "") else result[#result] = result[#result]..char end end return result end local output = {} -- the output for errors, prints, etc local saved_file = modstorage:get_string("_lua_saved") -- remember what file is currently being edited if saved_file == "" then saved_file = false -- if the file had no save name (it was still saved) end local lua_startup = split(modstorage:get_string("_lua_startup"), ",") -- the list of scripts to run at startup local lua_files = split(modstorage:get_string("_lua_files_list"), ",") -- the list of names of all saved files local ui_files = split(modstorage:get_string("_UI_files_list"), ",") -- UI files list local reg_funcs = {formspec_input={}, chatcommands={}, on_connect={}, joinplayer={}, sending_chat_message={}, recieving_chat_message={}} local selected_files = {0, 0} minetest.register_on_connect(function() -- some functions don't work after startup. this tries to replace them minetest.get_mod_storage = function() return modstorage end core.get_mod_storage = function() return modstorage end -- show formspec end) -- add whatever functions don't work after startup to here (if possible) ---------- -- FUNCTIONS FOR UI ---------- function print(...) -- replace print to output into the UI. (doesn't refresh untill the script has ended) params = {...} if #params == 1 then local str = params[1] if type(str) ~= "string" then str = dump(str) end table.insert(output, "") for i=1, str:len() do char = string.sub(str, i, i) if char == "\n" then table.insert(output, "") -- split multiple lines over multiple lines. without this, text with line breaks would not display properly else output[#output] = output[#output]..char end end else for i, v in pairs(params) do print(v) end end end function safe(func) -- run a function without crashing the game. All errors are displayed in the UI. f = function(...) -- This can be used for functions being registered with minetest, like "minetest.register_chat_command()" status, out = pcall(func, ...) if status then return out else table.insert(output, "#ff0000Error: "..out) minetest.debug("Error (func): "..out) return nil end end return f end ---------- -- CODE EXECUTION ---------- local function run(code, name) -- run a script if name == nil then name = saved_file end status, err = pcall(loadstring(code)) -- run if status then if saved_file == false then table.insert(output, "#00ff00finished") -- display that the script ran without errors else table.insert(output, "#00ff00"..name..": finished") -- display which script, if it was saved end else if err == "attempt to call a nil value" then err = "Syntax Error" end if saved_file == false then table.insert(output, "#ff0000Error: "..err) -- display errors minetest.log("Error (unsaved): "..err) else table.insert(output, "#ff0000"..name..": Error: "..err) minetest.log("Error ("..name.."): "..err) end end end local function on_startup() -- ran on startup. Runs all scripts registered for startup for i, v in pairs(lua_startup) do if v ~= "" then run(modstorage:get_string("_lua_file_"..v, v), v) -- errors still get displayed in the UI end end end on_startup() ---------- -- FILE READING AND SAVING ---------- local function load_lua() -- returns the contents of the file currently being edited if saved_file == false then return modstorage:get_string("_lua_temp") -- unsaved files are remembered (get saved on UI reloads - when clicking on buttons) else return modstorage:get_string("_lua_file_"..saved_file) end end local function save_lua(code) -- save a file if saved_file == false then modstorage:set_string("_lua_temp", code) else modstorage:set_string("_lua_file_"..saved_file, code) end end ---------- -- FORM DEFINITIONS ---------- local function startup_form() -- the formspec for adding or removing files for startup local startup_str = "" for i, v in pairs(lua_startup) do if i ~= 1 then startup_str = startup_str.."," end startup_str = startup_str .. form_esc(v) end local files_str = "" for i, v in pairs(lua_files) do if i ~= 1 then files_str = files_str.."," end files_str = files_str .. form_esc(v) end local form = "".. "size["..data.width..","..data.height.."]" .. "label[0,0.1;Startup Items:]".. "label["..data.width/2 ..",0.1;File List:]".. "textlist[0,0.5;"..data.width/2-0.1 ..","..data.height-1 ..";starts;"..startup_str.."]".. "textlist["..data.width/2 ..",0.5;"..data.width/2-0.1 ..","..data.height-1 ..";chooser;"..files_str.."]".. "label[0," .. data.height-0.3 .. ";double click items to add or remove from startup]".. "" .. create_tabs(5) return form end local function lua_editor() -- the main formspec for editing local output_str = "" -- convert the output to a string for i, v in pairs(output) do if output_str:len() > 0 then output_str = output_str .. "," end output_str = output_str .. form_esc(v) end local code = form_esc(load_lua()) -- create the form local form = "".. "size["..data.width..","..data.height.."]" .. "textarea[0.3,0.1;"..data.width ..","..data.height-3 ..";editor;Lua editor;"..code.."]".. "button[0," .. data.height-3.5 .. ";1,0;run;RUN]".. "button[1," .. data.height-3.5 .. ";1,0;clear;CLEAR]".. "button[2," .. data.height-3.5 .. ";1,0;save;SAVE]".. "textlist[0,"..data.height-3 ..";"..data.width-0.2 ..","..data.height-7 ..";output;"..output_str..";".. #output .."]".. "" .. create_tabs(1) return form end local function file_viewer() -- created with the formspec editor! local lua_files_item_str = "" for i, item in pairs(lua_files) do if i ~= 1 then lua_files_item_str = lua_files_item_str.."," end lua_files_item_str = lua_files_item_str .. form_esc(item) end local ui_select_item_str = "" for i, item in pairs(ui_files) do if i ~= 1 then ui_select_item_str = ui_select_item_str.."," end ui_select_item_str = ui_select_item_str .. form_esc(item) end local form = "" .. "size["..data.width..","..data.height.."]" .. "textlist[-0.2,0.2;"..data.width/2.02- -0.2 ..","..data.height- 1 ..";lua_select;"..lua_files_item_str.."]" .. "label[-0.2,-0.2;LUA FILES]" .. "field[0.1,"..data.height- 0.2 ..";3,1;new_lua;NEW;]" .. "field_close_on_enter[new_lua;false]" .. "button[2.6,"..data.height- 0.5 ..";0.5,1;add_lua;+]" .. "textlist["..data.width/1.97 ..",0.2;"..data.width- 0-(data.width/1.97) ..","..data.height- 1 ..";ui_select;"..ui_select_item_str.."]" .. "label["..data.width/1.96 ..",-0.2;FORMSPEC FILES]" .. "field["..data.width- 2.8 ..","..data.height- 0.2 ..";3,1;new_ui;NEW;]" .. "field_close_on_enter[new_ui;false]" .. "button["..data.width- 0.3 ..","..data.height- 0.5 ..";0.5,1;add_ui;+]" .. "label["..data.width/2.4 ..","..data.height- 0.8 ..";Double click a file to open it]" .. "button[3.1,"..data.height- 0.5 ..";1.1,1;del_lua;DELETE]" .. "button["..data.width- 4.2 ..","..data.height- 0.5 ..";1.1,1;del_ui;DELETE]" .. "" .. create_tabs(4) return form end ---------- -- FUNCTIONALITY ---------- minetest.register_on_formspec_input(function(formname, fields) -- EDITING PAGE ---------- if formname == "lua:editor" then if fields.run then --[RUN] button save_lua(fields.editor) run(fields.editor) minetest.show_formspec("lua:editor", lua_editor()) elseif fields.save then --[SAVE] button if saved_file == false then modstorage:set_string("_lua_temp", fields.editor) else modstorage:set_string("_lua_file_"..saved_file, fields.editor) end elseif fields.clear then --[CLEAR] button output = {} save_lua(fields.editor) minetest.show_formspec("lua:editor", lua_editor()) end -- STARTUP EDITOR ---------- elseif formname == "lua:startup" then -- double click a file to remove it from the list if fields.starts then local select = {["type"] = string.sub(fields.starts, 1, 3), ["row"] = tonumber(string.sub(fields.starts, 5, 5))} if select.type == "DCL" then table.remove(lua_startup, select.row) local startup_str = "" for i, v in pairs(lua_startup) do if v ~= "" then startup_str = startup_str..v.."," end end modstorage:set_string("_lua_startup", startup_str) minetest.show_formspec("lua:startup", startup_form()) end elseif fields.chooser then -- double click a file to add it to the list local select = {["type"] = string.sub(fields.chooser, 1, 3), ["row"] = tonumber(string.sub(fields.chooser, 5, 5))} if select.type == "DCL" then table.insert(lua_startup, lua_files[select.row]) local startup_str = "" for i, v in pairs(lua_startup) do if v ~= "" then startup_str = startup_str..v.."," end end modstorage:set_string("_lua_startup", startup_str) minetest.show_formspec("lua:startup", startup_form()) end end end end) ---------- ---------- -- PASTE FORMSPEC EDITOR HERE -- ---------- ---------- -- ---------- ---------- -- PASTE FORMSPEC EDITOR HERE -- ---------- ---------- ---------- -- UI FUNCTIONALITY ---------- minetest.register_on_formspec_input(function(formname, fields) -- FILE VIEWER ---------- if formname == "files:viewer" then if fields.del_lua then name = lua_files[selected_files[1] ] table.remove(lua_files, selected_files[1]) files_str = "" for i, v in pairs(lua_files) do if v ~= "" then files_str = files_str..v.."," -- remove the file from the list end end if name == saved_file then -- clear the editing area if the file was loaded saved_file = false modstorage:set_string("_lua_saved", "") save_lua("") end modstorage:set_string("_lua_files_list", files_str) minetest.show_formspec("files:viewer", file_viewer()) elseif fields.del_ui then name = ui_files[selected_files[2] ] table.remove(ui_files, selected_files[2]) files_str = "" for i, v in pairs(ui_files) do if v ~= "" then files_str = files_str..v.."," -- remove the file from the list end end if name == current_ui_file then -- clear the editing area if the file was loaded load_UI("new") end modstorage:set_string("_UI_files_list", files_str) minetest.show_formspec("files:viewer", file_viewer()) elseif fields.lua_select then -- click on a file to select it, double click to open it local index = tonumber(string.sub(fields.lua_select, 5)) if string.sub(fields.lua_select, 1, 3) == "DCL" then saved_file = lua_files[index] modstorage:set_string("_lua_saved", saved_file) minetest.show_formspec("lua:editor", lua_editor()) else selected_files[1] = index minetest.show_formspec("files:viewer", file_viewer()) end elseif fields.ui_select then -- click on a file to select it, double click to open it local index = tonumber(string.sub(fields.ui_select, 5)) if string.sub(fields.ui_select, 1, 3) == "DCL" then load_UI(ui_files[index]) reload_ui() else selected_files[2] = index minetest.show_formspec("files:viewer", file_viewer()) end elseif fields.key_enter_field == "new_lua" or fields.add_lua then local exist = false for i, v in pairs(lua_files) do if v == fields.new_lua then exist = true selected_files[1] = i end end if not exist then table.insert(lua_files, fields.new_lua) selected_files[1] = #lua_files files_str = "" for i, v in pairs(lua_files) do if v ~= "" then files_str = files_str..v.."," end end modstorage:set_string("_lua_files_list", files_str) saved_file = fields.new_lua minetest.show_formspec("lua:editor", lua_editor()) end elseif fields.key_enter_field == "new_ui" or fields.add_ui then local exist = false for i, v in pairs(ui_files) do if v == fields.new_ui then exist = true selected_files[2] = i end end if not exist then table.insert(ui_files, fields.new_ui) selected_files[2] = #ui_files files_str = "" for i, v in pairs(ui_files) do if v ~= "" then files_str = files_str..v.."," end end modstorage:set_string("_UI_files_list", files_str) load_UI(fields.new_ui) reload_ui() end end end if fields._option_tabs_ then if fields._option_tabs_ == "1" then minetest.show_formspec("lua:editor", lua_editor()) elseif fields._option_tabs_ == "2" then reload_ui() elseif fields._option_tabs_ == "4" then minetest.show_formspec("files:viewer", file_viewer()) elseif fields._option_tabs_ == "5" then minetest.show_formspec("lua:startup", startup_form()) else minetest.show_formspec("lua:unknown", "size["..data.width..","..data.height.."]label[1,1;COMING SOON]"..create_tabs(fields._option_tabs_)) end end end) ---------- -- REGISTER COMMAND ---------- core.register_chatcommand("dte", { -- register the chat command description = core.gettext("open a lua IDE"), func = function(parameter) minetest.show_formspec("lua:editor", lua_editor()) end, })