mirror of
https://github.com/zyedidia/micro.git
synced 2025-06-18 23:05:40 -04:00
Backup support
This commit is contained in:
parent
a86a6c464e
commit
e42cf3663b
@ -2,19 +2,51 @@ package buffer
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"io"
|
"io"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/zyedidia/micro/internal/config"
|
"github.com/zyedidia/micro/internal/config"
|
||||||
"github.com/zyedidia/micro/internal/util"
|
"github.com/zyedidia/micro/internal/util"
|
||||||
"golang.org/x/text/encoding"
|
"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
|
// Backup saves the current buffer to ConfigDir/backups
|
||||||
func (b *Buffer) Backup() error {
|
func (b *Buffer) Backup() error {
|
||||||
if !b.Settings["backup"].(bool) {
|
if !b.Settings["backup"].(bool) {
|
||||||
return nil
|
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) {
|
err := overwriteFile(name, encoding.Nop, func(file io.Writer) (e error) {
|
||||||
if len(b.lines) == 0 {
|
if len(b.lines) == 0 {
|
||||||
@ -43,6 +75,15 @@ func (b *Buffer) Backup() error {
|
|||||||
return err
|
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)
|
// ApplyBackup applies the corresponding backup file to this buffer (if one exists)
|
||||||
func (b *Buffer) ApplyBackup() error {
|
func (b *Buffer) ApplyBackup() error {
|
||||||
return nil
|
return nil
|
||||||
|
@ -4,8 +4,10 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"crypto/md5"
|
"crypto/md5"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
@ -25,6 +27,8 @@ import (
|
|||||||
"golang.org/x/text/transform"
|
"golang.org/x/text/transform"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const backup_time = 8000
|
||||||
|
|
||||||
var (
|
var (
|
||||||
OpenBuffers []*Buffer
|
OpenBuffers []*Buffer
|
||||||
LogBuf *Buffer
|
LogBuf *Buffer
|
||||||
@ -109,6 +113,10 @@ type Buffer struct {
|
|||||||
CurSuggestion int
|
CurSuggestion int
|
||||||
|
|
||||||
Messages []*Message
|
Messages []*Message
|
||||||
|
|
||||||
|
// counts the number of edits
|
||||||
|
// resets every backup_time edits
|
||||||
|
lastbackup time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewBufferFromFile opens a new buffer using the given path
|
// 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 {
|
if !found {
|
||||||
|
choice := 1 // ignore by default
|
||||||
|
|
||||||
b.SharedBuffer = new(SharedBuffer)
|
b.SharedBuffer = new(SharedBuffer)
|
||||||
b.Type = btype
|
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)
|
b.EventHandler = NewEventHandler(b.SharedBuffer, b.cursors)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -273,6 +308,7 @@ func (b *Buffer) Fini() {
|
|||||||
if !b.Modified() {
|
if !b.Modified() {
|
||||||
b.Serialize()
|
b.Serialize()
|
||||||
}
|
}
|
||||||
|
b.RemoveBackup()
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetName returns the name that should be displayed in the statusline
|
// 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.cursors = b.cursors
|
||||||
b.EventHandler.active = b.curCursor
|
b.EventHandler.active = b.curCursor
|
||||||
b.EventHandler.Insert(start, text)
|
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.cursors = b.cursors
|
||||||
b.EventHandler.active = b.curCursor
|
b.EventHandler.active = b.curCursor
|
||||||
b.EventHandler.Remove(start, end)
|
b.EventHandler.Remove(start, end)
|
||||||
|
|
||||||
|
go b.Backup()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TermMessage sends a message to the user in the terminal. This usually occurs before
|
// TermMessage sends a message to the user in the terminal. This usually occurs before
|
||||||
@ -25,6 +26,38 @@ func TermMessage(msg ...interface{}) {
|
|||||||
TempStart(screenb)
|
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
|
// TermError sends an error to the user in the terminal. Like TermMessage except formatted
|
||||||
// as an error
|
// as an error
|
||||||
func TermError(filename string, lineNum int, err string) {
|
func TermError(filename string, lineNum int, err string) {
|
||||||
|
Loading…
Reference in New Issue
Block a user