diff options
| -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 } |
