From 8fa34f23d80f6e21bcd64aa51c1500202381cc34 Mon Sep 17 00:00:00 2001 From: Zachary Yedidia Date: Mon, 14 Jan 2019 00:18:49 -0500 Subject: [PATCH] Handle same file open in multiple buffers --- cmd/micro/action/actions.go | 13 +++++- cmd/micro/action/bufhandler.go | 7 +-- cmd/micro/buffer/buffer.go | 76 ++++++++++++-------------------- cmd/micro/buffer/eventhandler.go | 44 +++++++++--------- cmd/micro/buffer/line_array.go | 34 ++++++++++++-- cmd/micro/buffer/loc.go | 15 +++++-- cmd/micro/buffer/message.go | 3 -- cmd/micro/buffer/save.go | 8 ++-- cmd/micro/buffer/serialize.go | 7 +-- cmd/micro/buffer/settings.go | 2 +- 10 files changed, 114 insertions(+), 95 deletions(-) diff --git a/cmd/micro/action/actions.go b/cmd/micro/action/actions.go index bf803f40..3e57a9ab 100644 --- a/cmd/micro/action/actions.go +++ b/cmd/micro/action/actions.go @@ -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 diff --git a/cmd/micro/action/bufhandler.go b/cmd/micro/action/bufhandler.go index 54d65374..d23a40b6 100644 --- a/cmd/micro/action/bufhandler.go +++ b/cmd/micro/action/bufhandler.go @@ -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 } diff --git a/cmd/micro/buffer/buffer.go b/cmd/micro/buffer/buffer.go index 8ee1a6bb..9fd6a5f3 100644 --- a/cmd/micro/buffer/buffer.go +++ b/cmd/micro/buffer/buffer.go @@ -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() } diff --git a/cmd/micro/buffer/eventhandler.go b/cmd/micro/buffer/eventhandler.go index e6bd37a1..8d88f182 100644 --- a/cmd/micro/buffer/eventhandler.go +++ b/cmd/micro/buffer/eventhandler.go @@ -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 } diff --git a/cmd/micro/buffer/line_array.go b/cmd/micro/buffer/line_array.go index 3a421076..e7e86896 100644 --- a/cmd/micro/buffer/line_array.go +++ b/cmd/micro/buffer/line_array.go @@ -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 diff --git a/cmd/micro/buffer/loc.go b/cmd/micro/buffer/loc.go index ac2d7705..be99610d 100644 --- a/cmd/micro/buffer/loc.go +++ b/cmd/micro/buffer/loc.go @@ -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) +} diff --git a/cmd/micro/buffer/message.go b/cmd/micro/buffer/message.go index b1049971..3b9e3ad4 100644 --- a/cmd/micro/buffer/message.go +++ b/cmd/micro/buffer/message.go @@ -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 } } diff --git a/cmd/micro/buffer/save.go b/cmd/micro/buffer/save.go index fe82a8e0..44872151 100644 --- a/cmd/micro/buffer/save.go +++ b/cmd/micro/buffer/save.go @@ -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 diff --git a/cmd/micro/buffer/serialize.go b/cmd/micro/buffer/serialize.go index a9d0c042..6edd24b5 100644 --- a/cmd/micro/buffer/serialize.go +++ b/cmd/micro/buffer/serialize.go @@ -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 } } } diff --git a/cmd/micro/buffer/settings.go b/cmd/micro/buffer/settings.go index fb3b5486..2894a5b5 100644 --- a/cmd/micro/buffer/settings.go +++ b/cmd/micro/buffer/settings.go @@ -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()