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