Data > Shell and the palette open the prompt from any sheet.
- commit
- 0ca888c95d9a206408b07fe0452c4f9267492b8b
- parent
- 9e32828d82420cddbb8491b3fa33c1ca4a5c8ce9
- author
- Dami <dami.etoile@gmail.com>
- kind
- fix risk score 2.0 of 3, Jev's estimate
| M | docs/ | 2 | +- |
| M | docs/ | 4 | ++-- |
| M | e2e/ | 9 | ++++++--- |
| M | e2e/ | 2 | +- |
| M | internal/ | 2 | +- |
| M | internal/ | 12 | ++++++++++-- |
| M | internal/ | 16 | ++++++++-------- |
| A | internal/ | 26 | ++++++++++++++++++++++++++ |
@@ -130,7 +130,7 @@ range, F2 renames or repoints it and Ctrl+D deletes it.
130130 131131 | Key | Action |132132 |---|---|133-| ! | Open the nushell prompt on the formula bar (the mode indicator says NU); in any workbook, it writes to the notebook sheet ([Nushell notebooks](../terminal/nushell.md#notebooks)) |133+| ! | On a notebook sheet, open the nushell prompt on the formula bar (the mode indicator says NU); on other sheets `!` starts an entry, and **Data > Shell** opens the prompt ([Nushell notebooks](../terminal/nushell.md#notebooks)) |134134 | Enter, Tab, Up, Down, Esc | At the prompt: run the line; complete a region or command; the workbook's earlier lines; stop a command running, or else go back to the grid |135135 | Enter, F2 | On a region's label line: run it again (and what reads it); edit its command at the prompt |136136 | F9 | Run all regions, each after those it reads |@@ -37,8 +37,8 @@ like a REPL, kept like a spreadsheet.
3737 012 nu work.012 # open one, or make it3838 ```3939 40-In any workbook, `!` (or **Data > Shell**, or the palette) opens the41-same prompt, writing into the workbook's notebook sheet, or a new one40+In any workbook, **Data > Shell** (or the palette, or `!` on a notebook41+sheet) opens the same prompt, writing into the workbook's notebook sheet, or a new one4242 named Shell 1 at the first command. Commands run `nu` as a separate4343 process, so nushell must be installed; without it, the prompt says so4444 and the rest of 012 works as before.@@ -44,14 +44,17 @@ func TestNotebookWithNu(t *testing.T) {
4444 s.waitFor("5.0 MB")4545 }4646 47-// ! in an ordinary workbook makes a Shell 1 sheet, and $in is the48-// selection.47+// Data > Shell in an ordinary workbook makes a Shell 1 sheet, and $in is48+// the selection. (! types itself there; it opens the prompt only on a49+// notebook sheet.)4950 func TestShellFromWorkbook(t *testing.T) {5051 needNu(t)5152 s := start(t, "")5253 s.keys("n", "<enter>", "3", "<enter>", "4", "<enter>", "<up>", "<up>", "<up>", "<shift+down>", "<shift+down>", "<shift+down>")5354 s.waitFor(" A1:A4 ")54- s.keys("!", "$in | math sum", "<enter>")55+ s.keys("<ctrl+k>", "Shell")56+ s.waitFor("Run a nushell pipeline")57+ s.keys("<enter>", "$in | math sum", "<enter>")5558 s.waitFor("r1: 1 row")5659 s.waitFor("Shell 1")5760 s.waitFor(" 7")@@ -7,7 +7,7 @@
77 <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">3</span><span style="background:var(--vt-palette-8)"> </span> Rent 1450 <span style="color:var(--vt-palette-8)">│</span> <span style="color:var(--vt-palette-8)">Select</span> <span style="color:var(--vt-palette-8)">chart</span> <span style="color:var(--vt-palette-8)">│</span>88 <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">4</span><span style="background:var(--vt-palette-8)"> </span> Groceries 612.4 <span style="color:var(--vt-palette-8)">│</span> <span style="color:var(--vt-palette-3);font-weight:bold">Sel</span>ect columns <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Ctrl+Space</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span>99 <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">5</span><span style="background:var(--vt-palette-8)"> </span> Transit 96 <span style="color:var(--vt-palette-8)">│</span> <span style="color:var(--vt-palette-8)">Move</span> <span style="color:var(--vt-palette-8)">sheet</span> <span style="color:var(--vt-palette-8)">left</span> <span style="color:var(--vt-palette-8)">Edit</span> <span style="color:var(--vt-palette-8)">›</span> <span style="color:var(--vt-palette-8)">Sheet</span> <span style="color:var(--vt-palette-8)">│</span>10-<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">6</span><span style="background:var(--vt-palette-8)"> </span> Savings r <span style="color:var(--vt-palette-1);text-decoration:underline wavy var(--vt-palette-1)">#DIV/0!</span> <span style="color:var(--vt-palette-8)">│</span> <span style="color:var(--vt-palette-3);font-weight:bold">S</span>h<span style="color:var(--vt-palette-3);font-weight:bold">el</span>l <span style="color:var(--vt-palette-8)">Data</span> <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">!</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span>10+<span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">6</span><span style="background:var(--vt-palette-8)"> </span> Savings r <span style="color:var(--vt-palette-1);text-decoration:underline wavy var(--vt-palette-1)">#DIV/0!</span> <span style="color:var(--vt-palette-8)">│</span> <span style="color:var(--vt-palette-3);font-weight:bold">S</span>h<span style="color:var(--vt-palette-3);font-weight:bold">el</span>l <span style="color:var(--vt-palette-8)">Data</span> <span style="color:var(--vt-palette-8)">│</span>1111 <span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6);font-weight:bold">7</span><span style="background:var(--vt-palette-6)"> </span> Total <span style="background:var(--vt-palette-6)"> </span><span style="color:var(--vt-palette-0);background:var(--vt-palette-6)">2158.4</span><span style="background:var(--vt-palette-6)"> </span> <span style="color:var(--vt-palette-8)">│</span> De<span style="color:var(--vt-palette-3);font-weight:bold">sel</span>ect <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">Esc</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span>1212 <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">8</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> In<span style="color:var(--vt-palette-3);font-weight:bold">se</span>rt columns <span style="color:var(--vt-palette-3);font-weight:bold">l</span>eft <span style="color:var(--vt-palette-8)">Insert</span> <span style="color:var(--vt-palette-8)">│</span>1313 <span style="background:var(--vt-palette-8)"> </span><span style="color:var(--vt-palette-15);background:var(--vt-palette-8)">9</span><span style="background:var(--vt-palette-8)"> </span> <span style="color:var(--vt-palette-8)">│</span> <span style="color:var(--vt-palette-8)">Stop</span> <span style="color:var(--vt-palette-8)">shell</span> <span style="color:var(--vt-palette-8)">command</span> <span style="color:var(--vt-palette-8)">Data</span> <span style="color:var(--vt-palette-8)">›</span> <span style="color:var(--vt-palette-8)">Shell</span> <span style="color:var(--vt-palette-8)">regio…</span> <span style="color:var(--vt-palette-8)">│</span>@@ -192,7 +192,7 @@ func TestMonochromeSelection(t *testing.T) {
192192 // its name and command as text; the table's cells are plain.193193 func TestMonochromeRegion(t *testing.T) {194194 m, _ := notebookModel(t, map[string]string{"ls": lsOut})195- press(t, m, "!", "ls", "<enter>", "<esc>")195+ shell(t, m, "ls", "<enter>", "<esc>")196196 m.cur = addr("C9")197197 for _, a := range []string{"A1", "B1"} {198198 c := cellAt(m, addr(a))@@ -10,7 +10,7 @@ import (
1010 "github.com/FelineStateMachine/012/internal/ui/nuprompt"1111 )1212 13-// Notebooks: ! (or Data > Shell, or 012 nu) opens a nushell prompt on13+// Notebooks: Data > Shell (or ! on a notebook sheet, or 012 nu) opens a nushell prompt on1414 // the formula bar. Each line run becomes a region of the notebook sheet1515 // (the one shown, or the first, or a new Shell 1): a live table named1616 // r1, r2 and so on, or name = pipeline to name it, which later commands@@ -52,7 +52,15 @@ func init() {
5252 enabled: func(m *Model) bool { return m.shell.running != nil },5353 run: func(m *Model) tea.Cmd { m.stopShell(); return nil }},5454 )55- keymap["!"] = "nu.prompt"55+ // ! opens the prompt only on a notebook sheet; elsewhere it types56+ // itself, so an entry can start with "!" as in Sheets.57+ register(&command{id: "nu.prompt_key", macro: macroNever, title: "Shell (notebook sheet)",58+ desc: "Run a nushell pipeline; its table becomes a live region of this notebook sheet",59+ typed: true,60+ enabled: func(m *Model) bool { return m.sheet.Notebook() },61+ hidden: func(*Model) bool { return true },62+ run: func(m *Model) tea.Cmd { return m.openShell("") }})63+ keymap["!"] = "nu.prompt_key"5664 keymap["f9"] = "nu.run_all"5765 }5866 @@ -48,7 +48,7 @@ func TestShellMakesRegions(t *testing.T) {
4848 "ls": lsOut,4949 "$r1 | where size > 1kb": "[[name, size]; [a.txt, 2kb], [c.txt, 5kb]]",5050 })51- press(t, m, "!")51+ shell(t, m)5252 if _, ok := m.overlay.(*nuprompt.Prompt); !ok || m.indicator() != "NU" {5353 t.Fatalf("no prompt: %T %q", m.overlay, m.indicator())5454 }@@ -99,7 +99,7 @@ func TestShellMakesRegions(t *testing.T) {
9999 100100 func TestShellFails(t *testing.T) {101101 m, _ := notebookModel(t, map[string]string{})102- press(t, m, "!", "lss", "<enter>")102+ shell(t, m, "lss", "<enter>")103103 if got := input(m, "A1"); got != "r1 lss failed" {104104 t.Errorf("label %q", got)105105 }@@ -113,7 +113,7 @@ func TestShellFails(t *testing.T) {
113113 }114114 // Named: name = pipeline, and redefining it.115115 m.shell.runner.(*fakeNu).out["ls"] = lsOut116- press(t, m, "!", "files = ls", "<enter>")116+ shell(t, m, "files = ls", "<enter>")117117 if _, r, ok := m.book().Region("files"); !ok || r.Command != "ls" {118118 t.Fatalf("no region files: %+v", r)119119 }@@ -128,7 +128,7 @@ func TestShellFails(t *testing.T) {
128128 129129 func TestShellCompletes(t *testing.T) {130130 m, _ := notebookModel(t, map[string]string{"ls": lsOut})131- press(t, m, "!", "ls", "<enter>", "$")131+ shell(t, m, "ls", "<enter>", "$")132132 p := m.overlay.(*nuprompt.Prompt)133133 if got := p.Shown(); len(got) != 1 || got[0].Text != "$r1" {134134 t.Fatalf("completions of $: %+v", got)@@ -158,7 +158,7 @@ func TestShellCompletes(t *testing.T) {
158158 159159 func TestShellTrust(t *testing.T) {160160 m, nu := notebookModel(t, map[string]string{"ls": lsOut})161- press(t, m, "!", "ls", "<enter>", "<esc>")161+ shell(t, m, "ls", "<enter>", "<esc>")162162 if m.book().MacroOrigin() != "here" {163163 t.Errorf("origin %q", m.book().MacroOrigin())164164 }@@ -189,7 +189,7 @@ func TestShellTrust(t *testing.T) {
189189 190190 func TestShellGuardsAndFreeze(t *testing.T) {191191 m, _ := notebookModel(t, map[string]string{"ls": lsOut})192- press(t, m, "!", "ls", "<enter>", "<esc>")192+ shell(t, m, "ls", "<enter>", "<esc>")193193 m.cur = addr("A3")194194 press(t, m, "x", "<enter>")195195 if got := input(m, "A3"); got != "a.txt" {@@ -214,7 +214,7 @@ func TestShellGuardsAndFreeze(t *testing.T) {
214214 // Sorting a region's table keeps the order through the next run.215215 func TestShellSortsRegion(t *testing.T) {216216 m, _ := notebookModel(t, map[string]string{"ls": lsOut})217- press(t, m, "!", "ls", "<enter>", "<esc>")217+ shell(t, m, "ls", "<enter>", "<esc>")218218 m.cur = addr("B3")219219 run(m, m.runCommand("data.sort_range_za"))220220 if got := input(m, "A3"); got != "c.txt" {@@ -230,7 +230,7 @@ func TestShellSortsRegion(t *testing.T) {
230230 // Opening a saved notebook shows its regions not run, and F9 runs them.231231 func TestShellReopened(t *testing.T) {232232 m, _ := notebookModel(t, map[string]string{"ls": lsOut, "$r1 | first": "{name: a.txt, size: 2kb}"})233- press(t, m, "!", "ls", "<enter>", "$r1 | first", "<enter>", "<esc>")233+ shell(t, m, "ls", "<enter>", "$r1 | first", "<enter>", "<esc>")234234 s := roundTripSheet(t, m.sheet)235235 m2, nu := notebookModel(t, map[string]string{"ls": lsOut, "$r1 | first": "{name: a.txt, size: 2kb}"})236236 m2.reset(s, "nb.012")@@ -0,0 +1,26 @@
1+package ui2+3+import "testing"4+5+// ! opens the shell prompt on a notebook sheet and starts an entry on6+// any other sheet, so a cell can begin with "!" as in Sheets.7+func TestBangOpensPromptOnlyOnNotebookSheets(t *testing.T) {8+ m := newModel()9+ press(t, m, "!")10+ if m.mode != modeEnter || m.line.Text() != "!" {11+ t.Fatalf("on a plain sheet, ! should start an entry; mode %v, entry %q", m.mode, m.line.Text())12+ }13+ press(t, m, "<esc>")14+ m.sheet.MakeNotebook()15+ press(t, m, "!")16+ if m.mode == modeEnter {17+ t.Fatalf("on a notebook sheet, ! should open the shell prompt, not an entry")18+ }19+}20+21+// shell opens the nushell prompt as Data > Shell does, then presses keys.22+func shell(t *testing.T, m *Model, keys ...string) {23+ t.Helper()24+ run(m, m.runCommand("nu.prompt"))25+ press(t, m, keys...)26+}