main b4521f0

file.go RawHistoryFolder

1package kvconf
2
3import (
4	"errors"
5	"fmt"
6	"io/fs"
7	"os"
8	"path/filepath"
9	"runtime"
10	"strings"
11)
12
13// ReadFile reads a config file safely: it must be a regular file (a FIFO or
14// device is rejected without blocking) no larger than opts.MaxBytes. A
15// missing file returns an error wrapping fs.ErrNotExist.
16func ReadFile(path string, opts Options) ([]byte, error) {
17	f, err := openNonblocking(path)
18	if err != nil {
19		return nil, err
20	}
21	defer f.Close() //nolint:errcheck // read only
22	info, err := f.Stat()
23	if err != nil {
24		return nil, err
25	}
26	if !info.Mode().IsRegular() {
27		return nil, &os.PathError{Op: "read", Path: path, Err: ErrNotRegular}
28	}
29	data, err := readLimited(f, opts.maxBytes())
30	if errors.Is(err, ErrTooLarge) {
31		return nil, fmt.Errorf("%w: %s", ErrTooLarge, path)
32	}
33	return data, err
34}
35
36// DefaultPath returns <config dir>/<app>/<name>. The config dir is
37// $XDG_CONFIG_HOME when it is an absolute path; otherwise ~/.config on Unix,
38// ~/Library/Application Support on macOS and %AppData% on Windows.
39func DefaultPath(app, name string) (string, error) {
40	if dir := os.Getenv("XDG_CONFIG_HOME"); filepath.IsAbs(dir) {
41		return filepath.Join(dir, app, name), nil
42	}
43	var dir string
44	switch runtime.GOOS {
45	case "darwin", "ios", "windows", "plan9":
46		d, err := os.UserConfigDir()
47		if err != nil {
48			return "", err
49		}
50		dir = d
51	default:
52		home, err := os.UserHomeDir()
53		if err != nil {
54			return "", err
55		}
56		dir = filepath.Join(home, ".config")
57	}
58	return filepath.Join(dir, app, name), nil
59}
60
61// Path is a file system path value: it accepts an absolute path or one
62// starting with ~/ (the home directory), and stores it cleaned. Use it as a
63// field type to validate paths while decoding.
64type Path string
65
66// UnmarshalText implements encoding.TextUnmarshaler.
67func (p *Path) UnmarshalText(text []byte) error {
68	s, err := ExpandPath(string(text))
69	if err != nil {
70		return err
71	}
72	*p = Path(s)
73	return nil
74}
75
76// MarshalText implements encoding.TextMarshaler.
77func (p Path) MarshalText() ([]byte, error) { return []byte(p), nil }
78
79// ExpandPath expands a leading ~/ and requires an absolute, NUL-free path.
80func ExpandPath(s string) (string, error) {
81	if s == "" || strings.IndexByte(s, 0) >= 0 {
82		return "", ValueError("must be a non-empty path")
83	}
84	if rest, ok := strings.CutPrefix(s, "~/"); ok || s == "~" {
85		if !ok {
86			rest = ""
87		}
88		home, err := os.UserHomeDir()
89		if err != nil || !filepath.IsAbs(home) {
90			return "", ValueError("no absolute home directory for ~")
91		}
92		s = filepath.Join(home, rest)
93	}
94	if !filepath.IsAbs(s) {
95		return "", ValueError("must be an absolute path or start with ~/")
96	}
97	return filepath.Clean(s), nil
98}
99
100// ErrNotRegular is returned by ReadFile for a directory, FIFO or device.
101var ErrNotRegular = errors.New("not a regular file")
102
103// isNotExist reports a missing file, including a missing parent directory.
104func isNotExist(err error) bool { return errors.Is(err, fs.ErrNotExist) }

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 1

Tags 3

↑↓ moveenter openesc close