A lot of changes that I wasn't able to properly commit months ago

This commit is contained in:
Luke Wilson
2021-08-20 18:58:09 -05:00
parent 7074a250a1
commit 2947128857
13 changed files with 621 additions and 6 deletions

10
pkg/buffer/buffer.go Executable file → Normal file
View File

@@ -23,6 +23,16 @@ type Buffer interface {
// so do not write to it.
Slice(startLine, startCol, endLine, endCol int) []byte
// RuneAtPos returns the UTF-8 rune at the byte position `pos` of the buffer. The
// position must be a correct position, otherwise zero is returned.
RuneAtPos(pos int) rune
// EachRuneAtPos executes the function `f` at each rune after byte position `pos`.
// This function should be used as opposed to performing a "per character" operation
// manually, as it enables caching buffer operations and safety checks. The function
// returns when the end of the buffer is met or `f` returns true.
EachRuneAtPos(pos int, f func(pos int, r rune) bool)
// Bytes returns all of the bytes in the buffer. This function is very likely
// to copy all of the data in the buffer. Use sparingly. Try using other methods,
// where possible.

58
pkg/buffer/cursor.go Executable file → Normal file
View File

@@ -1,6 +1,9 @@
package buffer
import "math"
import (
"math"
"unicode"
)
// So why is the code for moving the cursor in the buffer package, and not in the
// TextEdit component? Well, it used to be, but it sucked that way. The cursor
@@ -86,6 +89,41 @@ func (c Cursor) Down() Cursor {
return c
}
// NextWordBoundaryEnd proceeds to the position after the last character of the
// next word boundary to the right of the Cursor. A word boundary is the
// beginning or end of any sequence of similar or same-classed characters.
// Whitespace is skipped.
func (c Cursor) NextWordBoundaryEnd() Cursor {
// Get position of cursor in buffer as pos
// get classification of character at pos or assume none if whitespace
// for each pos until end of buffer: pos + 1 (at end)
// if pos char is not of previous pos char class:
// set cursor position as pos
//
// only skip contiguous characters for word characters
// jump to position *after* any symbols
pos := (*c.buffer).LineColToPos(c.line, c.col)
startClass := getRuneCharclass((*c.buffer).RuneAtPos(pos))
pos++
(*c.buffer).EachRuneAtPos(pos, func(rpos int, r rune) bool {
class := getRuneCharclass(r)
if class != startClass && class != charwhitespace {
return true
}
return false
})
c.line, c.col = (*c.buffer).PosToLineCol(pos)
return c
}
func (c Cursor) PrevWordBoundaryStart() Cursor {
return c
}
func (c Cursor) GetLineCol() (line, col int) {
return c.line, c.col
}
@@ -101,3 +139,21 @@ func (c Cursor) SetLineCol(line, col int) Cursor {
func (c Cursor) Eq(other Cursor) bool {
return c.buffer == other.buffer && c.line == other.line && c.col == other.col
}
type charclass uint8
const (
charwhitespace charclass = iota
charword
charsymbol
)
func getRuneCharclass(r rune) charclass {
if unicode.IsSpace(r) {
return charwhitespace
} else if r == '_' || unicode.IsLetter(r) || unicode.IsDigit(r) {
return charword
} else {
return charsymbol
}
}

0
pkg/buffer/highlighter.go Executable file → Normal file
View File

0
pkg/buffer/language.go Executable file → Normal file
View File

34
pkg/buffer/rope.go Executable file → Normal file
View File

@@ -96,6 +96,40 @@ func (b *RopeBuffer) Slice(startLine, startCol, endLine, endCol int) []byte {
return b.rope.Slice(b.LineColToPos(startLine, startCol), endPos+1)
}
// RuneAtPos returns the UTF-8 rune at the byte position `pos` of the buffer. The
// position must be a correct position, otherwise zero is returned.
func (b *RopeBuffer) RuneAtPos(pos int) (val rune) {
_, r := b.rope.SplitAt(pos)
l, _ := r.SplitAt(b.rope.Len() - pos)
l.EachLeaf(func(n *ropes.Node) bool {
data := n.Value() // Reference; not a copy.
val, _ = utf8.DecodeRune(data[0:])
return true
})
return 0
}
// EachRuneAtPos executes the function `f` at each rune after byte position `pos`.
// This function should be used as opposed to performing a "per character" operation
// manually, as it enables caching buffer operations and safety checks. The function
// returns when the end of the buffer is met or `f` returns true.
func (b *RopeBuffer) EachRuneAtPos(pos int, f func(pos int, r rune) bool) {
_, r := b.rope.SplitAt(pos)
l, _ := r.SplitAt(b.rope.Len() - pos)
l.EachLeaf(func(n *ropes.Node) bool {
data := n.Value() // Reference; not a copy.
for i, r := range string(data) {
if f(pos+i, r) {
return true
}
}
return false
})
}
// Bytes returns all of the bytes in the buffer. This function is very likely
// to copy all of the data in the buffer. Use sparingly. Try using other methods,
// where possible.