mirror of
https://github.com/zyedidia/micro.git
synced 2026-03-17 14:27:12 +09:00
Check if the file being edited has been modified by an external program
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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])
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user