Sort commands over a region set its order rather than moving its cells, so the table stays sorted when its command runs again. Tests check that CSV and XLSX downloads hold a region's values.
- commit
- 76a3015787daf5e5c9cfbea293f45949221949e6
- parent
- 7a46ce145239957c847e4203b67cb961c47340ff
- author
- Dami <dami.etoile@gmail.com>
- kind
- fix risk score 1.4 of 3, Jev's estimate
| M | internal/ | 27 | +++++++++++++++++++++++++++ |
| M | internal/ | 19 | +++++++++++++++++++ |
| M | internal/ | 16 | ++++++++++++++++ |
| M | internal/ | 3 | +++ |
@@ -173,6 +173,33 @@ func TestEncodeFiltered(t *testing.T) {
173173 }174174 }175175 176+// A notebook's regions download as their values, labels included.177+func TestExportRegion(t *testing.T) {178+ s := sheet.New()179+ s.MakeNotebook()180+ if err := s.AddRegion(sheet.Region{Name: "r1", Command: "ls"}); err != nil {181+ t.Fatal(err)182+ }183+ d := &sheet.RegionData{Rows: 2, Cols: 2, Values: []sheet.Value{{Kind: sheet.Text, Str: "name"}, {Kind: sheet.Text, Str: "n"}, {Kind: sheet.Text, Str: "a"}, {Kind: sheet.Number, Num: 1}}}184+ if err := s.ShowRegion("r1", d); err != nil {185+ t.Fatal(err)186+ }187+ if got := encode(t, s, CSV); got != "r1 ls,\nname,n\na,1\n" {188+ t.Errorf("CSV %q", got)189+ }190+ path := filepath.Join(t.TempDir(), "nb.xlsx")191+ if _, err := Export(context.Background(), path, XLSX, SnapBook(s), ExportOptions{}); err != nil {192+ t.Fatal(err)193+ }194+ res, err := Import(context.Background(), path, Options{})195+ if err != nil {196+ t.Fatal(err)197+ }198+ if c := res.Sheet.Cell(addr(t, "B3")); c == nil || c.IsFormula() || c.Value.Num != 1 {199+ t.Errorf("B3 read back as %+v", c)200+ }201+}202+176203 // TestNUONRoundTrip reads a table and writes it back as the same NUON.177204 func TestNUONRoundTrip(t *testing.T) {178205 inZone(t, mountain)@@ -250,6 +250,25 @@ func (m *Model) trustShell(run func(*Model) tea.Cmd) tea.Cmd {
250250 return nil251251 }252252 253+// sortRegion sorts the table of a region a sort's range r reaches, by254+// its columns in keys: the rows below its header, whatever else r255+// holds. The order is the region's, kept when it runs again.256+func (m *Model) sortRegion(reg sheet.Region, r sheet.Rect, keys []sheet.SortKey) tea.Cmd {257+ t, ok := m.sheet.RegionTable(reg.Name)258+ inTable := func(k sheet.SortKey) bool { return k.Col >= t.From.Col && k.Col <= t.To.Col }259+ if !ok || len(keys) == 0 || !slices.ContainsFunc(keys, inTable) || r.To.Row <= t.From.Row {260+ m.note = "Sort a region by a column of its table"261+ return nil262+ }263+ keys = slices.DeleteFunc(slices.Clone(keys), func(k sheet.SortKey) bool { return !inTable(k) })264+ if err := m.sheet.SortRegion(reg.Name, keys); err != nil {265+ m.fail(err.Error())266+ return nil267+ }268+ m.note = "Sorted " + reg.Name + "; it stays sorted when it runs again"269+ return nil270+}271+253272 // freezeRegion turns a region's table into plain values.254273 func (m *Model) freezeRegion(r sheet.Region) tea.Cmd {255274 if m.shell.running != nil && m.shell.running.name == r.Name {@@ -211,6 +211,22 @@ func TestShellGuardsAndFreeze(t *testing.T) {
211211 }212212 }213213 214+// Sorting a region's table keeps the order through the next run.215+func TestShellSortsRegion(t *testing.T) {216+ m, _ := notebookModel(t, map[string]string{"ls": lsOut})217+ press(t, m, "!", "ls", "<enter>", "<esc>")218+ m.cur = addr("B3")219+ run(m, m.runCommand("data.sort_range_za"))220+ if got := input(m, "A3"); got != "c.txt" {221+ t.Fatalf("sorted A3 %q, note %q", got, m.note)222+ }223+ m.cur = addr("A1")224+ press(t, m, "<enter>")225+ if got := input(m, "A3"); got != "c.txt" {226+ t.Errorf("after refresh A3 %q", got)227+ }228+}229+214230 // Opening a saved notebook shows its regions not run, and F9 runs them.215231 func TestShellReopened(t *testing.T) {216232 m, _ := notebookModel(t, map[string]string{"ls": lsOut, "$r1 | first": "{name: a.txt, size: 2kb}"})@@ -62,6 +62,9 @@ func (m *Model) sort(r sheet.Rect, keys []sheet.SortKey) tea.Cmd {
6262 m.note = "Nothing to sort"6363 return nil6464 }65+ if _, reg, ok := m.sheet.InRegion(r); ok {66+ return m.sortRegion(reg, r, keys)67+ }6568 if m.refuseEdit(r, false) {6669 return nil6770 }