src.elv.sh@v0.21.0-dev.0.20240515223629-06979efb9a2a/pkg/edit/completion_test.go (about) 1 package edit 2 3 import ( 4 "testing" 5 6 "src.elv.sh/pkg/cli/term" 7 "src.elv.sh/pkg/eval" 8 "src.elv.sh/pkg/eval/vals" 9 "src.elv.sh/pkg/testutil" 10 "src.elv.sh/pkg/ui" 11 ) 12 13 func TestCompletionAddon(t *testing.T) { 14 f := setup(t) 15 16 testutil.ApplyDir(testutil.Dir{"a": "", "b": ""}) 17 18 feedInput(f.TTYCtrl, "echo \t") 19 f.TestTTY(t, 20 "~> echo a \n", Styles, 21 " vvvv __", 22 " COMPLETING argument ", Styles, 23 "********************* ", term.DotHere, "\n", 24 "a b", Styles, 25 "+ ", 26 ) 27 } 28 29 func TestCompletionAddon_CompletesLongestCommonPrefix(t *testing.T) { 30 f := setup(t) 31 32 testutil.ApplyDir(testutil.Dir{"foo1": "", "foo2": "", "foo": "", "fox": ""}) 33 34 feedInput(f.TTYCtrl, "echo \t") 35 f.TestTTY(t, 36 "~> echo fo", Styles, 37 " vvvv", term.DotHere, 38 ) 39 40 feedInput(f.TTYCtrl, "\t") 41 f.TestTTY(t, 42 "~> echo foo \n", Styles, 43 " vvvv ____", 44 " COMPLETING argument ", Styles, 45 "********************* ", term.DotHere, "\n", 46 "foo foo1 foo2 fox", Styles, 47 "+++ ", 48 ) 49 } 50 51 func TestCompletionAddon_AppliesAutofix(t *testing.T) { 52 f := setup(t) 53 fooNs := eval.BuildNs().AddGoFn("a", func() {}).AddGoFn("b", func() {}).Ns() 54 f.Evaler.AddModule("foo", fooNs) 55 56 feedInput(f.TTYCtrl, "foo:") 57 f.TestTTY(t, 58 "~> foo:", Styles, 59 " !!!!", term.DotHere, "\n", 60 "Ctrl-A autofix: use foo Tab Enter autofix first", Styles, 61 "++++++ +++ +++++", 62 ) 63 64 feedInput(f.TTYCtrl, "\t") 65 f.TestTTY(t, 66 "~> foo:a\n", Styles, 67 " VVVVV", 68 " COMPLETING command ", Styles, 69 "******************** ", term.DotHere, "\n", 70 "foo:a foo:b", Styles, 71 "+++++ ", 72 ) 73 } 74 75 func TestCompleteFilename(t *testing.T) { 76 f := setup(t) 77 78 testutil.ApplyDir(testutil.Dir{"d": testutil.Dir{"a": "", "b": ""}}) 79 80 evals(f.Evaler, `var @cands = (edit:complete-filename ls ./d/a)`) 81 testGlobal(t, f.Evaler, 82 "cands", 83 vals.MakeList( 84 complexItem{Stem: "./d/a", CodeSuffix: " ", Display: ui.T("./d/a")}, 85 complexItem{Stem: "./d/b", CodeSuffix: " ", Display: ui.T("./d/b")})) 86 87 testThatOutputErrorIsBubbled(t, f, "edit:complete-filename ls ''") 88 } 89 90 func TestComplexCandidate_InEditModule(t *testing.T) { 91 // A sanity check that the complex-candidate command is part of the edit 92 // module. 93 f := setup(t) 94 95 evals(f.Evaler, `var stem = (edit:complex-candidate stem)[stem]`) 96 testGlobal(t, f.Evaler, "stem", "stem") 97 } 98 99 func TestCompletionArgCompleter_ArgsAndValueOutput(t *testing.T) { 100 f := setup(t) 101 102 evals(f.Evaler, 103 `var foo-args = []`, 104 `fn foo { }`, 105 `set edit:completion:arg-completer[foo] = {|@args| 106 set foo-args = $args 107 put 1val 108 edit:complex-candidate 2val &display=2VAL 109 }`) 110 111 feedInput(f.TTYCtrl, "foo foo1 foo2 \t") 112 f.TestTTY(t, 113 "~> foo foo1 foo2 1val\n", Styles, 114 " vvv ____", 115 " COMPLETING argument ", Styles, 116 "********************* ", term.DotHere, "\n", 117 "1val 2VAL", Styles, 118 "++++ ", 119 ) 120 testGlobal(t, f.Evaler, 121 "foo-args", vals.MakeList("foo", "foo1", "foo2", "")) 122 } 123 124 func TestCompletionArgCompleter_BytesOutput(t *testing.T) { 125 f := setup(t) 126 127 evals(f.Evaler, 128 `fn foo { }`, 129 `set edit:completion:arg-completer[foo] = {|@args| 130 echo 1val 131 echo 2val 132 }`) 133 134 feedInput(f.TTYCtrl, "foo foo1 foo2 \t") 135 f.TestTTY(t, 136 "~> foo foo1 foo2 1val\n", Styles, 137 " vvv ____", 138 " COMPLETING argument ", Styles, 139 "********************* ", term.DotHere, "\n", 140 "1val 2val", Styles, 141 "++++ ", 142 ) 143 } 144 145 func TestCompleteSudo(t *testing.T) { 146 f := setup(t) 147 148 evals(f.Evaler, 149 `fn foo { }`, 150 `set edit:completion:arg-completer[foo] = {|@args| 151 echo val1 152 echo val2 153 }`, 154 `var @cands = (edit:complete-sudo sudo foo '')`) 155 testGlobal(t, f.Evaler, "cands", vals.MakeList("val1", "val2")) 156 } 157 158 func TestCompletionMatcher(t *testing.T) { 159 f := setup(t) 160 161 testutil.ApplyDir(testutil.Dir{"foo": "", "oof": ""}) 162 163 evals(f.Evaler, `set edit:completion:matcher[''] = $edit:match-substr~`) 164 feedInput(f.TTYCtrl, "echo f\t") 165 f.TestTTY(t, 166 "~> echo foo \n", Styles, 167 " vvvv ____", 168 " COMPLETING argument ", Styles, 169 "********************* ", term.DotHere, "\n", 170 "foo oof", Styles, 171 "+++ ", 172 ) 173 } 174 175 func TestBuiltinMatchers(t *testing.T) { 176 f := setup(t) 177 178 evals(f.Evaler, 179 `var @prefix = (edit:match-prefix ab [ab abc cab acb ba [ab] [a b] [b a]])`, 180 `var @substr = (edit:match-substr ab [ab abc cab acb ba [ab] [a b] [b a]])`, 181 `var @subseq = (edit:match-subseq ab [ab abc cab acb ba [ab] [a b] [b a]])`, 182 ) 183 testGlobals(t, f.Evaler, map[string]any{ 184 "prefix": vals.MakeList(true, true, false, false, false, false, false, false), 185 "substr": vals.MakeList(true, true, true, false, false, true, false, false), 186 "subseq": vals.MakeList(true, true, true, true, false, true, true, false), 187 }) 188 189 testThatOutputErrorIsBubbled(t, f, "edit:match-prefix ab [ab]") 190 } 191 192 func TestBuiltinMatchers_Options(t *testing.T) { 193 f := setup(t) 194 195 // The two options work identically on all the builtin matchers, so we only 196 // test for match-prefix for simplicity. 197 evals(f.Evaler, 198 `var @a = (edit:match-prefix &ignore-case ab [abc aBc AbC])`, 199 `var @b = (edit:match-prefix &ignore-case aB [abc aBc AbC])`, 200 `var @c = (edit:match-prefix &smart-case ab [abc aBc Abc])`, 201 `var @d = (edit:match-prefix &smart-case aB [abc aBc AbC])`, 202 ) 203 testGlobals(t, f.Evaler, map[string]any{ 204 "a": vals.MakeList(true, true, true), 205 "b": vals.MakeList(true, true, true), 206 "c": vals.MakeList(true, true, true), 207 "d": vals.MakeList(false, true, false), 208 }) 209 210 testThatOutputErrorIsBubbled(t, f, "edit:match-prefix &ignore-case ab [ab]") 211 }