- commit
- d23e438e0b038b401edb6b08be69c19df3f31b00
- parent
- 23f0bf06ca18390900d605424164e381bd540711
- author
- Dami <dami.etoile@gmail.com>
| M | internal/ | 5 | +- |
| M | internal/ | 29 | ++++++++- |
| A | internal/ | 97 | ++++++++++++++++++++++++++++++ |
@@ -149,7 +149,10 @@ func (m *Model) confirmReplace(msg string, do func(*Model) tea.Cmd, replaces boo
149149 warn: true,150150 choices: []choice{151151 {key: "enter", label: "Replace", run: do},152- {key: "esc", label: "Cancel", run: func(*Model) tea.Cmd { return nil }},152+ {key: "esc", label: "Cancel", run: func(m *Model) tea.Cmd {153+ m.quitAfterSave = false // cancelling :wq's save cancels its quit154+ return nil155+ }},153156 },154157 })155158 return nil@@ -67,9 +67,34 @@ func (m *Model) openSave() tea.Cmd {
6767 return nil6868 }6969 70-// saveAsFile saves the sheet under a name typed for Save as or :w.70+// saveAsFile saves the sheet under a name typed for Save as or :w,71+// asking first when that's another file that exists.7172 func (m *Model) saveAsFile(text string) tea.Cmd {72- return m.saveAs(withExt(text), true)73+ name := withExt(text)74+ p, ok := m.path("save", name)75+ if !ok {76+ m.quitAfterSave = false77+ return nil78+ }79+ return m.confirmReplace(filepath.Base(name)+" exists.", func(m *Model) tea.Cmd {80+ return m.saveAs(name, true)81+ }, !m.isOpenFile(p) && exists(p))82+}83+84+// isOpenFile reports whether path on disk is the open file's, however85+// its name was typed. The open file has its own check, for changes86+// since it was opened (saveAs).87+func (m *Model) isOpenFile(path string) bool {88+ if m.filename == "" {89+ return false90+ }91+ open, err := m.root.Resolve(m.filename)92+ if err != nil {93+ return false94+ }95+ a, err1 := filepath.Abs(open)96+ b, err2 := filepath.Abs(path)97+ return err1 == nil && err2 == nil && a == b7398 }7499 75100 func (m *Model) openRetrieve() tea.Cmd {@@ -0,0 +1,97 @@
1+package ui2+3+import (4+ "os"5+ "path/filepath"6+ "strings"7+ "testing"8+9+ tea "charm.land/bubbletea/v2"10+11+ "github.com/FelineStateMachine/012/internal/confine"12+)13+14+// Save as onto another file that exists asks first: Enter replaces it,15+// Esc keeps it. Saving as the open file, or a new name, doesn't ask.16+func TestSaveAsAsksBeforeReplacing(t *testing.T) {17+ t.Chdir(t.TempDir())18+ writeSheet(t, "other.012", "theirs")19+ m := newModel()20+ press(t, m, "mine", "<enter>")21+22+ run(m, m.openSave())23+ press(t, m, "other", "<enter>")24+ if _, ok := m.overlay.(*choiceBar); !ok || !strings.Contains(line(m, contextLine), "other.012 exists.") ||25+ !strings.Contains(line(m, contextLine), "Replace") {26+ t.Fatalf("no question: overlay %T, %q", m.overlay, line(m, contextLine))27+ }28+ press(t, m, "<esc>")29+ if got, _ := readSheet("other.012"); a1Of(got) != "theirs" || m.filename != "" || !m.changed {30+ t.Fatalf("Esc wrote it: A1 %q, file %q, changed %v", a1Of(got), m.filename, m.changed)31+ }32+33+ run(m, m.openSave())34+ press(t, m, "other", "<enter>", "<enter>")35+ if got, _ := readSheet("other.012"); a1Of(got) != "mine" || m.filename != "other.012" || m.changed {36+ t.Fatalf("Enter didn't replace: A1 %q, file %q, changed %v", a1Of(got), m.filename, m.changed)37+ }38+39+ // Now it's the open file: Save as onto it saves without asking, as40+ // Save does.41+ press(t, m, "<up>", "again", "<enter>")42+ run(m, m.openSave())43+ press(t, m, "<enter>")44+ if got, _ := readSheet("other.012"); m.overlay != nil || a1Of(got) != "again" {45+ t.Fatalf("Save as the open file: overlay %T, A1 %q", m.overlay, a1Of(got))46+ }47+ // A new name doesn't ask.48+ run(m, m.openSave())49+ press(t, m, "fresh", "<enter>")50+ if _, err := os.Stat("fresh.012"); err != nil || m.overlay != nil {51+ t.Fatalf("new name: %v, overlay %T", err, m.overlay)52+ }53+}54+55+// In a served session the file is looked for inside the served56+// directory, and :w name and :wq name go the same way; cancelling :wq's57+// question cancels its quit.58+func TestServedSaveAsAndVimAsk(t *testing.T) {59+ top := t.TempDir()60+ served := filepath.Join(top, "served")61+ os.Mkdir(served, 0o755)62+ writeSheet(t, filepath.Join(served, "kept.012"), "kept")63+ t.Chdir(top)64+ root, err := confine.New(served)65+ if err != nil {66+ t.Fatal(err)67+ }68+ m := vimModel(t)69+ m.Serve(root, []string{"TERM=xterm-256color"})70+ press(t, m, "x", ":w kept", "<enter>")71+ if !strings.Contains(line(m, contextLine), "kept.012 exists.") {72+ t.Fatalf(":w kept: %q", line(m, contextLine))73+ }74+ press(t, m, "<esc>")75+ if got, _ := readSheet(filepath.Join(served, "kept.012")); a1Of(got) != "kept" {76+ t.Fatalf("Esc wrote it: %q", a1Of(got))77+ }78+ if _, ok := press(t, m, ":wq kept", "<enter>", "<esc>").(tea.QuitMsg); ok {79+ t.Fatal("cancelled :wq quit")80+ }81+ if m.quitAfterSave {82+ t.Error("cancelled :wq still waits to quit")83+ }84+ if _, ok := press(t, m, ":wq kept", "<enter>", "<enter>").(tea.QuitMsg); !ok {85+ t.Fatal(":wq kept, Replace didn't quit")86+ }87+ if got, _ := readSheet(filepath.Join(served, "kept.012")); a1Of(got) == "kept" {88+ t.Error("Replace didn't write the served file")89+ }90+ // A file by that name in the server's working directory isn't the91+ // served one: no question for a name that's new inside.92+ writeSheet(t, filepath.Join(top, "cwd.012"), "cwd")93+ press(t, m, ":w cwd", "<enter>")94+ if m.overlay != nil {95+ t.Errorf(":w cwd asked about the working directory's file: %q", line(m, contextLine))96+ }97+}