mirror of https://github.com/lapce/lapce.git
190 lines
5.2 KiB
Go
190 lines
5.2 KiB
Go
package editor
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
xi "github.com/dzhou121/xi-go/xi-client"
|
|
"github.com/therecipe/qt/widgets"
|
|
)
|
|
|
|
// Buffer is
|
|
type Buffer struct {
|
|
scence *widgets.QGraphicsScene
|
|
scenceLines map[int]*widgets.QGraphicsTextItem
|
|
font *Font
|
|
|
|
nInvalidBefore int
|
|
lines []*Line
|
|
nInvalidAfter int
|
|
revision int
|
|
xiView *xi.View
|
|
}
|
|
|
|
// NewBuffer creates a new buffer
|
|
func NewBuffer(editor *Editor, path string) *Buffer {
|
|
buffer := &Buffer{
|
|
scence: widgets.NewQGraphicsScene(nil),
|
|
scenceLines: map[int]*widgets.QGraphicsTextItem{},
|
|
lines: []*Line{},
|
|
font: NewFont(),
|
|
}
|
|
buffer.xiView, _ = editor.xi.NewView(path)
|
|
buffer.scence.ConnectMousePressEvent(func(event *widgets.QGraphicsSceneMouseEvent) {
|
|
scencePos := event.ScenePos()
|
|
x := scencePos.X()
|
|
y := scencePos.Y()
|
|
row := y / buffer.font.lineHeight
|
|
buffer.xiView.Click(int(row), int(x/buffer.font.width+0.5))
|
|
})
|
|
editor.buffersRWMutex.Lock()
|
|
editor.buffers[buffer.xiView.ID] = buffer
|
|
editor.buffersRWMutex.Unlock()
|
|
return buffer
|
|
}
|
|
|
|
func (b *Buffer) height() int {
|
|
return b.nInvalidBefore + len(b.lines) + b.nInvalidAfter
|
|
}
|
|
|
|
func (b *Buffer) applyUpdate(update *xi.UpdateNotification) {
|
|
inval := [][]int{}
|
|
oldHeight := b.height()
|
|
newInvalidBefore := 0
|
|
newLines := []*Line{}
|
|
newInvalidAfter := 0
|
|
oldIx := 0
|
|
|
|
for _, op := range update.Update.Ops {
|
|
n := op.N
|
|
switch op.Op {
|
|
case "invalidate":
|
|
curLine := newInvalidBefore + len(newLines) + newInvalidAfter
|
|
ix := curLine - b.nInvalidBefore
|
|
if ix+n > 0 && ix < len(b.lines) {
|
|
for i := Max(ix, 0); i < Min(ix+n, len(b.lines)); i++ {
|
|
if b.lines[i] != nil {
|
|
inval = append(inval, []int{i + b.nInvalidBefore, i + b.nInvalidBefore + 1})
|
|
}
|
|
}
|
|
}
|
|
if len(newLines) == 0 {
|
|
newInvalidBefore += n
|
|
} else {
|
|
newInvalidAfter += n
|
|
}
|
|
case "ins":
|
|
for i := 0; i < newInvalidAfter; i++ {
|
|
newLines = append(newLines, nil)
|
|
}
|
|
newInvalidAfter = 0
|
|
inval = append(inval, []int{newInvalidBefore + len(newLines), newInvalidBefore + len(newLines) + n})
|
|
for _, line := range op.Lines {
|
|
newLines = append(newLines, &Line{
|
|
text: line.Text,
|
|
})
|
|
}
|
|
case "copy", "update":
|
|
n := op.N
|
|
nRemaining := n
|
|
if oldIx < b.nInvalidBefore {
|
|
nInvalid := Min(n, b.nInvalidBefore-oldIx)
|
|
if len(newLines) == 0 {
|
|
newInvalidBefore += nInvalid
|
|
} else {
|
|
newInvalidAfter += nInvalid
|
|
}
|
|
oldIx += nInvalid
|
|
nRemaining -= nInvalid
|
|
}
|
|
if nRemaining > 0 && oldIx < b.nInvalidBefore+len(b.lines) {
|
|
for i := 0; i < newInvalidAfter; i++ {
|
|
newLines = append(newLines, nil)
|
|
}
|
|
newInvalidAfter = 0
|
|
nCopy := Min(nRemaining, b.nInvalidBefore+len(b.lines)-oldIx)
|
|
if oldIx != newInvalidBefore+len(newLines) || op.Op != "copy" {
|
|
inval = append(inval, []int{newInvalidBefore + len(newLines), newInvalidBefore + len(newLines) + nCopy})
|
|
}
|
|
startIx := oldIx - b.nInvalidBefore
|
|
if op.Op == "copy" {
|
|
newLines = append(newLines, b.lines[startIx:startIx+nCopy]...)
|
|
} else {
|
|
for ix := startIx; ix < startIx+nCopy; ix++ {
|
|
newLines = append(newLines, b.lines[ix])
|
|
}
|
|
}
|
|
oldIx += nCopy
|
|
nRemaining -= nCopy
|
|
}
|
|
if len(newLines) == 0 {
|
|
newInvalidBefore += nRemaining
|
|
} else {
|
|
newInvalidAfter += nRemaining
|
|
}
|
|
oldIx += nRemaining
|
|
case "skip":
|
|
oldIx += n
|
|
default:
|
|
fmt.Println("unknown op type", op.Op)
|
|
}
|
|
}
|
|
|
|
b.nInvalidBefore = newInvalidBefore
|
|
b.lines = newLines
|
|
b.nInvalidAfter = newInvalidAfter
|
|
b.revision++
|
|
|
|
if b.height() < oldHeight {
|
|
inval = append(inval, []int{b.height(), oldHeight})
|
|
}
|
|
fmt.Println(inval, len(b.lines))
|
|
for _, invalRange := range inval {
|
|
start := invalRange[0]
|
|
end := invalRange[1]
|
|
for i := start; i < end; i++ {
|
|
if i >= len(b.lines) {
|
|
scenceLine := b.getScenceLine(i)
|
|
b.scence.RemoveItem(scenceLine)
|
|
delete(b.scenceLines, i)
|
|
continue
|
|
}
|
|
line := b.lines[i]
|
|
scenceLine := b.getScenceLine(i)
|
|
textCursor := scenceLine.TextCursor()
|
|
if line == nil {
|
|
textCursor.Document().Clear()
|
|
} else {
|
|
textCursor.Document().SetPlainText(line.text)
|
|
// if len(line.text) > 3 {
|
|
// textCursor.SetPosition(1, gui.QTextCursor__MoveAnchor)
|
|
// textCursor.SetPosition(3, gui.QTextCursor__KeepAnchor)
|
|
// charFormat := gui.NewQTextCharFormat()
|
|
// // charFormat.SetBackground(gui.NewQBrush3(gui.NewQColor3(100, 100, 100, 255), core.Qt__SolidPattern))
|
|
// // charFormat.SetFontItalic(true)
|
|
// // charFormat.SetFontUnderline(true)
|
|
// charFormat.SetForeground(gui.NewQBrush3(gui.NewQColor3(100, 100, 100, 255), core.Qt__SolidPattern))
|
|
// textCursor.SetCharFormat(charFormat)
|
|
// // fmt.Println(scenceLine.TextCursor().SelectionStart())
|
|
// // fmt.Println(scenceLine.TextCursor().SelectionEnd())
|
|
// }
|
|
}
|
|
}
|
|
}
|
|
rect := b.scence.ItemsBoundingRect()
|
|
rect.SetLeft(0)
|
|
rect.SetTop(0)
|
|
b.scence.SetSceneRect(rect)
|
|
}
|
|
|
|
func (b *Buffer) getScenceLine(i int) *widgets.QGraphicsTextItem {
|
|
scenceLine, ok := b.scenceLines[i]
|
|
if ok {
|
|
return scenceLine
|
|
}
|
|
scenceLine = b.scence.AddText("", b.font.font)
|
|
scenceLine.SetPos2(0, b.font.lineHeight*float64(i)+b.font.shift)
|
|
scenceLine.Document().SetDocumentMargin(0)
|
|
b.scenceLines[i] = scenceLine
|
|
return scenceLine
|
|
}
|