Skip to content

Commit bdc57b4

Browse files
Adding Jumping to opening and closing brace logic and actions, ...
Simplifying findMatchingBrace(), Adding FindOpeningBrace() and FindClosingBrace(), Adding JumpToOpeningBrace() and JumpToClosingBrace() Adding brace jumping to default bindings
1 parent a10624c commit bdc57b4

File tree

7 files changed

+156
-33
lines changed

7 files changed

+156
-33
lines changed

internal/action/actions.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1407,6 +1407,28 @@ func (h *BufPane) JumpToMatchingBrace() bool {
14071407
return false
14081408
}
14091409

1410+
// JumpToOpeningBrace moves the cursor to the opening brace in current brace scope
1411+
func (h *BufPane) JumpToOpeningBrace() bool {
1412+
matchingBrace, found := h.Buf.FindOpeningBrace(h.Cursor.Loc)
1413+
if found {
1414+
h.Cursor.GotoLoc(matchingBrace)
1415+
h.Relocate()
1416+
return true
1417+
}
1418+
return false
1419+
}
1420+
1421+
// JumpToClosingBrace moves the cursor to the closing brace in current brace scope
1422+
func (h *BufPane) JumpToClosingBrace() bool {
1423+
matchingBrace, found := h.Buf.FindClosingBrace(h.Cursor.Loc)
1424+
if found {
1425+
h.Cursor.GotoLoc(matchingBrace)
1426+
h.Relocate()
1427+
return true
1428+
}
1429+
return false
1430+
}
1431+
14101432
// SelectAll selects the entire buffer
14111433
func (h *BufPane) SelectAll() bool {
14121434
h.Cursor.SetSelectionStart(h.Buf.Start())

internal/action/bufpane.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -840,6 +840,8 @@ var BufKeyActions = map[string]BufKeyAction{
840840
"RemoveAllMultiCursors": (*BufPane).RemoveAllMultiCursors,
841841
"SkipMultiCursor": (*BufPane).SkipMultiCursor,
842842
"JumpToMatchingBrace": (*BufPane).JumpToMatchingBrace,
843+
"JumpToOpeningBrace": (*BufPane).JumpToOpeningBrace,
844+
"JumpToClosingBrace": (*BufPane).JumpToClosingBrace,
843845
"JumpLine": (*BufPane).JumpLine,
844846
"Deselect": (*BufPane).Deselect,
845847
"ClearInfo": (*BufPane).ClearInfo,
@@ -922,4 +924,6 @@ var MultiActions = map[string]bool{
922924
"StartOfTextToggle": true,
923925
"EndOfLine": true,
924926
"JumpToMatchingBrace": true,
927+
"JumpToOpeningBrace": true,
928+
"JumpToClosingBrace": true,
925929
}

internal/action/defaults_darwin.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ var bufdefaults = map[string]string{
7474
"Ctrl-u": "ToggleMacro",
7575
"Ctrl-j": "PlayMacro",
7676
"Insert": "ToggleOverwriteMode",
77+
"Alt-<": "JumpToOpeningBrace",
78+
"Alt->": "JumpToClosingBrace",
7779

7880
// Emacs-style keybindings
7981
"Alt-f": "WordRight",

internal/action/defaults_other.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@ var bufdefaults = map[string]string{
7777
"Ctrl-u": "ToggleMacro",
7878
"Ctrl-j": "PlayMacro",
7979
"Insert": "ToggleOverwriteMode",
80+
"Alt-<": "JumpToOpeningBrace",
81+
"Alt->": "JumpToClosingBrace",
8082

8183
// Emacs-style keybindings
8284
"Alt-f": "WordRight",

internal/buffer/buffer.go

Lines changed: 120 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1140,50 +1140,137 @@ var BracePairs = [][2]rune{
11401140
{'[', ']'},
11411141
}
11421142

1143-
func (b *Buffer) findMatchingBrace(braceType [2]rune, start Loc, char rune) (Loc, bool) {
1144-
var i int
1145-
if char == braceType[0] {
1146-
for y := start.Y; y < b.LinesNum(); y++ {
1147-
l := []rune(string(b.LineBytes(y)))
1148-
xInit := 0
1149-
if y == start.Y {
1150-
xInit = start.X
1151-
}
1152-
for x := xInit; x < len(l); x++ {
1153-
r := l[x]
1154-
if r == braceType[0] {
1155-
i++
1156-
} else if r == braceType[1] {
1157-
i--
1158-
if i == 0 {
1159-
return Loc{x, y}, true
1160-
}
1143+
func (b *Buffer) findOpeningBrace(braceType [2]rune, start Loc) (Loc, bool) {
1144+
// Bound guard
1145+
start = clamp(start, b.LineArray)
1146+
if len(b.lines) == 0 {
1147+
return start, false
1148+
}
1149+
1150+
i := 1
1151+
// If we are on a closing brace, let the counter be incremented below when we traverse
1152+
curLine := []rune(string(b.LineBytes(start.Y)))
1153+
if start.X >= 0 && start.X < len(curLine) {
1154+
startChar := curLine[start.X]
1155+
if startChar == braceType[1] {
1156+
i = 0
1157+
}
1158+
}
1159+
1160+
for y := start.Y; y >= 0; y-- {
1161+
l := []rune(string(b.lines[y].data))
1162+
xInit := len(l) - 1
1163+
if y == start.Y && start.X < len(curLine) {
1164+
xInit = start.X
1165+
}
1166+
for x := xInit; x >= 0; x-- {
1167+
r := l[x]
1168+
if r == braceType[1] {
1169+
i++
1170+
} else if r == braceType[0] {
1171+
i--
1172+
if i == 0 {
1173+
return Loc{x, y}, true
11611174
}
11621175
}
11631176
}
1164-
} else if char == braceType[1] {
1165-
for y := start.Y; y >= 0; y-- {
1166-
l := []rune(string(b.lines[y].data))
1167-
xInit := len(l) - 1
1168-
if y == start.Y {
1169-
xInit = start.X
1177+
}
1178+
return start, false
1179+
}
1180+
1181+
// Returns the opening brace in current brace scope starting at the start location and a boolean
1182+
// indicating if an opening brace is found
1183+
func (b *Buffer) FindOpeningBrace(start Loc) (Loc, bool) {
1184+
currentDist := -1
1185+
currentMb := Loc{-1, -1}
1186+
for _, bp := range BracePairs {
1187+
mb, found := b.findOpeningBrace(bp, start)
1188+
if found {
1189+
dist := DiffLA(start, mb, b.LineArray)
1190+
if currentDist < 0 || dist < currentDist {
1191+
currentMb = mb
1192+
currentDist = dist
11701193
}
1171-
for x := xInit; x >= 0; x-- {
1172-
r := l[x]
1173-
if r == braceType[1] {
1174-
i++
1175-
} else if r == braceType[0] {
1176-
i--
1177-
if i == 0 {
1178-
return Loc{x, y}, true
1179-
}
1194+
}
1195+
}
1196+
1197+
if currentDist == -1 {
1198+
return start, false
1199+
} else {
1200+
return currentMb, true
1201+
}
1202+
}
1203+
1204+
func (b *Buffer) findClosingBrace(braceType [2]rune, start Loc) (Loc, bool) {
1205+
// Bound guard
1206+
start = clamp(start, b.LineArray)
1207+
if len(b.lines) == 0 {
1208+
return start, false
1209+
}
1210+
1211+
i := 1
1212+
// If we are on an opening brace, let the counter be incremented below when we traverse
1213+
curLine := []rune(string(b.LineBytes(start.Y)))
1214+
if start.X >= 0 && start.X < len(curLine) {
1215+
startChar := curLine[start.X]
1216+
if startChar == braceType[0] {
1217+
i = 0
1218+
}
1219+
}
1220+
1221+
for y := start.Y; y < b.LinesNum(); y++ {
1222+
l := []rune(string(b.LineBytes(y)))
1223+
xInit := 0
1224+
if y == start.Y && start.X >= 0 {
1225+
xInit = start.X
1226+
}
1227+
for x := xInit; x < len(l); x++ {
1228+
r := l[x]
1229+
if r == braceType[0] {
1230+
i++
1231+
} else if r == braceType[1] {
1232+
i--
1233+
if i == 0 {
1234+
return Loc{x, y}, true
11801235
}
11811236
}
11821237
}
11831238
}
11841239
return start, false
11851240
}
11861241

1242+
// Returns the closing brace in current brace scope starting at the start location and a boolean
1243+
// indicating if an closing brace is found
1244+
func (b *Buffer) FindClosingBrace(start Loc) (Loc, bool) {
1245+
currentDist := -1
1246+
currentMb := Loc{-1, -1}
1247+
for _, bp := range BracePairs {
1248+
mb, found := b.findClosingBrace(bp, start)
1249+
if found {
1250+
dist := DiffLA(start, mb, b.LineArray)
1251+
if currentDist < 0 || dist < currentDist {
1252+
currentMb = mb
1253+
currentDist = dist
1254+
}
1255+
}
1256+
}
1257+
1258+
if currentDist == -1 {
1259+
return start, false
1260+
} else {
1261+
return currentMb, true
1262+
}
1263+
}
1264+
1265+
func (b *Buffer) findMatchingBrace(braceType [2]rune, start Loc, char rune) (Loc, bool) {
1266+
if char == braceType[0] {
1267+
return b.findClosingBrace(braceType, start)
1268+
} else if char == braceType[1] {
1269+
return b.findOpeningBrace(braceType, start)
1270+
}
1271+
return start, false
1272+
}
1273+
11871274
// If there is a brace character (for example '{' or ']') at the given start location,
11881275
// FindMatchingBrace returns the location of the matching brace for it (for example '}'
11891276
// or '['). The second returned value is true if there was no matching brace found

runtime/help/defaultkeys.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ can change it!
3333
| PageDown | Move cursor down one page |
3434
| Ctrl-Home or Ctrl-UpArrow | Move cursor to start of document |
3535
| Ctrl-End or Ctrl-DownArrow | Move cursor to end of document |
36+
| Alt-< | Move cursor to opening brace in current brace scope |
37+
| Alt-> | Move cursor to closing brace in current brace scope |
3638
| Ctrl-l | Jump to a line in the file (prompts with #) |
3739
| Ctrl-w | Cycle between splits in the current tab (use `> vsplit` or `> hsplit` to create a split) |
3840

runtime/help/keybindings.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,8 @@ RemoveAllMultiCursors
268268
SkipMultiCursor
269269
None
270270
JumpToMatchingBrace
271+
JumpToOpeningBrace
272+
JumpToClosingBrace
271273
Autocomplete
272274
```
273275

@@ -520,6 +522,8 @@ conventions for text editing defaults.
520522
"Ctrl-u": "ToggleMacro",
521523
"Ctrl-j": "PlayMacro",
522524
"Insert": "ToggleOverwriteMode",
525+
"Alt-<": "JumpToOpeningBrace",
526+
"Alt->": "JumpToClosingBrace",
523527

524528
// Emacs-style keybindings
525529
"Alt-f": "WordRight",

0 commit comments

Comments
 (0)