]> git.lizzy.rs Git - micro.git/blobdiff - internal/buffer/save.go
Support csharp-script syntax. (#1425)
[micro.git] / internal / buffer / save.go
index d876b338cb03efb18431472faa43d6460f2a24f4..6f07ea6c8b6b923a6558986b73872ee7bfc765b7 100644 (file)
@@ -1,6 +1,7 @@
 package buffer
 
 import (
+       "bufio"
        "bytes"
        "errors"
        "io"
@@ -8,11 +9,13 @@ import (
        "os/exec"
        "os/signal"
        "path/filepath"
+       "runtime"
        "unicode"
        "unicode/utf8"
 
        "github.com/zyedidia/micro/internal/config"
-       . "github.com/zyedidia/micro/internal/util"
+       "github.com/zyedidia/micro/internal/screen"
+       "github.com/zyedidia/micro/internal/util"
        "golang.org/x/text/encoding"
        "golang.org/x/text/encoding/htmlindex"
        "golang.org/x/text/transform"
@@ -25,27 +28,42 @@ const LargeFileThreshold = 50000
 // overwriteFile opens the given file for writing, truncating if one exists, and then calls
 // the supplied function with the file as io.Writer object, also making sure the file is
 // closed afterwards.
-func overwriteFile(name string, enc encoding.Encoding, fn func(io.Writer) error) (err error) {
-       var file *os.File
+func overwriteFile(name string, enc encoding.Encoding, fn func(io.Writer) error, withSudo bool) (err error) {
+       var writeCloser io.WriteCloser
 
-       if file, err = os.OpenFile(name, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644); err != nil {
-               return
-       }
+       if withSudo {
+               cmd := exec.Command(config.GlobalSettings["sucmd"].(string), "dd", "bs=4k", "of="+name)
 
-       defer func() {
-               if e := file.Close(); e != nil && err == nil {
-                       err = e
+               if writeCloser, err = cmd.StdinPipe(); err != nil {
+                       return
                }
-       }()
 
-       w := transform.NewWriter(file, enc.NewEncoder())
-       // w := bufio.NewWriter(file)
+               c := make(chan os.Signal, 1)
+               signal.Notify(c, os.Interrupt)
+               go func() {
+                       <-c
+                       cmd.Process.Kill()
+               }()
 
-       if err = fn(w); err != nil {
+               defer func() {
+                       screenb := screen.TempFini()
+                       if e := cmd.Run(); e != nil && err == nil {
+                               err = e
+                       }
+                       screen.TempStart(screenb)
+               }()
+       } else if writeCloser, err = os.OpenFile(name, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644); err != nil {
                return
        }
 
-       // err = w.Flush()
+       w := bufio.NewWriter(transform.NewWriter(writeCloser, enc.NewEncoder()))
+       err = fn(w)
+       w.Flush()
+
+       if e := writeCloser.Close(); e != nil && err == nil {
+               err = e
+       }
+
        return
 }
 
@@ -55,10 +73,29 @@ func (b *Buffer) Save() error {
 }
 
 // SaveAs saves the buffer to a specified path (filename), creating the file if it does not exist
-func (b *Buffer) SaveAs(filename string) (err error) {
+func (b *Buffer) SaveAs(filename string) error {
+       return b.saveToFile(filename, false)
+}
+
+func (b *Buffer) SaveWithSudo() error {
+       return b.SaveAsWithSudo(b.Path)
+}
+
+func (b *Buffer) SaveAsWithSudo(filename string) error {
+       return b.saveToFile(filename, true)
+}
+
+func (b *Buffer) saveToFile(filename string, withSudo bool) error {
+       var err error
+       if b.Type.Readonly {
+               return errors.New("Cannot save readonly buffer")
+       }
        if b.Type.Scratch {
                return errors.New("Cannot save scratch buffer")
        }
+       if withSudo && runtime.GOOS == "windows" {
+               return errors.New("Save with sudo not supported on Windows")
+       }
 
        b.UpdateRules()
        if b.Settings["rmtrailingws"].(bool) {
@@ -74,38 +111,36 @@ func (b *Buffer) SaveAs(filename string) (err error) {
 
        if b.Settings["eofnewline"].(bool) {
                end := b.End()
-               if b.RuneAt(Loc{end.X - 1, end.Y}) != '\n' {
-                       b.Insert(end, "\n")
+               if b.RuneAt(Loc{end.X, end.Y}) != '\n' {
+                       b.insert(end, []byte{'\n'})
                }
        }
 
        // Update the last time this file was updated after saving
        defer func() {
-               b.ModTime, _ = GetModTime(filename)
+               b.ModTime, _ = util.GetModTime(filename)
                err = b.Serialize()
        }()
 
        // Removes any tilde and replaces with the absolute path to home
-       absFilename, _ := ReplaceHome(filename)
-
-       // TODO: save creates parent dirs
-       // // Get the leading path to the file | "." is returned if there's no leading path provided
-       // if dirname := filepath.Dir(absFilename); dirname != "." {
-       //      // Check if the parent dirs don't exist
-       //      if _, statErr := os.Stat(dirname); os.IsNotExist(statErr) {
-       //              // Prompt to make sure they want to create the dirs that are missing
-       //              if yes, canceled := messenger.YesNoPrompt("Parent folders \"" + dirname + "\" do not exist. Create them? (y,n)"); yes && !canceled {
-       //                      // Create all leading dir(s) since they don't exist
-       //                      if mkdirallErr := os.MkdirAll(dirname, os.ModePerm); mkdirallErr != nil {
-       //                              // If there was an error creating the dirs
-       //                              return mkdirallErr
-       //                      }
-       //              } else {
-       //                      // If they canceled the creation of leading dirs
-       //                      return errors.New("Save aborted")
-       //              }
-       //      }
-       // }
+       absFilename, _ := util.ReplaceHome(filename)
+
+       // Get the leading path to the file | "." is returned if there's no leading path provided
+       if dirname := filepath.Dir(absFilename); dirname != "." {
+               // Check if the parent dirs don't exist
+               if _, statErr := os.Stat(dirname); os.IsNotExist(statErr) {
+                       // Prompt to make sure they want to create the dirs that are missing
+                       if b.Settings["mkparents"].(bool) {
+                               // Create all leading dir(s) since they don't exist
+                               if mkdirallErr := os.MkdirAll(dirname, os.ModePerm); mkdirallErr != nil {
+                                       // If there was an error creating the dirs
+                                       return mkdirallErr
+                               }
+                       } else {
+                               return errors.New("Parent dirs don't exist, enable 'mkparents' for auto creation")
+                       }
+               }
+       }
 
        var fileSize int
 
@@ -114,7 +149,7 @@ func (b *Buffer) SaveAs(filename string) (err error) {
                return err
        }
 
-       err = overwriteFile(absFilename, enc, func(file io.Writer) (e error) {
+       fwriter := func(file io.Writer) (e error) {
                if len(b.lines) == 0 {
                        return
                }
@@ -142,9 +177,9 @@ func (b *Buffer) SaveAs(filename string) (err error) {
                        fileSize += len(eol) + len(l.data)
                }
                return
-       })
+       }
 
-       if err != nil {
+       if err = overwriteFile(absFilename, enc, fwriter, withSudo); err != nil {
                return err
        }
 
@@ -161,48 +196,5 @@ func (b *Buffer) SaveAs(filename string) (err error) {
        absPath, _ := filepath.Abs(filename)
        b.AbsPath = absPath
        b.isModified = false
-       return
-}
-
-// SaveWithSudo saves the buffer to the default path with sudo
-func (b *Buffer) SaveWithSudo() error {
-       return b.SaveAsWithSudo(b.Path)
-}
-
-// SaveAsWithSudo is the same as SaveAs except it uses a neat trick
-// with tee to use sudo so the user doesn't have to reopen micro with sudo
-func (b *Buffer) SaveAsWithSudo(filename string) error {
-       if b.Type.Scratch {
-               return errors.New("Cannot save scratch buffer")
-       }
-
-       b.UpdateRules()
-       b.Path = filename
-       absPath, _ := filepath.Abs(filename)
-       b.AbsPath = absPath
-
-       // Set up everything for the command
-       cmd := exec.Command(config.GlobalSettings["sucmd"].(string), "tee", filename)
-       cmd.Stdin = bytes.NewBuffer(b.Bytes())
-
-       // This is a trap for Ctrl-C so that it doesn't kill micro
-       // Instead we trap Ctrl-C to kill the program we're running
-       c := make(chan os.Signal, 1)
-       signal.Notify(c, os.Interrupt)
-       go func() {
-               for range c {
-                       cmd.Process.Kill()
-               }
-       }()
-
-       // Start the command
-       cmd.Start()
-       err := cmd.Wait()
-
-       if err == nil {
-               b.isModified = false
-               b.ModTime, _ = GetModTime(filename)
-               return b.Serialize()
-       }
        return err
 }