raiguard/tcom

git clone https://git.raiguard.me/raiguard/tcom

Caleb HeuerNo idea what I was working on...7992681

trunk
6.0 KiB273 linesraw
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 {
11	Bell,
12	Backspace,
13	Tab,
14	Line_Feed,
15	Form_Feed,
16	Carriage_Return,
17	// CSI
18	Reset_All,
19	Bold,
20	Dim,
21	Italic,
22	Underline,
23	// OSC
24}
25
26TTY_Text :: rune
27
28TTY_Resized :: distinct TTY_Pos
29
30TTY_Event :: union {
31	TTY_Ctrl,
32	TTY_Resized,
33	TTY_Text,
34}
35
36TTY_Error_Unknown_Escape_Seq :: struct {
37	bytes: []byte,
38}
39
40TTY_Error :: union {
41	TTY_Error_Unknown_Escape_Seq,
42	io.Error,
43}
44
45TTY_Pos :: [2]u16
46
47TTY_Reader :: struct {
48	buf: []byte,
49	br: bytes.Reader,
50	state: TTY_Reader_State,
51	start_i: i64,
52}
53
54TTY_Reader_State :: enum {
55	Normal,
56	Escape_Code,
57}
58
59tty_reader_init :: proc(r: ^TTY_Reader, buf: []byte) {
60	r.buf = buf
61	bytes.reader_init(&r.br, buf)
62}
63
64tty_reader_has_data :: proc(r: ^TTY_Reader) -> bool {
65	return r.br.i < bytes.reader_size(&r.br)
66}
67
68tty_reader_get_event :: proc(r: ^TTY_Reader) -> (e: TTY_Event, err: TTY_Error) {
69	if !tty_reader_has_data(r) {
70		return
71	}
72
73	br := &r.br
74	r.start_i = br.i
75
76	for {
77		// TODO: Peek and do things based on the peeked rune (numeric == reader_number)
78		ru, _, b_err := bytes.reader_read_rune(br)
79		// TODO: Emit some kind of error if we end in a premature state.
80		if b_err == .EOF {
81			return
82		} else if b_err != nil {
83			err = b_err
84			return
85		}
86
87		e, err = _tty_reader_process_rune(r, ru)
88		if e != nil || err != nil {
89			return
90		}
91	}
92}
93
94_tty_reader_process_rune :: proc(r: ^TTY_Reader, ru: rune) -> (e: TTY_Event, err: TTY_Error) {
95	switch r.state {
96	case .Normal:
97		return _tty_reader_process_rune_normal(r, ru)
98	case .Escape_Code:
99		return _tty_reader_process_rune_escape_code(r, ru)
100	}
101
102	return
103}
104
105_tty_reader_process_rune_normal :: proc(r: ^TTY_Reader, ru: rune) -> (e: TTY_Event, err: TTY_Error) {
106	switch ru {
107	// C0 control codes
108	case '\a': return .Bell, nil
109	case '\b': return .Backspace, nil
110	case '\t': return .Tab, nil
111	case '\n': return .Line_Feed, nil
112	case '\f': return .Form_Feed, nil
113	case '\r': return .Carriage_Return, nil
114	// Escape code
115	case '\e':
116		// FIXME: We need to distinguish between "the user pressed escape" and a possible escape sequence.
117		r.state = .Escape_Code
118		return
119	}
120
121	return TTY_Text(ru), nil
122}
123
124// https://en.wikipedia.org/wiki/ANSI_escape_code
125
126/*
127	For Control Sequence Introducer, or CSI, commands, the ESC [ (written as \e[, \x1b[ or \033[ in several programming
128	languages) is followed by any number (including none) of "parameter bytes" in the range 0x30–0x3F (ASCII 0–9:;<=>?),
129	then by any number of "intermediate bytes" in the range 0x20–0x2F (ASCII space and !"#$%&'()*+,-./), then finally by
130	a 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!
135	r.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
152	return
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// }