aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThijs Schreijer <thijs@thijsschreijer.nl>2025-06-09 22:31:00 +0200
committerThijs Schreijer <thijs@thijsschreijer.nl>2025-06-09 23:04:57 +0200
commitc157641a4e68e4b292a2149232dc75c6171c9310 (patch)
tree7289a03c1b2f8b7dbeefd7e3fdd2a46f79a36f02
parente62b0b470eb87b1cb7ed511ba7b14845a1958134 (diff)
downloadluasystem-fix/control.tar.gz
luasystem-fix/control.tar.bz2
luasystem-fix/control.zip
fix(term): report only printables as charactersfix/control
readkey would return "char" as type for control characters, yet they are not printable.
-rw-r--r--spec/04-term_spec.lua10
-rw-r--r--system/init.lua15
2 files changed, 20 insertions, 5 deletions
diff --git a/spec/04-term_spec.lua b/spec/04-term_spec.lua
index 0ce0033..5dea046 100644
--- a/spec/04-term_spec.lua
+++ b/spec/04-term_spec.lua
@@ -902,6 +902,14 @@ describe("Terminal:", function()
902 end) 902 end)
903 903
904 904
905 it("reads a control byte for single-byte control characters", function()
906 setbuffer("\000\031\127")
907 assert.are.same({"\000", "ctrl"}, {system.readansi(0)})
908 assert.are.same({"\031", "ctrl"}, {system.readansi(0)})
909 assert.are.same({"\127", "ctrl"}, {system.readansi(0)})
910 end)
911
912
905 it("reads a multi-byte characters one at a time", function() 913 it("reads a multi-byte characters one at a time", function()
906 setbuffer(string.char(226, 130, 172) .. -- "€" single 914 setbuffer(string.char(226, 130, 172) .. -- "€" single
907 string.char(240, 159, 154, 128)) -- "🚀" double 915 string.char(240, 159, 154, 128)) -- "🚀" double
@@ -952,7 +960,7 @@ describe("Terminal:", function()
952 960
953 it("returns a single <esc> character if no sequence is found", function() 961 it("returns a single <esc> character if no sequence is found", function()
954 setbuffer("\27") 962 setbuffer("\27")
955 assert.are.same({"\27", "char"}, {system.readansi(0)}) 963 assert.are.same({"\27", "ctrl"}, {system.readansi(0)})
956 end) 964 end)
957 965
958 966
diff --git a/system/init.lua b/system/init.lua
index f05b237..eeaf38f 100644
--- a/system/init.lua
+++ b/system/init.lua
@@ -290,13 +290,20 @@ do
290 -- @tparam number timeout the timeout in seconds. 290 -- @tparam number timeout the timeout in seconds.
291 -- @tparam[opt=system.sleep] function fsleep the function to call for sleeping. 291 -- @tparam[opt=system.sleep] function fsleep the function to call for sleeping.
292 -- @treturn[1] string the character that was received (can be multi-byte), or a complete ANSI sequence 292 -- @treturn[1] string the character that was received (can be multi-byte), or a complete ANSI sequence
293 -- @treturn[1] string the type of input: `"char"` for a single key, `"ansi"` for an ANSI sequence 293 -- @treturn[1] string the type of input: `"ctrl"` for 0-31 and 127 bytes, `"char"` for other UTF-8 characters, `"ansi"` for an ANSI sequence
294 -- @treturn[2] nil in case of an error 294 -- @treturn[2] nil in case of an error
295 -- @treturn[2] string error message; `"timeout"` if the timeout was reached. 295 -- @treturn[2] string error message; `"timeout"` if the timeout was reached.
296 -- @treturn[2] string partial result in case of an error while reading a sequence, the sequence so far. 296 -- @treturn[2] string partial result in case of an error while reading a sequence, the sequence so far.
297 -- The function retains its own internal buffer, so on the next call the incomplete buffer is used to 297 -- The function retains its own internal buffer, so on the next call the incomplete buffer is used to
298 -- complete the sequence. 298 -- complete the sequence.
299 -- @within Terminal_Input 299 -- @within Terminal_Input
300 -- @usage
301 -- local key, keytype = system.readansi(5)
302 -- if keytype == "char" then ... end -- printable character
303 -- if keytype ~= "char" then ... end -- non-printable character or sequence
304 -- if keytype == "ansi" then ... end -- a multi-byte sequence, but not a UTF8 character
305 -- if keytype ~= "ansi" then ... end -- a valid UTF8 character (which includes control characters)
306 -- if keytype == "ctrl" then ... end -- a single-byte ctrl character (0-31, 127)
300 function system.readansi(timeout, fsleep) 307 function system.readansi(timeout, fsleep)
301 if type(timeout) ~= "number" then 308 if type(timeout) ~= "number" then
302 error("arg #1 to readansi, expected timeout in seconds, got " .. type(timeout), 2) 309 error("arg #1 to readansi, expected timeout in seconds, got " .. type(timeout), 2)
@@ -320,7 +327,7 @@ do
320 if key2 == nil then 327 if key2 == nil then
321 -- no key available, return the escape key, on its own 328 -- no key available, return the escape key, on its own
322 sequence = nil 329 sequence = nil
323 return string.char(key), "char" 330 return string.char(key), "ctrl"
324 331
325 elseif key2 == 91 then 332 elseif key2 == 91 then
326 -- "[" means it is for sure an ANSI sequence 333 -- "[" means it is for sure an ANSI sequence
@@ -351,10 +358,10 @@ do
351 else 358 else
352 -- check UTF8 length 359 -- check UTF8 length
353 utf8_length = key < 128 and 1 or key < 224 and 2 or key < 240 and 3 or key < 248 and 4 360 utf8_length = key < 128 and 1 or key < 224 and 2 or key < 240 and 3 or key < 248 and 4
354 if utf8_length == 1 then 361 if utf8_length == 1 then
355 -- single byte character 362 -- single byte character
356 utf8_length = nil 363 utf8_length = nil
357 return string.char(key), "char" 364 return string.char(key), ((key <= 31 or key == 127) and "ctrl" or "char")
358 else 365 else
359 -- UTF8 sequence detected 366 -- UTF8 sequence detected
360 sequence = { key } 367 sequence = { key }