diff options
author | Thijs Schreijer <thijs@thijsschreijer.nl> | 2025-06-09 22:31:00 +0200 |
---|---|---|
committer | Thijs Schreijer <thijs@thijsschreijer.nl> | 2025-06-09 23:04:57 +0200 |
commit | c157641a4e68e4b292a2149232dc75c6171c9310 (patch) | |
tree | 7289a03c1b2f8b7dbeefd7e3fdd2a46f79a36f02 | |
parent | e62b0b470eb87b1cb7ed511ba7b14845a1958134 (diff) | |
download | luasystem-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.lua | 10 | ||||
-rw-r--r-- | system/init.lua | 15 |
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 } |