Skip to content

Commit 5653623

Browse files
committed
simplify
Signed-off-by: h8d13 <hadean-eon-dev@proton.me>
1 parent a9eac76 commit 5653623

3 files changed

Lines changed: 81 additions & 108 deletions

File tree

internal/buffer/chunk.go

Lines changed: 51 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,43 @@ func (b *Buffer) IndentChunk(cury int) (Chunk, bool) {
4040
return findIndentChunk(b.LineBytes, b.LinesNum(), cury, tabsize)
4141
}
4242

43-
// BraceChunk locates the innermost multi-line bracket pair around cur.
44-
// It reports false when no pair encloses the cursor within the scan
45-
// limit.
43+
// BraceChunk locates the innermost bracket pair around cur that spans
44+
// more than one line. It reports false when no pair encloses the cursor
45+
// within the scan limit.
4646
func (b *Buffer) BraceChunk(cur Loc) (Chunk, bool) {
47+
var cg Chunk
4748
tabsize := util.IntOpt(b.Settings["tabsize"])
48-
return findBraceChunk(b.LineBytes, b.LinesNum(), cur, tabsize)
49+
ymin, _ := chunkScanBounds(cur.Y, b.LinesNum())
50+
51+
cg.Start = -1
52+
// a line that leaves a bracket open anchors the chunk that bracket
53+
// opens, mirroring the indent mode's header rule
54+
line := []rune(string(b.LineBytes(cur.Y)))
55+
if x := lastOpenBrace(line); x >= 0 {
56+
pair, _ := braceDir(line[x])
57+
if cl, ok := b.findMatchingBrace(pair, Loc{x, cur.Y}, pair[0]); ok && cl.Y > cur.Y {
58+
cg.Start, cg.End = cur.Y, cl.Y
59+
}
60+
}
61+
// enclosing pairs living on a single line are not chunks: consume
62+
// them and keep scanning outward
63+
pos := cur
64+
for cg.Start < 0 {
65+
op, pair, ok := enclosingBrace(b.LineBytes, ymin, pos)
66+
if !ok {
67+
return cg, false
68+
}
69+
if cl, ok := b.findMatchingBrace(pair, op, pair[0]); ok && cl.Y > op.Y {
70+
cg.Start, cg.End = op.Y, cl.Y
71+
break
72+
}
73+
pos = op
74+
}
75+
76+
cg.StartIndent, _ = visualIndent(b.LineBytes(cg.Start), tabsize)
77+
cg.EndIndent, _ = visualIndent(b.LineBytes(cg.End), tabsize)
78+
cg.finalize(b.LineBytes, cur.Y, tabsize)
79+
return cg, true
4980
}
5081

5182
// visualIndent returns the display width of the line's leading whitespace
@@ -79,23 +110,10 @@ func chunkScanBounds(y, nlines int) (int, int) {
79110
}
80111

81112
// finalize turns raw boundaries into a drawable guide: place the guide
82-
// column one indent level left of the boundary indent and retarget
83-
// corners that a column-zero boundary leaves with no whitespace to draw
84-
// into.
113+
// column one indent level left of the boundary indent. Column-zero
114+
// boundary lines have no whitespace to hold their corner; the display
115+
// layer skips such corner rows and the bars just span the body.
85116
func (cg *Chunk) finalize(getLine func(int) []byte, cury, tabsize int) {
86-
// a boundary at column zero has no whitespace to hold the bottom
87-
// corner, leaving the bars dangling, so anchor the corner on the
88-
// chunk's last code line instead (such blocks read as ending there:
89-
// the closing token, if any, sits at top level)
90-
if cg.EndIndent == 0 {
91-
for y := cg.End - 1; y > cg.Start; y-- {
92-
if w, b := visualIndent(getLine(y), tabsize); !b {
93-
cg.End, cg.EndIndent = y, w
94-
break
95-
}
96-
}
97-
}
98-
99117
cg.GuideCol = cg.StartIndent
100118
if cg.EndIndent < cg.GuideCol {
101119
cg.GuideCol = cg.EndIndent
@@ -170,6 +188,19 @@ func findIndentChunk(getLine func(int) []byte, nlines, cury, tabsize int) (Chunk
170188
return cg, false
171189
}
172190

191+
// a dedent straight to column zero means the boundary is a sibling
192+
// statement, not part of the chunk (unlike bracket mode, where the
193+
// closer is the chunk's own last line), so anchor the corner on the
194+
// chunk's last code line instead of dangling the bars over it
195+
if cg.EndIndent == 0 {
196+
for y := cg.End - 1; y > cg.Start; y-- {
197+
if w, b := visualIndent(getLine(y), tabsize); !b {
198+
cg.End, cg.EndIndent = y, w
199+
break
200+
}
201+
}
202+
}
203+
173204
cg.finalize(getLine, cury, tabsize)
174205
return cg, true
175206
}
@@ -207,31 +238,6 @@ func lastOpenBrace(line []rune) int {
207238
return open[len(open)-1]
208239
}
209240

210-
// braceMatchForward finds the closer matching the opener at start,
211-
// scanning no further than line ymax
212-
func braceMatchForward(getLine func(int) []byte, ymax int, start Loc, pair [2]rune) (Loc, bool) {
213-
depth := 0
214-
for y := start.Y; y <= ymax; y++ {
215-
l := []rune(string(getLine(y)))
216-
x0 := 0
217-
if y == start.Y {
218-
x0 = start.X
219-
}
220-
for x := x0; x < len(l); x++ {
221-
switch l[x] {
222-
case pair[0]:
223-
depth++
224-
case pair[1]:
225-
depth--
226-
if depth == 0 {
227-
return Loc{x, y}, true
228-
}
229-
}
230-
}
231-
}
232-
return start, false
233-
}
234-
235241
// enclosingBrace scans backwards from cur (exclusive) for the nearest
236242
// bracket left open at the cursor, scanning no further than line ymin.
237243
// Type-blind pairing, as in lastOpenBrace.
@@ -257,40 +263,3 @@ func enclosingBrace(getLine func(int) []byte, ymin int, cur Loc) (Loc, [2]rune,
257263
}
258264
return cur, [2]rune{}, false
259265
}
260-
261-
// findBraceChunk locates the innermost bracket pair around cur that
262-
// spans more than one line.
263-
func findBraceChunk(getLine func(int) []byte, nlines int, cur Loc, tabsize int) (Chunk, bool) {
264-
var cg Chunk
265-
ymin, ymax := chunkScanBounds(cur.Y, nlines)
266-
267-
cg.Start = -1
268-
// a line that leaves a bracket open anchors the chunk that bracket
269-
// opens, mirroring the indent mode's header rule
270-
line := []rune(string(getLine(cur.Y)))
271-
if x := lastOpenBrace(line); x >= 0 {
272-
pair, _ := braceDir(line[x])
273-
if cl, ok := braceMatchForward(getLine, ymax, Loc{x, cur.Y}, pair); ok && cl.Y > cur.Y {
274-
cg.Start, cg.End = cur.Y, cl.Y
275-
}
276-
}
277-
// enclosing pairs living on a single line are not chunks: consume
278-
// them and keep scanning outward
279-
pos := cur
280-
for cg.Start < 0 {
281-
op, pair, ok := enclosingBrace(getLine, ymin, pos)
282-
if !ok {
283-
return cg, false
284-
}
285-
if cl, ok := braceMatchForward(getLine, ymax, op, pair); ok && cl.Y > op.Y {
286-
cg.Start, cg.End = op.Y, cl.Y
287-
break
288-
}
289-
pos = op
290-
}
291-
292-
cg.StartIndent, _ = visualIndent(getLine(cg.Start), tabsize)
293-
cg.EndIndent, _ = visualIndent(getLine(cg.End), tabsize)
294-
cg.finalize(getLine, cur.Y, tabsize)
295-
return cg, true
296-
}

internal/buffer/chunk_test.go

Lines changed: 27 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,13 @@ func TestFindIndentChunk(t *testing.T) {
6060
}
6161
}
6262

63-
func TestFindBraceChunk(t *testing.T) {
63+
func braceBuf(src string) *Buffer {
64+
b := NewBufferFromString(src, "", BTDefault)
65+
b.Settings["tabsize"] = float64(8)
66+
return b
67+
}
68+
69+
func TestBraceChunk(t *testing.T) {
6470
// 0: func main() {
6571
// 1: if x >
6672
// 2: 0 {
@@ -70,7 +76,7 @@ func TestFindBraceChunk(t *testing.T) {
7076
// 6: d,
7177
// 7: )
7278
// 8: }
73-
getLine, n := linesOf("func main() {\n\tif x >\n\t\t0 {\n\t\ta(b)\n\t}\n\tc(\n\t\td,\n\t)\n}")
79+
b := braceBuf("func main() {\n\tif x >\n\t\t0 {\n\t\ta(b)\n\t}\n\tc(\n\t\td,\n\t)\n}")
7480

7581
for _, c := range []struct {
7682
cur Loc
@@ -86,47 +92,43 @@ func TestFindBraceChunk(t *testing.T) {
8692
// col-0 `}` retargets nothing here (endIndent 8 > 0)
8793
{Loc{4, 3}, 2, 4, 0, true},
8894
} {
89-
cg, ok := findBraceChunk(getLine, n, c.cur, 8)
95+
cg, ok := b.BraceChunk(c.cur)
9096
if ok != c.ok || ok && (cg.Start != c.start || cg.End != c.end || cg.GuideCol != c.gcol) {
91-
t.Errorf("findBraceChunk(%v) = %+v,%v, want %+v", c.cur, cg, ok, c)
97+
t.Errorf("BraceChunk(%v) = %+v,%v, want %+v", c.cur, cg, ok, c)
9298
}
9399
}
94100

95-
// func chunk: bottom corner retargets off the col-0 `}` to the last
96-
// code line
97-
if cg, ok := findBraceChunk(getLine, n, Loc{0, 1}, 8); !ok || cg.Start != 0 || cg.End != 7 || cg.EndIndent != 8 {
98-
t.Errorf("func chunk: got %+v,%v, want start 0 end 7 endIndent 8", cg, ok)
101+
// func chunk: the col-0 `}` stays the boundary (no corner drawn
102+
// there, bars span the body)
103+
if cg, ok := b.BraceChunk(Loc{0, 1}); !ok || cg.Start != 0 || cg.End != 8 || cg.EndIndent != 0 {
104+
t.Errorf("func chunk: got %+v,%v, want start 0 end 8 endIndent 0", cg, ok)
105+
}
106+
107+
// Allman braces: the guide runs to the function's own closer, not
108+
// the inner for-loop's `}` above it
109+
b = braceBuf("f()\n{\n\ta();\n\tfor (;;) {\n\t\tb();\n\t}\n}")
110+
if cg, ok := b.BraceChunk(Loc{0, 1}); !ok || cg.Start != 1 || cg.End != 6 || cg.EndIndent != 0 {
111+
t.Errorf("allman: got %+v,%v, want start 1 end 6 endIndent 0", cg, ok)
99112
}
100113

101114
// no enclosing pair at top level
102-
getLine, n = linesOf("x := 1\ny := 2")
103-
if _, ok := findBraceChunk(getLine, n, Loc{0, 1}, 8); ok {
115+
if _, ok := braceBuf("x := 1\ny := 2").BraceChunk(Loc{0, 1}); ok {
104116
t.Error("chunk reported at top level")
105117
}
106118

107119
// unclosed opener is not a chunk
108-
getLine, n = linesOf("if x {\n\ta(")
109-
if _, ok := findBraceChunk(getLine, n, Loc{3, 1}, 8); ok {
120+
if _, ok := braceBuf("if x {\n\ta(").BraceChunk(Loc{3, 1}); ok {
110121
t.Error("unclosed chunk reported")
111122
}
112123

113124
// blank line inside a block still resolves (indent mode cannot)
114-
getLine, n = linesOf("if x {\n\ta()\n\n\tb()\n}")
115-
if cg, ok := findBraceChunk(getLine, n, Loc{0, 2}, 8); !ok || cg.Start != 0 || cg.End != 3 {
116-
t.Errorf("blank line: got %+v,%v, want start 0 end 3", cg, ok)
125+
if cg, ok := braceBuf("if x {\n\ta()\n\n\tb()\n}").BraceChunk(Loc{0, 2}); !ok || cg.Start != 0 || cg.End != 4 {
126+
t.Errorf("blank line: got %+v,%v, want start 0 end 4", cg, ok)
117127
}
118128

119129
// boundaries beyond the scan cap
120-
huge := func(i int) []byte {
121-
if i == 0 {
122-
return []byte("f(")
123-
}
124-
if i == 2*chunkScanLimit+2 {
125-
return []byte(")")
126-
}
127-
return []byte("\tx,")
128-
}
129-
if _, ok := findBraceChunk(huge, 2*chunkScanLimit+3, Loc{0, chunkScanLimit + 1}, 8); ok {
130+
huge := "f(\n" + strings.Repeat("\tx,\n", 2*chunkScanLimit+1) + ")"
131+
if _, ok := braceBuf(huge).BraceChunk(Loc{0, chunkScanLimit + 1}); ok {
130132
t.Error("chunk beyond scan limit reported")
131133
}
132134
}

runtime/help/options.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,9 @@ Here are the available options:
189189
* `bracket`: the chunk is the innermost `()`, `[]` or `{}` pair spanning
190190
more than one line around the cursor. Exact block extents for brace
191191
languages (multi-line conditions, mixed indentation), but brackets
192-
inside strings and comments miscount, as with `matchbrace`.
192+
inside strings and comments miscount, as with `matchbrace`. The
193+
guide only draws into whitespace, so a bracket in column zero gets
194+
no corner row; the bars stop at the body's edge.
193195

194196
default value: `indent`
195197

0 commit comments

Comments
 (0)