Backup support

This commit is contained in:
Zachary Yedidia 2019-12-21 19:55:23 -05:00
parent a86a6c464e
commit e42cf3663b
3 changed files with 116 additions and 2 deletions

View File

@ -2,19 +2,51 @@ package buffer
import (
"io"
"log"
"os"
"time"
"github.com/zyedidia/micro/internal/config"
"github.com/zyedidia/micro/internal/util"
"golang.org/x/text/encoding"
)
const backupMsg = `A backup was detected for this file. This likely means that micro
crashed while editing this file, or another instance of micro is currently
editing this file.
The backup was created at %s.
* 'recover' will apply the backup as unsaved changes to the current buffer.
When the buffer is closed, the backup will be removed.
* 'ignore' will ignore the backup, discarding its changes. The backup file
will be removed.
Options: [r]ecover, [i]gnore: `
// Backup saves the current buffer to ConfigDir/backups
func (b *Buffer) Backup() error {
if !b.Settings["backup"].(bool) {
return nil
}
name := config.ConfigDir + "/backups" + util.EscapePath(b.AbsPath)
sub := time.Now().Sub(b.lastbackup)
if sub < time.Duration(backup_time)*time.Millisecond {
log.Println("Backup event but not enough time has passed", sub)
return nil
}
b.lastbackup = time.Now()
backupdir := config.ConfigDir + "/backups/"
if _, err := os.Stat(backupdir); os.IsNotExist(err) {
os.Mkdir(backupdir, os.ModePerm)
log.Println("Creating backup dir")
}
name := backupdir + util.EscapePath(b.AbsPath)
log.Println("Backing up to", name)
err := overwriteFile(name, encoding.Nop, func(file io.Writer) (e error) {
if len(b.lines) == 0 {
@ -43,6 +75,15 @@ func (b *Buffer) Backup() error {
return err
}
// RemoveBackup removes any backup file associated with this buffer
func (b *Buffer) RemoveBackup() {
if !b.Settings["backup"].(bool) {
return
}
f := config.ConfigDir + "/backups/" + util.EscapePath(b.AbsPath)
os.Remove(f)
}
// ApplyBackup applies the corresponding backup file to this buffer (if one exists)
func (b *Buffer) ApplyBackup() error {
return nil

View File

@ -4,8 +4,10 @@ import (
"bytes"
"crypto/md5"
"errors"
"fmt"
"io"
"io/ioutil"
"log"
"os"
"path/filepath"
"strconv"
@ -25,6 +27,8 @@ import (
"golang.org/x/text/transform"
)
const backup_time = 8000
var (
OpenBuffers []*Buffer
LogBuf *Buffer
@ -109,6 +113,10 @@ type Buffer struct {
CurSuggestion int
Messages []*Message
// counts the number of edits
// resets every backup_time edits
lastbackup time.Time
}
// NewBufferFromFile opens a new buffer using the given path
@ -191,9 +199,36 @@ func NewBuffer(r io.Reader, size int64, path string, startcursor Loc, btype BufT
}
if !found {
choice := 1 // ignore by default
b.SharedBuffer = new(SharedBuffer)
b.Type = btype
b.LineArray = NewLineArray(uint64(size), FFAuto, reader)
if b.Settings["backup"].(bool) {
backupfile := config.ConfigDir + "/backups/" + EscapePath(absPath)
if info, err := os.Stat(backupfile); err == nil {
backup, err := os.Open(backupfile)
if err == nil {
t := info.ModTime()
msg := fmt.Sprintf(backupMsg, t.Format("Mon Jan _2 15:04 2006"))
choice = screen.TermPrompt(msg, []string{"r", "i", "recover", "ignore"}, true)
log.Println("Choice:", choice)
if choice%2 == 0 {
// recover
b.LineArray = NewLineArray(uint64(size), FFAuto, backup)
} else if choice%2 == 1 {
// delete
os.Remove(backupfile)
}
backup.Close()
}
}
}
if choice > 0 {
b.LineArray = NewLineArray(uint64(size), FFAuto, reader)
}
b.EventHandler = NewEventHandler(b.SharedBuffer, b.cursors)
}
@ -273,6 +308,7 @@ func (b *Buffer) Fini() {
if !b.Modified() {
b.Serialize()
}
b.RemoveBackup()
}
// GetName returns the name that should be displayed in the statusline
@ -297,6 +333,8 @@ func (b *Buffer) Insert(start Loc, text string) {
b.EventHandler.cursors = b.cursors
b.EventHandler.active = b.curCursor
b.EventHandler.Insert(start, text)
go b.Backup()
}
}
@ -305,6 +343,8 @@ func (b *Buffer) Remove(start, end Loc) {
b.EventHandler.cursors = b.cursors
b.EventHandler.active = b.curCursor
b.EventHandler.Remove(start, end)
go b.Backup()
}
}

View File

@ -5,6 +5,7 @@ import (
"fmt"
"os"
"strconv"
"strings"
)
// TermMessage sends a message to the user in the terminal. This usually occurs before
@ -25,6 +26,38 @@ func TermMessage(msg ...interface{}) {
TempStart(screenb)
}
// TermPrompt prints a prompt and requests the user for a response
// The result is matched against a list of options and the index of
// the match is returned
// If wait is true, the prompt re-prompts until a valid option is
// chosen, otherwise if wait is false, -1 is returned for no match
func TermPrompt(prompt string, options []string, wait bool) int {
screenb := TempFini()
idx := -1
// same behavior as do { ... } while (wait && idx == -1)
for ok := true; ok; ok = wait && idx == -1 {
reader := bufio.NewReader(os.Stdin)
fmt.Print(prompt)
resp, _ := reader.ReadString('\n')
resp = strings.TrimSpace(resp)
for i, opt := range options {
if resp == opt {
idx = i
}
}
if wait && idx == -1 {
fmt.Println("\nInvalid choice.")
}
}
TempStart(screenb)
return idx
}
// TermError sends an error to the user in the terminal. Like TermMessage except formatted
// as an error
func TermError(filename string, lineNum int, err string) {