v0.2.0 a558f49

uierrors.go RawHistoryFolder

1// Package uierrors holds the error helper shared by the NeferGUI adapters.
2package uierrors
3
4import (
5	"context"
6	"errors"
7)
8
9// Drop removes from err, and recursively from errors.Join trees, every leaf
10// that is exactly one of targets. Only bare leaves match: a real failure that
11// merely wraps a target (fmt.Errorf("x: %w", context.Canceled)) is kept, and so
12// is every sibling of a dropped leaf. It returns nil when nothing else remains.
13//
14// Use it to discard the context.Canceled a NeferGUI Run reports after the
15// adapter itself ended it, without hiding a real error joined to it.
16func Drop(err error, targets ...error) error {
17	if err == nil {
18		return nil
19	}
20	if j, ok := err.(interface{ Unwrap() []error }); ok {
21		var keep []error
22		for _, e := range j.Unwrap() {
23			if e = Drop(e, targets...); e != nil {
24				keep = append(keep, e)
25			}
26		}
27		switch len(keep) {
28		case 0:
29			return nil
30		case 1:
31			return keep[0]
32		}
33		return errors.Join(keep...)
34	}
35	for _, t := range targets {
36		if err == t {
37			return nil
38		}
39	}
40	return err
41}
42
43// Ended classifies how a NeferGUI Run ended. It is pure: the caller snapshots
44// its state once and passes it, so a context that ends between two reads cannot
45// flip the answer.
46//
47// explained says that this adapter or its caller ended the run on purpose (a
48// decision was made, or the parent context is done). parent is the parent
49// context's error at that snapshot, nil when it is still live. closed is
50// returned when the run ended without an explanation.
51//
52//   - explained: a bare context.Canceled (and bare parent) is the expected echo
53//     of our own cancellation and is dropped. Anything else, including a real
54//     error joined with it or an error that merely wraps Canceled, is kept.
55//   - not explained: the run ended on its own. When something real remains
56//     (an initial authorization, capability or font failure, say), exactly that
57//     is returned: calling it a compositor close would mislead. Only when
58//     nothing real remains, including a bare Canceled, is closed returned.
59func Ended(runErr error, explained bool, parent, closed error) error {
60	if explained {
61		return Drop(runErr, context.Canceled, parent)
62	}
63	if rest := Drop(runErr, context.Canceled, parent); rest != nil {
64		return rest
65	}
66	return closed
67}
68
69// IsCancellation reports whether err is non-nil and made only of
70// context.Canceled leaves, possibly wrapped with %w or joined. A failure
71// joined with, or merely mentioning, a cancellation is not one. Unlike Drop,
72// wrapped Canceled counts: it is how a caller's own cancellation surfaces
73// through layers that add context. DeadlineExceeded is deliberately not
74// included, so a bounded-operation timeout stays a real failure.
75func IsCancellation(err error) bool {
76	switch e := err.(type) {
77	case nil:
78		return false
79	case interface{ Unwrap() []error }:
80		children := e.Unwrap()
81		for _, child := range children {
82			if !IsCancellation(child) {
83				return false
84			}
85		}
86		return len(children) > 0
87	case interface{ Unwrap() error }:
88		return err == context.Canceled || IsCancellation(e.Unwrap())
89	default:
90		return err == context.Canceled
91	}
92}

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 2

Tags 9

↑↓ moveenter openesc close