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:
commit
76869593f0
@ -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")
|
||||
}
|
||||
|
@ -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
676
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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à",
|
||||
|
@ -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": "有線モデム",
|
||||
|
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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": "有线调制解调器",
|
||||
|
@ -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)),
|
||||
)
|
||||
}
|
||||
}
|
@ -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,
|
||||
|
@ -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).
|
||||
|
@ -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.
|
||||
|
@ -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
|
@ -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 }
|
@ -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
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
@ -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 (`.`),
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user