those diverged will eventually reunited
merge separated unicode modules/functions back to their normal variant.
This commit is contained in:
parent
a07a1b0152
commit
cf466874ef
|
@ -73,12 +73,9 @@ public TerminalWidget(Terminal terminal, InputHandler computer, int x, int y) {
|
|||
@Override
|
||||
public boolean charTyped(char ch, int modifiers) {
|
||||
if (ch >= 32 && ch <= 126 || ch >= 160) {
|
||||
if (ch <= 255){
|
||||
// Queue the char event for any printable chars in byte range
|
||||
computer.queueEvent("char", new Object[]{ Character.toString(ch) });
|
||||
}
|
||||
// always send 'charutf' event so that in the computer it only needs to poll one type of event
|
||||
computer.queueEvent("charutf", new Object[]{ StringUtil.utfToByteString(Character.toString(ch)) });
|
||||
// Queue the char event for any printable chars in byte range
|
||||
var s = Character.toString(ch);
|
||||
computer.queueEvent("char", new Object[]{ s, StringUtil.utfToByteString(s) });
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -135,8 +132,7 @@ private void paste() {
|
|||
if (!clipboard.isEmpty()) {
|
||||
// Clip to 512 characters and queue the event
|
||||
if (clipboard.length() > 512) clipboard = clipboard.substring(0, 512);
|
||||
computer.queueEvent("paste", new Object[]{ clipboard });
|
||||
computer.queueEvent("pasteutf", new Object[]{StringUtil.utfToByteString(clipboard)});
|
||||
computer.queueEvent("paste", new Object[]{ clipboard, StringUtil.utfToByteString(clipboard) });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
package dan200.computercraft.gametest
|
||||
|
||||
import dan200.computercraft.api.lua.Coerced
|
||||
import dan200.computercraft.api.lua.ObjectArguments
|
||||
import dan200.computercraft.client.gui.AbstractComputerScreen
|
||||
import dan200.computercraft.core.apis.RedstoneAPI
|
||||
import dan200.computercraft.core.apis.TermAPI
|
||||
|
@ -88,7 +89,7 @@ fun Computer_peripheral(context: GameTestHelper) = context.sequence {
|
|||
@ClientGameTest
|
||||
fun Open_on_client(context: GameTestHelper) = context.sequence {
|
||||
// Write "Hello, world!" and then print each event to the terminal.
|
||||
thenOnComputer { getApi<TermAPI>().write(Coerced("Hello, world!")) }
|
||||
thenOnComputer { getApi<TermAPI>().write(ObjectArguments("Hello, world!")) }
|
||||
thenStartComputer {
|
||||
val term = getApi<TermAPI>().terminal
|
||||
while (true) {
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
package dan200.computercraft.gametest
|
||||
|
||||
import dan200.computercraft.api.lua.Coerced
|
||||
import dan200.computercraft.api.lua.ObjectArguments
|
||||
import dan200.computercraft.client.pocket.ClientPocketComputers
|
||||
import dan200.computercraft.core.apis.TermAPI
|
||||
import dan200.computercraft.gametest.api.*
|
||||
|
@ -34,7 +35,7 @@ fun Sync_state(context: GameTestHelper) = context.sequence {
|
|||
context.givePocketComputer(unique)
|
||||
}
|
||||
// Write some text to the computer.
|
||||
thenOnComputer(unique) { getApi<TermAPI>().write(Coerced("Hello, world!")) }
|
||||
thenOnComputer(unique) { getApi<TermAPI>().write(ObjectArguments("Hello, world!")) }
|
||||
// And ensure its synced to the client.
|
||||
thenIdle(4)
|
||||
thenOnClient {
|
||||
|
@ -49,7 +50,7 @@ fun Sync_state(context: GameTestHelper) = context.sequence {
|
|||
val term = getApi<TermAPI>()
|
||||
term.setCursorPos(1, 1)
|
||||
term.setCursorBlink(true)
|
||||
term.write(Coerced("Updated text :)"))
|
||||
term.write(ObjectArguments("Updated text :)"))
|
||||
}
|
||||
// And ensure the new computer state and terminal are sent.
|
||||
thenIdle(4)
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
import dan200.computercraft.core.util.StringUtil;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A base class for all objects which interact with a terminal. Namely the {@link TermAPI} and monitors.
|
||||
|
@ -37,30 +38,15 @@ private static int getHighestBit(int group) {
|
|||
* @throws LuaException (hidden) If the terminal cannot be found.
|
||||
*/
|
||||
@LuaFunction
|
||||
public final void write(Coerced<String> textA) throws LuaException {
|
||||
var text = textA.value();
|
||||
var terminal = getTerminal();
|
||||
synchronized (terminal) {
|
||||
terminal.write(text);
|
||||
terminal.setCursorPos(terminal.getCursorX() + text.length(), terminal.getCursorY());
|
||||
public final void write(IArguments textA) throws LuaException {
|
||||
String text;
|
||||
if(textA.get(0) instanceof Map utfString && utfString.containsKey("bytestring")){
|
||||
text = utfString.get("bytestring").toString();
|
||||
text = StringUtil.byteStringToUtf8(text);
|
||||
}
|
||||
else{
|
||||
text = textA.getStringCoerced(0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write utf8 encoded byte string{@code text} at the current cursor position, moving the cursor to the end of the text.
|
||||
* <p>
|
||||
* Unlike functions like {@code write} and {@code print}, this does not wrap the text - it simply copies the
|
||||
* text to the current terminal line.
|
||||
* <p>
|
||||
* Developer should not use this function directly, but use the {@code utflib.term.write} to write Unicode strings.
|
||||
*
|
||||
* @param textA The utf8 encoded byte string to write.
|
||||
* @throws LuaException (hidden) If the terminal cannot be found.
|
||||
*/
|
||||
@LuaFunction
|
||||
public final void _writeutf8(Coerced<String> textA) throws LuaException {
|
||||
var text = textA.value();
|
||||
text = StringUtil.byteStringToUtf8(text);
|
||||
var terminal = getTerminal();
|
||||
synchronized (terminal) {
|
||||
terminal.write(text);
|
||||
|
@ -274,53 +260,25 @@ public final boolean getIsColour() throws LuaException {
|
|||
* }</pre>
|
||||
*/
|
||||
@LuaFunction
|
||||
public final void blit(ByteBuffer text, ByteBuffer textColour, ByteBuffer backgroundColour) throws LuaException {
|
||||
if (textColour.remaining() != text.remaining() || backgroundColour.remaining() != text.remaining()) {
|
||||
throw new LuaException(String.format("Arguments must be the same length (%d, %d, %d)", text.remaining(), textColour.remaining(), backgroundColour.remaining()));
|
||||
public final void blit(IArguments args) throws LuaException {
|
||||
String text;
|
||||
if(args.get(0) instanceof Map utfString && utfString.containsKey("bytestring")){
|
||||
text = utfString.get("bytestring").toString();
|
||||
text = StringUtil.byteStringToUtf8(text);
|
||||
}
|
||||
|
||||
var terminal = getTerminal();
|
||||
synchronized (terminal) {
|
||||
terminal.blit(text, textColour, backgroundColour);
|
||||
terminal.setCursorPos(terminal.getCursorX() + text.remaining(), terminal.getCursorY());
|
||||
else{
|
||||
text = args.getString(0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes utf8 encoded byte string {@code text} to the terminal with the specific foreground and background colours.
|
||||
* <p>
|
||||
* As with {@link #write(Coerced)}, the text will be written at the current cursor location, with the cursor
|
||||
* moving to the end of the text.
|
||||
* <p>
|
||||
* {@code textColour} and {@code backgroundColour} must both be strings the same length as teh decoded {@code text}.
|
||||
* All characters represent a single hexadecimal digit, which is converted to one of CC's colours. For instance,
|
||||
* {@code "a"} corresponds to purple.
|
||||
* <p>
|
||||
* Developer should not use this function directly, but use the {@code utflib.blit} to write Unicode strings.
|
||||
*
|
||||
* @param text The utf8 encoded byte string to write.
|
||||
* @param textColour The corresponding text colours.
|
||||
* @param backgroundColour The corresponding background colours.
|
||||
* @throws LuaException If the three inputs are not the same length.
|
||||
* @cc.see colors For a list of colour constants, and their hexadecimal values.
|
||||
* @cc.since 1.74
|
||||
* @cc.changed 1.80pr1 Standard computers can now use all 16 colors, being changed to grayscale on screen.
|
||||
* @cc.usage Prints "Hello, world!" in rainbow text.
|
||||
* <pre>{@code
|
||||
* term.blit("Hello, world!","01234456789ab","0000000000000")
|
||||
* }</pre>
|
||||
*/
|
||||
@LuaFunction
|
||||
public final void _blitutf8(String text, ByteBuffer textColour, ByteBuffer backgroundColour) throws LuaException {
|
||||
text = StringUtil.byteStringToUtf8(text);
|
||||
var textLen = text.codePointCount(0, text.length());
|
||||
ByteBuffer textColour = args.getBytes(1);
|
||||
ByteBuffer backgroundColour = args.getBytes(2);
|
||||
if (textColour.remaining() != textLen || backgroundColour.remaining() != textLen) {
|
||||
throw new LuaException("Arguments must be the same length");
|
||||
}
|
||||
|
||||
var terminal = getTerminal();
|
||||
synchronized (terminal) {
|
||||
terminal.blitUtf(text, textColour, backgroundColour);
|
||||
terminal.blit(text, textColour, backgroundColour);
|
||||
terminal.setCursorPos(terminal.getCursorX() + textLen, terminal.getCursorY());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -176,11 +176,11 @@ public synchronized void blit(ByteBuffer text, ByteBuffer textColour, ByteBuffer
|
|||
}
|
||||
}
|
||||
|
||||
public synchronized void blitUtf(String utf, ByteBuffer textColour, ByteBuffer backgroundColour) {
|
||||
public synchronized void blit(String text, ByteBuffer textColour, ByteBuffer backgroundColour) {
|
||||
var x = cursorX;
|
||||
var y = cursorY;
|
||||
if (y >= 0 && y < height) {
|
||||
this.text[y].write(utf, x);
|
||||
this.text[y].write(text, x);
|
||||
this.textColour[y].write(textColour, x);
|
||||
this.backgroundColour[y].write(backgroundColour, x);
|
||||
setChanged();
|
||||
|
|
|
@ -94,7 +94,9 @@ function sleep(nTime)
|
|||
end
|
||||
|
||||
function write(sText)
|
||||
expect(1, sText, "string", "number")
|
||||
if not utflib.isUTFString(sText) then
|
||||
expect(1, sText, "string", "number")
|
||||
end
|
||||
|
||||
local w, h = term.getSize()
|
||||
local x, y = term.getCursorPos()
|
||||
|
@ -112,26 +114,28 @@ function write(sText)
|
|||
end
|
||||
|
||||
-- Print the line with proper word wrapping
|
||||
sText = tostring(sText)
|
||||
if not utflib.isUTFString(sText) then
|
||||
sText = tostring(sText)
|
||||
end
|
||||
while #sText > 0 do
|
||||
local whitespace = string.match(sText, "^[ \t]+")
|
||||
local whitespace = sText:match("^[ \t]+")
|
||||
if whitespace then
|
||||
-- Print whitespace
|
||||
term.write(whitespace)
|
||||
x, y = term.getCursorPos()
|
||||
sText = string.sub(sText, #whitespace + 1)
|
||||
sText = sText:sub(#whitespace + 1)
|
||||
end
|
||||
|
||||
local newline = string.match(sText, "^\n")
|
||||
local newline = sText:match("^\n")
|
||||
if newline then
|
||||
-- Print newlines
|
||||
newLine()
|
||||
sText = string.sub(sText, 2)
|
||||
sText = sText:sub(2)
|
||||
end
|
||||
|
||||
local text = string.match(sText, "^[^ \t\n]+")
|
||||
local text = sText:match("^[^ \t\n]+")
|
||||
if text then
|
||||
sText = string.sub(sText, #text + 1)
|
||||
sText = sText:sub(#text + 1)
|
||||
if #text > w then
|
||||
-- Print a multiline word
|
||||
while #text > 0 do
|
||||
|
@ -139,7 +143,7 @@ function write(sText)
|
|||
newLine()
|
||||
end
|
||||
term.write(text)
|
||||
text = string.sub(text, w - x + 2)
|
||||
text = text:sub(w - x + 2)
|
||||
x, y = term.getCursorPos()
|
||||
end
|
||||
else
|
||||
|
@ -160,7 +164,8 @@ function print(...)
|
|||
local nLinesPrinted = 0
|
||||
local nLimit = select("#", ...)
|
||||
for n = 1, nLimit do
|
||||
local s = tostring(select(n, ...))
|
||||
local s = select(n, ...)
|
||||
if not utflib.isUTFString(s) then s = tostring(s) end
|
||||
if n < nLimit then
|
||||
s = s .. "\t"
|
||||
end
|
||||
|
@ -182,16 +187,17 @@ function printError(...)
|
|||
end
|
||||
end
|
||||
|
||||
function read(_sReplaceChar, _tHistory, _fnComplete, _sDefault)
|
||||
expect(1, _sReplaceChar, "string", "nil")
|
||||
function read(_sReplaceChar, _tHistory, _fnComplete, _sDefault, _bReadUnicode)
|
||||
if not utflib.isUTFString(_sReplaceChar) then expect(1, _sReplaceChar, "string", "nil") end
|
||||
expect(2, _tHistory, "table", "nil")
|
||||
expect(3, _fnComplete, "function", "nil")
|
||||
expect(4, _sDefault, "string", "nil")
|
||||
if not utflib.isUTFString(_sDefault) then expect(4, _sDefault, "string", "nil") end
|
||||
expect(5, _bReadUnicode, "boolean", "nil")
|
||||
|
||||
term.setCursorBlink(true)
|
||||
|
||||
local sLine
|
||||
if type(_sDefault) == "string" then
|
||||
if type(_sDefault) ~= "nil" then
|
||||
sLine = _sDefault
|
||||
else
|
||||
sLine = ""
|
||||
|
@ -199,7 +205,7 @@ function read(_sReplaceChar, _tHistory, _fnComplete, _sDefault)
|
|||
local nHistoryPos
|
||||
local nPos, nScroll = #sLine, 0
|
||||
if _sReplaceChar then
|
||||
_sReplaceChar = string.sub(_sReplaceChar, 1, 1)
|
||||
_sReplaceChar = _sReplaceChar:sub(1, 1)
|
||||
end
|
||||
|
||||
local tCompletions
|
||||
|
@ -240,9 +246,9 @@ function read(_sReplaceChar, _tHistory, _fnComplete, _sDefault)
|
|||
term.setCursorPos(sx, cy)
|
||||
local sReplace = _bClear and " " or _sReplaceChar
|
||||
if sReplace then
|
||||
term.write(string.rep(sReplace, math.max(#sLine - nScroll, 0)))
|
||||
term.write(sReplace:rep(math.max(#sLine - nScroll, 0)))
|
||||
else
|
||||
term.write(string.sub(sLine, nScroll + 1))
|
||||
term.write(sLine:sub(nScroll + 1))
|
||||
end
|
||||
|
||||
if nCompletion then
|
||||
|
@ -255,7 +261,7 @@ function read(_sReplaceChar, _tHistory, _fnComplete, _sDefault)
|
|||
term.setBackgroundColor(colors.gray)
|
||||
end
|
||||
if sReplace then
|
||||
term.write(string.rep(sReplace, #sCompletion))
|
||||
term.write(sReplace:rep(#sCompletion))
|
||||
else
|
||||
term.write(sCompletion)
|
||||
end
|
||||
|
@ -295,7 +301,7 @@ function read(_sReplaceChar, _tHistory, _fnComplete, _sDefault)
|
|||
if sEvent == "char" then
|
||||
-- Typed key
|
||||
clear()
|
||||
sLine = string.sub(sLine, 1, nPos) .. param .. string.sub(sLine, nPos + 1)
|
||||
sLine = sLine:sub(1, nPos) .. (_bReadUnicode and utflib.UTFString(param1) or param) .. sLine:sub(nPos + 1)
|
||||
nPos = nPos + 1
|
||||
recomplete()
|
||||
redraw()
|
||||
|
@ -303,8 +309,9 @@ function read(_sReplaceChar, _tHistory, _fnComplete, _sDefault)
|
|||
elseif sEvent == "paste" then
|
||||
-- Pasted text
|
||||
clear()
|
||||
sLine = string.sub(sLine, 1, nPos) .. param .. string.sub(sLine, nPos + 1)
|
||||
nPos = nPos + #param
|
||||
local pastedText = (_bReadUnicode and utflib.UTFString(param1) or param)
|
||||
sLine = sLine:sub(sLine, 1, nPos) .. pastedText .. sLine:sub(nPos + 1)
|
||||
nPos = nPos + #pastedText
|
||||
recomplete()
|
||||
redraw()
|
||||
|
||||
|
@ -394,7 +401,7 @@ function read(_sReplaceChar, _tHistory, _fnComplete, _sDefault)
|
|||
-- Backspace
|
||||
if nPos > 0 then
|
||||
clear()
|
||||
sLine = string.sub(sLine, 1, nPos - 1) .. string.sub(sLine, nPos + 1)
|
||||
sLine = sLine:sub(1, nPos - 1) .. sLine:sub(nPos + 1)
|
||||
nPos = nPos - 1
|
||||
if nScroll > 0 then nScroll = nScroll - 1 end
|
||||
recomplete()
|
||||
|
@ -414,7 +421,7 @@ function read(_sReplaceChar, _tHistory, _fnComplete, _sDefault)
|
|||
-- Delete
|
||||
if nPos < #sLine then
|
||||
clear()
|
||||
sLine = string.sub(sLine, 1, nPos) .. string.sub(sLine, nPos + 2)
|
||||
sLine = sLine:sub(1, nPos) .. sLine:sub(nPos + 2)
|
||||
recomplete()
|
||||
redraw()
|
||||
end
|
||||
|
|
|
@ -28,9 +28,12 @@ function slowWrite(text, rate)
|
|||
if rate < 0 then
|
||||
error("Rate must be positive", 2)
|
||||
end
|
||||
if not utflib.isUTFString(text) and type(text) ~= 'string' then
|
||||
text = tostring(text)
|
||||
end
|
||||
local to_sleep = 1 / rate
|
||||
|
||||
local wrapped_lines = wrap(tostring(text), (term.getSize()))
|
||||
local wrapped_lines = wrap(text, (term.getSize()))
|
||||
local wrapped_str = table.concat(wrapped_lines, "\n")
|
||||
|
||||
for n = 1, #wrapped_str do
|
||||
|
@ -180,10 +183,11 @@ local function tabulateCommon(bPaged, ...)
|
|||
if type(t) == "table" then
|
||||
for nu, sItem in pairs(t) do
|
||||
local ty = type(sItem)
|
||||
if ty ~= "string" and ty ~= "number" then
|
||||
if not utflib.isUTFString(ty) and ty ~= "string" and ty ~= "number" then
|
||||
error("bad argument #" .. n .. "." .. nu .. " (string expected, got " .. ty .. ")", 3)
|
||||
end
|
||||
nMaxLen = math.max(#tostring(sItem) + 1, nMaxLen)
|
||||
sItem1 = ty == "number" and tostring(sItem) or sItem
|
||||
nMaxLen = math.max(#sItem1 + 1, nMaxLen)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -898,22 +902,17 @@ unserialiseJSON = unserialise_json
|
|||
-- @usage print("https://example.com/?view=" .. textutils.urlEncode("some text&things"))
|
||||
-- @since 1.31
|
||||
function urlEncode(str)
|
||||
expect(1, str, "string")
|
||||
if not utflib.isUTFString(str) then
|
||||
expect(1, str, "string")
|
||||
end
|
||||
if str then
|
||||
str = string.gsub(str, "\n", "\r\n")
|
||||
str = string.gsub(str, "([^A-Za-z0-9 %-%_%.])", function(c)
|
||||
str = tostring(utflib.fromLatin(str))
|
||||
str = str:gsub("\n", "\r\n")
|
||||
str = str:gsub("([^A-Za-z0-9 %-%_%.])", function(c)
|
||||
local n = string.byte(c)
|
||||
if n < 128 then
|
||||
-- ASCII
|
||||
return string.format("%%%02X", n)
|
||||
else
|
||||
-- Non-ASCII (encode as UTF-8)
|
||||
return
|
||||
string.format("%%%02X", 192 + bit32.band(bit32.arshift(n, 6), 31)) ..
|
||||
string.format("%%%02X", 128 + bit32.band(n, 63))
|
||||
end
|
||||
return string.format("%%%02X", n)
|
||||
end)
|
||||
str = string.gsub(str, " ", "+")
|
||||
str = str:gsub(" ", "+")
|
||||
end
|
||||
return str
|
||||
end
|
||||
|
@ -938,27 +937,29 @@ local tEmpty = {}
|
|||
-- @usage textutils.complete( "pa", _ENV )
|
||||
-- @since 1.74
|
||||
function complete(sSearchText, tSearchTable)
|
||||
expect(1, sSearchText, "string")
|
||||
if not utflib.isUTFString(sSearchText) then
|
||||
expect(1, sSearchText, "string")
|
||||
end
|
||||
expect(2, tSearchTable, "table", "nil")
|
||||
|
||||
if g_tLuaKeywords[sSearchText] then return tEmpty end
|
||||
if g_tLuaKeywords[tostring(sSearchText)] then return tEmpty end
|
||||
local nStart = 1
|
||||
local nDot = string.find(sSearchText, ".", nStart, true)
|
||||
local nDot = sSearchText:find(".", nStart, true)
|
||||
local tTable = tSearchTable or _ENV
|
||||
while nDot do
|
||||
local sPart = string.sub(sSearchText, nStart, nDot - 1)
|
||||
local sPart = tostring(sSearchText:sub(nStart, nDot - 1))
|
||||
local value = tTable[sPart]
|
||||
if type(value) == "table" then
|
||||
tTable = value
|
||||
nStart = nDot + 1
|
||||
nDot = string.find(sSearchText, ".", nStart, true)
|
||||
nDot = sSearchText:find(".", nStart, true)
|
||||
else
|
||||
return tEmpty
|
||||
end
|
||||
end
|
||||
local nColon = string.find(sSearchText, ":", nStart, true)
|
||||
local nColon = sSearchText:find(":", nStart, true)
|
||||
if nColon then
|
||||
local sPart = string.sub(sSearchText, nStart, nColon - 1)
|
||||
local sPart = tostring(sSearchText:sub(nStart, nColon - 1))
|
||||
local value = tTable[sPart]
|
||||
if type(value) == "table" then
|
||||
tTable = value
|
||||
|
@ -968,7 +969,7 @@ function complete(sSearchText, tSearchTable)
|
|||
end
|
||||
end
|
||||
|
||||
local sPart = string.sub(sSearchText, nStart)
|
||||
local sPart = sSearchText:sub(nStart)
|
||||
local nPartLength = #sPart
|
||||
|
||||
local tResults = {}
|
||||
|
@ -976,9 +977,9 @@ function complete(sSearchText, tSearchTable)
|
|||
while tTable do
|
||||
for k, v in pairs(tTable) do
|
||||
if not tSeen[k] and type(k) == "string" then
|
||||
if string.find(k, sPart, 1, true) == 1 then
|
||||
if not g_tLuaKeywords[k] and string.match(k, "^[%a_][%a%d_]*$") then
|
||||
local sResult = string.sub(k, nPartLength + 1)
|
||||
if k:find(sPart, 1, true) == 1 then
|
||||
if not g_tLuaKeywords[k] and k:match("^[%a_][%a%d_]*$") then
|
||||
local sResult = k:sub(nPartLength + 1)
|
||||
if nColon then
|
||||
if type(v) == "function" then
|
||||
table.insert(tResults, sResult .. "(")
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
local expect = (require and require("cc.expect") or dofile("rom/modules/main/cc/expect.lua")).expect
|
||||
local expect = (dofile("rom/modules/main/cc/expect.lua")).expect
|
||||
|
||||
local UTFString = {}
|
||||
UTFString = {}
|
||||
local UTFString_mt = {
|
||||
__index = UTFString, __name = 'UTFString'
|
||||
}
|
||||
|
@ -12,6 +12,8 @@ local StringWrapper_mt = {
|
|||
__len = function() return #self.str end
|
||||
}
|
||||
|
||||
wrapStr = StringWrapper
|
||||
|
||||
local function rectify_string(s)
|
||||
-- check a string is a valid utf8 byte sequence
|
||||
-- if an invalid byte is found, consider it as raw codepoint and convert it to valid byte sequence
|
||||
|
@ -32,11 +34,11 @@ local function rectify_string(s)
|
|||
return result
|
||||
end
|
||||
|
||||
local function isUTFString(obj)
|
||||
function isUTFString(obj)
|
||||
return type(obj) == 'table' and getmetatable(obj) == UTFString_mt
|
||||
end
|
||||
|
||||
local function isStringWrapper(obj)
|
||||
function isStringWrapper(obj)
|
||||
return type(obj) == 'table' and getmetatable(obj) == StringWrapper_mt
|
||||
end
|
||||
|
||||
|
@ -59,7 +61,7 @@ end
|
|||
|
||||
setmetatable(StringWrapper, {__call = StringWrapper.new })
|
||||
|
||||
local function from_latin(s)
|
||||
function fromLatin(s)
|
||||
local str = ""
|
||||
for _, c in string.codes(s) do
|
||||
str = str .. utf8.char(c)
|
||||
|
@ -206,7 +208,7 @@ function UTFString:byte(i, j)
|
|||
return utf8.codepoint(self.bytestring, utf8.offset(self.bytestring, i), j and utf8.offset(self.bytestring, j))
|
||||
end
|
||||
|
||||
function UTFString:to_latin()
|
||||
function UTFString:toLatin()
|
||||
local str = ""
|
||||
for _, c in utf8.codes(self.bytestring) do
|
||||
if c > 255 then str = str .. "?"
|
||||
|
@ -787,11 +789,3 @@ function UTFString:reverse()
|
|||
for i = codes.n, 1, -1 do s = s .. utf8.char(codes[i]) end
|
||||
return unsafe_utfstring(s)
|
||||
end
|
||||
|
||||
return {
|
||||
UTFString = UTFString,
|
||||
from_latin = from_latin,
|
||||
isUTFString = isUTFString,
|
||||
isStringWrapper = isStringWrapper,
|
||||
wrap_str = StringWrapper
|
||||
}
|
|
@ -34,9 +34,6 @@ parent, and only one of which is visible at a time.
|
|||
]]
|
||||
|
||||
local expect = dofile("rom/modules/main/cc/expect.lua").expect
|
||||
local utflib = dofile("rom/modules/main/cc/utflib.lua")
|
||||
local isUTFString = utflib.isUTFString
|
||||
local u = utflib.UTFString
|
||||
|
||||
local tHex = {
|
||||
[colors.white] = "0",
|
||||
|
@ -164,9 +161,7 @@ function create(parent, nX, nY, nWidth, nHeight, bStartVisible)
|
|||
local function redrawLine(n)
|
||||
local tLine = tLines[n]
|
||||
parent.setCursorPos(nX, nY + n - 1)
|
||||
if isUTFString(tLine[1]) then
|
||||
parent._blitutf8(tostring(tLine[1]), tLine[2], tLine[3])
|
||||
else parent.blit(tLine[1], tLine[2], tLine[3]) end
|
||||
parent.blit(tLine[1], tLine[2], tLine[3])
|
||||
end
|
||||
|
||||
local function redraw()
|
||||
|
@ -260,35 +255,12 @@ function create(parent, nX, nY, nWidth, nHeight, bStartVisible)
|
|||
local window = {}
|
||||
|
||||
function window.write(sText)
|
||||
sText = tostring(sText)
|
||||
if not utflib.isUTFString(sText) then sText = tostring(sText) end
|
||||
internalBlit(sText, string_rep(tHex[nTextColor], #sText), string_rep(tHex[nBackgroundColor], #sText))
|
||||
end
|
||||
|
||||
function window._writeutf8(utfText)
|
||||
if not isUTFString(utfText) then
|
||||
if type(utfText) ~= "string" then utfText = tostring(utfText) end
|
||||
utfText = u(utfText)
|
||||
end
|
||||
internalBlit(utfText, string_rep(tHex[nTextColor], #utfText), string_rep(tHex[nBackgroundColor], #utfText))
|
||||
end
|
||||
|
||||
function window.blit(sText, sTextColor, sBackgroundColor)
|
||||
if type(sText) ~= "string" then expect(1, sText, "string") end
|
||||
if type(sTextColor) ~= "string" then expect(2, sTextColor, "string") end
|
||||
if type(sBackgroundColor) ~= "string" then expect(3, sBackgroundColor, "string") end
|
||||
if #sTextColor ~= #sText or #sBackgroundColor ~= #sText then
|
||||
error("Arguments must be the same length", 2)
|
||||
end
|
||||
sTextColor = sTextColor:lower()
|
||||
sBackgroundColor = sBackgroundColor:lower()
|
||||
internalBlit(sText, sTextColor, sBackgroundColor)
|
||||
end
|
||||
|
||||
function window._blitutf8(sText, sTextColor, sBackgroundColor)
|
||||
if not isUTFString(sText) then
|
||||
expect(1, sText, "string")
|
||||
sText = u(sText)
|
||||
end
|
||||
if not utflib.isUTFString(sText) and type(sText) ~= "string" then expect(1, sText, "string", "UTFString") end
|
||||
if type(sTextColor) ~= "string" then expect(2, sTextColor, "string") end
|
||||
if type(sBackgroundColor) ~= "string" then expect(3, sBackgroundColor, "string") end
|
||||
if #sTextColor ~= #sText or #sBackgroundColor ~= #sText then
|
||||
|
@ -494,9 +466,7 @@ function create(parent, nX, nY, nWidth, nHeight, bStartVisible)
|
|||
end
|
||||
|
||||
local line = tLines[y]
|
||||
if isUTFString(line[1]) then
|
||||
return tostring(line[1]), line[2], line[3]
|
||||
else return line[1], line[2], line[3] end
|
||||
return line[1], line[2], line[3]
|
||||
end
|
||||
|
||||
-- Other functions
|
||||
|
|
|
@ -29,7 +29,9 @@ the terminal.
|
|||
end
|
||||
]]
|
||||
local function wrap(text, width)
|
||||
expect(1, text, "string")
|
||||
if not utflib.isUTFString(text) then
|
||||
expect(1, text, "string")
|
||||
end
|
||||
expect(2, width, "number", "nil")
|
||||
width = width or term.getSize()
|
||||
|
||||
|
@ -42,18 +44,17 @@ local function wrap(text, width)
|
|||
end
|
||||
|
||||
local pos, length = 1, #text
|
||||
local sub, match = string.sub, string.match
|
||||
while pos <= length do
|
||||
local head = sub(text, pos, pos)
|
||||
local head = text:sub(pos, pos)
|
||||
if head == " " or head == "\t" then
|
||||
local whitespace = match(text, "^[ \t]+", pos)
|
||||
local whitespace = text:match("^[ \t]+", pos)
|
||||
current_line = current_line .. whitespace
|
||||
pos = pos + #whitespace
|
||||
elseif head == "\n" then
|
||||
push_line()
|
||||
pos = pos + 1
|
||||
else
|
||||
local word = match(text, "^[^ \t\n]+", pos)
|
||||
local word = text:match("^[^ \t\n]+", pos)
|
||||
pos = pos + #word
|
||||
if #word > width then
|
||||
-- Print a multiline word
|
||||
|
@ -64,8 +65,8 @@ local function wrap(text, width)
|
|||
space_remaining = width
|
||||
end
|
||||
|
||||
current_line = current_line .. sub(word, 1, space_remaining)
|
||||
word = sub(word, space_remaining + 1)
|
||||
current_line = current_line .. word:sub(1, space_remaining)
|
||||
word = word:sub(space_remaining + 1)
|
||||
end
|
||||
else
|
||||
-- Print a word normally
|
||||
|
@ -97,7 +98,9 @@ end
|
|||
-- @usage require "cc.strings".ensure_width("a short string", 20)
|
||||
-- @usage require "cc.strings".ensure_width("a rather long string which is truncated", 20)
|
||||
local function ensure_width(line, width)
|
||||
expect(1, line, "string")
|
||||
if not isUTFString(line) then
|
||||
expect(1, line, "string")
|
||||
end
|
||||
expect(2, width, "number", "nil")
|
||||
width = width or term.getSize()
|
||||
|
||||
|
|
|
@ -1,119 +0,0 @@
|
|||
-- SPDX-FileCopyrightText: 2020 The CC: Tweaked Developers
|
||||
--
|
||||
-- SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
--- Various utilities for working with strings and text.
|
||||
--
|
||||
-- @module cc.strings
|
||||
-- @since 1.95.0
|
||||
-- @see textutils For additional string related utilities.
|
||||
|
||||
local expect = (require and require("cc.expect") or dofile("rom/modules/main/cc/expect.lua")).expect
|
||||
local utflib = (require and require("cc.utflib") or dofile("rom/modules/main/cc/utflib.lua"))
|
||||
|
||||
--[[- 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}.
|
||||
|
||||
@tparam string text The string to wrap.
|
||||
@tparam[opt] number width The width to constrain to, defaults to the width of
|
||||
the terminal.
|
||||
@treturn { string... } The wrapped input string as a list of lines.
|
||||
@usage Wrap a string and write it to the terminal.
|
||||
|
||||
term.clear()
|
||||
local lines = require "cc.strings".wrap("This is a long piece of text", 10)
|
||||
for i = 1, #lines do
|
||||
term.setCursorPos(1, i)
|
||||
term.write(lines[i])
|
||||
end
|
||||
]]
|
||||
local function wrap(text, width)
|
||||
if not utflib.isUTFString(text) then
|
||||
expect(1, text, "string")
|
||||
end
|
||||
expect(2, width, "number", "nil")
|
||||
width = width or term.getSize()
|
||||
|
||||
|
||||
local lines, lines_n, current_line = {}, 0, ""
|
||||
local function push_line()
|
||||
lines_n = lines_n + 1
|
||||
lines[lines_n] = current_line
|
||||
current_line = ""
|
||||
end
|
||||
|
||||
local pos, length = 1, #text
|
||||
while pos <= length do
|
||||
local head = text:sub(pos, pos)
|
||||
if head == " " or head == "\t" then
|
||||
local whitespace = text:match("^[ \t]+", pos)
|
||||
current_line = current_line .. whitespace
|
||||
pos = pos + #whitespace
|
||||
elseif head == "\n" then
|
||||
push_line()
|
||||
pos = pos + 1
|
||||
else
|
||||
local word = text:match("^[^ \t\n]+", pos)
|
||||
pos = pos + #word
|
||||
if #word > width then
|
||||
-- Print a multiline word
|
||||
while #word > 0 do
|
||||
local space_remaining = width - #current_line - 1
|
||||
if space_remaining <= 0 then
|
||||
push_line()
|
||||
space_remaining = width
|
||||
end
|
||||
|
||||
current_line = current_line .. word:sub(1, space_remaining)
|
||||
word = word:sub(space_remaining + 1)
|
||||
end
|
||||
else
|
||||
-- Print a word normally
|
||||
if width - #current_line < #word then push_line() end
|
||||
current_line = current_line .. word
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
push_line()
|
||||
|
||||
-- Trim whitespace longer than width.
|
||||
for k, line in pairs(lines) do
|
||||
line = line:sub(1, width)
|
||||
lines[k] = line
|
||||
end
|
||||
|
||||
return lines
|
||||
end
|
||||
|
||||
--- Makes the input string a fixed width. This either truncates it, or pads it
|
||||
-- with spaces.
|
||||
--
|
||||
-- @tparam string line The string to normalise.
|
||||
-- @tparam[opt] number width The width to constrain to, defaults to the width of
|
||||
-- the terminal.
|
||||
--
|
||||
-- @treturn string The string with a specific width.
|
||||
-- @usage require "cc.strings".ensure_width("a short string", 20)
|
||||
-- @usage require "cc.strings".ensure_width("a rather long string which is truncated", 20)
|
||||
local function ensure_width(line, width)
|
||||
if not isUTFString(line) then
|
||||
expect(1, line, "string")
|
||||
end
|
||||
expect(2, width, "number", "nil")
|
||||
width = width or term.getSize()
|
||||
|
||||
line = line:sub(1, width)
|
||||
if #line < width then
|
||||
line = line .. (" "):rep(width - #line)
|
||||
end
|
||||
|
||||
return line
|
||||
end
|
||||
|
||||
return {
|
||||
wrap = wrap,
|
||||
ensure_width = ensure_width,
|
||||
}
|
|
@ -1,392 +0,0 @@
|
|||
local expect = (require and require("cc.expect") or dofile("rom/modules/main/cc/expect.lua")).expect
|
||||
local utflib = (require and require("cc.utflib") or dofile("rom/modules/main/cc/utflib.lua"))
|
||||
|
||||
local function twrite(sText)
|
||||
if not utflib.isUTFString(sText) then
|
||||
expect(1, sText, "UTFString") -- type(sText) == "UTFString" should never happened. Just use for triggering errors
|
||||
end
|
||||
term._writeutf8(tostring(sText))
|
||||
end
|
||||
|
||||
local function writeutf8(sText, targetTerm)
|
||||
if not utflib.isUTFString(sText) then
|
||||
expect(1, sText, "string", "number")
|
||||
end
|
||||
expect(2, targetTerm, "table", "nil")
|
||||
targetTerm = targetTerm or term
|
||||
|
||||
local w, h = targetTerm.getSize()
|
||||
local x, y = targetTerm.getCursorPos()
|
||||
|
||||
local nLinesPrinted = 0
|
||||
local function newLine()
|
||||
if y + 1 <= h then
|
||||
targetTerm.setCursorPos(1, y + 1)
|
||||
else
|
||||
targetTerm.setCursorPos(1, h)
|
||||
targetTerm.scroll(1)
|
||||
end
|
||||
x, y = targetTerm.getCursorPos()
|
||||
nLinesPrinted = nLinesPrinted + 1
|
||||
end
|
||||
|
||||
-- Print the line with proper word wrapping
|
||||
if not utflib.isUTFString(sText) then
|
||||
sText = utflib.UTFString(tostring(sText))
|
||||
end
|
||||
while #sText > 0 do
|
||||
local whitespace = sText:match("^[ \t]+")
|
||||
if whitespace then
|
||||
-- Print whitespace
|
||||
targetTerm.write(whitespace)
|
||||
x, y = term.getCursorPos()
|
||||
sText = sText:sub(#whitespace + 1)
|
||||
end
|
||||
|
||||
local newline = sText:match("^\n")
|
||||
if newline then
|
||||
-- Print newlines
|
||||
newLine()
|
||||
sText = sText:sub(2)
|
||||
end
|
||||
|
||||
local text = sText:match("^[^ \t\n]+")
|
||||
if text then
|
||||
sText = sText:sub(#text + 1)
|
||||
if #text > w then
|
||||
-- Print a multiline word
|
||||
while #text > 0 do
|
||||
if x > w then
|
||||
newLine()
|
||||
end
|
||||
targetTerm._writeutf8(tostring(text))
|
||||
text = text:sub(w - x + 2)
|
||||
x, y = term.getCursorPos()
|
||||
end
|
||||
else
|
||||
-- Print a word normally
|
||||
if x + #text - 1 > w then
|
||||
newLine()
|
||||
end
|
||||
targetTerm._writeutf8(tostring(text))
|
||||
x, y = term.getCursorPos()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return nLinesPrinted
|
||||
end
|
||||
|
||||
local function printutf8(...)
|
||||
local nLinesPrinted = 0
|
||||
local nLimit = select("#", ...)
|
||||
for n = 1, nLimit do
|
||||
local s = select(n, ...)
|
||||
if not utflib.isUTFString(s) then s = tostring(s) end
|
||||
if n < nLimit then
|
||||
s = s .. "\t"
|
||||
end
|
||||
nLinesPrinted = nLinesPrinted + writeutf8(s)
|
||||
end
|
||||
nLinesPrinted = nLinesPrinted + writeutf8("\n")
|
||||
return nLinesPrinted
|
||||
end
|
||||
|
||||
local function printErrorutf8(...)
|
||||
local oldColour
|
||||
if term.isColour() then
|
||||
oldColour = term.getTextColour()
|
||||
term.setTextColour(colors.red)
|
||||
end
|
||||
printutf8(...)
|
||||
if term.isColour() then
|
||||
term.setTextColour(oldColour)
|
||||
end
|
||||
end
|
||||
|
||||
local function readutf8(_sReplaceChar, _tHistory, _fnComplete, _sDefault)
|
||||
if not utflib.isUTFString(_sReplaceChar) then expect(1, _sReplaceChar, "string", "nil") end
|
||||
expect(2, _tHistory, "table", "nil")
|
||||
expect(3, _fnComplete, "function", "nil")
|
||||
if not utflib.isUTFString(_sDefault) then expect(4, _sDefault, "string", "nil") end
|
||||
|
||||
term.setCursorBlink(true)
|
||||
|
||||
local sLine
|
||||
if type(_sDefault) ~= "nil" then
|
||||
sLine = _sDefault
|
||||
else
|
||||
sLine = ""
|
||||
end
|
||||
sLine = utflib.UTFString(sLine)
|
||||
local nHistoryPos
|
||||
local nPos, nScroll = #sLine, 0
|
||||
if _sReplaceChar then
|
||||
_sReplaceChar = _sReplaceChar:sub(1, 1)
|
||||
end
|
||||
|
||||
local tCompletions
|
||||
local nCompletion
|
||||
local function recomplete()
|
||||
if _fnComplete and nPos == #sLine then
|
||||
tCompletions = _fnComplete(tostring(sLine))
|
||||
if tCompletions and #tCompletions > 0 then
|
||||
nCompletion = 1
|
||||
else
|
||||
nCompletion = nil
|
||||
end
|
||||
else
|
||||
tCompletions = nil
|
||||
nCompletion = nil
|
||||
end
|
||||
end
|
||||
|
||||
local function uncomplete()
|
||||
tCompletions = nil
|
||||
nCompletion = nil
|
||||
end
|
||||
|
||||
local w = term.getSize()
|
||||
local sx = term.getCursorPos()
|
||||
|
||||
local function redraw(_bClear)
|
||||
local cursor_pos = nPos - nScroll
|
||||
if sx + cursor_pos >= w then
|
||||
-- We've moved beyond the RHS, ensure we're on the edge.
|
||||
nScroll = sx + nPos - w
|
||||
elseif cursor_pos < 0 then
|
||||
-- We've moved beyond the LHS, ensure we're on the edge.
|
||||
nScroll = nPos
|
||||
end
|
||||
|
||||
local _, cy = term.getCursorPos()
|
||||
term.setCursorPos(sx, cy)
|
||||
local sReplace = _bClear and " " or _sReplaceChar
|
||||
if sReplace then
|
||||
term._writeutf8(sReplace:rep(math.max(#sLine - nScroll, 0)))
|
||||
else
|
||||
term._writeutf8(sLine:sub(nScroll + 1))
|
||||
end
|
||||
|
||||
if nCompletion then
|
||||
local sCompletion = tCompletions[nCompletion]
|
||||
local oldText, oldBg
|
||||
if not _bClear then
|
||||
oldText = term.getTextColor()
|
||||
oldBg = term.getBackgroundColor()
|
||||
term.setTextColor(colors.white)
|
||||
term.setBackgroundColor(colors.gray)
|
||||
end
|
||||
if sReplace then
|
||||
term._writeutf8(sReplace:rep(#sCompletion))
|
||||
else
|
||||
term._writeutf8(sCompletion)
|
||||
end
|
||||
if not _bClear then
|
||||
term.setTextColor(oldText)
|
||||
term.setBackgroundColor(oldBg)
|
||||
end
|
||||
end
|
||||
|
||||
term.setCursorPos(sx + nPos - nScroll, cy)
|
||||
end
|
||||
|
||||
local function clear()
|
||||
redraw(true)
|
||||
end
|
||||
|
||||
recomplete()
|
||||
redraw()
|
||||
|
||||
local function acceptCompletion()
|
||||
if nCompletion then
|
||||
-- Clear
|
||||
clear()
|
||||
|
||||
-- Find the common prefix of all the other suggestions which start with the same letter as the current one
|
||||
local sCompletion = tCompletions[nCompletion]
|
||||
sLine = sLine .. sCompletion
|
||||
nPos = #sLine
|
||||
|
||||
-- Redraw
|
||||
recomplete()
|
||||
redraw()
|
||||
end
|
||||
end
|
||||
while true do
|
||||
local sEvent, param, param1, param2 = os.pullEvent()
|
||||
if sEvent == "charutf" then
|
||||
-- Typed key
|
||||
clear()
|
||||
sLine = sLine:sub(1, nPos) .. param .. sLine:sub(nPos + 1)
|
||||
nPos = nPos + 1
|
||||
recomplete()
|
||||
redraw()
|
||||
|
||||
elseif sEvent == "pasteutf" then
|
||||
-- Pasted text
|
||||
clear()
|
||||
param = utflib.UTFString(param)
|
||||
sLine = sLine:sub(1, nPos) .. param .. sLine:sub(nPos + 1)
|
||||
nPos = nPos + #param
|
||||
recomplete()
|
||||
redraw()
|
||||
|
||||
elseif sEvent == "key" then
|
||||
if param == keys.enter or param == keys.numPadEnter then
|
||||
-- Enter/Numpad Enter
|
||||
if nCompletion then
|
||||
clear()
|
||||
uncomplete()
|
||||
redraw()
|
||||
end
|
||||
break
|
||||
|
||||
elseif param == keys.left then
|
||||
-- Left
|
||||
if nPos > 0 then
|
||||
clear()
|
||||
nPos = nPos - 1
|
||||
recomplete()
|
||||
redraw()
|
||||
end
|
||||
|
||||
elseif param == keys.right then
|
||||
-- Right
|
||||
if nPos < #sLine then
|
||||
-- Move right
|
||||
clear()
|
||||
nPos = nPos + 1
|
||||
recomplete()
|
||||
redraw()
|
||||
else
|
||||
-- Accept autocomplete
|
||||
acceptCompletion()
|
||||
end
|
||||
|
||||
elseif param == keys.up or param == keys.down then
|
||||
-- Up or down
|
||||
if nCompletion then
|
||||
-- Cycle completions
|
||||
clear()
|
||||
if param == keys.up then
|
||||
nCompletion = nCompletion - 1
|
||||
if nCompletion < 1 then
|
||||
nCompletion = #tCompletions
|
||||
end
|
||||
elseif param == keys.down then
|
||||
nCompletion = nCompletion + 1
|
||||
if nCompletion > #tCompletions then
|
||||
nCompletion = 1
|
||||
end
|
||||
end
|
||||
redraw()
|
||||
|
||||
elseif _tHistory then
|
||||
-- Cycle history
|
||||
clear()
|
||||
if param == keys.up then
|
||||
-- Up
|
||||
if nHistoryPos == nil then
|
||||
if #_tHistory > 0 then
|
||||
nHistoryPos = #_tHistory
|
||||
end
|
||||
elseif nHistoryPos > 1 then
|
||||
nHistoryPos = nHistoryPos - 1
|
||||
end
|
||||
else
|
||||
-- Down
|
||||
if nHistoryPos == #_tHistory then
|
||||
nHistoryPos = nil
|
||||
elseif nHistoryPos ~= nil then
|
||||
nHistoryPos = nHistoryPos + 1
|
||||
end
|
||||
end
|
||||
if nHistoryPos then
|
||||
sLine = _tHistory[nHistoryPos]
|
||||
nPos, nScroll = #sLine, 0
|
||||
else
|
||||
sLine = ""
|
||||
nPos, nScroll = 0, 0
|
||||
end
|
||||
uncomplete()
|
||||
redraw()
|
||||
|
||||
end
|
||||
|
||||
elseif param == keys.backspace then
|
||||
-- Backspace
|
||||
if nPos > 0 then
|
||||
clear()
|
||||
sLine = sLine:sub(1, nPos - 1) .. sLine:sub(nPos + 1)
|
||||
nPos = nPos - 1
|
||||
if nScroll > 0 then nScroll = nScroll - 1 end
|
||||
recomplete()
|
||||
redraw()
|
||||
end
|
||||
|
||||
elseif param == keys.home then
|
||||
-- Home
|
||||
if nPos > 0 then
|
||||
clear()
|
||||
nPos = 0
|
||||
recomplete()
|
||||
redraw()
|
||||
end
|
||||
|
||||
elseif param == keys.delete then
|
||||
-- Delete
|
||||
if nPos < #sLine then
|
||||
clear()
|
||||
sLine = sLine:sub(1, nPos) .. sLine:sub(nPos + 2)
|
||||
recomplete()
|
||||
redraw()
|
||||
end
|
||||
|
||||
elseif param == keys["end"] then
|
||||
-- End
|
||||
if nPos < #sLine then
|
||||
clear()
|
||||
nPos = #sLine
|
||||
recomplete()
|
||||
redraw()
|
||||
end
|
||||
|
||||
elseif param == keys.tab then
|
||||
-- Tab (accept autocomplete)
|
||||
acceptCompletion()
|
||||
|
||||
end
|
||||
|
||||
elseif sEvent == "mouse_click" or sEvent == "mouse_drag" and param == 1 then
|
||||
local _, cy = term.getCursorPos()
|
||||
if param1 >= sx and param1 <= w and param2 == cy then
|
||||
-- Ensure we don't scroll beyond the current line
|
||||
nPos = math.min(math.max(nScroll + param1 - sx, 0), #sLine)
|
||||
redraw()
|
||||
end
|
||||
|
||||
elseif sEvent == "term_resize" then
|
||||
-- Terminal resized
|
||||
w = term.getSize()
|
||||
redraw()
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
local _, cy = term.getCursorPos()
|
||||
term.setCursorBlink(false)
|
||||
term.setCursorPos(w + 1, cy)
|
||||
print()
|
||||
|
||||
return sLine
|
||||
end
|
||||
|
||||
|
||||
return {
|
||||
twrite = twrite,
|
||||
write = writeutf8,
|
||||
print = printutf8,
|
||||
printError = printErrorutf8,
|
||||
read = readutf8
|
||||
}
|
|
@ -1,308 +0,0 @@
|
|||
local expect = (require and require("cc.expect") or dofile("rom/modules/main/cc/expect.lua"))
|
||||
local utflib = (require and require("cc.utflib") or dofile("rom/modules/main/cc/utflib.lua"))
|
||||
local expect, field = expect.expect, expect.field
|
||||
local wrap = (require and require("cc.utflib.strings") or dofile("rom/modules/main/cc/utflib/strings.lua")).wrap
|
||||
local uterm = (require and require("cc.utflib.term") or dofile("rom/modules/main/cc/utflib/term.lua"))
|
||||
|
||||
local write, print = uterm.write, uterm.print
|
||||
|
||||
local utextutils = {}
|
||||
|
||||
--- Slowly writes string text at current cursor position,
|
||||
-- character-by-character.
|
||||
--
|
||||
-- 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,
|
||||
-- Defaults to 20.
|
||||
-- @usage textutils.slowWrite("Hello, world!")
|
||||
-- @usage textutils.slowWrite("Hello, world!", 5)
|
||||
-- @since 1.3
|
||||
function utextutils.slowWrite(text, rate)
|
||||
expect(2, rate, "number", "nil")
|
||||
rate = rate or 20
|
||||
if rate < 0 then
|
||||
error("Rate must be positive", 2)
|
||||
end
|
||||
if not utflib.isUTFString(text) and type(text) ~= 'string' then
|
||||
text = tostring(text)
|
||||
end
|
||||
local to_sleep = 1 / rate
|
||||
|
||||
local wrapped_lines = wrap(text, (term.getSize()))
|
||||
local wrapped_str = table.concat(wrapped_lines, "\n")
|
||||
|
||||
for n = 1, #wrapped_str do
|
||||
sleep(to_sleep)
|
||||
write(wrapped_str:sub(n, n))
|
||||
end
|
||||
end
|
||||
|
||||
--- Slowly prints string text at current cursor position,
|
||||
-- character-by-character.
|
||||
--
|
||||
-- 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,
|
||||
-- Defaults to 20.
|
||||
-- @usage textutils.slowPrint("Hello, world!")
|
||||
-- @usage textutils.slowPrint("Hello, world!", 5)
|
||||
function utextutils.slowPrint(sText, nRate)
|
||||
slowWrite(sText, nRate)
|
||||
print()
|
||||
end
|
||||
|
||||
--- Takes input time and formats it in a more readable format such as `6:30 PM`.
|
||||
-- differ from the normal version, this accepts custom 12-hour format.
|
||||
--
|
||||
-- @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`)
|
||||
-- @tparam[opt] string amFormat format for time before noon. default to `%s AM`. can be UTFString
|
||||
-- @tparam[opt] string pmFormat format for time after noon. default to `%s PM`. can be UTFString
|
||||
-- @treturn string The formatted time
|
||||
-- @usage Print the current in-game time as a 12-hour clock.
|
||||
--
|
||||
-- textutils.formatTime(os.time())
|
||||
-- @usage Print the local time as a 24-hour clock.
|
||||
--
|
||||
-- textutils.formatTime(os.time("local"), true)
|
||||
function utextutils.formatTime(nTime, bTwentyFourHour, amFormat, pmFormat)
|
||||
expect(1, nTime, "number")
|
||||
expect(2, bTwentyFourHour, "boolean", "nil")
|
||||
if not isUTFString(amFormat) then expect(3, amFormat, "string", "nil") end
|
||||
if not isUTFString(pmFormat) then expect(4, pmFormat, "string", "nil") end
|
||||
local sTOD = nil
|
||||
if not bTwentyFourHour then
|
||||
if nTime >= 12 then
|
||||
sTOD = utflib.UTFString(pmFormat or "%s PM")
|
||||
else
|
||||
sTOD = utflib.UTFString(amFormat or "%s AM")
|
||||
end
|
||||
if nTime >= 13 then
|
||||
nTime = nTime - 12
|
||||
end
|
||||
end
|
||||
|
||||
local nHour = math.floor(nTime)
|
||||
local nMinute = math.floor((nTime - nHour) * 60)
|
||||
if sTOD then
|
||||
return sTOD:format(string.format("%d:%02d", nHour == 0 and 12 or nHour, nMinute))
|
||||
else
|
||||
return utflib.UTFString(string.format("%d:%02d", nHour, nMinute))
|
||||
end
|
||||
end
|
||||
|
||||
local function makePagedScroll(_term, _nFreeLines, _cont_hint)
|
||||
local nativeScroll = _term.scroll
|
||||
local nFreeLines = _nFreeLines or 0
|
||||
_cont_hint = utflib.UTFString(_cont_hint or "Press any key to continue")
|
||||
return function(_n)
|
||||
for _ = 1, _n do
|
||||
nativeScroll(1)
|
||||
|
||||
if nFreeLines <= 0 then
|
||||
local _, h = _term.getSize()
|
||||
_term.setCursorPos(1, h)
|
||||
_term._writeutf8(_cont_hint)
|
||||
os.pullEvent("key")
|
||||
_term.clearLine()
|
||||
_term.setCursorPos(1, h)
|
||||
else
|
||||
nFreeLines = nFreeLines - 1
|
||||
end
|
||||
end
|
||||
end
|
||||
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
|
||||
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.
|
||||
Differ from the normal version, this allows customization on the continue prompt.
|
||||
|
||||
@tparam string text The text to print to the screen.
|
||||
@tparam[opt] number free_lines The number of lines which will be
|
||||
automatically scrolled before the first prompt appears (meaning free_lines +
|
||||
1 lines will be printed). This can be set to the cursor's y position - 2 to
|
||||
always try to fill the screen. Defaults to 0, meaning only one line is
|
||||
displayed before prompting.
|
||||
@tparam[opt] string contHint The continue prompt shown if the action cannot be
|
||||
completed without scrolling. Use the default prompt when not provided. Can be UTFString.
|
||||
@treturn number The number of lines printed.
|
||||
|
||||
@usage Generates several lines of text and then prints it, paging once the
|
||||
bottom of the terminal is reached.
|
||||
|
||||
local lines = {}
|
||||
for i = 1, 30 do lines[i] = ("This is line #%d"):format(i) end
|
||||
local message = table.concat(lines, "\n")
|
||||
|
||||
local width, height = term.getCursorPos()
|
||||
textutils.pagedPrint(message, height - 2)
|
||||
]]
|
||||
function utextutils.pagedPrint(text, free_lines, contHint)
|
||||
expect(2, free_lines, "number", "nil")
|
||||
if not utflib.isUTFString(contHint) then expect(3, contHint, "string", "nil") end
|
||||
-- Setup a redirector
|
||||
local oldTerm = term.current()
|
||||
local newTerm = {}
|
||||
for k, v in pairs(oldTerm) do
|
||||
newTerm[k] = v
|
||||
end
|
||||
|
||||
newTerm.scroll = makePagedScroll(oldTerm, free_lines, contHint)
|
||||
term.redirect(newTerm)
|
||||
|
||||
-- Print the text
|
||||
local result
|
||||
local ok, err = pcall(function()
|
||||
if text ~= nil then
|
||||
result = print(text)
|
||||
else
|
||||
result = print()
|
||||
end
|
||||
end)
|
||||
|
||||
-- Removed the redirector
|
||||
term.redirect(oldTerm)
|
||||
|
||||
-- Propagate errors
|
||||
if not ok then
|
||||
error(err, 0)
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
local function tabulateCommon(bPaged, ...)
|
||||
local tAll = table.pack(...)
|
||||
for i = 1, tAll.n do
|
||||
expect(i, tAll[i], "number", "table")
|
||||
end
|
||||
|
||||
local w, h = term.getSize()
|
||||
local nMaxLen = w / 8
|
||||
for n, t in ipairs(tAll) do
|
||||
if type(t) == "table" then
|
||||
for nu, sItem in pairs(t) do
|
||||
local ty = type(sItem)
|
||||
if not utflib.isUTFString(sItem) and ty ~= "string" and ty ~= "number" then
|
||||
error("bad argument #" .. n .. "." .. nu .. " (string expected, got " .. ty .. ")", 3)
|
||||
end
|
||||
sItem1 = ty == "number" and tostring(sItem) or sItem
|
||||
nMaxLen = math.max(#sItem1 + 1, nMaxLen)
|
||||
end
|
||||
end
|
||||
end
|
||||
local nCols = math.floor(w / nMaxLen)
|
||||
local nLines = 0
|
||||
local function newLine()
|
||||
if bPaged and nLines >= h - 3 then
|
||||
pagedPrint()
|
||||
else
|
||||
print()
|
||||
end
|
||||
nLines = nLines + 1
|
||||
end
|
||||
|
||||
local function drawCols(_t)
|
||||
local nCol = 1
|
||||
for _, s in ipairs(_t) do
|
||||
if nCol > nCols then
|
||||
nCol = 1
|
||||
newLine()
|
||||
end
|
||||
|
||||
local cx, cy = term.getCursorPos()
|
||||
cx = 1 + (nCol - 1) * nMaxLen
|
||||
term.setCursorPos(cx, cy)
|
||||
uterm.write(s, term)
|
||||
|
||||
nCol = nCol + 1
|
||||
end
|
||||
print()
|
||||
end
|
||||
|
||||
local previous_colour = term.getTextColour()
|
||||
for _, t in ipairs(tAll) do
|
||||
if type(t) == "table" then
|
||||
if #t > 0 then
|
||||
drawCols(t)
|
||||
end
|
||||
elseif type(t) == "number" then
|
||||
term.setTextColor(t)
|
||||
end
|
||||
end
|
||||
term.setTextColor(previous_colour)
|
||||
end
|
||||
|
||||
--[[- Prints tables in a structured form.
|
||||
|
||||
This accepts multiple arguments, either a table or a number. When
|
||||
encountering a table, this will be treated as a table row, with each column
|
||||
width being auto-adjusted.
|
||||
|
||||
When encountering a number, this sets the text color of the subsequent rows to it.
|
||||
|
||||
@tparam {string...}|number ... The rows and text colors to display.
|
||||
@since 1.3
|
||||
@usage
|
||||
|
||||
textutils.tabulate(
|
||||
colors.orange, { "1", "2", "3" },
|
||||
colors.lightBlue, { "A", "B", "C" }
|
||||
)
|
||||
]]
|
||||
function utextutils.tabulate(...)
|
||||
return tabulateCommon(false, ...)
|
||||
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
|
||||
input should the whole output not fit on the display.
|
||||
|
||||
@tparam {string...}|number ... The rows and text colors to display.
|
||||
@see textutils.tabulate
|
||||
@see textutils.pagedPrint
|
||||
@since 1.3
|
||||
|
||||
@usage Generates a long table, tabulates it, and prints it to the screen.
|
||||
|
||||
local rows = {}
|
||||
for i = 1, 30 do rows[i] = {("Row #%d"):format(i), math.random(1, 400)} end
|
||||
|
||||
textutils.pagedTabulate(colors.orange, {"Column", "Value"}, colors.lightBlue, table.unpack(rows))
|
||||
]]
|
||||
function utextutils.pagedTabulate(...)
|
||||
return tabulateCommon(true, ...)
|
||||
end
|
||||
|
||||
--- Replaces certain characters in a string to make it safe for use in URLs or POST data.
|
||||
--
|
||||
-- @tparam string str The string to encode
|
||||
-- @treturn string The encoded string.
|
||||
-- @usage print("https://example.com/?view=" .. textutils.urlEncode("some text&things"))
|
||||
-- @since 1.31
|
||||
function utextutils.urlEncode(str)
|
||||
if not utflib.isUTFString(str) then
|
||||
expect(1, str, "string")
|
||||
end
|
||||
if str then
|
||||
str = utflib.UTFString(str)
|
||||
str = str:gsub("\n", "\r\n")
|
||||
str = str:gsub("([^A-Za-z0-9 %-%_%.])", function(c)
|
||||
local n = string.byte(c)
|
||||
return string.format("%%%02X", n)
|
||||
end)
|
||||
str = str:gsub(" ", "+")
|
||||
end
|
||||
return str
|
||||
end
|
||||
|
||||
return utextutils
|
|
@ -339,7 +339,7 @@ while #tProcesses > 0 do
|
|||
resizeWindows()
|
||||
redrawMenu()
|
||||
|
||||
elseif sEvent == "char" or sEvent == "charutf" or sEvent == "key" or sEvent == "key_up" or sEvent == "paste" or sEvent == "pasteutf" or sEvent == "terminate" or sEvent == "file_transfer" then
|
||||
elseif sEvent == "char" or sEvent == "key" or sEvent == "key_up" or sEvent == "paste" or sEvent == "terminate" or sEvent == "file_transfer" then
|
||||
-- Basic input, just passthrough to current process
|
||||
resumeProcess(nCurrentProcess, table.unpack(tEventData, 1, tEventData.n))
|
||||
if cullProcess(nCurrentProcess) then
|
||||
|
|
|
@ -53,8 +53,6 @@ else
|
|||
errorColour = colours.white
|
||||
end
|
||||
|
||||
local utflib = (require and require("cc.utflib") or dofile("rom/modules/main/cc/utflib.lua"))
|
||||
local uterm = (require and require("cc.utflib.term") or dofile("rom/modules/main/cc/utflib/term.lua"))
|
||||
local unicodeMode = settings.get("edit.unicode")
|
||||
|
||||
local runHandler = [[multishell.setTitle(multishell.getCurrent(), %q)
|
||||
|
@ -211,8 +209,7 @@ local function tryWrite(sLine, regex, colour)
|
|||
else
|
||||
term.setTextColour(colour(match))
|
||||
end
|
||||
if utflib.isUTFString(match) then uterm.twrite(match)
|
||||
else term.write(match) end
|
||||
term.write(match)
|
||||
term.setTextColour(textColour)
|
||||
return sLine:sub(#match + 1)
|
||||
end
|
||||
|
@ -276,8 +273,7 @@ local function writeCompletion(sLine)
|
|||
local sCompletion = tCompletions[nCompletion]
|
||||
term.setTextColor(colours.white)
|
||||
term.setBackgroundColor(colours.grey)
|
||||
if utflib.isUTFString(sCompletion) then uterm.write(sCompletion)
|
||||
else term.write(sCompletion) end
|
||||
term.write(sCompletion)
|
||||
term.setTextColor(textColour)
|
||||
term.setBackgroundColor(bgColour)
|
||||
end
|
||||
|
@ -791,12 +787,12 @@ while bRunning do
|
|||
end
|
||||
end
|
||||
|
||||
elseif unicodeMode and sEvent == "charutf" or not unicodeMode and sEvent == "char" then
|
||||
elseif sEvent == "char" then
|
||||
if not bMenu and not bReadOnly then
|
||||
-- Input text
|
||||
local sLine = tLines[y]
|
||||
if unicodeMode then
|
||||
tLines[y] = sLine:sub(1, x - 1) .. utflib.UTFString(param) .. sLine:sub(x)
|
||||
tLines[y] = sLine:sub(1, x - 1) .. utflib.UTFString(param2) .. sLine:sub(x)
|
||||
else
|
||||
tLines[y] = sLine:sub(1, x - 1) .. param .. sLine:sub(x)
|
||||
end
|
||||
|
@ -812,7 +808,7 @@ while bRunning do
|
|||
end
|
||||
end
|
||||
|
||||
elseif unicodeMode and sEvent == "pasteutf" or not unicodeMode and sEvent == "paste" then
|
||||
elseif sEvent == "paste" then
|
||||
if not bReadOnly then
|
||||
-- Close menu if open
|
||||
if bMenu then
|
||||
|
@ -822,12 +818,9 @@ while bRunning do
|
|||
end
|
||||
-- Input text
|
||||
local sLine = tLines[y]
|
||||
if unicodeMode then
|
||||
tLines[y] = sLine:sub(1, x - 1) .. utflib.UTFString(param) .. sLine:sub(x)
|
||||
else
|
||||
tLines[y] = sLine:sub(1, x - 1) .. param .. sLine:sub(x)
|
||||
end
|
||||
setCursor(x + #param , y)
|
||||
local copiedText = unicodeMode and utflib.UTFString(param2) or param
|
||||
tLines[y] = sLine:sub(1, x - 1) .. copiedText .. sLine:sub(x)
|
||||
setCursor(x + #copiedText , y)
|
||||
end
|
||||
|
||||
elseif sEvent == "mouse_click" then
|
||||
|
|
|
@ -42,14 +42,6 @@ print("Call exit() to exit.")
|
|||
term.setTextColour(colours.white)
|
||||
|
||||
local unicodeMode = settings.get("lua.unicode")
|
||||
local ulib = require("cc.utflib")
|
||||
local write, print, printError, read
|
||||
if unicodeMode then
|
||||
local uterm = require("cc.utflib.term")
|
||||
write, print, printError, read = uterm.write, uterm.print, uterm.printError, uterm.read
|
||||
else
|
||||
write, print, printError, read = _G.write, _G.print, _G.printError, _G.read
|
||||
end
|
||||
|
||||
local chunk_idx, chunk_map = 1, {}
|
||||
while running do
|
||||
|
@ -83,7 +75,7 @@ while running do
|
|||
end
|
||||
|
||||
local name, offset = "=lua[" .. chunk_idx .. "]", 0
|
||||
if ulib.isUTFString(input) then input = tostring(input) end
|
||||
if utflib.isUTFString(input) then input = tostring(input) end
|
||||
local func, err = load(input, name, "t", tEnv)
|
||||
if load("return " .. input) then
|
||||
-- We wrap the expression with a call to _echo(...), which prevents tail
|
||||
|
|
|
@ -86,14 +86,6 @@ else
|
|||
end
|
||||
|
||||
local unicodeMode = settings.get("shell.unicode")
|
||||
local write, print, printError, read
|
||||
if unicodeMode then
|
||||
local uterm = require("cc.utflib.term")
|
||||
write, print, printError, read = uterm.write, uterm.print, uterm.printError, uterm.read
|
||||
else
|
||||
write, print, printError, read = _G.write, _G.print, _G.printError, _G.read
|
||||
end
|
||||
local utflib = require("cc.utflib")
|
||||
|
||||
local function tokenise(...)
|
||||
local args = { ... }
|
||||
|
@ -368,10 +360,10 @@ end
|
|||
|
||||
local function pathWithExtension(_sPath, _sExt)
|
||||
local nLen = #sPath
|
||||
local sEndChar = string.sub(_sPath, nLen, nLen)
|
||||
local sEndChar = _sPath:sub(nLen, nLen)
|
||||
-- Remove any trailing slashes so we can add an extension to the path safely
|
||||
if sEndChar == "/" or sEndChar == "\\" then
|
||||
_sPath = string.sub(_sPath, 1, nLen - 1)
|
||||
_sPath = _sPath:sub(1, nLen - 1)
|
||||
end
|
||||
return _sPath .. "." .. _sExt
|
||||
end
|
||||
|
@ -388,10 +380,12 @@ end
|
|||
-- shell.resolveProgram("hello")
|
||||
-- -- => rom/programs/fun/hello.lua
|
||||
function shell.resolveProgram(command)
|
||||
expect(1, command, "string")
|
||||
if not utflib.isUTFString(command) then
|
||||
expect(1, command, "string")
|
||||
end
|
||||
-- Substitute aliases firsts
|
||||
if tAliases[command] ~= nil then
|
||||
command = tAliases[command]
|
||||
if tAliases[tostring(command)] ~= nil then
|
||||
command = tAliases[tostring(command)]
|
||||
end
|
||||
|
||||
-- If the path is a global path, use it directly
|
||||
|
@ -409,7 +403,7 @@ function shell.resolveProgram(command)
|
|||
end
|
||||
|
||||
-- Otherwise, look on the path variable
|
||||
for sPath in string.gmatch(sPath, "[^:]+") do
|
||||
for sPath in sPath:gmatch("[^:]+") do
|
||||
sPath = fs.combine(shell.resolve(sPath), command)
|
||||
if fs.exists(sPath) and not fs.isDir(sPath) then
|
||||
return sPath
|
||||
|
@ -547,11 +541,13 @@ end
|
|||
-- @see shell.getCompletionInfo
|
||||
-- @since 1.74
|
||||
function shell.complete(sLine)
|
||||
expect(1, sLine, "string")
|
||||
if not utflib.isUTFString(sLine) then
|
||||
expect(1, sLine, "string")
|
||||
end
|
||||
if #sLine > 0 then
|
||||
local tWords = tokenise(sLine)
|
||||
local nIndex = #tWords
|
||||
if string.sub(sLine, #sLine, #sLine) == " " then
|
||||
if sLine:sub(#sLine, #sLine) == " " then
|
||||
nIndex = nIndex + 1
|
||||
end
|
||||
if nIndex == 1 then
|
||||
|
@ -774,7 +770,7 @@ else
|
|||
|
||||
local ok, result
|
||||
local co = coroutine.create(read)
|
||||
assert(coroutine.resume(co, nil, tCommandHistory, complete))
|
||||
assert(coroutine.resume(co, nil, tCommandHistory, complete, nil, unicodeMode))
|
||||
|
||||
while coroutine.status(co) ~= "dead" do
|
||||
local event = table.pack(os.pullEvent())
|
||||
|
|
Loading…
Reference in New Issue