Handle same file open in multiple buffers
This commit is contained in:
parent
a5e7122b30
commit
8fa34f23d8
10 changed files with 114 additions and 95 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
Loading…
Reference in a new issue