github.com/elves/elvish@v0.15.0/pkg/edit/builtins_test.go (about)

     1  package edit
     2  
     3  import (
     4  	"io"
     5  	"testing"
     6  
     7  	"github.com/elves/elvish/pkg/cli"
     8  	"github.com/elves/elvish/pkg/cli/term"
     9  	"github.com/elves/elvish/pkg/eval/vals"
    10  	"github.com/elves/elvish/pkg/tt"
    11  	"github.com/elves/elvish/pkg/ui"
    12  )
    13  
    14  func TestBindingTable(t *testing.T) {
    15  	f := setup()
    16  	defer f.Cleanup()
    17  
    18  	evals(f.Evaler, `called = $false`)
    19  	evals(f.Evaler, `m = (edit:binding-table [&a={ called = $true }])`)
    20  	_, ok := getGlobal(f.Evaler, "m").(BindingMap)
    21  	if !ok {
    22  		t.Errorf("edit:binding-table did not create BindingMap variable")
    23  	}
    24  }
    25  
    26  func TestCloseListing(t *testing.T) {
    27  	f := setup()
    28  	defer f.Cleanup()
    29  
    30  	f.Editor.app.MutateState(func(s *cli.State) { s.Addon = cli.Empty{} })
    31  	evals(f.Evaler, `edit:close-listing`)
    32  
    33  	if listing := f.Editor.app.CopyState().Addon; listing != nil {
    34  		t.Errorf("got listing %v, want nil", listing)
    35  	}
    36  }
    37  
    38  func TestDumpBuf(t *testing.T) {
    39  	f := setup()
    40  	defer f.Cleanup()
    41  
    42  	feedInput(f.TTYCtrl, "echo")
    43  	// Wait until the buffer we want has shown up.
    44  	f.TestTTY(t,
    45  		"~> echo", Styles,
    46  		"   vvvv", term.DotHere,
    47  	)
    48  
    49  	evals(f.Evaler, `html = (edit:-dump-buf)`)
    50  	testGlobal(t, f.Evaler,
    51  		"html",
    52  		`~&gt; <span class="sgr-32">echo</span>`+"\n")
    53  }
    54  
    55  func TestInsertRaw(t *testing.T) {
    56  	f := setup()
    57  	defer f.Cleanup()
    58  
    59  	f.TTYCtrl.Inject(term.K('V', ui.Ctrl))
    60  	wantBuf := f.MakeBuffer(
    61  		"~> ", term.DotHere, "\n",
    62  		" RAW ", Styles,
    63  		"*****",
    64  	)
    65  	f.TTYCtrl.TestBuffer(t, wantBuf)
    66  	// Since we do not use real terminals in the test, we cannot have a
    67  	// realistic test case against actual raw inputs. However, we can still
    68  	// check that the builtin command does call the SetRawInput method with 1.
    69  	if raw := f.TTYCtrl.RawInput(); raw != 1 {
    70  		t.Errorf("RawInput() -> %d, want 1", raw)
    71  	}
    72  
    73  	// Raw mode does not respond to non-key events.
    74  	f.TTYCtrl.Inject(term.MouseEvent{})
    75  	f.TTYCtrl.TestBuffer(t, wantBuf)
    76  
    77  	// Raw mode is dismissed after a single key event.
    78  	f.TTYCtrl.Inject(term.K('+'))
    79  	f.TestTTY(t,
    80  		"~> +", Styles,
    81  		"   v", term.DotHere,
    82  	)
    83  }
    84  
    85  func TestEndOfHistory(t *testing.T) {
    86  	f := setup()
    87  	defer f.Cleanup()
    88  
    89  	evals(f.Evaler, `edit:end-of-history`)
    90  	f.TestTTYNotes(t, "End of history")
    91  }
    92  
    93  func TestKey(t *testing.T) {
    94  	f := setup()
    95  	defer f.Cleanup()
    96  
    97  	evals(f.Evaler, `k = (edit:key a)`)
    98  	wantK := ui.K('a')
    99  	if k, _ := f.Evaler.Global().Index("k"); k != wantK {
   100  		t.Errorf("$k is %v, want %v", k, wantK)
   101  	}
   102  }
   103  
   104  func TestRedraw(t *testing.T) {
   105  	f := setup()
   106  	defer f.Cleanup()
   107  
   108  	evals(f.Evaler,
   109  		`edit:current-command = echo`,
   110  		`edit:redraw`)
   111  	f.TestTTY(t,
   112  		"~> echo", Styles,
   113  		"   vvvv", term.DotHere)
   114  	evals(f.Evaler, `edit:redraw &full=$true`)
   115  	// TODO(xiaq): Test that this is actually a full redraw.
   116  	f.TestTTY(t,
   117  		"~> echo", Styles,
   118  		"   vvvv", term.DotHere)
   119  }
   120  
   121  func TestReturnCode(t *testing.T) {
   122  	f := setup()
   123  	defer f.Cleanup()
   124  
   125  	f.Editor.app.CodeArea().MutateState(func(s *cli.CodeAreaState) {
   126  		s.Buffer.Content = "test code"
   127  	})
   128  	evals(f.Evaler, `edit:return-line`)
   129  	code, err := f.Wait()
   130  	if code != "test code" {
   131  		t.Errorf("got code %q, want %q", code, "test code")
   132  	}
   133  	if err != nil {
   134  		t.Errorf("got err %v, want nil", err)
   135  	}
   136  }
   137  
   138  func TestReturnEOF(t *testing.T) {
   139  	f := setup()
   140  	defer f.Cleanup()
   141  
   142  	evals(f.Evaler, `edit:return-eof`)
   143  	if _, err := f.Wait(); err != io.EOF {
   144  		t.Errorf("got err %v, want %v", err, io.EOF)
   145  	}
   146  }
   147  
   148  func TestSmartEnter_InsertsNewlineWhenIncomplete(t *testing.T) {
   149  	f := setup()
   150  	defer f.Cleanup()
   151  
   152  	cli.SetCodeBuffer(f.Editor.app, cli.CodeBuffer{Content: "put [", Dot: 5})
   153  	evals(f.Evaler, `edit:smart-enter`)
   154  	wantBuf := cli.CodeBuffer{Content: "put [\n", Dot: 6}
   155  	if buf := cli.GetCodeBuffer(f.Editor.app); buf != wantBuf {
   156  		t.Errorf("got code buffer %v, want %v", buf, wantBuf)
   157  	}
   158  }
   159  
   160  func TestSmartEnter_AcceptsCodeWhenWholeBufferIsComplete(t *testing.T) {
   161  	f := setup()
   162  	defer f.Cleanup()
   163  
   164  	cli.SetCodeBuffer(f.Editor.app, cli.CodeBuffer{Content: "put []", Dot: 5})
   165  	evals(f.Evaler, `edit:smart-enter`)
   166  	wantCode := "put []"
   167  	if code, _ := f.Wait(); code != wantCode {
   168  		t.Errorf("got return code %q, want %q", code, wantCode)
   169  	}
   170  }
   171  
   172  func TestWordify(t *testing.T) {
   173  	f := setup()
   174  	defer f.Cleanup()
   175  
   176  	evals(f.Evaler, `@words = (edit:wordify 'ls str [list]')`)
   177  	wantWords := vals.MakeList("ls", "str", "[list]")
   178  	if words, _ := f.Evaler.Global().Index("words"); !vals.Equal(words, wantWords) {
   179  		t.Errorf("$words is %v, want %v", words, wantWords)
   180  	}
   181  }
   182  
   183  var bufferBuiltinsTests = []struct {
   184  	name      string
   185  	bufBefore cli.CodeBuffer
   186  	bufAfter  cli.CodeBuffer
   187  }{
   188  	{
   189  		"move-dot-left",
   190  		cli.CodeBuffer{Content: "ab", Dot: 1},
   191  		cli.CodeBuffer{Content: "ab", Dot: 0},
   192  	},
   193  	{
   194  		"move-dot-right",
   195  		cli.CodeBuffer{Content: "ab", Dot: 1},
   196  		cli.CodeBuffer{Content: "ab", Dot: 2},
   197  	},
   198  	{
   199  		"kill-rune-left",
   200  		cli.CodeBuffer{Content: "ab", Dot: 1},
   201  		cli.CodeBuffer{Content: "b", Dot: 0},
   202  	},
   203  	{
   204  		"kill-rune-right",
   205  		cli.CodeBuffer{Content: "ab", Dot: 1},
   206  		cli.CodeBuffer{Content: "a", Dot: 1},
   207  	},
   208  }
   209  
   210  func TestBufferBuiltins(t *testing.T) {
   211  	f := setup()
   212  	app := f.Editor.app
   213  	defer f.Cleanup()
   214  
   215  	for _, test := range bufferBuiltinsTests {
   216  		t.Run(test.name, func(t *testing.T) {
   217  			app.CodeArea().MutateState(func(s *cli.CodeAreaState) {
   218  				s.Buffer = test.bufBefore
   219  			})
   220  			evals(f.Evaler, "edit:"+test.name)
   221  			if buf := app.CodeArea().CopyState().Buffer; buf != test.bufAfter {
   222  				t.Errorf("got buf %v, want %v", buf, test.bufAfter)
   223  			}
   224  		})
   225  	}
   226  }
   227  
   228  // Tests for pure movers.
   229  
   230  func TestMoveDotLeftRight(t *testing.T) {
   231  	tt.Test(t, tt.Fn("moveDotLeft", moveDotLeft), tt.Table{
   232  		tt.Args("foo", 0).Rets(0),
   233  		tt.Args("bar", 3).Rets(2),
   234  		tt.Args("精灵", 0).Rets(0),
   235  		tt.Args("精灵", 3).Rets(0),
   236  		tt.Args("精灵", 6).Rets(3),
   237  	})
   238  	tt.Test(t, tt.Fn("moveDotRight", moveDotRight), tt.Table{
   239  		tt.Args("foo", 0).Rets(1),
   240  		tt.Args("bar", 3).Rets(3),
   241  		tt.Args("精灵", 0).Rets(3),
   242  		tt.Args("精灵", 3).Rets(6),
   243  		tt.Args("精灵", 6).Rets(6),
   244  	})
   245  }
   246  
   247  func TestMoveDotSOLEOL(t *testing.T) {
   248  	buffer := "abc\ndef"
   249  	// Index:
   250  	//         012 34567
   251  	tt.Test(t, tt.Fn("moveDotSOL", moveDotSOL), tt.Table{
   252  		tt.Args(buffer, 0).Rets(0),
   253  		tt.Args(buffer, 1).Rets(0),
   254  		tt.Args(buffer, 2).Rets(0),
   255  		tt.Args(buffer, 3).Rets(0),
   256  		tt.Args(buffer, 4).Rets(4),
   257  		tt.Args(buffer, 5).Rets(4),
   258  		tt.Args(buffer, 6).Rets(4),
   259  		tt.Args(buffer, 7).Rets(4),
   260  	})
   261  	tt.Test(t, tt.Fn("moveDotEOL", moveDotEOL), tt.Table{
   262  		tt.Args(buffer, 0).Rets(3),
   263  		tt.Args(buffer, 1).Rets(3),
   264  		tt.Args(buffer, 2).Rets(3),
   265  		tt.Args(buffer, 3).Rets(3),
   266  		tt.Args(buffer, 4).Rets(7),
   267  		tt.Args(buffer, 5).Rets(7),
   268  		tt.Args(buffer, 6).Rets(7),
   269  		tt.Args(buffer, 7).Rets(7),
   270  	})
   271  }
   272  
   273  func TestMoveDotUpDown(t *testing.T) {
   274  	buffer := "abc\n精灵语\ndef"
   275  	// Index:
   276  	//         012 34 7 0  34567
   277  	// + 10 *  0        1
   278  
   279  	tt.Test(t, tt.Fn("moveDotUp", moveDotUp), tt.Table{
   280  		tt.Args(buffer, 0).Rets(0),  // a -> a
   281  		tt.Args(buffer, 1).Rets(1),  // b -> b
   282  		tt.Args(buffer, 2).Rets(2),  // c -> c
   283  		tt.Args(buffer, 3).Rets(3),  // EOL1 -> EOL1
   284  		tt.Args(buffer, 4).Rets(0),  // 精 -> a
   285  		tt.Args(buffer, 7).Rets(2),  // 灵 -> c
   286  		tt.Args(buffer, 10).Rets(3), // 语 -> EOL1
   287  		tt.Args(buffer, 13).Rets(3), // EOL2 -> EOL1
   288  		tt.Args(buffer, 14).Rets(4), // d -> 精
   289  		tt.Args(buffer, 15).Rets(4), // e -> 精 (jump left half width)
   290  		tt.Args(buffer, 16).Rets(7), // f -> 灵
   291  		tt.Args(buffer, 17).Rets(7), // EOL3 -> 灵 (jump left half width)
   292  	})
   293  
   294  	tt.Test(t, tt.Fn("moveDotDown", moveDotDown), tt.Table{
   295  		tt.Args(buffer, 0).Rets(4),   // a -> 精
   296  		tt.Args(buffer, 1).Rets(4),   // b -> 精 (jump left half width)
   297  		tt.Args(buffer, 2).Rets(7),   // c -> 灵
   298  		tt.Args(buffer, 3).Rets(7),   // EOL1 -> 灵 (jump left half width)
   299  		tt.Args(buffer, 4).Rets(14),  // 精 -> d
   300  		tt.Args(buffer, 7).Rets(16),  // 灵 -> f
   301  		tt.Args(buffer, 10).Rets(17), // 语 -> EOL3
   302  		tt.Args(buffer, 13).Rets(17), // EOL2 -> EOL3
   303  		tt.Args(buffer, 14).Rets(14), // d -> d
   304  		tt.Args(buffer, 15).Rets(15), // e -> e
   305  		tt.Args(buffer, 16).Rets(16), // f -> f
   306  		tt.Args(buffer, 17).Rets(17), // EOL3 -> EOL3
   307  	})
   308  }
   309  
   310  // Word movement tests.
   311  
   312  // The string below is carefully chosen to test all word, small-word, and
   313  // alnum-word move/kill functions, because it contains features to set the
   314  // different movement behaviors apart.
   315  //
   316  // The string is annotated with carets (^) to indicate the beginning of words,
   317  // and periods (.) to indicate trailing runes of words. Indices are also
   318  // annotated.
   319  //
   320  //   cd ~/downloads; rm -rf 2018aug07-pics/*;
   321  //   ^. ^........... ^. ^.. ^................  (word)
   322  //   ^. ^.^........^ ^. ^^. ^........^^...^..  (small-word)
   323  //   ^.   ^........  ^.  ^. ^........ ^...     (alnum-word)
   324  //   01234567890123456789012345678901234567890
   325  //   0         1         2         3         4
   326  //
   327  //   word boundaries:         0 3      16 19    23
   328  //   small-word boundaries:   0 3 5 14 16 19 20 23 32 33 37
   329  //   alnum-word boundaries:   0   5    16    20 23    33
   330  //
   331  var wordMoveTestBuffer = "cd ~/downloads; rm -rf 2018aug07-pics/*;"
   332  
   333  var (
   334  	// word boundaries: 0 3 16 19 23
   335  	moveDotLeftWordTests = tt.Table{
   336  		tt.Args(wordMoveTestBuffer, 0).Rets(0),
   337  		tt.Args(wordMoveTestBuffer, 1).Rets(0),
   338  		tt.Args(wordMoveTestBuffer, 2).Rets(0),
   339  		tt.Args(wordMoveTestBuffer, 3).Rets(0),
   340  		tt.Args(wordMoveTestBuffer, 4).Rets(3),
   341  		tt.Args(wordMoveTestBuffer, 16).Rets(3),
   342  		tt.Args(wordMoveTestBuffer, 19).Rets(16),
   343  		tt.Args(wordMoveTestBuffer, 23).Rets(19),
   344  		tt.Args(wordMoveTestBuffer, 40).Rets(23),
   345  	}
   346  	moveDotRightWordTests = tt.Table{
   347  		tt.Args(wordMoveTestBuffer, 0).Rets(3),
   348  		tt.Args(wordMoveTestBuffer, 1).Rets(3),
   349  		tt.Args(wordMoveTestBuffer, 2).Rets(3),
   350  		tt.Args(wordMoveTestBuffer, 3).Rets(16),
   351  		tt.Args(wordMoveTestBuffer, 16).Rets(19),
   352  		tt.Args(wordMoveTestBuffer, 19).Rets(23),
   353  		tt.Args(wordMoveTestBuffer, 23).Rets(40),
   354  	}
   355  
   356  	// small-word boundaries: 0 3 5 14 16 19 20 23 32 33 37
   357  	moveDotLeftSmallWordTests = tt.Table{
   358  		tt.Args(wordMoveTestBuffer, 0).Rets(0),
   359  		tt.Args(wordMoveTestBuffer, 1).Rets(0),
   360  		tt.Args(wordMoveTestBuffer, 2).Rets(0),
   361  		tt.Args(wordMoveTestBuffer, 3).Rets(0),
   362  		tt.Args(wordMoveTestBuffer, 4).Rets(3),
   363  		tt.Args(wordMoveTestBuffer, 5).Rets(3),
   364  		tt.Args(wordMoveTestBuffer, 14).Rets(5),
   365  		tt.Args(wordMoveTestBuffer, 16).Rets(14),
   366  		tt.Args(wordMoveTestBuffer, 19).Rets(16),
   367  		tt.Args(wordMoveTestBuffer, 20).Rets(19),
   368  		tt.Args(wordMoveTestBuffer, 23).Rets(20),
   369  		tt.Args(wordMoveTestBuffer, 32).Rets(23),
   370  		tt.Args(wordMoveTestBuffer, 33).Rets(32),
   371  		tt.Args(wordMoveTestBuffer, 37).Rets(33),
   372  		tt.Args(wordMoveTestBuffer, 40).Rets(37),
   373  	}
   374  	moveDotRightSmallWordTests = tt.Table{
   375  		tt.Args(wordMoveTestBuffer, 0).Rets(3),
   376  		tt.Args(wordMoveTestBuffer, 1).Rets(3),
   377  		tt.Args(wordMoveTestBuffer, 2).Rets(3),
   378  		tt.Args(wordMoveTestBuffer, 3).Rets(5),
   379  		tt.Args(wordMoveTestBuffer, 5).Rets(14),
   380  		tt.Args(wordMoveTestBuffer, 14).Rets(16),
   381  		tt.Args(wordMoveTestBuffer, 16).Rets(19),
   382  		tt.Args(wordMoveTestBuffer, 19).Rets(20),
   383  		tt.Args(wordMoveTestBuffer, 20).Rets(23),
   384  		tt.Args(wordMoveTestBuffer, 23).Rets(32),
   385  		tt.Args(wordMoveTestBuffer, 32).Rets(33),
   386  		tt.Args(wordMoveTestBuffer, 33).Rets(37),
   387  		tt.Args(wordMoveTestBuffer, 37).Rets(40),
   388  	}
   389  
   390  	// alnum-word boundaries: 0 5 16 20 23 33
   391  	moveDotLeftAlnumWordTests = tt.Table{
   392  		tt.Args(wordMoveTestBuffer, 0).Rets(0),
   393  		tt.Args(wordMoveTestBuffer, 1).Rets(0),
   394  		tt.Args(wordMoveTestBuffer, 2).Rets(0),
   395  		tt.Args(wordMoveTestBuffer, 3).Rets(0),
   396  		tt.Args(wordMoveTestBuffer, 4).Rets(0),
   397  		tt.Args(wordMoveTestBuffer, 5).Rets(0),
   398  		tt.Args(wordMoveTestBuffer, 6).Rets(5),
   399  		tt.Args(wordMoveTestBuffer, 16).Rets(5),
   400  		tt.Args(wordMoveTestBuffer, 20).Rets(16),
   401  		tt.Args(wordMoveTestBuffer, 23).Rets(20),
   402  		tt.Args(wordMoveTestBuffer, 33).Rets(23),
   403  		tt.Args(wordMoveTestBuffer, 40).Rets(33),
   404  	}
   405  	moveDotRightAlnumWordTests = tt.Table{
   406  		tt.Args(wordMoveTestBuffer, 0).Rets(5),
   407  		tt.Args(wordMoveTestBuffer, 1).Rets(5),
   408  		tt.Args(wordMoveTestBuffer, 2).Rets(5),
   409  		tt.Args(wordMoveTestBuffer, 3).Rets(5),
   410  		tt.Args(wordMoveTestBuffer, 4).Rets(5),
   411  		tt.Args(wordMoveTestBuffer, 5).Rets(16),
   412  		tt.Args(wordMoveTestBuffer, 16).Rets(20),
   413  		tt.Args(wordMoveTestBuffer, 20).Rets(23),
   414  		tt.Args(wordMoveTestBuffer, 23).Rets(33),
   415  		tt.Args(wordMoveTestBuffer, 33).Rets(40),
   416  	}
   417  )
   418  
   419  func TestMoveDotWord(t *testing.T) {
   420  	tt.Test(t, tt.Fn("moveDotLeftWord", moveDotLeftWord), moveDotLeftWordTests)
   421  	tt.Test(t, tt.Fn("moveDotRightWord", moveDotRightWord), moveDotRightWordTests)
   422  }
   423  
   424  func TestMoveDotSmallWord(t *testing.T) {
   425  	tt.Test(t,
   426  		tt.Fn("moveDotLeftSmallWord", moveDotLeftSmallWord),
   427  		moveDotLeftSmallWordTests,
   428  	)
   429  	tt.Test(t,
   430  		tt.Fn("moveDotRightSmallWord", moveDotRightSmallWord),
   431  		moveDotRightSmallWordTests,
   432  	)
   433  }
   434  
   435  func TestMoveDotAlnumWord(t *testing.T) {
   436  	tt.Test(t,
   437  		tt.Fn("moveDotLeftAlnumWord", moveDotLeftAlnumWord),
   438  		moveDotLeftAlnumWordTests,
   439  	)
   440  	tt.Test(t,
   441  		tt.Fn("moveDotRightAlnumWord", moveDotRightAlnumWord),
   442  		moveDotRightAlnumWordTests,
   443  	)
   444  }