mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2025-02-08 15:10:05 +00:00
Pull in CC:T 1.108.1, some other fixes
- Use Cobalt's new Java patcher, allowing us to use Java 17 syntax. As such, update a couple classes to make use of that. - Pull in latest ROM. This is very noisy (due to the link syntax changes), but mostly trivial changes. - Fix wget and pastebin programs using http methods which don't exist. - Make fs.open create the parent directory when opening for write, much like newer CC versions.
This commit is contained in:
parent
35e227ed02
commit
136fbd2589
@ -1,27 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
plugins {
|
||||
application
|
||||
alias(libs.plugins.kotlin)
|
||||
}
|
||||
|
||||
java {
|
||||
toolchain {
|
||||
languageVersion.set(JavaLanguageVersion.of(17))
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(libs.bundles.asm)
|
||||
implementation(libs.bundles.kotlin)
|
||||
}
|
||||
|
||||
tasks.jar {
|
||||
manifest.attributes("Main-Class" to "cc.tweaked.build.MainKt")
|
||||
}
|
@ -1,61 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package cc.tweaked.build
|
||||
|
||||
import org.objectweb.asm.ClassReader
|
||||
import org.objectweb.asm.ClassVisitor
|
||||
import org.objectweb.asm.ClassWriter
|
||||
import org.objectweb.asm.util.CheckClassAdapter
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
|
||||
/** Generate additional classes which don't exist in the original source set. */
|
||||
interface ClassEmitter {
|
||||
/** Emit a class if it does not already exist. */
|
||||
fun generate(name: String, classReader: ClassReader? = null, flags: Int = 0, write: (ClassVisitor) -> Unit)
|
||||
}
|
||||
|
||||
/** An implementation of [ClassEmitter] which writes files to a directory. */
|
||||
class FileClassEmitter(private val outputDir: Path) : ClassEmitter {
|
||||
private val emitted = mutableSetOf<String>()
|
||||
override fun generate(name: String, classReader: ClassReader?, flags: Int, write: (ClassVisitor) -> Unit) {
|
||||
if (!emitted.add(name)) return
|
||||
|
||||
val cw = NonLoadingClassWriter(classReader, flags)
|
||||
write(CheckClassAdapter(cw))
|
||||
|
||||
val outputFile = outputDir.resolve("$name.class")
|
||||
Files.createDirectories(outputFile.parent)
|
||||
Files.write(outputFile, cw.toByteArray())
|
||||
}
|
||||
}
|
||||
|
||||
/** A unordered pair, such that (x, y) = (y, x) */
|
||||
private class UnorderedPair<T>(private val x: T, private val y: T) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other !is cc.tweaked.build.UnorderedPair<*>) return false
|
||||
return (x == other.x && y == other.y) || (x == other.y && y == other.x)
|
||||
}
|
||||
|
||||
override fun hashCode(): Int = x.hashCode() xor y.hashCode()
|
||||
override fun toString(): String = "UnorderedPair($x, $y)"
|
||||
}
|
||||
|
||||
private val subclassRelations = mapOf<UnorderedPair<String>, String>(
|
||||
)
|
||||
|
||||
/** A [ClassWriter] extension which avoids loading classes when computing frames. */
|
||||
private class NonLoadingClassWriter(reader: ClassReader?, flags: Int) : ClassWriter(reader, flags) {
|
||||
override fun getCommonSuperClass(type1: String, type2: String): String {
|
||||
if (type1 == "java/lang/Object" || type2 == "java/lang/Object") return "java/lang/Object"
|
||||
|
||||
val subclass = subclassRelations[UnorderedPair(type1, type2)]
|
||||
if (subclass != null) return subclass
|
||||
|
||||
println("[WARN] Guessing the super-class of $type1 and $type2.")
|
||||
return "java/lang/Object"
|
||||
}
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package cc.tweaked.build
|
||||
|
||||
import org.objectweb.asm.ClassReader
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Paths
|
||||
import kotlin.io.path.extension
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
if (args.size != 2) {
|
||||
System.err.println("Expected: INPUT OUTPUT")
|
||||
exitProcess(1)
|
||||
}
|
||||
|
||||
val inputDir = Paths.get(args[0])
|
||||
val outputDir = Paths.get(args[1])
|
||||
|
||||
val emitter = FileClassEmitter(outputDir)
|
||||
Files.find(inputDir, Int.MAX_VALUE, { path, _ -> path.extension == "class" }).use { files ->
|
||||
files.forEach { inputFile ->
|
||||
val reader = Files.newInputStream(inputFile).use { ClassReader(it) }
|
||||
emitter.generate(reader.className, flags = 0) { cw -> reader.accept(Unlambda(emitter, cw), 0) }
|
||||
}
|
||||
}
|
||||
}
|
@ -1,210 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package cc.tweaked.build
|
||||
|
||||
import org.objectweb.asm.*
|
||||
import org.objectweb.asm.Opcodes.*
|
||||
|
||||
class Unlambda(private val emitter: ClassEmitter, visitor: ClassVisitor) :
|
||||
ClassVisitor(ASM9, visitor) {
|
||||
internal lateinit var className: String
|
||||
private var isInterface: Boolean = false
|
||||
|
||||
private var lambda = 0
|
||||
|
||||
override fun visit(version: Int, access: Int, name: String, signature: String?, superName: String, interfaces: Array<out String>?) {
|
||||
super.visit(V1_6, access, name, signature, superName, interfaces)
|
||||
|
||||
if (version != V1_8) throw IllegalStateException("Expected Java version 8")
|
||||
className = name
|
||||
isInterface = (access and ACC_INTERFACE) != 0
|
||||
}
|
||||
|
||||
override fun visitMethod(access: Int, name: String, descriptor: String, signature: String?, exceptions: Array<out String>?): MethodVisitor? {
|
||||
val access = if (access.and(ACC_STATIC) != 0) access.and(ACC_PRIVATE.inv()) else access
|
||||
val mw = super.visitMethod(access, name, descriptor, signature, exceptions) ?: return null
|
||||
|
||||
if (isInterface && name != "<clinit>") {
|
||||
if ((access and ACC_STATIC) != 0) println("[WARN] $className.$name is a static method")
|
||||
else if ((access and ACC_ABSTRACT) == 0) println("[WARN] $className.$name is a default method")
|
||||
}
|
||||
|
||||
return UnlambdaMethodVisitor(this, emitter, mw)
|
||||
}
|
||||
|
||||
internal fun nextLambdaName(): String {
|
||||
val name = "lambda$lambda"
|
||||
lambda++
|
||||
return name
|
||||
}
|
||||
}
|
||||
|
||||
internal class UnlambdaMethodVisitor(
|
||||
private val parent: Unlambda,
|
||||
private val emitter: ClassEmitter,
|
||||
methodVisitor: MethodVisitor,
|
||||
) : MethodVisitor(ASM9, methodVisitor) {
|
||||
private class Bridge(val lambda: Handle, val bridgeName: String)
|
||||
|
||||
private val bridgeMethods = mutableListOf<Bridge>()
|
||||
|
||||
override fun visitMethodInsn(opcode: Int, owner: String, name: String, descriptor: String, isInterface: Boolean) {
|
||||
if (opcode == INVOKESTATIC && isInterface) println("[WARN] Invoke interface $owner.$name in ${parent.className}")
|
||||
super.visitMethodInsn(opcode, owner, name, descriptor, isInterface)
|
||||
}
|
||||
|
||||
override fun visitInvokeDynamicInsn(name: String, descriptor: String, handle: Handle, vararg arguments: Any) {
|
||||
if (handle.owner == "java/lang/invoke/LambdaMetafactory" && handle.name == "metafactory" && handle.desc == "(Ljava/lang/invoke/MethodHandles\$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;") {
|
||||
visitLambda(name, descriptor, arguments[0] as Type, arguments[1] as Handle)
|
||||
} else {
|
||||
super.visitInvokeDynamicInsn(name, descriptor, handle, *arguments)
|
||||
}
|
||||
}
|
||||
|
||||
private fun visitLambda(name: String, descriptor: String, signature: Type, lambda: Handle) {
|
||||
val interfaceTy = Type.getReturnType(descriptor)
|
||||
val fields = Type.getArgumentTypes(descriptor)
|
||||
|
||||
val lambdaName = parent.nextLambdaName()
|
||||
val className = "${parent.className}\$$lambdaName"
|
||||
val bridgeName = "${lambdaName}Bridge"
|
||||
|
||||
emitter.generate(className, flags = ClassWriter.COMPUTE_MAXS) { cw ->
|
||||
cw.visit(V1_6, ACC_FINAL, className, null, "java/lang/Object", arrayOf(interfaceTy.internalName))
|
||||
for ((i, ty) in fields.withIndex()) {
|
||||
cw.visitField(ACC_PRIVATE or ACC_FINAL, "field$i", ty.descriptor, null, null)
|
||||
.visitEnd()
|
||||
}
|
||||
|
||||
cw.visitMethod(ACC_STATIC, "create", Type.getMethodDescriptor(interfaceTy, *fields), null, null).let { mw ->
|
||||
mw.visitCode()
|
||||
mw.visitTypeInsn(NEW, className)
|
||||
mw.visitInsn(DUP)
|
||||
for ((i, ty) in fields.withIndex()) mw.visitVarInsn(ty.getOpcode(ILOAD), i)
|
||||
mw.visitMethodInsn(INVOKESPECIAL, className, "<init>", Type.getMethodDescriptor(Type.VOID_TYPE, *fields), false)
|
||||
mw.visitInsn(ARETURN)
|
||||
mw.visitMaxs(0, 0)
|
||||
mw.visitEnd()
|
||||
}
|
||||
|
||||
cw.visitMethod(0, "<init>", Type.getMethodDescriptor(Type.VOID_TYPE, *fields), null, null).let { mw ->
|
||||
mw.visitCode()
|
||||
mw.visitVarInsn(ALOAD, 0)
|
||||
mw.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false)
|
||||
for ((i, ty) in fields.withIndex()) {
|
||||
mw.visitVarInsn(ALOAD, 0)
|
||||
mw.visitVarInsn(ty.getOpcode(ILOAD), i + 1)
|
||||
mw.visitFieldInsn(PUTFIELD, className, "field$i", ty.descriptor)
|
||||
}
|
||||
mw.visitInsn(RETURN)
|
||||
mw.visitMaxs(0, 0)
|
||||
mw.visitEnd()
|
||||
}
|
||||
|
||||
cw.visitMethod(ACC_PUBLIC, name, signature.descriptor, null, null).let { mw ->
|
||||
mw.visitCode()
|
||||
|
||||
val targetArgs = when (lambda.tag) {
|
||||
H_INVOKEVIRTUAL, H_INVOKESPECIAL -> arrayOf(
|
||||
Type.getObjectType(lambda.owner),
|
||||
*Type.getArgumentTypes(lambda.desc),
|
||||
)
|
||||
|
||||
H_INVOKESTATIC, H_NEWINVOKESPECIAL -> Type.getArgumentTypes(lambda.desc)
|
||||
else -> throw IllegalStateException("Unhandled opcode")
|
||||
}
|
||||
var targetArgOffset = 0
|
||||
|
||||
// If we're a ::new method handle, create the object.
|
||||
if (lambda.tag == H_NEWINVOKESPECIAL) {
|
||||
mw.visitTypeInsn(NEW, lambda.owner)
|
||||
mw.visitInsn(DUP)
|
||||
}
|
||||
|
||||
// Load our fields
|
||||
for ((i, ty) in fields.withIndex()) {
|
||||
mw.visitVarInsn(ALOAD, 0)
|
||||
mw.visitFieldInsn(GETFIELD, className, "field$i", ty.descriptor)
|
||||
|
||||
val expectedTy = targetArgs[targetArgOffset]
|
||||
if (ty != expectedTy) println("$ty != $expectedTy")
|
||||
targetArgOffset++
|
||||
}
|
||||
|
||||
// Load the additional arguments
|
||||
val arguments = signature.argumentTypes
|
||||
for ((i, ty) in arguments.withIndex()) {
|
||||
mw.visitVarInsn(ty.getOpcode(ILOAD), i + 1)
|
||||
val expectedTy = targetArgs[targetArgOffset]
|
||||
if (ty != expectedTy) {
|
||||
println("[WARN] $ty != $expectedTy, adding a cast")
|
||||
mw.visitTypeInsn(CHECKCAST, expectedTy.internalName)
|
||||
}
|
||||
targetArgOffset++
|
||||
}
|
||||
|
||||
// Invoke our init call
|
||||
mw.visitMethodInsn(
|
||||
when (lambda.tag) {
|
||||
H_INVOKEVIRTUAL, H_INVOKESPECIAL -> INVOKEVIRTUAL
|
||||
H_INVOKESTATIC -> INVOKESTATIC
|
||||
H_NEWINVOKESPECIAL -> INVOKESPECIAL
|
||||
else -> throw IllegalStateException("Unhandled opcode")
|
||||
},
|
||||
lambda.owner, if (lambda.tag == H_INVOKESPECIAL) bridgeName else lambda.name, lambda.desc, false,
|
||||
)
|
||||
|
||||
|
||||
if (lambda.tag != H_NEWINVOKESPECIAL) {
|
||||
val expectedRetTy = signature.returnType
|
||||
val retTy = Type.getReturnType(lambda.desc)
|
||||
if (expectedRetTy != retTy) {
|
||||
// println("[WARN] $retTy != $expectedRetTy, adding a cast")
|
||||
if (retTy == Type.INT_TYPE && expectedRetTy.descriptor == "Ljava/lang/Object;") {
|
||||
mw.visitMethodInsn(INVOKESTATIC, "jav/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;", false)
|
||||
} else {
|
||||
// println("[ERROR] Unhandled")
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// A little ugly special handling for ::new
|
||||
mw.visitInsn(
|
||||
if (lambda.tag == H_NEWINVOKESPECIAL) ARETURN else signature.returnType.getOpcode(IRETURN),
|
||||
)
|
||||
mw.visitMaxs(0, 0)
|
||||
mw.visitEnd()
|
||||
}
|
||||
|
||||
cw.visitEnd()
|
||||
}
|
||||
|
||||
// If we're a ::new method handle, create the object.
|
||||
if (lambda.tag == H_INVOKESPECIAL) {
|
||||
bridgeMethods.add(Bridge(lambda, bridgeName))
|
||||
}
|
||||
|
||||
visitMethodInsn(INVOKESTATIC, className, "create", Type.getMethodDescriptor(interfaceTy, *fields), false)
|
||||
}
|
||||
|
||||
override fun visitEnd() {
|
||||
super.visitEnd()
|
||||
|
||||
for (bridge in bridgeMethods) {
|
||||
println("[INFO] Using bridge method ${bridge.bridgeName} for ${bridge.lambda}")
|
||||
val mw = parent.visitMethod(ACC_PUBLIC, bridge.bridgeName, bridge.lambda.desc, null, null) ?: continue
|
||||
mw.visitCode()
|
||||
mw.visitVarInsn(ALOAD, 0)
|
||||
for ((i, ty) in Type.getArgumentTypes(bridge.lambda.desc)
|
||||
.withIndex()) mw.visitVarInsn(ty.getOpcode(ILOAD), i + 1)
|
||||
mw.visitMethodInsn(INVOKESPECIAL, bridge.lambda.owner, bridge.lambda.name, bridge.lambda.desc, false)
|
||||
mw.visitInsn(Type.getReturnType(bridge.lambda.desc).getOpcode(IRETURN))
|
||||
val size = 1 + Type.getArgumentTypes(bridge.lambda.desc).size
|
||||
mw.visitMaxs(size, size)
|
||||
mw.visitEnd()
|
||||
}
|
||||
}
|
||||
}
|
@ -20,13 +20,16 @@ version = modVersion
|
||||
base.archivesName.convention("cc-tweaked-$mcVersion")
|
||||
|
||||
java {
|
||||
// Last version able to set a --release as low as 6
|
||||
toolchain.languageVersion.set(JavaLanguageVersion.of(11))
|
||||
toolchain {
|
||||
languageVersion.set(JavaLanguageVersion.of(17))
|
||||
}
|
||||
|
||||
withSourcesJar()
|
||||
}
|
||||
|
||||
tasks.compileJava { options.release.set(8) }
|
||||
val runtimeToolchain = javaToolchains.launcherFor {
|
||||
languageVersion.set(JavaLanguageVersion.of(8))
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
@ -38,10 +41,6 @@ repositories {
|
||||
}
|
||||
|
||||
volde {
|
||||
forgeCapabilities {
|
||||
setSrgsAsFallback(true)
|
||||
}
|
||||
|
||||
runs {
|
||||
named("client") {
|
||||
programArg("SquidDev")
|
||||
@ -50,6 +49,10 @@ volde {
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType(net.fabricmc.loom.task.RunTask::class.java).configureEach {
|
||||
javaLauncher.set(runtimeToolchain)
|
||||
}
|
||||
|
||||
configurations {
|
||||
val shade by registering
|
||||
compileOnly { extendsFrom(shade.get()) }
|
||||
@ -71,10 +74,12 @@ dependencies {
|
||||
},
|
||||
)
|
||||
|
||||
compileOnly("com.google.code.findbugs:jsr305:3.0.2")
|
||||
compileOnly("org.jetbrains:annotations:24.0.1")
|
||||
modImplementation("maven.modrinth:computercraft:1.50")
|
||||
"shade"("org.squiddev:Cobalt")
|
||||
|
||||
"buildTools"(project(":build-tools"))
|
||||
"buildTools"("cc.tweaked.cobalt:build-tools")
|
||||
}
|
||||
|
||||
// Point compileJava to emit to classes/uninstrumentedJava/main, and then add a task to instrument these classes,
|
||||
@ -88,12 +93,9 @@ val instrumentJava = tasks.register(mainSource.getTaskName("Instrument", "Java")
|
||||
inputs.dir(untransformedClasses).withPropertyName("inputDir")
|
||||
outputs.dir(javaClassesDir).withPropertyName("outputDir")
|
||||
|
||||
javaLauncher.set(
|
||||
javaToolchains.launcherFor {
|
||||
languageVersion.set(JavaLanguageVersion.of(17))
|
||||
},
|
||||
)
|
||||
mainClass.set("cc.tweaked.build.MainKt")
|
||||
// Run under Java 8, so we can check compatibility of methods.
|
||||
javaLauncher.set(runtimeToolchain)
|
||||
mainClass.set("cc.tweaked.cobalt.build.MainKt")
|
||||
classpath = buildTools
|
||||
|
||||
args = listOf(
|
||||
@ -129,6 +131,8 @@ tasks.jar {
|
||||
}
|
||||
|
||||
tasks.processResources {
|
||||
inputs.property("version", project.version)
|
||||
inputs.property("mcVersion", mcVersion)
|
||||
filesMatching("mcmod.info") {
|
||||
expand("version" to project.version, "mcVersion" to mcVersion)
|
||||
}
|
||||
|
@ -4,6 +4,6 @@
|
||||
|
||||
org.gradle.jvmargs=-Xmx3G
|
||||
|
||||
modVersion=1.105.1
|
||||
modVersion=1.108.1
|
||||
|
||||
mcVersion=1.4.7
|
||||
|
@ -38,5 +38,3 @@ val mcVersion: String by settings
|
||||
rootProject.name = "cc-tweaked-$mcVersion"
|
||||
|
||||
includeBuild("vendor/Cobalt")
|
||||
|
||||
include("build-tools")
|
||||
|
@ -4,8 +4,6 @@
|
||||
|
||||
package dan200.computercraft.api.lua;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* A wrapper type for "coerced" values.
|
||||
* <p>
|
||||
@ -20,27 +18,9 @@ import java.util.Objects;
|
||||
* }
|
||||
* }</pre>
|
||||
*
|
||||
* @param <T> The type of the underlying value.
|
||||
* @param value The argument value.
|
||||
* @param <T> The type of the underlying value.
|
||||
* @see IArguments#getStringCoerced(int)
|
||||
*/
|
||||
public final class Coerced<T> {
|
||||
private final T value;
|
||||
|
||||
public Coerced(T value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public T value() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj == this || obj instanceof Coerced && Objects.equals(value, ((Coerced<?>) obj).value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(value);
|
||||
}
|
||||
public record Coerced<T>(T value) {
|
||||
}
|
||||
|
@ -4,6 +4,9 @@
|
||||
|
||||
package dan200.computercraft.api.lua;
|
||||
|
||||
import org.jetbrains.annotations.Contract;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
@ -39,6 +42,7 @@ public abstract class IArguments {
|
||||
* @throws IllegalStateException If accessing these arguments outside the scope of the original function. See
|
||||
* {@link #escapes()}.
|
||||
*/
|
||||
@Nullable
|
||||
public abstract Object get(int index) throws LuaException;
|
||||
|
||||
/**
|
||||
@ -70,8 +74,8 @@ public abstract class IArguments {
|
||||
* @see #get(int) To get a single argument.
|
||||
*/
|
||||
public Object[] getAll() throws LuaException {
|
||||
Object[] result = new Object[count()];
|
||||
for (int i = 0; i < result.length; i++) result[i] = get(i);
|
||||
var result = new Object[count()];
|
||||
for (var i = 0; i < result.length; i++) result[i] = get(i);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -84,9 +88,9 @@ public abstract class IArguments {
|
||||
* @see #getFiniteDouble(int) if you require this to be finite (i.e. not infinite or NaN).
|
||||
*/
|
||||
public double getDouble(int index) throws LuaException {
|
||||
Object value = get(index);
|
||||
if (!(value instanceof Number)) throw LuaValues.badArgumentOf(this, index, "number");
|
||||
return ((Number) value).doubleValue();
|
||||
var value = get(index);
|
||||
if (!(value instanceof Number number)) throw LuaValues.badArgumentOf(this, index, "number");
|
||||
return number.doubleValue();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -108,9 +112,9 @@ public abstract class IArguments {
|
||||
* @throws LuaException If the value is not a long.
|
||||
*/
|
||||
public long getLong(int index) throws LuaException {
|
||||
Object value = get(index);
|
||||
if (!(value instanceof Number)) throw LuaValues.badArgumentOf(this, index, "number");
|
||||
return LuaValues.checkFiniteNum(index, (Number) value).longValue();
|
||||
var value = get(index);
|
||||
if (!(value instanceof Number number)) throw LuaValues.badArgumentOf(this, index, "number");
|
||||
return LuaValues.checkFiniteNum(index, number).longValue();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -132,9 +136,9 @@ public abstract class IArguments {
|
||||
* @throws LuaException If the value is not a boolean.
|
||||
*/
|
||||
public boolean getBoolean(int index) throws LuaException {
|
||||
Object value = get(index);
|
||||
if (!(value instanceof Boolean)) throw LuaValues.badArgumentOf(this, index, "boolean");
|
||||
return (Boolean) value;
|
||||
var value = get(index);
|
||||
if (!(value instanceof Boolean bool)) throw LuaValues.badArgumentOf(this, index, "boolean");
|
||||
return bool;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -145,9 +149,9 @@ public abstract class IArguments {
|
||||
* @throws LuaException If the value is not a string.
|
||||
*/
|
||||
public String getString(int index) throws LuaException {
|
||||
Object value = get(index);
|
||||
if (!(value instanceof String)) throw LuaValues.badArgumentOf(this, index, "string");
|
||||
return (String) value;
|
||||
var value = get(index);
|
||||
if (!(value instanceof String string)) throw LuaValues.badArgumentOf(this, index, "string");
|
||||
return string;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -167,12 +171,12 @@ public abstract class IArguments {
|
||||
* @see Coerced
|
||||
*/
|
||||
public String getStringCoerced(int index) throws LuaException {
|
||||
Object value = get(index);
|
||||
var value = get(index);
|
||||
if (value == null) return "nil";
|
||||
if (value instanceof Boolean || value instanceof String) return value.toString();
|
||||
if (value instanceof Number) {
|
||||
double asDouble = ((Number) value).doubleValue();
|
||||
int asInt = (int) asDouble;
|
||||
if (value instanceof Number number) {
|
||||
var asDouble = number.doubleValue();
|
||||
var asInt = (int) asDouble;
|
||||
return asInt == asDouble ? Integer.toString(asInt) : Double.toString(asDouble);
|
||||
}
|
||||
|
||||
@ -212,7 +216,7 @@ public abstract class IArguments {
|
||||
* @throws LuaException If the value is not a table.
|
||||
*/
|
||||
public Map<?, ?> getTable(int index) throws LuaException {
|
||||
Object value = get(index);
|
||||
var value = get(index);
|
||||
if (!(value instanceof Map)) throw LuaValues.badArgumentOf(this, index, "table");
|
||||
return (Map<?, ?>) value;
|
||||
}
|
||||
@ -225,10 +229,10 @@ public abstract class IArguments {
|
||||
* @throws LuaException If the value is not a number.
|
||||
*/
|
||||
public Optional<Double> optDouble(int index) throws LuaException {
|
||||
Object value = get(index);
|
||||
var value = get(index);
|
||||
if (value == null) return Optional.empty();
|
||||
if (!(value instanceof Number)) throw LuaValues.badArgumentOf(this, index, "number");
|
||||
return Optional.of(((Number) value).doubleValue());
|
||||
if (!(value instanceof Number number)) throw LuaValues.badArgumentOf(this, index, "number");
|
||||
return Optional.of(number.doubleValue());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -250,10 +254,10 @@ public abstract class IArguments {
|
||||
* @throws LuaException If the value is not a number.
|
||||
*/
|
||||
public Optional<Long> optLong(int index) throws LuaException {
|
||||
Object value = get(index);
|
||||
var value = get(index);
|
||||
if (value == null) return Optional.empty();
|
||||
if (!(value instanceof Number)) throw LuaValues.badArgumentOf(this, index, "number");
|
||||
return Optional.of(LuaValues.checkFiniteNum(index, (Number) value).longValue());
|
||||
if (!(value instanceof Number number)) throw LuaValues.badArgumentOf(this, index, "number");
|
||||
return Optional.of(LuaValues.checkFiniteNum(index, number).longValue());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -264,7 +268,7 @@ public abstract class IArguments {
|
||||
* @throws LuaException If the value is not finite.
|
||||
*/
|
||||
public Optional<Double> optFiniteDouble(int index) throws LuaException {
|
||||
Optional<Double> value = optDouble(index);
|
||||
var value = optDouble(index);
|
||||
if (value.isPresent()) LuaValues.checkFiniteNum(index, value.get());
|
||||
return value;
|
||||
}
|
||||
@ -277,10 +281,10 @@ public abstract class IArguments {
|
||||
* @throws LuaException If the value is not a boolean.
|
||||
*/
|
||||
public Optional<Boolean> optBoolean(int index) throws LuaException {
|
||||
Object value = get(index);
|
||||
var value = get(index);
|
||||
if (value == null) return Optional.empty();
|
||||
if (!(value instanceof Boolean)) throw LuaValues.badArgumentOf(this, index, "boolean");
|
||||
return Optional.of((Boolean) value);
|
||||
if (!(value instanceof Boolean bool)) throw LuaValues.badArgumentOf(this, index, "boolean");
|
||||
return Optional.of(bool);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -291,10 +295,10 @@ public abstract class IArguments {
|
||||
* @throws LuaException If the value is not a string.
|
||||
*/
|
||||
public Optional<String> optString(int index) throws LuaException {
|
||||
Object value = get(index);
|
||||
var value = get(index);
|
||||
if (value == null) return Optional.empty();
|
||||
if (!(value instanceof String)) throw LuaValues.badArgumentOf(this, index, "string");
|
||||
return Optional.of((String) value);
|
||||
if (!(value instanceof String string)) throw LuaValues.badArgumentOf(this, index, "string");
|
||||
return Optional.of(string);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -318,7 +322,7 @@ public abstract class IArguments {
|
||||
* @throws LuaException If the value is not a string or not a valid option for this enum.
|
||||
*/
|
||||
public <T extends Enum<T>> Optional<T> optEnum(int index, Class<T> klass) throws LuaException {
|
||||
Optional<String> str = optString(index);
|
||||
var str = optString(index);
|
||||
return str.isPresent() ? Optional.of(LuaValues.checkEnum(index, klass, str.get())) : Optional.empty();
|
||||
}
|
||||
|
||||
@ -330,7 +334,7 @@ public abstract class IArguments {
|
||||
* @throws LuaException If the value is not a table.
|
||||
*/
|
||||
public Optional<Map<?, ?>> optTable(int index) throws LuaException {
|
||||
Object value = get(index);
|
||||
var value = get(index);
|
||||
if (value == null) return Optional.empty();
|
||||
if (!(value instanceof Map)) throw LuaValues.badArgumentOf(this, index, "map");
|
||||
return Optional.of((Map<?, ?>) value);
|
||||
@ -340,7 +344,7 @@ public abstract class IArguments {
|
||||
* Get an argument as a double.
|
||||
*
|
||||
* @param index The argument number.
|
||||
* @param def The public value, if this argument is not given.
|
||||
* @param def The default value, if this argument is not given.
|
||||
* @return The argument's value, or {@code def} if none was provided.
|
||||
* @throws LuaException If the value is not a number.
|
||||
*/
|
||||
@ -352,7 +356,7 @@ public abstract class IArguments {
|
||||
* Get an argument as an int.
|
||||
*
|
||||
* @param index The argument number.
|
||||
* @param def The public value, if this argument is not given.
|
||||
* @param def The default value, if this argument is not given.
|
||||
* @return The argument's value, or {@code def} if none was provided.
|
||||
* @throws LuaException If the value is not a number.
|
||||
*/
|
||||
@ -364,7 +368,7 @@ public abstract class IArguments {
|
||||
* Get an argument as a long.
|
||||
*
|
||||
* @param index The argument number.
|
||||
* @param def The public value, if this argument is not given.
|
||||
* @param def The default value, if this argument is not given.
|
||||
* @return The argument's value, or {@code def} if none was provided.
|
||||
* @throws LuaException If the value is not a number.
|
||||
*/
|
||||
@ -376,7 +380,7 @@ public abstract class IArguments {
|
||||
* Get an argument as a finite number (not infinite or NaN).
|
||||
*
|
||||
* @param index The argument number.
|
||||
* @param def The public value, if this argument is not given.
|
||||
* @param def The default value, if this argument is not given.
|
||||
* @return The argument's value, or {@code def} if none was provided.
|
||||
* @throws LuaException If the value is not finite.
|
||||
*/
|
||||
@ -388,7 +392,7 @@ public abstract class IArguments {
|
||||
* Get an argument as a boolean.
|
||||
*
|
||||
* @param index The argument number.
|
||||
* @param def The public value, if this argument is not given.
|
||||
* @param def The default value, if this argument is not given.
|
||||
* @return The argument's value, or {@code def} if none was provided.
|
||||
* @throws LuaException If the value is not a boolean.
|
||||
*/
|
||||
@ -400,11 +404,13 @@ public abstract class IArguments {
|
||||
* Get an argument as a string.
|
||||
*
|
||||
* @param index The argument number.
|
||||
* @param def The public value, if this argument is not given.
|
||||
* @param def The default value, if this argument is not given.
|
||||
* @return The argument's value, or {@code def} if none was provided.
|
||||
* @throws LuaException If the value is not a string.
|
||||
*/
|
||||
public String optString(int index, String def) throws LuaException {
|
||||
@Nullable
|
||||
@Contract("_, !null -> !null")
|
||||
public String optString(int index, @Nullable String def) throws LuaException {
|
||||
return optString(index).orElse(def);
|
||||
}
|
||||
|
||||
@ -412,11 +418,13 @@ public abstract class IArguments {
|
||||
* Get an argument as a table.
|
||||
*
|
||||
* @param index The argument number.
|
||||
* @param def The public value, if this argument is not given.
|
||||
* @param def The default value, if this argument is not given.
|
||||
* @return The argument's value, or {@code def} if none was provided.
|
||||
* @throws LuaException If the value is not a table.
|
||||
*/
|
||||
public Map<?, ?> optTable(int index, Map<Object, Object> def) throws LuaException {
|
||||
@Nullable
|
||||
@Contract("_, !null -> !null")
|
||||
public Map<?, ?> optTable(int index, @Nullable Map<Object, Object> def) throws LuaException {
|
||||
return optTable(index).orElse(def);
|
||||
}
|
||||
|
||||
@ -436,9 +444,11 @@ public abstract class IArguments {
|
||||
* yourself.
|
||||
*
|
||||
* @return An {@link IArguments} instance which can escape the current scope. May be {@code this}.
|
||||
* @throws LuaException For the same reasons as {@link #get(int)}.
|
||||
* @throws LuaException For the same reasons as {@link #get(int)}.
|
||||
* @throws IllegalStateException If marking these arguments as escaping outside the scope of the original function.
|
||||
*/
|
||||
public IArguments escapes() throws LuaException {
|
||||
// TODO(1.21.0): Make this return void, require that it mutates this.
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
@ -4,21 +4,25 @@
|
||||
|
||||
package dan200.computercraft.api.lua;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.Serial;
|
||||
|
||||
/**
|
||||
* An exception representing an error in Lua, like that raised by the {@code error()} function.
|
||||
*/
|
||||
public class LuaException extends Exception {
|
||||
@Serial
|
||||
private static final long serialVersionUID = -6136063076818512651L;
|
||||
private final boolean hasLevel;
|
||||
private final int level;
|
||||
|
||||
public LuaException(String message) {
|
||||
public LuaException(@Nullable String message) {
|
||||
super(message);
|
||||
hasLevel = false;
|
||||
level = 1;
|
||||
}
|
||||
|
||||
public LuaException(String message, int level) {
|
||||
public LuaException(@Nullable String message, int level) {
|
||||
super(message);
|
||||
hasLevel = true;
|
||||
this.level = level;
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
package dan200.computercraft.api.lua;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Map;
|
||||
|
||||
@ -23,9 +24,9 @@ public final class LuaValues {
|
||||
* @return The encoded string.
|
||||
*/
|
||||
public static ByteBuffer encode(String string) {
|
||||
byte[] chars = new byte[string.length()];
|
||||
for (int i = 0; i < chars.length; i++) {
|
||||
char c = string.charAt(i);
|
||||
var chars = new byte[string.length()];
|
||||
for (var i = 0; i < chars.length; i++) {
|
||||
var c = string.charAt(i);
|
||||
chars[i] = c < 256 ? (byte) c : 63;
|
||||
}
|
||||
|
||||
@ -53,7 +54,7 @@ public final class LuaValues {
|
||||
* @return A string representation of the given value's type, in a similar format to that provided by Lua's
|
||||
* {@code type} function.
|
||||
*/
|
||||
public static String getType(Object value) {
|
||||
public static String getType(@Nullable Object value) {
|
||||
if (value == null) return "nil";
|
||||
if (value instanceof String) return "string";
|
||||
if (value instanceof Boolean) return "boolean";
|
||||
@ -147,7 +148,7 @@ public final class LuaValues {
|
||||
* @throws LuaException If this is not a known enum value.
|
||||
*/
|
||||
public static <T extends Enum<T>> T checkEnum(int index, Class<T> klass, String value) throws LuaException {
|
||||
for (T possibility : klass.getEnumConstants()) {
|
||||
for (var possibility : klass.getEnumConstants()) {
|
||||
if (possibility.name().equalsIgnoreCase(value)) return possibility;
|
||||
}
|
||||
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
package dan200.computercraft.api.lua;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
@ -44,6 +45,7 @@ public final class ObjectArguments extends IArguments {
|
||||
return new ObjectArguments(args.subList(count, args.size()));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Object get(int index) {
|
||||
return index >= args.size() ? null : args.get(index);
|
||||
|
@ -152,6 +152,6 @@ public class FixedWidthFontRenderer {
|
||||
if (c >= '0' && c <= '9') return c - '0';
|
||||
if (c >= 'a' && c <= 'f') return c - 'a' + 10;
|
||||
if (c >= 'A' && c <= 'F') return c - 'A' + 10;
|
||||
return 15 - def.ordinal();
|
||||
return def.ordinal();
|
||||
}
|
||||
}
|
||||
|
@ -371,27 +371,36 @@ public class FSAPI implements ILuaAPI {
|
||||
public final Object[] open(String path, String mode) throws LuaException {
|
||||
try {
|
||||
switch (mode) {
|
||||
// Open the file for reading, then create a wrapper around the reader
|
||||
case "r":
|
||||
case "r" -> {
|
||||
// Open the file for reading, then create a wrapper around the reader
|
||||
return new Object[]{ new EncodedReadableHandle(getFileSystem().openForRead(path)) };
|
||||
// Open the file for writing, then create a wrapper around the writer
|
||||
case "w":
|
||||
}
|
||||
case "w" -> {
|
||||
// Open the file for writing, then create a wrapper around the writer
|
||||
FileSystemExtensions.makeParentDir(fileSystem, path);
|
||||
return new Object[]{ new EncodedWritableHandle(getFileSystem().openForWrite(path, false)) };
|
||||
// Open the file for appending, then create a wrapper around the writer
|
||||
case "a":
|
||||
}
|
||||
case "a" -> {
|
||||
// Open the file for appending, then create a wrapper around the writer
|
||||
FileSystemExtensions.makeParentDir(fileSystem, path);
|
||||
return new Object[]{ new EncodedWritableHandle(getFileSystem().openForWrite(path, true)) };
|
||||
// Open the file for binary reading, then create a wrapper around the reader
|
||||
case "rb":
|
||||
}
|
||||
case "rb" -> {
|
||||
// Open the file for binary reading, then create a wrapper around the reader
|
||||
IMountedFileBinary reader = getFileSystem().openForBinaryRead(path);
|
||||
return new Object[]{ new BinaryReadableHandle(reader) };
|
||||
// Open the file for binary writing, then create a wrapper around the writer
|
||||
case "wb":
|
||||
}
|
||||
case "wb" -> {
|
||||
// Open the file for binary writing, then create a wrapper around the writer
|
||||
FileSystemExtensions.makeParentDir(fileSystem, path);
|
||||
return new Object[]{ new BinaryWritableHandle(getFileSystem().openForBinaryWrite(path, false)) };
|
||||
// Open the file for binary appending, then create a wrapper around the reader
|
||||
case "ab":
|
||||
}
|
||||
case "ab" -> {
|
||||
// Open the file for binary appending, then create a wrapper around the reader
|
||||
FileSystemExtensions.makeParentDir(fileSystem, path);
|
||||
return new Object[]{ new BinaryWritableHandle(getFileSystem().openForBinaryWrite(path, true)) };
|
||||
default:
|
||||
throw new LuaException("Unsupported mode");
|
||||
}
|
||||
default -> throw new LuaException("Unsupported mode");
|
||||
}
|
||||
} catch (FileSystemException e) {
|
||||
return new Object[]{ null, e.getMessage() };
|
||||
@ -442,29 +451,6 @@ public class FSAPI implements ILuaAPI {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Searches for files matching a string with wildcards.
|
||||
* <p>
|
||||
* This string is formatted like a normal path string, but can include any
|
||||
* number of wildcards ({@code *}) to look for files matching anything.
|
||||
* For example, <code>rom/*/command*</code> will look for any path starting with
|
||||
* {@code command} inside any subdirectory of {@code /rom}.
|
||||
*
|
||||
* @param path The wildcard-qualified path to search for.
|
||||
* @return A list of paths that match the search string.
|
||||
* @throws LuaException If the path doesn't exist.
|
||||
* @cc.since 1.6
|
||||
*/
|
||||
@LuaFunction
|
||||
public final String[] find(String path) throws LuaException {
|
||||
try {
|
||||
return FileSystemExtensions.find(getFileSystem(), path);
|
||||
} catch (FileSystemException e) {
|
||||
throw new LuaException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the capacity of the drive the path is located on.
|
||||
*
|
||||
|
@ -7,54 +7,15 @@ import com.google.common.base.Splitter;
|
||||
import dan200.computer.core.FileSystem;
|
||||
import dan200.computer.core.FileSystemException;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Arrays;
|
||||
import java.util.Deque;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Backports additional methods from {@link FileSystem}.
|
||||
*/
|
||||
final class FileSystemExtensions {
|
||||
private static void findIn(FileSystem fs, String dir, List<String> matches, Pattern wildPattern) throws FileSystemException {
|
||||
String[] list = fs.list(dir);
|
||||
for (String entry : list) {
|
||||
String entryPath = dir.isEmpty() ? entry : dir + "/" + entry;
|
||||
if (wildPattern.matcher(entryPath).matches()) {
|
||||
matches.add(entryPath);
|
||||
}
|
||||
if (fs.isDir(entryPath)) {
|
||||
findIn(fs, entryPath, matches, wildPattern);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static synchronized String[] find(FileSystem fs, String wildPath) throws FileSystemException {
|
||||
// Match all the files on the system
|
||||
wildPath = sanitizePath(wildPath, true);
|
||||
|
||||
// If we don't have a wildcard at all just check the file exists
|
||||
int starIndex = wildPath.indexOf('*');
|
||||
if (starIndex == -1) {
|
||||
return fs.exists(wildPath) ? new String[]{ wildPath } : new String[0];
|
||||
}
|
||||
|
||||
// Find the all non-wildcarded directories. For instance foo/bar/baz* -> foo/bar
|
||||
int prevDir = wildPath.substring(0, starIndex).lastIndexOf('/');
|
||||
String startDir = prevDir == -1 ? "" : wildPath.substring(0, prevDir);
|
||||
|
||||
// If this isn't a directory then just abort
|
||||
if (!fs.isDir(startDir)) return new String[0];
|
||||
|
||||
// Scan as normal, starting from this directory
|
||||
Pattern wildPattern = Pattern.compile("^\\Q" + wildPath.replaceAll("\\*", "\\\\E[^\\\\/]*\\\\Q") + "\\E$");
|
||||
List<String> matches = new ArrayList<>();
|
||||
findIn(fs, startDir, matches, wildPattern);
|
||||
|
||||
// Return matches
|
||||
String[] array = matches.toArray(new String[0]);
|
||||
Arrays.sort(array);
|
||||
return array;
|
||||
}
|
||||
|
||||
public static String getDirectory(String path) {
|
||||
path = sanitizePath(path, true);
|
||||
if (path.isEmpty()) {
|
||||
@ -69,6 +30,11 @@ final class FileSystemExtensions {
|
||||
}
|
||||
}
|
||||
|
||||
public static void makeParentDir(FileSystem fileSystem, String path) throws FileSystemException {
|
||||
var parent = getDirectory(path);
|
||||
if (!parent.isEmpty()) fileSystem.makeDir(parent);
|
||||
}
|
||||
|
||||
private static final Pattern threeDotsPattern = Pattern.compile("^\\.{3,}$");
|
||||
|
||||
public static String sanitizePath(String path, boolean allowWildcards) {
|
||||
|
@ -9,10 +9,10 @@ public final class StringUtil {
|
||||
}
|
||||
|
||||
public static String normaliseLabel(String label) {
|
||||
int length = Math.min(32, label.length());
|
||||
StringBuilder builder = new StringBuilder(length);
|
||||
for (int i = 0; i < length; i++) {
|
||||
char c = label.charAt(i);
|
||||
var length = Math.min(32, label.length());
|
||||
var builder = new StringBuilder(length);
|
||||
for (var i = 0; i < length; i++) {
|
||||
var c = label.charAt(i);
|
||||
if ((c >= ' ' && c <= '~') || (c >= 161 && c <= 172) || (c >= 174 && c <= 255)) {
|
||||
builder.append(c);
|
||||
} else {
|
||||
|
@ -687,7 +687,7 @@ settings.define("paint.default_extension", {
|
||||
|
||||
settings.define("list.show_hidden", {
|
||||
default = false,
|
||||
description = [[Show hidden files (those starting with "." in the Lua REPL).]],
|
||||
description = [[Whether the list program show hidden files (those starting with ".").]],
|
||||
type = "boolean",
|
||||
})
|
||||
|
||||
|
@ -3,20 +3,20 @@
|
||||
-- SPDX-License-Identifier: LicenseRef-CCPL
|
||||
|
||||
--[[- Constants and functions for colour values, suitable for working with
|
||||
@{term} and @{redstone}.
|
||||
[`term`] and [`redstone`].
|
||||
|
||||
This is useful in conjunction with @{redstone.setBundledOutput|Bundled Cables}
|
||||
from mods like Project Red, and @{term.setTextColour|colors on Advanced
|
||||
Computers and Advanced Monitors}.
|
||||
This is useful in conjunction with [Bundled Cables][`redstone.setBundledOutput`]
|
||||
from mods like Project Red, and [colors on Advanced Computers and Advanced
|
||||
Monitors][`term.setTextColour`].
|
||||
|
||||
For the non-American English version just replace @{colors} with @{colours}.
|
||||
For the non-American English version just replace [`colors`] with [`colours`].
|
||||
This alternative API is exactly the same, except the colours use British English
|
||||
(e.g. @{colors.gray} is spelt @{colours.grey}).
|
||||
(e.g. [`colors.gray`] is spelt [`colours.grey`]).
|
||||
|
||||
On basic terminals (such as the Computer and Monitor), all the colors are
|
||||
converted to grayscale. This means you can still use all 16 colors on the
|
||||
screen, but they will appear as the nearest tint of gray. You can check if a
|
||||
terminal supports color by using the function @{term.isColor}.
|
||||
terminal supports color by using the function [`term.isColor`].
|
||||
|
||||
Grayscale colors are calculated by taking the average of the three components,
|
||||
i.e. `(red + green + blue) / 3`.
|
||||
@ -140,67 +140,67 @@ i.e. `(red + green + blue) / 3`.
|
||||
|
||||
local expect = dofile("rom/modules/main/cc/expect.lua").expect
|
||||
|
||||
--- White: Written as `0` in paint files and @{term.blit}, has a default
|
||||
--- White: Written as `0` in paint files and [`term.blit`], has a default
|
||||
-- terminal colour of #F0F0F0.
|
||||
white = 0x1
|
||||
|
||||
--- Orange: Written as `1` in paint files and @{term.blit}, has a
|
||||
--- Orange: Written as `1` in paint files and [`term.blit`], has a
|
||||
-- default terminal colour of #F2B233.
|
||||
orange = 0x2
|
||||
|
||||
--- Magenta: Written as `2` in paint files and @{term.blit}, has a
|
||||
--- Magenta: Written as `2` in paint files and [`term.blit`], has a
|
||||
-- default terminal colour of #E57FD8.
|
||||
magenta = 0x4
|
||||
|
||||
--- Light blue: Written as `3` in paint files and @{term.blit}, has a
|
||||
--- Light blue: Written as `3` in paint files and [`term.blit`], has a
|
||||
-- default terminal colour of #99B2F2.
|
||||
lightBlue = 0x8
|
||||
|
||||
--- Yellow: Written as `4` in paint files and @{term.blit}, has a
|
||||
--- Yellow: Written as `4` in paint files and [`term.blit`], has a
|
||||
-- default terminal colour of #DEDE6C.
|
||||
yellow = 0x10
|
||||
|
||||
--- Lime: Written as `5` in paint files and @{term.blit}, has a default
|
||||
--- Lime: Written as `5` in paint files and [`term.blit`], has a default
|
||||
-- terminal colour of #7FCC19.
|
||||
lime = 0x20
|
||||
|
||||
--- Pink: Written as `6` in paint files and @{term.blit}, has a default
|
||||
--- Pink: Written as `6` in paint files and [`term.blit`], has a default
|
||||
-- terminal colour of #F2B2CC.
|
||||
pink = 0x40
|
||||
|
||||
--- Gray: Written as `7` in paint files and @{term.blit}, has a default
|
||||
--- Gray: Written as `7` in paint files and [`term.blit`], has a default
|
||||
-- terminal colour of #4C4C4C.
|
||||
gray = 0x80
|
||||
|
||||
--- Light gray: Written as `8` in paint files and @{term.blit}, has a
|
||||
--- Light gray: Written as `8` in paint files and [`term.blit`], has a
|
||||
-- default terminal colour of #999999.
|
||||
lightGray = 0x100
|
||||
|
||||
--- Cyan: Written as `9` in paint files and @{term.blit}, has a default
|
||||
--- Cyan: Written as `9` in paint files and [`term.blit`], has a default
|
||||
-- terminal colour of #4C99B2.
|
||||
cyan = 0x200
|
||||
|
||||
--- Purple: Written as `a` in paint files and @{term.blit}, has a
|
||||
--- Purple: Written as `a` in paint files and [`term.blit`], has a
|
||||
-- default terminal colour of #B266E5.
|
||||
purple = 0x400
|
||||
|
||||
--- Blue: Written as `b` in paint files and @{term.blit}, has a default
|
||||
--- Blue: Written as `b` in paint files and [`term.blit`], has a default
|
||||
-- terminal colour of #3366CC.
|
||||
blue = 0x800
|
||||
|
||||
--- Brown: Written as `c` in paint files and @{term.blit}, has a default
|
||||
--- Brown: Written as `c` in paint files and [`term.blit`], has a default
|
||||
-- terminal colour of #7F664C.
|
||||
brown = 0x1000
|
||||
|
||||
--- Green: Written as `d` in paint files and @{term.blit}, has a default
|
||||
--- Green: Written as `d` in paint files and [`term.blit`], has a default
|
||||
-- terminal colour of #57A64E.
|
||||
green = 0x2000
|
||||
|
||||
--- Red: Written as `e` in paint files and @{term.blit}, has a default
|
||||
--- Red: Written as `e` in paint files and [`term.blit`], has a default
|
||||
-- terminal colour of #CC4C4C.
|
||||
red = 0x4000
|
||||
|
||||
--- Black: Written as `f` in paint files and @{term.blit}, has a default
|
||||
--- Black: Written as `f` in paint files and [`term.blit`], has a default
|
||||
-- terminal colour of #111111.
|
||||
black = 0x8000
|
||||
|
||||
@ -313,18 +313,18 @@ function unpackRGB(rgb)
|
||||
bit32.band(rgb, 0xFF) / 255
|
||||
end
|
||||
|
||||
--- Either calls @{colors.packRGB} or @{colors.unpackRGB}, depending on how many
|
||||
--- Either calls [`colors.packRGB`] or [`colors.unpackRGB`], depending on how many
|
||||
-- arguments it receives.
|
||||
--
|
||||
-- @tparam[1] number r The red channel, as an argument to @{colors.packRGB}.
|
||||
-- @tparam[1] number g The green channel, as an argument to @{colors.packRGB}.
|
||||
-- @tparam[1] number b The blue channel, as an argument to @{colors.packRGB}.
|
||||
-- @tparam[2] number rgb The combined hexadecimal color, as an argument to @{colors.unpackRGB}.
|
||||
-- @treturn[1] number The combined hexadecimal colour, as returned by @{colors.packRGB}.
|
||||
-- @treturn[2] number The red channel, as returned by @{colors.unpackRGB}
|
||||
-- @treturn[2] number The green channel, as returned by @{colors.unpackRGB}
|
||||
-- @treturn[2] number The blue channel, as returned by @{colors.unpackRGB}
|
||||
-- @deprecated Use @{packRGB} or @{unpackRGB} directly.
|
||||
-- @tparam[1] number r The red channel, as an argument to [`colors.packRGB`].
|
||||
-- @tparam[1] number g The green channel, as an argument to [`colors.packRGB`].
|
||||
-- @tparam[1] number b The blue channel, as an argument to [`colors.packRGB`].
|
||||
-- @tparam[2] number rgb The combined hexadecimal color, as an argument to [`colors.unpackRGB`].
|
||||
-- @treturn[1] number The combined hexadecimal colour, as returned by [`colors.packRGB`].
|
||||
-- @treturn[2] number The red channel, as returned by [`colors.unpackRGB`]
|
||||
-- @treturn[2] number The green channel, as returned by [`colors.unpackRGB`]
|
||||
-- @treturn[2] number The blue channel, as returned by [`colors.unpackRGB`]
|
||||
-- @deprecated Use [`packRGB`] or [`unpackRGB`] directly.
|
||||
-- @usage
|
||||
-- ```lua
|
||||
-- colors.rgb8(0xb23399)
|
||||
|
@ -2,7 +2,7 @@
|
||||
--
|
||||
-- SPDX-License-Identifier: LicenseRef-CCPL
|
||||
|
||||
--- An alternative version of @{colors} for lovers of British spelling.
|
||||
--- An alternative version of [`colors`] for lovers of British spelling.
|
||||
--
|
||||
-- @see colors
|
||||
-- @module colours
|
||||
@ -13,14 +13,14 @@ for k, v in pairs(colors) do
|
||||
colours[k] = v
|
||||
end
|
||||
|
||||
--- Grey. Written as `7` in paint files and @{term.blit}, has a default
|
||||
--- Grey. Written as `7` in paint files and [`term.blit`], has a default
|
||||
-- terminal colour of #4C4C4C.
|
||||
--
|
||||
-- @see colors.gray
|
||||
colours.grey = colors.gray
|
||||
colours.gray = nil --- @local
|
||||
|
||||
--- Light grey. Written as `8` in paint files and @{term.blit}, has a
|
||||
--- Light grey. Written as `8` in paint files and [`term.blit`], has a
|
||||
-- default terminal colour of #999999.
|
||||
--
|
||||
-- @see colors.lightGray
|
||||
|
@ -5,20 +5,19 @@
|
||||
--[[- Execute [Minecraft commands][mc] and gather data from the results from
|
||||
a command computer.
|
||||
|
||||
:::note
|
||||
This API is only available on Command computers. It is not accessible to normal
|
||||
players.
|
||||
:::
|
||||
> [!NOTE]
|
||||
> This API is only available on Command computers. It is not accessible to normal
|
||||
> players.
|
||||
|
||||
While one may use @{commands.exec} directly to execute a command, the
|
||||
While one may use [`commands.exec`] directly to execute a command, the
|
||||
commands API also provides helper methods to execute every command. For
|
||||
instance, `commands.say("Hi!")` is equivalent to `commands.exec("say Hi!")`.
|
||||
|
||||
@{commands.async} provides a similar interface to execute asynchronous
|
||||
[`commands.async`] provides a similar interface to execute asynchronous
|
||||
commands. `commands.async.say("Hi!")` is equivalent to
|
||||
`commands.execAsync("say Hi!")`.
|
||||
|
||||
[mc]: https://minecraft.gamepedia.com/Commands
|
||||
[mc]: https://minecraft.wiki/w/Commands
|
||||
|
||||
@module commands
|
||||
@usage Set the block above this computer to stone:
|
||||
@ -31,7 +30,7 @@ end
|
||||
|
||||
--- The builtin commands API, without any generated command helper functions
|
||||
--
|
||||
-- This may be useful if a built-in function (such as @{commands.list}) has been
|
||||
-- This may be useful if a built-in function (such as [`commands.list`]) has been
|
||||
-- overwritten by a command.
|
||||
local native = commands.native or commands
|
||||
|
||||
@ -112,7 +111,7 @@ end
|
||||
|
||||
--- A table containing asynchronous wrappers for all commands.
|
||||
--
|
||||
-- As with @{commands.execAsync}, this returns the "task id" of the enqueued
|
||||
-- As with [`commands.execAsync`], this returns the "task id" of the enqueued
|
||||
-- command.
|
||||
-- @see execAsync
|
||||
-- @usage Asynchronously sets the block above the computer to stone.
|
||||
|
@ -9,10 +9,9 @@ locally attached drive, specify “side” as one of the six sides (e.g. `left`)
|
||||
use a remote disk drive, specify its name as printed when enabling its modem
|
||||
(e.g. `drive_0`).
|
||||
|
||||
:::tip
|
||||
All computers (except command computers), turtles and pocket computers can be
|
||||
placed within a disk drive to access it's internal storage like a disk.
|
||||
:::
|
||||
> [!TIP]
|
||||
> All computers (except command computers), turtles and pocket computers can be
|
||||
> placed within a disk drive to access it's internal storage like a disk.
|
||||
|
||||
@module disk
|
||||
@since 1.2
|
||||
@ -95,9 +94,9 @@ end
|
||||
--- Whether the current disk is a [music disk][disk] as opposed to a floppy disk
|
||||
-- or other item.
|
||||
--
|
||||
-- If this returns true, you will can @{disk.playAudio|play} the record.
|
||||
-- If this returns true, you will can [play][`disk.playAudio`] the record.
|
||||
--
|
||||
-- [disk]: https://minecraft.gamepedia.com/Music_Disc
|
||||
-- [disk]: https://minecraft.wiki/w/Music_Disc
|
||||
--
|
||||
-- @tparam string name The name of the disk drive.
|
||||
-- @treturn boolean If the disk is present and has audio saved on it.
|
||||
@ -110,10 +109,10 @@ end
|
||||
|
||||
--- Get the title of the audio track from the music record in the drive.
|
||||
--
|
||||
-- This generally returns the same as @{disk.getLabel} for records.
|
||||
-- This generally returns the same as [`disk.getLabel`] for records.
|
||||
--
|
||||
-- @tparam string name The name of the disk drive.
|
||||
-- @treturn string|false|nil The track title, @{false} if there is not a music
|
||||
-- @treturn string|false|nil The track title, [`false`] if there is not a music
|
||||
-- record in the drive or `nil` if no drive is present.
|
||||
function getAudioTitle(name)
|
||||
if isDrive(name) then
|
||||
@ -126,7 +125,7 @@ end
|
||||
--
|
||||
-- If any record is already playing on any disk drive, it stops before the
|
||||
-- target drive starts playing. The record stops when it reaches the end of the
|
||||
-- track, when it is removed from the drive, when @{disk.stopAudio} is called, or
|
||||
-- track, when it is removed from the drive, when [`disk.stopAudio`] is called, or
|
||||
-- when another record is started.
|
||||
--
|
||||
-- @tparam string name The name of the disk drive.
|
||||
@ -138,7 +137,7 @@ function playAudio(name)
|
||||
end
|
||||
|
||||
--- Stops the music record in the drive from playing, if it was started with
|
||||
-- @{disk.playAudio}.
|
||||
-- [`disk.playAudio`].
|
||||
--
|
||||
-- @tparam string name The name o the disk drive.
|
||||
function stopAudio(name)
|
||||
@ -165,7 +164,7 @@ end
|
||||
|
||||
--- Returns a number which uniquely identifies the disk in the drive.
|
||||
--
|
||||
-- Note, unlike @{disk.getLabel}, this does not return anything for other media,
|
||||
-- Note, unlike [`disk.getLabel`], this does not return anything for other media,
|
||||
-- such as computers or turtles.
|
||||
--
|
||||
-- @tparam string name The name of the disk drive.
|
||||
|
@ -13,19 +13,19 @@ local fs = _ENV
|
||||
for k, v in pairs(native) do fs[k] = v end
|
||||
|
||||
--[[- Provides completion for a file or directory name, suitable for use with
|
||||
@{_G.read}.
|
||||
[`_G.read`].
|
||||
|
||||
When a directory is a possible candidate for completion, two entries are
|
||||
included - one with a trailing slash (indicating that entries within this
|
||||
directory exist) and one without it (meaning this entry is an immediate
|
||||
completion candidate). `include_dirs` can be set to @{false} to only include
|
||||
completion candidate). `include_dirs` can be set to [`false`] to only include
|
||||
those with a trailing slash.
|
||||
|
||||
@tparam[1] string path The path to complete.
|
||||
@tparam[1] string location The location where paths are resolved from.
|
||||
@tparam[1,opt=true] boolean include_files When @{false}, only directories will
|
||||
@tparam[1,opt=true] boolean include_files When [`false`], only directories will
|
||||
be included in the returned list.
|
||||
@tparam[1,opt=true] boolean include_dirs When @{false}, "raw" directories will
|
||||
@tparam[1,opt=true] boolean include_dirs When [`false`], "raw" directories will
|
||||
not be included in the returned list.
|
||||
|
||||
@tparam[2] string path The path to complete.
|
||||
@ -133,6 +133,93 @@ function fs.complete(sPath, sLocation, bIncludeFiles, bIncludeDirs)
|
||||
return {}
|
||||
end
|
||||
|
||||
local function find_aux(path, parts, i, out)
|
||||
local part = parts[i]
|
||||
if not part then
|
||||
-- If we're at the end of the pattern, ensure our path exists and append it.
|
||||
if fs.exists(path) then out[#out + 1] = path end
|
||||
elseif part.exact then
|
||||
-- If we're an exact match, just recurse into this directory.
|
||||
return find_aux(fs.combine(path, part.contents), parts, i + 1, out)
|
||||
else
|
||||
-- Otherwise we're a pattern. Check we're a directory, then recurse into each
|
||||
-- matching file.
|
||||
if not fs.isDir(path) then return end
|
||||
|
||||
local files = fs.list(path)
|
||||
for j = 1, #files do
|
||||
local file = files[j]
|
||||
if file:find(part.contents) then find_aux(fs.combine(path, file), parts, i + 1, out) end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local find_escape = {
|
||||
-- Escape standard Lua pattern characters
|
||||
["^"] = "%^", ["$"] = "%$", ["("] = "%(", [")"] = "%)", ["%"] = "%%",
|
||||
["."] = "%.", ["["] = "%[", ["]"] = "%]", ["+"] = "%+", ["-"] = "%-",
|
||||
-- Aside from our wildcards.
|
||||
["*"] = ".*",
|
||||
["?"] = ".",
|
||||
}
|
||||
|
||||
--[[- Searches for files matching a string with wildcards.
|
||||
|
||||
This string looks like a normal path string, but can include wildcards, which
|
||||
can match multiple paths:
|
||||
|
||||
- "?" matches any single character in a file name.
|
||||
- "*" matches any number of characters.
|
||||
|
||||
For example, `rom/*/command*` will look for any path starting with `command`
|
||||
inside any subdirectory of `/rom`.
|
||||
|
||||
Note that these wildcards match a single segment of the path. For instance
|
||||
`rom/*.lua` will include `rom/startup.lua` but _not_ include `rom/programs/list.lua`.
|
||||
|
||||
@tparam string path The wildcard-qualified path to search for.
|
||||
@treturn { string... } A list of paths that match the search string.
|
||||
@throws If the supplied path was invalid.
|
||||
@since 1.6
|
||||
@changed 1.106.0 Added support for the `?` wildcard.
|
||||
|
||||
@usage List all Markdown files in the help folder
|
||||
|
||||
fs.find("rom/help/*.md")
|
||||
]]
|
||||
function fs.find(pattern)
|
||||
expect(1, pattern, "string")
|
||||
|
||||
pattern = fs.combine(pattern) -- Normalise the path, removing ".."s.
|
||||
|
||||
-- If the pattern is trying to search outside the computer root, just abort.
|
||||
-- This will fail later on anyway.
|
||||
if pattern == ".." or pattern:sub(1, 3) == "../" then
|
||||
error("/" .. pattern .. ": Invalid Path", 2)
|
||||
end
|
||||
|
||||
-- If we've no wildcards, just check the file exists.
|
||||
if not pattern:find("[*?]") then
|
||||
if fs.exists(pattern) then return { pattern } else return {} end
|
||||
end
|
||||
|
||||
local parts = {}
|
||||
for part in pattern:gmatch("[^/]+") do
|
||||
if part:find("[*?]") then
|
||||
parts[#parts + 1] = {
|
||||
exact = false,
|
||||
contents = "^" .. part:gsub(".", find_escape) .. "$",
|
||||
}
|
||||
else
|
||||
parts[#parts + 1] = { exact = true, contents = part }
|
||||
end
|
||||
end
|
||||
|
||||
local out = {}
|
||||
find_aux("", parts, 1, out)
|
||||
return out
|
||||
end
|
||||
|
||||
--- Returns true if a path is mounted to the parent filesystem.
|
||||
--
|
||||
-- The root filesystem "/" is considered a mount, along with disk folders and
|
||||
|
@ -2,21 +2,20 @@
|
||||
--
|
||||
-- SPDX-License-Identifier: LicenseRef-CCPL
|
||||
|
||||
--[[- Use @{modem|modems} to locate the position of the current turtle or
|
||||
--[[- Use [modems][`modem`] to locate the position of the current turtle or
|
||||
computers.
|
||||
|
||||
It broadcasts a PING message over @{rednet} and wait for responses. In order for
|
||||
It broadcasts a PING message over [`rednet`] and wait for responses. In order for
|
||||
this system to work, there must be at least 4 computers used as gps hosts which
|
||||
will respond and allow trilateration. Three of these hosts should be in a plane,
|
||||
and the fourth should be either above or below the other three. The three in a
|
||||
plane should not be in a line with each other. You can set up hosts using the
|
||||
gps program.
|
||||
|
||||
:::note
|
||||
When entering in the coordinates for the host you need to put in the `x`, `y`,
|
||||
and `z` coordinates of the block that the modem is connected to, not the modem.
|
||||
All modem distances are measured from the block that the modem is placed on.
|
||||
:::
|
||||
> [!NOTE]
|
||||
> When entering in the coordinates for the host you need to put in the `x`, `y`,
|
||||
> and `z` coordinates of the block that the modem is connected to, not the modem.
|
||||
> All modem distances are measured from the block that the modem is placed on.
|
||||
|
||||
Also note that you may choose which axes x, y, or z refers to - so long as your
|
||||
systems have the same definition as any GPS servers that're in range, it works
|
||||
|
@ -74,10 +74,10 @@ handleMetatable = {
|
||||
|
||||
This can be used in a for loop to iterate over all lines of a file
|
||||
|
||||
Once the end of the file has been reached, @{nil} will be returned. The file is
|
||||
Once the end of the file has been reached, [`nil`] will be returned. The file is
|
||||
*not* automatically closed.
|
||||
|
||||
@param ... The argument to pass to @{Handle:read} for each line.
|
||||
@param ... The argument to pass to [`Handle:read`] for each line.
|
||||
@treturn function():string|nil The line iterator.
|
||||
@throws If the file cannot be opened for reading
|
||||
@since 1.3
|
||||
@ -324,14 +324,14 @@ each time it is called, returns a new line from the file.
|
||||
|
||||
This can be used in a for loop to iterate over all lines of a file
|
||||
|
||||
Once the end of the file has been reached, @{nil} will be returned. The file is
|
||||
Once the end of the file has been reached, [`nil`] will be returned. The file is
|
||||
automatically closed.
|
||||
|
||||
If no file name is given, the @{io.input|current input} will be used instead.
|
||||
If no file name is given, the [current input][`io.input`] will be used instead.
|
||||
In this case, the handle is not used.
|
||||
|
||||
@tparam[opt] string filename The name of the file to extract lines from
|
||||
@param ... The argument to pass to @{Handle:read} for each line.
|
||||
@param ... The argument to pass to [`Handle:read`] for each line.
|
||||
@treturn function():string|nil The line iterator.
|
||||
@throws If the file cannot be opened for reading
|
||||
|
||||
@ -362,7 +362,7 @@ function lines(filename, ...)
|
||||
end
|
||||
|
||||
--- Open a file with the given mode, either returning a new file handle
|
||||
-- or @{nil}, plus an error message.
|
||||
-- or [`nil`], plus an error message.
|
||||
--
|
||||
-- The `mode` string can be any of the following:
|
||||
-- - **"r"**: Read mode
|
||||
@ -410,11 +410,11 @@ end
|
||||
|
||||
--- Read from the currently opened input file.
|
||||
--
|
||||
-- This is equivalent to `io.input():read(...)`. See @{Handle:read|the
|
||||
-- documentation} there for full details.
|
||||
-- This is equivalent to `io.input():read(...)`. See [the documentation][`Handle:read`]
|
||||
-- there for full details.
|
||||
--
|
||||
-- @tparam string ... The formats to read, defaulting to a whole line.
|
||||
-- @treturn (string|nil)... The data read, or @{nil} if nothing can be read.
|
||||
-- @treturn (string|nil)... The data read, or [`nil`] if nothing can be read.
|
||||
function read(...)
|
||||
return currentInput:read(...)
|
||||
end
|
||||
@ -438,8 +438,8 @@ end
|
||||
|
||||
--- Write to the currently opened output file.
|
||||
--
|
||||
-- This is equivalent to `io.output():write(...)`. See @{Handle:write|the
|
||||
-- documentation} there for full details.
|
||||
-- This is equivalent to `io.output():write(...)`. See [the documentation][`Handle:write`]
|
||||
-- there for full details.
|
||||
--
|
||||
-- @tparam string ... The strings to write
|
||||
-- @changed 1.81.0 Multiple arguments are now allowed.
|
||||
|
@ -51,7 +51,7 @@ end
|
||||
--
|
||||
-- @tparam string image The string containing the raw-image data.
|
||||
-- @treturn table The parsed image data, suitable for use with
|
||||
-- @{paintutils.drawImage}.
|
||||
-- [`paintutils.drawImage`].
|
||||
-- @since 1.80pr1
|
||||
function parseImage(image)
|
||||
expect(1, image, "string")
|
||||
@ -69,7 +69,7 @@ end
|
||||
-- @tparam string path The file to load.
|
||||
--
|
||||
-- @treturn table|nil The parsed image data, suitable for use with
|
||||
-- @{paintutils.drawImage}, or `nil` if the file does not exist.
|
||||
-- [`paintutils.drawImage`], or `nil` if the file does not exist.
|
||||
-- @usage Load an image and draw it.
|
||||
--
|
||||
-- local image = paintutils.loadImage("data/example.nfp")
|
||||
@ -93,7 +93,7 @@ end
|
||||
--
|
||||
-- @tparam number xPos The x position to draw at, where 1 is the far left.
|
||||
-- @tparam number yPos The y position to draw at, where 1 is the very top.
|
||||
-- @tparam[opt] number colour The @{colors|color} of this pixel. This will be
|
||||
-- @tparam[opt] number colour The [color][`colors`] of this pixel. This will be
|
||||
-- the current background colour if not specified.
|
||||
function drawPixel(xPos, yPos, colour)
|
||||
expect(1, xPos, "number")
|
||||
@ -115,7 +115,7 @@ end
|
||||
-- @tparam number startY The starting y position of the line.
|
||||
-- @tparam number endX The end x position of the line.
|
||||
-- @tparam number endY The end y position of the line.
|
||||
-- @tparam[opt] number colour The @{colors|color} of this pixel. This will be
|
||||
-- @tparam[opt] number colour The [color][`colors`] of this pixel. This will be
|
||||
-- the current background colour if not specified.
|
||||
-- @usage paintutils.drawLine(2, 3, 30, 7, colors.red)
|
||||
function drawLine(startX, startY, endX, endY, colour)
|
||||
@ -189,7 +189,7 @@ end
|
||||
-- @tparam number startY The starting y position of the line.
|
||||
-- @tparam number endX The end x position of the line.
|
||||
-- @tparam number endY The end y position of the line.
|
||||
-- @tparam[opt] number colour The @{colors|color} of this pixel. This will be
|
||||
-- @tparam[opt] number colour The [color][`colors`] of this pixel. This will be
|
||||
-- the current background colour if not specified.
|
||||
-- @usage paintutils.drawBox(2, 3, 30, 7, colors.red)
|
||||
function drawBox(startX, startY, endX, endY, nColour)
|
||||
@ -242,7 +242,7 @@ end
|
||||
-- @tparam number startY The starting y position of the line.
|
||||
-- @tparam number endX The end x position of the line.
|
||||
-- @tparam number endY The end y position of the line.
|
||||
-- @tparam[opt] number colour The @{colors|color} of this pixel. This will be
|
||||
-- @tparam[opt] number colour The [color][`colors`] of this pixel. This will be
|
||||
-- the current background colour if not specified.
|
||||
-- @usage paintutils.drawFilledBox(2, 3, 30, 7, colors.red)
|
||||
function drawFilledBox(startX, startY, endX, endY, nColour)
|
||||
@ -278,7 +278,7 @@ function drawFilledBox(startX, startY, endX, endY, nColour)
|
||||
end
|
||||
end
|
||||
|
||||
--- Draw an image loaded by @{paintutils.parseImage} or @{paintutils.loadImage}.
|
||||
--- Draw an image loaded by [`paintutils.parseImage`] or [`paintutils.loadImage`].
|
||||
--
|
||||
-- @tparam table image The parsed image data.
|
||||
-- @tparam number xPos The x position to start drawing at.
|
||||
|
@ -6,36 +6,34 @@
|
||||
|
||||
Functions are not actually executed simultaneously, but rather this API will
|
||||
automatically switch between them whenever they yield (e.g. whenever they call
|
||||
@{coroutine.yield}, or functions that call that - such as @{os.pullEvent} - or
|
||||
[`coroutine.yield`], or functions that call that - such as [`os.pullEvent`] - or
|
||||
functions that call that, etc - basically, anything that causes the function
|
||||
to "pause").
|
||||
|
||||
Each function executed in "parallel" gets its own copy of the event queue,
|
||||
and so "event consuming" functions (again, mostly anything that causes the
|
||||
script to pause - eg @{os.sleep}, @{rednet.receive}, most of the @{turtle} API,
|
||||
script to pause - eg [`os.sleep`], [`rednet.receive`], most of the [`turtle`] API,
|
||||
etc) can safely be used in one without affecting the event queue accessed by
|
||||
the other.
|
||||
|
||||
|
||||
:::caution
|
||||
When using this API, be careful to pass the functions you want to run in
|
||||
parallel, and _not_ the result of calling those functions.
|
||||
|
||||
For instance, the following is correct:
|
||||
|
||||
```lua
|
||||
local function do_sleep() sleep(1) end
|
||||
parallel.waitForAny(do_sleep, rednet.receive)
|
||||
```
|
||||
|
||||
but the following is **NOT**:
|
||||
|
||||
```lua
|
||||
local function do_sleep() sleep(1) end
|
||||
parallel.waitForAny(do_sleep(), rednet.receive)
|
||||
```
|
||||
|
||||
:::
|
||||
> [!WARNING]
|
||||
> When using this API, be careful to pass the functions you want to run in
|
||||
> parallel, and _not_ the result of calling those functions.
|
||||
>
|
||||
> For instance, the following is correct:
|
||||
>
|
||||
> ```lua
|
||||
> local function do_sleep() sleep(1) end
|
||||
> parallel.waitForAny(do_sleep, rednet.receive)
|
||||
> ```
|
||||
>
|
||||
> but the following is **NOT**:
|
||||
>
|
||||
> ```lua
|
||||
> local function do_sleep() sleep(1) end
|
||||
> parallel.waitForAny(do_sleep(), rednet.receive)
|
||||
> ```
|
||||
|
||||
@module parallel
|
||||
@since 1.2
|
||||
@ -100,7 +98,7 @@ end
|
||||
|
||||
--[[- Switches between execution of the functions, until any of them
|
||||
finishes. If any of the functions errors, the message is propagated upwards
|
||||
from the @{parallel.waitForAny} call.
|
||||
from the [`parallel.waitForAny`] call.
|
||||
|
||||
@tparam function ... The functions this task will run
|
||||
@usage Print a message every second until the `q` key is pressed.
|
||||
@ -128,7 +126,7 @@ end
|
||||
|
||||
--[[- Switches between execution of the functions, until all of them are
|
||||
finished. If any of the functions errors, the message is propagated upwards
|
||||
from the @{parallel.waitForAll} call.
|
||||
from the [`parallel.waitForAll`] call.
|
||||
|
||||
@tparam function ... The functions this task will run
|
||||
@usage Start off two timers and wait for them both to run.
|
||||
|
@ -5,8 +5,8 @@
|
||||
--[[- Find and control peripherals attached to this computer.
|
||||
|
||||
Peripherals are blocks (or turtle and pocket computer upgrades) which can
|
||||
be controlled by a computer. For instance, the @{speaker} peripheral allows a
|
||||
computer to play music and the @{monitor} peripheral allows you to display text
|
||||
be controlled by a computer. For instance, the [`speaker`] peripheral allows a
|
||||
computer to play music and the [`monitor`] peripheral allows you to display text
|
||||
in the world.
|
||||
|
||||
## Referencing peripherals
|
||||
@ -18,10 +18,10 @@ computer will be called `"bottom"` in your Lua code, one to the left called
|
||||
`"right"`, `"front"`, `"back"`).
|
||||
|
||||
You can list the names of all peripherals with the `peripherals` program, or the
|
||||
@{peripheral.getNames} function.
|
||||
[`peripheral.getNames`] function.
|
||||
|
||||
It's also possible to use peripherals which are further away from your computer
|
||||
through the use of @{modem|Wired Modems}. Place one modem against your computer
|
||||
through the use of [Wired Modems][`modem`]. Place one modem against your computer
|
||||
(you may need to sneak and right click), run Networking Cable to your
|
||||
peripheral, and then place another modem against that block. You can then right
|
||||
click the modem to use (or *attach*) the peripheral. This will print a
|
||||
@ -32,24 +32,23 @@ clipboard.
|
||||
## Using peripherals
|
||||
|
||||
Once you have the name of a peripheral, you can call functions on it using the
|
||||
@{peripheral.call} function. This takes the name of our peripheral, the name of
|
||||
[`peripheral.call`] function. This takes the name of our peripheral, the name of
|
||||
the function we want to call, and then its arguments.
|
||||
|
||||
:::info
|
||||
Some bits of the peripheral API call peripheral functions *methods* instead
|
||||
(for example, the @{peripheral.getMethods} function). Don't worry, they're the
|
||||
same thing!
|
||||
:::
|
||||
> [!INFO]
|
||||
> Some bits of the peripheral API call peripheral functions *methods* instead
|
||||
> (for example, the [`peripheral.getMethods`] function). Don't worry, they're the
|
||||
> same thing!
|
||||
|
||||
Let's say we have a monitor above our computer (and so "top") and want to
|
||||
@{monitor.write|write some text to it}. We'd write the following:
|
||||
[write some text to it][`monitor.write`]. We'd write the following:
|
||||
|
||||
```lua
|
||||
peripheral.call("top", "write", "This is displayed on a monitor!")
|
||||
```
|
||||
|
||||
Once you start calling making a couple of peripheral calls this can get very
|
||||
repetitive, and so we can @{peripheral.wrap|wrap} a peripheral. This builds a
|
||||
repetitive, and so we can [wrap][`peripheral.wrap`] a peripheral. This builds a
|
||||
table of all the peripheral's functions so you can use it like an API or module.
|
||||
|
||||
For instance, we could have written the above example as follows:
|
||||
@ -66,7 +65,7 @@ called, you just need to know it's there. For instance, if you're writing a
|
||||
music player, you just need a speaker - it doesn't matter if it's above or below
|
||||
the computer.
|
||||
|
||||
Thankfully there's a quick way to do this: @{peripheral.find}. This takes a
|
||||
Thankfully there's a quick way to do this: [`peripheral.find`]. This takes a
|
||||
*peripheral type* and returns all the attached peripherals which are of this
|
||||
type.
|
||||
|
||||
@ -76,10 +75,10 @@ are just called `"speaker"`, and monitors `"monitor"`. Some peripherals might
|
||||
have more than one type - a Minecraft chest is both a `"minecraft:chest"` and
|
||||
`"inventory"`.
|
||||
|
||||
You can get all the types a peripheral has with @{peripheral.getType}, and check
|
||||
a peripheral is a specific type with @{peripheral.hasType}.
|
||||
You can get all the types a peripheral has with [`peripheral.getType`], and check
|
||||
a peripheral is a specific type with [`peripheral.hasType`].
|
||||
|
||||
To return to our original example, let's use @{peripheral.find} to find an
|
||||
To return to our original example, let's use [`peripheral.find`] to find an
|
||||
attached speaker:
|
||||
|
||||
```lua
|
||||
@ -233,7 +232,7 @@ function getMethods(name)
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Get the name of a peripheral wrapped with @{peripheral.wrap}.
|
||||
--- Get the name of a peripheral wrapped with [`peripheral.wrap`].
|
||||
--
|
||||
-- @tparam table peripheral The peripheral to get the name of.
|
||||
-- @treturn string The name of the given peripheral.
|
||||
@ -274,7 +273,7 @@ function call(name, method, ...)
|
||||
end
|
||||
|
||||
--- Get a table containing all functions available on a peripheral. These can
|
||||
-- then be called instead of using @{peripheral.call} every time.
|
||||
-- then be called instead of using [`peripheral.call`] every time.
|
||||
--
|
||||
-- @tparam string name The name of the peripheral to wrap.
|
||||
-- @treturn table|nil The table containing the peripheral's methods, or `nil` if
|
||||
@ -309,7 +308,7 @@ function wrap(name)
|
||||
end
|
||||
|
||||
--[[- Find all peripherals of a specific type, and return the
|
||||
@{peripheral.wrap|wrapped} peripherals.
|
||||
[wrapped][`peripheral.wrap`] peripherals.
|
||||
|
||||
@tparam string ty The type of peripheral to look for.
|
||||
@tparam[opt] function(name:string, wrapped:table):boolean filter A
|
||||
@ -329,7 +328,7 @@ and returns if it should be included in the result.
|
||||
return modem.isWireless() -- Check this modem is wireless.
|
||||
end) }
|
||||
|
||||
@usage This abuses the `filter` argument to call @{rednet.open} on every modem.
|
||||
@usage This abuses the `filter` argument to call [`rednet.open`] on every modem.
|
||||
|
||||
peripheral.find("modem", rednet.open)
|
||||
@since 1.6
|
||||
|
@ -2,42 +2,41 @@
|
||||
--
|
||||
-- SPDX-License-Identifier: LicenseRef-CCPL
|
||||
|
||||
--[[- Communicate with other computers by using @{modem|modems}. @{rednet}
|
||||
provides a layer of abstraction on top of the main @{modem} peripheral, making
|
||||
--[[- Communicate with other computers by using [modems][`modem`]. [`rednet`]
|
||||
provides a layer of abstraction on top of the main [`modem`] peripheral, making
|
||||
it slightly easier to use.
|
||||
|
||||
## Basic usage
|
||||
In order to send a message between two computers, each computer must have a
|
||||
modem on one of its sides (or in the case of pocket computers and turtles, the
|
||||
modem must be equipped as an upgrade). The two computers should then call
|
||||
@{rednet.open}, which sets up the modems ready to send and receive messages.
|
||||
[`rednet.open`], which sets up the modems ready to send and receive messages.
|
||||
|
||||
Once rednet is opened, you can send messages using @{rednet.send} and receive
|
||||
them using @{rednet.receive}. It's also possible to send a message to _every_
|
||||
rednet-using computer using @{rednet.broadcast}.
|
||||
Once rednet is opened, you can send messages using [`rednet.send`] and receive
|
||||
them using [`rednet.receive`]. It's also possible to send a message to _every_
|
||||
rednet-using computer using [`rednet.broadcast`].
|
||||
|
||||
:::caution Network security
|
||||
|
||||
While rednet provides a friendly way to send messages to specific computers, it
|
||||
doesn't provide any guarantees about security. Other computers could be
|
||||
listening in to your messages, or even pretending to send messages from other computers!
|
||||
|
||||
If you're playing on a multi-player server (or at least one where you don't
|
||||
trust other players), it's worth encrypting or signing your rednet messages.
|
||||
:::
|
||||
> [Network security][!WARNING]
|
||||
>
|
||||
> While rednet provides a friendly way to send messages to specific computers, it
|
||||
> doesn't provide any guarantees about security. Other computers could be
|
||||
> listening in to your messages, or even pretending to send messages from other computers!
|
||||
>
|
||||
> If you're playing on a multi-player server (or at least one where you don't
|
||||
> trust other players), it's worth encrypting or signing your rednet messages.
|
||||
|
||||
## Protocols and hostnames
|
||||
Several rednet messages accept "protocol"s - simple string names describing what
|
||||
a message is about. When sending messages using @{rednet.send} and
|
||||
@{rednet.broadcast}, you can optionally specify a protocol for the message. This
|
||||
same protocol can then be given to @{rednet.receive}, to ignore all messages not
|
||||
a message is about. When sending messages using [`rednet.send`] and
|
||||
[`rednet.broadcast`], you can optionally specify a protocol for the message. This
|
||||
same protocol can then be given to [`rednet.receive`], to ignore all messages not
|
||||
using this protocol.
|
||||
|
||||
It's also possible to look-up computers based on protocols, providing a basic
|
||||
system for service discovery and [DNS]. A computer can advertise that it
|
||||
supports a particular protocol with @{rednet.host}, also providing a friendly
|
||||
supports a particular protocol with [`rednet.host`], also providing a friendly
|
||||
"hostname". Other computers may then find all computers which support this
|
||||
protocol using @{rednet.lookup}.
|
||||
protocol using [`rednet.lookup`].
|
||||
|
||||
[DNS]: https://en.wikipedia.org/wiki/Domain_Name_System "Domain Name System"
|
||||
|
||||
@ -50,7 +49,7 @@ bare-bones but flexible interface.
|
||||
|
||||
local expect = dofile("rom/modules/main/cc/expect.lua").expect
|
||||
|
||||
--- The channel used by the Rednet API to @{broadcast} messages.
|
||||
--- The channel used by the Rednet API to [`broadcast`] messages.
|
||||
CHANNEL_BROADCAST = 65535
|
||||
|
||||
--- The channel used by the Rednet API to repeat messages.
|
||||
@ -68,12 +67,12 @@ local function id_as_channel(id)
|
||||
return (id or os.getComputerID()) % MAX_ID_CHANNELS
|
||||
end
|
||||
|
||||
--[[- Opens a modem with the given @{peripheral} name, allowing it to send and
|
||||
--[[- Opens a modem with the given [`peripheral`] name, allowing it to send and
|
||||
receive messages over rednet.
|
||||
|
||||
This will open the modem on two channels: one which has the same
|
||||
@{os.getComputerID|ID} as the computer, and another on
|
||||
@{CHANNEL_BROADCAST|the broadcast channel}.
|
||||
[ID][`os.getComputerID`] as the computer, and another on
|
||||
[the broadcast channel][`CHANNEL_BROADCAST`].
|
||||
|
||||
@tparam string modem The name of the modem to open.
|
||||
@throws If there is no such modem with the given name
|
||||
@ -83,7 +82,7 @@ rednet messages using it.
|
||||
rednet.open("back")
|
||||
|
||||
@usage Open rednet on all attached modems. This abuses the "filter" argument to
|
||||
@{peripheral.find}.
|
||||
[`peripheral.find`].
|
||||
|
||||
peripheral.find("modem", rednet.open)
|
||||
@see rednet.close
|
||||
@ -98,7 +97,7 @@ function open(modem)
|
||||
peripheral.call(modem, "open", CHANNEL_BROADCAST)
|
||||
end
|
||||
|
||||
--- Close a modem with the given @{peripheral} name, meaning it can no longer
|
||||
--- Close a modem with the given [`peripheral`] name, meaning it can no longer
|
||||
-- send and receive rednet messages.
|
||||
--
|
||||
-- @tparam[opt] string modem The side the modem exists on. If not given, all
|
||||
@ -151,21 +150,21 @@ end
|
||||
|
||||
--[[- Allows a computer or turtle with an attached modem to send a message
|
||||
intended for a sycomputer with a specific ID. At least one such modem must first
|
||||
be @{rednet.open|opened} before sending is possible.
|
||||
be [opened][`rednet.open`] before sending is possible.
|
||||
|
||||
Assuming the target was in range and also had a correctly opened modem, the
|
||||
target computer may then use @{rednet.receive} to collect the message.
|
||||
target computer may then use [`rednet.receive`] to collect the message.
|
||||
|
||||
@tparam number recipient The ID of the receiving computer.
|
||||
@param message The message to send. Like with @{modem.transmit}, this can
|
||||
@param message The message to send. Like with [`modem.transmit`], this can
|
||||
contain any primitive type (numbers, booleans and strings) as well as
|
||||
tables. Other types (like functions), as well as metatables, will not be
|
||||
transmitted.
|
||||
@tparam[opt] string protocol The "protocol" to send this message under. When
|
||||
using @{rednet.receive} one can filter to only receive messages sent under a
|
||||
using [`rednet.receive`] one can filter to only receive messages sent under a
|
||||
particular protocol.
|
||||
@treturn boolean If this message was successfully sent (i.e. if rednet is
|
||||
currently @{rednet.open|open}). Note, this does not guarantee the message was
|
||||
currently [open][`rednet.open`]). Note, this does not guarantee the message was
|
||||
actually _received_.
|
||||
@changed 1.6 Added protocol parameter.
|
||||
@changed 1.82.0 Now returns whether the message was successfully sent.
|
||||
@ -217,13 +216,13 @@ function send(recipient, message, protocol)
|
||||
return sent
|
||||
end
|
||||
|
||||
--[[- Broadcasts a string message over the predefined @{CHANNEL_BROADCAST}
|
||||
--[[- Broadcasts a string message over the predefined [`CHANNEL_BROADCAST`]
|
||||
channel. The message will be received by every device listening to rednet.
|
||||
|
||||
@param message The message to send. This should not contain coroutines or
|
||||
functions, as they will be converted to @{nil}.
|
||||
functions, as they will be converted to [`nil`].
|
||||
@tparam[opt] string protocol The "protocol" to send this message under. When
|
||||
using @{rednet.receive} one can filter to only receive messages sent under a
|
||||
using [`rednet.receive`] one can filter to only receive messages sent under a
|
||||
particular protocol.
|
||||
@see rednet.receive
|
||||
@changed 1.6 Added protocol parameter.
|
||||
@ -311,7 +310,7 @@ function receive(protocol_filter, timeout)
|
||||
end
|
||||
|
||||
--[[- Register the system as "hosting" the desired protocol under the specified
|
||||
name. If a rednet @{rednet.lookup|lookup} is performed for that protocol (and
|
||||
name. If a rednet [lookup][`rednet.lookup`] is performed for that protocol (and
|
||||
maybe name) on the same network, the registered system will automatically
|
||||
respond via a background process, hence providing the system performing the
|
||||
lookup with its ID number.
|
||||
@ -343,8 +342,8 @@ function host(protocol, hostname)
|
||||
end
|
||||
end
|
||||
|
||||
--- Stop @{rednet.host|hosting} a specific protocol, meaning it will no longer
|
||||
-- respond to @{rednet.lookup} requests.
|
||||
--- Stop [hosting][`rednet.host`] a specific protocol, meaning it will no longer
|
||||
-- respond to [`rednet.lookup`] requests.
|
||||
--
|
||||
-- @tparam string protocol The protocol to unregister your self from.
|
||||
-- @since 1.6
|
||||
@ -353,7 +352,7 @@ function unhost(protocol)
|
||||
hostnames[protocol] = nil
|
||||
end
|
||||
|
||||
--[[- Search the local rednet network for systems @{rednet.host|hosting} the
|
||||
--[[- Search the local rednet network for systems [hosting][`rednet.host`] the
|
||||
desired protocol and returns any computer IDs that respond as "registered"
|
||||
against it.
|
||||
|
||||
@ -365,7 +364,7 @@ match is found).
|
||||
|
||||
@treturn[1] number... A list of computer IDs hosting the given protocol.
|
||||
@treturn[2] number|nil The computer ID with the provided hostname and protocol,
|
||||
or @{nil} if none exists.
|
||||
or [`nil`] if none exists.
|
||||
@since 1.6
|
||||
@usage Find all computers which are hosting the `"chat"` protocol.
|
||||
|
||||
@ -450,7 +449,7 @@ end
|
||||
local started = false
|
||||
|
||||
--- Listen for modem messages and converts them into rednet messages, which may
|
||||
-- then be @{receive|received}.
|
||||
-- then be [received][`receive`].
|
||||
--
|
||||
-- This is automatically started in the background on computer startup, and
|
||||
-- should not be called manually.
|
||||
|
@ -2,13 +2,31 @@
|
||||
--
|
||||
-- SPDX-License-Identifier: LicenseRef-CCPL
|
||||
|
||||
--- Read and write configuration options for CraftOS and your programs.
|
||||
--
|
||||
-- By default, the settings API will load its configuration from the
|
||||
-- `/.settings` file. One can then use @{settings.save} to update the file.
|
||||
--
|
||||
-- @module settings
|
||||
-- @since 1.78
|
||||
--[[- Read and write configuration options for CraftOS and your programs.
|
||||
|
||||
When a computer starts, it reads the current value of settings from the
|
||||
`/.settings` file. These values then may be [read][`settings.get`] or
|
||||
[modified][`settings.set`].
|
||||
|
||||
> [!WARNING]
|
||||
> Calling [`settings.set`] does _not_ update the settings file by default. You
|
||||
> _must_ call [`settings.save`] to persist values.
|
||||
|
||||
@module settings
|
||||
@since 1.78
|
||||
@usage Define an basic setting `123` and read its value.
|
||||
|
||||
settings.define("my.setting", {
|
||||
description = "An example setting",
|
||||
default = 123,
|
||||
type = number,
|
||||
})
|
||||
print("my.setting = " .. settings.get("my.setting")) -- 123
|
||||
|
||||
You can then use the `set` program to change its value (e.g. `set my.setting 456`),
|
||||
and then re-run the `example` program to check it has changed.
|
||||
|
||||
]]
|
||||
|
||||
local expect = dofile("rom/modules/main/cc/expect.lua")
|
||||
local type, expect, field = type, expect.expect, expect.field
|
||||
@ -40,9 +58,9 @@ for _, v in ipairs(valid_types) do valid_types[v] = true end
|
||||
-- Options for this setting. This table accepts the following fields:
|
||||
--
|
||||
-- - `description`: A description which may be printed when running the `set` program.
|
||||
-- - `default`: A default value, which is returned by @{settings.get} if the
|
||||
-- - `default`: A default value, which is returned by [`settings.get`] if the
|
||||
-- setting has not been changed.
|
||||
-- - `type`: Require values to be of this type. @{set|Setting} the value to another type
|
||||
-- - `type`: Require values to be of this type. [Setting][`set`] the value to another type
|
||||
-- will error.
|
||||
-- @since 1.87.0
|
||||
function define(name, options)
|
||||
@ -66,9 +84,9 @@ function define(name, options)
|
||||
details[name] = options
|
||||
end
|
||||
|
||||
--- Remove a @{define|definition} of a setting.
|
||||
--- Remove a [definition][`define`] of a setting.
|
||||
--
|
||||
-- If a setting has been changed, this does not remove its value. Use @{settings.unset}
|
||||
-- If a setting has been changed, this does not remove its value. Use [`settings.unset`]
|
||||
-- for that.
|
||||
--
|
||||
-- @tparam string name The name of this option
|
||||
@ -92,13 +110,18 @@ local function set_value(name, new)
|
||||
end
|
||||
end
|
||||
|
||||
--- Set the value of a setting.
|
||||
--
|
||||
-- @tparam string name The name of the setting to set
|
||||
-- @param value The setting's value. This cannot be `nil`, and must be
|
||||
-- serialisable by @{textutils.serialize}.
|
||||
-- @throws If this value cannot be serialised
|
||||
-- @see settings.unset
|
||||
--[[- Set the value of a setting.
|
||||
|
||||
> [!WARNING]
|
||||
> Calling [`settings.set`] does _not_ update the settings file by default. You
|
||||
> _must_ call [`settings.save`] to persist values.
|
||||
|
||||
@tparam string name The name of the setting to set
|
||||
@param value The setting's value. This cannot be `nil`, and must be
|
||||
serialisable by [`textutils.serialize`].
|
||||
@throws If this value cannot be serialised
|
||||
@see settings.unset
|
||||
]]
|
||||
function set(name, value)
|
||||
expect(1, name, "string")
|
||||
expect(2, value, "number", "string", "boolean", "table")
|
||||
@ -134,7 +157,7 @@ end
|
||||
--
|
||||
-- @tparam string name The name of the setting to get.
|
||||
-- @treturn { description? = string, default? = any, type? = string, value? = any }
|
||||
-- Information about this setting. This includes all information from @{settings.define},
|
||||
-- Information about this setting. This includes all information from [`settings.define`],
|
||||
-- as well as this setting's value.
|
||||
-- @since 1.87.0
|
||||
function getDetails(name)
|
||||
@ -148,8 +171,8 @@ end
|
||||
|
||||
--- Remove the value of a setting, setting it to the default.
|
||||
--
|
||||
-- @{settings.get} will return the default value until the setting's value is
|
||||
-- @{settings.set|set}, or the computer is rebooted.
|
||||
-- [`settings.get`] will return the default value until the setting's value is
|
||||
-- [set][`settings.set`], or the computer is rebooted.
|
||||
--
|
||||
-- @tparam string name The name of the setting to unset.
|
||||
-- @see settings.set
|
||||
@ -159,7 +182,7 @@ function unset(name)
|
||||
set_value(name, nil)
|
||||
end
|
||||
|
||||
--- Resets the value of all settings. Equivalent to calling @{settings.unset}
|
||||
--- Resets the value of all settings. Equivalent to calling [`settings.unset`]
|
||||
--- on every setting.
|
||||
--
|
||||
-- @see settings.unset
|
||||
|
@ -17,9 +17,9 @@ end
|
||||
|
||||
local term = _ENV
|
||||
|
||||
--- Redirects terminal output to a monitor, a @{window}, or any other custom
|
||||
--- Redirects terminal output to a monitor, a [`window`], or any other custom
|
||||
-- terminal object. Once the redirect is performed, any calls to a "term"
|
||||
-- function - or to a function that makes use of a term function, as @{print} -
|
||||
-- function - or to a function that makes use of a term function, as [`print`] -
|
||||
-- will instead operate with the new terminal object.
|
||||
--
|
||||
-- A "terminal object" is simply a table that contains functions with the same
|
||||
@ -29,9 +29,9 @@ local term = _ENV
|
||||
-- The redirect can be undone by pointing back to the previous terminal object
|
||||
-- (which this function returns whenever you switch).
|
||||
--
|
||||
-- @tparam Redirect target The terminal redirect the @{term} API will draw to.
|
||||
-- @tparam Redirect target The terminal redirect the [`term`] API will draw to.
|
||||
-- @treturn Redirect The previous redirect object, as returned by
|
||||
-- @{term.current}.
|
||||
-- [`term.current`].
|
||||
-- @since 1.31
|
||||
-- @usage
|
||||
-- Redirect to a monitor on the right of the computer.
|
||||
@ -60,7 +60,7 @@ end
|
||||
-- @treturn Redirect The current terminal redirect
|
||||
-- @since 1.6
|
||||
-- @usage
|
||||
-- Create a new @{window} which draws to the current redirect target.
|
||||
-- Create a new [`window`] which draws to the current redirect target.
|
||||
--
|
||||
-- window.create(term.current(), 1, 1, 10, 10)
|
||||
term.current = function()
|
||||
@ -70,7 +70,7 @@ end
|
||||
--- Get the native terminal object of the current computer.
|
||||
--
|
||||
-- It is recommended you do not use this function unless you absolutely have
|
||||
-- to. In a multitasked environment, @{term.native} will _not_ be the current
|
||||
-- to. In a multitasked environment, [`term.native`] will _not_ be the current
|
||||
-- terminal object, and so drawing may interfere with other programs.
|
||||
--
|
||||
-- @treturn Redirect The native terminal redirect.
|
||||
|
@ -14,7 +14,7 @@ local wrap = dofile("rom/modules/main/cc/strings.lua").wrap
|
||||
--- Slowly writes string text at current cursor position,
|
||||
-- character-by-character.
|
||||
--
|
||||
-- Like @{_G.write}, this does not insert a newline at the end.
|
||||
-- Like [`_G.write`], this does not insert a newline at the end.
|
||||
--
|
||||
-- @tparam string text The the text to write to the screen
|
||||
-- @tparam[opt] number rate The number of characters to write each second,
|
||||
@ -42,7 +42,7 @@ end
|
||||
--- Slowly prints string text at current cursor position,
|
||||
-- character-by-character.
|
||||
--
|
||||
-- Like @{print}, this inserts a newline after printing.
|
||||
-- Like [`print`], this inserts a newline after printing.
|
||||
--
|
||||
-- @tparam string sText The the text to write to the screen
|
||||
-- @tparam[opt] number nRate The number of characters to write each second,
|
||||
@ -56,7 +56,7 @@ end
|
||||
|
||||
--- Takes input time and formats it in a more readable format such as `6:30 PM`.
|
||||
--
|
||||
-- @tparam number nTime The time to format, as provided by @{os.time}.
|
||||
-- @tparam number nTime The time to format, as provided by [`os.time`].
|
||||
-- @tparam[opt] boolean bTwentyFourHour Whether to format this as a 24-hour
|
||||
-- clock (`18:30`) rather than a 12-hour one (`6:30 AM`)
|
||||
-- @treturn string The formatted time
|
||||
@ -114,7 +114,7 @@ end
|
||||
--[[- Prints a given string to the display.
|
||||
|
||||
If the action can be completed without scrolling, it acts much the same as
|
||||
@{print}; otherwise, it will throw up a "Press any key to continue" prompt at
|
||||
[`print`]; otherwise, it will throw up a "Press any key to continue" prompt at
|
||||
the bottom of the display. Each press will cause it to scroll down and write a
|
||||
single line more before prompting again, if need be.
|
||||
|
||||
@ -253,7 +253,7 @@ end
|
||||
--[[- Prints tables in a structured form, stopping and prompting for input should
|
||||
the result not fit on the terminal.
|
||||
|
||||
This functions identically to @{textutils.tabulate}, but will prompt for user
|
||||
This functions identically to [`textutils.tabulate`], but will prompt for user
|
||||
input should the whole output not fit on the display.
|
||||
|
||||
@tparam {string...}|number ... The rows and text colors to display.
|
||||
@ -424,12 +424,31 @@ do
|
||||
if map[c] == nil then map[c] = hexify(c) end
|
||||
end
|
||||
|
||||
serializeJSONString = function(s)
|
||||
return ('"%s"'):format(s:gsub("[\0-\x1f\"\\]", map):gsub("[\x7f-\xff]", hexify))
|
||||
serializeJSONString = function(s, options)
|
||||
if options and options.unicode_strings and s:find("[\x80-\xff]") then
|
||||
local retval = '"'
|
||||
for _, code in utf8.codes(s) do
|
||||
if code > 0xFFFF then
|
||||
-- Encode the codepoint as a UTF-16 surrogate pair
|
||||
code = code - 0x10000
|
||||
local high, low = bit32.extract(code, 10, 10) + 0xD800, bit32.extract(code, 0, 10) + 0xDC00
|
||||
retval = retval .. ("\\u%04X\\u%04X"):format(high, low)
|
||||
elseif code <= 0x5C and map[string.char(code)] then -- 0x5C = `\`, don't run `string.char` if we don't need to
|
||||
retval = retval .. map[string.char(code)]
|
||||
elseif code < 0x20 or code >= 0x7F then
|
||||
retval = retval .. ("\\u%04X"):format(code)
|
||||
else
|
||||
retval = retval .. string.char(code)
|
||||
end
|
||||
end
|
||||
return retval .. '"'
|
||||
else
|
||||
return ('"%s"'):format(s:gsub("[\0-\x1f\"\\]", map):gsub("[\x7f-\xff]", hexify))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function serializeJSONImpl(t, tTracking, bNBTStyle)
|
||||
local function serializeJSONImpl(t, tTracking, options)
|
||||
local sType = type(t)
|
||||
if t == empty_json_array then return "[]"
|
||||
elseif t == json_null then return "null"
|
||||
@ -450,13 +469,14 @@ local function serializeJSONImpl(t, tTracking, bNBTStyle)
|
||||
local nObjectSize = 0
|
||||
local nArraySize = 0
|
||||
local largestArrayIndex = 0
|
||||
local bNBTStyle = options and options.nbt_style
|
||||
for k, v in pairs(t) do
|
||||
if type(k) == "string" then
|
||||
local sEntry
|
||||
if bNBTStyle then
|
||||
sEntry = tostring(k) .. ":" .. serializeJSONImpl(v, tTracking, bNBTStyle)
|
||||
sEntry = tostring(k) .. ":" .. serializeJSONImpl(v, tTracking, options)
|
||||
else
|
||||
sEntry = serializeJSONString(k) .. ":" .. serializeJSONImpl(v, tTracking, bNBTStyle)
|
||||
sEntry = serializeJSONString(k, options) .. ":" .. serializeJSONImpl(v, tTracking, options)
|
||||
end
|
||||
if nObjectSize == 0 then
|
||||
sObjectResult = sObjectResult .. sEntry
|
||||
@ -473,7 +493,7 @@ local function serializeJSONImpl(t, tTracking, bNBTStyle)
|
||||
if t[k] == nil then --if the array is nil at index k the value is "null" as to keep the unused indexes in between used ones.
|
||||
sEntry = "null"
|
||||
else -- if the array index does not point to a nil we serialise it's content.
|
||||
sEntry = serializeJSONImpl(t[k], tTracking, bNBTStyle)
|
||||
sEntry = serializeJSONImpl(t[k], tTracking, options)
|
||||
end
|
||||
if nArraySize == 0 then
|
||||
sArrayResult = sArrayResult .. sEntry
|
||||
@ -492,7 +512,7 @@ local function serializeJSONImpl(t, tTracking, bNBTStyle)
|
||||
end
|
||||
|
||||
elseif sType == "string" then
|
||||
return serializeJSONString(t)
|
||||
return serializeJSONString(t, options)
|
||||
|
||||
elseif sType == "number" or sType == "boolean" then
|
||||
return tostring(t)
|
||||
@ -682,13 +702,13 @@ do
|
||||
|
||||
--[[- Converts a serialised JSON string back into a reassembled Lua object.
|
||||
|
||||
This may be used with @{textutils.serializeJSON}, or when communicating
|
||||
This may be used with [`textutils.serializeJSON`], or when communicating
|
||||
with command blocks or web APIs.
|
||||
|
||||
If a `null` value is encountered, it is converted into `nil`. It can be converted
|
||||
into @{textutils.json_null} with the `parse_null` option.
|
||||
into [`textutils.json_null`] with the `parse_null` option.
|
||||
|
||||
If an empty array is encountered, it is converted into @{textutils.empty_json_array}.
|
||||
If an empty array is encountered, it is converted into [`textutils.empty_json_array`].
|
||||
It can be converted into a new empty table with the `parse_empty_array` option.
|
||||
|
||||
@tparam string s The serialised string to deserialise.
|
||||
@ -697,12 +717,12 @@ do
|
||||
|
||||
- `nbt_style`: When true, this will accept [stringified NBT][nbt] strings,
|
||||
as produced by many commands.
|
||||
- `parse_null`: When true, `null` will be parsed as @{json_null}, rather than
|
||||
- `parse_null`: When true, `null` will be parsed as [`json_null`], rather than
|
||||
`nil`.
|
||||
- `parse_empty_array`: When false, empty arrays will be parsed as a new table.
|
||||
By default (or when this value is true), they are parsed as @{empty_json_array}.
|
||||
By default (or when this value is true), they are parsed as [`empty_json_array`].
|
||||
|
||||
[nbt]: https://minecraft.gamepedia.com/NBT_format
|
||||
[nbt]: https://minecraft.wiki/w/NBT_format
|
||||
@return[1] The deserialised object
|
||||
@treturn[2] nil If the object could not be deserialised.
|
||||
@treturn string A message describing why the JSON string is invalid.
|
||||
@ -714,7 +734,7 @@ do
|
||||
|
||||
textutils.unserialiseJSON('{"name": "Steve", "age": null}')
|
||||
|
||||
@usage Unserialise a basic JSON object, returning null values as @{json_null}.
|
||||
@usage Unserialise a basic JSON object, returning null values as [`json_null`].
|
||||
|
||||
textutils.unserialiseJSON('{"name": "Steve", "age": null}', { parse_null = true })
|
||||
]]
|
||||
@ -793,7 +813,7 @@ serialise = serialize -- GB version
|
||||
|
||||
--- Converts a serialised string back into a reassembled Lua object.
|
||||
--
|
||||
-- This is mainly used together with @{textutils.serialise}.
|
||||
-- This is mainly used together with [`textutils.serialise`].
|
||||
--
|
||||
-- @tparam string s The serialised string to deserialise.
|
||||
-- @return[1] The deserialised object
|
||||
@ -813,32 +833,57 @@ end
|
||||
|
||||
unserialise = unserialize -- GB version
|
||||
|
||||
--- Returns a JSON representation of the given data.
|
||||
--
|
||||
-- This function attempts to guess whether a table is a JSON array or
|
||||
-- object. However, empty tables are assumed to be empty objects - use
|
||||
-- @{textutils.empty_json_array} to mark an empty array.
|
||||
--
|
||||
-- This is largely intended for interacting with various functions from the
|
||||
-- @{commands} API, though may also be used in making @{http} requests.
|
||||
--
|
||||
-- @param t The value to serialise. Like @{textutils.serialise}, this should not
|
||||
-- contain recursive tables or functions.
|
||||
-- @tparam[opt] boolean bNBTStyle Whether to produce NBT-style JSON (non-quoted keys)
|
||||
-- instead of standard JSON.
|
||||
-- @treturn string The JSON representation of the input.
|
||||
-- @throws If the object contains a value which cannot be
|
||||
-- serialised. This includes functions and tables which appear multiple
|
||||
-- times.
|
||||
-- @usage textutils.serialiseJSON({ values = { 1, "2", true } })
|
||||
-- @since 1.7
|
||||
-- @see textutils.json_null Use to serialise a JSON `null` value.
|
||||
-- @see textutils.empty_json_array Use to serialise a JSON empty array.
|
||||
function serializeJSON(t, bNBTStyle)
|
||||
--[[- Returns a JSON representation of the given data.
|
||||
|
||||
This function attempts to guess whether a table is a JSON array or
|
||||
object. However, empty tables are assumed to be empty objects - use
|
||||
[`textutils.empty_json_array`] to mark an empty array.
|
||||
|
||||
This is largely intended for interacting with various functions from the
|
||||
[`commands`] API, though may also be used in making [`http`] requests.
|
||||
|
||||
@param[1] t The value to serialise. Like [`textutils.serialise`], this should not
|
||||
contain recursive tables or functions.
|
||||
@tparam[1,opt] { nbt_style? = boolean, unicode_strings? = boolean } options Options for serialisation.
|
||||
- `nbt_style`: Whether to produce NBT-style JSON (non-quoted keys) instead of standard JSON.
|
||||
- `unicode_strings`: Whether to treat strings as containing UTF-8 characters instead of
|
||||
using the default 8-bit character set.
|
||||
|
||||
@param[2] t The value to serialise. Like [`textutils.serialise`], this should not
|
||||
contain recursive tables or functions.
|
||||
@tparam[2] boolean bNBTStyle Whether to produce NBT-style JSON (non-quoted keys)
|
||||
instead of standard JSON.
|
||||
|
||||
@treturn string The JSON representation of the input.
|
||||
@throws If the object contains a value which cannot be serialised. This includes
|
||||
functions and tables which appear multiple times.
|
||||
|
||||
@usage Serialise a simple object
|
||||
|
||||
textutils.serialiseJSON({ values = { 1, "2", true } })
|
||||
|
||||
@usage Serialise an object to a NBT-style string
|
||||
|
||||
textutils.serialiseJSON({ values = { 1, "2", true } }, { nbt_style = true })
|
||||
|
||||
@since 1.7
|
||||
@changed 1.106.0 Added `options` overload and `unicode_strings` option.
|
||||
|
||||
@see textutils.json_null Use to serialise a JSON `null` value.
|
||||
@see textutils.empty_json_array Use to serialise a JSON empty array.
|
||||
]]
|
||||
function serializeJSON(t, options)
|
||||
expect(1, t, "table", "string", "number", "boolean")
|
||||
expect(2, bNBTStyle, "boolean", "nil")
|
||||
expect(2, options, "table", "boolean", "nil")
|
||||
if type(options) == "boolean" then
|
||||
options = { nbt_style = options }
|
||||
elseif type(options) == "table" then
|
||||
field(options, "nbt_style", "boolean", "nil")
|
||||
field(options, "unicode_strings", "boolean", "nil")
|
||||
end
|
||||
|
||||
local tTracking = {}
|
||||
return serializeJSONImpl(t, tTracking, bNBTStyle or false)
|
||||
return serializeJSONImpl(t, tTracking, options)
|
||||
end
|
||||
|
||||
serialiseJSON = serializeJSON -- GB version
|
||||
@ -884,7 +929,7 @@ local tEmpty = {}
|
||||
-- variable name or table index.
|
||||
--
|
||||
-- @tparam[opt] table tSearchTable The table to find variables in, defaulting to
|
||||
-- the global environment (@{_G}). The function also searches the "parent"
|
||||
-- the global environment ([`_G`]). The function also searches the "parent"
|
||||
-- environment via the `__index` metatable field.
|
||||
--
|
||||
-- @treturn { string... } The (possibly empty) list of completions.
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
--- A basic 3D vector type and some common vector operations. This may be useful
|
||||
-- when working with coordinates in Minecraft's world (such as those from the
|
||||
-- @{gps} API).
|
||||
-- [`gps`] API).
|
||||
--
|
||||
-- An introduction to vectors can be found on [Wikipedia][wiki].
|
||||
--
|
||||
@ -180,7 +180,7 @@ local vmetatable = {
|
||||
__eq = vector.equals,
|
||||
}
|
||||
|
||||
--- Construct a new @{Vector} with the given coordinates.
|
||||
--- Construct a new [`Vector`] with the given coordinates.
|
||||
--
|
||||
-- @tparam number x The X coordinate or direction of the vector.
|
||||
-- @tparam number y The Y coordinate or direction of the vector.
|
||||
|
@ -2,10 +2,10 @@
|
||||
--
|
||||
-- SPDX-License-Identifier: LicenseRef-CCPL
|
||||
|
||||
--[[- A @{term.Redirect|terminal redirect} occupying a smaller area of an
|
||||
--[[- A [terminal redirect][`term.Redirect`] occupying a smaller area of an
|
||||
existing terminal. This allows for easy definition of spaces within the display
|
||||
that can be written/drawn to, then later redrawn/repositioned/etc as need
|
||||
be. The API itself contains only one function, @{window.create}, which returns
|
||||
be. The API itself contains only one function, [`window.create`], which returns
|
||||
the windows themselves.
|
||||
|
||||
Windows are considered terminal objects - as such, they have access to nearly
|
||||
@ -60,11 +60,11 @@ local string_sub = string.sub
|
||||
|
||||
--[[- Returns a terminal object that is a space within the specified parent
|
||||
terminal object. This can then be used (or even redirected to) in the same
|
||||
manner as eg a wrapped monitor. Refer to @{term|the term API} for a list of
|
||||
manner as eg a wrapped monitor. Refer to [the term API][`term`] for a list of
|
||||
functions available to it.
|
||||
|
||||
@{term} itself may not be passed as the parent, though @{term.native} is
|
||||
acceptable. Generally, @{term.current} or a wrapped monitor will be most
|
||||
[`term`] itself may not be passed as the parent, though [`term.native`] is
|
||||
acceptable. Generally, [`term.current`] or a wrapped monitor will be most
|
||||
suitable, though windows may even have other windows assigned as their
|
||||
parents.
|
||||
|
||||
@ -131,11 +131,7 @@ function create(parent, nX, nY, nWidth, nHeight, bStartVisible)
|
||||
local sEmptyTextColor = tEmptyColorLines[nTextColor]
|
||||
local sEmptyBackgroundColor = tEmptyColorLines[nBackgroundColor]
|
||||
for y = 1, nHeight do
|
||||
tLines[y] = {
|
||||
text = sEmptyText,
|
||||
textColor = sEmptyTextColor,
|
||||
backgroundColor = sEmptyBackgroundColor,
|
||||
}
|
||||
tLines[y] = { sEmptyText, sEmptyTextColor, sEmptyBackgroundColor }
|
||||
end
|
||||
|
||||
for i = 0, 15 do
|
||||
@ -165,7 +161,7 @@ function create(parent, nX, nY, nWidth, nHeight, bStartVisible)
|
||||
local function redrawLine(n)
|
||||
local tLine = tLines[n]
|
||||
parent.setCursorPos(nX, nY + n - 1)
|
||||
parent.blit(tLine.text, tLine.textColor, tLine.backgroundColor)
|
||||
parent.blit(tLine[1], tLine[2], tLine[3])
|
||||
end
|
||||
|
||||
local function redraw()
|
||||
@ -188,9 +184,9 @@ function create(parent, nX, nY, nWidth, nHeight, bStartVisible)
|
||||
-- Modify line
|
||||
local tLine = tLines[nCursorY]
|
||||
if nStart == 1 and nEnd == nWidth then
|
||||
tLine.text = sText
|
||||
tLine.textColor = sTextColor
|
||||
tLine.backgroundColor = sBackgroundColor
|
||||
tLine[1] = sText
|
||||
tLine[2] = sTextColor
|
||||
tLine[3] = sBackgroundColor
|
||||
else
|
||||
local sClippedText, sClippedTextColor, sClippedBackgroundColor
|
||||
if nStart < 1 then
|
||||
@ -210,9 +206,9 @@ function create(parent, nX, nY, nWidth, nHeight, bStartVisible)
|
||||
sClippedBackgroundColor = sBackgroundColor
|
||||
end
|
||||
|
||||
local sOldText = tLine.text
|
||||
local sOldTextColor = tLine.textColor
|
||||
local sOldBackgroundColor = tLine.backgroundColor
|
||||
local sOldText = tLine[1]
|
||||
local sOldTextColor = tLine[2]
|
||||
local sOldBackgroundColor = tLine[3]
|
||||
local sNewText, sNewTextColor, sNewBackgroundColor
|
||||
if nStart > 1 then
|
||||
local nOldEnd = nStart - 1
|
||||
@ -231,9 +227,9 @@ function create(parent, nX, nY, nWidth, nHeight, bStartVisible)
|
||||
sNewBackgroundColor = sNewBackgroundColor .. string_sub(sOldBackgroundColor, nOldStart, nWidth)
|
||||
end
|
||||
|
||||
tLine.text = sNewText
|
||||
tLine.textColor = sNewTextColor
|
||||
tLine.backgroundColor = sNewBackgroundColor
|
||||
tLine[1] = sNewText
|
||||
tLine[2] = sNewTextColor
|
||||
tLine[3] = sNewBackgroundColor
|
||||
end
|
||||
|
||||
-- Redraw line
|
||||
@ -251,7 +247,7 @@ function create(parent, nX, nY, nWidth, nHeight, bStartVisible)
|
||||
end
|
||||
end
|
||||
|
||||
--- The window object. Refer to the @{window|module's documentation} for
|
||||
--- The window object. Refer to the [module's documentation][`window`] for
|
||||
-- a full description.
|
||||
--
|
||||
-- @type Window
|
||||
@ -280,11 +276,10 @@ function create(parent, nX, nY, nWidth, nHeight, bStartVisible)
|
||||
local sEmptyTextColor = tEmptyColorLines[nTextColor]
|
||||
local sEmptyBackgroundColor = tEmptyColorLines[nBackgroundColor]
|
||||
for y = 1, nHeight do
|
||||
tLines[y] = {
|
||||
text = sEmptyText,
|
||||
textColor = sEmptyTextColor,
|
||||
backgroundColor = sEmptyBackgroundColor,
|
||||
}
|
||||
local line = tLines[y]
|
||||
line[1] = sEmptyText
|
||||
line[2] = sEmptyTextColor
|
||||
line[3] = sEmptyBackgroundColor
|
||||
end
|
||||
if bVisible then
|
||||
redraw()
|
||||
@ -295,14 +290,10 @@ function create(parent, nX, nY, nWidth, nHeight, bStartVisible)
|
||||
|
||||
function window.clearLine()
|
||||
if nCursorY >= 1 and nCursorY <= nHeight then
|
||||
local sEmptyText = sEmptySpaceLine
|
||||
local sEmptyTextColor = tEmptyColorLines[nTextColor]
|
||||
local sEmptyBackgroundColor = tEmptyColorLines[nBackgroundColor]
|
||||
tLines[nCursorY] = {
|
||||
text = sEmptyText,
|
||||
textColor = sEmptyTextColor,
|
||||
backgroundColor = sEmptyBackgroundColor,
|
||||
}
|
||||
local line = tLines[nCursorY]
|
||||
line[1] = sEmptySpaceLine
|
||||
line[2] = tEmptyColorLines[nTextColor]
|
||||
line[3] = tEmptyColorLines[nBackgroundColor]
|
||||
if bVisible then
|
||||
redrawLine(nCursorY)
|
||||
updateCursorColor()
|
||||
@ -431,11 +422,7 @@ function create(parent, nX, nY, nWidth, nHeight, bStartVisible)
|
||||
if y >= 1 and y <= nHeight then
|
||||
tNewLines[newY] = tLines[y]
|
||||
else
|
||||
tNewLines[newY] = {
|
||||
text = sEmptyText,
|
||||
textColor = sEmptyTextColor,
|
||||
backgroundColor = sEmptyBackgroundColor,
|
||||
}
|
||||
tNewLines[newY] = { sEmptyText, sEmptyTextColor, sEmptyBackgroundColor }
|
||||
end
|
||||
end
|
||||
tLines = tNewLines
|
||||
@ -467,8 +454,8 @@ function create(parent, nX, nY, nWidth, nHeight, bStartVisible)
|
||||
--
|
||||
-- @tparam number y The y position of the line to get.
|
||||
-- @treturn string The textual content of this line.
|
||||
-- @treturn string The text colours of this line, suitable for use with @{term.blit}.
|
||||
-- @treturn string The background colours of this line, suitable for use with @{term.blit}.
|
||||
-- @treturn string The text colours of this line, suitable for use with [`term.blit`].
|
||||
-- @treturn string The background colours of this line, suitable for use with [`term.blit`].
|
||||
-- @throws If `y` is not between 1 and this window's height.
|
||||
-- @since 1.84.0
|
||||
function window.getLine(y)
|
||||
@ -478,7 +465,8 @@ function create(parent, nX, nY, nWidth, nHeight, bStartVisible)
|
||||
error("Line is out of range.", 2)
|
||||
end
|
||||
|
||||
return tLines[y].text, tLines[y].textColor, tLines[y].backgroundColor
|
||||
local line = tLines[y]
|
||||
return line[1], line[2], line[3]
|
||||
end
|
||||
|
||||
-- Other functions
|
||||
@ -574,26 +562,22 @@ function create(parent, nX, nY, nWidth, nHeight, bStartVisible)
|
||||
local sEmptyBackgroundColor = tEmptyColorLines[nBackgroundColor]
|
||||
for y = 1, new_height do
|
||||
if y > nHeight then
|
||||
tNewLines[y] = {
|
||||
text = sEmptyText,
|
||||
textColor = sEmptyTextColor,
|
||||
backgroundColor = sEmptyBackgroundColor,
|
||||
}
|
||||
tNewLines[y] = { sEmptyText, sEmptyTextColor, sEmptyBackgroundColor }
|
||||
else
|
||||
local tOldLine = tLines[y]
|
||||
if new_width == nWidth then
|
||||
tNewLines[y] = tOldLine
|
||||
elseif new_width < nWidth then
|
||||
tNewLines[y] = {
|
||||
text = string_sub(tOldLine.text, 1, new_width),
|
||||
textColor = string_sub(tOldLine.textColor, 1, new_width),
|
||||
backgroundColor = string_sub(tOldLine.backgroundColor, 1, new_width),
|
||||
string_sub(tOldLine[1], 1, new_width),
|
||||
string_sub(tOldLine[2], 1, new_width),
|
||||
string_sub(tOldLine[3], 1, new_width),
|
||||
}
|
||||
else
|
||||
tNewLines[y] = {
|
||||
text = tOldLine.text .. string_sub(sEmptyText, nWidth + 1, new_width),
|
||||
textColor = tOldLine.textColor .. string_sub(sEmptyTextColor, nWidth + 1, new_width),
|
||||
backgroundColor = tOldLine.backgroundColor .. string_sub(sEmptyBackgroundColor, nWidth + 1, new_width),
|
||||
tOldLine[1] .. string_sub(sEmptyText, nWidth + 1, new_width),
|
||||
tOldLine[2] .. string_sub(sEmptyTextColor, nWidth + 1, new_width),
|
||||
tOldLine[3] .. string_sub(sEmptyBackgroundColor, nWidth + 1, new_width),
|
||||
}
|
||||
end
|
||||
end
|
||||
|
@ -1,3 +1,62 @@
|
||||
# New features in CC: Tweaked 1.108.1
|
||||
|
||||
Several bug fixes:
|
||||
* Prevent no-opped players breaking or placing command computers.
|
||||
* Allow using `@LuaFunction`-annotated methods on classes defined in child classloaders.
|
||||
|
||||
# New features in CC: Tweaked 1.108.0
|
||||
|
||||
* Remove compression from terminal/monitor packets. Vanilla applies its own compression, so this ends up being less helpful than expected.
|
||||
* `/computercraft` command now supports permission mods.
|
||||
* Split some GUI textures into sprite sheets.
|
||||
* Support the `%g` character class in string pattern matching.
|
||||
|
||||
Several bug fixes:
|
||||
* Fix crash when playing some modded records via a disk drive.
|
||||
* Fix race condition when computers attach or detach from a monitor.
|
||||
* Fix the "max websocket message" config option not being read.
|
||||
* `tostring` now correctly obeys `__name`.
|
||||
* Fix several inconsistencies with pattern matching character classes.
|
||||
|
||||
# New features in CC: Tweaked 1.107.0
|
||||
|
||||
* Add `disabled_generic_methods` config option to disable generic methods.
|
||||
* Add basic integration with EMI.
|
||||
* Enchanted turtle tools now render with a glint.
|
||||
* Update several translations (PatriikPlays, 1Turtle, Ale32bit).
|
||||
|
||||
Several bug fixes:
|
||||
* Fix client config file being generated on a dedicated server.
|
||||
* Fix numbers ending in "f" or "d" being treated as avalid.
|
||||
* Fix `string.pack`'s "z" specifier causing out-of-bounds errors.
|
||||
* Fix several issues with `turtle.dig`'s custom actions (tilling, making paths).
|
||||
|
||||
# New features in CC: Tweaked 1.106.1
|
||||
|
||||
Several bug fixes:
|
||||
* Block the CGNAT range (100.64.0.0/10) by default.
|
||||
* Fix conflicts with other mods replacing reach distance.
|
||||
|
||||
# New features in CC: Tweaked 1.106.0
|
||||
|
||||
* Numerous documentation improvements (MCJack123, znepb, penguinencounter).
|
||||
* Port `fs.find` to Lua. This also allows using `?` as a wildcard.
|
||||
* Computers cursors now glow in the dark.
|
||||
* Allow changing turtle upgrades from the GUI.
|
||||
* Add option to serialize Unicode strings to JSON (MCJack123).
|
||||
* Small optimisations to the `window` API.
|
||||
* Turtle upgrades can now preserve NBT from upgrade item stack and when broken.
|
||||
* Add support for tool enchantments and durability via datapacks. This is disabled for the built-in tools.
|
||||
|
||||
Several bug fixes:
|
||||
* Fix turtles rendering incorrectly when upside down.
|
||||
* Fix misplaced calls to IArguments.escapes.
|
||||
* Lua REPL no longer accepts `)(` as a valid expression.
|
||||
* Fix several inconsistencies with `require`/`package.path` in the Lua REPL (Wojbie).
|
||||
* Fix turtle being able to place water buckets outside its reach distance.
|
||||
* Fix private several IP address ranges not being blocked by the `$private` rule.
|
||||
* Improve permission checks in the `/computercraft` command.
|
||||
|
||||
# New features in CC: Tweaked 1.105.0
|
||||
|
||||
* Optimise JSON string parsing.
|
||||
|
@ -12,4 +12,4 @@ commands.give( "dan200", "minecraft:diamond", 64 )
|
||||
This works with any command. Use "commands.async" instead of "commands" to execute asynchronously.
|
||||
|
||||
The commands API is only available on Command Computers.
|
||||
Visit http://minecraft.gamepedia.com/Commands for documentation on all commands.
|
||||
Visit https://minecraft.wiki/w/Commands for documentation on all commands.
|
||||
|
@ -6,4 +6,4 @@ if sEvent == "key" and nKey == keys.enter then
|
||||
-- Do something
|
||||
end
|
||||
|
||||
See http://www.minecraftwiki.net/wiki/Key_codes, or the source code, for a complete reference.
|
||||
See https://www.minecraft.wiki/w/Key_codes, or the source code, for a complete reference.
|
||||
|
@ -1,25 +1,7 @@
|
||||
New features in CC: Tweaked 1.105.0
|
||||
|
||||
* Optimise JSON string parsing.
|
||||
* Add `colors.fromBlit` (Erb3).
|
||||
* Upload file size limit is now configurable (khankul).
|
||||
* Wired cables no longer have a distance limit.
|
||||
* Java methods now coerce values to strings consistently with Lua.
|
||||
* Add custom timeout support to the HTTP API.
|
||||
* Support custom proxies for HTTP requests (Lemmmy).
|
||||
* The `speaker` program now errors when playing HTML files.
|
||||
* `edit` now shows an error message when editing read-only files.
|
||||
* Update Ukranian translation (SirEdvin).
|
||||
New features in CC: Tweaked 1.108.1
|
||||
|
||||
Several bug fixes:
|
||||
* Allow GPS hosts to only be 1 block apart.
|
||||
* Fix "Turn On"/"Turn Off" buttons being inverted in the computer GUI (Erb3).
|
||||
* Fix arrow keys not working in the printout UI.
|
||||
* Several documentation fixes (zyxkad, Lupus590, Commandcracker).
|
||||
* Fix monitor renderer debug text always being visible on Forge.
|
||||
* Fix crash when another mod changes the LoggerContext.
|
||||
* Fix the `monitor_renderer` option not being present in Fabric config files.
|
||||
* Pasting on MacOS/OSX now uses Cmd+V rather than Ctrl+V.
|
||||
* Fix turtles placing blocks upside down when at y<0.
|
||||
* Prevent no-opped players breaking or placing command computers.
|
||||
* Allow using `@LuaFunction`-annotated methods on classes defined in child classloaders.
|
||||
|
||||
Type "help changelog" to see the full version history.
|
||||
|
@ -9,11 +9,11 @@ DFPWM (Dynamic Filter Pulse Width Modulation) is an audio codec designed by Grea
|
||||
format compared to raw PCM data, only using 1 bit per sample, but is simple enough to simple enough to encode and decode
|
||||
in real time.
|
||||
|
||||
Typically DFPWM audio is read from @{fs.BinaryReadHandle|the filesystem} or a @{http.Response|a web request} as a
|
||||
string, and converted a format suitable for @{speaker.playAudio}.
|
||||
Typically DFPWM audio is read from [the filesystem][`fs.BinaryReadHandle`] or a [a web request][`http.Response`] as a
|
||||
string, and converted a format suitable for [`speaker.playAudio`].
|
||||
|
||||
## Encoding and decoding files
|
||||
This modules exposes two key functions, @{make_decoder} and @{make_encoder}, which construct a new decoder or encoder.
|
||||
This modules exposes two key functions, [`make_decoder`] and [`make_encoder`], which construct a new decoder or encoder.
|
||||
The returned encoder/decoder is itself a function, which converts between the two kinds of data.
|
||||
|
||||
These encoders and decoders have lots of hidden state, so you should be careful to use the same encoder or decoder for
|
||||
@ -95,10 +95,9 @@ end
|
||||
The returned encoder is itself a function. This function accepts a table of amplitude data between -128 and 127 and
|
||||
returns the encoded DFPWM data.
|
||||
|
||||
:::caution Reusing encoders
|
||||
Encoders have lots of internal state which tracks the state of the current stream. If you reuse an encoder for multiple
|
||||
streams, or use different encoders for the same stream, the resulting audio may not sound correct.
|
||||
:::
|
||||
> [Reusing encoders][!WARNING]
|
||||
> Encoders have lots of internal state which tracks the state of the current stream. If you reuse an encoder for multiple
|
||||
> streams, or use different encoders for the same stream, the resulting audio may not sound correct.
|
||||
|
||||
@treturn function(pcm: { number... }):string The encoder function
|
||||
@see encode A helper function for encoding an entire file of audio at once.
|
||||
@ -138,10 +137,9 @@ end
|
||||
The returned decoder is itself a function. This function accepts a string and returns a table of amplitudes, each value
|
||||
between -128 and 127.
|
||||
|
||||
:::caution Reusing decoders
|
||||
Decoders have lots of internal state which tracks the state of the current stream. If you reuse an decoder for multiple
|
||||
streams, or use different decoders for the same stream, the resulting audio may not sound correct.
|
||||
:::
|
||||
> [Reusing decoders][!WARNING]
|
||||
> Decoders have lots of internal state which tracks the state of the current stream. If you reuse an decoder for
|
||||
> multiple streams, or use different decoders for the same stream, the resulting audio may not sound correct.
|
||||
|
||||
@treturn function(dfpwm: string):{ number... } The encoder function
|
||||
@see decode A helper function for decoding an entire file of audio at once.
|
||||
@ -167,7 +165,7 @@ local function make_decoder()
|
||||
local low_pass_charge = 0
|
||||
local previous_charge, previous_bit = 0, false
|
||||
|
||||
return function (input, output)
|
||||
return function (input)
|
||||
expect(1, input, "string")
|
||||
|
||||
local output, output_n = {}, 0
|
||||
@ -200,7 +198,7 @@ end
|
||||
--[[- A convenience function for decoding a complete file of audio at once.
|
||||
|
||||
This should only be used for short files. For larger files, one should read the file in chunks and process it using
|
||||
@{make_decoder}.
|
||||
[`make_decoder`].
|
||||
|
||||
@tparam string input The DFPWM data to convert.
|
||||
@treturn { number... } The produced amplitude data.
|
||||
@ -214,7 +212,7 @@ end
|
||||
--[[- A convenience function for encoding a complete file of audio at once.
|
||||
|
||||
This should only be used for complete pieces of audio. If you are writing writing multiple chunks to the same place,
|
||||
you should use an encoder returned by @{make_encoder} instead.
|
||||
you should use an encoder returned by [`make_encoder`] instead.
|
||||
|
||||
@tparam { number... } input The table of amplitude data.
|
||||
@treturn string The encoded DFPWM data.
|
||||
|
@ -3,11 +3,11 @@
|
||||
-- SPDX-License-Identifier: LicenseRef-CCPL
|
||||
|
||||
--- A collection of helper methods for working with input completion, such
|
||||
-- as that require by @{_G.read}.
|
||||
-- as that require by [`_G.read`].
|
||||
--
|
||||
-- @module cc.completion
|
||||
-- @see cc.shell.completion For additional helpers to use with
|
||||
-- @{shell.setCompletionFunction}.
|
||||
-- [`shell.setCompletionFunction`].
|
||||
-- @since 1.85.0
|
||||
|
||||
local expect = require "cc.expect".expect
|
||||
@ -34,7 +34,7 @@ end
|
||||
-- @tparam { string... } choices The list of choices to complete from.
|
||||
-- @tparam[opt] boolean add_space Whether to add a space after the completed item.
|
||||
-- @treturn { string... } A list of suffixes of matching strings.
|
||||
-- @usage Call @{_G.read}, completing the names of various animals.
|
||||
-- @usage Call [`_G.read`], completing the names of various animals.
|
||||
--
|
||||
-- local completion = require "cc.completion"
|
||||
-- local animals = { "dog", "cat", "lion", "unicorn" }
|
||||
@ -76,7 +76,7 @@ local function side(text, add_space)
|
||||
return choice_impl(text, sides, add_space)
|
||||
end
|
||||
|
||||
--- Complete a @{settings|setting}.
|
||||
--- Complete a [setting][`settings`].
|
||||
--
|
||||
-- @tparam string text The input string to complete.
|
||||
-- @tparam[opt] boolean add_space Whether to add a space after the completed settings.
|
||||
@ -92,7 +92,7 @@ end
|
||||
|
||||
local command_list
|
||||
|
||||
--- Complete the name of a Minecraft @{commands|command}.
|
||||
--- Complete the name of a Minecraft [command][`commands`].
|
||||
--
|
||||
-- @tparam string text The input string to complete.
|
||||
-- @tparam[opt] boolean add_space Whether to add a space after the completed command.
|
||||
|
@ -2,7 +2,7 @@
|
||||
--
|
||||
-- SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
--[[- The @{cc.expect} library provides helper functions for verifying that
|
||||
--[[- The [`cc.expect`] library provides helper functions for verifying that
|
||||
function arguments are well-formed and of the correct type.
|
||||
|
||||
@module cc.expect
|
||||
|
@ -2,10 +2,10 @@
|
||||
--
|
||||
-- SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
--- Read and draw nbt ("Nitrogen Fingers Text") images.
|
||||
--- Read and draw nft ("Nitrogen Fingers Text") images.
|
||||
--
|
||||
-- nft ("Nitrogen Fingers Text") is a file format for drawing basic images.
|
||||
-- Unlike the images that @{paintutils.parseImage} uses, nft supports coloured
|
||||
-- Unlike the images that [`paintutils.parseImage`] uses, nft supports coloured
|
||||
-- text as well as simple coloured pixels.
|
||||
--
|
||||
-- @module cc.image.nft
|
||||
@ -87,7 +87,7 @@ end
|
||||
|
||||
--- Draw an nft image to the screen.
|
||||
--
|
||||
-- @tparam table image An image, as returned from @{load} or @{draw}.
|
||||
-- @tparam table image An image, as returned from [`load`] or [`parse`].
|
||||
-- @tparam number xPos The x position to start drawing at.
|
||||
-- @tparam number xPos The y position to start drawing at.
|
||||
-- @tparam[opt] term.Redirect target The terminal redirect to draw to. Defaults to the
|
||||
|
@ -4,10 +4,9 @@
|
||||
|
||||
--[[- A pretty-printer for Lua errors.
|
||||
|
||||
:::warning
|
||||
This is an internal module and SHOULD NOT be used in your own code. It may
|
||||
be removed or changed at any time.
|
||||
:::
|
||||
> [!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 consumes a list of messages and "annotations" and displays the error to the
|
||||
terminal.
|
||||
@ -100,7 +99,7 @@ local code_accent = pretty.text("\x95", colours.cyan)
|
||||
over the underlying source, exposing the following functions:
|
||||
- `get_pos`: Get the line and column of an opaque position.
|
||||
- `get_line`: Get the source code for an opaque position.
|
||||
@tparam table message The message to display, as produced by @{cc.internal.syntax.errors}.
|
||||
@tparam table message The message to display, as produced by [`cc.internal.syntax.errors`].
|
||||
]]
|
||||
return function(context, message)
|
||||
expect(1, context, "table")
|
||||
|
@ -4,10 +4,9 @@
|
||||
|
||||
--[[- Internal tools for working with errors.
|
||||
|
||||
:::warning
|
||||
This is an internal module and SHOULD NOT be used in your own code. It may
|
||||
be removed or changed at any time.
|
||||
:::
|
||||
> [!DANGER]
|
||||
> This is an internal module and SHOULD NOT be used in your own code. It may
|
||||
> be removed or changed at any time.
|
||||
|
||||
@local
|
||||
]]
|
||||
|
@ -2,12 +2,11 @@
|
||||
--
|
||||
-- SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
--[[- Upload a list of files, as received by the @{event!file_transfer} event.
|
||||
--[[- Upload a list of files, as received by the [`event!file_transfer`] event.
|
||||
|
||||
:::warning
|
||||
This is an internal module and SHOULD NOT be used in your own code. It may
|
||||
be removed or changed at any time.
|
||||
:::
|
||||
> [!DANGER]
|
||||
> This is an internal module and SHOULD NOT be used in your own code. It may
|
||||
> be removed or changed at any time.
|
||||
|
||||
@local
|
||||
]]
|
||||
|
@ -4,14 +4,13 @@
|
||||
|
||||
--[[- The error messages reported by our lexer and parser.
|
||||
|
||||
:::warning
|
||||
This is an internal module and SHOULD NOT be used in your own code. It may
|
||||
be removed or changed at any time.
|
||||
:::
|
||||
> [!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 list of factory methods which take source positions and produce
|
||||
appropriate error messages targeting that location. These error messages can
|
||||
then be displayed to the user via @{cc.internal.error_printer}.
|
||||
then be displayed to the user via [`cc.internal.error_printer`].
|
||||
|
||||
@local
|
||||
]]
|
||||
@ -121,7 +120,7 @@ function errors.unfinished_string(start_pos, end_pos, quote)
|
||||
end
|
||||
|
||||
--[[- A string which ends with an escape sequence (so a literal `"foo\`). This
|
||||
is slightly different from @{unfinished_string}, as we don't want to suggest
|
||||
is slightly different from [`unfinished_string`], as we don't want to suggest
|
||||
adding a quote.
|
||||
|
||||
@tparam number start_pos The start position of the string.
|
||||
@ -467,7 +466,7 @@ function errors.standalone_name(pos)
|
||||
}
|
||||
end
|
||||
|
||||
--[[- A statement of the form `x.y`. This is similar to @{standalone_name}, but
|
||||
--[[- A statement of the form `x.y`. This is similar to [`standalone_name`], but
|
||||
when the next token is on another line.
|
||||
|
||||
@tparam number pos The position right after this name.
|
||||
|
@ -4,10 +4,9 @@
|
||||
|
||||
--[[- The main entrypoint to our Lua parser
|
||||
|
||||
:::warning
|
||||
This is an internal module and SHOULD NOT be used in your own code. It may
|
||||
be removed or changed at any time.
|
||||
:::
|
||||
> [!DANGER]
|
||||
> This is an internal module and SHOULD NOT be used in your own code. It may
|
||||
> be removed or changed at any time.
|
||||
|
||||
@local
|
||||
]]
|
||||
@ -21,6 +20,8 @@ local error_printer = require "cc.internal.error_printer"
|
||||
local error_sentinel = {}
|
||||
|
||||
local function make_context(input)
|
||||
expect(1, input, "string")
|
||||
|
||||
local context = {}
|
||||
|
||||
local lines = { 1 }
|
||||
@ -73,8 +74,9 @@ local function parse(input, start_symbol)
|
||||
expect(2, start_symbol, "number")
|
||||
|
||||
local context = make_context(input)
|
||||
function context.report(msg)
|
||||
expect(1, msg, "table")
|
||||
function context.report(msg, ...)
|
||||
expect(1, msg, "table", "function")
|
||||
if type(msg) == "function" then msg = msg(...) end
|
||||
error_printer(context, msg)
|
||||
error(error_sentinel)
|
||||
end
|
||||
@ -110,8 +112,9 @@ local function parse_repl(input)
|
||||
local context = make_context(input)
|
||||
|
||||
local last_error = nil
|
||||
function context.report(msg)
|
||||
expect(1, msg, "table")
|
||||
function context.report(msg, ...)
|
||||
expect(1, msg, "table", "function")
|
||||
if type(msg) == "function" then msg = msg(...) end
|
||||
last_error = msg
|
||||
error(error_sentinel)
|
||||
end
|
||||
|
@ -4,17 +4,16 @@
|
||||
|
||||
--[[- A lexer for Lua source code.
|
||||
|
||||
:::warning
|
||||
This is an internal module and SHOULD NOT be used in your own code. It may
|
||||
be removed or changed at any time.
|
||||
:::
|
||||
> [!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 module provides utilities for lexing Lua code, returning tokens compatible
|
||||
with @{cc.internal.syntax.parser}. While all lexers are roughly the same, there
|
||||
with [`cc.internal.syntax.parser`]. While all lexers are roughly the same, there
|
||||
are some design choices worth drawing attention to:
|
||||
|
||||
- The lexer uses Lua patterns (i.e. @{string.find}) as much as possible,
|
||||
trying to avoid @{string.sub} loops except when needed. This allows us to
|
||||
- The lexer uses Lua patterns (i.e. [`string.find`]) as much as possible,
|
||||
trying to avoid [`string.sub`] loops except when needed. This allows us to
|
||||
move string processing to native code, which ends up being much faster.
|
||||
|
||||
- We try to avoid allocating where possible. There are some cases we need to
|
||||
@ -96,7 +95,7 @@ local function lex_number(context, str, start)
|
||||
local contents = sub(str, start, pos - 1)
|
||||
if not tonumber(contents) then
|
||||
-- TODO: Separate error for "2..3"?
|
||||
context.report(errors.malformed_number(start, pos - 1))
|
||||
context.report(errors.malformed_number, start, pos - 1)
|
||||
end
|
||||
|
||||
return tokens.NUMBER, pos - 1
|
||||
@ -118,14 +117,14 @@ local function lex_string(context, str, start_pos, quote)
|
||||
return tokens.STRING, pos
|
||||
elseif c == "\n" or c == "\r" or c == "" then
|
||||
-- We don't call newline here, as that's done for the next token.
|
||||
context.report(errors.unfinished_string(start_pos, pos, quote))
|
||||
context.report(errors.unfinished_string, start_pos, pos, quote)
|
||||
return tokens.STRING, pos - 1
|
||||
elseif c == "\\" then
|
||||
c = sub(str, pos + 1, pos + 1)
|
||||
if c == "\n" or c == "\r" then
|
||||
pos = newline(context, str, pos + 1, c)
|
||||
elseif c == "" then
|
||||
context.report(errors.unfinished_string_escape(start_pos, pos, quote))
|
||||
context.report(errors.unfinished_string_escape, start_pos, pos, quote)
|
||||
return tokens.STRING, pos
|
||||
elseif c == "z" then
|
||||
pos = pos + 2
|
||||
@ -133,7 +132,7 @@ local function lex_string(context, str, start_pos, quote)
|
||||
local next_pos, _, c = find(str, "([%S\r\n])", pos)
|
||||
|
||||
if not next_pos then
|
||||
context.report(errors.unfinished_string(start_pos, #str, quote))
|
||||
context.report(errors.unfinished_string, start_pos, #str, quote)
|
||||
return tokens.STRING, #str
|
||||
end
|
||||
|
||||
@ -178,7 +177,7 @@ end
|
||||
-- @tparam number start The start position, after the input boundary.
|
||||
-- @tparam number len The expected length of the boundary. Equal to 1 + the
|
||||
-- number of `=`.
|
||||
-- @treturn number|nil The end position, or @{nil} if this is not terminated.
|
||||
-- @treturn number|nil The end position, or [`nil`] if this is not terminated.
|
||||
local function lex_long_str(context, str, start, len)
|
||||
local pos = start
|
||||
while true do
|
||||
@ -196,7 +195,7 @@ local function lex_long_str(context, str, start, len)
|
||||
elseif c == "[" then
|
||||
local ok, boundary_pos = lex_long_str_boundary(str, pos + 1, "[")
|
||||
if ok and boundary_pos - pos == len and len == 1 then
|
||||
context.report(errors.nested_long_str(pos, boundary_pos))
|
||||
context.report(errors.nested_long_str, pos, boundary_pos)
|
||||
end
|
||||
|
||||
pos = boundary_pos
|
||||
@ -238,12 +237,12 @@ local function lex_token(context, str, pos)
|
||||
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))
|
||||
context.report(errors.unfinished_long_string, pos, boundary_pos, boundary_pos - pos)
|
||||
return tokens.ERROR, #str
|
||||
elseif pos + 1 == boundary_pos then -- Just a "["
|
||||
return tokens.OSQUARE, pos
|
||||
else -- Malformed long string, for instance "[="
|
||||
context.report(errors.malformed_long_string(pos, boundary_pos, boundary_pos - pos))
|
||||
context.report(errors.malformed_long_string, pos, boundary_pos, boundary_pos - pos)
|
||||
return tokens.ERROR, boundary_pos
|
||||
end
|
||||
|
||||
@ -260,7 +259,7 @@ local function lex_token(context, str, pos)
|
||||
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))
|
||||
context.report(errors.unfinished_long_comment, pos, boundary_pos, boundary_pos - comment_pos)
|
||||
return tokens.ERROR, #str
|
||||
end
|
||||
end
|
||||
@ -317,18 +316,18 @@ local function lex_token(context, str, pos)
|
||||
if end_pos - pos <= 3 then
|
||||
local contents = sub(str, pos, end_pos)
|
||||
if contents == "&&" then
|
||||
context.report(errors.wrong_and(pos, end_pos))
|
||||
context.report(errors.wrong_and, pos, end_pos)
|
||||
return tokens.AND, end_pos
|
||||
elseif contents == "||" then
|
||||
context.report(errors.wrong_or(pos, end_pos))
|
||||
context.report(errors.wrong_or, pos, end_pos)
|
||||
return tokens.OR, end_pos
|
||||
elseif contents == "!=" or contents == "<>" then
|
||||
context.report(errors.wrong_ne(pos, end_pos))
|
||||
context.report(errors.wrong_ne, pos, end_pos)
|
||||
return tokens.NE, end_pos
|
||||
end
|
||||
end
|
||||
|
||||
context.report(errors.unexpected_character(pos))
|
||||
context.report(errors.unexpected_character, pos)
|
||||
return tokens.ERROR, end_pos
|
||||
end
|
||||
end
|
||||
|
@ -4,10 +4,9 @@
|
||||
|
||||
--[[- A parser for Lua programs and expressions.
|
||||
|
||||
:::warning
|
||||
This is an internal module and SHOULD NOT be used in your own code. It may
|
||||
be removed or changed at any time.
|
||||
:::
|
||||
> [!DANGER]
|
||||
> This is an internal module and SHOULD NOT be used in your own code. It may
|
||||
> be removed or changed at any time.
|
||||
|
||||
Most of the code in this module is automatically generated from the Lua grammar,
|
||||
hence being mostly unreadable!
|
||||
@ -244,7 +243,7 @@ local function handle_error(context, stack, stack_n, token, token_start, token_e
|
||||
end
|
||||
end
|
||||
|
||||
context.report(errors.unexpected_token(token, token_start, token_end))
|
||||
context.report(errors.unexpected_token, token, token_start, token_end)
|
||||
return false
|
||||
end
|
||||
|
||||
|
@ -5,12 +5,12 @@
|
||||
--[[- A pretty printer for rendering data structures in an aesthetically
|
||||
pleasing manner.
|
||||
|
||||
In order to display something using @{cc.pretty}, you build up a series of
|
||||
@{Doc|documents}. These behave a little bit like strings; you can concatenate
|
||||
In order to display something using [`cc.pretty`], you build up a series of
|
||||
[documents][`Doc`]. These behave a little bit like strings; you can concatenate
|
||||
them together and then print them to the screen.
|
||||
|
||||
However, documents also allow you to control how they should be printed. There
|
||||
are several functions (such as @{nest} and @{group}) which allow you to control
|
||||
are several functions (such as [`nest`] and [`group`]) which allow you to control
|
||||
the "layout" of the document. When you come to display the document, the 'best'
|
||||
(most compact) layout is used.
|
||||
|
||||
@ -37,7 +37,7 @@ local expect, field = expect.expect, expect.field
|
||||
local type, getmetatable, setmetatable, colours, str_write, tostring = type, getmetatable, setmetatable, colours, write, tostring
|
||||
local debug_info, debug_local = debug.getinfo, debug.getlocal
|
||||
|
||||
--- @{table.insert} alternative, but with the length stored inline.
|
||||
--- [`table.insert`] alternative, but with the length stored inline.
|
||||
local function append(out, value)
|
||||
local n = out.n + 1
|
||||
out[n], out.n = value, n
|
||||
@ -59,10 +59,10 @@ local empty = mk_doc({ tag = "nil" })
|
||||
--- A document with a single space in it.
|
||||
local space = mk_doc({ tag = "text", text = " " })
|
||||
|
||||
--- A line break. When collapsed with @{group}, this will be replaced with @{empty}.
|
||||
--- A line break. When collapsed with [`group`], this will be replaced with [`empty`].
|
||||
local line = mk_doc({ tag = "line", flat = empty })
|
||||
|
||||
--- A line break. When collapsed with @{group}, this will be replaced with @{space}.
|
||||
--- A line break. When collapsed with [`group`], this will be replaced with [`space`].
|
||||
local space_line = mk_doc({ tag = "line", flat = space })
|
||||
|
||||
local text_cache = { [""] = empty, [" "] = space, ["\n"] = space_line }
|
||||
@ -73,7 +73,7 @@ end
|
||||
|
||||
--- Create a new document from a string.
|
||||
--
|
||||
-- If your string contains multiple lines, @{group} will flatten the string
|
||||
-- If your string contains multiple lines, [`group`] will flatten the string
|
||||
-- into a single line, with spaces between each line.
|
||||
--
|
||||
-- @tparam string text The string to construct a new document with.
|
||||
@ -453,7 +453,7 @@ end
|
||||
|
||||
--- Pretty-print an arbitrary object, converting it into a document.
|
||||
--
|
||||
-- This can then be rendered with @{write} or @{print}.
|
||||
-- This can then be rendered with [`write`] or [`print`].
|
||||
--
|
||||
-- @param obj The object to pretty-print.
|
||||
-- @tparam[opt] { function_args = boolean, function_source = boolean } options
|
||||
@ -479,7 +479,7 @@ local function pretty(obj, options)
|
||||
return pretty_impl(obj, actual_options, {})
|
||||
end
|
||||
|
||||
--[[- A shortcut for calling @{pretty} and @{print} together.
|
||||
--[[- A shortcut for calling [`pretty`] and [`print`] together.
|
||||
|
||||
@param obj The object to pretty-print.
|
||||
@tparam[opt] { function_args = boolean, function_source = boolean } options
|
||||
|
@ -2,8 +2,8 @@
|
||||
--
|
||||
-- SPDX-License-Identifier: LicenseRef-CCPL
|
||||
|
||||
--[[- A pure Lua implementation of the builtin @{require} function and
|
||||
@{package} library.
|
||||
--[[- A pure Lua implementation of the builtin [`require`] function and
|
||||
[`package`] library.
|
||||
|
||||
Generally you do not need to use this module - it is injected into the every
|
||||
program's environment. However, it may be useful when building a custom shell or
|
||||
@ -11,7 +11,7 @@ when running programs yourself.
|
||||
|
||||
@module cc.require
|
||||
@since 1.88.0
|
||||
@see using_require For an introduction on how to use @{require}.
|
||||
@see using_require For an introduction on how to use [`require`].
|
||||
@usage Construct the package and require function, and insert them into a
|
||||
custom environment.
|
||||
|
||||
@ -110,13 +110,13 @@ local function make_require(package)
|
||||
end
|
||||
end
|
||||
|
||||
--- Build an implementation of Lua's @{package} library, and a @{require}
|
||||
--- Build an implementation of Lua's [`package`] library, and a [`require`]
|
||||
-- function to load modules within it.
|
||||
--
|
||||
-- @tparam table env The environment to load packages into.
|
||||
-- @tparam string dir The directory that relative packages are loaded from.
|
||||
-- @treturn function The new @{require} function.
|
||||
-- @treturn table The new @{package} library.
|
||||
-- @treturn function The new [`require`] function.
|
||||
-- @treturn table The new [`package`] library.
|
||||
local function make_package(env, dir)
|
||||
expect(1, env, "table")
|
||||
expect(2, dir, "string")
|
||||
|
@ -4,16 +4,16 @@
|
||||
|
||||
--[[- A collection of helper methods for working with shell completion.
|
||||
|
||||
Most programs may be completed using the @{build} helper method, rather than
|
||||
Most programs may be completed using the [`build`] helper method, rather than
|
||||
manually switching on the argument index.
|
||||
|
||||
Note, the helper functions within this module do not accept an argument index,
|
||||
and so are not directly usable with the @{shell.setCompletionFunction}. Instead,
|
||||
wrap them using @{build}, or your own custom function.
|
||||
and so are not directly usable with the [`shell.setCompletionFunction`]. Instead,
|
||||
wrap them using [`build`], or your own custom function.
|
||||
|
||||
@module cc.shell.completion
|
||||
@since 1.85.0
|
||||
@see cc.completion For more general helpers, suitable for use with @{_G.read}.
|
||||
@see cc.completion For more general helpers, suitable for use with [`_G.read`].
|
||||
@see shell.setCompletionFunction
|
||||
|
||||
@usage Register a completion handler for example.lua which prompts for a
|
||||
@ -135,15 +135,15 @@ end
|
||||
--[[- A helper function for building shell completion arguments.
|
||||
|
||||
This accepts a series of single-argument completion functions, and combines
|
||||
them into a function suitable for use with @{shell.setCompletionFunction}.
|
||||
them into a function suitable for use with [`shell.setCompletionFunction`].
|
||||
|
||||
@tparam nil|table|function ... Every argument to @{build} represents an argument
|
||||
@tparam nil|table|function ... Every argument to [`build`] represents an argument
|
||||
to the program you wish to complete. Each argument can be one of three types:
|
||||
|
||||
- `nil`: This argument will not be completed.
|
||||
|
||||
- A function: This argument will be completed with the given function. It is
|
||||
called with the @{shell} object, the string to complete and the arguments
|
||||
called with the [`shell`] object, the string to complete and the arguments
|
||||
before this one.
|
||||
|
||||
- A table: This acts as a more powerful version of the function case. The table
|
||||
@ -197,12 +197,12 @@ return {
|
||||
programWithArgs = programWithArgs,
|
||||
|
||||
-- Re-export various other functions
|
||||
help = wrap(help.completeTopic), --- Wraps @{help.completeTopic} as a @{build} compatible function.
|
||||
choice = wrap(completion.choice), --- Wraps @{cc.completion.choice} as a @{build} compatible function.
|
||||
peripheral = wrap(completion.peripheral), --- Wraps @{cc.completion.peripheral} as a @{build} compatible function.
|
||||
side = wrap(completion.side), --- Wraps @{cc.completion.side} as a @{build} compatible function.
|
||||
setting = wrap(completion.setting), --- Wraps @{cc.completion.setting} as a @{build} compatible function.
|
||||
command = wrap(completion.command), --- Wraps @{cc.completion.command} as a @{build} compatible function.
|
||||
help = wrap(help.completeTopic), --- Wraps [`help.completeTopic`] as a [`build`] compatible function.
|
||||
choice = wrap(completion.choice), --- Wraps [`cc.completion.choice`] as a [`build`] compatible function.
|
||||
peripheral = wrap(completion.peripheral), --- Wraps [`cc.completion.peripheral`] as a [`build`] compatible function.
|
||||
side = wrap(completion.side), --- Wraps [`cc.completion.side`] as a [`build`] compatible function.
|
||||
setting = wrap(completion.setting), --- Wraps [`cc.completion.setting`] as a [`build`] compatible function.
|
||||
command = wrap(completion.command), --- Wraps [`cc.completion.command`] as a [`build`] compatible function.
|
||||
|
||||
build = build,
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ local expect = (require and require("cc.expect") or dofile("rom/modules/main/cc/
|
||||
--[[- Wraps a block of text, so that each line fits within the given width.
|
||||
|
||||
This may be useful if you want to wrap text before displaying it to a
|
||||
@{monitor} or @{printer} without using @{_G.print|print}.
|
||||
[`monitor`] or [`printer`] without using [print][`_G.print`].
|
||||
|
||||
@tparam string text The string to wrap.
|
||||
@tparam[opt] number width The width to constrain to, defaults to the width of
|
||||
|
@ -6,17 +6,17 @@
|
||||
--
|
||||
-- When multiple programs are running, it displays a tab bar at the top of the
|
||||
-- screen, which allows you to switch between programs. New programs can be
|
||||
-- launched using the `fg` or `bg` programs, or using the @{shell.openTab} and
|
||||
-- @{multishell.launch} functions.
|
||||
-- launched using the `fg` or `bg` programs, or using the [`shell.openTab`] and
|
||||
-- [`multishell.launch`] functions.
|
||||
--
|
||||
-- Each process is identified by its ID, which corresponds to its position in
|
||||
-- the tab list. As tabs may be opened and closed, this ID is _not_ constant
|
||||
-- over a program's run. As such, be careful not to use stale IDs.
|
||||
--
|
||||
-- As with @{shell}, @{multishell} is not a "true" API. Instead, it is a
|
||||
-- As with [`shell`], [`multishell`] is not a "true" API. Instead, it is a
|
||||
-- standard program, which launches a shell and injects its API into the shell's
|
||||
-- environment. This API is not available in the global environment, and so is
|
||||
-- not available to @{os.loadAPI|APIs}.
|
||||
-- not available to [APIs][`os.loadAPI`].
|
||||
--
|
||||
-- @module[module] multishell
|
||||
-- @since 1.6
|
||||
@ -222,7 +222,7 @@ local multishell = {} --- @export
|
||||
--- Get the currently visible process. This will be the one selected on
|
||||
-- the tab bar.
|
||||
--
|
||||
-- Note, this is different to @{getCurrent}, which returns the process which is
|
||||
-- Note, this is different to [`getCurrent`], which returns the process which is
|
||||
-- currently executing.
|
||||
--
|
||||
-- @treturn number The currently visible process's index.
|
||||
@ -235,7 +235,7 @@ end
|
||||
--
|
||||
-- @tparam number n The process index to switch to.
|
||||
-- @treturn boolean If the process was changed successfully. This will
|
||||
-- return @{false} if there is no process with this id.
|
||||
-- return [`false`] if there is no process with this id.
|
||||
-- @see getFocus
|
||||
function multishell.setFocus(n)
|
||||
expect(1, n, "number")
|
||||
@ -250,9 +250,9 @@ end
|
||||
--- Get the title of the given tab.
|
||||
--
|
||||
-- This starts as the name of the program, but may be changed using
|
||||
-- @{multishell.setTitle}.
|
||||
-- [`multishell.setTitle`].
|
||||
-- @tparam number n The process index.
|
||||
-- @treturn string|nil The current process title, or @{nil} if the
|
||||
-- @treturn string|nil The current process title, or [`nil`] if the
|
||||
-- process doesn't exist.
|
||||
function multishell.getTitle(n)
|
||||
expect(1, n, "number")
|
||||
|
@ -275,6 +275,12 @@ local function drawCanvas()
|
||||
end
|
||||
end
|
||||
|
||||
local function termResize()
|
||||
w, h = term.getSize()
|
||||
drawCanvas()
|
||||
drawInterface()
|
||||
end
|
||||
|
||||
local menu_choices = {
|
||||
Save = function()
|
||||
if bReadOnly then
|
||||
@ -376,6 +382,8 @@ local function accessMenu()
|
||||
nMenuPosEnd = nMenuPosEnd + 1
|
||||
nMenuPosStart = nMenuPosEnd
|
||||
end
|
||||
elseif id == "term_resize" then
|
||||
termResize()
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -434,9 +442,7 @@ local function handleEvents()
|
||||
drawInterface()
|
||||
end
|
||||
elseif id == "term_resize" then
|
||||
w, h = term.getSize()
|
||||
drawCanvas()
|
||||
drawInterface()
|
||||
termResize()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -56,14 +56,6 @@ local function get(url)
|
||||
)
|
||||
|
||||
if response then
|
||||
-- If spam protection is activated, we get redirected to /paste with Content-Type: text/html
|
||||
local headers = response.getResponseHeaders()
|
||||
if not headers["Content-Type"] or not headers["Content-Type"]:find("^text/plain") then
|
||||
io.stderr:write("Failed.\n")
|
||||
print("Pastebin blocked the download due to spam protection. Please complete the captcha in a web browser: https://pastebin.com/" .. textutils.urlEncode(paste))
|
||||
return
|
||||
end
|
||||
|
||||
print("Success.")
|
||||
|
||||
local sResponse = response.readAll()
|
||||
|
@ -36,13 +36,6 @@ local function getFilename(sUrl)
|
||||
end
|
||||
|
||||
local function get(sUrl)
|
||||
-- Check if the URL is valid
|
||||
local ok, err = http.checkURL(url)
|
||||
if not ok then
|
||||
printError(err or "Invalid URL.")
|
||||
return
|
||||
end
|
||||
|
||||
write("Connecting to " .. sUrl .. "... ")
|
||||
|
||||
local response = http.get(sUrl , nil , true)
|
||||
|
@ -25,21 +25,13 @@ local tEnv = {
|
||||
}
|
||||
setmetatable(tEnv, { __index = _ENV })
|
||||
|
||||
-- Replace our package.path, so that it loads from the current directory, rather
|
||||
-- than from /rom/programs. This makes it a little more friendly to use and
|
||||
-- closer to what you'd expect.
|
||||
-- Replace our require with new instance that loads from the current directory
|
||||
-- rather than from /rom/programs. This makes it more friendly to use and closer
|
||||
-- to what you'd expect.
|
||||
do
|
||||
local make_package = require "cc.require".make
|
||||
local dir = shell.dir()
|
||||
if dir:sub(1, 1) ~= "/" then dir = "/" .. dir end
|
||||
if dir:sub(-1) ~= "/" then dir = dir .. "/" end
|
||||
|
||||
local strip_path = "?;?.lua;?/init.lua;"
|
||||
local path = package.path
|
||||
if path:sub(1, #strip_path) == strip_path then
|
||||
path = path:sub(#strip_path + 1)
|
||||
end
|
||||
|
||||
package.path = dir .. "?;" .. dir .. "?.lua;" .. dir .. "?/init.lua;" .. path
|
||||
_ENV.require, _ENV.package = make_package(_ENV, dir)
|
||||
end
|
||||
|
||||
if term.isColour() then
|
||||
@ -82,18 +74,13 @@ while running do
|
||||
|
||||
local name, offset = "=lua[" .. chunk_idx .. "]", 0
|
||||
|
||||
local force_print = 0
|
||||
local func, err = load(input, name, "t", tEnv)
|
||||
|
||||
local expr_func = load("return _echo(" .. input .. ");", name, "t", tEnv)
|
||||
if not func then
|
||||
if expr_func then
|
||||
func = expr_func
|
||||
offset = 13
|
||||
force_print = 1
|
||||
end
|
||||
elseif expr_func then
|
||||
func = expr_func
|
||||
if load("return " .. input) then
|
||||
-- We wrap the expression with a call to _echo(...), which prevents tail
|
||||
-- calls (and thus confusing errors). Note we check this is a valid
|
||||
-- expression separately, to avoid accepting inputs like `)--` (which are
|
||||
-- parsed as `_echo()--)`.
|
||||
func = load("return _echo(" .. input .. "\n)", name, "t", tEnv)
|
||||
offset = 13
|
||||
end
|
||||
|
||||
@ -103,9 +90,8 @@ while running do
|
||||
|
||||
local results = table.pack(exception.try(func))
|
||||
if results[1] then
|
||||
local n = 1
|
||||
while n < results.n or n <= force_print do
|
||||
local value = results[n + 1]
|
||||
for i = 2, results.n do
|
||||
local value = results[i]
|
||||
local ok, serialised = pcall(pretty.pretty, value, {
|
||||
function_args = settings.get("lua.function_args"),
|
||||
function_source = settings.get("lua.function_source"),
|
||||
@ -115,7 +101,6 @@ while running do
|
||||
else
|
||||
print(tostring(value))
|
||||
end
|
||||
n = n + 1
|
||||
end
|
||||
else
|
||||
printError(results[2])
|
||||
|
@ -4,29 +4,29 @@
|
||||
|
||||
--[[- The shell API provides access to CraftOS's command line interface.
|
||||
|
||||
It allows you to @{run|start programs}, @{setCompletionFunction|add completion
|
||||
for a program}, and much more.
|
||||
It allows you to [start programs][`run`], [add completion for a
|
||||
program][`setCompletionFunction`], and much more.
|
||||
|
||||
@{shell} is not a "true" API. Instead, it is a standard program, which injects
|
||||
[`shell`] is not a "true" API. Instead, it is a standard program, which injects
|
||||
its API into the programs that it launches. This allows for multiple shells to
|
||||
run at the same time, but means that the API is not available in the global
|
||||
environment, and so is unavailable to other @{os.loadAPI|APIs}.
|
||||
environment, and so is unavailable to other [APIs][`os.loadAPI`].
|
||||
|
||||
## Programs and the program path
|
||||
When you run a command with the shell, either from the prompt or
|
||||
@{shell.run|from Lua code}, the shell API performs several steps to work out
|
||||
[from Lua code][`shell.run`], the shell API performs several steps to work out
|
||||
which program to run:
|
||||
|
||||
1. Firstly, the shell attempts to resolve @{shell.aliases|aliases}. This allows
|
||||
1. Firstly, the shell attempts to resolve [aliases][`shell.aliases`]. This allows
|
||||
us to use multiple names for a single command. For example, the `list`
|
||||
program has two aliases: `ls` and `dir`. When you write `ls /rom`, that's
|
||||
expanded to `list /rom`.
|
||||
|
||||
2. Next, the shell attempts to find where the program actually is. For this, it
|
||||
uses the @{shell.path|program path}. This is a colon separated list of
|
||||
uses the [program path][`shell.path`]. This is a colon separated list of
|
||||
directories, each of which is checked to see if it contains the program.
|
||||
|
||||
`list` or `list.lua` doesn't exist in `.` (the current directory), so she
|
||||
`list` or `list.lua` doesn't exist in `.` (the current directory), so the
|
||||
shell now looks in `/rom/programs`, where `list.lua` can be found!
|
||||
|
||||
3. Finally, the shell reads the file and checks if the file starts with a
|
||||
@ -192,7 +192,7 @@ end
|
||||
|
||||
--- Run a program with the supplied arguments.
|
||||
--
|
||||
-- Unlike @{shell.run}, each argument is passed to the program verbatim. While
|
||||
-- Unlike [`shell.run`], each argument is passed to the program verbatim. While
|
||||
-- `shell.run("echo", "b c")` runs `echo` with `b` and `c`,
|
||||
-- `shell.execute("echo", "b c")` runs `echo` with a single argument `b c`.
|
||||
--
|
||||
@ -276,7 +276,7 @@ function shell.exit()
|
||||
end
|
||||
|
||||
--- Return the current working directory. This is what is displayed before the
|
||||
-- `> ` of the shell prompt, and is used by @{shell.resolve} to handle relative
|
||||
-- `> ` of the shell prompt, and is used by [`shell.resolve`] to handle relative
|
||||
-- paths.
|
||||
--
|
||||
-- @treturn string The current working directory.
|
||||
@ -313,10 +313,10 @@ function shell.path()
|
||||
return sPath
|
||||
end
|
||||
|
||||
--- Set the @{path|current program path}.
|
||||
--- Set the [current program path][`path`].
|
||||
--
|
||||
-- Be careful to prefix directories with a `/`. Otherwise they will be searched
|
||||
-- for from the @{shell.dir|current directory}, rather than the computer's root.
|
||||
-- for from the [current directory][`shell.dir`], rather than the computer's root.
|
||||
--
|
||||
-- @tparam string path The new program path.
|
||||
-- @since 1.2
|
||||
@ -327,8 +327,8 @@ end
|
||||
|
||||
--- Resolve a relative path to an absolute path.
|
||||
--
|
||||
-- The @{fs} and @{io} APIs work using absolute paths, and so we must convert
|
||||
-- any paths relative to the @{dir|current directory} to absolute ones. This
|
||||
-- The [`fs`] and [`io`] APIs work using absolute paths, and so we must convert
|
||||
-- any paths relative to the [current directory][`dir`] to absolute ones. This
|
||||
-- does nothing when the path starts with `/`.
|
||||
--
|
||||
-- @tparam string path The path to resolve.
|
||||
@ -357,10 +357,10 @@ local function pathWithExtension(_sPath, _sExt)
|
||||
return _sPath .. "." .. _sExt
|
||||
end
|
||||
|
||||
--- Resolve a program, using the @{path|program path} and list of @{aliases|aliases}.
|
||||
--- Resolve a program, using the [program path][`path`] and list of [aliases][`aliases`].
|
||||
--
|
||||
-- @tparam string command The name of the program
|
||||
-- @treturn string|nil The absolute path to the program, or @{nil} if it could
|
||||
-- @treturn string|nil The absolute path to the program, or [`nil`] if it could
|
||||
-- not be found.
|
||||
-- @since 1.2
|
||||
-- @changed 1.80pr1 Now searches for files with and without the `.lua` extension.
|
||||
@ -406,7 +406,7 @@ function shell.resolveProgram(command)
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Return a list of all programs on the @{shell.path|path}.
|
||||
--- Return a list of all programs on the [path][`shell.path`].
|
||||
--
|
||||
-- @tparam[opt] boolean include_hidden Include hidden files. Namely, any which
|
||||
-- start with `.`.
|
||||
@ -518,7 +518,7 @@ end
|
||||
-- completed to `ls rom/`.
|
||||
--
|
||||
-- Completion handlers for your program may be registered with
|
||||
-- @{shell.setCompletionFunction}.
|
||||
-- [`shell.setCompletionFunction`].
|
||||
--
|
||||
-- @tparam string sLine The input to complete.
|
||||
-- @treturn { string }|nil The list of possible completions.
|
||||
@ -614,7 +614,7 @@ end
|
||||
--- Get a table containing all completion functions.
|
||||
--
|
||||
-- This should only be needed when building custom shells. Use
|
||||
-- @{setCompletionFunction} to add a completion function.
|
||||
-- [`setCompletionFunction`] to add a completion function.
|
||||
--
|
||||
-- @treturn { [string] = { fnComplete = function } } A table mapping the
|
||||
-- absolute path of programs, to their completion functions.
|
||||
@ -676,12 +676,12 @@ function shell.aliases()
|
||||
end
|
||||
|
||||
if multishell then
|
||||
--- Open a new @{multishell} tab running a command.
|
||||
--- Open a new [`multishell`] tab running a command.
|
||||
--
|
||||
-- This behaves similarly to @{shell.run}, but instead returns the process
|
||||
-- This behaves similarly to [`shell.run`], but instead returns the process
|
||||
-- index.
|
||||
--
|
||||
-- This function is only available if the @{multishell} API is.
|
||||
-- This function is only available if the [`multishell`] API is.
|
||||
--
|
||||
-- @tparam string ... The command line to run.
|
||||
-- @see shell.run
|
||||
@ -706,7 +706,7 @@ if multishell then
|
||||
end
|
||||
end
|
||||
|
||||
--- Switch to the @{multishell} tab with the given index.
|
||||
--- Switch to the [`multishell`] tab with the given index.
|
||||
--
|
||||
-- @tparam number id The tab to switch to.
|
||||
-- @see multishell.setFocus
|
||||
|
2
vendor/Cobalt
vendored
2
vendor/Cobalt
vendored
@ -1 +1 @@
|
||||
Subproject commit a630403859ff6321d10292fbf94dcfb30b507d9d
|
||||
Subproject commit 6b0d2e234ebbfb1ffd20cde071ecd4c782be6da1
|
Loading…
x
Reference in New Issue
Block a user