autocmd.lua 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479
  1. local buf_storage = require("nui.utils.buf_storage")
  2. local is_type = require("nui.utils").is_type
  3. local feature = require("nui.utils")._.feature
  4. local autocmd = {
  5. event = {
  6. -- after adding a buffer to the buffer list
  7. BufAdd = "BufAdd",
  8. -- deleting a buffer from the buffer list
  9. BufDelete = "BufDelete",
  10. -- after entering a buffer
  11. BufEnter = "BufEnter",
  12. -- after renaming a buffer
  13. BufFilePost = "BufFilePost",
  14. -- before renaming a buffer
  15. BufFilePre = "BufFilePre",
  16. -- just after buffer becomes hidden
  17. BufHidden = "BufHidden",
  18. -- before leaving a buffer
  19. BufLeave = "BufLeave",
  20. -- after the 'modified' state of a buffer changes
  21. BufModifiedSet = "BufModifiedSet",
  22. -- after creating any buffer
  23. BufNew = "BufNew",
  24. -- when creating a buffer for a new file
  25. BufNewFile = "BufNewFile",
  26. -- read buffer using command
  27. BufReadCmd = "BufReadCmd",
  28. -- after reading a buffer
  29. BufReadPost = "BufReadPost",
  30. -- before reading a buffer
  31. BufReadPre = "BufReadPre",
  32. -- just before unloading a buffer
  33. BufUnload = "BufUnload",
  34. -- after showing a buffer in a window
  35. BufWinEnter = "BufWinEnter",
  36. -- just after buffer removed from window
  37. BufWinLeave = "BufWinLeave",
  38. -- just before really deleting a buffer
  39. BufWipeout = "BufWipeout",
  40. -- write buffer using command
  41. BufWriteCmd = "BufWriteCmd",
  42. -- after writing a buffer
  43. BufWritePost = "BufWritePost",
  44. -- before writing a buffer
  45. BufWritePre = "BufWritePre",
  46. -- info was received about channel
  47. ChanInfo = "ChanInfo",
  48. -- channel was opened
  49. ChanOpen = "ChanOpen",
  50. -- command undefined
  51. CmdUndefined = "CmdUndefined",
  52. -- command line was modified
  53. CmdlineChanged = "CmdlineChanged",
  54. -- after entering cmdline mode
  55. CmdlineEnter = "CmdlineEnter",
  56. -- before leaving cmdline mode
  57. CmdlineLeave = "CmdlineLeave",
  58. -- after entering the cmdline window
  59. CmdWinEnter = "CmdwinEnter",
  60. -- before leaving the cmdline window
  61. CmdWinLeave = "CmdwinLeave",
  62. -- after loading a colorscheme
  63. ColorScheme = "ColorScheme",
  64. -- before loading a colorscheme
  65. ColorSchemePre = "ColorSchemePre",
  66. -- after popup menu changed
  67. CompleteChanged = "CompleteChanged",
  68. -- after finishing insert complete
  69. CompleteDone = "CompleteDone",
  70. -- idem, before clearing info
  71. CompleteDonePre = "CompleteDonePre",
  72. -- cursor in same position for a while
  73. CursorHold = "CursorHold",
  74. -- idem, in Insert mode
  75. CursorHoldI = "CursorHoldI",
  76. -- cursor was moved
  77. CursorMoved = "CursorMoved",
  78. -- cursor was moved in Insert mode
  79. CursorMovedI = "CursorMovedI",
  80. -- diffs have been updated
  81. DiffUpdated = "DiffUpdated",
  82. -- directory changed
  83. DirChanged = "DirChanged",
  84. -- after changing the 'encoding' option
  85. EncodingChanged = "EncodingChanged",
  86. -- before exiting
  87. ExitPre = "ExitPre",
  88. -- append to a file using command
  89. FileAppendCmd = "FileAppendCmd",
  90. -- after appending to a file
  91. FileAppendPost = "FileAppendPost",
  92. -- before appending to a file
  93. FileAppendPre = "FileAppendPre",
  94. -- before first change to read-only file
  95. FileChangedRO = "FileChangedRO",
  96. -- after shell command that changed file
  97. FileChangedShell = "FileChangedShell",
  98. -- after (not) reloading changed file
  99. FileChangedShellPost = "FileChangedShellPost",
  100. -- read from a file using command
  101. FileReadCmd = "FileReadCmd",
  102. -- after reading a file
  103. FileReadPost = "FileReadPost",
  104. -- before reading a file
  105. FileReadPre = "FileReadPre",
  106. -- new file type detected (user defined)
  107. FileType = "FileType",
  108. -- write to a file using command
  109. FileWriteCmd = "FileWriteCmd",
  110. -- after writing a file
  111. FileWritePost = "FileWritePost",
  112. -- before writing a file
  113. FileWritePre = "FileWritePre",
  114. -- after reading from a filter
  115. FilterReadPost = "FilterReadPost",
  116. -- before reading from a filter
  117. FilterReadPre = "FilterReadPre",
  118. -- after writing to a filter
  119. FilterWritePost = "FilterWritePost",
  120. -- before writing to a filter
  121. FilterWritePre = "FilterWritePre",
  122. -- got the focus
  123. FocusGained = "FocusGained",
  124. -- lost the focus to another app
  125. FocusLost = "FocusLost",
  126. -- if calling a function which doesn't exist
  127. FuncUndefined = "FuncUndefined",
  128. -- after starting the GUI
  129. GUIEnter = "GUIEnter",
  130. -- after starting the GUI failed
  131. GUIFailed = "GUIFailed",
  132. -- when changing Insert/Replace mode
  133. InsertChange = "InsertChange",
  134. -- before inserting a char
  135. InsertCharPre = "InsertCharPre",
  136. -- when entering Insert mode
  137. InsertEnter = "InsertEnter",
  138. -- just after leaving Insert mode
  139. InsertLeave = "InsertLeave",
  140. -- just before leaving Insert mode
  141. InsertLeavePre = "InsertLeavePre",
  142. -- just before popup menu is displayed
  143. MenuPopup = "MenuPopup",
  144. -- after changing the mode
  145. ModeChanged = "ModeChanged",
  146. -- after setting any option
  147. OptionSet = "OptionSet",
  148. -- after :make, :grep etc.
  149. QuickFixCmdPost = "QuickFixCmdPost",
  150. -- before :make, :grep etc.
  151. QuickFixCmdPre = "QuickFixCmdPre",
  152. -- before :quit
  153. QuitPre = "QuitPre",
  154. -- upon string reception from a remote vim
  155. RemoteReply = "RemoteReply",
  156. -- when the search wraps around the document
  157. SearchWrapped = "SearchWrapped",
  158. -- after loading a session file
  159. SessionLoadPost = "SessionLoadPost",
  160. -- after ":!cmd"
  161. ShellCmdPost = "ShellCmdPost",
  162. -- after ":1,2!cmd", ":w !cmd", ":r !cmd".
  163. ShellFilterPost = "ShellFilterPost",
  164. -- after nvim process received a signal
  165. Signal = "Signal",
  166. -- sourcing a Vim script using command
  167. SourceCmd = "SourceCmd",
  168. -- after sourcing a Vim script
  169. SourcePost = "SourcePost",
  170. -- before sourcing a Vim script
  171. SourcePre = "SourcePre",
  172. -- spell file missing
  173. SpellFileMissing = "SpellFileMissing",
  174. -- after reading from stdin
  175. StdinReadPost = "StdinReadPost",
  176. -- before reading from stdin
  177. StdinReadPre = "StdinReadPre",
  178. -- found existing swap file
  179. SwapExists = "SwapExists",
  180. -- syntax selected
  181. Syntax = "Syntax",
  182. -- a tab has closed
  183. TabClosed = "TabClosed",
  184. -- after entering a tab page
  185. TabEnter = "TabEnter",
  186. -- before leaving a tab page
  187. TabLeave = "TabLeave",
  188. -- when creating a new tab
  189. TabNew = "TabNew",
  190. -- after entering a new tab
  191. TabNewEntered = "TabNewEntered",
  192. -- after changing 'term'
  193. TermChanged = "TermChanged",
  194. -- after the process exits
  195. TermClose = "TermClose",
  196. -- after entering Terminal mode
  197. TermEnter = "TermEnter",
  198. -- after leaving Terminal mode
  199. TermLeave = "TermLeave",
  200. -- after opening a terminal buffer
  201. TermOpen = "TermOpen",
  202. -- after setting "v:termresponse"
  203. TermResponse = "TermResponse",
  204. -- text was modified
  205. TextChanged = "TextChanged",
  206. -- text was modified in Insert mode(no popup)
  207. TextChangedI = "TextChangedI",
  208. -- text was modified in Insert mode(popup)
  209. TextChangedP = "TextChangedP",
  210. -- after a yank or delete was done (y, d, c)
  211. TextYankPost = "TextYankPost",
  212. -- after UI attaches
  213. UIEnter = "UIEnter",
  214. -- after UI detaches
  215. UILeave = "UILeave",
  216. -- user defined autocommand
  217. User = "User",
  218. -- whenthe user presses the same key 42 times
  219. UserGettingBored = "UserGettingBored",
  220. -- after starting Vim
  221. VimEnter = "VimEnter",
  222. -- before exiting Vim
  223. VimLeave = "VimLeave",
  224. -- before exiting Vim and writing ShaDa file
  225. VimLeavePre = "VimLeavePre",
  226. -- after Vim window was resized
  227. VimResized = "VimResized",
  228. -- after Nvim is resumed
  229. VimResume = "VimResume",
  230. -- before Nvim is suspended
  231. VimSuspend = "VimSuspend",
  232. -- after closing a window
  233. WinClosed = "WinClosed",
  234. -- after entering a window
  235. WinEnter = "WinEnter",
  236. -- before leaving a window
  237. WinLeave = "WinLeave",
  238. -- when entering a new window
  239. WinNew = "WinNew",
  240. -- after scrolling a window
  241. WinScrolled = "WinScrolled",
  242. -- alias for `BufAdd`
  243. BufCreate = "BufAdd",
  244. -- alias for `BufReadPost`
  245. BufRead = "BufReadPost",
  246. -- alias for `BufWritePre`
  247. BufWrite = "BufWritePre",
  248. -- alias for `EncodingChanged`
  249. FileEncoding = "EncodingChanged",
  250. },
  251. buf = {
  252. storage = buf_storage.create("nui.utils.autocmd", { _next_handler_id = 1 }),
  253. },
  254. }
  255. ---@param callback fun(event: table): nil
  256. ---@param bufnr integer
  257. local function to_stored_handler(callback, bufnr)
  258. local handler_id = autocmd.buf.storage[bufnr]._next_handler_id
  259. autocmd.buf.storage[bufnr]._next_handler_id = handler_id + 1
  260. autocmd.buf.storage[bufnr][handler_id] = callback
  261. local command = string.format(":lua require('nui.utils.autocmd').execute_stored_handler(%s, %s)", bufnr, handler_id)
  262. return command
  263. end
  264. ---@param bufnr integer
  265. ---@param handler_id number
  266. function autocmd.execute_stored_handler(bufnr, handler_id)
  267. local handler = autocmd.buf.storage[bufnr][handler_id]
  268. if is_type("function", handler) then
  269. handler()
  270. end
  271. end
  272. ---@param name string
  273. ---@param opts { clear?: boolean }
  274. function autocmd.create_group(name, opts)
  275. if feature.lua_autocmd then
  276. return vim.api.nvim_create_augroup(name, opts)
  277. end
  278. vim.cmd(string.format(
  279. [[
  280. augroup %s
  281. %s
  282. augroup end
  283. ]],
  284. name,
  285. opts.clear and "autocmd!" or ""
  286. ))
  287. end
  288. ---@param name string
  289. function autocmd.delete_group(name)
  290. if feature.lua_autocmd then
  291. return vim.api.nvim_del_augroup_by_name(name)
  292. end
  293. vim.cmd(string.format(
  294. [[
  295. autocmd! %s
  296. augroup! %s
  297. ]],
  298. name,
  299. name
  300. ))
  301. end
  302. ---@param event string|string[]
  303. ---@param opts table
  304. ---@param bufnr? integer # to store callback if lua autocmd is not available
  305. function autocmd.create(event, opts, bufnr)
  306. if feature.lua_autocmd then
  307. return vim.api.nvim_create_autocmd(event, opts)
  308. end
  309. event = type(event) == "table" and table.concat(event, ",") or event --[[@as string]]
  310. local pattern = is_type("table", opts.pattern) and table.concat(opts.pattern, ",") or opts.pattern
  311. if opts.buffer then
  312. pattern = string.format("<buffer=%s>", opts.buffer)
  313. end
  314. if opts.callback then
  315. local buffer = opts.buffer or bufnr
  316. if not buffer then
  317. error("[nui.utils.autocmd] missing param: bufnr")
  318. end
  319. opts.command = to_stored_handler(opts.callback, buffer)
  320. end
  321. vim.cmd(
  322. string.format(
  323. "autocmd %s %s %s %s %s %s",
  324. opts.group or "",
  325. event,
  326. pattern,
  327. opts.once and "++once" or "",
  328. opts.nested and "++nested" or "",
  329. opts.command
  330. )
  331. )
  332. end
  333. ---@param opts table
  334. function autocmd.delete(opts)
  335. if feature.lua_autocmd then
  336. for _, item in ipairs(vim.api.nvim_get_autocmds(opts)) do
  337. if item.id then
  338. vim.api.nvim_del_autocmd(item.id)
  339. end
  340. end
  341. return
  342. end
  343. local event = is_type("table", opts.event) and table.concat(opts.event, ",") or opts.event
  344. local pattern = is_type("table", opts.pattern) and table.concat(opts.pattern, ",") or opts.pattern
  345. if opts.buffer then
  346. pattern = string.format("<buffer=%s>", opts.buffer)
  347. end
  348. vim.cmd(string.format("autocmd! %s %s %s", opts.group or "", event or "*", pattern or ""))
  349. end
  350. ---@param event string|string[]
  351. ---@param opts table
  352. function autocmd.exec(event, opts)
  353. local events = type(event) == "table" and event or { event } --[=[@as string[]]=]
  354. if feature.lua_autocmd then
  355. vim.api.nvim_exec_autocmds(events, {
  356. group = opts.group,
  357. pattern = opts.pattern,
  358. buffer = opts.buffer,
  359. modeline = opts.modeline,
  360. data = opts.data,
  361. })
  362. return
  363. end
  364. for _, event_name in ipairs(events) do
  365. local command = string.format(
  366. [[doautocmd %s %s %s %s]],
  367. opts.modeline == false and "<nomodeline>" or "",
  368. opts.group or "",
  369. event_name,
  370. opts.pattern or ""
  371. )
  372. if opts.buffer then
  373. vim.api.nvim_buf_call(opts.buffer, function()
  374. vim.cmd(command)
  375. end)
  376. else
  377. vim.cmd(command)
  378. end
  379. end
  380. end
  381. -- @deprecated
  382. ---@deprecated
  383. ---@param event string | string[]
  384. ---@param pattern string | string[]
  385. ---@param cmd string
  386. ---@param options nil | table<"'once'" | "'nested'", boolean>
  387. function autocmd.define(event, pattern, cmd, options)
  388. local opts = options or {}
  389. opts.pattern = pattern
  390. opts.command = cmd
  391. autocmd.create(event, opts)
  392. end
  393. -- @deprecated
  394. ---@deprecated
  395. ---@param group_name string
  396. ---@param auto_clear boolean
  397. ---@param definitions table<"'event'" | "'pattern'" | "'cmd'" | "'options'", any>
  398. function autocmd.define_grouped(group_name, auto_clear, definitions)
  399. if not is_type("boolean", auto_clear) then
  400. error("invalid param type: auto_clear, expected boolean")
  401. end
  402. autocmd.create_group(group_name, { clear = auto_clear })
  403. for _, definition in ipairs(definitions) do
  404. autocmd.define(definition.event, definition.pattern, definition.cmd, definition.options)
  405. end
  406. end
  407. -- @deprecated
  408. ---@deprecated
  409. ---@param group_name nil | string
  410. ---@param event nil | string | string[]
  411. ---@param pattern nil | string | string[]
  412. function autocmd.remove(group_name, event, pattern)
  413. autocmd.delete({
  414. event = event,
  415. group = group_name,
  416. pattern = pattern,
  417. })
  418. end
  419. ---@param bufnr number
  420. ---@param event string | string[]
  421. ---@param handler string | function
  422. ---@param options nil | table<"'once'" | "'nested'", boolean>
  423. function autocmd.buf.define(bufnr, event, handler, options)
  424. local opts = options or {}
  425. opts.buffer = bufnr
  426. if is_type("function", handler) then
  427. opts.callback = handler
  428. else
  429. opts.command = handler
  430. end
  431. autocmd.create(event, opts, bufnr)
  432. end
  433. ---@param bufnr number
  434. ---@param group_name nil | string
  435. ---@param event nil | string | string[]
  436. function autocmd.buf.remove(bufnr, group_name, event)
  437. autocmd.delete({
  438. buffer = bufnr,
  439. event = event,
  440. group = group_name,
  441. })
  442. end
  443. return autocmd