main ece7682

rawterm.go RawHistoryFolder

1//go:build linux || darwin
2
3// Package rawterm provides the small set of terminal-control primitives vev
4// needs directly on top of the frozen stdlib syscall package. The common raw
5// mode and terminal-size operations use platform-specific termios requests.
6package rawterm
7
8import (
9	"fmt"
10	"syscall"
11	"unsafe"
12)
13
14// State is an opaque snapshot of a terminal's termios settings, captured by
15// MakeRaw and consumed by Restore.
16type State struct {
17	termios syscall.Termios
18}
19
20// Winsize mirrors the kernel's struct winsize for TIOCGWINSZ/TIOCSWINSZ.
21// Zero pixel dimensions mean the controlling terminal did not report them.
22type Winsize struct {
23	Row    uint16
24	Col    uint16
25	Xpixel uint16
26	Ypixel uint16
27}
28
29// ioctl issues a SYS_IOCTL syscall against fd with the given request and
30// argument pointer, returning a Go error on failure. The pointer-to-uintptr
31// conversion must happen inside the Syscall argument list (unsafe.Pointer
32// rule 4), so arg stays a live pointer until the call.
33func ioctl(fd int, req uintptr, arg unsafe.Pointer) error {
34	_, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), req, uintptr(arg))
35	if errno != 0 {
36		return errno
37	}
38	return nil
39}
40
41// IsTerminal reports whether fd refers to a terminal device.
42func IsTerminal(fd int) bool {
43	var t syscall.Termios
44	return ioctl(fd, reqGetTermios, unsafe.Pointer(&t)) == nil
45}
46
47// MakeRaw puts the terminal referenced by fd into raw mode and returns its
48// pre-modification state, so a caller can later restore it. The flag
49// changes applied match golang.org/x/term's MakeRaw semantics exactly.
50func MakeRaw(fd int) (*State, error) {
51	var oldState State
52	if err := ioctl(fd, reqGetTermios, unsafe.Pointer(&oldState.termios)); err != nil {
53		return nil, fmt.Errorf("rawterm: get termios: %w", err)
54	}
55
56	raw := oldState.termios
57	raw.Iflag &^= syscall.IGNBRK | syscall.BRKINT | syscall.PARMRK | syscall.ISTRIP | syscall.INLCR | syscall.IGNCR | syscall.ICRNL | syscall.IXON
58	raw.Oflag &^= syscall.OPOST
59	raw.Lflag &^= syscall.ECHO | syscall.ECHONL | syscall.ICANON | syscall.ISIG | syscall.IEXTEN
60	raw.Cflag &^= syscall.CSIZE | syscall.PARENB
61	raw.Cflag |= syscall.CS8
62	raw.Cc[syscall.VMIN] = 1
63	raw.Cc[syscall.VTIME] = 0
64
65	if err := ioctl(fd, reqSetTermios, unsafe.Pointer(&raw)); err != nil {
66		return nil, fmt.Errorf("rawterm: set termios: %w", err)
67	}
68
69	return &oldState, nil
70}
71
72// Restore applies a previously captured State back to the terminal
73// referenced by fd.
74func Restore(fd int, s *State) error {
75	if err := ioctl(fd, reqSetTermios, unsafe.Pointer(&s.termios)); err != nil {
76		return fmt.Errorf("rawterm: set termios: %w", err)
77	}
78	return nil
79}
80
81// GetSize returns the terminal's current column and row count.
82func GetSize(fd int) (cols, rows int, err error) {
83	ws, err := GetWinsize(fd)
84	if err != nil {
85		return 0, 0, err
86	}
87	return int(ws.Col), int(ws.Row), nil
88}
89
90// GetWinsize returns the terminal's cell and optional pixel dimensions.
91func GetWinsize(fd int) (Winsize, error) {
92	var ws Winsize
93	if err := ioctl(fd, syscall.TIOCGWINSZ, unsafe.Pointer(&ws)); err != nil {
94		return Winsize{}, fmt.Errorf("rawterm: get winsize: %w", err)
95	}
96	return ws, nil
97}
98
99// SetWinsize sets the terminal's column and row count.
100func SetWinsize(fd int, cols, rows uint16) error {
101	return SetWinsizeFull(fd, Winsize{Row: rows, Col: cols})
102}
103
104// SetWinsizeFull sets the terminal's cell and optional pixel dimensions.
105func SetWinsizeFull(fd int, ws Winsize) error {
106	if err := ioctl(fd, syscall.TIOCSWINSZ, unsafe.Pointer(&ws)); err != nil {
107		return fmt.Errorf("rawterm: set winsize: %w", err)
108	}
109	return nil
110}
111
112// ForegroundProcessGroup returns the process group id currently in the
113// foreground for the terminal referenced by fd (TIOCGPGRP).
114func ForegroundProcessGroup(fd int) (int, error) {
115	var pgid int32
116	if err := ioctl(fd, syscall.TIOCGPGRP, unsafe.Pointer(&pgid)); err != nil {
117		return 0, fmt.Errorf("rawterm: get foreground process group: %w", err)
118	}
119	return int(pgid), nil
120}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 12

Tags 11

↑↓ moveenter openesc close