queries
bash.scm c.scm cpp.scm css.scm dockerfile.scm go.scm html.scm javascript.scm lua.scm markdown.scm php.scm python.scm sql.scm tsx.scm typescript.scmsamples
format.txt lsp.c ollama.py test.c test.cpp test.css test.dockerfile test.html test.js test.lua test.md test.php test.py test.rb test.sh test.sql test.ts test.tsxvendor
github.com
mitjafelicijan
go-tree-sitter
.gitignore LICENSE Makefile README.md alloc.c alloc.h api.h array.h atomic.h bindings.c bindings.go bindings.h bits.h clock.h error_costs.h get_changed_ranges.c get_changed_ranges.h host.h iter.go language.c language.h length.h lexer.c lexer.h node.c parser.c parser.h point.h ptypes.h query.c reduce_action.h reusable_node.h stack.c stack.h subtree.c subtree.h test_grammar.go test_grammar.js test_grammar_generate.sh tree.c tree.h tree_cursor.c tree_cursor.h umachine.h unicode.h urename.h utf.h utf16.h utf8.h wasm_store.c wasm_store.hnsf
termbox-go
AUTHORS LICENSE README.md api.go api_common.go api_windows.go collect_terminfo.py escwait.go escwait_darwin.go syscalls_darwin.go syscalls_darwin_amd64.go syscalls_dragonfly.go syscalls_freebsd.go syscalls_linux.go syscalls_netbsd.go syscalls_openbsd.go syscalls_windows.go termbox.go termbox_common.go termbox_windows.go terminfo.go terminfo_builtin.go
vendor/github.com/nsf/termbox-go/api.go
raw
1// +build !windows
2
3package termbox
4
5import (
6 "fmt"
7 "os"
8 "os/signal"
9 "runtime"
10 "syscall"
11 "time"
12
13 "github.com/mattn/go-runewidth"
14)
15
16// public API
17
18// Initializes termbox library. This function should be called before any other functions.
19// After successful initialization, the library must be finalized using 'Close' function.
20//
21// Example usage:
22// err := termbox.Init()
23// if err != nil {
24// panic(err)
25// }
26// defer termbox.Close()
27func Init() error {
28 if IsInit {
29 return nil
30 }
31
32 var err error
33
34 if runtime.GOOS == "openbsd" || runtime.GOOS == "freebsd" {
35 out, err = os.OpenFile("/dev/tty", os.O_RDWR, 0)
36 if err != nil {
37 return err
38 }
39 in = int(out.Fd())
40 } else {
41 out, err = os.OpenFile("/dev/tty", os.O_WRONLY, 0)
42 if err != nil {
43 return err
44 }
45 in, err = syscall.Open("/dev/tty", syscall.O_RDONLY, 0)
46 if err != nil {
47 return err
48 }
49 }
50
51 err = setup_term()
52 if err != nil {
53 return fmt.Errorf("termbox: error while reading terminfo data: %v", err)
54 }
55
56 signal.Notify(sigwinch, syscall.SIGWINCH)
57 signal.Notify(sigio, syscall.SIGIO)
58
59 _, err = fcntl(in, syscall.F_SETFL, syscall.O_ASYNC|syscall.O_NONBLOCK)
60 if err != nil {
61 return err
62 }
63 _, err = fcntl(in, syscall.F_SETOWN, syscall.Getpid())
64 if runtime.GOOS != "darwin" && err != nil {
65 return err
66 }
67 err = tcgetattr(out.Fd(), &orig_tios)
68 if err != nil {
69 return err
70 }
71
72 tios := orig_tios
73 tios.Iflag &^= syscall_IGNBRK | syscall_BRKINT | syscall_PARMRK |
74 syscall_ISTRIP | syscall_INLCR | syscall_IGNCR |
75 syscall_ICRNL | syscall_IXON
76 tios.Lflag &^= syscall_ECHO | syscall_ECHONL | syscall_ICANON |
77 syscall_ISIG | syscall_IEXTEN
78 tios.Cflag &^= syscall_CSIZE | syscall_PARENB
79 tios.Cflag |= syscall_CS8
80 tios.Cc[syscall_VMIN] = 1
81 tios.Cc[syscall_VTIME] = 0
82
83 err = tcsetattr(out.Fd(), &tios)
84 if err != nil {
85 return err
86 }
87
88 out.WriteString(funcs[t_enter_ca])
89 out.WriteString(funcs[t_enter_keypad])
90 out.WriteString(funcs[t_hide_cursor])
91 out.WriteString(funcs[t_clear_screen])
92
93 termw, termh = get_term_size(out.Fd())
94 back_buffer.init(termw, termh)
95 front_buffer.init(termw, termh)
96 back_buffer.clear()
97 front_buffer.clear()
98
99 go func() {
100 buf := make([]byte, 128)
101 for {
102 select {
103 case <-sigio:
104 for {
105 n, err := syscall.Read(in, buf)
106 if err == syscall.EAGAIN || err == syscall.EWOULDBLOCK {
107 break
108 }
109 select {
110 case input_comm <- input_event{buf[:n], err}:
111 ie := <-input_comm
112 buf = ie.data[:128]
113 case <-quit:
114 return
115 }
116 }
117 case <-quit:
118 return
119 }
120 }
121 }()
122
123 IsInit = true
124 return nil
125}
126
127// Interrupt an in-progress call to PollEvent by causing it to return
128// EventInterrupt. Note that this function will block until the PollEvent
129// function has successfully been interrupted.
130func Interrupt() {
131 interrupt_comm <- struct{}{}
132}
133
134// Finalizes termbox library, should be called after successful initialization
135// when termbox's functionality isn't required anymore.
136func Close() {
137 if !IsInit {
138 return
139 }
140
141 quit <- 1
142 out.WriteString(funcs[t_show_cursor])
143 out.WriteString(funcs[t_sgr0])
144 out.WriteString(funcs[t_clear_screen])
145 out.WriteString(funcs[t_exit_ca])
146 out.WriteString(funcs[t_exit_keypad])
147 out.WriteString(funcs[t_exit_mouse])
148 tcsetattr(out.Fd(), &orig_tios)
149
150 out.Close()
151 syscall.Close(in)
152
153 // reset the state, so that on next Init() it will work again
154 termw = 0
155 termh = 0
156 input_mode = InputEsc
157 out = nil
158 in = 0
159 lastfg = attr_invalid
160 lastbg = attr_invalid
161 lastx = coord_invalid
162 lasty = coord_invalid
163 cursor_x = cursor_hidden
164 cursor_y = cursor_hidden
165 foreground = ColorDefault
166 background = ColorDefault
167 IsInit = false
168}
169
170// Synchronizes the internal back buffer with the terminal.
171func Flush() error {
172 // invalidate cursor position
173 lastx = coord_invalid
174 lasty = coord_invalid
175
176 update_size_maybe()
177
178 for y := 0; y < front_buffer.height; y++ {
179 line_offset := y * front_buffer.width
180 for x := 0; x < front_buffer.width; {
181 cell_offset := line_offset + x
182 back := &back_buffer.cells[cell_offset]
183 front := &front_buffer.cells[cell_offset]
184 if back.Ch < ' ' {
185 back.Ch = ' '
186 }
187 w := runewidth.RuneWidth(back.Ch)
188 if w == 0 || w == 2 && runewidth.IsAmbiguousWidth(back.Ch) {
189 w = 1
190 }
191 if *back == *front {
192 x += w
193 continue
194 }
195 *front = *back
196 send_attr(back.Fg, back.Bg)
197
198 if w == 2 && x == front_buffer.width-1 {
199 // there's not enough space for 2-cells rune,
200 // let's just put a space in there
201 send_char(x, y, ' ')
202 } else {
203 send_char(x, y, back.Ch)
204 if w == 2 {
205 next := cell_offset + 1
206 front_buffer.cells[next] = Cell{
207 Ch: 0,
208 Fg: back.Fg,
209 Bg: back.Bg,
210 }
211 }
212 }
213 x += w
214 }
215 }
216 if !is_cursor_hidden(cursor_x, cursor_y) {
217 write_cursor(cursor_x, cursor_y)
218 }
219 return flush()
220}
221
222// Sets the position of the cursor. See also HideCursor().
223func SetCursor(x, y int) {
224 if is_cursor_hidden(cursor_x, cursor_y) && !is_cursor_hidden(x, y) {
225 outbuf.WriteString(funcs[t_show_cursor])
226 }
227
228 if !is_cursor_hidden(cursor_x, cursor_y) && is_cursor_hidden(x, y) {
229 outbuf.WriteString(funcs[t_hide_cursor])
230 }
231
232 cursor_x, cursor_y = x, y
233 if !is_cursor_hidden(cursor_x, cursor_y) {
234 write_cursor(cursor_x, cursor_y)
235 }
236}
237
238// The shortcut for SetCursor(-1, -1).
239func HideCursor() {
240 SetCursor(cursor_hidden, cursor_hidden)
241}
242
243// Changes cell's parameters in the internal back buffer at the specified
244// position.
245func SetCell(x, y int, ch rune, fg, bg Attribute) {
246 if x < 0 || x >= back_buffer.width {
247 return
248 }
249 if y < 0 || y >= back_buffer.height {
250 return
251 }
252
253 back_buffer.cells[y*back_buffer.width+x] = Cell{ch, fg, bg}
254}
255
256// Returns the specified cell from the internal back buffer.
257func GetCell(x, y int) Cell {
258 return back_buffer.cells[y*back_buffer.width+x]
259}
260
261// Changes cell's character (rune) in the internal back buffer at the
262// specified position.
263func SetChar(x, y int, ch rune) {
264 if x < 0 || x >= back_buffer.width {
265 return
266 }
267 if y < 0 || y >= back_buffer.height {
268 return
269 }
270
271 back_buffer.cells[y*back_buffer.width+x].Ch = ch
272}
273
274// Changes cell's foreground attributes in the internal back buffer at
275// the specified position.
276func SetFg(x, y int, fg Attribute) {
277 if x < 0 || x >= back_buffer.width {
278 return
279 }
280 if y < 0 || y >= back_buffer.height {
281 return
282 }
283
284 back_buffer.cells[y*back_buffer.width+x].Fg = fg
285}
286
287// Changes cell's background attributes in the internal back buffer at
288// the specified position.
289func SetBg(x, y int, bg Attribute) {
290 if x < 0 || x >= back_buffer.width {
291 return
292 }
293 if y < 0 || y >= back_buffer.height {
294 return
295 }
296
297 back_buffer.cells[y*back_buffer.width+x].Bg = bg
298}
299
300// Returns a slice into the termbox's back buffer. You can get its dimensions
301// using 'Size' function. The slice remains valid as long as no 'Clear' or
302// 'Flush' function calls were made after call to this function.
303func CellBuffer() []Cell {
304 return back_buffer.cells
305}
306
307// After getting a raw event from PollRawEvent function call, you can parse it
308// again into an ordinary one using termbox logic. That is parse an event as
309// termbox would do it. Returned event in addition to usual Event struct fields
310// sets N field to the amount of bytes used within 'data' slice. If the length
311// of 'data' slice is zero or event cannot be parsed for some other reason, the
312// function will return a special event type: EventNone.
313//
314// IMPORTANT: EventNone may contain a non-zero N, which means you should skip
315// these bytes, because termbox cannot recognize them.
316//
317// NOTE: This API is experimental and may change in future.
318func ParseEvent(data []byte) Event {
319 event := Event{Type: EventKey}
320 status := extract_event(data, &event, false)
321 if status != event_extracted {
322 return Event{Type: EventNone, N: event.N}
323 }
324 return event
325}
326
327// Wait for an event and return it. This is a blocking function call. Instead
328// of EventKey and EventMouse it returns EventRaw events. Raw event is written
329// into `data` slice and Event's N field is set to the amount of bytes written.
330// The minimum required length of the 'data' slice is 1. This requirement may
331// vary on different platforms.
332//
333// NOTE: This API is experimental and may change in future.
334func PollRawEvent(data []byte) Event {
335 if len(data) == 0 {
336 panic("len(data) >= 1 is a requirement")
337 }
338
339 var event Event
340 if extract_raw_event(data, &event) {
341 return event
342 }
343
344 for {
345 select {
346 case ev := <-input_comm:
347 if ev.err != nil {
348 return Event{Type: EventError, Err: ev.err}
349 }
350
351 inbuf = append(inbuf, ev.data...)
352 input_comm <- ev
353 if extract_raw_event(data, &event) {
354 return event
355 }
356 case <-interrupt_comm:
357 event.Type = EventInterrupt
358 return event
359
360 case <-sigwinch:
361 event.Type = EventResize
362 event.Width, event.Height = get_term_size(out.Fd())
363 return event
364 }
365 }
366}
367
368// Wait for an event and return it. This is a blocking function call.
369func PollEvent() Event {
370 // Constant governing macOS specific behavior. See https://github.com/nsf/termbox-go/issues/132
371 // This is an arbitrary delay which hopefully will be enough time for any lagging
372 // partial escape sequences to come through.
373 const esc_wait_delay = 100 * time.Millisecond
374
375 var event Event
376 var esc_wait_timer *time.Timer
377 var esc_timeout <-chan time.Time
378
379 // try to extract event from input buffer, return on success
380 event.Type = EventKey
381 status := extract_event(inbuf, &event, true)
382 if event.N != 0 {
383 copy(inbuf, inbuf[event.N:])
384 inbuf = inbuf[:len(inbuf)-event.N]
385 }
386 if status == event_extracted {
387 return event
388 } else if status == esc_wait {
389 esc_wait_timer = time.NewTimer(esc_wait_delay)
390 esc_timeout = esc_wait_timer.C
391 }
392
393 for {
394 select {
395 case ev := <-input_comm:
396 if esc_wait_timer != nil {
397 if !esc_wait_timer.Stop() {
398 <-esc_wait_timer.C
399 }
400 esc_wait_timer = nil
401 }
402
403 if ev.err != nil {
404 return Event{Type: EventError, Err: ev.err}
405 }
406
407 inbuf = append(inbuf, ev.data...)
408 input_comm <- ev
409 status := extract_event(inbuf, &event, true)
410 if event.N != 0 {
411 copy(inbuf, inbuf[event.N:])
412 inbuf = inbuf[:len(inbuf)-event.N]
413 }
414 if status == event_extracted {
415 return event
416 } else if status == esc_wait {
417 esc_wait_timer = time.NewTimer(esc_wait_delay)
418 esc_timeout = esc_wait_timer.C
419 }
420 case <-esc_timeout:
421 esc_wait_timer = nil
422
423 status := extract_event(inbuf, &event, false)
424 if event.N != 0 {
425 copy(inbuf, inbuf[event.N:])
426 inbuf = inbuf[:len(inbuf)-event.N]
427 }
428 if status == event_extracted {
429 return event
430 }
431 case <-interrupt_comm:
432 event.Type = EventInterrupt
433 return event
434
435 case <-sigwinch:
436 event.Type = EventResize
437 event.Width, event.Height = get_term_size(out.Fd())
438 return event
439 }
440 }
441}
442
443// Returns the size of the internal back buffer (which is mostly the same as
444// terminal's window size in characters). But it doesn't always match the size
445// of the terminal window, after the terminal size has changed, the internal
446// back buffer will get in sync only after Clear or Flush function calls.
447func Size() (width int, height int) {
448 return termw, termh
449}
450
451// Clears the internal back buffer.
452func Clear(fg, bg Attribute) error {
453 foreground, background = fg, bg
454 err := update_size_maybe()
455 back_buffer.clear()
456 return err
457}
458
459// Sets termbox input mode. Termbox has two input modes:
460//
461// 1. Esc input mode. When ESC sequence is in the buffer and it doesn't match
462// any known sequence. ESC means KeyEsc. This is the default input mode.
463//
464// 2. Alt input mode. When ESC sequence is in the buffer and it doesn't match
465// any known sequence. ESC enables ModAlt modifier for the next keyboard event.
466//
467// Both input modes can be OR'ed with Mouse mode. Setting Mouse mode bit up will
468// enable mouse button press/release and drag events.
469//
470// If 'mode' is InputCurrent, returns the current input mode. See also Input*
471// constants.
472func SetInputMode(mode InputMode) InputMode {
473 if mode == InputCurrent {
474 return input_mode
475 }
476 if mode&(InputEsc|InputAlt) == 0 {
477 mode |= InputEsc
478 }
479 if mode&(InputEsc|InputAlt) == InputEsc|InputAlt {
480 mode &^= InputAlt
481 }
482 if mode&InputMouse != 0 {
483 out.WriteString(funcs[t_enter_mouse])
484 } else {
485 out.WriteString(funcs[t_exit_mouse])
486 }
487
488 input_mode = mode
489 return input_mode
490}
491
492// Sets the termbox output mode. Termbox has four output options:
493//
494// 1. OutputNormal => [1..8]
495// This mode provides 8 different colors:
496// black, red, green, yellow, blue, magenta, cyan, white
497// Shortcut: ColorBlack, ColorRed, ...
498// Attributes: AttrBold, AttrUnderline, AttrReverse
499//
500// Example usage:
501// SetCell(x, y, '@', ColorBlack | AttrBold, ColorRed);
502//
503// 2. Output256 => [1..256]
504// In this mode you can leverage the 256 terminal mode:
505// 0x01 - 0x08: the 8 colors as in OutputNormal
506// 0x09 - 0x10: Color* | AttrBold
507// 0x11 - 0xe8: 216 different colors
508// 0xe9 - 0x1ff: 24 different shades of grey
509//
510// Example usage:
511// SetCell(x, y, '@', 184, 240);
512// SetCell(x, y, '@', 0xb8, 0xf0);
513//
514// 3. Output216 => [1..216]
515// This mode supports the 3rd range of the 256 mode only.
516// But you don't need to provide an offset.
517//
518// 4. OutputGrayscale => [1..26]
519// This mode supports the 4th range of the 256 mode
520// and black and white colors from 3th range of the 256 mode
521// But you don't need to provide an offset.
522//
523// In all modes, 0x00 represents the default color.
524//
525// `go run _demos/output.go` to see its impact on your terminal.
526//
527// If 'mode' is OutputCurrent, it returns the current output mode.
528//
529// Note that this may return a different OutputMode than the one requested,
530// as the requested mode may not be available on the target platform.
531func SetOutputMode(mode OutputMode) OutputMode {
532 if mode == OutputCurrent {
533 return output_mode
534 }
535
536 output_mode = mode
537 return output_mode
538}
539
540// Sync comes handy when something causes desync between termbox's understanding
541// of a terminal buffer and the reality. Such as a third party process. Sync
542// forces a complete resync between the termbox and a terminal, it may not be
543// visually pretty though.
544func Sync() error {
545 front_buffer.clear()
546 err := send_clear()
547 if err != nil {
548 return err
549 }
550
551 return Flush()
552}