dirchanged_spec.lua 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373
  1. local t = require('test.testutil')
  2. local n = require('test.functional.testnvim')()
  3. local clear = n.clear
  4. local command = n.command
  5. local eq = t.eq
  6. local eval = n.eval
  7. local request = n.request
  8. local is_os = t.is_os
  9. describe('autocmd DirChanged and DirChangedPre', function()
  10. local curdir = t.fix_slashes(vim.uv.cwd())
  11. local dirs = {
  12. curdir .. '/Xtest-functional-autocmd-dirchanged.dir1',
  13. curdir .. '/Xtest-functional-autocmd-dirchanged.dir2',
  14. curdir .. '/Xtest-functional-autocmd-dirchanged.dir3',
  15. }
  16. local win_dirs = {
  17. curdir .. '\\XTEST-FUNCTIONAL-AUTOCMD-DIRCHANGED.DIR1',
  18. curdir .. '\\XTEST-FUNCTIONAL-AUTOCMD-DIRCHANGED.DIR2',
  19. curdir .. '\\XTEST-FUNCTIONAL-AUTOCMD-DIRCHANGED.DIR3',
  20. }
  21. setup(function()
  22. for _, dir in pairs(dirs) do
  23. t.mkdir(dir)
  24. end
  25. end)
  26. teardown(function()
  27. for _, dir in pairs(dirs) do
  28. n.rmdir(dir)
  29. end
  30. end)
  31. before_each(function()
  32. clear()
  33. command(
  34. 'autocmd DirChangedPre * let [g:evpre, g:amatchpre, g:cdprecount] '
  35. .. '= [copy(v:event), expand("<amatch>"), 1 + get(g:, "cdprecount", 0)]'
  36. )
  37. command(
  38. 'autocmd DirChanged * let [g:getcwd, g:ev, g:amatch, g:cdcount] '
  39. .. '= [getcwd(), copy(v:event), expand("<amatch>"), 1 + get(g:, "cdcount", 0)]'
  40. )
  41. -- Normalize path separators.
  42. command(
  43. [[autocmd DirChangedPre * let g:evpre['directory'] = substitute(g:evpre['directory'], '\\', '/', 'g')]]
  44. )
  45. command([[autocmd DirChanged * let g:ev['cwd'] = substitute(g:ev['cwd'], '\\', '/', 'g')]])
  46. command([[autocmd DirChanged * let g:getcwd = substitute(g:getcwd, '\\', '/', 'g')]])
  47. end)
  48. it('set v:event and <amatch>', function()
  49. command('lcd ' .. dirs[1])
  50. eq({ directory = dirs[1], scope = 'window', changed_window = false }, eval('g:evpre'))
  51. eq({ cwd = dirs[1], scope = 'window', changed_window = false }, eval('g:ev'))
  52. eq('window', eval('g:amatchpre'))
  53. eq('window', eval('g:amatch'))
  54. eq(1, eval('g:cdprecount'))
  55. eq(1, eval('g:cdcount'))
  56. command('tcd ' .. dirs[2])
  57. eq({ directory = dirs[2], scope = 'tabpage', changed_window = false }, eval('g:evpre'))
  58. eq({ cwd = dirs[2], scope = 'tabpage', changed_window = false }, eval('g:ev'))
  59. eq('tabpage', eval('g:amatchpre'))
  60. eq('tabpage', eval('g:amatch'))
  61. eq(2, eval('g:cdprecount'))
  62. eq(2, eval('g:cdcount'))
  63. command('cd ' .. dirs[3])
  64. eq({ directory = dirs[3], scope = 'global', changed_window = false }, eval('g:evpre'))
  65. eq({ cwd = dirs[3], scope = 'global', changed_window = false }, eval('g:ev'))
  66. eq('global', eval('g:amatchpre'))
  67. eq('global', eval('g:amatch'))
  68. eq(3, eval('g:cdprecount'))
  69. eq(3, eval('g:cdcount'))
  70. end)
  71. it('DirChanged set getcwd() during event #6260', function()
  72. command('lcd ' .. dirs[1])
  73. eq(dirs[1], eval('g:getcwd'))
  74. command('tcd ' .. dirs[2])
  75. eq(dirs[2], eval('g:getcwd'))
  76. command('cd ' .. dirs[3])
  77. eq(dirs[3], eval('g:getcwd'))
  78. end)
  79. it('disallow recursion', function()
  80. command('set shellslash')
  81. -- Set up a _nested_ handler.
  82. command('autocmd DirChanged * nested lcd ' .. dirs[3])
  83. command('lcd ' .. dirs[1])
  84. eq({ cwd = dirs[1], scope = 'window', changed_window = false }, eval('g:ev'))
  85. eq(1, eval('g:cdcount'))
  86. -- autocmd changed to dirs[3], but did NOT trigger another DirChanged.
  87. eq(dirs[3], eval('getcwd()'))
  88. end)
  89. it('only DirChangedPre is triggered if :cd fails', function()
  90. command('let g:ev = {}')
  91. command('let g:cdcount = 0')
  92. local status1, err1 = pcall(function()
  93. command('lcd ' .. dirs[1] .. '/doesnotexist')
  94. end)
  95. eq(
  96. { directory = dirs[1] .. '/doesnotexist', scope = 'window', changed_window = false },
  97. eval('g:evpre')
  98. )
  99. eq({}, eval('g:ev'))
  100. eq('window', eval('g:amatchpre'))
  101. eq(1, eval('g:cdprecount'))
  102. eq(0, eval('g:cdcount'))
  103. local status2, err2 = pcall(function()
  104. command('lcd ' .. dirs[2] .. '/doesnotexist')
  105. end)
  106. eq(
  107. { directory = dirs[2] .. '/doesnotexist', scope = 'window', changed_window = false },
  108. eval('g:evpre')
  109. )
  110. eq({}, eval('g:ev'))
  111. eq('window', eval('g:amatchpre'))
  112. eq(2, eval('g:cdprecount'))
  113. eq(0, eval('g:cdcount'))
  114. local status3, err3 = pcall(function()
  115. command('lcd ' .. dirs[3] .. '/doesnotexist')
  116. end)
  117. eq(
  118. { directory = dirs[3] .. '/doesnotexist', scope = 'window', changed_window = false },
  119. eval('g:evpre')
  120. )
  121. eq({}, eval('g:ev'))
  122. eq('window', eval('g:amatchpre'))
  123. eq(3, eval('g:cdprecount'))
  124. eq(0, eval('g:cdcount'))
  125. eq(false, status1)
  126. eq(false, status2)
  127. eq(false, status3)
  128. eq('E344:', string.match(err1, 'E%d*:'))
  129. eq('E344:', string.match(err2, 'E%d*:'))
  130. eq('E344:', string.match(err3, 'E%d*:'))
  131. end)
  132. it("are triggered by 'autochdir'", function()
  133. command('set autochdir')
  134. command('split ' .. dirs[1] .. '/foo')
  135. eq({ directory = dirs[1], scope = 'window', changed_window = false }, eval('g:evpre'))
  136. eq({ cwd = dirs[1], scope = 'window', changed_window = false }, eval('g:ev'))
  137. eq('auto', eval('g:amatchpre'))
  138. eq('auto', eval('g:amatch'))
  139. eq(1, eval('g:cdprecount'))
  140. eq(1, eval('g:cdcount'))
  141. command('split ' .. dirs[2] .. '/bar')
  142. eq({ directory = dirs[2], scope = 'window', changed_window = false }, eval('g:evpre'))
  143. eq({ cwd = dirs[2], scope = 'window', changed_window = false }, eval('g:ev'))
  144. eq('auto', eval('g:amatch'))
  145. eq(2, eval('g:cdprecount'))
  146. eq(2, eval('g:cdcount'))
  147. end)
  148. it('do not trigger if directory has not changed', function()
  149. command('lcd ' .. dirs[1])
  150. eq({ directory = dirs[1], scope = 'window', changed_window = false }, eval('g:evpre'))
  151. eq({ cwd = dirs[1], scope = 'window', changed_window = false }, eval('g:ev'))
  152. eq('window', eval('g:amatchpre'))
  153. eq('window', eval('g:amatch'))
  154. eq(1, eval('g:cdprecount'))
  155. eq(1, eval('g:cdcount'))
  156. command('let g:evpre = {}')
  157. command('let g:ev = {}')
  158. command('lcd ' .. dirs[1])
  159. eq({}, eval('g:evpre'))
  160. eq({}, eval('g:ev'))
  161. eq(1, eval('g:cdprecount'))
  162. eq(1, eval('g:cdcount'))
  163. if is_os('win') then
  164. command('lcd ' .. win_dirs[1])
  165. eq({}, eval('g:evpre'))
  166. eq({}, eval('g:ev'))
  167. eq(1, eval('g:cdprecount'))
  168. eq(1, eval('g:cdcount'))
  169. end
  170. command('tcd ' .. dirs[2])
  171. eq({ directory = dirs[2], scope = 'tabpage', changed_window = false }, eval('g:evpre'))
  172. eq({ cwd = dirs[2], scope = 'tabpage', changed_window = false }, eval('g:ev'))
  173. eq('tabpage', eval('g:amatchpre'))
  174. eq('tabpage', eval('g:amatch'))
  175. eq(2, eval('g:cdprecount'))
  176. eq(2, eval('g:cdcount'))
  177. command('let g:evpre = {}')
  178. command('let g:ev = {}')
  179. command('tcd ' .. dirs[2])
  180. eq({}, eval('g:evpre'))
  181. eq({}, eval('g:ev'))
  182. eq(2, eval('g:cdprecount'))
  183. eq(2, eval('g:cdcount'))
  184. if is_os('win') then
  185. command('tcd ' .. win_dirs[2])
  186. eq({}, eval('g:evpre'))
  187. eq({}, eval('g:ev'))
  188. eq(2, eval('g:cdprecount'))
  189. eq(2, eval('g:cdcount'))
  190. end
  191. command('cd ' .. dirs[3])
  192. eq({ directory = dirs[3], scope = 'global', changed_window = false }, eval('g:evpre'))
  193. eq({ cwd = dirs[3], scope = 'global', changed_window = false }, eval('g:ev'))
  194. eq('global', eval('g:amatch'))
  195. eq(3, eval('g:cdprecount'))
  196. eq(3, eval('g:cdcount'))
  197. command('let g:evpre = {}')
  198. command('let g:ev = {}')
  199. command('cd ' .. dirs[3])
  200. eq({}, eval('g:evpre'))
  201. eq({}, eval('g:ev'))
  202. eq(3, eval('g:cdprecount'))
  203. eq(3, eval('g:cdcount'))
  204. if is_os('win') then
  205. command('cd ' .. win_dirs[3])
  206. eq({}, eval('g:evpre'))
  207. eq({}, eval('g:ev'))
  208. eq(3, eval('g:cdprecount'))
  209. eq(3, eval('g:cdcount'))
  210. end
  211. command('set autochdir')
  212. command('split ' .. dirs[1] .. '/foo')
  213. eq({ directory = dirs[1], scope = 'window', changed_window = false }, eval('g:evpre'))
  214. eq({ cwd = dirs[1], scope = 'window', changed_window = false }, eval('g:ev'))
  215. eq('auto', eval('g:amatchpre'))
  216. eq('auto', eval('g:amatch'))
  217. eq(4, eval('g:cdprecount'))
  218. eq(4, eval('g:cdcount'))
  219. command('let g:evpre = {}')
  220. command('let g:ev = {}')
  221. command('split ' .. dirs[1] .. '/bar')
  222. eq({}, eval('g:evpre'))
  223. eq({}, eval('g:ev'))
  224. eq(4, eval('g:cdprecount'))
  225. eq(4, eval('g:cdcount'))
  226. if is_os('win') then
  227. command('split ' .. win_dirs[1] .. '/baz')
  228. eq({}, eval('g:evpre'))
  229. eq({}, eval('g:ev'))
  230. eq(4, eval('g:cdprecount'))
  231. eq(4, eval('g:cdcount'))
  232. end
  233. end)
  234. it('are triggered by switching to win/tab with different CWD #6054', function()
  235. command('lcd ' .. dirs[3]) -- window 3
  236. command('split ' .. dirs[2] .. '/foo') -- window 2
  237. command('lcd ' .. dirs[2])
  238. command('split ' .. dirs[1] .. '/bar') -- window 1
  239. command('lcd ' .. dirs[1])
  240. command('2wincmd w') -- window 2
  241. eq({ directory = dirs[2], scope = 'window', changed_window = true }, eval('g:evpre'))
  242. eq({ cwd = dirs[2], scope = 'window', changed_window = true }, eval('g:ev'))
  243. eq('window', eval('g:amatchpre'))
  244. eq('window', eval('g:amatch'))
  245. eq(4, eval('g:cdprecount'))
  246. eq(4, eval('g:cdcount'))
  247. command('tabnew') -- tab 2 (tab-local CWD)
  248. eq(4, eval('g:cdprecount')) -- same CWD, no DirChangedPre event
  249. eq(4, eval('g:cdcount')) -- same CWD, no DirChanged event
  250. command('tcd ' .. dirs[3])
  251. command('tabnext') -- tab 1 (no tab-local CWD)
  252. eq({ directory = dirs[2], scope = 'window', changed_window = true }, eval('g:evpre'))
  253. eq({ cwd = dirs[2], scope = 'window', changed_window = true }, eval('g:ev'))
  254. eq('window', eval('g:amatchpre'))
  255. eq('window', eval('g:amatch'))
  256. command('tabnext') -- tab 2
  257. eq({ directory = dirs[3], scope = 'tabpage', changed_window = true }, eval('g:evpre'))
  258. eq({ cwd = dirs[3], scope = 'tabpage', changed_window = true }, eval('g:ev'))
  259. eq('tabpage', eval('g:amatchpre'))
  260. eq('tabpage', eval('g:amatch'))
  261. eq(7, eval('g:cdprecount'))
  262. eq(7, eval('g:cdcount'))
  263. command('tabnext') -- tab 1
  264. command('3wincmd w') -- window 3
  265. eq(9, eval('g:cdprecount'))
  266. eq(9, eval('g:cdcount'))
  267. command('tabnext') -- tab 2 (has the *same* CWD)
  268. eq(9, eval('g:cdprecount')) -- same CWD, no DirChangedPre event
  269. eq(9, eval('g:cdcount')) -- same CWD, no DirChanged event
  270. if is_os('win') then
  271. command('tabnew') -- tab 3
  272. eq(9, eval('g:cdprecount')) -- same CWD, no DirChangedPre event
  273. eq(9, eval('g:cdcount')) -- same CWD, no DirChanged event
  274. command('tcd ' .. win_dirs[3])
  275. eq(9, eval('g:cdprecount')) -- same CWD, no DirChangedPre event
  276. eq(9, eval('g:cdcount')) -- same CWD, no DirChanged event
  277. command('tabnext') -- tab 1
  278. eq(9, eval('g:cdprecount')) -- same CWD, no DirChangedPre event
  279. eq(9, eval('g:cdcount')) -- same CWD, no DirChanged event
  280. command('tabprevious') -- tab 3
  281. eq(9, eval('g:cdprecount')) -- same CWD, no DirChangedPre event
  282. eq(9, eval('g:cdcount')) -- same CWD, no DirChanged event
  283. command('tabprevious') -- tab 2
  284. eq(9, eval('g:cdprecount')) -- same CWD, no DirChangedPre event
  285. eq(9, eval('g:cdcount')) -- same CWD, no DirChanged event
  286. command('tabprevious') -- tab 1
  287. eq(9, eval('g:cdprecount')) -- same CWD, no DirChangedPre event
  288. eq(9, eval('g:cdcount')) -- same CWD, no DirChanged event
  289. command('lcd ' .. win_dirs[3]) -- window 3
  290. eq(9, eval('g:cdprecount')) -- same CWD, no DirChangedPre event
  291. eq(9, eval('g:cdcount')) -- same CWD, no DirChanged event
  292. command('tabnext') -- tab 2
  293. eq(9, eval('g:cdprecount')) -- same CWD, no DirChangedPre event
  294. eq(9, eval('g:cdcount')) -- same CWD, no DirChanged event
  295. command('tabnext') -- tab 3
  296. eq(9, eval('g:cdprecount')) -- same CWD, no DirChangedPre event
  297. eq(9, eval('g:cdcount')) -- same CWD, no DirChanged event
  298. command('tabnext') -- tab 1
  299. eq(9, eval('g:cdprecount')) -- same CWD, no DirChangedPre event
  300. eq(9, eval('g:cdcount')) -- same CWD, no DirChanged event
  301. command('tabprevious') -- tab 3
  302. eq(9, eval('g:cdprecount')) -- same CWD, no DirChangedPre event
  303. eq(9, eval('g:cdcount')) -- same CWD, no DirChanged event
  304. end
  305. end)
  306. it('are triggered by nvim_set_current_dir()', function()
  307. request('nvim_set_current_dir', dirs[1])
  308. eq({ directory = dirs[1], scope = 'global', changed_window = false }, eval('g:evpre'))
  309. eq({ cwd = dirs[1], scope = 'global', changed_window = false }, eval('g:ev'))
  310. eq(1, eval('g:cdprecount'))
  311. eq(1, eval('g:cdcount'))
  312. request('nvim_set_current_dir', dirs[2])
  313. eq({ directory = dirs[2], scope = 'global', changed_window = false }, eval('g:evpre'))
  314. eq({ cwd = dirs[2], scope = 'global', changed_window = false }, eval('g:ev'))
  315. eq(2, eval('g:cdprecount'))
  316. eq(2, eval('g:cdcount'))
  317. eq(
  318. 'Vim:E344: Can\'t find directory "/doesnotexist" in cdpath',
  319. t.pcall_err(request, 'nvim_set_current_dir', '/doesnotexist')
  320. )
  321. eq({ directory = '/doesnotexist', scope = 'global', changed_window = false }, eval('g:evpre'))
  322. eq(3, eval('g:cdprecount'))
  323. eq(2, eval('g:cdcount'))
  324. end)
  325. it('work when local to buffer', function()
  326. command('let g:triggeredpre = 0')
  327. command('let g:triggered = 0')
  328. command('autocmd DirChangedPre <buffer> let g:triggeredpre = 1')
  329. command('autocmd DirChanged <buffer> let g:triggered = 1')
  330. command('cd ' .. dirs[1])
  331. eq(1, eval('g:triggeredpre'))
  332. eq(1, eval('g:triggered'))
  333. end)
  334. end)