Gim/internal/motion/delimiter_utils.go
Hayden Hargreaves 1aa1954d35
All checks were successful
Run Test Suite / test (push) Successful in 42s
feat: implemented the % motion! tested as well
2026-04-09 09:32:25 -07:00

114 lines
1.9 KiB
Go

package motion
import "git.gophernest.net/azpect/TextEditor/internal/core"
// Delimiter matching helpers operate on byte-indexed columns, which matches the
// rest of the editor cursor model.
func isDelimiter(ch byte) bool {
switch ch {
case '{', '}', '[', ']', '(', ')':
return true
default:
return false
}
}
func isOpeningDelimiter(ch byte) bool {
return ch == '{' || ch == '[' || ch == '('
}
func getOppositeDelimiter(ch byte) (byte, bool) {
switch ch {
case '{':
return '}', true
case '}':
return '{', true
case '[':
return ']', true
case ']':
return '[', true
case '(':
return ')', true
case ')':
return '(', true
default:
return 0, false
}
}
func findDelimiterOnLine(line string, startCol int) (int, byte, bool) {
if len(line) == 0 {
return 0, 0, false
}
col := max(0, startCol)
for col < len(line) {
ch := line[col]
if isDelimiter(ch) {
return col, ch, true
}
col++
}
return 0, 0, false
}
func findMatchingForward(buf *core.Buffer, line, col int, startDelim, matchDelim byte) (core.Position, bool) {
depth := 0
for y := line; y < buf.LineCount(); y++ {
text := buf.Line(y)
xStart := 0
if y == line {
xStart = col + 1
}
for x := xStart; x < len(text); x++ {
ch := text[x]
if ch == startDelim {
depth++
continue
}
if ch == matchDelim {
if depth == 0 {
return core.Position{Line: y, Col: x}, true
}
depth--
}
}
}
return core.Position{}, false
}
func findMatchingBackward(buf *core.Buffer, line, col int, startDelim, matchDelim byte) (core.Position, bool) {
depth := 0
for y := line; y >= 0; y-- {
text := buf.Line(y)
xStart := len(text) - 1
if y == line {
xStart = col - 1
}
for x := xStart; x >= 0; x-- {
ch := text[x]
if ch == startDelim {
depth++
continue
}
if ch == matchDelim {
if depth == 0 {
return core.Position{Line: y, Col: x}, true
}
depth--
}
}
}
return core.Position{}, false
}