1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2025-10-31 21:52:59 +00:00

Merge branch 'mc-1.15.x' into mc-1.16.x

This commit is contained in:
Jonathan Coates
2021-06-19 12:41:30 +01:00
79 changed files with 1320 additions and 472 deletions

View File

@@ -27,21 +27,24 @@ function setPath(_sPath)
sPath = _sPath
end
local extensions = { "", ".md", ".txt" }
--- Returns the location of the help file for the given topic.
--
-- @tparam string topic The topic to find
-- @treturn string|nil The path to the given topic's help file, or `nil` if it
-- cannot be found.
-- @usage help.lookup("disk")
function lookup(_sTopic)
expect(1, _sTopic, "string")
function lookup(topic)
expect(1, topic, "string")
-- Look on the path variable
for sPath in string.gmatch(sPath, "[^:]+") do
sPath = fs.combine(sPath, _sTopic)
if fs.exists(sPath) and not fs.isDir(sPath) then
return sPath
elseif fs.exists(sPath .. ".txt") and not fs.isDir(sPath .. ".txt") then
return sPath .. ".txt"
for path in string.gmatch(sPath, "[^:]+") do
path = fs.combine(path, topic)
for _, extension in ipairs(extensions) do
local file = path .. extension
if fs.exists(file) and not fs.isDir(file) then
return file
end
end
end
@@ -66,8 +69,11 @@ function topics()
for _, sFile in pairs(tList) do
if string.sub(sFile, 1, 1) ~= "." then
if not fs.isDir(fs.combine(sPath, sFile)) then
if #sFile > 4 and sFile:sub(-4) == ".txt" then
sFile = sFile:sub(1, -5)
for i = 2, #extensions do
local extension = extensions[i]
if #sFile > #extension and sFile:sub(-#extension) == extension then
sFile = sFile:sub(1, -#extension - 1)
end
end
tItems[sFile] = true
end

View File

@@ -1,5 +1,5 @@
craft is a program for Crafty Turtles. Craft will craft a stack of items using the current inventory.
ex:
"craft" will craft as many items as possible
"craft all" will craft as many items as possible
"craft 5" will craft at most 5 times

View File

@@ -29,8 +29,8 @@ local completion = require "cc.completion"
--- Complete the name of a file relative to the current working directory.
--
-- @tparam table shell The shell we're completing in
-- @tparam { string... } choices The list of choices to complete from.
-- @tparam table shell The shell we're completing in.
-- @tparam string text Current text to complete.
-- @treturn { string... } A list of suffixes of matching files.
local function file(shell, text)
return fs.complete(text, shell.dir(), true, false)
@@ -38,8 +38,8 @@ end
--- Complete the name of a directory relative to the current working directory.
--
-- @tparam table shell The shell we're completing in
-- @tparam { string... } choices The list of choices to complete from.
-- @tparam table shell The shell we're completing in.
-- @tparam string text Current text to complete.
-- @treturn { string... } A list of suffixes of matching directories.
local function dir(shell, text)
return fs.complete(text, shell.dir(), false, true)
@@ -48,8 +48,8 @@ end
--- Complete the name of a file or directory relative to the current working
-- directory.
--
-- @tparam table shell The shell we're completing in
-- @tparam { string... } choices The list of choices to complete from.
-- @tparam table shell The shell we're completing in.
-- @tparam string text Current text to complete.
-- @tparam { string... } previous The shell arguments before this one.
-- @tparam[opt] boolean add_space Whether to add a space after the completed item.
-- @treturn { string... } A list of suffixes of matching files and directories.
@@ -74,14 +74,46 @@ end
--- Complete the name of a program.
--
-- @tparam table shell The shell we're completing in
-- @tparam { string... } choices The list of choices to complete from.
-- @tparam table shell The shell we're completing in.
-- @tparam string text Current text to complete.
-- @treturn { string... } A list of suffixes of matching programs.
-- @see shell.completeProgram
local function program(shell, text)
return shell.completeProgram(text)
end
--- Complete arguments of a program.
--
-- @tparam table shell The shell we're completing in.
-- @tparam string text Current text to complete.
-- @tparam { string... } previous The shell arguments before this one.
-- @tparam number starting Which argument index this program and args start at.
-- @treturn { string... } A list of suffixes of matching programs or arguments.
local function programWithArgs(shell, text, previous, starting)
if #previous + 1 == starting then
local tCompletionInfo = shell.getCompletionInfo()
if text:sub(-1) ~= "/" and tCompletionInfo[shell.resolveProgram(text)] then
return { " " }
else
local results = shell.completeProgram(text)
for n = 1, #results do
local sResult = results[n]
if sResult:sub(-1) ~= "/" and tCompletionInfo[shell.resolveProgram(text .. sResult)] then
results[n] = sResult .. " "
end
end
return results
end
else
local program = previous[starting]
local resolved = shell.resolveProgram(program)
if not resolved then return end
local tCompletion = shell.getCompletionInfo()[resolved]
if not tCompletion then return end
return tCompletion.fnComplete(shell, #previous - starting + 1, text, { program, table.unpack(previous, starting + 1, #previous) })
end
end
--[[- A helper function for building shell completion arguments.
This accepts a series of single-argument completion functions, and combines
@@ -144,6 +176,7 @@ return {
dir = dir,
dirOrFile = dirOrFile,
program = program,
programWithArgs = programWithArgs,
-- Re-export various other functions
help = wrap(help.completeTopic), --- Wraps @{help.completeTopic} as a @{build} compatible function.

View File

@@ -14,16 +14,127 @@ if sTopic == "index" then
end
local strings = require "cc.strings"
local function word_wrap(text, width)
local lines = strings.wrap(text, width)
local function min_of(a, b, default)
if not a and not b then return default end
if not a then return b end
if not b then return a end
return math.min(a, b)
end
--[[- Parse a markdown string, extracting headings and highlighting some basic
constructs.
The implementation of this is horrible. SquidDev shouldn't be allowed to write
parsers, especially ones they think might be "performance critical".
]]
local function parse_markdown(text)
local len = #text
local oob = len + 1
-- Some patterns to match headers and bullets on the start of lines.
-- The `%f[^\n\0]` is some wonderful logic to match the start of a line /or/
-- the start of the document.
local heading = "%f[^\n\0](#+ +)([^\n]*)"
local bullet = "%f[^\n\0]( *)[.*]( +)"
local code = "`([^`]+)`"
local new_text, fg, bg = "", "", ""
local function append(txt, fore, back)
new_text = new_text .. txt
fg = fg .. (fore or "0"):rep(#txt)
bg = bg .. (back or "f"):rep(#txt)
end
local next_header = text:find(heading)
local next_bullet = text:find(bullet)
local next_block = min_of(next_header, next_bullet, oob)
local next_code, next_code_end = text:find(code)
local sections = {}
local start = 1
while start <= len do
if start == next_block then
if start == next_header then
local _, fin, head, content = text:find(heading, start)
sections[#new_text + 1] = content
append(head .. content, "4", "f")
start = fin + 1
next_header = text:find(heading, start)
else
local _, fin, space, content = text:find(bullet, start)
append(space .. "\7" .. content)
start = fin + 1
next_bullet = text:find(bullet, start)
end
next_block = min_of(next_header, next_bullet, oob)
elseif next_code and next_code_end < next_block then
-- Basic inline code blocks
if start < next_code then append(text:sub(start, next_code - 1)) end
local content = text:match(code, next_code)
append(content, "0", "7")
start = next_code_end + 1
next_code, next_code_end = text:find(code, start)
else
-- Normal text
append(text:sub(start, next_block - 1))
start = next_block
-- Rescan for a new code block
if next_code then next_code, next_code_end = text:find(code, start) end
end
end
return new_text, fg, bg, sections
end
local function word_wrap_basic(text, width)
local lines, fg, bg = strings.wrap(text, width), {}, {}
local fg_line, bg_line = ("0"):rep(width), ("f"):rep(width)
-- Normalise the strings suitable for use with blit. We could skip this and
-- just use term.write, but saves us a clearLine call.
for k, line in pairs(lines) do
lines[k] = strings.ensure_width(line, width)
fg[k] = fg_line
bg[k] = bg_line
end
return lines
return lines, fg, bg, {}
end
local function word_wrap_markdown(text, width)
-- Add in styling for Markdown-formatted text.
local text, fg, bg, sections = parse_markdown(text)
local lines = strings.wrap(text, width)
local fglines, bglines, section_list, section_n = {}, {}, {}, 1
-- Normalise the strings suitable for use with blit. We could skip this and
-- just use term.write, but saves us a clearLine call.
local start = 1
for k, line in pairs(lines) do
-- I hate this with a burning passion, but it works!
local pos = text:find(line, start, true)
lines[k], fglines[k], bglines[k] =
strings.ensure_width(line, width),
strings.ensure_width(fg:sub(pos, pos + #line), width),
strings.ensure_width(bg:sub(pos, pos + #line), width)
if sections[pos] then
section_list[section_n], section_n = { content = sections[pos], offset = k - 1 }, section_n + 1
end
start = pos + 1
end
return lines, fglines, bglines, section_list
end
local sFile = help.lookup(sTopic)
@@ -33,31 +144,40 @@ if not file then
return
end
local contents = file:read("*a"):gsub("(\n *)[-*]( +)", "%1\7%2")
local contents = file:read("*a")
file:close()
local word_wrap = sFile:sub(-3) == ".md" and word_wrap_markdown or word_wrap_basic
local width, height = term.getSize()
local lines = word_wrap(contents, width)
local lines, fg, bg, sections = word_wrap(contents, width)
local print_height = #lines
-- If we fit within the screen, just display without pagination.
if print_height <= height then
print(contents)
local _, y = term.getCursorPos()
for i = 1, print_height do
if y + i - 1 > height then
term.scroll(1)
term.setCursorPos(1, height)
else
term.setCursorPos(1, y + i - 1)
end
term.blit(lines[i], fg[i], bg[i])
end
return
end
local current_section = nil
local offset = 0
local function draw()
local fg, bg = ("0"):rep(width), ("f"):rep(width)
for y = 1, height - 1 do
term.setCursorPos(1, y)
if y + offset > print_height then
-- Should only happen if we resize the terminal to a larger one
-- than actually needed for the current text.
term.clearLine()
else
term.blit(lines[y + offset], fg, bg)
--- Find the currently visible seciton, or nil if this document has no sections.
--
-- This could potentially be a binary search, but right now it's not worth it.
local function find_section()
for i = #sections, 1, -1 do
if sections[i].offset <= offset then
return i
end
end
end
@@ -68,7 +188,10 @@ local function draw_menu()
term.clearLine()
local tag = "Help: " .. sTopic
term.write("Help: " .. sTopic)
if current_section then
tag = tag .. (" (%s)"):format(sections[current_section].content)
end
term.write(tag)
if width >= #tag + 16 then
term.setCursorPos(width - 14, height)
@@ -76,11 +199,31 @@ local function draw_menu()
end
end
local function draw()
for y = 1, height - 1 do
term.setCursorPos(1, y)
if y + offset > print_height then
-- Should only happen if we resize the terminal to a larger one
-- than actually needed for the current text.
term.clearLine()
else
term.blit(lines[y + offset], fg[y + offset], bg[y + offset])
end
end
local new_section = find_section()
if new_section ~= current_section then
current_section = new_section
draw_menu()
end
end
draw()
draw_menu()
while true do
local event, param = os.pullEvent()
local event, param = os.pullEventRaw()
if event == "key" then
if param == keys.up and offset > 0 then
offset = offset - 1
@@ -97,6 +240,12 @@ while true do
elseif param == keys.home then
offset = 0
draw()
elseif param == keys.left and current_section and current_section > 1 then
offset = sections[current_section - 1].offset
draw()
elseif param == keys.right and current_section and current_section < #sections then
offset = sections[current_section + 1].offset
draw()
elseif param == keys["end"] then
offset = print_height - height
draw()
@@ -124,6 +273,8 @@ while true do
offset = math.max(math.min(offset, print_height - height), 0)
draw()
draw_menu()
elseif event == "terminate" then
break
end
end

View File

@@ -51,7 +51,7 @@ local function get(sUrl)
local sResponse = response.readAll()
response.close()
return sResponse
return sResponse or ""
end
if run then
@@ -79,7 +79,12 @@ else
local res = get(url)
if not res then return end
local file = fs.open(sPath, "wb")
local file, err = fs.open(sPath, "wb")
if not file then
printError("Cannot save file: " .. err)
return
end
file.write(res)
file.close()

View File

@@ -7,7 +7,7 @@ local function printUsage()
end
local tArgs = { ... }
if #tArgs < 2 then
if #tArgs < 2 or tArgs[1] == "scale" and #tArgs < 3 then
printUsage()
return
end
@@ -21,7 +21,7 @@ if tArgs[1] == "scale" then
local nRes = tonumber(tArgs[3])
if nRes == nil or nRes < 0.5 or nRes > 5 then
print("Invalid scale: " .. nRes)
print("Invalid scale: " .. tArgs[3])
return
end

View File

@@ -9,20 +9,19 @@ if not turtle.craft then
end
local tArgs = { ... }
local nLimit = nil
if #tArgs < 1 then
local nLimit = tonumber(tArgs[1])
if not nLimit and tArgs[1] ~= "all" then
local programName = arg[0] or fs.getName(shell.getRunningProgram())
print("Usage: " .. programName .. " [number]")
print("Usage: " .. programName .. " all|<number>")
return
else
nLimit = tonumber(tArgs[1])
end
local nCrafted = 0
local nOldCount = turtle.getItemCount(turtle.getSelectedSlot())
if turtle.craft(nLimit) then
local nNewCount = turtle.getItemCount(turtle.getSelectedSlot())
if nOldCount <= nLimit then
if not nLimit or nOldCount <= nLimit then
nCrafted = nNewCount
else
nCrafted = nOldCount - nNewCount

View File

@@ -81,9 +81,17 @@ shell.setCompletionFunction("rom/programs/monitor.lua", completion.build(
if previous[2] == "scale" then
return completion.peripheral(shell, text, previous, true)
else
return completion.program(shell, text, previous)
return completion.programWithArgs(shell, text, previous, 3)
end
end
end,
{
function(shell, text, previous)
if previous[2] ~= "scale" then
return completion.programWithArgs(shell, text, previous, 3)
end
end,
many = true,
}
))
shell.setCompletionFunction("rom/programs/move.lua", completion.build(
@@ -98,11 +106,11 @@ shell.setCompletionFunction("rom/programs/rename.lua", completion.build(
{ completion.dirOrFile, true },
completion.dirOrFile
))
shell.setCompletionFunction("rom/programs/shell.lua", completion.build(completion.program))
shell.setCompletionFunction("rom/programs/shell.lua", completion.build({ completion.programWithArgs, 2, many = true }))
shell.setCompletionFunction("rom/programs/type.lua", completion.build(completion.dirOrFile))
shell.setCompletionFunction("rom/programs/set.lua", completion.build({ completion.setting, true }))
shell.setCompletionFunction("rom/programs/advanced/bg.lua", completion.build(completion.program))
shell.setCompletionFunction("rom/programs/advanced/fg.lua", completion.build(completion.program))
shell.setCompletionFunction("rom/programs/advanced/bg.lua", completion.build({ completion.programWithArgs, 2, many = true }))
shell.setCompletionFunction("rom/programs/advanced/fg.lua", completion.build({ completion.programWithArgs, 2, many = true }))
shell.setCompletionFunction("rom/programs/fun/dj.lua", completion.build(
{ completion.choice, { "play", "play ", "stop " } },
completion.peripheral