Skip to content

Commit 79757f5

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 ec8bb7c commit 79757f5

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
@@ -1653,6 +1653,28 @@ func (h *BufPane) JumpToMatchingBrace() bool {
16531653
return false
16541654
}
16551655

1656+
// JumpToOpeningBrace moves the cursor to the opening brace in current brace scope
1657+
func (h *BufPane) JumpToOpeningBrace() bool {
1658+
matchingBrace, found := h.Buf.FindOpeningBrace(h.Cursor.Loc)
1659+
if found {
1660+
h.Cursor.GotoLoc(matchingBrace)
1661+
h.Relocate()
1662+
return true
1663+
}
1664+
return false
1665+
}
1666+
1667+
// JumpToClosingBrace moves the cursor to the closing brace in current brace scope
1668+
func (h *BufPane) JumpToClosingBrace() bool {
1669+
matchingBrace, found := h.Buf.FindClosingBrace(h.Cursor.Loc)
1670+
if found {
1671+
h.Cursor.GotoLoc(matchingBrace)
1672+
h.Relocate()
1673+
return true
1674+
}
1675+
return false
1676+
}
1677+
16561678
// SelectAll selects the entire buffer
16571679
func (h *BufPane) SelectAll() bool {
16581680
h.Cursor.SetSelectionStart(h.Buf.Start())

internal/action/bufpane.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -847,6 +847,8 @@ var BufKeyActions = map[string]BufKeyAction{
847847
"SkipMultiCursor": (*BufPane).SkipMultiCursor,
848848
"SkipMultiCursorBack": (*BufPane).SkipMultiCursorBack,
849849
"JumpToMatchingBrace": (*BufPane).JumpToMatchingBrace,
850+
"JumpToOpeningBrace": (*BufPane).JumpToOpeningBrace,
851+
"JumpToClosingBrace": (*BufPane).JumpToClosingBrace,
850852
"JumpLine": (*BufPane).JumpLine,
851853
"Deselect": (*BufPane).Deselect,
852854
"ClearInfo": (*BufPane).ClearInfo,
@@ -930,4 +932,6 @@ var MultiActions = map[string]bool{
930932
"StartOfTextToggle": true,
931933
"EndOfLine": true,
932934
"JumpToMatchingBrace": true,
935+
"JumpToOpeningBrace": true,
936+
"JumpToClosingBrace": true,
933937
}

internal/action/defaults_darwin.go

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

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

internal/action/defaults_other.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ var bufdefaults = map[string]string{
7979
"Ctrl-u": "ToggleMacro",
8080
"Ctrl-j": "PlayMacro",
8181
"Insert": "ToggleOverwriteMode",
82+
"Alt-<": "JumpToOpeningBrace",
83+
"Alt->": "JumpToClosingBrace",
8284

8385
// Emacs-style keybindings
8486
"Alt-f": "WordRight",

internal/buffer/buffer.go

Lines changed: 120 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1173,50 +1173,137 @@ var BracePairs = [][2]rune{
11731173
{'[', ']'},
11741174
}
11751175

1176-
func (b *Buffer) findMatchingBrace(braceType [2]rune, start Loc, char rune) (Loc, bool) {
1177-
var i int
1178-
if char == braceType[0] {
1179-
for y := start.Y; y < b.LinesNum(); y++ {
1180-
l := []rune(string(b.LineBytes(y)))
1181-
xInit := 0
1182-
if y == start.Y {
1183-
xInit = start.X
1184-
}
1185-
for x := xInit; x < len(l); x++ {
1186-
r := l[x]
1187-
if r == braceType[0] {
1188-
i++
1189-
} else if r == braceType[1] {
1190-
i--
1191-
if i == 0 {
1192-
return Loc{x, y}, true
1193-
}
1176+
func (b *Buffer) findOpeningBrace(braceType [2]rune, start Loc) (Loc, bool) {
1177+
// Bound guard
1178+
start = clamp(start, b.LineArray)
1179+
if len(b.lines) == 0 {
1180+
return start, false
1181+
}
1182+
1183+
i := 1
1184+
// If we are on a closing brace, let the counter be incremented below when we traverse
1185+
curLine := []rune(string(b.LineBytes(start.Y)))
1186+
if start.X >= 0 && start.X < len(curLine) {
1187+
startChar := curLine[start.X]
1188+
if startChar == braceType[1] {
1189+
i = 0
1190+
}
1191+
}
1192+
1193+
for y := start.Y; y >= 0; y-- {
1194+
l := []rune(string(b.lines[y].data))
1195+
xInit := len(l) - 1
1196+
if y == start.Y && start.X < len(curLine) {
1197+
xInit = start.X
1198+
}
1199+
for x := xInit; x >= 0; x-- {
1200+
r := l[x]
1201+
if r == braceType[1] {
1202+
i++
1203+
} else if r == braceType[0] {
1204+
i--
1205+
if i == 0 {
1206+
return Loc{x, y}, true
11941207
}
11951208
}
11961209
}
1197-
} else if char == braceType[1] {
1198-
for y := start.Y; y >= 0; y-- {
1199-
l := []rune(string(b.lines[y].data))
1200-
xInit := len(l) - 1
1201-
if y == start.Y {
1202-
xInit = start.X
1210+
}
1211+
return start, false
1212+
}
1213+
1214+
// Returns the opening brace in current brace scope starting at the start location and a boolean
1215+
// indicating if an opening brace is found
1216+
func (b *Buffer) FindOpeningBrace(start Loc) (Loc, bool) {
1217+
currentDist := -1
1218+
currentMb := Loc{-1, -1}
1219+
for _, bp := range BracePairs {
1220+
mb, found := b.findOpeningBrace(bp, start)
1221+
if found {
1222+
dist := DiffLA(start, mb, b.LineArray)
1223+
if currentDist < 0 || dist < currentDist {
1224+
currentMb = mb
1225+
currentDist = dist
12031226
}
1204-
for x := xInit; x >= 0; x-- {
1205-
r := l[x]
1206-
if r == braceType[1] {
1207-
i++
1208-
} else if r == braceType[0] {
1209-
i--
1210-
if i == 0 {
1211-
return Loc{x, y}, true
1212-
}
1227+
}
1228+
}
1229+
1230+
if currentDist == -1 {
1231+
return start, false
1232+
} else {
1233+
return currentMb, true
1234+
}
1235+
}
1236+
1237+
func (b *Buffer) findClosingBrace(braceType [2]rune, start Loc) (Loc, bool) {
1238+
// Bound guard
1239+
start = clamp(start, b.LineArray)
1240+
if len(b.lines) == 0 {
1241+
return start, false
1242+
}
1243+
1244+
i := 1
1245+
// If we are on an opening brace, let the counter be incremented below when we traverse
1246+
curLine := []rune(string(b.LineBytes(start.Y)))
1247+
if start.X >= 0 && start.X < len(curLine) {
1248+
startChar := curLine[start.X]
1249+
if startChar == braceType[0] {
1250+
i = 0
1251+
}
1252+
}
1253+
1254+
for y := start.Y; y < b.LinesNum(); y++ {
1255+
l := []rune(string(b.LineBytes(y)))
1256+
xInit := 0
1257+
if y == start.Y && start.X >= 0 {
1258+
xInit = start.X
1259+
}
1260+
for x := xInit; x < len(l); x++ {
1261+
r := l[x]
1262+
if r == braceType[0] {
1263+
i++
1264+
} else if r == braceType[1] {
1265+
i--
1266+
if i == 0 {
1267+
return Loc{x, y}, true
12131268
}
12141269
}
12151270
}
12161271
}
12171272
return start, false
12181273
}
12191274

1275+
// Returns the closing brace in current brace scope starting at the start location and a boolean
1276+
// indicating if an closing brace is found
1277+
func (b *Buffer) FindClosingBrace(start Loc) (Loc, bool) {
1278+
currentDist := -1
1279+
currentMb := Loc{-1, -1}
1280+
for _, bp := range BracePairs {
1281+
mb, found := b.findClosingBrace(bp, start)
1282+
if found {
1283+
dist := DiffLA(start, mb, b.LineArray)
1284+
if currentDist < 0 || dist < currentDist {
1285+
currentMb = mb
1286+
currentDist = dist
1287+
}
1288+
}
1289+
}
1290+
1291+
if currentDist == -1 {
1292+
return start, false
1293+
} else {
1294+
return currentMb, true
1295+
}
1296+
}
1297+
1298+
func (b *Buffer) findMatchingBrace(braceType [2]rune, start Loc, char rune) (Loc, bool) {
1299+
if char == braceType[0] {
1300+
return b.findClosingBrace(braceType, start)
1301+
} else if char == braceType[1] {
1302+
return b.findOpeningBrace(braceType, start)
1303+
}
1304+
return start, false
1305+
}
1306+
12201307
// If there is a brace character (for example '{' or ']') at the given start location,
12211308
// FindMatchingBrace returns the location of the matching brace for it (for example '}'
12221309
// 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
@@ -341,6 +341,8 @@ JumpLine
341341
Deselect
342342
ClearInfo
343343
None
344+
JumpToOpeningBrace
345+
JumpToClosingBrace
344346
```
345347

346348
The `StartOfTextToggle` and `SelectToStartOfTextToggle` actions toggle between
@@ -604,6 +606,8 @@ conventions for text editing defaults.
604606
"Ctrl-u": "ToggleMacro",
605607
"Ctrl-j": "PlayMacro",
606608
"Insert": "ToggleOverwriteMode",
609+
"Alt-<": "JumpToOpeningBrace",
610+
"Alt->": "JumpToClosingBrace",
607611

608612
// Emacs-style keybindings
609613
"Alt-f": "WordRight",

0 commit comments

Comments
 (0)