v0.4.0 2de4e1d

edit_test.go RawHistoryFolder

1package edit
2
3import (
4	"strings"
5	"testing"
6	"unicode/utf8"
7)
8
9type memoryClipboard struct{ data []byte }
10
11func (m *memoryClipboard) ReadText(limit int) ([]byte, error) { return m.data, nil }
12func (m *memoryClipboard) WriteText(v []byte) error           { m.data = append([]byte(nil), v...); return nil }
13
14type fakeIME struct {
15	enabled, disabled int
16	surrounding       string
17	cursor, anchor    int
18}
19
20func (f *fakeIME) Enable()                              { f.enabled++ }
21func (f *fakeIME) Disable()                             { f.disabled++ }
22func (f *fakeIME) Surrounding(v string, c, a int)       { f.surrounding = v; f.cursor = c; f.anchor = a }
23func (f *fakeIME) CursorRect(x, y, w, h float64)        {}
24func (f *fakeIME) ContentType(password, multiline bool) {}
25func TestGraphemesAndIME(t *testing.T) {
26	s := State{}
27	s.Sync("e\u0301👩<U+200D>💻a")
28	s.Move(len(s.Value), false)
29	s.Left(false)
30	if s.Cursor != len("e\u0301👩<U+200D>💻") {
31		t.Fatal(s.Cursor)
32	}
33	s.Left(false)
34	if s.Cursor != len("e\u0301") {
35		t.Fatal(s.Cursor)
36	}
37	s.Select(0, len("e\u0301"))
38	if !s.Delete(true, false) || s.Value != "👩<U+200D>💻a" {
39		t.Fatalf("%+v", s)
40	}
41	s.Select(len("👩<U+200D>💻"), len("👩<U+200D>💻"))
42	if !s.Done(IMEBatch{DeleteBefore: 1, Commit: "é"}) || s.Value != "éa" {
43		t.Fatalf("delete bytes: %+v", s)
44	}
45	s.Done(IMEBatch{Preedit: "候補", Begin: 0, End: len("候補")})
46	if s.Preedit != "候補" || s.Value != "éa" {
47		t.Fatal(s)
48	}
49	ime := &fakeIME{}
50	s.Focus(ime, false, false)
51	s.Blur(ime)
52	if ime.enabled != 1 || ime.disabled != 1 || s.Preedit != "" {
53		t.Fatal(s, ime)
54	}
55}
56func TestClipboardAndMask(t *testing.T) {
57	s := State{}
58	s.Sync("s3crét")
59	s.Select(0, len(s.Value))
60	c := &memoryClipboard{}
61	if err := s.Copy(c, true); err != nil || len(c.data) != 0 {
62		t.Fatal(err)
63	}
64	if s.Mask() != "••••••" {
65		t.Fatal(s.Mask())
66	}
67	c.data = []byte{0xff}
68	if changed, _ := s.Paste(c); changed {
69		t.Fatal(s)
70	}
71	c.data = []byte(strings.Repeat("x", MaxClipboardBytes+1))
72	if changed, _ := s.Paste(c); changed {
73		t.Fatal(s)
74	}
75	c.data = []byte("ok")
76	if changed, _ := s.Paste(c); !changed || s.Value != "ok" {
77		t.Fatal(s)
78	}
79}
80func FuzzEdit(f *testing.F) {
81	f.Add([]byte("e\u0301👩<U+200D>💻🧪"))
82	f.Add([]byte{0xff, 0x80, 0x00})
83	f.Fuzz(func(t *testing.T, data []byte) {
84		if len(data) > 2048 {
85			return
86		}
87		var s State
88		for _, op := range data {
89			switch op % 7 {
90			case 0:
91				s.Left(false)
92			case 1:
93				s.Right(true)
94			case 2:
95				s.Insert("e\u0301")
96			case 3:
97				s.Insert("👩<U+200D>💻")
98			case 4:
99				s.Delete(true, false)
100			case 5:
101				s.Delete(false, true)
102			case 6:
103				s.Sync(strings.ToValidUTF8(string(data), "�"))
104			}
105			if !utf8.ValidString(s.Value) {
106				t.Fatal("invalid UTF-8")
107			}
108			for _, p := range []int{s.Cursor, s.Anchor} {
109				ok := false
110				for _, v := range Boundaries(s.Value) {
111					if p == v {
112						ok = true
113					}
114				}
115				if !ok {
116					t.Fatalf("not boundary %d in %q", p, s.Value)
117				}
118			}
119		}
120	})
121}

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 12

↑↓ moveenter openesc close