dirchanged_spec.lua 13 KB

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