feat: 初始提交 LeonOS 实现

添加 LeonOS 的基本实现,包括:
- 核心 API 模块(colors, disk, gps, keys, multishell, parallel, rednet, redstone, settings, vector)
- 命令行程序(about, alias, bg, clear, copy, delete, edit, fg, help, list, lua, mkdir, move, paint, peripherals, programs, reboot, set, shutdown, threads)
- 系统启动脚本和包管理
- 文档(README.md, LICENSE)
- 开发工具(devbin)和更新程序

实现功能:
- 完整的线程管理系统
- 兼容 ComputerCraft 的 API 设计
- 改进的 shell 和命令补全系统
- 多标签终端支持
- 设置管理系统
This commit is contained in:
2025-08-31 16:54:18 +08:00
commit 90a901f58e
94 changed files with 8372 additions and 0 deletions

View File

@@ -0,0 +1,201 @@
-- override the fs library to use this resolution function where necessary
-- almost identical to the override used in .OS
local fs = rawget(_G, "fs")
-- split a file path into segments
function fs.split(path)
local s = {}
for S in path:gmatch("[^/\\]+") do
if S == ".." then
s[#s] = nil
elseif S ~= "." then
s[#s+1] = S
end
end
return s
end
-- package isn't loaded yet, so unfortunately this is necessary
local function expect(...)
return require and require("cc.expect").expect(...)
end
-- path resolution:
-- if the path begins with /rc, then redirect to wherever that actually
-- is; otherwise, resolve the path based on the current program's working
-- directory
-- this is to allow .OS to run from anywhere
local function resolve(path)
local thread = package and package.loaded.thread
local root = (thread and thread.getroot()) or "/"
local pwd = (thread and thread.dir()) or "/"
if path:sub(1,1) ~= "/" then
path = fs.combine(pwd, path)
end
path = fs.combine(root, path)
local segments = fs.split(path)
if segments[1] == "rc" then
return fs.combine(_RC_ROM_DIR, table.concat(segments, "/", 2, #segments))
else
return path
end
end
-- override: fs.combine
local combine = fs.combine
function fs.combine(...)
return "/" .. combine(...)
end
-- override: fs.getDir
local getDir = fs.getDir
function fs.getDir(p)
return "/" .. getDir(p)
end
-- override: fs.exists
local exists = fs.exists
function fs.exists(path)
expect(1, path, "string")
return exists(resolve(path))
end
-- override: fs.list
local list = fs.list
function fs.list(path)
expect(1, path, "string")
path = resolve(path)
local _, files = pcall(list, path)
if not _ then return nil, files end
if path == "/" then
-- inject /rc into the root listing
if not exists("/rc") then
files[#files+1] = "rc"
end
end
return files
end
-- override: fs.getSize
local getSize = fs.getSize
function fs.getSize(path)
expect(1, path, "string")
return getSize((resolve(path)))
end
-- override: fs.isDir
local isDir = fs.isDir
function fs.isDir(path)
expect(1, path, "string")
return isDir(resolve(path))
end
-- override: fs.makeDir
local makeDir = fs.makeDir
function fs.makeDir(path)
expect(1, path, "string")
return makeDir(resolve(path))
end
-- override: fs.move
local move = fs.move
function fs.move(a, b)
expect(1, a, "string")
expect(2, b, "string")
return move(resolve(a), resolve(b))
end
-- override: fs.copy
local copy = fs.copy
function fs.copy(a, b)
expect(1, a, "string")
expect(2, b, "string")
return copy(resolve(a), resolve(b))
end
-- override: fs.delete
local delete = fs.delete
function fs.delete(path)
expect(1, path, "string")
return delete(resolve(path))
end
-- override: fs.open
local open = fs.open
function fs.open(file, mode)
expect(1, file, "string")
expect(2, mode, "string")
return open(resolve(file), mode or "r")
end
-- override: fs.find
local find = fs.find
function fs.find(path)
expect(1, path, "string")
return find(resolve(path))
end
-- override: fs.attributes
local attributes = fs.attributes
function fs.attributes(path)
expect(1, path, "string")
return attributes(resolve(path))
end
-- new: fs.complete
function fs.complete(path, location, include_files, include_dirs)
expect(1, path, "string")
expect(2, location, "string")
expect(3, include_files, "boolean", "nil")
expect(4, include_dirs, "boolean", "nil")
if include_files == nil then include_files = true end
if include_dirs == nil then include_dirs = true end
if path:sub(1,1) == "/" and path:sub(-1) ~= "/" then
location = fs.getDir(path)
elseif path:sub(-1) == "/" then
location = path
else
location = fs.combine(location, fs.getDir(path))
end
local completions = {}
if not fs.exists(location) or not fs.isDir(location) then
return completions
end
local name = fs.getName(path)
if path:sub(-1) == "/" then name = "" end
local files = fs.list(location)
for i=1, #files, 1 do
local file = files[i]
local full = fs.combine(location, file)
if file:sub(1, #name) == name then
local dir = fs.isDir(full)
if (dir and include_dirs) or include_files then
completions[#completions+1] = file:sub(#name+1)
if #completions[#completions] == 0 then
completions[#completions] = nil
end
end
if dir then
completions[#completions+1] = file:sub(#name+1) .. "/"
end
end
end
return completions
end
function fs.isDriveRoot(path)
expect(1, path, "string")
if #path == 0 then path = "/" end
return path == "/" or fs.getDrive(path) == fs.getDrive(fs.getDir(path))
end

View File

@@ -0,0 +1,131 @@
-- package library --
local rc = ...
_G.package = {}
package.config = "/\n;\n?\n!\n-"
package.cpath = ""
package.path = "/rc/apis/?.lua;/rc/modules/main/?.lua;./lib/?.lua;./lib/?/init.lua;./?.lua;./?/init.lua"
local function rm(api)
local tab = _G[api]
_G[api] = nil
return tab
end
package.loaded = {
_G = _G,
os = os,
rc = rc,
math = math,
utf8 = utf8,
table = table,
debug = debug,
bit32 = rawget(_G, "bit32"),
string = string,
package = package,
coroutine = coroutine,
-- CC-specific ones
peripheral = rm("peripheral"),
redstone = rm("redstone"),
commands = rm("commands"),
pocket = rm("pocket"),
turtle = rm("turtle"),
http = rm("http"),
term = rm("term"),
fs = rm("fs"),
rs = rm("rs"),
-- LeonOS-PC APIs
periphemu = rm("periphemu"),
mounter = rm("mounter"),
config = rm("config"),
-- CCEmuX API
ccemux = rm("ccemux")
}
package.preload = {}
package.searchers = {
-- check package.preload
function(mod)
if package.preload[mod] then
return package.preload[mod]
else
return nil, "no field package.preload['" .. mod .. "']"
end
end,
-- check for lua library
function(mod)
local ok, err = package.searchpath(mod, package.path, ".", "/")
if not ok then
return nil, err
end
local func, loaderr = loadfile(ok)
if not func then
return nil, loaderr
end
return func()
end,
}
local fs = package.loaded.fs
-- require isn't here yet
local expect = loadfile("/rc/modules/main/cc/expect.lua")()
package.loaded["cc.expect"] = expect
function package.searchpath(name, path, sep, rep)
expect(1, name, "string")
expect(2, path, "string")
expect(3, sep, "string", "nil")
expect(4, rep, "string", "nil")
sep = "%" .. (sep or ".")
rep = rep or "/"
name = name:gsub(sep, rep)
local serr = ""
for search in path:gmatch("[^;]+") do
search = search:gsub("%?", name)
if fs.exists(search) then
return search
else
if #serr > 0 then
serr = serr .. "\n "
end
serr = serr .. "no file '" .. search .. "'"
end
end
return nil, serr
end
function _G.require(mod)
expect(1, mod, "string")
if package.loaded[mod] then
return package.loaded[mod]
end
local serr = "module '" .. mod .. "' not found:"
for _, searcher in ipairs(package.searchers) do
local result, err = searcher(mod)
if result then
package.loaded[mod] = result
return result
else
serr = serr .. "\n " .. err
end
end
error(serr, 2)
end

View File

@@ -0,0 +1,388 @@
-- some term things
-- e.g. redirects, read()
local rc = ...
-- we need a couple of these
local colors = require("colors")
local native = require("term")
local strings = require("cc.strings")
local expect = require("cc.expect").expect
local term = {}
package.loaded.term = term
local thread = require("rc.thread")
local valid = {
write = true,
scroll = true,
getCursorPos = true,
setCursorPos = true,
getCursorBlink = true,
setCursorBlink = true,
getSize = true,
clear = true,
clearLine = true,
getTextColor = true,
getTextColour = true,
setTextColor = true,
setTextColour = true,
getBackgroundColor = true,
getBackgroundColour = true,
setBackgroundColor = true,
setBackgroundColour = true,
isColor = true,
isColour = true,
blit = true,
setPaletteColor = true,
setPaletteColour = true,
getPaletteColor = true,
getPaletteColour = true,
-- LeonOS-PC graphics mode settings
setGraphicsMode = not not native.setGraphicsMode,
getGraphicsMode = not not native.getGraphicsMode,
drawPixels = not not native.drawPixels,
getPixels = not not native.getPixels,
setPixel = not not native.setPixel,
getPixel = not not native.getPixel
}
for k in pairs(valid) do
term[k] = function(...)
local redirect = thread.getTerm()
if not redirect[k] then
error("redirect object does not implement term."..k, 2)
end
return redirect[k](...)
end
end
function term.current()
return thread.getTerm()
end
function term.native()
return native
end
function term.redirect(obj)
expect(1, obj, "table")
return thread.setTerm(obj)
end
function term.at(x, y)
term.setCursorPos(x, y)
return term
end
local keys = require("keys")
-- rc.write
-- [[
function rc.write(text)
expect(1, text, "string")
local lines = 0
local w, h = term.getSize()
local x, y = term.getCursorPos()
local elements = strings.splitElements(text, w)
strings.wrappedWriteElements(elements, w, false, {
newline = function()
lines = lines + 1
x = 1
if y >= h then
term.scroll(1)
else
y = y + 1
end
term.at(x, y)
end,
append = function(newText)
term.at(x, y).write(newText)
x = x + #newText
end,
getX = function()
return x
end
})
return lines
end
--]]
-- old write() used for redrawing in read()
local function write(text)
expect(1, text, "string")
local lines = 0
local w, h = term.getSize()
local function inc_cy(cy)
lines = lines + 1
if cy > h - 1 then
term.scroll(1)
return cy
else
return cy + 1
end
end
while #text > 0 do
local nl = text:find("\n") or #text
local chunk = text:sub(1, nl)
text = text:sub(#chunk + 1)
local has_nl = chunk:sub(-1) == "\n"
if has_nl then chunk = chunk:sub(1, -2) end
local cx, cy = term.getCursorPos()
while #chunk > 0 do
if cx > w then
term.setCursorPos(1, inc_cy(cy))
cx, cy = term.getCursorPos()
end
local to_write = chunk:sub(1, w - cx + 1)
term.write(to_write)
chunk = chunk:sub(#to_write + 1)
cx, cy = term.getCursorPos()
end
if has_nl then
term.setCursorPos(1, inc_cy(cy))
end
end
return lines
end--]]
rc.write = write
-- read
local empty = {}
function term.read(replace, history, complete, default)
expect(1, replace, "string", "nil")
expect(2, history, "table", "nil")
expect(3, complete, "function", "nil")
expect(4, default, "string", "nil")
if replace then replace = replace:sub(1, 1) end
local hist = history or {}
history = {}
for i=1, #hist, 1 do
history[i] = hist[i]
end
local buffer = default or ""
local prev_buf = buffer
history[#history+1] = buffer
local hist_pos = #history
local cursor_pos = 0
local stx, sty = term.getCursorPos()
local w, h = term.getSize()
local dirty = false
local completions = {}
local comp_id = 0
local function clearCompletion()
if completions[comp_id] then
write((" "):rep(#completions[comp_id]))
end
end
local function full_redraw(force)
if force or dirty then
if complete and buffer ~= prev_buf then
completions = complete(buffer) or empty
comp_id = math.min(1, #completions)
end
prev_buf = buffer
term.setCursorPos(stx, sty)
local text = buffer
if replace then text = replace:rep(#text) end
local ln = write(text)
if completions[comp_id] then
local oldfg = term.getTextColor()
local oldbg = term.getBackgroundColor()
term.setTextColor(colors.white)
term.setBackgroundColor(colors.gray)
ln = ln + write(completions[comp_id])
term.setTextColor(oldfg)
term.setBackgroundColor(oldbg)
else
ln = ln + write(" ")
end
if sty + ln > h then
sty = sty - (sty + ln - h)
end
end
-- set cursor to the appropriate spot
local cx, cy = stx, sty
cx = cx + #buffer - cursor_pos-- + #(completions[comp_id] or "")
while cx > w do
cx = cx - w
cy = cy + 1
end
term.setCursorPos(cx, cy)
end
term.setCursorBlink(true)
while true do
full_redraw()
-- get input
local evt, id = rc.pullEvent()
if evt == "char" then
dirty = true
clearCompletion()
if cursor_pos == 0 then
buffer = buffer .. id
elseif cursor_pos == #buffer then
buffer = id .. buffer
else
buffer = buffer:sub(0, -cursor_pos - 1)..id..buffer:sub(-cursor_pos)
end
elseif evt == "paste" then
dirty = true
clearCompletion()
if cursor_pos == 0 then
buffer = buffer .. id
elseif cursor_pos == #buffer then
buffer = id .. buffer
else
buffer = buffer:sub(0, -cursor_pos - 1)..id..
buffer:sub(-cursor_pos+(#id-1))
end
elseif evt == "key" then
id = keys.getName(id)
if id == "backspace" and #buffer > 0 then
dirty = true
if cursor_pos == 0 then
buffer = buffer:sub(1, -2)
clearCompletion()
elseif cursor_pos < #buffer then
buffer = buffer:sub(0, -cursor_pos - 2)..buffer:sub(-cursor_pos)
end
elseif id == "delete" and cursor_pos > 0 then
dirty = true
if cursor_pos == #buffer then
buffer = buffer:sub(2)
elseif cursor_pos == 1 then
buffer = buffer:sub(1, -2)
else
buffer = buffer:sub(0, -cursor_pos - 1) .. buffer:sub(-cursor_pos + 1)
end
cursor_pos = cursor_pos - 1
elseif id == "up" then
if #completions > 1 then
dirty = true
clearCompletion()
if comp_id > 1 then
comp_id = comp_id - 1
else
comp_id = #completions
end
elseif hist_pos > 1 then
cursor_pos = 0
history[hist_pos] = buffer
hist_pos = hist_pos - 1
buffer = (" "):rep(#buffer)
full_redraw(true)
buffer = history[hist_pos]
dirty = true
end
elseif id == "down" then
if #completions > 1 then
dirty = true
clearCompletion()
if comp_id < #completions then
comp_id = comp_id + 1
else
comp_id = 1
end
elseif hist_pos < #history then
cursor_pos = 0
history[hist_pos] = buffer
hist_pos = hist_pos + 1
buffer = (" "):rep(#buffer)
full_redraw(true)
buffer = history[hist_pos]
dirty = true
end
elseif id == "left" then
if cursor_pos < #buffer then
clearCompletion()
cursor_pos = cursor_pos + 1
end
elseif id == "right" then
if cursor_pos > 0 then
cursor_pos = cursor_pos - 1
elseif comp_id > 0 then
dirty = true
buffer = buffer .. completions[comp_id]
end
elseif id == "tab" then
if comp_id > 0 then
dirty = true
buffer = buffer .. completions[comp_id]
end
elseif id == "home" then
cursor_pos = #buffer
elseif id == "end" then
cursor_pos = 0
elseif id == "enter" then
clearCompletion()
write("\n")
break
end
end
end
term.setCursorBlink(false)
return buffer
end
rc.read = term.read
setmetatable(term, {__index = native})

View File

@@ -0,0 +1,13 @@
_G.io = require("rc.io")
function _G.print(...)
local args = table.pack(...)
for i=1, args.n, 1 do
args[i] = tostring(args[i])
end
io.stdout:write(table.concat(args, "\t"), "\n")
return true
end

View File

@@ -0,0 +1,184 @@
-- rc.peripheral
local old = require("peripheral")
local expect = require("cc.expect").expect
local p = {}
package.loaded.peripheral = p
local sides = {"bottom", "top", "left", "right", "front", "back"}
function p.getNames()
local names = {}
for i=1, #sides, 1 do
local side = sides[i]
if old.isPresent(side) then
names[#names+1] = side
if old.hasType(side, "modem") and not old.call(side, "isWireless") then
local remote_names = old.call(side, "getNamesRemote")
for j=1, #remote_names, 1 do
names[#names+1] = remote_names[j]
end
end
end
end
return names
end
-- figure out where a peripheral is
-- returns 0 if the peripheral is directly connected,
-- and a side if it's connected through a modem
local function findByName(name)
if old.isPresent(name) then
return 0
else
for i=1, #sides, 1 do
local side = sides[i]
if old.hasType(side, "modem") and not old.call(side, "isWireless") then
if old.call(side, "isPresentRemote", name) then
return side
end
end
end
end
end
function p.isPresent(name)
expect(1, name, "string")
return not not findByName(name)
end
function p.getType(per)
expect(1, per, "string", "table")
if type(per) == "string" then
local place = findByName(per)
if place == 0 then
return old.getType(per)
elseif place then
return old.call(place, "getTypeRemote", per)
end
else
return table.unpack(per.__types)
end
end
function p.hasType(per, ptype)
expect(1, per, "string", "table")
expect(2, ptype, "string")
if type(per) == "string" then
local place = findByName(per)
if place == 0 then
return old.hasType(per, ptype)
elseif place then
return old.call(place, "hasTypeRemote", per, ptype)
end
else
return per.__types[ptype]
end
end
function p.getMethods(name)
expect(1, name, "string")
local place = findByName(name)
if place == 0 then
return old.getMethods(name)
elseif place then
return old.call(place, "getMethodsRemote", name)
end
end
function p.getName(per)
expect(1, per, "table")
return per.__info.name
end
function p.call(name, method, ...)
expect(1, name, "string")
expect(2, method, "string")
local place = findByName(name)
if place == 0 then
return old.call(name, method, ...)
elseif place then
return old.call(place, "callRemote", name, method, ...)
end
end
function p.wrap(name)
expect(1, name, "string")
local place = findByName(name)
if not place then return end
local methods, types
if place == 0 then
methods = old.getMethods(name)
types = table.pack(old.getType(name))
else
methods = old.call(place, "getMethodsRemote", name)
types = table.pack(old.call(place, "getTypesRemote", name))
end
for i=1, #types, 1 do
types[types[i]] = true
end
local wrapper = {
__info = {
name = name,
types = types,
}
}
if place == 0 then
for i=1, #methods, 1 do
wrapper[methods[i]] = function(...)
return old.call(name, methods[i], ...)
end
end
else
for i=1, #methods, 1 do
wrapper[methods[i]] = function(...)
return old.call(place, "callRemote", name, methods[i], ...)
end
end
end
return wrapper
end
function p.find(ptype, filter)
expect(1, ptype, "string")
expect(2, filter, "function", "nil")
local wrapped = {}
for _, name in ipairs(p.getNames()) do
if p.hasType(name, ptype) then
local wrapper = p.wrap(name)
if (p.filter and p.filter(name, wrapper)) or not p.filter then
wrapped[#wrapped+1] = wrapper
end
end
end
return table.unpack(wrapped)
end

View File

@@ -0,0 +1,168 @@
-- HTTP library adapted from .OS --
-- native http.request: function(
-- url:string[, post:string[, headers:table[, binarymode:boolean]]])
-- post is the data to POST. otherwise a GET is sent.
-- OR: function(parameters:table)
-- where parameters = {
-- url = string, -- the URL
-- body = string, -- the data to POST/PATCH/PUT
-- headers = table, -- request headers
-- binary = boolean, -- self explanatory
-- method = string} -- the HTTP method to use - one of:
-- - GET
-- - POST
-- - HEAD
-- - OPTIONS
-- - PUT
-- - DELETE
-- - PATCH
-- - TRACE
--
-- native http.checkURL: function(url:string)
-- url is a URL to try to reach. queues a http_check event with the result.
-- native http.websocket(url:string[, headers:table])
-- url is the url to which to open a websocket. queues a websocket_success
-- event on success, and websocket_failure on failure.
-- native http.addListener(port:number) (LeonOS-PC only)
-- add a listener on the specified port. when that port receives data,
-- the listener queues a http_request(port:number, request, response).
-- !!the response is not send until response.close() is called!!
-- native http.removeListener(port:number) (LeonOS-PC only)
-- remove the listener from that port
local rc = ...
if not package.loaded.http then
return
end
local old = package.loaded.http
local http = {}
package.loaded.http = http
local field = require("cc.expect").field
local expect = require("cc.expect").expect
local function listenForResponse(url)
while true do
local sig, a, b, c = rc.pullEvent()
if sig == "http_success" and a == url then
return b
elseif sig == "http_failure" and a == url then
return nil, b, c
end
end
end
function http.request(url, post, headers, binary, method, sync)
if type(url) ~= "table" then
url = {
url = url,
body = post,
headers = headers,
binary = binary,
method = method or (post and "POST") or "GET",
sync = not not sync
}
end
field(url, "url", "string")
field(url, "body", "string", "nil")
field(url, "headers", "table", "nil")
field(url, "binary", "boolean", "nil")
field(url, "method", "string")
field(url, "sync", "boolean", "nil")
local ok, err = old.request(url)
if not ok then
return nil, err
end
if sync then return listenForResponse(url.url) end
end
function http.get(url, headers, binary)
if type(url) == "table" then
url.sync = true
return http.request(url)
else
return http.request(url, nil, headers, binary, "GET", true)
end
end
function http.post(url, body, headers, binary)
if type(url) == "table" then
url.sync = true
url.method = "POST"
return http.request(url)
else
return http.request(url, body, headers, binary, "POST", true)
end
end
http.checkURLAsync = old.checkURL
function http.checkURL(url)
expect(1, url, "string")
local ok, err = old.checkURL(url)
if not ok then
return nil, err
end
local sig, _url, a, b
repeat
sig, _url, a, b = coroutine.yield()
until sig == "http_check" and url == _url
return a, b
end
http.websocketAsync = old.websocket
function http.websocket(url, headers)
expect(1, url, "string")
expect(2, headers, "string")
local ok, err = old.websocket(url, headers)
if not ok then
return nil, err
end
while true do
local sig, a, b, c = coroutine.yield()
if sig == "websocket_success" and a == url then
return b, c
elseif sig == "websocket_failure" and a == url then
return nil, b
end
end
end
if old.addListener then
function http.listen(port, callback)
expect(1, port, "number")
expect(2, callback, "function")
old.addListener(port)
while true do
local sig, a, b, c = coroutine.yield()
if sig == "stop_listener" and a == port then
old.removeListener(port)
break
elseif sig == "http_request" and a == port then
if not callback(b, c) then
old.removeListener(port)
break
end
end
end
end
else
function http.listen()
error("This functionality requires LeonOS-PC", 0)
end
end

View File

@@ -0,0 +1,36 @@
-- rc.commands
local expect = require("cc.expect").expect
if not package.loaded.commands then return end
local native = package.loaded.commands
local c = {}
package.loaded.commands = c
c.native = native
c.async = {}
for k, v in pairs(native) do c[k] = v end
local command_list = native.list()
for i=1, #command_list, 1 do
local command = command_list[i]
c.async[command] = function(...)
return c.execAsync(command, ...)
end
c[command] = c[command] or function(...)
return c.exec(command, ...)
end
end
function c.exec(command, ...)
expect(1, command, "string")
return c.native.exec(table.concat(table.pack(command, ...), " "))
end
function c.execAsync(command, ...)
expect(1, command, "string")
return c.native.execAsync(table.concat(table.pack(command, ...), " "))
end

View File

@@ -0,0 +1,101 @@
-- setting definitions
local settings = require("settings")
settings.define("list.show_hidden", {
description = "Show hidden files in list's output",
type = "boolean",
default = false
})
settings.define("bios.compat_mode", {
description = "Attempt some LeonOS compatibility by injecting APIs into _G.",
type = "boolean",
default = false
})
settings.define("shell.tracebacks", {
description = "Show error tracebacks in the shell.",
type = "boolean",
default = false
})
settings.define("edit.scroll_offset", {
description = "How many lines to keep between the cursor and the screen edge.",
type = "number",
default = 3
})
settings.define("edit.force_highlight", {
description = "Whether to use the highlighting editor, even on basic computers.",
type = "boolean",
default = false
})
settings.define("edit.scroll_factor", {
description = "Related to how many lines the editor should jump at a time when scrolling. Determined by term_height/scroll_factor. Adjust this for performance.",
type = "number",
default = 8
})
settings.define("edit.color_separator", {
description = "What color separating characters (e.g. ()[];{}) should be.",
type = "string",
default = "lightBlue"
})
settings.define("edit.color_operator", {
description = "What color operators (e.g. +-/*) should be.",
type = "string",
default = "lightGray"
})
settings.define("edit.color_keyword", {
description = "What color keywords (e.g. local, for, if) should be.",
type = "string",
default = "orange"
})
settings.define("edit.color_boolean", {
description = "What color booleans (true/false) should be.",
type = "string",
default = "purple"
})
settings.define("edit.color_comment", {
description = "What color comments should be.",
type = "string",
default = "gray"
})
settings.define("edit.color_global", {
description = "What color globals (e.g. print, require) should be.",
type = "string",
default = "lime"
})
settings.define("edit.color_string", {
description = "What color strings should be.",
type = "string",
default = "red"
})
settings.define("edit.color_number", {
description = "What color numbers (e.g. 2, 0xF3, 0.42) should be.",
type = "string",
default = "magenta"
})
settings.define("bios.restrict_globals", {
description = "Disallow global variables",
type = "boolean",
default = false
})
settings.define("bios.parallel_startup", {
description = "Run startup scripts from /startup in parallel",
type = "boolean",
default = false
})
settings.load()