diff --git a/README.md b/README.md index cd71808..f8217f4 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,9 @@ Shows all installed scripts. Shows all available configurations. - ``: Shows the value of a specific configuration. - ` `: Updates the value of a specific configuration. +## refresh +Downloads the names of all programs and libraries of the official repository. +Refreshes autocomplete. ## help Shows all available commands and their description. - ``: Shows the description of a command by name. diff --git a/scm.lua b/scm.lua index 3a01f10..0f31229 100644 --- a/scm.lua +++ b/scm.lua @@ -12,6 +12,7 @@ scm.config = { ["programSuffix"] = "-prog", ["librarySuffix"] = "-lib", ["infoFile"] = "files.txt", -- provides the structure of a git repo (paths to all files) + ["apiGithubGetRepos"] = "https://api.github.com/orgs/mc-cc-scripts/repos?type=all&per_page=100&page=1", -- Local Settings ["installScript"] = "1kKZ8zTS", ["rootDirectory"] = "", @@ -24,7 +25,10 @@ scm.config = { ["printPrefix"] = "[scm] ", ["logDate"] = false, ["writeLogFile"] = false, - ["logFilePath"] = "logs/scm-log.txt" + ["logFilePath"] = "logs/scm-log.txt", + ["repoScriptsFile"] = "scm-repo-scripts.txt", -- will be saved in configDirectory as well + ["allowCLIPrefix"] = true, + ["cliPrefix"] = false } ---------------- @@ -130,6 +134,16 @@ $ config Updates the configuration ]] }, + ["refresh"] = { + func = function (args) + scm:refreshAutocomplete() + end, + description = [[ +$ refresh + Downloads the names of all programs and libraries of the official repository. + Refreshes autocomplete. + ]] + }, ["help"] = { ---@param args table func = function (args) @@ -152,6 +166,142 @@ $ help } } +function scm:refreshRepoScripts () + self:log("Downloading program and library names from GitHub...") + local repoScripts = {} + + local programs = {} + local libraries = {} + + local request = http.get(self.config["apiGithubGetRepos"]) + if request then + local response = request.readAll() + request.close() + + local responseTable = textutils.unserializeJSON(response) + + local programSuffix = self.config["programSuffix"] + local librarySuffix = self.config["librarySuffix"] + + for i = 1, #responseTable, 1 do + local scriptName = responseTable[i]["name"] + if string.sub(scriptName, -string.len(programSuffix)) == programSuffix then + programs[ + string.sub(scriptName, 0, string.len(scriptName)-string.len(programSuffix)) + ] = {} + elseif string.sub(scriptName, -string.len(librarySuffix)) == librarySuffix then + libraries[ + string.sub(scriptName, 0, string.len(scriptName)-string.len(librarySuffix)) + ] = {} + end + end + scm:log("Done") + else + scm:log("Download failed") + end + + self.commands["add"]["args"] = programs + self.commands["require"]["args"] = libraries + + repoScripts["libraries"] = libraries + repoScripts["programs"] = programs + + local file = fs.open(self.config["configDirectory"] .. self.config["repoScriptsFile"], "w") + if file then + file.write(textutils.serializeJSON(repoScripts)) + file.close() + end +end + +function scm:loadRepoScripts () + local file = fs.open(self.config["configDirectory"] .. self.config["repoScriptsFile"], "r") + + if not file then + self:refreshRepoScripts() + else + local repoScripts = textutils.unserializeJSON(file.readAll()) or nil + if repoScripts then + self.commands["add"]["args"] = repoScripts["programs"] + self.commands["require"]["args"] = repoScripts["libraries"] + end + + file.close() + end +end + +function scm:prepareAutocomplete () + -- prepare update and remove + scm:loadScripts() + local installedScripts = {} + for i = 1, #self.scripts, 1 do + installedScripts[self.scripts[i].name] = {} + end + installedScripts["all"] = {} + + self.commands["update"]["args"] = installedScripts + self.commands["remove"]["args"] = installedScripts + + -- prepare add and require + self:loadRepoScripts() + + -- prepare config + local availableConfigs = {} + + for k, _ in pairs(self.config) do + availableConfigs[k] = {} + end + + self.commands["config"]["args"] = availableConfigs + + -- prepare help + local availableCommands = {} + + for k, _ in pairs(self.commands) do + availableCommands[k] = {} + end + + self.commands["help"]["args"] = availableCommands +end + +---@param shell table +---@param index integer +---@param argument string +---@param previous table +---@return table | nil +local function completionFunction (shell, index, argument, previous) + local commands = {} + for k, _ in pairs(scm.commands) do + commands[k] = scm.commands[k]["args"] or {} + end + + local currArg = commands + for i = 2, #previous do + if currArg[previous[i]] then + currArg = currArg[previous[i]] + else + return nil + end + end + + local results = {} + for word, _ in pairs(currArg) do + if word:sub(1, #argument) == argument then + results[#results+1] = word:sub(#argument + 1) + end + end + return results; +end + +local function updateAutocomplete () + shell.setCompletionFunction("scm", completionFunction) +end + +function scm:refreshAutocomplete () + scm:refreshRepoScripts() + scm:prepareAutocomplete() + updateAutocomplete() +end + ---@param message string function scm:log (message) local datetime = "" @@ -392,7 +542,7 @@ function scm:addScript (sourceObject, success) if self.scripts[i].source[sourceObject.sourceName] then self.scripts[i].source[sourceObject.sourceName] = sourceObject.source[sourceObject.sourceName] self:saveScripts() - + return true end end @@ -400,13 +550,22 @@ function scm:addScript (sourceObject, success) if not scriptExists then scm:log("Script added: " .. sourceObject.name) - table.insert(self.scripts, sourceObject) + table.insert(self.scripts, sourceObject) else scm:log("Script already exists.") + return false end - + self:saveScripts() + -- update for autocomplete + self.commands["update"]["args"] = self.commands["update"]["args"] or {} + self.commands["remove"]["args"] = self.commands["remove"]["args"] or {} + self.commands["update"]["args"][sourceObject.name] = {} + self.commands["remove"]["args"][sourceObject.name] = {} + self:prepareAutocomplete() + updateAutocomplete() + return true end @@ -454,6 +613,7 @@ function scm:removeScript (name, keepScriptConfig) self:saveScripts() end + -- delete file if scriptType and fs.exists(self.config[scriptType .. "Directory"] .. name .. ".lua") then fs.delete(self.config[scriptType .. "Directory"] .. name .. self.config[scriptType .. "Suffix"]) if scriptType == "library" then @@ -464,6 +624,10 @@ function scm:removeScript (name, keepScriptConfig) if scriptType == "program" then fs.delete(name) end + + -- update autocomplete + self:prepareAutocomplete() + updateAutocomplete() end function scm:removeAllScripts () @@ -689,10 +853,59 @@ function scm:init () self:loadScripts() end +---@param resetPosition boolean | nil +function scm:cli (resetPosition, args) + if resetPosition ~= nil and resetPosition == true then + term.setCursorPos(1, 7) + end + + -- enable autocomplete + self:prepareAutocomplete() + updateAutocomplete() + + -- enable newline starting with `scm ` + if self.config["allowCLIPrefix"] then + self.config["cliPrefix"] = true + self:saveConfig() + end + + -- some interface + local _, cursorY = term.getCursorPos() + if cursorY < 7 then cursorY = 7 end + term.setCursorPos(1, cursorY) + term.blit(" ","ffffffffffffffffffffffffffffffff","44444444444444444444444444444444") + term.setCursorPos(1, cursorY) + term.scroll(1) + term.blit(" SCM - Script Manager ","ffffffffffffffffffffffffffffffff","44444444444444444444444444444444") + term.setCursorPos(1, cursorY) + term.scroll(1) + term.blit(" Autocomplete enabled. ","77777777777777777777777777777777","44444444444444444444444444444444") + term.setCursorPos(1, cursorY) + term.scroll(1) + term.blit(" Type `scm help` to learn more. ","77777777ffffffff7777777777777777","44444444444444444444444444444444") + term.setCursorPos(1, cursorY) + term.scroll(1) + term.blit(" ","ffffffffffffffffffffffffffffffff","44444444444444444444444444444444") + term.setCursorPos(1, cursorY) + term.scroll(2) + + if self.config["cliPrefix"] then + shell.run(read(nil, nil, shell.complete, "scm ")) + end +end + ---@param args table function scm:handleArguments (args) + if #args == 0 then + self:cli(false, args) + return + end + if args[1] and self.commands[args[1]] then self.commands[args[1]]["func"](args) + if self.config["cliPrefix"] then + shell.run(read(nil, nil, shell.complete, "scm ")) + end end end