v0.1.0 47aaa0f

edit_mixed_direction_test.go RawHistoryFolder

1package ui
2
3import (
4	"math"
5	"testing"
6
7	"github.com/bnema/nefergui/internal/css"
8	"github.com/bnema/nefergui/internal/layout"
9	"github.com/bnema/nefergui/internal/text"
10	"github.com/go-text/typesetting/di"
11)
12
13func TestMixedDirectionSelectionVisualRects(t *testing.T) {
14	catalog, err := text.Load(text.DirectorySource("../../testdata/fonts"))
15	if err != nil {
16		t.Fatal(err)
17	}
18	r := newRuntime()
19	r.setTextEngine(text.NewEngine(catalog))
20	r.styles = css.Compile(css.UA(), css.Parse(`input {width:300px;height:28px;font-size:16px}`))
21	value := "abc مرحبا def"
22	view := func(f *Frame) { f.Root().Input("mixed", &value) }
23	r.Build(view)
24	r.route(key("Tab"))
25	r.Build(view)
26	n := r.output.Tree.Children[0]
27	if len(n.Lines) != 1 {
28		t.Fatalf("expected one line, got %d", len(n.Lines))
29	}
30	rtl := false
31	ltr := false
32	for _, run := range n.Lines[0].Runs {
33		rtl = rtl || run.Direction == di.DirectionRTL
34		ltr = ltr || run.Direction == di.DirectionLTR
35	}
36	if !rtl || !ltr {
37		t.Fatalf("not mixed bidi: %+v", n.Lines[0].Runs)
38	}
39	s := r.edits[n.ID]
40	s.Select(len("abc "), len("abc مرحبا "))
41	r.Redraw()
42	r.Build(view)
43	n = r.output.Tree.Children[0]
44	boundaries := textBoundaries(value, n, r.output.Display)
45	if len(boundaries) != 1 {
46		t.Fatalf("expected one visual line: %+v", boundaries)
47	}
48	// Selected logical clusters must paint at the corresponding visual span,
49	// including the reversed RTL run and the following LTR separator.
50	var rects []layout.Rect
51	for _, c := range r.output.Display {
52		if c.ID == n.ID && c.Op == "selection" {
53			rects = append(rects, c.Rect)
54		}
55	}
56	if len(rects) < 6 {
57		t.Fatalf("missing per-cluster bidi highlights: %+v", rects)
58	}
59	for _, b := range []int{len("abc "), len("abc م"), len("abc مر"), len("abc مرح"), len("abc مرحب")} {
60		has := false
61		for _, edge := range boundaries[0] {
62			if edge.byteOffset != b {
63				continue
64			}
65			for _, rect := range rects {
66				if math.Abs(rect.X-edge.x) < 0.01 || math.Abs(rect.X+rect.W-edge.x) < 0.01 {
67					has = true
68				}
69			}
70		}
71		if !has {
72			t.Fatalf("byte edge %d not painted at visual cluster: %+v rects=%+v", b, boundaries[0], rects)
73		}
74	}
75}

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