@@ -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.
4646func (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 .
85116func (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- }
0 commit comments