hl_spec.lua 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  1. local t = require('test.testutil')
  2. local n = require('test.functional.testnvim')()
  3. local Screen = require('test.functional.ui.screen')
  4. local exec_lua = n.exec_lua
  5. local eq = t.eq
  6. local eval = n.eval
  7. local command = n.command
  8. local clear = n.clear
  9. local api = n.api
  10. describe('vim.hl.range', function()
  11. local screen
  12. before_each(function()
  13. clear()
  14. screen = Screen.new(60, 6)
  15. screen:add_extra_attr_ids({
  16. [100] = { foreground = Screen.colors.Blue, background = Screen.colors.Yellow, bold = true },
  17. })
  18. api.nvim_set_option_value('list', true, {})
  19. api.nvim_set_option_value('listchars', 'eol:$', {})
  20. api.nvim_buf_set_lines(0, 0, -1, true, {
  21. 'asdfghjkl',
  22. '«口=口»',
  23. 'qwertyuiop',
  24. '口口=口口',
  25. 'zxcvbnm',
  26. })
  27. screen:expect([[
  28. ^asdfghjkl{1:$} |
  29. «口=口»{1:$} |
  30. qwertyuiop{1:$} |
  31. 口口=口口{1:$} |
  32. zxcvbnm{1:$} |
  33. |
  34. ]])
  35. end)
  36. it('works with charwise selection', function()
  37. exec_lua(function()
  38. local ns = vim.api.nvim_create_namespace('')
  39. vim.hl.range(0, ns, 'Search', { 1, 5 }, { 3, 10 })
  40. end)
  41. screen:expect([[
  42. ^asdfghjkl{1:$} |
  43. «口{10:=口»}{100:$} |
  44. {10:qwertyuiop}{100:$} |
  45. {10:口口=口}口{1:$} |
  46. zxcvbnm{1:$} |
  47. |
  48. ]])
  49. end)
  50. it('works with linewise selection', function()
  51. exec_lua(function()
  52. local ns = vim.api.nvim_create_namespace('')
  53. vim.hl.range(0, ns, 'Search', { 0, 0 }, { 4, 0 }, { regtype = 'V' })
  54. end)
  55. screen:expect([[
  56. {10:^asdfghjkl}{100:$} |
  57. {10:«口=口»}{100:$} |
  58. {10:qwertyuiop}{100:$} |
  59. {10:口口=口口}{100:$} |
  60. {10:zxcvbnm}{100:$} |
  61. |
  62. ]])
  63. end)
  64. it('works with blockwise selection', function()
  65. exec_lua(function()
  66. local ns = vim.api.nvim_create_namespace('')
  67. vim.hl.range(0, ns, 'Search', { 0, 0 }, { 4, 4 }, { regtype = '\022' })
  68. end)
  69. screen:expect([[
  70. {10:^asdf}ghjkl{1:$} |
  71. {10:«口=}口»{1:$} |
  72. {10:qwer}tyuiop{1:$} |
  73. {10:口口}=口口{1:$} |
  74. {10:zxcv}bnm{1:$} |
  75. |
  76. ]])
  77. end)
  78. it('works with blockwise selection with width', function()
  79. exec_lua(function()
  80. local ns = vim.api.nvim_create_namespace('')
  81. vim.hl.range(0, ns, 'Search', { 0, 4 }, { 4, 7 }, { regtype = '\0226' })
  82. end)
  83. screen:expect([[
  84. ^asdf{10:ghjkl}{1:$} |
  85. «口={10:口»}{1:$} |
  86. qwer{10:tyuiop}{1:$} |
  87. 口口{10:=口口}{1:$} |
  88. zxcv{10:bnm}{1:$} |
  89. |
  90. ]])
  91. end)
  92. it('can use -1 or v:maxcol to indicate end of line', function()
  93. exec_lua(function()
  94. local ns = vim.api.nvim_create_namespace('')
  95. vim.hl.range(0, ns, 'Search', { 0, 4 }, { 1, -1 }, {})
  96. vim.hl.range(0, ns, 'Search', { 2, 6 }, { 3, vim.v.maxcol }, {})
  97. end)
  98. screen:expect([[
  99. ^asdf{10:ghjkl}{100:$} |
  100. {10:«口=口»}{100:$} |
  101. qwerty{10:uiop}{100:$} |
  102. {10:口口=口口}{1:$} |
  103. zxcvbnm{1:$} |
  104. |
  105. ]])
  106. end)
  107. it('removes highlight after given `timeout`', function()
  108. local timeout = 300
  109. exec_lua(function()
  110. local ns = vim.api.nvim_create_namespace('')
  111. vim.hl.range(0, ns, 'Search', { 0, 0 }, { 4, 0 }, { timeout = timeout })
  112. end)
  113. screen:expect({
  114. grid = [[
  115. {10:^asdfghjkl}{100:$} |
  116. {10:«口=口»}{100:$} |
  117. {10:qwertyuiop}{100:$} |
  118. {10:口口=口口}{1:$} |
  119. zxcvbnm{1:$} |
  120. |
  121. ]],
  122. timeout = timeout / 3,
  123. })
  124. screen:expect([[
  125. ^asdfghjkl{1:$} |
  126. «口=口»{1:$} |
  127. qwertyuiop{1:$} |
  128. 口口=口口{1:$} |
  129. zxcvbnm{1:$} |
  130. |
  131. ]])
  132. end)
  133. it('shows multiple highlights with different timeouts simultaneously', function()
  134. local timeout1 = 300
  135. local timeout2 = 600
  136. exec_lua(function()
  137. local ns = vim.api.nvim_create_namespace('')
  138. vim.hl.range(0, ns, 'Search', { 0, 0 }, { 4, 0 }, { timeout = timeout1 })
  139. vim.hl.range(0, ns, 'Search', { 2, 6 }, { 3, 5 }, { timeout = timeout2 })
  140. end)
  141. screen:expect({
  142. grid = [[
  143. {10:^asdfghjkl}{100:$} |
  144. {10:«口=口»}{100:$} |
  145. {10:qwertyuiop}{100:$} |
  146. {10:口口=口口}{1:$} |
  147. zxcvbnm{1:$} |
  148. |
  149. ]],
  150. timeout = timeout1 / 3,
  151. })
  152. screen:expect({
  153. grid = [[
  154. ^asdfghjkl{1:$} |
  155. «口=口»{1:$} |
  156. qwerty{10:uiop}{100:$} |
  157. {10:口口}=口口{1:$} |
  158. zxcvbnm{1:$} |
  159. |
  160. ]],
  161. timeout = timeout1 + ((timeout2 - timeout1) / 3),
  162. })
  163. screen:expect([[
  164. ^asdfghjkl{1:$} |
  165. «口=口»{1:$} |
  166. qwertyuiop{1:$} |
  167. 口口=口口{1:$} |
  168. zxcvbnm{1:$} |
  169. |
  170. ]])
  171. end)
  172. it('allows cancelling a highlight that has not timed out', function()
  173. exec_lua(function()
  174. local timeout = 3000
  175. local range_timer
  176. local range_hl_clear
  177. local ns = vim.api.nvim_create_namespace('')
  178. range_timer, range_hl_clear = vim.hl.range(
  179. 0,
  180. ns,
  181. 'Search',
  182. { 0, 0 },
  183. { 4, 0 },
  184. { timeout = timeout }
  185. )
  186. if range_timer and not range_timer:is_closing() then
  187. range_timer:close()
  188. assert(range_hl_clear)
  189. range_hl_clear()
  190. range_hl_clear() -- Exercise redundant call
  191. end
  192. end)
  193. screen:expect({
  194. grid = [[
  195. ^asdfghjkl{1:$} |
  196. «口=口»{1:$} |
  197. qwertyuiop{1:$} |
  198. 口口=口口{1:$} |
  199. zxcvbnm{1:$} |
  200. |
  201. ]],
  202. unchanged = true,
  203. })
  204. end)
  205. end)
  206. describe('vim.hl.on_yank', function()
  207. before_each(function()
  208. clear()
  209. end)
  210. it('does not show errors even if buffer is wiped before timeout', function()
  211. command('new')
  212. n.feed('ifoo<esc>') -- set '[, ']
  213. exec_lua(function()
  214. vim.hl.on_yank({
  215. timeout = 10,
  216. on_macro = true,
  217. event = { operator = 'y', regtype = 'v' },
  218. })
  219. vim.cmd('bwipeout!')
  220. end)
  221. vim.uv.sleep(10)
  222. n.feed('<cr>') -- avoid hang if error message exists
  223. eq('', eval('v:errmsg'))
  224. end)
  225. it('does not close timer twice', function()
  226. exec_lua(function()
  227. vim.hl.on_yank({ timeout = 10, on_macro = true, event = { operator = 'y' } })
  228. vim.uv.sleep(10)
  229. vim.schedule(function()
  230. vim.hl.on_yank({ timeout = 0, on_macro = true, event = { operator = 'y' } })
  231. end)
  232. end)
  233. eq('', eval('v:errmsg'))
  234. end)
  235. it('does not show in another window', function()
  236. command('vsplit')
  237. exec_lua(function()
  238. vim.api.nvim_buf_set_mark(0, '[', 1, 1, {})
  239. vim.api.nvim_buf_set_mark(0, ']', 1, 1, {})
  240. vim.hl.on_yank({ timeout = math.huge, on_macro = true, event = { operator = 'y' } })
  241. end)
  242. local ns = api.nvim_create_namespace('nvim.hlyank')
  243. local win = api.nvim_get_current_win()
  244. eq({ win }, api.nvim__ns_get(ns).wins)
  245. command('wincmd w')
  246. eq({ win }, api.nvim__ns_get(ns).wins)
  247. -- Use a new vim.hl.on_yank() call to cancel the previous timer
  248. exec_lua(function()
  249. vim.hl.on_yank({ timeout = 0, on_macro = true, event = { operator = 'y' } })
  250. end)
  251. end)
  252. it('removes old highlight if new one is created before old one times out', function()
  253. command('vnew')
  254. exec_lua(function()
  255. vim.api.nvim_buf_set_mark(0, '[', 1, 1, {})
  256. vim.api.nvim_buf_set_mark(0, ']', 1, 1, {})
  257. vim.hl.on_yank({ timeout = math.huge, on_macro = true, event = { operator = 'y' } })
  258. end)
  259. local ns = api.nvim_create_namespace('nvim.hlyank')
  260. eq(api.nvim_get_current_win(), api.nvim__ns_get(ns).wins[1])
  261. command('wincmd w')
  262. exec_lua(function()
  263. vim.api.nvim_buf_set_mark(0, '[', 1, 1, {})
  264. vim.api.nvim_buf_set_mark(0, ']', 1, 1, {})
  265. vim.hl.on_yank({ timeout = math.huge, on_macro = true, event = { operator = 'y' } })
  266. end)
  267. local win = api.nvim_get_current_win()
  268. eq({ win }, api.nvim__ns_get(ns).wins)
  269. command('wincmd w')
  270. eq({ win }, api.nvim__ns_get(ns).wins)
  271. -- Use a new vim.hl.on_yank() call to cancel the previous timer
  272. exec_lua(function()
  273. vim.hl.on_yank({ timeout = 0, on_macro = true, event = { operator = 'y' } })
  274. end)
  275. end)
  276. it('highlights last character with exclusive motion', function()
  277. local screen = Screen.new(60, 4)
  278. screen:add_extra_attr_ids({
  279. [100] = { foreground = Screen.colors.Blue, background = Screen.colors.Yellow, bold = true },
  280. })
  281. command('autocmd TextYankPost * lua vim.hl.on_yank{timeout=100000}')
  282. api.nvim_buf_set_lines(0, 0, -1, true, {
  283. [[foo(bar) 'baz']],
  284. [[foo(bar) 'baz']],
  285. })
  286. n.feed('yw')
  287. screen:expect([[
  288. {2:^foo}(bar) 'baz' |
  289. foo(bar) 'baz' |
  290. {1:~ }|
  291. |
  292. ]])
  293. n.feed("yi'")
  294. screen:expect([[
  295. foo(bar) '{2:^baz}' |
  296. foo(bar) 'baz' |
  297. {1:~ }|
  298. |
  299. ]])
  300. n.feed('yvj')
  301. screen:expect([[
  302. foo(bar) '{2:^baz'} |
  303. {2:foo(bar) '}baz' |
  304. {1:~ }|
  305. |
  306. ]])
  307. -- Use a new vim.hl.on_yank() call to cancel the previous timer
  308. exec_lua(function()
  309. vim.hl.on_yank({ timeout = 0, on_macro = true, event = { operator = 'y' } })
  310. end)
  311. end)
  312. end)