Check if the file being edited has been modified by an external program

This commit is contained in:
Zachary Yedidia
2016-05-29 17:58:06 -04:00
parent ee9f2a3d9c
commit 19717dd3ae
5 changed files with 75 additions and 15 deletions

View File

@@ -6,6 +6,7 @@ import (
"os"
"path/filepath"
"strings"
"time"
"github.com/vinzmay/go-rope"
)
@@ -29,6 +30,9 @@ type Buffer struct {
IsModified bool
// Stores the last modification time of the file the buffer is pointing to
ModTime time.Time
// Provide efficient and easy access to text and lines so the rope String does not
// need to be constantly recalculated
// These variables are updated in the update() function
@@ -45,6 +49,7 @@ type Buffer struct {
type SerializedBuffer struct {
EventHandler *EventHandler
Cursor Cursor
ModTime time.Time
}
// NewBuffer creates a new buffer from `txt` with path and name `path`
@@ -58,6 +63,8 @@ func NewBuffer(txt, path string) *Buffer {
b.Path = path
b.Name = path
b.ModTime, _ = GetModTime(b.Path)
b.EventHandler = NewEventHandler(b)
b.Update()
@@ -88,12 +95,15 @@ func NewBuffer(txt, path string) *Buffer {
if settings["savecursor"].(bool) {
b.Cursor = buffer.Cursor
b.Cursor.buf = b
b.Cursor.Clamp()
b.Cursor.Relocate()
}
if settings["saveundo"].(bool) {
b.EventHandler = buffer.EventHandler
b.EventHandler.buf = b
// We should only use last time's eventhandler if the file wasn't by someone else in the meantime
if b.ModTime == buffer.ModTime {
b.EventHandler = buffer.EventHandler
b.EventHandler.buf = b
}
}
}
file.Close()
@@ -115,6 +125,43 @@ func (b *Buffer) String() string {
return ""
}
// CheckModTime makes sure that the file this buffer points to hasn't been updated
// by an external program since it was last read
// If it has, we ask the user if they would like to reload the file
func (b *Buffer) CheckModTime() {
modTime, ok := GetModTime(b.Path)
if ok {
if modTime != b.ModTime {
choice, canceled := messenger.YesNoPrompt("The file has changed since it was last read. Reload file? (y,n)")
messenger.Reset()
messenger.Clear()
if !choice || canceled {
// Don't load new changes -- do nothing
b.ModTime, _ = GetModTime(b.Path)
} else {
// Load new changes
data, err := ioutil.ReadFile(b.Path)
txt := string(data)
if err != nil {
messenger.Error(err.Error())
return
}
if txt == "" {
b.r = new(rope.Rope)
} else {
b.r = rope.New(txt)
}
b.ModTime, _ = GetModTime(b.Path)
b.Cursor.Relocate()
b.IsModified = false
b.Update()
}
}
}
}
// Update fetches the string from the rope and updates the `text` and `lines` in the buffer
func (b *Buffer) Update() {
b.Lines = strings.Split(b.String(), "\n")
@@ -137,6 +184,7 @@ func (b *Buffer) Serialize() error {
err = enc.Encode(SerializedBuffer{
b.EventHandler,
b.Cursor,
b.ModTime,
})
// err = enc.Encode(b.Cursor)
}
@@ -149,10 +197,13 @@ func (b *Buffer) Serialize() error {
// SaveAs saves the buffer to a specified path (filename), creating the file if it does not exist
func (b *Buffer) SaveAs(filename string) error {
b.UpdateRules()
b.Name = filename
b.Path = filename
data := []byte(b.String())
err := ioutil.WriteFile(filename, data, 0644)
if err == nil {
b.IsModified = false
b.ModTime, _ = GetModTime(filename)
return b.Serialize()
}
return err

View File

@@ -63,17 +63,7 @@ type Cursor struct {
OrigSelection [2]int
}
// Clamp makes sure that the cursor is in the bounds of the buffer
// It cannot be less than 0 or greater than the buffer length
func (c *Cursor) Clamp() {
loc := c.Loc()
if loc < 0 {
c.SetLoc(0)
} else if loc > c.buf.Len() {
c.SetLoc(c.buf.Len())
}
}
// Goto puts the cursor at the given cursor's location and gives the current cursor its selection too
func (c *Cursor) Goto(b Cursor) {
c.X, c.Y, c.LastVisualX = b.X, b.Y, b.LastVisualX
c.OrigSelection, c.CurSelection = b.OrigSelection, b.CurSelection
@@ -331,6 +321,7 @@ func (c *Cursor) Right() {
if c.Loc() == c.buf.Len() {
return
}
// TermMessage(Count(c.buf.Lines[c.Y]))
if c.X < Count(c.buf.Lines[c.Y]) {
c.X++
} else {

View File

@@ -98,9 +98,11 @@ func (m *Messenger) Error(msg ...interface{}) {
func (m *Messenger) YesNoPrompt(prompt string) (bool, bool) {
m.Message(prompt)
_, h := screen.Size()
for {
m.Clear()
m.Display()
screen.ShowCursor(Count(m.message), h-1)
screen.Show()
event := screen.PollEvent()

View File

@@ -1,9 +1,11 @@
package main
import (
"os"
"path/filepath"
"strconv"
"strings"
"time"
"unicode/utf8"
)
@@ -126,6 +128,16 @@ func EscapePath(path string) string {
return strings.Replace(path, "/", "%", -1)
}
// GetModTime returns the last modification time for a given file
// It also returns a boolean if there was a problem accessing the file
func GetModTime(path string) (time.Time, bool) {
info, err := os.Stat(path)
if err != nil {
return time.Now(), false
}
return info.ModTime(), true
}
func runePos(p int, str string) int {
return utf8.RuneCountInString(str[:p])
}

View File

@@ -201,8 +201,10 @@ func (v *View) ReOpen() {
}
buf := NewBuffer(string(file), filename)
v.Buf = buf
v.matches = Match(v)
v.Cursor.Relocate()
buf.Cursor.Goto(*v.Cursor)
v.Cursor = &buf.Cursor
v.matches = Match(v)
v.Relocate()
}
}
@@ -272,6 +274,8 @@ func (v *View) HandleEvent(event tcell.Event) {
// By default it's true because most events should cause a relocate
relocate := true
v.Buf.CheckModTime()
switch e := event.(type) {
case *tcell.EventResize:
// Window resized