All checks were successful
Run Test Suite / test (push) Successful in 56s
Not sure if this is perfect, but it seems to be working
86 lines
2.3 KiB
Go
86 lines
2.3 KiB
Go
package command
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"os"
|
|
|
|
"git.gophernest.net/azpect/TextEditor/internal/action"
|
|
"git.gophernest.net/azpect/TextEditor/internal/core"
|
|
tea "github.com/charmbracelet/bubbletea"
|
|
)
|
|
|
|
func writeBuffer(m action.Model, buf *core.Buffer, args []string, force bool) (tea.Cmd, error) {
|
|
// Key Steps:
|
|
// - Backup Creation (if backup is set): Copy original to .bak file
|
|
// - Line Ending Application: Apply fileformat setting (unix=\n, dos=\r\n, mac=\r)
|
|
// - Character Encoding: Convert from internal representation to fileencoding
|
|
// - Atomic Write: Write to temporary file first (.swp or similar)
|
|
// - Atomic Rename: Rename temp file to target filename (atomic operation)
|
|
// - Metadata Update: Clear modified flag, update timestamp
|
|
// Safety Mechanisms:
|
|
// " Vim's write safety
|
|
// 1. Check file permissions
|
|
// 2. If file exists and 'writebackup' set:
|
|
// - Create backup (.bak)
|
|
// 3. Write to temporary file (.swp~)
|
|
// 4. Verify write succeeded
|
|
// 5. Rename temp to target (atomic)
|
|
// 6. Remove backup if 'backup' not set
|
|
// 7. Update buffer metadata
|
|
|
|
// TODO: Implement atomic and safe writes
|
|
|
|
// Check readonly flag ONLY if not forced with !
|
|
if !force && buf.ReadOnly {
|
|
return nil, fmt.Errorf("cannot write to 'readonly' buffer")
|
|
}
|
|
|
|
if !force && buf.Type == core.ScatchBuffer {
|
|
return nil, fmt.Errorf("cannot write to buffer of type 'ScratchBuffer'")
|
|
}
|
|
|
|
// Get the filename; differs by the type
|
|
var filename string
|
|
if len(args) > 0 {
|
|
filename = args[0]
|
|
} else {
|
|
if buf.Filename == "" {
|
|
return nil, fmt.Errorf("cannot write: no file name provided")
|
|
}
|
|
filename = buf.Filename
|
|
}
|
|
|
|
file, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0664)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer file.Close()
|
|
|
|
// Same write operation regardless of where the file came from
|
|
var bytes int
|
|
|
|
// Using a bufio.Writer because its more efficient
|
|
writer := bufio.NewWriter(file)
|
|
for _, line := range buf.Lines {
|
|
n, err := writer.WriteString(line.String() + "\n")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
bytes += n
|
|
}
|
|
|
|
if err := writer.Flush(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
m.SetCommandOutput(&core.CommandOutput{
|
|
Lines: []string{fmt.Sprintf("'%s', %dL %db written", filename, buf.LineCount(), bytes)},
|
|
Inline: true,
|
|
})
|
|
buf.SetModified(false)
|
|
|
|
return nil, nil
|
|
}
|