github.com/kolbycrouch/elvish@v0.14.1-0.20210614162631-215b9ac1c423/pkg/edit/builtins_test.go (about)

     1  package edit
     2  
     3  import (
     4  	"io"
     5  	"testing"
     6  
     7  	"src.elv.sh/pkg/cli/term"
     8  	"src.elv.sh/pkg/cli/tk"
     9  	"src.elv.sh/pkg/eval/vals"
    10  	"src.elv.sh/pkg/tt"
    11  	"src.elv.sh/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").(bindingsMap)
    21  	if !ok {
    22  		t.Errorf("edit:binding-table did not create BindingMap variable")
    23  	}
    24  }
    25  
    26  func TestCloseMode(t *testing.T) {
    27  	f := setup()
    28  	defer f.Cleanup()
    29  
    30  	f.Editor.app.SetAddon(tk.Empty{}, false)
    31  	evals(f.Evaler, `edit:close-mode`)
    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 TestClear(t *testing.T) {
   122  	f := setup()
   123  	defer f.Cleanup()
   124  
   125  	evals(f.Evaler, `edit:current-command = echo`, `edit:clear`)
   126  	f.TestTTY(t,
   127  		"~> echo", Styles,
   128  		"   vvvv", term.DotHere)
   129  	if cleared := f.TTYCtrl.ScreenCleared(); cleared != 1 {
   130  		t.Errorf("screen cleared %v times, want 1", cleared)
   131  	}
   132  }
   133  
   134  func TestReturnCode(t *testing.T) {
   135  	f := setup()
   136  	defer f.Cleanup()
   137  
   138  	f.Editor.app.CodeArea().MutateState(func(s *tk.CodeAreaState) {
   139  		s.Buffer.Content = "test code"
   140  	})
   141  	evals(f.Evaler, `edit:return-line`)
   142  	code, err := f.Wait()
   143  	if code != "test code" {
   144  		t.Errorf("got code %q, want %q", code, "test code")
   145  	}
   146  	if err != nil {
   147  		t.Errorf("got err %v, want nil", err)
   148  	}
   149  }
   150  
   151  func TestReturnEOF(t *testing.T) {
   152  	f := setup()
   153  	defer f.Cleanup()
   154  
   155  	evals(f.Evaler, `edit:return-eof`)
   156  	if _, err := f.Wait(); err != io.EOF {
   157  		t.Errorf("got err %v, want %v", err, io.EOF)
   158  	}
   159  }
   160  
   161  func TestSmartEnter_InsertsNewlineWhenIncomplete(t *testing.T) {
   162  	f := setup()
   163  	defer f.Cleanup()
   164  
   165  	f.SetCodeBuffer(tk.CodeBuffer{Content: "put [", Dot: 5})
   166  	evals(f.Evaler, `edit:smart-enter`)
   167  	wantBuf := tk.CodeBuffer{Content: "put [\n", Dot: 6}
   168  	if buf := f.Editor.app.CodeArea().CopyState().Buffer; buf != wantBuf {
   169  		t.Errorf("got code buffer %v, want %v", buf, wantBuf)
   170  	}
   171  }
   172  
   173  func TestSmartEnter_AcceptsCodeWhenWholeBufferIsComplete(t *testing.T) {
   174  	f := setup()
   175  	defer f.Cleanup()
   176  
   177  	f.SetCodeBuffer(tk.CodeBuffer{Content: "put []", Dot: 5})
   178  	evals(f.Evaler, `edit:smart-enter`)
   179  	wantCode := "put []"
   180  	if code, _ := f.Wait(); code != wantCode {
   181  		t.Errorf("got return code %q, want %q", code, wantCode)
   182  	}
   183  }
   184  
   185  func TestWordify(t *testing.T) {
   186  	f := setup()
   187  	defer f.Cleanup()
   188  
   189  	evals(f.Evaler, `@words = (edit:wordify 'ls str [list]')`)
   190  	wantWords := vals.MakeList("ls", "str", "[list]")
   191  	if words, _ := f.Evaler.Global().Index("words"); !vals.Equal(words, wantWords) {
   192  		t.Errorf("$words is %v, want %v", words, wantWords)
   193  	}
   194  }
   195  
   196  var bufferBuiltinsTests = []struct {
   197  	name      string
   198  	bufBefore tk.CodeBuffer
   199  	bufAfter  tk.CodeBuffer
   200  }{
   201  	{
   202  		"move-dot-left",
   203  		tk.CodeBuffer{Content: "ab", Dot: 1},
   204  		tk.CodeBuffer{Content: "ab", Dot: 0},
   205  	},
   206  	{
   207  		"move-dot-right",
   208  		tk.CodeBuffer{Content: "ab", Dot: 1},
   209  		tk.CodeBuffer{Content: "ab", Dot: 2},
   210  	},
   211  	{
   212  		"kill-rune-left",
   213  		tk.CodeBuffer{Content: "ab", Dot: 1},
   214  		tk.CodeBuffer{Content: "b", Dot: 0},
   215  	},
   216  	{
   217  		"kill-rune-right",
   218  		tk.CodeBuffer{Content: "ab", Dot: 1},
   219  		tk.CodeBuffer{Content: "a", Dot: 1},
   220  	},
   221  }
   222  
   223  func TestBufferBuiltins(t *testing.T) {
   224  	f := setup()
   225  	app := f.Editor.app
   226  	defer f.Cleanup()
   227  
   228  	for _, test := range bufferBuiltinsTests {
   229  		t.Run(test.name, func(t *testing.T) {
   230  			app.CodeArea().MutateState(func(s *tk.CodeAreaState) {
   231  				s.Buffer = test.bufBefore
   232  			})
   233  			evals(f.Evaler, "edit:"+test.name)
   234  			if buf := app.CodeArea().CopyState().Buffer; buf != test.bufAfter {
   235  				t.Errorf("got buf %v, want %v", buf, test.bufAfter)
   236  			}
   237  		})
   238  	}
   239  }
   240  
   241  // Tests for pure movers.
   242  
   243  func TestMoveDotLeftRight(t *testing.T) {
   244  	tt.Test(t, tt.Fn("moveDotLeft", moveDotLeft), tt.Table{
   245  		tt.Args("foo", 0).Rets(0),
   246  		tt.Args("bar", 3).Rets(2),
   247  		tt.Args("精灵", 0).Rets(0),
   248  		tt.Args("精灵", 3).Rets(0),
   249  		tt.Args("精灵", 6).Rets(3),
   250  	})
   251  	tt.Test(t, tt.Fn("moveDotRight", moveDotRight), tt.Table{
   252  		tt.Args("foo", 0).Rets(1),
   253  		tt.Args("bar", 3).Rets(3),
   254  		tt.Args("精灵", 0).Rets(3),
   255  		tt.Args("精灵", 3).Rets(6),
   256  		tt.Args("精灵", 6).Rets(6),
   257  	})
   258  }
   259  
   260  func TestMoveDotSOLEOL(t *testing.T) {
   261  	buffer := "abc\ndef"
   262  	// Index:
   263  	//         012 34567
   264  	tt.Test(t, tt.Fn("moveDotSOL", moveDotSOL), tt.Table{
   265  		tt.Args(buffer, 0).Rets(0),
   266  		tt.Args(buffer, 1).Rets(0),
   267  		tt.Args(buffer, 2).Rets(0),
   268  		tt.Args(buffer, 3).Rets(0),
   269  		tt.Args(buffer, 4).Rets(4),
   270  		tt.Args(buffer, 5).Rets(4),
   271  		tt.Args(buffer, 6).Rets(4),
   272  		tt.Args(buffer, 7).Rets(4),
   273  	})
   274  	tt.Test(t, tt.Fn("moveDotEOL", moveDotEOL), tt.Table{
   275  		tt.Args(buffer, 0).Rets(3),
   276  		tt.Args(buffer, 1).Rets(3),
   277  		tt.Args(buffer, 2).Rets(3),
   278  		tt.Args(buffer, 3).Rets(3),
   279  		tt.Args(buffer, 4).Rets(7),
   280  		tt.Args(buffer, 5).Rets(7),
   281  		tt.Args(buffer, 6).Rets(7),
   282  		tt.Args(buffer, 7).Rets(7),
   283  	})
   284  }
   285  
   286  func TestMoveDotUpDown(t *testing.T) {
   287  	buffer := "abc\n精灵语\ndef"
   288  	// Index:
   289  	//         012 34 7 0  34567
   290  	// + 10 *  0        1
   291  
   292  	tt.Test(t, tt.Fn("moveDotUp", moveDotUp), tt.Table{
   293  		tt.Args(buffer, 0).Rets(0),  // a -> a
   294  		tt.Args(buffer, 1).Rets(1),  // b -> b
   295  		tt.Args(buffer, 2).Rets(2),  // c -> c
   296  		tt.Args(buffer, 3).Rets(3),  // EOL1 -> EOL1
   297  		tt.Args(buffer, 4).Rets(0),  // 精 -> a
   298  		tt.Args(buffer, 7).Rets(2),  // 灵 -> c
   299  		tt.Args(buffer, 10).Rets(3), // 语 -> EOL1
   300  		tt.Args(buffer, 13).Rets(3), // EOL2 -> EOL1
   301  		tt.Args(buffer, 14).Rets(4), // d -> 精
   302  		tt.Args(buffer, 15).Rets(4), // e -> 精 (jump left half width)
   303  		tt.Args(buffer, 16).Rets(7), // f -> 灵
   304  		tt.Args(buffer, 17).Rets(7), // EOL3 -> 灵 (jump left half width)
   305  	})
   306  
   307  	tt.Test(t, tt.Fn("moveDotDown", moveDotDown), tt.Table{
   308  		tt.Args(buffer, 0).Rets(4),   // a -> 精
   309  		tt.Args(buffer, 1).Rets(4),   // b -> 精 (jump left half width)
   310  		tt.Args(buffer, 2).Rets(7),   // c -> 灵
   311  		tt.Args(buffer, 3).Rets(7),   // EOL1 -> 灵 (jump left half width)
   312  		tt.Args(buffer, 4).Rets(14),  // 精 -> d
   313  		tt.Args(buffer, 7).Rets(16),  // 灵 -> f
   314  		tt.Args(buffer, 10).Rets(17), // 语 -> EOL3
   315  		tt.Args(buffer, 13).Rets(17), // EOL2 -> EOL3
   316  		tt.Args(buffer, 14).Rets(14), // d -> d
   317  		tt.Args(buffer, 15).Rets(15), // e -> e
   318  		tt.Args(buffer, 16).Rets(16), // f -> f
   319  		tt.Args(buffer, 17).Rets(17), // EOL3 -> EOL3
   320  	})
   321  }
   322  
   323  // Word movement tests.
   324  
   325  // The string below is carefully chosen to test all word, small-word, and
   326  // alnum-word move/kill functions, because it contains features to set the
   327  // different movement behaviors apart.
   328  //
   329  // The string is annotated with carets (^) to indicate the beginning of words,
   330  // and periods (.) to indicate trailing runes of words. Indices are also
   331  // annotated.
   332  //
   333  //   cd ~/downloads; rm -rf 2018aug07-pics/*;
   334  //   ^. ^........... ^. ^.. ^................  (word)
   335  //   ^. ^.^........^ ^. ^^. ^........^^...^..  (small-word)
   336  //   ^.   ^........  ^.  ^. ^........ ^...     (alnum-word)
   337  //   01234567890123456789012345678901234567890
   338  //   0         1         2         3         4
   339  //
   340  //   word boundaries:         0 3      16 19    23
   341  //   small-word boundaries:   0 3 5 14 16 19 20 23 32 33 37
   342  //   alnum-word boundaries:   0   5    16    20 23    33
   343  //
   344  var wordMoveTestBuffer = "cd ~/downloads; rm -rf 2018aug07-pics/*;"
   345  
   346  var (
   347  	// word boundaries: 0 3 16 19 23
   348  	moveDotLeftWordTests = tt.Table{
   349  		tt.Args(wordMoveTestBuffer, 0).Rets(0),
   350  		tt.Args(wordMoveTestBuffer, 1).Rets(0),
   351  		tt.Args(wordMoveTestBuffer, 2).Rets(0),
   352  		tt.Args(wordMoveTestBuffer, 3).Rets(0),
   353  		tt.Args(wordMoveTestBuffer, 4).Rets(3),
   354  		tt.Args(wordMoveTestBuffer, 16).Rets(3),
   355  		tt.Args(wordMoveTestBuffer, 19).Rets(16),
   356  		tt.Args(wordMoveTestBuffer, 23).Rets(19),
   357  		tt.Args(wordMoveTestBuffer, 40).Rets(23),
   358  	}
   359  	moveDotRightWordTests = tt.Table{
   360  		tt.Args(wordMoveTestBuffer, 0).Rets(3),
   361  		tt.Args(wordMoveTestBuffer, 1).Rets(3),
   362  		tt.Args(wordMoveTestBuffer, 2).Rets(3),
   363  		tt.Args(wordMoveTestBuffer, 3).Rets(16),
   364  		tt.Args(wordMoveTestBuffer, 16).Rets(19),
   365  		tt.Args(wordMoveTestBuffer, 19).Rets(23),
   366  		tt.Args(wordMoveTestBuffer, 23).Rets(40),
   367  	}
   368  
   369  	// small-word boundaries: 0 3 5 14 16 19 20 23 32 33 37
   370  	moveDotLeftSmallWordTests = tt.Table{
   371  		tt.Args(wordMoveTestBuffer, 0).Rets(0),
   372  		tt.Args(wordMoveTestBuffer, 1).Rets(0),
   373  		tt.Args(wordMoveTestBuffer, 2).Rets(0),
   374  		tt.Args(wordMoveTestBuffer, 3).Rets(0),
   375  		tt.Args(wordMoveTestBuffer, 4).Rets(3),
   376  		tt.Args(wordMoveTestBuffer, 5).Rets(3),
   377  		tt.Args(wordMoveTestBuffer, 14).Rets(5),
   378  		tt.Args(wordMoveTestBuffer, 16).Rets(14),
   379  		tt.Args(wordMoveTestBuffer, 19).Rets(16),
   380  		tt.Args(wordMoveTestBuffer, 20).Rets(19),
   381  		tt.Args(wordMoveTestBuffer, 23).Rets(20),
   382  		tt.Args(wordMoveTestBuffer, 32).Rets(23),
   383  		tt.Args(wordMoveTestBuffer, 33).Rets(32),
   384  		tt.Args(wordMoveTestBuffer, 37).Rets(33),
   385  		tt.Args(wordMoveTestBuffer, 40).Rets(37),
   386  	}
   387  	moveDotRightSmallWordTests = tt.Table{
   388  		tt.Args(wordMoveTestBuffer, 0).Rets(3),
   389  		tt.Args(wordMoveTestBuffer, 1).Rets(3),
   390  		tt.Args(wordMoveTestBuffer, 2).Rets(3),
   391  		tt.Args(wordMoveTestBuffer, 3).Rets(5),
   392  		tt.Args(wordMoveTestBuffer, 5).Rets(14),
   393  		tt.Args(wordMoveTestBuffer, 14).Rets(16),
   394  		tt.Args(wordMoveTestBuffer, 16).Rets(19),
   395  		tt.Args(wordMoveTestBuffer, 19).Rets(20),
   396  		tt.Args(wordMoveTestBuffer, 20).Rets(23),
   397  		tt.Args(wordMoveTestBuffer, 23).Rets(32),
   398  		tt.Args(wordMoveTestBuffer, 32).Rets(33),
   399  		tt.Args(wordMoveTestBuffer, 33).Rets(37),
   400  		tt.Args(wordMoveTestBuffer, 37).Rets(40),
   401  	}
   402  
   403  	// alnum-word boundaries: 0 5 16 20 23 33
   404  	moveDotLeftAlnumWordTests = tt.Table{
   405  		tt.Args(wordMoveTestBuffer, 0).Rets(0),
   406  		tt.Args(wordMoveTestBuffer, 1).Rets(0),
   407  		tt.Args(wordMoveTestBuffer, 2).Rets(0),
   408  		tt.Args(wordMoveTestBuffer, 3).Rets(0),
   409  		tt.Args(wordMoveTestBuffer, 4).Rets(0),
   410  		tt.Args(wordMoveTestBuffer, 5).Rets(0),
   411  		tt.Args(wordMoveTestBuffer, 6).Rets(5),
   412  		tt.Args(wordMoveTestBuffer, 16).Rets(5),
   413  		tt.Args(wordMoveTestBuffer, 20).Rets(16),
   414  		tt.Args(wordMoveTestBuffer, 23).Rets(20),
   415  		tt.Args(wordMoveTestBuffer, 33).Rets(23),
   416  		tt.Args(wordMoveTestBuffer, 40).Rets(33),
   417  	}
   418  	moveDotRightAlnumWordTests = tt.Table{
   419  		tt.Args(wordMoveTestBuffer, 0).Rets(5),
   420  		tt.Args(wordMoveTestBuffer, 1).Rets(5),
   421  		tt.Args(wordMoveTestBuffer, 2).Rets(5),
   422  		tt.Args(wordMoveTestBuffer, 3).Rets(5),
   423  		tt.Args(wordMoveTestBuffer, 4).Rets(5),
   424  		tt.Args(wordMoveTestBuffer, 5).Rets(16),
   425  		tt.Args(wordMoveTestBuffer, 16).Rets(20),
   426  		tt.Args(wordMoveTestBuffer, 20).Rets(23),
   427  		tt.Args(wordMoveTestBuffer, 23).Rets(33),
   428  		tt.Args(wordMoveTestBuffer, 33).Rets(40),
   429  	}
   430  )
   431  
   432  func TestMoveDotWord(t *testing.T) {
   433  	tt.Test(t, tt.Fn("moveDotLeftWord", moveDotLeftWord), moveDotLeftWordTests)
   434  	tt.Test(t, tt.Fn("moveDotRightWord", moveDotRightWord), moveDotRightWordTests)
   435  }
   436  
   437  func TestMoveDotSmallWord(t *testing.T) {
   438  	tt.Test(t,
   439  		tt.Fn("moveDotLeftSmallWord", moveDotLeftSmallWord),
   440  		moveDotLeftSmallWordTests,
   441  	)
   442  	tt.Test(t,
   443  		tt.Fn("moveDotRightSmallWord", moveDotRightSmallWord),
   444  		moveDotRightSmallWordTests,
   445  	)
   446  }
   447  
   448  func TestMoveDotAlnumWord(t *testing.T) {
   449  	tt.Test(t,
   450  		tt.Fn("moveDotLeftAlnumWord", moveDotLeftAlnumWord),
   451  		moveDotLeftAlnumWordTests,
   452  	)
   453  	tt.Test(t,
   454  		tt.Fn("moveDotRightAlnumWord", moveDotRightAlnumWord),
   455  		moveDotRightAlnumWordTests,
   456  	)
   457  }