Skip to content

Commit fa9283f

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, Allowing matchbraceleft for finding open and close bracket actions
1 parent ec8bb7c commit fa9283f

File tree

7 files changed

+191
-54
lines changed

7 files changed

+191
-54
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: 155 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1173,86 +1173,187 @@ 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
1176+
func (b *Buffer) resolveBraceStartLoc(start Loc) Loc {
1177+
var onbrace bool = false
1178+
// TODO: maybe can be more efficient with utf8 package
1179+
curLine := []rune(string(b.LineBytes(start.Y)))
1180+
// See if we are on any brace
1181+
if start.X >= 0 && start.X < len(curLine) {
1182+
startChar := curLine[start.X]
1183+
for _, bp := range BracePairs {
1184+
if startChar == bp[0] || startChar == bp[1] {
1185+
return start
11841186
}
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-
}
1187+
}
1188+
}
1189+
1190+
if !onbrace && b.Settings["matchbraceleft"].(bool) {
1191+
if start.X-1 >= 0 && start.X-1 < len(curLine) {
1192+
startChar := curLine[start.X-1]
1193+
for _, bp := range BracePairs {
1194+
if startChar == bp[0] || startChar == bp[1] {
1195+
return Loc{start.X - 1, start.Y}
11941196
}
11951197
}
11961198
}
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
1199+
}
1200+
return start
1201+
}
1202+
1203+
func (b *Buffer) findOpeningBrace(braceType [2]rune, start Loc) (Loc, bool) {
1204+
// Bound guard
1205+
start = clamp(start, b.LineArray)
1206+
if len(b.lines) == 0 {
1207+
return start, false
1208+
}
1209+
1210+
i := 1
1211+
// If we are on a closing brace, let the counter be incremented below when we traverse
1212+
curLine := []rune(string(b.LineBytes(start.Y)))
1213+
if start.X >= 0 && start.X < len(curLine) {
1214+
startChar := curLine[start.X]
1215+
if startChar == braceType[1] {
1216+
i = 0
1217+
}
1218+
}
1219+
1220+
for y := start.Y; y >= 0; y-- {
1221+
l := []rune(string(b.lines[y].data))
1222+
xInit := len(l) - 1
1223+
if y == start.Y && start.X < len(curLine) {
1224+
xInit = start.X
1225+
}
1226+
for x := xInit; x >= 0; x-- {
1227+
r := l[x]
1228+
if r == braceType[1] {
1229+
i++
1230+
} else if r == braceType[0] {
1231+
i--
1232+
if i == 0 {
1233+
return Loc{x, y}, true
1234+
}
12031235
}
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-
}
1236+
}
1237+
}
1238+
return start, false
1239+
}
1240+
1241+
// Returns the opening brace in current brace scope starting at the start location and a boolean
1242+
// indicating if an opening brace is found
1243+
func (b *Buffer) FindOpeningBrace(start Loc) (Loc, bool) {
1244+
currentDist := -1
1245+
currentMb := Loc{-1, -1}
1246+
start = b.resolveBraceStartLoc(start)
1247+
for _, bp := range BracePairs {
1248+
mb, found := b.findOpeningBrace(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) findClosingBrace(braceType [2]rune, start Loc) (Loc, bool) {
1266+
// Bound guard
1267+
start = clamp(start, b.LineArray)
1268+
if len(b.lines) == 0 {
1269+
return start, false
1270+
}
1271+
1272+
i := 1
1273+
// If we are on an opening brace, let the counter be incremented below when we traverse
1274+
curLine := []rune(string(b.LineBytes(start.Y)))
1275+
if start.X >= 0 && start.X < len(curLine) {
1276+
startChar := curLine[start.X]
1277+
if startChar == braceType[0] {
1278+
i = 0
1279+
}
1280+
}
1281+
1282+
for y := start.Y; y < b.LinesNum(); y++ {
1283+
l := []rune(string(b.LineBytes(y)))
1284+
xInit := 0
1285+
if y == start.Y && start.X >= 0 {
1286+
xInit = start.X
1287+
}
1288+
for x := xInit; x < len(l); x++ {
1289+
r := l[x]
1290+
if r == braceType[0] {
1291+
i++
1292+
} else if r == braceType[1] {
1293+
i--
1294+
if i == 0 {
1295+
return Loc{x, y}, true
12131296
}
12141297
}
12151298
}
12161299
}
12171300
return start, false
12181301
}
12191302

1303+
// Returns the closing brace in current brace scope starting at the start location and a boolean
1304+
// indicating if an closing brace is found
1305+
func (b *Buffer) FindClosingBrace(start Loc) (Loc, bool) {
1306+
currentDist := -1
1307+
currentMb := Loc{-1, -1}
1308+
start = b.resolveBraceStartLoc(start)
1309+
for _, bp := range BracePairs {
1310+
mb, found := b.findClosingBrace(bp, start)
1311+
if found {
1312+
dist := DiffLA(start, mb, b.LineArray)
1313+
if currentDist < 0 || dist < currentDist {
1314+
currentMb = mb
1315+
currentDist = dist
1316+
}
1317+
}
1318+
}
1319+
1320+
if currentDist == -1 {
1321+
return start, false
1322+
} else {
1323+
return currentMb, true
1324+
}
1325+
}
1326+
1327+
func (b *Buffer) findMatchingBrace(braceType [2]rune, start Loc, char rune) (Loc, bool) {
1328+
if char == braceType[0] {
1329+
return b.findClosingBrace(braceType, start)
1330+
} else if char == braceType[1] {
1331+
return b.findOpeningBrace(braceType, start)
1332+
}
1333+
return start, false
1334+
}
1335+
12201336
// If there is a brace character (for example '{' or ']') at the given start location,
12211337
// FindMatchingBrace returns the location of the matching brace for it (for example '}'
12221338
// or '['). The second returned value is true if there was no matching brace found
12231339
// for given starting location but it was found for the location one character left
12241340
// of it. The third returned value is true if the matching brace was found at all.
12251341
func (b *Buffer) FindMatchingBrace(start Loc) (Loc, bool, bool) {
1226-
// TODO: maybe can be more efficient with utf8 package
1342+
newstart := b.resolveBraceStartLoc(start)
1343+
var left bool = false
1344+
if start != newstart {
1345+
left = true
1346+
start = newstart
1347+
}
12271348
curLine := []rune(string(b.LineBytes(start.Y)))
12281349

1229-
// first try to find matching brace for the given location (it has higher priority)
12301350
if start.X >= 0 && start.X < len(curLine) {
12311351
startChar := curLine[start.X]
1232-
12331352
for _, bp := range BracePairs {
12341353
if startChar == bp[0] || startChar == bp[1] {
12351354
mb, found := b.findMatchingBrace(bp, start, startChar)
12361355
if found {
1237-
return mb, false, true
1238-
}
1239-
}
1240-
}
1241-
}
1242-
1243-
if b.Settings["matchbraceleft"].(bool) {
1244-
// failed to find matching brace for the given location, so try to find matching
1245-
// brace for the location one character left of it
1246-
if start.X-1 >= 0 && start.X-1 < len(curLine) {
1247-
leftChar := curLine[start.X-1]
1248-
left := Loc{start.X - 1, start.Y}
1249-
1250-
for _, bp := range BracePairs {
1251-
if leftChar == bp[0] || leftChar == bp[1] {
1252-
mb, found := b.findMatchingBrace(bp, left, leftChar)
1253-
if found {
1254-
return mb, true, true
1255-
}
1356+
return mb, left, true
12561357
}
12571358
}
12581359
}

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)