All checks were successful
Run Test Suite / test (push) Successful in 56s
Not sure if this is perfect, but it seems to be working
206 lines
4.4 KiB
Go
206 lines
4.4 KiB
Go
package textobject
|
|
|
|
import (
|
|
"git.gophernest.net/azpect/TextEditor/internal/action"
|
|
"git.gophernest.net/azpect/TextEditor/internal/core"
|
|
)
|
|
|
|
// Word implements text object for words (iw/aw)
|
|
type Word struct{}
|
|
|
|
func (to Word) GetRange(m action.Model, cursor core.Position, modifier string) (core.Position, core.Position, core.MotionType) {
|
|
buf := m.ActiveBuffer()
|
|
line := buf.Line(cursor.Line)
|
|
|
|
// Find word boundaries
|
|
start := findWordStart(line, cursor.Col)
|
|
end := findWordEnd(line, cursor.Col, modifier == "a")
|
|
|
|
// Word object's don't span lines
|
|
start.Line = cursor.Line
|
|
end.Line = cursor.Line
|
|
|
|
return start, end, core.CharwiseInclusive
|
|
}
|
|
|
|
// Word implements text object for WORDs (iW/aW)
|
|
type WORD struct{}
|
|
|
|
func (to WORD) GetRange(m action.Model, cursor core.Position, modifier string) (core.Position, core.Position, core.MotionType) {
|
|
buf := m.ActiveBuffer()
|
|
line := buf.Line(cursor.Line)
|
|
|
|
// Find word boundaries
|
|
start := findWORDStart(line, cursor.Col)
|
|
end := findWORDEnd(line, cursor.Col, modifier == "a")
|
|
|
|
// Word object's don't span lines
|
|
start.Line = cursor.Line
|
|
end.Line = cursor.Line
|
|
|
|
return start, end, core.CharwiseInclusive
|
|
}
|
|
|
|
// isWordChar: Returns true if the character is a word character (alphanumeric
|
|
// or underscore). COPIED FROM internal/motion/word.go
|
|
func isWordChar(c byte) bool {
|
|
return (c >= 'a' && c <= 'z') ||
|
|
(c >= 'A' && c <= 'Z') ||
|
|
(c >= '0' && c <= '9') ||
|
|
c == '_'
|
|
}
|
|
|
|
func findWordStart(line string, col int) core.Position {
|
|
if col >= len(line) || col < 0 {
|
|
return core.Position{Line: 0, Col: 0}
|
|
}
|
|
|
|
curChar := line[col]
|
|
|
|
// Don't start on whitespace (shouldn't happen for text objects)
|
|
if curChar == ' ' || curChar == '\t' {
|
|
return core.Position{Line: 0, Col: col}
|
|
}
|
|
|
|
// Determine if we're on word char or punctuation
|
|
onWordChar := isWordChar(curChar)
|
|
|
|
// Move backwards while in the same character class
|
|
i := col
|
|
for i > 0 {
|
|
prevChar := line[i-1]
|
|
|
|
// Stop at whitespace
|
|
if prevChar == ' ' || prevChar == '\t' {
|
|
break
|
|
}
|
|
|
|
// Stop if character class changes
|
|
if isWordChar(prevChar) != onWordChar {
|
|
break
|
|
}
|
|
|
|
i--
|
|
}
|
|
|
|
return core.Position{Line: 0, Col: i}
|
|
}
|
|
|
|
func findWordEnd(line string, col int, includeWhitespace bool) core.Position {
|
|
if col >= len(line) || col < 0 {
|
|
return core.Position{Line: 0, Col: 0}
|
|
}
|
|
|
|
curChar := line[col]
|
|
|
|
// Don't start on whitespace
|
|
if curChar == ' ' || curChar == '\t' {
|
|
return core.Position{Line: 0, Col: col}
|
|
}
|
|
|
|
// Determine if we're on word char or punctuation
|
|
onWordChar := isWordChar(curChar)
|
|
|
|
// Move forward while in the same character class
|
|
i := col
|
|
for i < len(line) {
|
|
c := line[i]
|
|
|
|
// Stop at whitespace
|
|
if c == ' ' || c == '\t' {
|
|
break
|
|
}
|
|
|
|
// Stop if character class changes
|
|
if isWordChar(c) != onWordChar {
|
|
break
|
|
}
|
|
|
|
i++
|
|
}
|
|
|
|
// i is now one past the end, so back up
|
|
i--
|
|
|
|
// If including whitespace, skip trailing spaces/tabs
|
|
if includeWhitespace {
|
|
i++ // Move forward to whitespace
|
|
for i < len(line) && (line[i] == ' ' || line[i] == '\t') {
|
|
i++
|
|
}
|
|
i-- // Back to last whitespace
|
|
}
|
|
|
|
return core.Position{Line: 0, Col: i}
|
|
}
|
|
|
|
func findWORDStart(line string, col int) core.Position {
|
|
if col >= len(line) || col < 0 {
|
|
return core.Position{Line: 0, Col: 0}
|
|
}
|
|
|
|
curChar := line[col]
|
|
|
|
// Don't start on whitespace (shouldn't happen for text objects)
|
|
if curChar == ' ' || curChar == '\t' {
|
|
return core.Position{Line: 0, Col: col}
|
|
}
|
|
|
|
// For WORD, all non-whitespace is one class
|
|
// Just move backwards until we hit whitespace or start of line
|
|
i := col
|
|
for i > 0 {
|
|
prevChar := line[i-1]
|
|
|
|
// Stop at whitespace
|
|
if prevChar == ' ' || prevChar == '\t' {
|
|
break
|
|
}
|
|
|
|
i--
|
|
}
|
|
|
|
return core.Position{Line: 0, Col: i}
|
|
}
|
|
|
|
func findWORDEnd(line string, col int, includeWhitespace bool) core.Position {
|
|
if col >= len(line) || col < 0 {
|
|
return core.Position{Line: 0, Col: 0}
|
|
}
|
|
|
|
curChar := line[col]
|
|
|
|
// Don't start on whitespace
|
|
if curChar == ' ' || curChar == '\t' {
|
|
return core.Position{Line: 0, Col: col}
|
|
}
|
|
|
|
// For WORD, all non-whitespace is one class
|
|
// Move forward until we hit whitespace or end of line
|
|
i := col
|
|
for i < len(line) {
|
|
c := line[i]
|
|
|
|
// Stop at whitespace
|
|
if c == ' ' || c == '\t' {
|
|
break
|
|
}
|
|
|
|
i++
|
|
}
|
|
|
|
// i is now one past the end, so back up
|
|
i--
|
|
|
|
// If including whitespace, skip trailing spaces/tabs
|
|
if includeWhitespace {
|
|
i++ // Move forward to whitespace
|
|
for i < len(line) && (line[i] == ' ' || line[i] == '\t') {
|
|
i++
|
|
}
|
|
i-- // Back to last whitespace
|
|
}
|
|
|
|
return core.Position{Line: 0, Col: i}
|
|
}
|