raiguard/tcom
git clone https://git.raiguard.me/raiguard/tcom
7992681
trunk
1#+build linux 2#+vet explicit-allocators 3package tcom 4 5import "core:bytes" 6import "core:io" 7import "core:log" 8import "core:strconv" 9 10TTY_Ctrl :: enum { 11Bell , 12Backspace , 13Tab , 14Line_Feed , 15Form_Feed , 16Carriage_Return , 17// CSI 18Reset_All , 19Bold , 20Dim , 21Italic , 22Underline , 23// OSC 24} 25 26TTY_Text :: rune 27 28TTY_Resized :: distinct TTY_Pos 29 30TTY_Event :: union { 31TTY_Ctrl , 32TTY_Resized , 33TTY_Text , 34} 35 36TTY_Error_Unknown_Escape_Seq :: struct { 37bytes : [] byte , 38} 39 40TTY_Error :: union { 41TTY_Error_Unknown_Escape_Seq , 42io . Error , 43} 44 45TTY_Pos :: [ 2 ] u16 46 47TTY_Reader :: struct { 48buf : [] byte , 49br : bytes . Reader , 50state : TTY_Reader_State , 51start_i : i64 , 52} 53 54TTY_Reader_State :: enum { 55Normal , 56Escape_Code , 57} 58 59tty_reader_init :: proc ( r : ^ TTY_Reader , buf : [] byte ) { 60r . buf = buf 61bytes . reader_init ( & r . br , buf ) 62} 63 64tty_reader_has_data :: proc ( r : ^ TTY_Reader ) -> bool { 65return r . br . i < bytes . reader_size ( & r . br ) 66} 67 68tty_reader_get_event :: proc ( r : ^ TTY_Reader ) -> ( e : TTY_Event , err : TTY_Error ) { 69if ! tty_reader_has_data ( r ) { 70return 71} 72 73br := & r . br 74r . start_i = br . i 75 76for { 77// TODO: Peek and do things based on the peeked rune (numeric == reader_number) 78ru , _ , b_err := bytes . reader_read_rune ( br ) 79// TODO: Emit some kind of error if we end in a premature state. 80if b_err == . EOF { 81return 82} else if b_err != nil { 83err = b_err 84return 85} 86 87e , err = _tty_reader_process_rune ( r , ru ) 88if e != nil || err != nil { 89return 90} 91} 92} 93 94_tty_reader_process_rune :: proc ( r : ^ TTY_Reader , ru : rune ) -> ( e : TTY_Event , err : TTY_Error ) { 95switch r . state { 96case . Normal : 97return _tty_reader_process_rune_normal ( r , ru ) 98case . Escape_Code : 99return _tty_reader_process_rune_escape_code ( r , ru ) 100} 101 102return 103} 104 105_tty_reader_process_rune_normal :: proc ( r : ^ TTY_Reader , ru : rune ) -> ( e : TTY_Event , err : TTY_Error ) { 106switch ru { 107// C0 control codes 108case '\a' : return . Bell , nil 109case '\b' : return . Backspace , nil 110case '\t' : return . Tab , nil 111case '\n' : return . Line_Feed , nil 112case '\f' : return . Form_Feed , nil 113case '\r' : return . Carriage_Return , nil 114// Escape code 115case '\e' : 116// FIXME: We need to distinguish between "the user pressed escape" and a possible escape sequence. 117r . state = . Escape_Code 118return 119} 120 121return TTY_Text ( ru ), nil 122} 123 124// https://en.wikipedia.org/wiki/ANSI_escape_code 125 126/* 127For Control Sequence Introducer, or CSI, commands, the ESC [ (written as \e[, \x1b[ or \033[ in several programming 128languages) is followed by any number (including none) of "parameter bytes" in the range 0x30–0x3F (ASCII 0–9:;<=>?), 129then by any number of "intermediate bytes" in the range 0x20–0x2F (ASCII space and !"#$%&'()*+,-./), then finally by 130a single "final byte" in the range 0x40–0x7E (ASCII @A–Z[\]^_`a–z{|}~). 131*/ 132 133_tty_reader_process_rune_escape_code :: proc ( r : ^ TTY_Reader , ru : rune ) -> ( e : TTY_Event , err : TTY_Error ) { 134// TODO: Actually do the thing! 135r . state = . Normal 136// if ru >= '0' && ru <= '9' { 137// bytes.reader_unread_rune(&r.br) or_return 138// log.debug(string(r.buf[r.br.i:])) 139// n: int 140// arg, ok := strconv.parse_uint(string(r.buf[r.br.i:]), n=&n) 141// if !ok { 142// err = TTY_Error_Unknown_Escape_Seq{} 143// return 144// } 145 146// r.esc_code.args[r.esc_code.args_len] = arg 147// r.esc_code.args_len += 1 148 149// bytes.reader_seek(&r.br, auto_cast n, .Current) 150// } 151 152return 153} 154 155// tty_parse_next :: proc(br: ^bytes.Reader, is_stdin: bool) -> (e: Sys_Event) { 156// if br.i >= bytes.reader_size(br) { 157// return 158// } 159 160// start_i := int(br.i) 161 162// did_first_iter: bool 163// for { 164// // TODO: s is unused 165// r, s, err := bytes.reader_read_rune(br) 166// if err != nil && err != .EOF { 167// end_i := int(br.i) 168// return Sys_Event_IO_Error{br.s[start_i:end_i], start_i, end_i, err} 169// } 170// if err == .EOF { 171// break 172// } 173 174// if r == '\e' { 175// if did_first_iter { 176// // End of sequence of "standard" characters. 177// err = bytes.reader_unread_rune(br) 178// if err != nil { 179// end_i := int(br.i) 180// return Sys_Event_IO_Error{br.s[start_i:end_i], start_i, end_i, err} 181// } 182// break 183// } else { 184// esc_err: TTY_Parse_Escape_Code_Error 185// e, esc_err = tty_parse_escape_code(br, &s) 186// if esc_err != nil { 187// log.error(err) // TODO: Return to business logic. 188// } else { 189// return e 190// } 191// } 192// } 193 194// did_first_iter = true 195// } 196 197// end_i := int(br.i) 198// if end_i > start_i { 199// return Sys_Event_TTY_Text{string(br.s[start_i:end_i])} 200// } 201 202// return 203// } 204 205// TTY_Unknown_Escape_Code_Error :: struct {} 206 207// TTY_Parse_Escape_Code_Error :: union { 208// TTY_Unknown_Escape_Code_Error, 209// io.Error, 210// } 211 212// tty_parse_escape_code :: proc(br: ^bytes.Reader, s: ^int) -> (e: Sys_Event, err: TTY_Parse_Escape_Code_Error) { 213// // When this function is called, the caller has already seen the escape character, so we will proceed assuming that 214// // it existed. 215 216// // TODO: This is terrible no-good awful. 217// switch _read(br, s) or_return { 218// case '[': 219// switch _read(br, s) or_return { 220// case '0': 221// switch _read(br, s) or_return { 222// case 'm': 223// e = Sys_Event_TTY_Control{.Reset_All} 224// return 225// case: 226// err = TTY_Unknown_Escape_Code_Error{} 227// return 228// } 229// case '1': 230// switch _read(br, s) or_return { 231// case 'm': 232// e = Sys_Event_TTY_Control{.Bold} 233// return 234// case: 235// err = TTY_Unknown_Escape_Code_Error{} 236// return 237// } 238// case '2': 239// switch _read(br, s) or_return { 240// case 'm': 241// e = Sys_Event_TTY_Control{.Dim} 242// return 243// case: 244// err = TTY_Unknown_Escape_Code_Error{} 245// return 246// } 247// case '3': 248// switch _read(br, s) or_return { 249// case 'm': 250// e = Sys_Event_TTY_Control{.Italic} 251// return 252// case: 253// err = TTY_Unknown_Escape_Code_Error{} 254// return 255// } 256// case '4': 257// switch _read(br, s) or_return { 258// case 'm': 259// e = Sys_Event_TTY_Control{.Underline} 260// return 261// case: 262// err = TTY_Unknown_Escape_Code_Error{} 263// return 264// } 265// case: 266// err = TTY_Unknown_Escape_Code_Error{} 267// return 268// } 269// case: 270// err = TTY_Unknown_Escape_Code_Error{} 271// return 272// } 273// }