Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cmd/micro/initlua.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ func luaImportMicroUtil() *lua.LTable {
ulua.L.SetField(pkg, "SemVersion", luar.New(ulua.L, util.SemVersion))
ulua.L.SetField(pkg, "HttpRequest", luar.New(ulua.L, util.HttpRequest))
ulua.L.SetField(pkg, "CharacterCountInString", luar.New(ulua.L, util.CharacterCountInString))
ulua.L.SetField(pkg, "GetTextLengthAfterLastLinebreak", luar.New(ulua.L, util.GetTextLengthAfterLastLinebreak))
ulua.L.SetField(pkg, "RuneStr", luar.New(ulua.L, func(r rune) string {
return string(r)
}))
Expand Down
6 changes: 3 additions & 3 deletions internal/action/actions.go
Original file line number Diff line number Diff line change
Expand Up @@ -1618,11 +1618,11 @@ func (h *BufPane) paste(clip string) {
}

if h.Cursor.HasSelection() {
h.Cursor.DeleteSelection()
h.Cursor.ResetSelection()
h.Buf.Replace(h.Cursor.CurSelection[0], h.Cursor.CurSelection[1], clip)
} else {
h.Buf.Insert(h.Cursor.Loc, clip)
}

h.Buf.Insert(h.Cursor.Loc, clip)
// h.Cursor.Loc = h.Cursor.Loc.Move(Count(clip), h.Buf)
h.freshClip = false
InfoBar.Message("Pasted clipboard")
Expand Down
14 changes: 9 additions & 5 deletions internal/action/bufpane.go
Original file line number Diff line number Diff line change
Expand Up @@ -627,17 +627,21 @@ func (h *BufPane) DoRuneInsert(r rune) {
if !h.PluginCB("preRune", string(r)) {
continue
}
if c.HasSelection() {
c.DeleteSelection()
c.ResetSelection()
}

if h.Buf.OverwriteMode {
if c.HasSelection() {
c.DeleteSelection()
c.ResetSelection()
}
next := c.Loc
next.X++
h.Buf.Replace(c.Loc, next, string(r))
} else {
h.Buf.Insert(c.Loc, string(r))
if c.HasSelection() {
h.Buf.Replace(c.CurSelection[0], c.CurSelection[1], string(r))
} else {
h.Buf.Insert(c.Loc, string(r))
}
}
if recordingMacro {
curmacro = append(curmacro, r)
Expand Down
17 changes: 13 additions & 4 deletions internal/buffer/buffer.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,13 +123,13 @@ type SharedBuffer struct {
origHash [md5.Size]byte
}

func (b *SharedBuffer) insert(pos Loc, value []byte) {
func (b *SharedBuffer) insert(pos Loc, value []byte) Loc {
b.HasSuggestions = false
b.LineArray.insert(pos, value)
endPos := b.LineArray.insert(pos, value)
b.setModified()

inslines := bytes.Count(value, []byte{'\n'})
b.MarkModified(pos.Y, pos.Y+inslines)
b.MarkModified(pos.Y, endPos.Y)
return endPos
}

func (b *SharedBuffer) remove(start, end Loc) []byte {
Expand Down Expand Up @@ -573,6 +573,15 @@ func (b *Buffer) Remove(start, end Loc) {
}
}

// Replace replaces the characters between the start and end locations with the given text
func (b *Buffer) Replace(start, end Loc, text string) {
if !b.Type.Readonly {
b.EventHandler.cursors = b.cursors
b.EventHandler.active = b.curCursor
b.EventHandler.Replace(start, end, text)
}
}

// FileType returns the buffer's filetype
func (b *Buffer) FileType() string {
return b.Settings["filetype"].(string)
Expand Down
112 changes: 67 additions & 45 deletions internal/buffer/eventhandler.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ type Delta struct {

// DoTextEvent runs a text event
func (eh *EventHandler) DoTextEvent(t *TextEvent, useUndo bool) {
oldl := eh.buf.LinesNum()
oldend := t.Deltas[0].End
oldtext := t.Deltas[0].Text

if useUndo {
eh.Execute(t)
Expand All @@ -55,54 +56,60 @@ func (eh *EventHandler) DoTextEvent(t *TextEvent, useUndo bool) {
return
}

moveCursorInsert := func(loc, start, end Loc, textX int, isMultiLine bool) Loc {
if start.Y != loc.Y && loc.GreaterThan(start) {
loc.Y += end.Y - start.Y
} else if loc.Y == start.Y && loc.GreaterEqual(start) {
loc.Y += end.Y - start.Y
if isMultiLine {
loc.X += textX - start.X
} else {
loc.X += textX
}
}
return loc
}

moveCursorRemove := func(loc, start, end Loc) Loc {
if loc.Y != end.Y && loc.GreaterThan(end) {
loc.Y -= end.Y - start.Y
} else if loc.Y == end.Y && loc.GreaterEqual(end) {
loc = loc.MoveLA(-DiffLA(start, end, eh.buf.LineArray), eh.buf.LineArray)
}
return loc
}

text := t.Deltas[0].Text
start := t.Deltas[0].Start
lastnl := -1
var endX int
end := t.Deltas[0].End
var textX int
var isMultiLine bool
if t.EventType == TextEventInsert {
linecount := eh.buf.LinesNum() - oldl
textcount := util.CharacterCount(text)
lastnl = bytes.LastIndex(text, []byte{'\n'})
if lastnl >= 0 {
endX = util.CharacterCount(text[lastnl+1:])
textX = endX
textX, isMultiLine = util.GetTextLengthAfterLastLinebreak(text)
} else if t.EventType == TextEventReplace {
textX, isMultiLine = util.GetTextLengthAfterLastLinebreak(oldtext)
}

moveCursor := func(loc Loc) Loc {
if t.EventType == TextEventInsert {
return moveCursorInsert(loc, start, end, textX, isMultiLine)
} else if t.EventType == TextEventRemove {
return moveCursorRemove(loc, start, end)
} else {
endX = start.X + textcount
textX = textcount
loc = moveCursorRemove(loc, start, oldend)
return moveCursorInsert(loc, start, end, textX, isMultiLine)
}
t.Deltas[0].End = clamp(Loc{endX, start.Y + linecount}, eh.buf.LineArray)
}
end := t.Deltas[0].End

for _, c := range eh.cursors {
move := func(loc Loc) Loc {
if t.EventType == TextEventInsert {
if start.Y != loc.Y && loc.GreaterThan(start) {
loc.Y += end.Y - start.Y
} else if loc.Y == start.Y && loc.GreaterEqual(start) {
loc.Y += end.Y - start.Y
if lastnl >= 0 {
loc.X += textX - start.X
} else {
loc.X += textX
}
}
return loc
} else {
if loc.Y != end.Y && loc.GreaterThan(end) {
loc.Y -= end.Y - start.Y
} else if loc.Y == end.Y && loc.GreaterEqual(end) {
loc = loc.MoveLA(-DiffLA(start, end, eh.buf.LineArray), eh.buf.LineArray)
}
return loc
}
if c.Num < t.C.Num {
continue
}
c.Loc = move(c.Loc)
c.CurSelection[0] = move(c.CurSelection[0])
c.CurSelection[1] = move(c.CurSelection[1])
c.OrigSelection[0] = move(c.OrigSelection[0])
c.OrigSelection[1] = move(c.OrigSelection[1])
c.Loc = moveCursor(c.Loc)
c.CurSelection[0] = moveCursor(c.CurSelection[0])
c.CurSelection[1] = moveCursor(c.CurSelection[1])
c.OrigSelection[0] = moveCursor(c.OrigSelection[0])
c.OrigSelection[1] = moveCursor(c.OrigSelection[1])
c.Relocate()
c.StoreVisualX()
}
Expand All @@ -115,8 +122,8 @@ func (eh *EventHandler) DoTextEvent(t *TextEvent, useUndo bool) {
// ExecuteTextEvent runs a text event
func ExecuteTextEvent(t *TextEvent, buf *SharedBuffer) {
if t.EventType == TextEventInsert {
for _, d := range t.Deltas {
buf.insert(d.Start, d.Text)
for i, d := range t.Deltas {
t.Deltas[i].End = buf.insert(d.Start, d.Text)
}
} else if t.EventType == TextEventRemove {
for i, d := range t.Deltas {
Expand All @@ -125,9 +132,8 @@ func ExecuteTextEvent(t *TextEvent, buf *SharedBuffer) {
} else if t.EventType == TextEventReplace {
for i, d := range t.Deltas {
t.Deltas[i].Text = buf.remove(d.Start, d.End)
buf.insert(d.Start, d.Text)
t.Deltas[i].Start = d.Start
t.Deltas[i].End = Loc{d.Start.X + util.CharacterCount(d.Text), d.Start.Y}
t.Deltas[i].End = buf.insert(d.Start, d.Text)
}
for i, j := 0, len(t.Deltas)-1; i < j; i, j = i+1, j-1 {
t.Deltas[i], t.Deltas[j] = t.Deltas[j], t.Deltas[i]
Expand Down Expand Up @@ -230,8 +236,14 @@ func (eh *EventHandler) MultipleReplace(deltas []Delta) {

// Replace deletes from start to end and replaces it with the given string
func (eh *EventHandler) Replace(start, end Loc, replace string) {
eh.Remove(start, end)
eh.Insert(start, replace)
text := []byte(replace)
e := &TextEvent{
C: *eh.cursors[eh.active],
EventType: TextEventReplace,
Deltas: []Delta{{text, start, end}},
Time: time.Now(),
}
eh.DoTextEvent(e, true)
}

// Execute a textevent and add it to the undo stack
Expand Down Expand Up @@ -274,6 +286,11 @@ func (eh *EventHandler) Undo() bool {
}

eh.UndoOneEvent()

t = eh.UndoStack.Peek()
if t == nil || t.EventType == TextEventReplace {
break
}
}
return true
}
Expand Down Expand Up @@ -321,6 +338,11 @@ func (eh *EventHandler) Redo() bool {
}

eh.RedoOneEvent()

t = eh.UndoStack.Peek()
if t == nil || t.EventType == TextEventReplace {
break
}
}
return true
}
Expand Down
5 changes: 3 additions & 2 deletions internal/buffer/line_array.go
Original file line number Diff line number Diff line change
Expand Up @@ -200,8 +200,8 @@ func (la *LineArray) newlineBelow(y int) {
}
}

// Inserts a byte array at a given location
func (la *LineArray) insert(pos Loc, value []byte) {
// Inserts a byte array at a given location and returns the location where the insertion ends
func (la *LineArray) insert(pos Loc, value []byte) Loc {
la.lock.Lock()
defer la.lock.Unlock()

Expand All @@ -221,6 +221,7 @@ func (la *LineArray) insert(pos Loc, value []byte) {
la.insertByte(Loc{x, y}, value[i])
x++
}
return Loc{x, y}
}

// InsertByte inserts a byte at a given location
Expand Down
12 changes: 12 additions & 0 deletions internal/util/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -521,6 +521,18 @@ func HasTrailingWhitespace(b []byte) bool {
return IsWhitespace(r)
}

// GetTextLengthAfterLastLinebreak returns the length of the remaining
// characters after the last line break in the given byte array and whether it
// contains line breaks.
// If no line breaks were found, it returns the length of all characters in the byte array.
func GetTextLengthAfterLastLinebreak(text []byte) (int, bool) {
lastnl := bytes.LastIndex(text, []byte{'\n'})
if lastnl >= 0 {
return CharacterCount(text[lastnl+1:]), true
}
return CharacterCount(text), false
}

// IntOpt turns a float64 setting to an int
func IntOpt(opt any) int {
return int(opt.(float64))
Expand Down
Loading