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 `~> <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 }