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  }