comment_spec.lua 22 KB

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