lapce/plugin/view.go

183 lines
3.7 KiB
Go

package plugin
import (
"strings"
"unsafe"
)
// Line is
type Line struct {
Text string
length int
}
// LineCache is
type LineCache struct {
NbLines int
ViewID string
Lines []*Line
Raw []byte
}
// View is
type View struct {
Rev uint64
Row int
Col int
Offset int
ID string
Path string
Syntax string
LineCache *LineCache
}
// SetRaw sets
func (v *View) SetRaw(raw []byte) {
l := v.LineCache
l.Raw = raw
lines := []*Line{}
lineRunes := []rune{}
for _, c := range []rune(*(*string)(unsafe.Pointer(&raw))) {
lineRunes = append(lineRunes, c)
if c == '\n' {
line := &Line{
Text: string(lineRunes),
}
line.length = len(line.Text)
lines = append(lines, line)
lineRunes = []rune{}
}
}
if len(lineRunes) > 0 {
line := &Line{
Text: string(lineRunes),
}
line.length = len(line.Text)
lines = append(lines, line)
}
l.Lines = lines
}
// ApplyUpdate applies update
func (v *View) ApplyUpdate(update *Update) (int, int, int, int, string, string, bool) {
l := v.LineCache
v.Rev = update.Rev
i := 0
startCopy := update.Delta.Els[i].Copy
startOffset := 0
if startCopy != nil {
i++
startOffset = startCopy[1]
}
startRow, startCol := v.GetPos(startOffset)
text := ""
deletedText := ""
if i < len(update.Delta.Els) {
text = update.Delta.Els[i].Insert
if text != "" {
i++
}
}
endOffset := len(l.Raw)
if i < len(update.Delta.Els) {
endCopyEl := update.Delta.Els[i]
endOffset = endCopyEl.Copy[0]
}
endRow, endCol := v.GetPos(endOffset)
if startOffset == endOffset && text == "" {
return startRow, startCol, endRow, endCol, text, deletedText, false
}
deletedText = string(l.Raw[startOffset:endOffset])
diff := endOffset - startOffset
if diff < len(text) {
for i := 0; i < len(text)-diff; i++ {
l.Raw = append(l.Raw, 0)
}
}
copy(l.Raw[startOffset+len(text):], l.Raw[endOffset:])
copy(l.Raw[startOffset:startOffset+len(text)], []byte(text))
if diff > len(text) {
l.Raw = l.Raw[:len(l.Raw)-(diff-len(text))]
}
oldN := endRow - startRow
newLines := strings.Split(text, "\n")
for i := range newLines {
if i < len(newLines)-1 {
newLines[i] += "\n"
}
}
newN := len(newLines) - 1
if newN > oldN {
for i := 0; i < newN-oldN; i++ {
l.Lines = append(l.Lines, nil)
}
copy(l.Lines[startRow+newN:], l.Lines[endRow:])
}
for i := 0; i <= newN; i++ {
newText := newLines[i]
idx := startRow + i
if newN == 0 {
startLine := l.Lines[startRow]
endLine := l.Lines[endRow]
l.Lines[idx] = &Line{
Text: string(startLine.Text[:startCol]) + newText + string(endLine.Text[endCol:]),
}
} else if i == 0 {
line := l.Lines[startRow]
l.Lines[idx] = &Line{
Text: string(line.Text[:startCol]) + newText,
}
} else if i == newN {
line := l.Lines[startRow+newN]
l.Lines[idx] = &Line{
Text: newText + string(line.Text[endCol:]),
}
} else {
l.Lines[idx] = &Line{
Text: newText,
}
}
l.Lines[idx].length = len(l.Lines[idx].Text)
}
if newN < oldN {
copy(l.Lines[startRow+newN+1:], l.Lines[endRow+1:])
l.Lines = l.Lines[:len(l.Lines)-(oldN-newN)]
}
v.Offset = startOffset + len(text)
v.Row, v.Col = v.GetPos(v.Offset)
return startRow, startCol, endRow, endCol, text, deletedText, true
}
// GetPos gets pos
func (v *View) GetPos(offset int) (int, int) {
row := 0
idx := 0
var line *Line
for row, line = range v.LineCache.Lines {
if idx+line.length > offset {
return row, offset - idx
}
idx += line.length
}
return row, line.length
}
// GetOffset is
func (v *View) GetOffset(row, col int) int {
offset := 0
lines := v.LineCache.Lines
for i := 0; i <= row; i++ {
if i == row {
offset += col
return offset
}
offset += lines[i].length
}
return offset
}