v0.1.0 7deb448

wire_command.go RawHistoryFolder

1package ports
2
3import (
4	"encoding/binary"
5	"errors"
6	"math"
7)
8
9// ErrTooManyCommandArgs reports that a request exceeds the wire count limit.
10var ErrTooManyCommandArgs = errors.New("ports: too many command arguments")
11
12// CommandRequest asks the daemon to run one control command. Version must
13// stay first so a future payload layout can still be rejected cleanly.
14type CommandRequest struct {
15	Version       uint16
16	Self          bool
17	Slug          string
18	Args          []string
19	TargetSession string
20	TargetTab     string
21	TargetPane    string
22	JSON          bool
23}
24
25// CommandResult reports a control command's outcome.
26type CommandResult struct {
27	OK     bool
28	Code   uint16
29	Text   string
30	Output string
31}
32
33// PeekCommandVersion returns the leading protocol version from a
34// CommandRequest payload.
35func PeekCommandVersion(b []byte) (uint16, bool) {
36	if len(b) < 2 {
37		return 0, false
38	}
39	return binary.BigEndian.Uint16(b), true
40}
41
42// MarshalCommandRequest encodes m into a CommandRequest payload.
43func MarshalCommandRequest(m CommandRequest) ([]byte, error) {
44	if len(m.Args) > math.MaxUint16 {
45		return nil, ErrTooManyCommandArgs
46	}
47
48	w := payloadWriter{}
49	w.putUint16(m.Version)
50	if m.Self {
51		w.putUint8(1)
52	} else {
53		w.putUint8(0)
54	}
55	w.putString(m.Slug)
56	w.putUint16(uint16(len(m.Args)))
57	for _, arg := range m.Args {
58		w.putLongString(arg)
59	}
60	w.putString(m.TargetSession)
61	w.putString(m.TargetTab)
62	w.putString(m.TargetPane)
63	if m.JSON {
64		w.putUint8(1)
65	} else {
66		w.putUint8(0)
67	}
68	return w.b, nil
69}
70
71// UnmarshalCommandRequest decodes a strict CommandRequest payload.
72func UnmarshalCommandRequest(b []byte) (CommandRequest, error) {
73	r := payloadReader{b: b}
74	var m CommandRequest
75	var err error
76
77	if m.Version, err = r.getUint16(); err != nil {
78		return CommandRequest{}, err
79	}
80	selfFlag, err := r.getUint8()
81	if err != nil {
82		return CommandRequest{}, err
83	}
84	m.Self = selfFlag != 0
85	if m.Slug, err = r.getString(); err != nil {
86		return CommandRequest{}, err
87	}
88	argCount, err := r.getUint16()
89	if err != nil {
90		return CommandRequest{}, err
91	}
92	// Every argument requires at least a uint32 length prefix. Reject an
93	// impossible count before allocating from untrusted input.
94	if uint64(argCount) > uint64(len(r.b)/4) {
95		return CommandRequest{}, errShortPayload
96	}
97	if argCount != 0 {
98		m.Args = make([]string, 0, int(argCount))
99		for range int(argCount) {
100			arg, err := r.getLongString()
101			if err != nil {
102				return CommandRequest{}, err
103			}
104			m.Args = append(m.Args, arg)
105		}
106	}
107	if m.TargetSession, err = r.getString(); err != nil {
108		return CommandRequest{}, err
109	}
110	if m.TargetTab, err = r.getString(); err != nil {
111		return CommandRequest{}, err
112	}
113	if m.TargetPane, err = r.getString(); err != nil {
114		return CommandRequest{}, err
115	}
116	jsonFlag, err := r.getUint8()
117	if err != nil {
118		return CommandRequest{}, err
119	}
120	m.JSON = jsonFlag != 0
121	if err := r.done(); err != nil {
122		return CommandRequest{}, err
123	}
124	return m, nil
125}
126
127// MarshalCommandResult encodes m into a CommandResult payload.
128func MarshalCommandResult(m CommandResult) []byte {
129	w := payloadWriter{}
130	if m.OK {
131		w.putUint8(1)
132	} else {
133		w.putUint8(0)
134	}
135	w.putUint16(m.Code)
136	w.putString(m.Text)
137	w.putLongString(m.Output)
138	return w.b
139}
140
141// UnmarshalCommandResult decodes a strict CommandResult payload.
142func UnmarshalCommandResult(b []byte) (CommandResult, error) {
143	r := payloadReader{b: b}
144	var m CommandResult
145	var err error
146
147	okFlag, err := r.getUint8()
148	if err != nil {
149		return CommandResult{}, err
150	}
151	m.OK = okFlag != 0
152	if m.Code, err = r.getUint16(); err != nil {
153		return CommandResult{}, err
154	}
155	if m.Text, err = r.getString(); err != nil {
156		return CommandResult{}, err
157	}
158	if m.Output, err = r.getLongString(); err != nil {
159		return CommandResult{}, err
160	}
161	if err := r.done(); err != nil {
162		return CommandResult{}, err
163	}
164	return m, nil
165}

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