1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2025-08-25 23:12:18 +00:00

Merge branch 'mc-1.21.x' into mc-1.21.y

This commit is contained in:
Jonathan Coates 2025-06-28 11:31:23 +01:00
commit 76869593f0
No known key found for this signature in database
GPG Key ID: B9E431FF07C98D06
24 changed files with 925 additions and 717 deletions

View File

@ -68,5 +68,5 @@ tasks.ideaSyncTask {
tasks.named("checkDependencyConsistency", DependencyCheck::class.java) {
val libs = project.extensions.getByType<VersionCatalogsExtension>().named("libs")
// Minecraft depends on asm, but Fabric forces it to a more recent version
override(libs.findLibrary("asm").get(), "9.7.1")
override(libs.findLibrary("asm").get(), "9.8")
}

View File

@ -12,7 +12,7 @@ neogradle.subsystems.conventions.runs.enabled=false
# Mod properties
isUnstable=true
modVersion=1.115.1
modVersion=1.116.0
# Minecraft properties: We want to configure this here so we can read it in settings.gradle
mcVersion=1.21.6

676
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -13,10 +13,10 @@
},
"devDependencies": {
"@rollup/plugin-node-resolve": "^16.0.0",
"@rollup/plugin-typescript": "^12.0.0",
"@rollup/plugin-typescript": "^12.0.0 && <12.1.3",
"@rollup/plugin-url": "^8.0.1",
"@swc/core": "^1.3.92",
"@types/node": "^22.0.0",
"@types/node": "^24.0.0",
"lightningcss": "^1.22.0",
"preact-render-to-string": "^6.2.1",
"rehype": "^13.0.0",

View File

@ -205,8 +205,10 @@
"item.computercraft.treasure_disk": "Disketa",
"itemGroup.computercraft": "ComputerCraft",
"tag.item.computercraft.computer": "Počítače",
"tag.item.computercraft.disks": "Disky",
"tag.item.computercraft.dyeable": "Obarvitelné předměty",
"tag.item.computercraft.monitor": "Monitory",
"tag.item.computercraft.pocket_computers": "Kapesní počítače",
"tag.item.computercraft.turtle": "Roboti",
"tag.item.computercraft.turtle_can_place": "Roboty-umístitelné předměty",
"tag.item.computercraft.wired_modem": "Drátové modemy",

View File

@ -177,6 +177,7 @@
"item.computercraft.treasure_disk": "Diskette",
"itemGroup.computercraft": "ComputerCraft",
"tag.item.computercraft.computer": "Computer",
"tag.item.computercraft.dyeable": "Färbbare Elemente",
"tag.item.computercraft.monitor": "Monitore",
"tag.item.computercraft.turtle": "Turtles",
"tag.item.computercraft.wired_modem": "Verkabelte Modems",

View File

@ -205,8 +205,10 @@
"item.computercraft.treasure_disk": "Disco Floppy",
"itemGroup.computercraft": "ComputerCraft",
"tag.item.computercraft.computer": "Computer",
"tag.item.computercraft.dyeable": "Oggetti colorabili",
"tag.item.computercraft.monitor": "Monitor",
"tag.item.computercraft.turtle": "Tartarughe",
"tag.item.computercraft.turtle_can_place": "Oggetti piazzabili da tartarughe",
"tag.item.computercraft.wired_modem": "Modem cablati",
"tracking_field.computercraft.avg": "%s (media)",
"tracking_field.computercraft.computer_tasks.name": "Attività",

View File

@ -205,8 +205,10 @@
"item.computercraft.treasure_disk": "フロッピーディスク",
"itemGroup.computercraft": "ComputerCraft",
"tag.item.computercraft.computer": "コンピューター",
"tag.item.computercraft.disks": "ディスク",
"tag.item.computercraft.dyeable": "染色可能なアイテム",
"tag.item.computercraft.monitor": "モニター",
"tag.item.computercraft.pocket_computers": "ポケットコンピューター",
"tag.item.computercraft.turtle": "タートル",
"tag.item.computercraft.turtle_can_place": "タートルが設置可能なアイテム",
"tag.item.computercraft.wired_modem": "有線モデム",

View File

@ -92,6 +92,7 @@
"item.computercraft.printed_pages": "Wydrukowane Strony",
"item.computercraft.treasure_disk": "Dyskietka",
"itemGroup.computercraft": "ComputerCraft",
"tag.item.computercraft.turtle_can_place": "Przedmioty stawialne przez Żółwia",
"upgrade.computercraft.speaker.adjective": "Hałaśliwy",
"upgrade.computercraft.wireless_modem_advanced.adjective": "Enderowy",
"upgrade.computercraft.wireless_modem_normal.adjective": "Bezprzewodowy",

View File

@ -205,8 +205,10 @@
"item.computercraft.treasure_disk": "Disket",
"itemGroup.computercraft": "ComputerCraft",
"tag.item.computercraft.computer": "Bilgisayarlar",
"tag.item.computercraft.disks": "Diskler",
"tag.item.computercraft.dyeable": "Boyanabilir eşyalar",
"tag.item.computercraft.monitor": "Monitörler",
"tag.item.computercraft.pocket_computers": "Cep Bilgisayarları",
"tag.item.computercraft.turtle": "Turtlelar",
"tag.item.computercraft.turtle_can_place": "Kaplumbağa-yerleştirilebilir eşyalar",
"tag.item.computercraft.wired_modem": "Kablolu modemler",

View File

@ -205,8 +205,10 @@
"item.computercraft.treasure_disk": "软盘",
"itemGroup.computercraft": "ComputerCraft",
"tag.item.computercraft.computer": "计算机",
"tag.item.computercraft.disks": "磁盘",
"tag.item.computercraft.dyeable": "可染色物品",
"tag.item.computercraft.monitor": "监视器",
"tag.item.computercraft.pocket_computers": "便携式计算机",
"tag.item.computercraft.turtle": "海龟",
"tag.item.computercraft.turtle_can_place": "可放置海龟物品",
"tag.item.computercraft.wired_modem": "有线调制解调器",

View File

@ -0,0 +1,37 @@
// SPDX-FileCopyrightText: 2025 The CC: Tweaked Developers
//
// SPDX-License-Identifier: MPL-2.0
package dan200.computercraft.gametest
import dan200.computercraft.core.util.Colour
import dan200.computercraft.gametest.api.GameTest
import dan200.computercraft.gametest.api.craftItem
import dan200.computercraft.gametest.api.immediate
import dan200.computercraft.shared.ModRegistry
import dan200.computercraft.shared.util.DataComponentUtil
import dan200.computercraft.test.shared.ItemStackMatcher.isStack
import net.minecraft.gametest.framework.GameTestHelper
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Items
import org.hamcrest.MatcherAssert.assertThat
class Disk_Test {
/**
* Ensure disks
*/
@GameTest(template = "default")
fun Can_craft_disk(helper: GameTestHelper) = helper.immediate {
assertThat(
"Disk without dye",
helper.craftItem(ItemStack(Items.REDSTONE), ItemStack(Items.PAPER)),
isStack(DataComponentUtil.createDyedStack(ModRegistry.Items.DISK.get(), Colour.BLUE.hex)),
)
assertThat(
"Disk with dye",
helper.craftItem(ItemStack(Items.REDSTONE), ItemStack(Items.PAPER), ItemStack(Items.GREEN_DYE)),
isStack(DataComponentUtil.createDyedStack(ModRegistry.Items.DISK.get(), Colour.GREEN.hex)),
)
}
}

View File

@ -116,6 +116,7 @@ object TestHooks {
Computer_Test::class.java,
CraftOs_Test::class.java,
Details_Test::class.java,
Disk_Test::class.java,
Disk_Drive_Test::class.java,
Inventory_Test::class.java,
Loot_Test::class.java,

View File

@ -1,3 +1,23 @@
# New features in CC: Tweaked 1.116.0
* Add `turtle.getEquippedLeft()` and `turtle.getEquippedRight()`.
* Add item tags for floppy disks and pocket computers.
* Support multi-line strings and comments in `edit`.
* Computer and pocket computer terminal sizes can be set with the `computercraft:terminal_size` component.
* Border and sidebar textures now use vanilla's nine-sliced format.
Several bug fixes:
* Ignore shader compilation errors when running with Pojav.
* Fix several issues with character input.
* Fix pocket computer dyes being lost when equipping/unequipping upgrades.
* Fix superflous warnings from allocation tracking.
* Fix `__lt`/`__le` not working on heterogeneous types.
* Many documentation fixes (Lemmmy, matematikaadit, McJack12).
* Fix `0` being treated as a valid colour in `window` and `colour.toBlit`.
* Fix out-of-bounds when pasting too lon text.
* Fix syntax highlighting of string escapes (LorneHyde).
* Fix sidebar texture of advanced computers being offset.
# New features in CC: Tweaked 1.115.1
* Update various translations (cyb3r, kevk2156, teamer337, yakku).

View File

@ -1,11 +1,21 @@
New features in CC: Tweaked 1.115.1
New features in CC: Tweaked 1.116.0
* Update various translations (cyb3r, kevk2156, teamer337, yakku).
* Support Fabric's item lookup API for registering media providers.
* Add `turtle.getEquippedLeft()` and `turtle.getEquippedRight()`.
* Add item tags for floppy disks and pocket computers.
* Support multi-line strings and comments in `edit`.
* Computer and pocket computer terminal sizes can be set with the `computercraft:terminal_size` component.
* Border and sidebar textures now use vanilla's nine-sliced format.
Several bug fixes:
* Fix crashes on Create 6.0 (ellellie).
* Fix `speaker.playAudio` not updating speaker volume.
* Resize pocket lectern textures to fix issues with generating mipmaps.
* Ignore shader compilation errors when running with Pojav.
* Fix several issues with character input.
* Fix pocket computer dyes being lost when equipping/unequipping upgrades.
* Fix superflous warnings from allocation tracking.
* Fix `__lt`/`__le` not working on heterogeneous types.
* Many documentation fixes (Lemmmy, matematikaadit, McJack12).
* Fix `0` being treated as a valid colour in `window` and `colour.toBlit`.
* Fix out-of-bounds when pasting too lon text.
* Fix syntax highlighting of string escapes (LorneHyde).
* Fix sidebar texture of advanced computers being offset.
Type "help changelog" to see the full version history.

View File

@ -0,0 +1,58 @@
-- SPDX-FileCopyrightText: 2025 The CC: Tweaked Developers
--
-- SPDX-License-Identifier: MPL-2.0
--[[- Launches a program, reports any errors, and prompts the user to close the
tab.
> [!DANGER]
> This is an internal module and SHOULD NOT be used in your own code. It may
> be removed or changed at any time.
This is used by the `edit` program to launch the running code.
@tparam string title The title of the multishell tab.
@tparam string path The path to the file.
@tparam string contents The contents of the file.
@local
]]
return function(title, path, contents)
multishell.setTitle(multishell.getCurrent(), title)
local current = term.current()
local fn, err = load(contents, path, nil, _ENV)
if fn then
local exception = require "cc.internal.exception"
local ok, err, co = exception.try(fn)
term.redirect(current)
term.setTextColor(term.isColour() and colours.yellow or colours.white)
term.setBackgroundColor(colours.black)
term.setCursorBlink(false)
if not ok then
printError(err)
exception.report(err, co, { [path] = contents })
end
else
local parser = require "cc.internal.syntax"
if parser.parse_program(contents) then printError(err) end
end
local message = "Press any key to continue."
local _, y = term.getCursorPos()
local w, h = term.getSize()
local wrapped = require("cc.strings").wrap(message, w)
term.setTextColor(colours.white)
term.setBackgroundColor(colours.black)
local start_y = h - #wrapped + 1
if y >= start_y then term.scroll(y - start_y + 1) end
for i = 1, #wrapped do
term.setCursorPos(1, start_y + i - 1)
term.write(wrapped[i])
end
os.pullEvent('key')
require "cc.internal.event".discard_char()
end

View File

@ -0,0 +1,125 @@
-- SPDX-FileCopyrightText: 2017 Daniel Ratcliffe
--
-- SPDX-License-Identifier: LicenseRef-CCPL
--[[- A simple menu bar.
> [!DANGER]
> This is an internal module and SHOULD NOT be used in your own code. It may
> be removed or changed at any time.
This provides a shared implementation of the menu bar used by the `edit` and
`paint` programs. This draws a menu bar at the bottom of the string, with a list
of options.
@local
]]
local expect = require "cc.expect".expect
--[[- Create a new menu bar.
This should be called every time the menu is displayed.
@tparam { string... } items The menu items to display.
@return The menu.
]]
local function create(items)
expect(1, items, "table")
return {
items = items,
selected = 1,
}
end
--[[- Draw the menu bar at the bottom of the screen.
This should be called when first displaying the menu, and if the whole screen is
redrawn (e.g. after a [`term_resize`]).
@param menu The menu bar to draw.
]]
local function draw(menu)
expect(1, menu, "table")
local _, height = term.getSize()
term.setCursorPos(1, height)
term.clearLine()
local active_colour = term.isColour() and colours.yellow or colours.white
term.setTextColour(colours.white)
for k, v in pairs(menu.items) do
if menu.selected == k then
term.setTextColour(active_colour)
term.write("[")
term.setTextColour(colours.white)
term.write(v)
term.setTextColour(active_colour)
term.write("]")
term.setTextColour(colours.white)
else
term.write(" " .. v .. " ")
end
end
end
--[[- Process an event.
@param menu The menu bar to update.
@tparam string The event name.
@param ... Additional arguments to the event.
@treturn nil|boolean|string Either:
- If no action was taken, return `nil`.
- If the menu was closed, return `false`.
- If an item was selected, return the item as a string.
]]
local function handle_event(menu, event, ...)
expect(1, menu, "table")
if event == "key" then
local key = ...
if key == keys.right then
-- Move right
menu.selected = menu.selected + 1
if menu.selected > #menu.items then menu.selected = 1 end
draw(menu)
elseif key == keys.left and menu.selected > 1 then
-- Move left
menu.selected = menu.selected - 1
if menu.selected < 1 then menu.selected = #menu.items end
draw(menu)
elseif key == keys.enter or key == keys.numPadEnter then
-- Select an option
return menu.items[menu.selected]
elseif key == keys.leftCtrl or keys == keys.rightCtrl or keys == keys.rightAlt then
-- Cancel the menu
return false
end
elseif event == "char" then
-- Select menu items
local char = (...):lower()
for _, item in pairs(menu.items) do
if item:sub(1, 1):lower() == char then return item end
end
elseif event == "mouse_click" then
local _, x, y = ...
local _, height = term.getSize()
if y ~= height then return false end -- Exit the menu
local item_start = 1
for _, item in ipairs(menu.items) do
local item_end = item_start + #item + 2
if x >= item_start and x < item_end then return item end
item_start = item_end
end
end
return nil
end
return { create = create, draw = draw, handle_event = handle_event }

View File

@ -101,16 +101,21 @@ local function lex_number(context, str, start)
return tokens.NUMBER, pos - 1
end
--- Lex a quoted string.
--
-- @param context The current parser context.
-- @tparam string str The string we're lexing.
-- @tparam number start_pos The start position of the string.
-- @tparam string quote The quote character, either " or '.
-- @treturn number The token id for strings.
-- @treturn number The new position.
local function lex_string(context, str, start_pos, quote)
local pos = start_pos + 1
local lex_string_zap
--[[- Lex a quoted string.
@param context The current parser context.
@tparam string str The string we're lexing.
@tparam number pos The position to start lexing from.
@tparam number start_pos The actual start position of the string.
@tparam string quote The quote character, either " or '.
@treturn number The token id for strings.
@treturn number The new position.
@treturn nil A placeholder value.
@treturn table|nil The continuation function when the string is not finished.
]]
local function lex_string(context, str, pos, start_pos, quote)
while true do
local c = sub(str, pos, pos)
if c == quote then
@ -125,24 +130,9 @@ local function lex_string(context, str, start_pos, quote)
pos = newline(context, str, pos + 1, c)
elseif c == "" then
context.report(errors.unfinished_string_escape, start_pos, pos, quote)
return tokens.STRING, pos
return tokens.STRING, pos, nil, { lex_string, 1, 1, quote }
elseif c == "z" then
pos = pos + 2
while true do
local next_pos, _, c = find(str, "([%S\r\n])", pos)
if not next_pos then
context.report(errors.unfinished_string, start_pos, #str, quote)
return tokens.STRING, #str
end
if c == "\n" or c == "\r" then
pos = newline(context, str, next_pos, c)
else
pos = next_pos
break
end
end
return lex_string_zap(context, str, pos + 2, start_pos, quote)
else
pos = pos + 2
end
@ -152,6 +142,39 @@ local function lex_string(context, str, start_pos, quote)
end
end
--[[- Lex the remainder of a zap escape sequence (`\z`). This consumes all leading
whitespace, and then continues lexing the string.
@param context The current parser context.
@tparam string str The string we're lexing.
@tparam number pos The position to start lexing from.
@tparam number start_pos The actual start position of the string.
@tparam string quote The quote character, either " or '.
@treturn number The token id for strings.
@treturn number The new position.
@treturn nil A placeholder value.
@treturn table|nil The continuation function when the string is not finished.
]]
lex_string_zap = function(context, str, pos, start_pos, quote)
while true do
local next_pos, _, c = find(str, "([%S\r\n])", pos)
if not next_pos then
context.report(errors.unfinished_string, start_pos, #str, quote)
return tokens.STRING, #str, nil, { lex_string_zap, 1, 1, quote }
end
if c == "\n" or c == "\r" then
pos = newline(context, str, next_pos, c)
else
pos = next_pos
break
end
end
return lex_string(context, str, pos, start_pos, quote)
end
--- Consume the start or end of a long string.
-- @tparam string str The input string.
-- @tparam number pos The start position. This must be after the first `[` or `]`.
@ -205,6 +228,45 @@ local function lex_long_str(context, str, start, len)
end
end
--[[- Lex the remainder of a long string.
@param context The current parser context.
@tparam string str The string we're lexing.
@tparam number pos The position to start lexing from.
@tparam number start_pos The actual start position of the string.
@tparam number boundary_length The length of the boundary.
@treturn number The token id for strings.
@treturn number The new position.
@treturn nil A placeholder value.
@treturn table|nil The continuation function when the string is not finished.
]]
local function lex_long_string(context, str, pos, start_pos, boundary_length)
local end_pos = lex_long_str(context, str, pos, boundary_length)
if end_pos then return tokens.STRING, end_pos end
context.report(errors.unfinished_long_string, start_pos, pos - 1, boundary_length)
return tokens.STRING, #str, nil, { lex_long_string, 0, 0, boundary_length }
end
--[[- Lex the remainder of a long comment.
@param context The current parser context.
@tparam string str The comment we're lexing.
@tparam number pos The position to start lexing from.
@tparam number start_pos The actual start position of the comment.
@tparam number boundary_length The length of the boundary.
@treturn number The token id for comments.
@treturn number The new position.
@treturn nil A placeholder value.
@treturn table|nil The continuation function when the comment is not finished.
]]
local function lex_long_comment(context, str, pos, start_pos, boundary_length)
local end_pos = lex_long_str(context, str, pos, boundary_length)
if end_pos then return tokens.COMMENT, end_pos end
context.report(errors.unfinished_long_comment, start_pos, pos - 1, boundary_length)
return tokens.COMMENT, #str, nil, { lex_long_comment, 0, 0, boundary_length }
end
--- Lex a single token, assuming we have removed all leading whitespace.
--
@ -229,16 +291,12 @@ local function lex_token(context, str, pos)
elseif c >= "0" and c <= "9" then return lex_number(context, str, pos)
-- Strings
elseif c == "\"" or c == "\'" then return lex_string(context, str, pos, c)
elseif c == "\"" or c == "\'" then return lex_string(context, str, pos + 1, pos, c)
elseif c == "[" then
local ok, boundary_pos = lex_long_str_boundary(str, pos + 1, "[")
if ok then -- Long string
local end_pos = lex_long_str(context, str, boundary_pos + 1, boundary_pos - pos)
if end_pos then return tokens.STRING, end_pos end
context.report(errors.unfinished_long_string, pos, boundary_pos, boundary_pos - pos)
return tokens.STRING, #str
return lex_long_string(context, str, boundary_pos + 1, pos, boundary_pos - pos)
elseif pos + 1 == boundary_pos then -- Just a "["
return tokens.OSQUARE, pos
else -- Malformed long string, for instance "[="
@ -256,11 +314,7 @@ local function lex_token(context, str, pos)
if sub(str, comment_pos, comment_pos) == "[" then
local ok, boundary_pos = lex_long_str_boundary(str, comment_pos + 1, "[")
if ok then
local end_pos = lex_long_str(context, str, boundary_pos + 1, boundary_pos - comment_pos)
if end_pos then return tokens.COMMENT, end_pos end
context.report(errors.unfinished_long_comment, pos, boundary_pos, boundary_pos - comment_pos)
return tokens.COMMENT, #str
return lex_long_comment(context, str, boundary_pos + 1, pos, boundary_pos - comment_pos)
end
end
@ -357,8 +411,8 @@ local function lex_one(context, str, pos)
elseif c == "\r" or c == "\n" then
pos = newline(context, str, start_pos, c)
else
local token_id, end_pos, content = lex_token(context, str, start_pos)
return token_id, start_pos, end_pos, content
local token_id, end_pos, content, continue = lex_token(context, str, start_pos)
return token_id, start_pos, end_pos, content, continue
end
end
end

View File

@ -30,81 +30,40 @@ local x, y = 1, 1
local w, h = term.getSize()
local scrollX, scrollY = 0, 0
local tLines = {}
local tLines, tLineLexStates = {}, {}
local bRunning = true
-- Colours
local highlightColour, keywordColour, commentColour, textColour, bgColour, stringColour, errorColour
if term.isColour() then
local isColour = term.isColour()
local highlightColour, keywordColour, textColour, bgColour, errorColour
if isColour then
bgColour = colours.black
textColour = colours.white
highlightColour = colours.yellow
keywordColour = colours.yellow
commentColour = colours.green
stringColour = colours.red
errorColour = colours.red
else
bgColour = colours.black
textColour = colours.white
highlightColour = colours.white
keywordColour = colours.white
commentColour = colours.white
stringColour = colours.white
errorColour = colours.white
end
local runHandler = [[multishell.setTitle(multishell.getCurrent(), %q)
local current = term.current()
local contents, name = %q, %q
local fn, err = load(contents, name, nil, _ENV)
if fn then
local exception = require "cc.internal.exception"
local ok, err, co = exception.try(fn, ...)
term.redirect(current)
term.setTextColor(term.isColour() and colours.yellow or colours.white)
term.setBackgroundColor(colours.black)
term.setCursorBlink(false)
if not ok then
printError(err)
exception.report(err, co, { [name] = contents })
end
else
local parser = require "cc.internal.syntax"
if parser.parse_program(contents) then printError(err) end
end
local message = "Press any key to continue."
if ok then message = "Program finished. " .. message end
local _, y = term.getCursorPos()
local w, h = term.getSize()
local wrapped = require("cc.strings").wrap(message, w)
local start_y = h - #wrapped + 1
if y >= start_y then term.scroll(y - start_y + 1) end
for i = 1, #wrapped do
term.setCursorPos(1, start_y + i - 1)
term.write(wrapped[i])
end
os.pullEvent('key')
require "cc.internal.event".discard_char()
]]
-- Menus
local bMenu = false
local nMenuItem = 1
local tMenuItems = {}
local menu = require "cc.internal.menu"
local current_menu
local menu_items = {}
if not bReadOnly then
table.insert(tMenuItems, "Save")
table.insert(menu_items, "Save")
end
if shell.openTab then
table.insert(tMenuItems, "Run")
table.insert(menu_items, "Run")
end
if peripheral.find("printer") then
table.insert(tMenuItems, "Print")
table.insert(menu_items, "Print")
end
table.insert(tMenuItems, "Exit")
table.insert(menu_items, "Exit")
local status_ok, status_text
local function set_status(text, ok)
@ -138,6 +97,7 @@ local function load(_sPath)
local sLine = file:read()
while sLine do
table.insert(tLines, sLine)
table.insert(tLineLexStates, false)
sLine = file:read()
end
file:close()
@ -145,6 +105,7 @@ local function load(_sPath)
if #tLines == 0 then
table.insert(tLines, "")
table.insert(tLineLexStates, false)
end
end
@ -180,8 +141,9 @@ local tokens = require "cc.internal.syntax.parser".tokens
local lex_one = require "cc.internal.syntax.lexer".lex_one
local token_colours = {
[tokens.STRING] = stringColour,
[tokens.COMMENT] = commentColour,
[tokens.STRING] = isColour and colours.red or textColour,
[tokens.COMMENT] = isColour and colours.green or colours.lightGrey,
[tokens.NUMBER] = isColour and colours.magenta or textColour,
-- Keywords
[tokens.AND] = keywordColour,
[tokens.BREAK] = keywordColour,
@ -213,26 +175,6 @@ end
local lex_context = { line = function() end, report = function() end }
local function writeHighlighted(line)
local pos, colour = 1, nil
while true do
local token, _, finish = lex_one(lex_context, line, pos)
if not token then break end
local new_colour = token_colours[token]
if new_colour ~= colour then
term.setTextColor(new_colour)
colour = new_colour
end
term.write(line:sub(pos, finish))
pos = finish + 1
end
term.write(line:sub(pos))
end
local tCompletions
local nCompletion
@ -252,7 +194,7 @@ end
local function recomplete()
local sLine = tLines[y]
if not bMenu and not bReadOnly and x == #sLine + 1 then
if not bReadOnly and x == #sLine + 1 then
tCompletions = complete(sLine)
if tCompletions and #tCompletions > 0 then
nCompletion = 1
@ -276,34 +218,94 @@ local function writeCompletion(sLine)
end
end
local function redrawText()
local cursorX, cursorY = x, y
for y = 1, h - 1 do
term.setCursorPos(1 - scrollX, y)
--- Check if two values are equal. If both values are lists, then the contents will be
-- checked for equality, to a depth of 1.
--
-- @param x The first value.
-- @param x The second value.
-- @treturn boolean Whether the values are equal.
local function shallowEqual(x, y)
if x == y then return true end
if type(x) ~= "table" or type(y) ~= "table" then return false end
if #x ~= #y then return false end
for i = 1, #x do if x[i] ~= y[i] then return false end end
return true
end
local function redrawLines(line, endLine)
if not endLine then endLine = line end
local colour = term.getTextColour()
-- Highlight all lines between line and endLine, highlighting further lines if their
-- lexer state has changed and aborting at the end of the screen.
local changed = false
while (changed or line <= endLine) and line - scrollY < h do
term.setCursorPos(1 - scrollX, line - scrollY)
term.clearLine()
local sLine = tLines[y + scrollY]
if sLine ~= nil then
writeHighlighted(sLine)
if cursorY == y and cursorX == #sLine + 1 then
writeCompletion()
local contents = tLines[line]
if not contents then break end
-- Lex our first token, either taking our continuation state (if present) or
-- the default lexer.
local pos, token, _, finish, continuation = 1
local lex_state = tLineLexStates[line]
if lex_state then
token, finish, _, continuation = lex_state[1](lex_context, contents, table.unpack(lex_state, 2))
else
token, _, finish, _, continuation = lex_one(lex_context, contents, 1)
end
while token do
-- Print out that token
local new_colour = token_colours[token]
if new_colour ~= colour then
term.setTextColor(new_colour)
colour = new_colour
end
term.write(contents:sub(pos, finish))
pos = finish + 1
-- If we have a continuation, then we've reached the end of the line. Abort.
if continuation then break end
-- Otherwise lex another token and continue.
token, _, finish, _, continuation = lex_one(lex_context, contents, pos)
end
-- Print the rest of the line. We don't strictly speaking need this, as it will
-- only ever contain whitespace.
term.write(contents:sub(pos))
if line == y and x == #contents + 1 then
writeCompletion()
colour = term.getTextColour()
end
line = line + 1
-- Update the lext state of the next line. If that has changed, then
-- re-highlight it too. We store the continuation as nil rather than
-- false, to ensure we use the array part of the table.
if continuation == nil then continuation = false end
if tLineLexStates[line] ~= nil and not shallowEqual(tLineLexStates[line], continuation) then
tLineLexStates[line] = continuation or false
changed = true
else
changed = false
end
end
term.setTextColor(colours.white)
term.setCursorPos(x - scrollX, y - scrollY)
end
local function redrawLine(_nY)
local sLine = tLines[_nY]
if sLine then
term.setCursorPos(1 - scrollX, _nY - scrollY)
term.clearLine()
writeHighlighted(sLine)
if _nY == y and x == #sLine + 1 then
writeCompletion()
end
term.setCursorPos(x - scrollX, _nY - scrollY)
end
local function redrawText()
redrawLines(scrollY + 1, scrollY + h - 1)
end
local function redrawMenu()
@ -319,22 +321,9 @@ local function redrawMenu()
term.write(y)
term.setCursorPos(1, h)
if bMenu then
if current_menu then
-- Draw menu
term.setTextColour(textColour)
for nItem, sItem in pairs(tMenuItems) do
if nItem == nMenuItem then
term.setTextColour(highlightColour)
term.write("[")
term.setTextColour(textColour)
term.write(sItem)
term.setTextColour(highlightColour)
term.write("]")
term.setTextColour(textColour)
else
term.write(" " .. sItem .. " ")
end
end
menu.draw(current_menu)
else
-- Draw status
term.setTextColour(status_ok and highlightColour or errorColour)
@ -344,6 +333,7 @@ local function redrawMenu()
-- Reset cursor
term.setCursorPos(x - scrollX, y - scrollY)
term.setCursorBlink(not current_menu)
end
local tMenuFuncs = {
@ -421,7 +411,8 @@ local tMenuFuncs = {
end
end
bMenu = false
local old_menu = current_menu
current_menu = nil
term.redirect(printerTerminal)
local ok, error = pcall(function()
term.scroll()
@ -439,7 +430,7 @@ local tMenuFuncs = {
redrawMenu()
sleep(0.5)
end
bMenu = true
current_menu = old_menu
if nPage > 1 then
set_status("Printed " .. nPage .. " Pages")
@ -462,7 +453,8 @@ local tMenuFuncs = {
return
end
local ok = save(sTempPath, function(file)
file.write(runHandler:format(sTitle, table.concat(tLines, "\n"), "@/" .. sPath))
local runHandler = [[return require("cc.internal.edit_runner")(%q, %q, %q)]]
file.write(runHandler:format(sTitle, "@/" .. sPath, table.concat(tLines, "\n")))
end)
if ok then
local nTask = shell.openTab("/" .. sTempPath)
@ -479,15 +471,6 @@ local tMenuFuncs = {
end,
}
local function doMenuItem(_n)
tMenuFuncs[tMenuItems[_n]]()
if bMenu then
bMenu = false
term.setCursorBlink(true)
end
redrawMenu()
end
local function setCursor(newX, newY)
local _, oldY = x, y
x, y = newX, newY
@ -519,12 +502,10 @@ local function setCursor(newX, newY)
if bRedraw then
redrawText()
elseif y ~= oldY then
redrawLine(oldY)
redrawLine(y)
redrawLines(math.min(y, oldY), math.max(y, oldY))
else
redrawLine(y)
redrawLines(y)
end
term.setCursorPos(screenX, screenY)
redrawMenu()
end
@ -550,20 +531,36 @@ local function acceptCompletion()
end
end
local function handleMenuEvent(event)
assert(current_menu)
local result = menu.handle_event(current_menu, table.unpack(event, 1, event.n))
if result == false then
current_menu = nil
redrawMenu()
elseif result ~= nil then
tMenuFuncs[result]()
current_menu = nil
redrawMenu()
end
end
-- Handle input
while bRunning do
local sEvent, param, param2, param3 = os.pullEvent()
if sEvent == "key" then
if param == keys.up then
-- Up
if not bMenu then
local event = table.pack(os.pullEvent())
if event[1] == "key" then
if current_menu then
handleMenuEvent(event)
else
local key = event[2]
if key == keys.up then
if nCompletion then
-- Cycle completions
nCompletion = nCompletion - 1
if nCompletion < 1 then
nCompletion = #tCompletions
end
redrawLine(y)
redrawLines(y)
elseif y > 1 then
-- Move cursor up
@ -572,19 +569,15 @@ while bRunning do
y - 1
)
end
end
elseif param == keys.down then
-- Down
if not bMenu then
-- Move cursor down
elseif key == keys.down then
if nCompletion then
-- Cycle completions
nCompletion = nCompletion + 1
if nCompletion > #tCompletions then
nCompletion = 1
end
redrawLine(y)
redrawLines(y)
elseif y < #tLines then
-- Move cursor down
@ -593,11 +586,8 @@ while bRunning do
y + 1
)
end
end
elseif param == keys.tab then
-- Tab
if not bMenu and not bReadOnly then
elseif key == keys.tab and not bReadOnly then
if nCompletion and x == #tLines[y] + 1 then
-- Accept autocomplete
acceptCompletion()
@ -607,11 +597,8 @@ while bRunning do
tLines[y] = string.sub(sLine, 1, x - 1) .. " " .. string.sub(sLine, x)
setCursor(x + 4, y)
end
end
elseif param == keys.pageUp then
-- Page Up
if not bMenu then
elseif key == keys.pageUp then
-- Move up a page
local newY
if y - (h - 1) >= 1 then
@ -623,11 +610,7 @@ while bRunning do
math.min(x, #tLines[newY] + 1),
newY
)
end
elseif param == keys.pageDown then
-- Page Down
if not bMenu then
elseif key == keys.pageDown then
-- Move down a page
local newY
if y + (h - 1) <= #tLines then
@ -637,48 +620,29 @@ while bRunning do
end
local newX = math.min(x, #tLines[newY] + 1)
setCursor(newX, newY)
end
elseif param == keys.home then
-- Home
if not bMenu then
elseif key == keys.home then
-- Move cursor to the beginning
if x > 1 then
setCursor(1, y)
end
end
elseif param == keys["end"] then
-- End
if not bMenu then
elseif key == keys["end"] then
-- Move cursor to the end
local nLimit = #tLines[y] + 1
if x < nLimit then
setCursor(nLimit, y)
end
end
elseif param == keys.left then
-- Left
if not bMenu then
elseif key == keys.left then
if x > 1 then
-- Move cursor left
setCursor(x - 1, y)
elseif x == 1 and y > 1 then
setCursor(#tLines[y - 1] + 1, y - 1)
end
else
-- Move menu left
nMenuItem = nMenuItem - 1
if nMenuItem < 1 then
nMenuItem = #tMenuItems
end
redrawMenu()
end
elseif param == keys.right then
-- Right
if not bMenu then
elseif key == keys.right then
local nLimit = #tLines[y] + 1
if x < nLimit then
-- Move cursor right
@ -690,35 +654,23 @@ while bRunning do
-- Go to next line
setCursor(1, y + 1)
end
else
-- Move menu right
nMenuItem = nMenuItem + 1
if nMenuItem > #tMenuItems then
nMenuItem = 1
end
redrawMenu()
end
elseif param == keys.delete then
-- Delete
if not bMenu and not bReadOnly then
elseif key == keys.delete and not bReadOnly then
local nLimit = #tLines[y] + 1
if x < nLimit then
local sLine = tLines[y]
tLines[y] = string.sub(sLine, 1, x - 1) .. string.sub(sLine, x + 1)
recomplete()
redrawLine(y)
redrawLines(y)
elseif y < #tLines then
tLines[y] = tLines[y] .. tLines[y + 1]
table.remove(tLines, y + 1)
table.remove(tLineLexStates, y + 1)
recomplete()
redrawText()
end
end
elseif param == keys.backspace then
-- Backspace
if not bMenu and not bReadOnly then
elseif key == keys.backspace and not bReadOnly then
if x > 1 then
-- Remove character
local sLine = tLines[y]
@ -734,14 +686,12 @@ while bRunning do
local sPrevLen = #tLines[y - 1]
tLines[y - 1] = tLines[y - 1] .. tLines[y]
table.remove(tLines, y)
table.remove(tLineLexStates, y)
setCursor(sPrevLen + 1, y - 1)
redrawText()
end
end
elseif param == keys.enter or param == keys.numPadEnter then
-- Enter/Numpad Enter
if not bMenu and not bReadOnly then
elseif (key == keys.enter or key == keys.numPadEnter) and not bReadOnly then
-- Newline
local sLine = tLines[y]
local _, spaces = string.find(sLine, "^[ ]+")
@ -750,99 +700,60 @@ while bRunning do
end
tLines[y] = string.sub(sLine, 1, x - 1)
table.insert(tLines, y + 1, string.rep(' ', spaces) .. string.sub(sLine, x))
table.insert(tLineLexStates, y + 1, false)
setCursor(spaces + 1, y + 1)
redrawText()
elseif bMenu then
-- Menu selection
doMenuItem(nMenuItem)
end
elseif param == keys.leftCtrl or param == keys.rightCtrl then
-- Menu toggle
bMenu = not bMenu
if bMenu then
term.setCursorBlink(false)
else
term.setCursorBlink(true)
end
redrawMenu()
elseif param == keys.rightAlt then
if bMenu then
bMenu = false
term.setCursorBlink(true)
elseif key == keys.leftCtrl or key == keys.rightCtrl then
current_menu = menu.create(menu_items)
redrawMenu()
end
end
elseif sEvent == "char" then
if not bMenu and not bReadOnly then
elseif event[1] == "char" then
if current_menu then
handleMenuEvent(event)
elseif not bReadOnly then
-- Input text
local sLine = tLines[y]
tLines[y] = string.sub(sLine, 1, x - 1) .. param .. string.sub(sLine, x)
tLines[y] = string.sub(sLine, 1, x - 1) .. event[2] .. string.sub(sLine, x)
setCursor(x + 1, y)
elseif bMenu then
-- Select menu items
for n, sMenuItem in ipairs(tMenuItems) do
if string.lower(string.sub(sMenuItem, 1, 1)) == string.lower(param) then
doMenuItem(n)
break
end
end
end
elseif sEvent == "paste" then
if not bReadOnly then
-- Close menu if open
if bMenu then
bMenu = false
term.setCursorBlink(true)
redrawMenu()
end
-- Input text
local sLine = tLines[y]
tLines[y] = string.sub(sLine, 1, x - 1) .. param .. string.sub(sLine, x)
setCursor(x + #param, y)
elseif event[1] == "paste" and not bReadOnly then
-- Close menu if open
if current_menu then
current_menu = nil
redrawMenu()
end
elseif sEvent == "mouse_click" then
local cx, cy = param2, param3
if not bMenu then
if param == 1 then
-- Input text
local text = event[2]
local sLine = tLines[y]
tLines[y] = string.sub(sLine, 1, x - 1) .. text .. string.sub(sLine, x)
setCursor(x + #text, y)
elseif event[1] == "mouse_click" then
local button, cx, cy = event[2], event[3], event[4]
if current_menu then
handleMenuEvent(event)
else
if button == 1 then
-- Left click
if cy < h then
local newY = math.min(math.max(scrollY + cy, 1), #tLines)
local newX = math.min(math.max(scrollX + cx, 1), #tLines[newY] + 1)
setCursor(newX, newY)
else
bMenu = true
current_menu = menu.create(menu_items)
redrawMenu()
end
end
else
if cy == h then
local nMenuPosEnd = 1
local nMenuPosStart = 1
for n, sMenuItem in ipairs(tMenuItems) do
nMenuPosEnd = nMenuPosEnd + #sMenuItem + 1
if cx > nMenuPosStart and cx < nMenuPosEnd then
doMenuItem(n)
end
nMenuPosEnd = nMenuPosEnd + 1
nMenuPosStart = nMenuPosEnd
end
else
bMenu = false
term.setCursorBlink(true)
redrawMenu()
end
end
elseif sEvent == "mouse_scroll" then
if not bMenu then
if param == -1 then
elseif event[1] == "mouse_scroll" then
if not current_menu then
local direction = event[2]
if direction == -1 then
-- Scroll up
if scrollY > 0 then
-- Move cursor up
@ -850,7 +761,7 @@ while bRunning do
redrawText()
end
elseif param == 1 then
elseif direction == 1 then
-- Scroll down
local nMaxScroll = #tLines - (h - 1)
if scrollY < nMaxScroll then
@ -862,7 +773,7 @@ while bRunning do
end
end
elseif sEvent == "term_resize" then
elseif event[1] == "term_resize" then
w, h = term.getSize()
setCursor(x, y)
redrawMenu()

View File

@ -20,6 +20,7 @@ local canvasColour = colours.black
local canvas = {}
-- The menu options
local menu = require "cc.internal.menu"
local mChoices = { "Save", "Exit" }
-- The message displayed in the footer bar
@ -299,10 +300,7 @@ local menu_choices = {
end
return false
end,
Exit = function()
require "cc.internal.event".discard_char() -- Consume stray "char" events from pressing Ctrl then E separately.
return true
end,
Exit = function() return true end,
}
--[[
@ -310,80 +308,25 @@ local menu_choices = {
returns: true if the program is to be exited; false otherwise
]]
local function accessMenu()
-- Selected menu option
local selection = 1
local current_menu = menu.create(mChoices)
term.setBackgroundColour(colours.black)
menu.draw(current_menu)
while true do
-- Draw the menu
term.setCursorPos(1, h)
term.clearLine()
term.setTextColour(colours.white)
for k, v in pairs(mChoices) do
if selection == k then
term.setTextColour(colours.yellow)
term.write("[")
term.setTextColour(colours.white)
term.write(v)
term.setTextColour(colours.yellow)
term.write("]")
term.setTextColour(colours.white)
else
term.write(" " .. v .. " ")
end
-- Handle input in the menu
local event = table.pack(os.pullEvent())
local result = menu.handle_event(current_menu, table.unpack(event, 1, event.n))
if result == false then
return false
elseif result ~= nil then
return menu_choices[result]()
end
-- Handle input in the menu
local id, param1, param2, param3 = os.pullEvent()
if id == "key" then
local key = param1
-- Handle menu shortcuts.
for _, menu_item in ipairs(mChoices) do
local k = keys[menu_item:sub(1, 1):lower()]
if k and k == key then
return menu_choices[menu_item]()
end
end
if key == keys.right then
-- Move right
selection = selection + 1
if selection > #mChoices then
selection = 1
end
elseif key == keys.left and selection > 1 then
-- Move left
selection = selection - 1
if selection < 1 then
selection = #mChoices
end
elseif key == keys.enter or key == keys.numPadEnter then
-- Select an option
return menu_choices[mChoices[selection]]()
elseif key == keys.leftCtrl or keys == keys.rightCtrl then
-- Cancel the menu
return false
end
elseif id == "mouse_click" then
local cx, cy = param2, param3
if cy ~= h then return false end -- Exit the menu
local nMenuPosEnd = 1
local nMenuPosStart = 1
for _, sMenuItem in ipairs(mChoices) do
nMenuPosEnd = nMenuPosEnd + #sMenuItem + 1
if cx > nMenuPosStart and cx < nMenuPosEnd then
return menu_choices[sMenuItem]()
end
nMenuPosEnd = nMenuPosEnd + 1
nMenuPosStart = nMenuPosEnd
end
elseif id == "term_resize" then
if event[1] == "term_resize" then
termResize()
menu.draw(current_menu)
end
end
end

View File

@ -300,7 +300,7 @@ function shell.setDir(dir)
sDir = fs.combine(dir, "")
end
--- Set the path where programs are located.
--- Get the path where programs are located.
--
-- The path is composed of a list of directory names in a string, each separated
-- by a colon (`:`). On normal turtles will look in the current directory (`.`),

View File

@ -70,18 +70,33 @@ We expected a closing delimiter (]=]) somewhere after this comment was started.
1:1-1:5 COMMENT --[=[
```
But incomplete opening `[`s are treated as a line comment:
```lua
--[
--[=
return
```
```txt
1:1-1:3 COMMENT --[
2:1-2:4 COMMENT --[=
3:1-3:6 RETURN return
```
Nested comments are rejected, just as Lua 5.1 does:
```lua
--[[ [[ ]]
--[[ [[ ]] return
```
```txt
[[ cannot be nested inside another [[ ... ]]
|
1 | --[[ [[ ]]
1 | --[[ [[ ]] return
| ^^
1:1-1:10 COMMENT --[[ [[ ]]
1:12-1:17 RETURN return
```
# Strings

View File

@ -56,7 +56,7 @@ import { type DataExport, WithExport } from "./components/WithExport";
for (const file of await fs.readdir(sourceDir, { withFileTypes: true, recursive: true })) {
if(!file.isFile() || !file.name.endsWith(".html")) continue;
const sourcePath = path.join(file.path, file.name);
const sourcePath = path.join(file.parentPath, file.name);
const contents = await fs.readFile(sourcePath, "utf-8");
const { result } = await processor.process(contents);