Handle same file open in multiple buffers

This commit is contained in:
Zachary Yedidia 2019-01-14 00:18:49 -05:00
parent a5e7122b30
commit 8fa34f23d8
10 changed files with 114 additions and 95 deletions

View file

@ -1094,7 +1094,12 @@ func (h *BufHandler) Unsplit() bool {
// NextSplit changes the view to the next split
func (h *BufHandler) NextSplit() bool {
a := MainTab().active
a = util.Clamp(a+1, 0, len(MainTab().Panes))
if a < len(MainTab().Panes)-1 {
a++
} else {
a = 0
}
MainTab().SetActive(a)
return false
@ -1103,7 +1108,11 @@ func (h *BufHandler) NextSplit() bool {
// PreviousSplit changes the view to the previous split
func (h *BufHandler) PreviousSplit() bool {
a := MainTab().active
a = util.Clamp(a-1, 0, len(MainTab().Panes))
if a > 0 {
a--
} else {
a = len(MainTab().Panes) - 1
}
MainTab().SetActive(a)
return false

View file

@ -57,8 +57,7 @@ type BufHandler struct {
Buf *buffer.Buffer
cursors []*buffer.Cursor
Cursor *buffer.Cursor // the active cursor
Cursor *buffer.Cursor // the active cursor
StartLine int // Vertical scrolling
StartCol int // Horizontal scrolling
@ -104,11 +103,9 @@ func NewBufHandler(buf *buffer.Buffer, win display.Window) *BufHandler {
h.Buf = buf
h.Window = win
h.cursors = []*buffer.Cursor{buffer.NewCursor(buf, buf.StartCursor)}
h.Cursor = h.cursors[0]
h.Cursor = h.Buf.GetActiveCursor()
h.mouseReleased = true
buf.SetCursors(h.cursors)
return h
}

View file

@ -60,11 +60,8 @@ type Buffer struct {
// Name of the buffer on the status line
name string
// Whether or not the buffer has been modified since it was opened
isModified bool
// Stores the last modification time of the file the buffer is pointing to
ModTime time.Time
ModTime *time.Time
SyntaxDef *highlight.Def
Highlighter *highlight.Highlighter
@ -135,15 +132,31 @@ func NewBuffer(reader io.Reader, size int64, path string, cursorPosition []strin
}
config.InitLocalSettings(b.Settings, b.Path)
b.LineArray = NewLineArray(uint64(size), FFAuto, reader)
found := false
for _, buf := range OpenBuffers {
if buf.AbsPath == absPath {
found = true
b.LineArray = buf.LineArray
b.EventHandler = buf.EventHandler
b.ModTime = buf.ModTime
b.isModified = buf.isModified
}
}
if !found {
b.LineArray = NewLineArray(uint64(size), FFAuto, reader)
b.EventHandler = NewEventHandler(b.LineArray, b.cursors)
b.ModTime = new(time.Time)
b.isModified = new(bool)
*b.isModified = false
*b.ModTime = time.Time{}
}
b.Path = path
b.AbsPath = absPath
// The last time this file was modified
b.ModTime, _ = GetModTime(b.Path)
b.EventHandler = NewEventHandler(b)
*b.ModTime, _ = GetModTime(b.Path)
b.UpdateRules()
@ -164,6 +177,8 @@ func NewBuffer(reader io.Reader, size int64, path string, cursorPosition []strin
}
}
b.AddCursor(NewCursor(b, b.StartCursor))
if !b.Settings["fastdirty"].(bool) {
if size > LargeFileThreshold {
// If the file is larger than LargeFileThreshold fastdirty needs to be on
@ -217,38 +232,14 @@ func (b *Buffer) ReOpen() error {
}
b.EventHandler.ApplyDiff(txt)
b.ModTime, err = GetModTime(b.Path)
b.isModified = false
*b.ModTime, err = GetModTime(b.Path)
*b.isModified = false
for _, c := range b.cursors {
c.Relocate()
}
return err
}
// LineBytes returns line n as an array of bytes
func (b *Buffer) LineBytes(n int) []byte {
if n >= len(b.lines) || n < 0 {
return []byte{}
}
return b.lines[n].data
}
// LinesNum returns the number of lines in the buffer
func (b *Buffer) LinesNum() int {
return len(b.lines)
}
// Start returns the start of the buffer
func (b *Buffer) Start() Loc {
return Loc{0, 0}
}
// End returns the location of the last character in the buffer
func (b *Buffer) End() Loc {
numlines := len(b.lines)
return Loc{utf8.RuneCount(b.lines[numlines-1].data), numlines - 1}
}
// RuneAt returns the rune at a given location in the buffer
func (b *Buffer) RuneAt(loc Loc) rune {
line := b.LineBytes(loc.Y)
@ -271,7 +262,7 @@ func (b *Buffer) RuneAt(loc Loc) rune {
// being opened
func (b *Buffer) Modified() bool {
if b.Settings["fastdirty"].(bool) {
return b.isModified
return *b.isModified
}
var buff [md5.Size]byte
@ -314,20 +305,6 @@ func calcHash(b *Buffer, out *[md5.Size]byte) error {
return nil
}
func (b *Buffer) insert(pos Loc, value []byte) {
b.isModified = true
b.LineArray.insert(pos, value)
}
func (b *Buffer) remove(start, end Loc) []byte {
b.isModified = true
sub := b.LineArray.remove(start, end)
return sub
}
func (b *Buffer) deleteToEnd(start Loc) {
b.isModified = true
b.LineArray.deleteToEnd(start)
}
// UpdateRules updates the syntax rules and filetype for this buffer
// This is called when the colorscheme changes
func (b *Buffer) UpdateRules() {
@ -422,6 +399,7 @@ func (b *Buffer) SetCursors(c []*Cursor) {
// AddCursor adds a new cursor to the list
func (b *Buffer) AddCursor(c *Cursor) {
b.cursors = append(b.cursors, c)
b.EventHandler.cursors = b.cursors
b.UpdateCursors()
}

View file

@ -37,7 +37,7 @@ type Delta struct {
}
// ExecuteTextEvent runs a text event
func ExecuteTextEvent(t *TextEvent, buf *Buffer) {
func ExecuteTextEvent(t *TextEvent, buf *LineArray) {
if t.EventType == TextEventInsert {
for _, d := range t.Deltas {
buf.insert(d.Start, d.Text)
@ -60,24 +60,26 @@ func ExecuteTextEvent(t *TextEvent, buf *Buffer) {
}
// UndoTextEvent undoes a text event
func UndoTextEvent(t *TextEvent, buf *Buffer) {
func UndoTextEvent(t *TextEvent, buf *LineArray) {
t.EventType = -t.EventType
ExecuteTextEvent(t, buf)
}
// EventHandler executes text manipulations and allows undoing and redoing
type EventHandler struct {
buf *Buffer
buf *LineArray
cursors []*Cursor
UndoStack *TEStack
RedoStack *TEStack
}
// NewEventHandler returns a new EventHandler
func NewEventHandler(buf *Buffer) *EventHandler {
func NewEventHandler(la *LineArray, cursors []*Cursor) *EventHandler {
eh := new(EventHandler)
eh.UndoStack = new(TEStack)
eh.RedoStack = new(TEStack)
eh.buf = buf
eh.buf = la
eh.cursors = cursors
return eh
}
@ -91,12 +93,12 @@ func (eh *EventHandler) ApplyDiff(new string) {
loc := eh.buf.Start()
for _, d := range diff {
if d.Type == dmp.DiffDelete {
eh.Remove(loc, loc.Move(utf8.RuneCountInString(d.Text), eh.buf))
eh.Remove(loc, loc.MoveLA(utf8.RuneCountInString(d.Text), eh.buf))
} else {
if d.Type == dmp.DiffInsert {
eh.Insert(loc, d.Text)
}
loc = loc.Move(utf8.RuneCountInString(d.Text), eh.buf)
loc = loc.MoveLA(utf8.RuneCountInString(d.Text), eh.buf)
}
}
}
@ -105,21 +107,21 @@ func (eh *EventHandler) ApplyDiff(new string) {
func (eh *EventHandler) Insert(start Loc, textStr string) {
text := []byte(textStr)
e := &TextEvent{
C: *eh.buf.GetActiveCursor(),
C: *eh.cursors[0],
EventType: TextEventInsert,
Deltas: []Delta{{text, start, Loc{0, 0}}},
Time: time.Now(),
}
eh.Execute(e)
e.Deltas[0].End = start.Move(utf8.RuneCount(text), eh.buf)
e.Deltas[0].End = start.MoveLA(utf8.RuneCount(text), eh.buf)
end := e.Deltas[0].End
for _, c := range eh.buf.GetCursors() {
for _, c := range eh.cursors {
move := func(loc Loc) Loc {
if start.Y != end.Y && loc.GreaterThan(start) {
loc.Y += end.Y - start.Y
} else if loc.Y == start.Y && loc.GreaterEqual(start) {
loc = loc.Move(utf8.RuneCount(text), eh.buf)
loc = loc.MoveLA(utf8.RuneCount(text), eh.buf)
}
return loc
}
@ -135,19 +137,19 @@ func (eh *EventHandler) Insert(start Loc, textStr string) {
// Remove creates a remove text event and executes it
func (eh *EventHandler) Remove(start, end Loc) {
e := &TextEvent{
C: *eh.buf.GetActiveCursor(),
C: *eh.cursors[0],
EventType: TextEventRemove,
Deltas: []Delta{{[]byte{}, start, end}},
Time: time.Now(),
}
eh.Execute(e)
for _, c := range eh.buf.GetCursors() {
for _, c := range eh.cursors {
move := func(loc Loc) Loc {
if start.Y != end.Y && loc.GreaterThan(end) {
loc.Y -= end.Y - start.Y
} else if loc.Y == end.Y && loc.GreaterEqual(end) {
loc = loc.Move(-Diff(start, end, eh.buf), eh.buf)
loc = loc.MoveLA(-DiffLA(start, end, eh.buf), eh.buf)
}
return loc
}
@ -163,7 +165,7 @@ func (eh *EventHandler) Remove(start, end Loc) {
// MultipleReplace creates an multiple insertions executes them
func (eh *EventHandler) MultipleReplace(deltas []Delta) {
e := &TextEvent{
C: *eh.buf.GetActiveCursor(),
C: *eh.cursors[0],
EventType: TextEventReplace,
Deltas: deltas,
Time: time.Now(),
@ -239,9 +241,9 @@ func (eh *EventHandler) UndoOneEvent() {
// Set the cursor in the right place
teCursor := t.C
if teCursor.Num >= 0 && teCursor.Num < eh.buf.NumCursors() {
t.C = *eh.buf.GetCursor(teCursor.Num)
eh.buf.GetCursor(teCursor.Num).Goto(teCursor)
if teCursor.Num >= 0 && teCursor.Num < len(eh.cursors) {
t.C = *eh.cursors[teCursor.Num]
eh.cursors[teCursor.Num].Goto(teCursor)
} else {
teCursor.Num = -1
}
@ -286,9 +288,9 @@ func (eh *EventHandler) RedoOneEvent() {
UndoTextEvent(t, eh.buf)
teCursor := t.C
if teCursor.Num >= 0 && teCursor.Num < eh.buf.NumCursors() {
t.C = *eh.buf.GetCursor(teCursor.Num)
eh.buf.GetCursor(teCursor.Num).Goto(teCursor)
if teCursor.Num >= 0 && teCursor.Num < len(eh.cursors) {
t.C = *eh.cursors[teCursor.Num]
eh.cursors[teCursor.Num].Goto(teCursor)
} else {
teCursor.Num = -1
}

View file

@ -52,9 +52,10 @@ type FileFormat byte
// A LineArray simply stores and array of lines and makes it easy to insert
// and delete in it
type LineArray struct {
lines []Line
endings FileFormat
initsize uint64
lines []Line
endings FileFormat
initsize uint64
isModified *bool
}
// Append efficiently appends lines together
@ -161,6 +162,7 @@ func (la *LineArray) newlineBelow(y int) {
// Inserts a byte array at a given location
func (la *LineArray) insert(pos Loc, value []byte) {
*la.isModified = true
x, y := runeToByteIndex(pos.X, la.lines[pos.Y].data), pos.Y
for i := 0; i < len(value); i++ {
if value[i] == '\n' {
@ -201,6 +203,7 @@ func (la *LineArray) split(pos Loc) {
// removes from start to end
func (la *LineArray) remove(start, end Loc) []byte {
*la.isModified = true
sub := la.Substr(start, end)
startX := runeToByteIndex(start.X, la.lines[start.Y].data)
endX := runeToByteIndex(end.X, la.lines[end.Y].data)
@ -219,6 +222,7 @@ func (la *LineArray) remove(start, end Loc) []byte {
// deleteToEnd deletes from the end of a line to the position
func (la *LineArray) deleteToEnd(pos Loc) {
*la.isModified = true
la.lines[pos.Y].data = la.lines[pos.Y].data[:pos.X]
}
@ -258,6 +262,30 @@ func (la *LineArray) Substr(start, end Loc) []byte {
return str
}
// LinesNum returns the number of lines in the buffer
func (la *LineArray) LinesNum() int {
return len(la.lines)
}
// Start returns the start of the buffer
func (la *LineArray) Start() Loc {
return Loc{0, 0}
}
// End returns the location of the last character in the buffer
func (la *LineArray) End() Loc {
numlines := len(la.lines)
return Loc{utf8.RuneCount(la.lines[numlines-1].data), numlines - 1}
}
// LineBytes returns line n as an array of bytes
func (la *LineArray) LineBytes(n int) []byte {
if n >= len(la.lines) || n < 0 {
return []byte{}
}
return la.lines[n].data
}
// State gets the highlight state for the given line number
func (la *LineArray) State(lineN int) highlight.State {
return la.lines[lineN].state

View file

@ -52,7 +52,7 @@ func (l Loc) LessEqual(b Loc) bool {
// The following functions require a buffer to know where newlines are
// Diff returns the distance between two locations
func Diff(a, b Loc, buf *Buffer) int {
func DiffLA(a, b Loc, buf *LineArray) int {
if a.Y == b.Y {
if a.X > b.X {
return a.X - b.X
@ -75,7 +75,7 @@ func Diff(a, b Loc, buf *Buffer) int {
}
// This moves the location one character to the right
func (l Loc) right(buf *Buffer) Loc {
func (l Loc) right(buf *LineArray) Loc {
if l == buf.End() {
return Loc{l.X + 1, l.Y}
}
@ -89,7 +89,7 @@ func (l Loc) right(buf *Buffer) Loc {
}
// This moves the given location one character to the left
func (l Loc) left(buf *Buffer) Loc {
func (l Loc) left(buf *LineArray) Loc {
if l == buf.Start() {
return Loc{l.X - 1, l.Y}
}
@ -104,7 +104,7 @@ func (l Loc) left(buf *Buffer) Loc {
// Move moves the cursor n characters to the left or right
// It moves the cursor left if n is negative
func (l Loc) Move(n int, buf *Buffer) Loc {
func (l Loc) MoveLA(n int, buf *LineArray) Loc {
if n > 0 {
for i := 0; i < n; i++ {
l = l.right(buf)
@ -116,3 +116,10 @@ func (l Loc) Move(n int, buf *Buffer) Loc {
}
return l
}
func (l Loc) Diff(a, b Loc, buf *Buffer) int {
return DiffLA(a, b, buf.LineArray)
}
func (l Loc) Move(n int, buf *Buffer) Loc {
return l.MoveLA(n, buf.LineArray)
}

View file

@ -1,8 +1,6 @@
package buffer
import (
"log"
"github.com/zyedidia/micro/cmd/micro/config"
"github.com/zyedidia/tcell"
)
@ -50,7 +48,6 @@ func (m *Message) Style() tcell.Style {
}
case MTError:
if style, ok := config.Colorscheme["gutter-error"]; ok {
log.Println("Return error")
return style
}
}

View file

@ -73,7 +73,7 @@ func (b *Buffer) SaveAs(filename string) error {
// Update the last time this file was updated after saving
defer func() {
b.ModTime, _ = GetModTime(filename)
*b.ModTime, _ = GetModTime(filename)
}()
// Removes any tilde and replaces with the absolute path to home
@ -146,7 +146,7 @@ func (b *Buffer) SaveAs(filename string) error {
b.Path = filename
absPath, _ := filepath.Abs(filename)
b.AbsPath = absPath
b.isModified = false
*b.isModified = false
return b.Serialize()
}
@ -182,8 +182,8 @@ func (b *Buffer) SaveAsWithSudo(filename string) error {
err := cmd.Wait()
if err == nil {
b.isModified = false
b.ModTime, _ = GetModTime(filename)
*b.isModified = false
*b.ModTime, _ = GetModTime(filename)
return b.Serialize()
}
return err

View file

@ -36,7 +36,7 @@ func (b *Buffer) Serialize() error {
err := gob.NewEncoder(file).Encode(SerializedBuffer{
b.EventHandler,
b.GetActiveCursor().Loc,
b.ModTime,
*b.ModTime,
})
return err
})
@ -61,9 +61,10 @@ func (b *Buffer) Unserialize() error {
if b.Settings["saveundo"].(bool) {
// We should only use last time's eventhandler if the file wasn't modified by someone else in the meantime
if b.ModTime == buffer.ModTime {
if *b.ModTime == buffer.ModTime {
b.EventHandler = buffer.EventHandler
b.EventHandler.buf = b
b.EventHandler.cursors = b.cursors
b.EventHandler.buf = b.LineArray
}
}
}

View file

@ -30,7 +30,7 @@ func (b *Buffer) SetOption(option, value string) error {
} else if option == "filetype" {
b.UpdateRules()
} else if option == "fileformat" {
b.isModified = true
*b.isModified = true
} else if option == "syntax" {
if !nativeValue.(bool) {
b.ClearMatches()