comment_spec.lua (23899B)
1 local t = require('test.testutil') 2 local n = require('test.functional.testnvim')() 3 4 local api = n.api 5 local clear = n.clear 6 local eq = t.eq 7 local exec_capture = n.exec_capture 8 local exec_lua = n.exec_lua 9 local feed = n.feed 10 11 -- Reference text 12 -- aa 13 -- aa 14 -- aa 15 -- 16 -- aa 17 -- aa 18 -- aa 19 local example_lines = { 'aa', ' aa', ' aa', '', ' aa', ' aa', 'aa' } 20 21 local set_commentstring = function(commentstring) 22 api.nvim_set_option_value('commentstring', commentstring, { buf = 0 }) 23 end 24 25 local get_lines = function(from, to) 26 from, to = from or 0, to or -1 27 return api.nvim_buf_get_lines(0, from, to, false) 28 end 29 30 local set_lines = function(lines, from, to) 31 from, to = from or 0, to or -1 32 api.nvim_buf_set_lines(0, from, to, false, lines) 33 end 34 35 local set_cursor = function(row, col) 36 api.nvim_win_set_cursor(0, { row, col }) 37 end 38 39 local get_cursor = function() 40 return api.nvim_win_get_cursor(0) 41 end 42 43 local setup_treesitter = function() 44 -- NOTE: This leverages bundled Vimscript and Lua tree-sitter parsers 45 api.nvim_set_option_value('filetype', 'vim', { buf = 0 }) 46 exec_lua('vim.treesitter.start()') 47 end 48 49 before_each(function() 50 -- avoid options, but we still need TS parsers 51 clear({ args_rm = { '--cmd' }, args = { '--clean', '--cmd', n.runtime_set } }) 52 end) 53 54 describe('commenting', function() 55 before_each(function() 56 set_lines(example_lines) 57 set_commentstring('# %s') 58 end) 59 60 describe('toggle_lines()', function() 61 local toggle_lines = function(...) 62 exec_lua('require("vim._comment").toggle_lines(...)', ...) 63 end 64 65 it('works', function() 66 toggle_lines(3, 5) 67 eq(get_lines(2, 5), { ' # aa', ' #', ' # aa' }) 68 69 toggle_lines(3, 5) 70 eq(get_lines(2, 5), { ' aa', '', ' aa' }) 71 end) 72 73 it("works with different 'commentstring' options", function() 74 local validate = function(lines_before, lines_after, lines_again) 75 set_lines(lines_before) 76 toggle_lines(1, #lines_before) 77 eq(get_lines(), lines_after) 78 toggle_lines(1, #lines_before) 79 eq(get_lines(), lines_again or lines_before) 80 end 81 82 -- Single whitespace inside comment parts (main case) 83 set_commentstring('# %s #') 84 -- - General case 85 validate( 86 { 'aa', ' aa', 'aa ', ' aa ' }, 87 { '# aa #', '# aa #', '# aa #', '# aa #' } 88 ) 89 -- - Tabs 90 validate( 91 { 'aa', '\taa', 'aa\t', '\taa\t' }, 92 { '# aa #', '# \taa #', '# aa\t #', '# \taa\t #' } 93 ) 94 -- - With indent 95 validate({ ' aa', ' aa' }, { ' # aa #', ' # aa #' }) 96 -- - With blank/empty lines 97 validate( 98 { ' aa', '', ' ', '\t' }, 99 { ' # aa #', ' ##', ' ##', ' ##' }, 100 { ' aa', '', '', '' } 101 ) 102 103 set_commentstring('# %s') 104 validate({ 'aa', ' aa', 'aa ', ' aa ' }, { '# aa', '# aa', '# aa ', '# aa ' }) 105 validate({ 'aa', '\taa', 'aa\t', '\taa\t' }, { '# aa', '# \taa', '# aa\t', '# \taa\t' }) 106 validate({ ' aa', ' aa' }, { ' # aa', ' # aa' }) 107 validate( 108 { ' aa', '', ' ', '\t' }, 109 { ' # aa', ' #', ' #', ' #' }, 110 { ' aa', '', '', '' } 111 ) 112 113 set_commentstring('%s #') 114 validate({ 'aa', ' aa', 'aa ', ' aa ' }, { 'aa #', ' aa #', 'aa #', ' aa #' }) 115 validate({ 'aa', '\taa', 'aa\t', '\taa\t' }, { 'aa #', '\taa #', 'aa\t #', '\taa\t #' }) 116 validate({ ' aa', ' aa' }, { ' aa #', ' aa #' }) 117 validate( 118 { ' aa', '', ' ', '\t' }, 119 { ' aa #', ' #', ' #', ' #' }, 120 { ' aa', '', '', '' } 121 ) 122 123 -- No whitespace in parts 124 set_commentstring('#%s#') 125 validate({ 'aa', ' aa', 'aa ', ' aa ' }, { '#aa#', '# aa#', '#aa #', '# aa #' }) 126 validate({ 'aa', '\taa', 'aa\t', '\taa\t' }, { '#aa#', '#\taa#', '#aa\t#', '#\taa\t#' }) 127 validate({ ' aa', ' aa' }, { ' #aa#', ' # aa#' }) 128 validate( 129 { ' aa', '', ' ', '\t' }, 130 { ' #aa#', ' ##', ' ##', ' ##' }, 131 { ' aa', '', '', '' } 132 ) 133 134 set_commentstring('#%s') 135 validate({ 'aa', ' aa', 'aa ', ' aa ' }, { '#aa', '# aa', '#aa ', '# aa ' }) 136 validate({ 'aa', '\taa', 'aa\t', '\taa\t' }, { '#aa', '#\taa', '#aa\t', '#\taa\t' }) 137 validate({ ' aa', ' aa' }, { ' #aa', ' # aa' }) 138 validate({ ' aa', '', ' ', '\t' }, { ' #aa', ' #', ' #', ' #' }, { ' aa', '', '', '' }) 139 140 set_commentstring('%s#') 141 validate({ 'aa', ' aa', 'aa ', ' aa ' }, { 'aa#', ' aa#', 'aa #', ' aa #' }) 142 validate({ 'aa', '\taa', 'aa\t', '\taa\t' }, { 'aa#', '\taa#', 'aa\t#', '\taa\t#' }) 143 validate({ ' aa', ' aa' }, { ' aa#', ' aa#' }) 144 validate({ ' aa', '', ' ', '\t' }, { ' aa#', ' #', ' #', ' #' }, { ' aa', '', '', '' }) 145 146 -- Extra whitespace inside comment parts 147 set_commentstring('# %s #') 148 validate( 149 { 'aa', ' aa', 'aa ', ' aa ' }, 150 { '# aa #', '# aa #', '# aa #', '# aa #' } 151 ) 152 validate( 153 { 'aa', '\taa', 'aa\t', '\taa\t' }, 154 { '# aa #', '# \taa #', '# aa\t #', '# \taa\t #' } 155 ) 156 validate({ ' aa', ' aa' }, { ' # aa #', ' # aa #' }) 157 validate( 158 { ' aa', '', ' ', '\t' }, 159 { ' # aa #', ' ##', ' ##', ' ##' }, 160 { ' aa', '', '', '' } 161 ) 162 163 set_commentstring('# %s') 164 validate({ 'aa', ' aa', 'aa ', ' aa ' }, { '# aa', '# aa', '# aa ', '# aa ' }) 165 validate({ 'aa', '\taa', 'aa\t', '\taa\t' }, { '# aa', '# \taa', '# aa\t', '# \taa\t' }) 166 validate({ ' aa', ' aa' }, { ' # aa', ' # aa' }) 167 validate( 168 { ' aa', '', ' ', '\t' }, 169 { ' # aa', ' #', ' #', ' #' }, 170 { ' aa', '', '', '' } 171 ) 172 173 set_commentstring('%s #') 174 validate({ 'aa', ' aa', 'aa ', ' aa ' }, { 'aa #', ' aa #', 'aa #', ' aa #' }) 175 validate({ 'aa', '\taa', 'aa\t', '\taa\t' }, { 'aa #', '\taa #', 'aa\t #', '\taa\t #' }) 176 validate({ ' aa', ' aa' }, { ' aa #', ' aa #' }) 177 validate( 178 { ' aa', '', ' ', '\t' }, 179 { ' aa #', ' #', ' #', ' #' }, 180 { ' aa', '', '', '' } 181 ) 182 183 -- Whitespace outside of comment parts 184 set_commentstring(' # %s # ') 185 validate( 186 { 'aa', ' aa', 'aa ', ' aa ' }, 187 { ' # aa # ', ' # aa # ', ' # aa # ', ' # aa # ' } 188 ) 189 validate( 190 { 'aa', '\taa', 'aa\t', '\taa\t' }, 191 { ' # aa # ', ' # \taa # ', ' # aa\t # ', ' # \taa\t # ' } 192 ) 193 validate({ ' aa', ' aa' }, { ' # aa # ', ' # aa # ' }) 194 validate( 195 { ' aa', '', ' ', '\t' }, 196 { ' # aa # ', ' ##', ' ##', ' ##' }, 197 { ' aa', '', '', '' } 198 ) 199 200 set_commentstring(' # %s ') 201 validate( 202 { 'aa', ' aa', 'aa ', ' aa ' }, 203 { ' # aa ', ' # aa ', ' # aa ', ' # aa ' } 204 ) 205 validate( 206 { 'aa', '\taa', 'aa\t', '\taa\t' }, 207 { ' # aa ', ' # \taa ', ' # aa\t ', ' # \taa\t ' } 208 ) 209 validate({ ' aa', ' aa' }, { ' # aa ', ' # aa ' }) 210 validate( 211 { ' aa', '', ' ', '\t' }, 212 { ' # aa ', ' #', ' #', ' #' }, 213 { ' aa', '', '', '' } 214 ) 215 216 set_commentstring(' %s # ') 217 validate( 218 { 'aa', ' aa', 'aa ', ' aa ' }, 219 { ' aa # ', ' aa # ', ' aa # ', ' aa # ' } 220 ) 221 validate( 222 { 'aa', '\taa', 'aa\t', '\taa\t' }, 223 { ' aa # ', ' \taa # ', ' aa\t # ', ' \taa\t # ' } 224 ) 225 validate({ ' aa', ' aa' }, { ' aa # ', ' aa # ' }) 226 validate( 227 { ' aa', '', ' ', '\t' }, 228 { ' aa # ', ' #', ' #', ' #' }, 229 { ' aa', '', '', '' } 230 ) 231 232 -- LaTeX 233 set_commentstring('% %s') 234 validate({ 'aa', ' aa', 'aa ', ' aa ' }, { '% aa', '% aa', '% aa ', '% aa ' }) 235 validate({ 'aa', '\taa', 'aa\t', '\taa\t' }, { '% aa', '% \taa', '% aa\t', '% \taa\t' }) 236 validate({ ' aa', ' aa' }, { ' % aa', ' % aa' }) 237 validate( 238 { ' aa', '', ' ', '\t' }, 239 { ' % aa', ' %', ' %', ' %' }, 240 { ' aa', '', '', '' } 241 ) 242 end) 243 244 it('respects tree-sitter injections', function() 245 setup_treesitter() 246 247 local lines = { 248 'set background=dark', 249 'lua << EOF', 250 'print(1)', 251 'vim.api.nvim_exec2([[', 252 ' set background=light', 253 ']])', 254 'EOF', 255 } 256 257 -- Single line comments 258 local validate = function(line, ref_output) 259 set_lines(lines) 260 toggle_lines(line, line) 261 eq(get_lines(line - 1, line)[1], ref_output) 262 end 263 264 validate(1, '"set background=dark') 265 validate(2, '"lua << EOF') 266 validate(3, '-- print(1)') 267 validate(4, '-- vim.api.nvim_exec2([[') 268 validate(5, ' "set background=light') 269 validate(6, '-- ]])') 270 validate(7, '"EOF') 271 272 -- Multiline comments should be computed based on first line 'commentstring' 273 set_lines(lines) 274 toggle_lines(1, 3) 275 local out_lines = get_lines() 276 eq(out_lines[1], '"set background=dark') 277 eq(out_lines[2], '"lua << EOF') 278 eq(out_lines[3], '"print(1)') 279 end) 280 281 it('correctly computes indent', function() 282 toggle_lines(2, 4) 283 eq(get_lines(1, 4), { ' # aa', ' # aa', ' #' }) 284 end) 285 286 it('correctly detects comment/uncomment', function() 287 local validate = function(from, to, ref_lines) 288 set_lines({ '', 'aa', '# aa', '# aa', 'aa', '' }) 289 toggle_lines(from, to) 290 eq(get_lines(), ref_lines) 291 end 292 293 -- It should uncomment only if all non-blank lines are comments 294 validate(3, 4, { '', 'aa', 'aa', 'aa', 'aa', '' }) 295 validate(2, 4, { '', '# aa', '# # aa', '# # aa', 'aa', '' }) 296 validate(3, 5, { '', 'aa', '# # aa', '# # aa', '# aa', '' }) 297 validate(1, 6, { '#', '# aa', '# # aa', '# # aa', '# aa', '#' }) 298 299 -- Blank lines should be ignored when making a decision 300 set_lines({ '# aa', '', ' ', '\t', '# aa' }) 301 toggle_lines(1, 5) 302 eq(get_lines(), { 'aa', '', ' ', '\t', 'aa' }) 303 end) 304 305 it('correctly matches comment parts during checking and uncommenting', function() 306 local validate = function(from, to, ref_lines) 307 set_lines({ '/*aa*/', '/* aa */', '/* aa */' }) 308 toggle_lines(from, to) 309 eq(get_lines(), ref_lines) 310 end 311 312 -- Should first try to match 'commentstring' parts exactly with their 313 -- whitespace, with fallback on trimmed parts 314 set_commentstring('/*%s*/') 315 validate(1, 3, { 'aa', ' aa ', ' aa ' }) 316 validate(2, 3, { '/*aa*/', ' aa ', ' aa ' }) 317 validate(3, 3, { '/*aa*/', '/* aa */', ' aa ' }) 318 319 set_commentstring('/* %s */') 320 validate(1, 3, { 'aa', 'aa', ' aa ' }) 321 validate(2, 3, { '/*aa*/', 'aa', ' aa ' }) 322 validate(3, 3, { '/*aa*/', '/* aa */', ' aa ' }) 323 324 set_commentstring('/* %s */') 325 validate(1, 3, { 'aa', ' aa ', 'aa' }) 326 validate(2, 3, { '/*aa*/', ' aa ', 'aa' }) 327 validate(3, 3, { '/*aa*/', '/* aa */', 'aa' }) 328 329 set_commentstring(' /*%s*/ ') 330 validate(1, 3, { 'aa', ' aa ', ' aa ' }) 331 validate(2, 3, { '/*aa*/', ' aa ', ' aa ' }) 332 validate(3, 3, { '/*aa*/', '/* aa */', ' aa ' }) 333 end) 334 335 it('uncomments on inconsistent indent levels', function() 336 set_lines({ '# aa', ' # aa', ' # aa' }) 337 toggle_lines(1, 3) 338 eq(get_lines(), { 'aa', ' aa', ' aa' }) 339 end) 340 341 it('respects tabs', function() 342 api.nvim_set_option_value('expandtab', false, { buf = 0 }) 343 set_lines({ '\t\taa', '\t\taa' }) 344 345 toggle_lines(1, 2) 346 eq(get_lines(), { '\t\t# aa', '\t\t# aa' }) 347 348 toggle_lines(1, 2) 349 eq(get_lines(), { '\t\taa', '\t\taa' }) 350 end) 351 352 it('works with trailing whitespace', function() 353 -- Without right-hand side 354 set_commentstring('# %s') 355 set_lines({ ' aa', ' aa ', ' ' }) 356 toggle_lines(1, 3) 357 eq(get_lines(), { ' # aa', ' # aa ', ' #' }) 358 toggle_lines(1, 3) 359 eq(get_lines(), { ' aa', ' aa ', '' }) 360 361 -- With right-hand side 362 set_commentstring('%s #') 363 set_lines({ ' aa', ' aa ', ' ' }) 364 toggle_lines(1, 3) 365 eq(get_lines(), { ' aa #', ' aa #', ' #' }) 366 toggle_lines(1, 3) 367 eq(get_lines(), { ' aa', ' aa ', '' }) 368 369 -- Trailing whitespace after right side should be preserved for non-blanks 370 set_commentstring('%s #') 371 set_lines({ ' aa # ', ' aa #\t', ' # ', ' #\t' }) 372 toggle_lines(1, 4) 373 eq(get_lines(), { ' aa ', ' aa\t', '', '' }) 374 end) 375 end) 376 377 describe('Operator', function() 378 it('works in Normal mode', function() 379 set_cursor(2, 2) 380 feed('gc', 'ap') 381 eq(get_lines(), { '# aa', '# aa', '# aa', '#', ' aa', ' aa', 'aa' }) 382 -- Cursor moves to start line 383 eq(get_cursor(), { 1, 0 }) 384 385 -- Supports `v:count` 386 set_lines(example_lines) 387 set_cursor(2, 0) 388 feed('2gc', 'ap') 389 eq(get_lines(), { '# aa', '# aa', '# aa', '#', '# aa', '# aa', '# aa' }) 390 end) 391 392 it('allows dot-repeat in Normal mode', function() 393 local doubly_commented = { '# # aa', '# # aa', '# # aa', '# #', '# aa', '# aa', '# aa' } 394 395 set_lines(example_lines) 396 set_cursor(2, 2) 397 feed('gc', 'ap') 398 feed('.') 399 eq(get_lines(), doubly_commented) 400 401 -- Not immediate dot-repeat 402 set_lines(example_lines) 403 set_cursor(2, 2) 404 feed('gc', 'ap') 405 set_cursor(7, 0) 406 feed('.') 407 eq(get_lines(), doubly_commented) 408 end) 409 410 it('works in Visual mode', function() 411 set_cursor(2, 2) 412 feed('v', 'ap', 'gc') 413 eq(get_lines(), { '# aa', '# aa', '# aa', '#', ' aa', ' aa', 'aa' }) 414 415 -- Cursor moves to start line 416 eq(get_cursor(), { 1, 0 }) 417 end) 418 419 it('allows dot-repeat after initial Visual mode', function() 420 -- local example_lines = { 'aa', ' aa', ' aa', '', ' aa', ' aa', 'aa' } 421 422 set_lines(example_lines) 423 set_cursor(2, 2) 424 feed('vip', 'gc') 425 eq(get_lines(), { '# aa', '# aa', '# aa', '', ' aa', ' aa', 'aa' }) 426 eq(get_cursor(), { 1, 0 }) 427 428 -- Dot-repeat after first application in Visual mode should apply to the same 429 -- relative region 430 feed('.') 431 eq(get_lines(), example_lines) 432 433 set_cursor(3, 0) 434 feed('.') 435 eq(get_lines(), { 'aa', ' aa', ' # aa', ' #', ' # aa', ' aa', 'aa' }) 436 end) 437 438 it("respects 'commentstring'", function() 439 set_commentstring('/*%s*/') 440 set_cursor(2, 2) 441 feed('gc', 'ap') 442 eq(get_lines(), { '/*aa*/', '/* aa*/', '/* aa*/', '/**/', ' aa', ' aa', 'aa' }) 443 end) 444 445 it("works with empty 'commentstring'", function() 446 set_commentstring('') 447 set_cursor(2, 2) 448 feed('gc', 'ap') 449 eq(get_lines(), example_lines) 450 eq(exec_capture('1messages'), [[Option 'commentstring' is empty.]]) 451 end) 452 453 it('respects tree-sitter injections', function() 454 setup_treesitter() 455 456 local lines = { 457 'set background=dark', 458 'lua << EOF', 459 'print(1)', 460 'vim.api.nvim_exec2([[', 461 ' set background=light', 462 ']])', 463 'EOF', 464 } 465 466 -- Single line comments 467 local validate = function(line, ref_output) 468 set_lines(lines) 469 set_cursor(line, 0) 470 feed('gc_') 471 eq(get_lines(line - 1, line)[1], ref_output) 472 end 473 474 validate(1, '"set background=dark') 475 validate(2, '"lua << EOF') 476 validate(3, '-- print(1)') 477 validate(4, '-- vim.api.nvim_exec2([[') 478 validate(5, ' "set background=light') 479 validate(6, '-- ]])') 480 validate(7, '"EOF') 481 482 -- Has proper dot-repeat which recomputes 'commentstring' 483 set_lines(lines) 484 485 set_cursor(1, 0) 486 feed('gc_') 487 eq(get_lines()[1], '"set background=dark') 488 489 set_cursor(3, 0) 490 feed('.') 491 eq(get_lines()[3], '-- print(1)') 492 493 -- Multiline comments should be computed based on cursor position 494 -- which in case of Visual selection means its left part 495 set_lines(lines) 496 set_cursor(1, 0) 497 feed('v2j', 'gc') 498 local out_lines = get_lines() 499 eq(out_lines[1], '"set background=dark') 500 eq(out_lines[2], '"lua << EOF') 501 eq(out_lines[3], '"print(1)') 502 end) 503 504 it("recomputes local 'commentstring' based on cursor position", function() 505 setup_treesitter() 506 local lines = { 507 ' print(1)', 508 'lua << EOF', 509 ' print(1)', 510 'EOF', 511 } 512 set_lines(lines) 513 514 set_cursor(1, 1) 515 feed('gc_') 516 eq(get_lines()[1], ' "print(1)') 517 518 set_lines(lines) 519 set_cursor(3, 2) 520 feed('.') 521 eq(get_lines()[3], ' -- print(1)') 522 end) 523 524 it('preserves marks', function() 525 set_cursor(2, 0) 526 -- Set '`<' and '`>' marks 527 feed('VV') 528 feed('gc', 'ip') 529 eq(api.nvim_buf_get_mark(0, '<'), { 2, 0 }) 530 eq(api.nvim_buf_get_mark(0, '>'), { 2, 2147483647 }) 531 end) 532 end) 533 534 describe('Current line', function() 535 it('works', function() 536 set_lines(example_lines) 537 set_cursor(1, 1) 538 feed('gcc') 539 eq(get_lines(0, 2), { '# aa', ' aa' }) 540 541 -- Does not comment empty line 542 set_lines(example_lines) 543 set_cursor(4, 0) 544 feed('gcc') 545 eq(get_lines(2, 5), { ' aa', '', ' aa' }) 546 547 -- Supports `v:count` 548 set_lines(example_lines) 549 set_cursor(2, 0) 550 feed('2gcc') 551 eq(get_lines(0, 3), { 'aa', ' # aa', ' # aa' }) 552 end) 553 554 it('allows dot-repeat', function() 555 set_lines(example_lines) 556 set_cursor(1, 1) 557 feed('gcc') 558 feed('.') 559 eq(get_lines(), example_lines) 560 561 -- Not immediate dot-repeat 562 set_lines(example_lines) 563 set_cursor(1, 1) 564 feed('gcc') 565 set_cursor(7, 0) 566 feed('.') 567 eq(get_lines(6, 7), { '# aa' }) 568 end) 569 570 it('respects tree-sitter injections', function() 571 setup_treesitter() 572 573 local lines = { 574 'set background=dark', 575 'lua << EOF', 576 'print(1)', 577 'EOF', 578 } 579 set_lines(lines) 580 581 set_cursor(1, 0) 582 feed('gcc') 583 eq(get_lines(), { '"set background=dark', 'lua << EOF', 'print(1)', 'EOF' }) 584 585 -- Should work with dot-repeat 586 set_cursor(3, 0) 587 feed('.') 588 eq(get_lines(), { '"set background=dark', 'lua << EOF', '-- print(1)', 'EOF' }) 589 end) 590 591 it('respects tree-sitter commentstring metadata', function() 592 exec_lua [=[ 593 vim.treesitter.query.set('vim', 'highlights', [[ 594 ((list) @_list (#set! @_list bo.commentstring "!! %s")) 595 ]]) 596 ]=] 597 setup_treesitter() 598 599 local lines = { 600 'set background=dark', 601 'let mylist = [', 602 [[ \"a",]], 603 [[ \"b",]], 604 [[ \"c",]], 605 ' \\]', 606 } 607 set_lines(lines) 608 609 set_cursor(1, 0) 610 feed('gcc') 611 eq( 612 { '"set background=dark', 'let mylist = [', [[ \"a",]], [[ \"b",]], [[ \"c",]], ' \\]' }, 613 get_lines() 614 ) 615 616 -- Should work with dot-repeat 617 set_cursor(4, 0) 618 feed('.') 619 eq({ 620 '"set background=dark', 621 'let mylist = [', 622 [[ \"a",]], 623 [[ !! \"b",]], 624 [[ \"c",]], 625 ' \\]', 626 }, get_lines()) 627 end) 628 629 it('only applies the innermost tree-sitter commentstring metadata', function() 630 exec_lua [=[ 631 vim.treesitter.query.set('vim', 'highlights', [[ 632 ((list) @_list (#gsub! @_list "(.*)" "%1") (#set! bo.commentstring "!! %s")) 633 ((script_file) @_src (#set! @_src bo.commentstring "## %s")) 634 ]]) 635 ]=] 636 setup_treesitter() 637 638 local lines = { 639 'set background=dark', 640 'let mylist = [', 641 [[ \"a",]], 642 [[ \"b",]], 643 [[ \"c",]], 644 ' \\]', 645 } 646 set_lines(lines) 647 648 set_cursor(1, 0) 649 feed('gcc') 650 eq({ 651 '## set background=dark', 652 'let mylist = [', 653 [[ \"a",]], 654 [[ \"b",]], 655 [[ \"c",]], 656 ' \\]', 657 }, get_lines()) 658 659 -- Should work with dot-repeat 660 set_cursor(4, 0) 661 feed('.') 662 eq({ 663 '## set background=dark', 664 'let mylist = [', 665 [[ \"a",]], 666 [[ !! \"b",]], 667 [[ \"c",]], 668 ' \\]', 669 }, get_lines()) 670 end) 671 672 it('respects injected tree-sitter commentstring metadata', function() 673 exec_lua [=[ 674 vim.treesitter.query.set('lua', 'highlights', [[ 675 ((string) @string (#set! @string bo.commentstring "; %s")) 676 ]]) 677 ]=] 678 setup_treesitter() 679 680 local lines = { 681 'set background=dark', 682 'lua << EOF', 683 'print[[', 684 'Inside string', 685 ']]', 686 'EOF', 687 } 688 set_lines(lines) 689 690 set_cursor(1, 0) 691 feed('gcc') 692 eq({ 693 '"set background=dark', 694 'lua << EOF', 695 'print[[', 696 'Inside string', 697 ']]', 698 'EOF', 699 }, get_lines()) 700 701 -- Should work with dot-repeat 702 set_cursor(4, 0) 703 feed('.') 704 eq({ 705 '"set background=dark', 706 'lua << EOF', 707 'print[[', 708 '; Inside string', 709 ']]', 710 'EOF', 711 }, get_lines()) 712 713 set_cursor(3, 0) 714 feed('.') 715 eq({ 716 '"set background=dark', 717 'lua << EOF', 718 '-- print[[', 719 '; Inside string', 720 ']]', 721 'EOF', 722 }, get_lines()) 723 end) 724 725 it('works across combined injections #30799', function() 726 exec_lua [=[ 727 vim.treesitter.query.set('lua', 'injections', [[ 728 ((function_call 729 name: (_) @_vimcmd_identifier 730 arguments: (arguments 731 (string 732 content: _ @injection.content))) 733 (#eq? @_vimcmd_identifier "vim.cmd") 734 (#set! injection.language "vim") 735 (#set! injection.combined)) 736 ]]) 737 ]=] 738 739 api.nvim_set_option_value('filetype', 'lua', { buf = 0 }) 740 exec_lua('vim.treesitter.start()') 741 742 local lines = { 743 'vim.cmd([[" some text]])', 744 'local a = 123', 745 'vim.cmd([[" some more text]])', 746 } 747 set_lines(lines) 748 749 set_cursor(2, 0) 750 feed('gcc') 751 eq({ 752 'vim.cmd([[" some text]])', 753 '-- local a = 123', 754 'vim.cmd([[" some more text]])', 755 }, get_lines()) 756 end) 757 end) 758 759 describe('Textobject', function() 760 it('works', function() 761 set_lines({ 'aa', '# aa', '# aa', 'aa' }) 762 set_cursor(2, 0) 763 feed('d', 'gc') 764 eq(get_lines(), { 'aa', 'aa' }) 765 end) 766 767 it('allows dot-repeat', function() 768 set_lines({ 'aa', '# aa', '# aa', 'aa', '# aa' }) 769 set_cursor(2, 0) 770 feed('d', 'gc') 771 set_cursor(3, 0) 772 feed('.') 773 eq(get_lines(), { 'aa', 'aa' }) 774 end) 775 776 it('does nothing when not inside textobject', function() 777 -- Builtin operators 778 feed('d', 'gc') 779 eq(get_lines(), example_lines) 780 781 -- Comment operator 782 local validate_no_action = function(line, col) 783 set_lines(example_lines) 784 set_cursor(line, col) 785 feed('gc', 'gc') 786 eq(get_lines(), example_lines) 787 end 788 789 validate_no_action(1, 1) 790 validate_no_action(2, 2) 791 792 -- Doesn't work (but should) because both `[` and `]` are set to (1, 0) 793 -- (instead of more reasonable (1, -1) or (0, 2147483647)). 794 -- validate_no_action(1, 0) 795 end) 796 797 it('respects tree-sitter injections', function() 798 setup_treesitter() 799 local lines = { 800 '"set background=dark', 801 '"set termguicolors', 802 'lua << EOF', 803 '-- print(1)', 804 '-- print(2)', 805 'EOF', 806 } 807 set_lines(lines) 808 809 set_cursor(1, 0) 810 feed('dgc') 811 eq(get_lines(), { 'lua << EOF', '-- print(1)', '-- print(2)', 'EOF' }) 812 813 -- Should work with dot-repeat 814 set_cursor(2, 0) 815 feed('.') 816 eq(get_lines(), { 'lua << EOF', 'EOF' }) 817 end) 818 end) 819 end)