diff --git a/cmd/micro/clean.go b/cmd/micro/clean.go index a4ba076f6..26726ba9b 100644 --- a/cmd/micro/clean.go +++ b/cmd/micro/clean.go @@ -3,6 +3,7 @@ package main import ( "bufio" "encoding/gob" + "errors" "fmt" "os" "path/filepath" @@ -11,6 +12,7 @@ import ( "github.com/zyedidia/micro/v2/internal/buffer" "github.com/zyedidia/micro/v2/internal/config" + "github.com/zyedidia/micro/v2/internal/util" ) func shouldContinue() bool { @@ -42,7 +44,11 @@ func CleanConfig() { settingsFile := filepath.Join(config.ConfigDir, "settings.json") err := config.WriteSettings(settingsFile) if err != nil { - fmt.Println("Error writing settings.json file: " + err.Error()) + if errors.Is(err, util.ErrOverwrite) { + fmt.Println(err.Error()) + } else { + fmt.Println("Error writing settings.json file: " + err.Error()) + } } // detect unused options @@ -80,7 +86,11 @@ func CleanConfig() { err := config.OverwriteSettings(settingsFile) if err != nil { - fmt.Println("Error overwriting settings.json file: " + err.Error()) + if errors.Is(err, util.ErrOverwrite) { + fmt.Println(err.Error()) + } else { + fmt.Println("Error overwriting settings.json file: " + err.Error()) + } } fmt.Println("Removed unused options") diff --git a/internal/action/command.go b/internal/action/command.go index 3b9d3b810..18c83b9bf 100644 --- a/internal/action/command.go +++ b/internal/action/command.go @@ -657,7 +657,16 @@ func SetGlobalOptionNative(option string, nativeValue interface{}) error { delete(b.LocalSettings, option) } - return config.WriteSettings(filepath.Join(config.ConfigDir, "settings.json")) + err := config.WriteSettings(filepath.Join(config.ConfigDir, "settings.json")) + if err != nil { + if errors.Is(err, util.ErrOverwrite) { + screen.TermMessage(err) + err = errors.Unwrap(err) + } + return err + } + + return nil } func SetGlobalOption(option, value string) error { @@ -782,7 +791,11 @@ func (h *BufPane) BindCmd(args []string) { _, err := TryBindKey(parseKeyArg(args[0]), args[1], true) if err != nil { - InfoBar.Error(err) + if errors.Is(err, util.ErrOverwrite) { + screen.TermMessage(err) + } else { + InfoBar.Error(err) + } } } @@ -795,7 +808,11 @@ func (h *BufPane) UnbindCmd(args []string) { err := UnbindKey(parseKeyArg(args[0])) if err != nil { - InfoBar.Error(err) + if errors.Is(err, util.ErrOverwrite) { + screen.TermMessage(err) + } else { + InfoBar.Error(err) + } } } diff --git a/internal/buffer/save.go b/internal/buffer/save.go index 4a9d8e029..56d62aca6 100644 --- a/internal/buffer/save.go +++ b/internal/buffer/save.go @@ -272,6 +272,13 @@ func (b *Buffer) saveToFile(filename string, withSudo bool, autoSave bool) error result := <-saveResponseChan err = result.err if err != nil { + if errors.Is(err, fs.ErrPermission) { + return errors.Unwrap(err) + } + if errors.Is(err, util.ErrOverwrite) { + screen.TermMessage(err) + err = errors.Unwrap(err) + } return err } @@ -325,6 +332,7 @@ func (b *Buffer) safeWrite(path string, withSudo bool) (int, error) { size, err := b.overwriteFile(path, withSudo) if err != nil { + err = util.OverwriteError{err, backupName} return size, err } b.forceKeepBackup = false diff --git a/internal/util/util.go b/internal/util/util.go index a566c9bd5..64610b024 100644 --- a/internal/util/util.go +++ b/internal/util/util.go @@ -48,8 +48,41 @@ var ( // To be used for file writes before umask is applied FileMode os.FileMode = 0666 + + // To be used for fails on (over-)write with safe writes + ErrOverwrite = OverwriteError{} ) +const OverwriteFailMsg = `An error occurred while writing to the file: + +%s + +The file may be corrupted now. The good news is that it has been +successfully backed up. Next time you open this file with Micro, +Micro will ask if you want to recover it from the backup. + +The backup path is: + +%s` + +// OverwriteError is a custom error to add additional information +type OverwriteError struct { + What error + BackupName string +} + +func (e OverwriteError) Error() string { + return fmt.Sprintf(OverwriteFailMsg, e.What, e.BackupName) +} + +func (e OverwriteError) Is(target error) bool { + return target == ErrOverwrite +} + +func (e OverwriteError) Unwrap() error { + return e.What +} + func init() { var err error SemVersion, err = semver.Make(Version) @@ -670,6 +703,8 @@ func SafeWrite(path string, bytes []byte, rename bool) error { if err != nil { if rename { os.Remove(tmp) + } else { + err = OverwriteError{err, tmp} } return err }