Handle same file open in multiple buffers

This commit is contained in:
Zachary Yedidia
2019-01-14 00:18:49 -05:00
parent 7a20c2db76
commit 23926af9f7
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 // NextSplit changes the view to the next split
func (h *BufHandler) NextSplit() bool { func (h *BufHandler) NextSplit() bool {
a := MainTab().active 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) MainTab().SetActive(a)
return false return false
@@ -1103,7 +1108,11 @@ func (h *BufHandler) NextSplit() bool {
// PreviousSplit changes the view to the previous split // PreviousSplit changes the view to the previous split
func (h *BufHandler) PreviousSplit() bool { func (h *BufHandler) PreviousSplit() bool {
a := MainTab().active 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) MainTab().SetActive(a)
return false return false

View File

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

View File

@@ -60,11 +60,8 @@ type Buffer struct {
// Name of the buffer on the status line // Name of the buffer on the status line
name string 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 // Stores the last modification time of the file the buffer is pointing to
ModTime time.Time ModTime *time.Time
SyntaxDef *highlight.Def SyntaxDef *highlight.Def
Highlighter *highlight.Highlighter Highlighter *highlight.Highlighter
@@ -135,15 +132,31 @@ func NewBuffer(reader io.Reader, size int64, path string, cursorPosition []strin
} }
config.InitLocalSettings(b.Settings, b.Path) config.InitLocalSettings(b.Settings, b.Path)
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.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.Path = path
b.AbsPath = absPath b.AbsPath = absPath
// The last time this file was modified // The last time this file was modified
b.ModTime, _ = GetModTime(b.Path) *b.ModTime, _ = GetModTime(b.Path)
b.EventHandler = NewEventHandler(b)
b.UpdateRules() 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 !b.Settings["fastdirty"].(bool) {
if size > LargeFileThreshold { if size > LargeFileThreshold {
// If the file is larger than LargeFileThreshold fastdirty needs to be on // 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.EventHandler.ApplyDiff(txt)
b.ModTime, err = GetModTime(b.Path) *b.ModTime, err = GetModTime(b.Path)
b.isModified = false *b.isModified = false
for _, c := range b.cursors { for _, c := range b.cursors {
c.Relocate() c.Relocate()
} }
return err 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 // RuneAt returns the rune at a given location in the buffer
func (b *Buffer) RuneAt(loc Loc) rune { func (b *Buffer) RuneAt(loc Loc) rune {
line := b.LineBytes(loc.Y) line := b.LineBytes(loc.Y)
@@ -271,7 +262,7 @@ func (b *Buffer) RuneAt(loc Loc) rune {
// being opened // being opened
func (b *Buffer) Modified() bool { func (b *Buffer) Modified() bool {
if b.Settings["fastdirty"].(bool) { if b.Settings["fastdirty"].(bool) {
return b.isModified return *b.isModified
} }
var buff [md5.Size]byte var buff [md5.Size]byte
@@ -314,20 +305,6 @@ func calcHash(b *Buffer, out *[md5.Size]byte) error {
return nil 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 // UpdateRules updates the syntax rules and filetype for this buffer
// This is called when the colorscheme changes // This is called when the colorscheme changes
func (b *Buffer) UpdateRules() { func (b *Buffer) UpdateRules() {
@@ -422,6 +399,7 @@ func (b *Buffer) SetCursors(c []*Cursor) {
// AddCursor adds a new cursor to the list // AddCursor adds a new cursor to the list
func (b *Buffer) AddCursor(c *Cursor) { func (b *Buffer) AddCursor(c *Cursor) {
b.cursors = append(b.cursors, c) b.cursors = append(b.cursors, c)
b.EventHandler.cursors = b.cursors
b.UpdateCursors() b.UpdateCursors()
} }

View File

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

View File

@@ -55,6 +55,7 @@ type LineArray struct {
lines []Line lines []Line
endings FileFormat endings FileFormat
initsize uint64 initsize uint64
isModified *bool
} }
// Append efficiently appends lines together // Append efficiently appends lines together
@@ -161,6 +162,7 @@ func (la *LineArray) newlineBelow(y int) {
// Inserts a byte array at a given location // Inserts a byte array at a given location
func (la *LineArray) insert(pos Loc, value []byte) { func (la *LineArray) insert(pos Loc, value []byte) {
*la.isModified = true
x, y := runeToByteIndex(pos.X, la.lines[pos.Y].data), pos.Y x, y := runeToByteIndex(pos.X, la.lines[pos.Y].data), pos.Y
for i := 0; i < len(value); i++ { for i := 0; i < len(value); i++ {
if value[i] == '\n' { if value[i] == '\n' {
@@ -201,6 +203,7 @@ func (la *LineArray) split(pos Loc) {
// removes from start to end // removes from start to end
func (la *LineArray) remove(start, end Loc) []byte { func (la *LineArray) remove(start, end Loc) []byte {
*la.isModified = true
sub := la.Substr(start, end) sub := la.Substr(start, end)
startX := runeToByteIndex(start.X, la.lines[start.Y].data) startX := runeToByteIndex(start.X, la.lines[start.Y].data)
endX := runeToByteIndex(end.X, la.lines[end.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 // deleteToEnd deletes from the end of a line to the position
func (la *LineArray) deleteToEnd(pos Loc) { func (la *LineArray) deleteToEnd(pos Loc) {
*la.isModified = true
la.lines[pos.Y].data = la.lines[pos.Y].data[:pos.X] 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 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 // State gets the highlight state for the given line number
func (la *LineArray) State(lineN int) highlight.State { func (la *LineArray) State(lineN int) highlight.State {
return la.lines[lineN].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 // The following functions require a buffer to know where newlines are
// Diff returns the distance between two locations // 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.Y == b.Y {
if a.X > b.X { if a.X > b.X {
return 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 // 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() { if l == buf.End() {
return Loc{l.X + 1, l.Y} 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 // 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() { if l == buf.Start() {
return Loc{l.X - 1, l.Y} 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 // Move moves the cursor n characters to the left or right
// It moves the cursor left if n is negative // 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 { if n > 0 {
for i := 0; i < n; i++ { for i := 0; i < n; i++ {
l = l.right(buf) l = l.right(buf)
@@ -116,3 +116,10 @@ func (l Loc) Move(n int, buf *Buffer) Loc {
} }
return l 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 package buffer
import ( import (
"log"
"github.com/zyedidia/micro/cmd/micro/config" "github.com/zyedidia/micro/cmd/micro/config"
"github.com/zyedidia/tcell" "github.com/zyedidia/tcell"
) )
@@ -50,7 +48,6 @@ func (m *Message) Style() tcell.Style {
} }
case MTError: case MTError:
if style, ok := config.Colorscheme["gutter-error"]; ok { if style, ok := config.Colorscheme["gutter-error"]; ok {
log.Println("Return error")
return style 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 // Update the last time this file was updated after saving
defer func() { defer func() {
b.ModTime, _ = GetModTime(filename) *b.ModTime, _ = GetModTime(filename)
}() }()
// Removes any tilde and replaces with the absolute path to home // 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 b.Path = filename
absPath, _ := filepath.Abs(filename) absPath, _ := filepath.Abs(filename)
b.AbsPath = absPath b.AbsPath = absPath
b.isModified = false *b.isModified = false
return b.Serialize() return b.Serialize()
} }
@@ -182,8 +182,8 @@ func (b *Buffer) SaveAsWithSudo(filename string) error {
err := cmd.Wait() err := cmd.Wait()
if err == nil { if err == nil {
b.isModified = false *b.isModified = false
b.ModTime, _ = GetModTime(filename) *b.ModTime, _ = GetModTime(filename)
return b.Serialize() return b.Serialize()
} }
return err return err

View File

@@ -36,7 +36,7 @@ func (b *Buffer) Serialize() error {
err := gob.NewEncoder(file).Encode(SerializedBuffer{ err := gob.NewEncoder(file).Encode(SerializedBuffer{
b.EventHandler, b.EventHandler,
b.GetActiveCursor().Loc, b.GetActiveCursor().Loc,
b.ModTime, *b.ModTime,
}) })
return err return err
}) })
@@ -61,9 +61,10 @@ func (b *Buffer) Unserialize() error {
if b.Settings["saveundo"].(bool) { 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 // 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 = 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" { } else if option == "filetype" {
b.UpdateRules() b.UpdateRules()
} else if option == "fileformat" { } else if option == "fileformat" {
b.isModified = true *b.isModified = true
} else if option == "syntax" { } else if option == "syntax" {
if !nativeValue.(bool) { if !nativeValue.(bool) {
b.ClearMatches() b.ClearMatches()