diff --git a/cmd/micro/micro_test.go b/cmd/micro/micro_test.go index 09cad432cb..5f1b71d8c8 100644 --- a/cmd/micro/micro_test.go +++ b/cmd/micro/micro_test.go @@ -11,6 +11,7 @@ import ( "github.com/micro-editor/micro/v2/internal/buffer" "github.com/micro-editor/micro/v2/internal/config" "github.com/micro-editor/micro/v2/internal/screen" + "github.com/micro-editor/micro/v2/internal/util" "github.com/micro-editor/tcell/v2" "github.com/stretchr/testify/assert" ) @@ -157,8 +158,9 @@ func openFile(file string) { func findBuffer(file string) *buffer.Buffer { var buf *buffer.Buffer + _, file = util.ResolvePath(file) for _, b := range buffer.OpenBuffers { - if b.Path == file { + if b.AbsPath == file { buf = b } } diff --git a/internal/buffer/buffer.go b/internal/buffer/buffer.go index 2735ca467c..6c077d8f90 100644 --- a/internal/buffer/buffer.go +++ b/internal/buffer/buffer.go @@ -355,9 +355,9 @@ func NewBufferFromString(text, path string, btype BufType) *Buffer { // Places the cursor at startcursor. If startcursor is -1, -1 places the // cursor at an autodetected location (based on savecursor or :LINE:COL) func NewBuffer(r io.Reader, size int64, path string, btype BufType, cmd Command) *Buffer { - absPath, err := filepath.Abs(path) - if err != nil { - absPath = path + absPath := path + if btype == BTDefault && path != "" { + path, absPath = util.ResolvePath(path) } b := new(Buffer) @@ -391,6 +391,7 @@ func NewBuffer(r io.Reader, size int64, path string, btype BufType, cmd Command) } config.UpdatePathGlobLocals(b.Settings, absPath) + var err error b.encoding, err = htmlindex.Get(b.Settings["encoding"].(string)) if err != nil { b.encoding = unicode.UTF8 @@ -489,7 +490,7 @@ func NewBuffer(r io.Reader, size int64, path string, btype BufType, cmd Command) } } - err = config.RunPluginFn("onBufferOpen", luar.New(ulua.L, b)) + err := config.RunPluginFn("onBufferOpen", luar.New(ulua.L, b)) if err != nil { screen.TermMessage(err) } @@ -524,10 +525,12 @@ func (b *Buffer) Close() { // Fini should be called when a buffer is closed and performs // some cleanup func (b *Buffer) Fini() { - if !b.Modified() { - b.Serialize() + if !b.Shared() { + if !b.Modified() { + b.Serialize() + } + b.CancelBackup() } - b.CancelBackup() if b.Type == BTStdout { fmt.Fprint(util.Stdout, string(b.Bytes())) diff --git a/internal/buffer/save.go b/internal/buffer/save.go index 44e8f4a3ed..a1d86172b0 100644 --- a/internal/buffer/save.go +++ b/internal/buffer/save.go @@ -285,10 +285,7 @@ func (b *Buffer) saveToFile(filename string, withSudo bool, autoSave bool) error return errors.New("Error: " + filename + " is not a regular file and cannot be saved") } - absFilename, err := filepath.Abs(filename) - if err != nil { - return err - } + filename, absFilename := util.ResolvePath(filename) // Get the leading path to the file | "." is returned if there's no leading path provided if dirname := filepath.Dir(absFilename); dirname != "." { diff --git a/internal/util/util.go b/internal/util/util.go index cad6374349..e10ebc90e8 100644 --- a/internal/util/util.go +++ b/internal/util/util.go @@ -489,6 +489,40 @@ func DetermineEscapePath(dir string, path string) (string, string) { return url, "" } +// ResolvePath provides the absolute file path for the given relative file path +// as well as resolves symlinks in both. It returns the relative path with +// symlinks resolved and the absolute path with symlinks resolved. If it fails +// to get the absolute path or to resolve symlinks, it returns unresolved path +// in place of resolved one. The only exception is the case in which the target +// file doesn't exist. We leave the path handling to EvalSymlinks() and use the +// path causing the error as target path. +func ResolvePath(path string) (string, string) { + resolvedPath, err := filepath.EvalSymlinks(path) + if err == nil { + path = resolvedPath + } else if errors.Is(err, fs.ErrNotExist) { + if e, ok := err.(*fs.PathError); ok { + path = e.Path + } + } + + absPath, err := filepath.Abs(path) + if err != nil { + absPath = path + } + + resolvedPath, err = filepath.EvalSymlinks(absPath) + if err == nil { + absPath = resolvedPath + } else if errors.Is(err, fs.ErrNotExist) { + if e, ok := err.(*fs.PathError); ok { + absPath = e.Path + } + } + + return path, absPath +} + // GetLeadingWhitespace returns the leading whitespace of the given byte array func GetLeadingWhitespace(b []byte) []byte { ws := []byte{}