1-- General usage autocmds.
2
3-- DESCRIPTION:
4-- All autocmds are defined here.
5
6-- Sections:
7-- ## EXTRA LOGIC
8-- -> 1. Events to load plugins faster.
9-- -> 2. Save/restore window layout when possible.
10-- -> 3. Launch alpha greeter on startup.
11-- -> 4. Update neotree when closing the git client.
12-- -> 5. Create parent directories when saving a file.
13--
14-- ## COOL HACKS
15-- -> 6. Effect: URL underline.
16-- -> 7. Customize right click contextual menu.
17-- -> 8. Unlist quickfix buffers if the filetype changes.
18-- -> 9. Close all notifications on BufWritePre.
19--
20-- ## COMMANDS
21-- -> 10. Neotest commands.
22-- -> Extra commands.
23
24local autocmd = vim.api.nvim_create_autocmd
25local cmd = vim.api.nvim_create_user_command
26local utils = require("base.utils")
27local is_available = utils.is_available
28
29-- ## EXTRA LOGIC -----------------------------------------------------------
30-- 1. Events to load plugins faster → 'BaseFile'/'BaseGitFile'/'BaseDefered':
31-- this is pretty much the same thing as the event 'BufEnter',
32-- but without increasing the startup time displayed in the greeter.
33autocmd({ "BufReadPost", "BufNewFile", "BufWritePost" }, {
34 desc = "Nvim user events for file detection (BaseFile and BaseGitFile)",
35 callback = function(args)
36 local empty_buffer = vim.fn.resolve(vim.fn.expand "%") == ""
37 local greeter = vim.api.nvim_get_option_value("filetype", { buf = args.buf }) == "alpha"
38 local git_repo = utils.run_cmd(
39 { "git", "-C", vim.fn.fnamemodify(vim.fn.resolve(vim.fn.expand "%"), ":p:h"), "rev-parse" }, false)
40
41 -- For any file exept empty buffer, or the greeter (alpha)
42 if not (empty_buffer or greeter) then
43 utils.trigger_event("User BaseFile")
44
45 -- Is the buffer part of a git repo?
46 if git_repo then
47 utils.trigger_event("User BaseGitFile")
48 end
49 end
50 end,
51})
52autocmd({ "VimEnter" }, {
53 desc = "Nvim user event that trigger a few ms after nvim starts",
54 callback = function()
55 -- If nvim is opened passing a filename, trigger the event inmediatelly.
56 if #vim.fn.argv() >= 1 then
57 -- In order to avoid visual glitches.
58 utils.trigger_event("User BaseDefered", true)
59 utils.trigger_event("BufEnter", true) -- also, initialize tabline_buffers.
60 else -- Wait some ms before triggering the event.
61 vim.defer_fn(function()
62 utils.trigger_event("User BaseDefered")
63 end, 70)
64 end
65 end,
66})
67
68-- 2. Save/restore window layout when possible.
69autocmd({ "BufWinLeave", "BufWritePost", "WinLeave" }, {
70 desc = "Save view with mkview for real files",
71 callback = function(args)
72 if vim.b[args.buf].view_activated then
73 vim.cmd.mkview { mods = { emsg_silent = true } }
74 end
75 end,
76})
77autocmd("BufWinEnter", {
78 desc = "Try to load file view if available and enable view saving for real files",
79 callback = function(args)
80 if not vim.b[args.buf].view_activated then
81 local filetype =
82 vim.api.nvim_get_option_value("filetype", { buf = args.buf })
83 local buftype =
84 vim.api.nvim_get_option_value("buftype", { buf = args.buf })
85 local ignore_filetypes = { "gitcommit", "gitrebase", "svg", "hgcommit" }
86 if
87 buftype == ""
88 and filetype
89 and filetype ~= ""
90 and not vim.tbl_contains(ignore_filetypes, filetype)
91 then
92 vim.b[args.buf].view_activated = true
93 vim.cmd.loadview { mods = { emsg_silent = true } }
94 end
95 end
96 end,
97})
98
99-- 3. Launch alpha greeter on startup
100if is_available "alpha-nvim" then
101 autocmd({ "User", "BufEnter" }, {
102 desc = "Disable status and tablines for alpha",
103 callback = function(args)
104 local is_filetype_alpha = vim.api.nvim_get_option_value(
105 "filetype", { buf = 0 }) == "alpha"
106 local is_empty_file = vim.api.nvim_get_option_value(
107 "buftype", { buf = 0 }) == "nofile"
108 if ((args.event == "User" and args.file == "AlphaReady") or
109 (args.event == "BufEnter" and is_filetype_alpha)) and
110 not vim.g.before_alpha
111 then
112 vim.g.before_alpha = {
113 showtabline = vim.opt.showtabline:get(),
114 laststatus = vim.opt.laststatus:get()
115 }
116 vim.opt.showtabline, vim.opt.laststatus = 0, 0
117 elseif
118 vim.g.before_alpha
119 and args.event == "BufEnter"
120 and not is_empty_file
121 then
122 vim.opt.laststatus = vim.g.before_alpha.laststatus
123 vim.opt.showtabline = vim.g.before_alpha.showtabline
124 vim.g.before_alpha = nil
125 end
126 end,
127 })
128 autocmd("VimEnter", {
129 desc = "Start Alpha only when nvim is opened with no arguments",
130 callback = function()
131 -- Precalculate conditions.
132 local lines = vim.api.nvim_buf_get_lines(0, 0, 2, false)
133 local buf_not_empty = vim.fn.argc() > 0
134 or #lines > 1
135 or (#lines == 1 and lines[1]:len() > 0)
136 local buflist_not_empty = #vim.tbl_filter(
137 function(bufnr) return vim.bo[bufnr].buflisted end,
138 vim.api.nvim_list_bufs()
139 ) > 1
140 local buf_not_modifiable = not vim.o.modifiable
141
142 -- Return instead of opening alpha if any of these conditions occur.
143 if buf_not_modifiable or buf_not_empty or buflist_not_empty then
144 return
145 end
146 for _, arg in pairs(vim.v.argv) do
147 if arg == "-b"
148 or arg == "-c"
149 or vim.startswith(arg, "+")
150 or arg == "-S"
151 then
152 return
153 end
154 end
155
156 -- All good? Show alpha.
157 require("alpha").start(true, require("alpha").default_config)
158 vim.schedule(function() vim.cmd.doautocmd "FileType" end)
159 end,
160 })
161end
162
163-- 4. Update neotree when closin the git client.
164if is_available "neo-tree.nvim" then
165 autocmd("TermClose", {
166 pattern = { "*lazygit", "*gitui" },
167 desc = "Refresh Neo-Tree git when closing lazygit/gitui",
168 callback = function()
169 local manager_avail, manager = pcall(require, "neo-tree.sources.manager")
170 if manager_avail then
171 for _, source in ipairs {
172 "filesystem",
173 "git_status",
174 "document_symbols",
175 } do
176 local module = "neo-tree.sources." .. source
177 if package.loaded[module] then
178 manager.refresh(require(module).name)
179 end
180 end
181 end
182 end,
183 })
184end
185
186-- 5. Create parent directories when saving a file.
187autocmd("BufWritePre", {
188 desc = "Automatically create parent directories if they don't exist when saving a file",
189 callback = function(args)
190 local buf_is_valid_and_listed = vim.api.nvim_buf_is_valid(args.buf)
191 and vim.bo[args.buf].buflisted
192
193 if buf_is_valid_and_listed then
194 vim.fn.mkdir(vim.fn.fnamemodify(
195 vim.loop.fs_realpath(args.match) or args.match, ":p:h"), "p")
196 end
197 end,
198})
199
200-- ## COOL HACKS ------------------------------------------------------------
201-- 6. Effect: URL underline.
202vim.api.nvim_set_hl(0, 'HighlightURL', { underline = true })
203autocmd({ "VimEnter", "FileType", "BufEnter", "WinEnter" }, {
204 desc = "URL Highlighting",
205 callback = function() utils.set_url_effect() end,
206})
207
208-- 7. Customize right click contextual menu.
209autocmd("VimEnter", {
210 desc = "Disable right contextual menu warning message",
211 callback = function()
212 -- Disable right click message
213 vim.api.nvim_command [[aunmenu PopUp.How-to\ disable\ mouse]]
214 -- vim.api.nvim_command [[aunmenu PopUp.-1-]] -- You can remode a separator like this.
215 vim.api.nvim_command [[menu PopUp.Toggle\ \Breakpoint <cmd>:lua require('dap').toggle_breakpoint()<CR>]]
216 vim.api.nvim_command [[menu PopUp.-2- <Nop>]]
217 vim.api.nvim_command [[menu PopUp.Start\ \Compiler <cmd>:CompilerOpen<CR>]]
218 vim.api.nvim_command [[menu PopUp.Start\ \Debugger <cmd>:DapContinue<CR>]]
219 vim.api.nvim_command [[menu PopUp.Run\ \Test <cmd>:Neotest run<CR>]]
220 end,
221})
222
223-- 8. Unlist quickfix buffers if the filetype changes.
224autocmd("FileType", {
225 desc = "Unlist quickfist buffers",
226 pattern = "qf",
227 callback = function() vim.opt_local.buflisted = false end,
228})
229
230-- 9. Close all notifications on BufWritePre.
231autocmd("BufWritePre", {
232 desc = "Close all notifications on BufWritePre",
233 callback = function()
234 require("notify").dismiss({ pending = true, silent = true })
235 end,
236})
237
238-- ## COMMANDS --------------------------------------------------------------
239
240-- 10. Testing commands
241-- Aditional commands to the ones implemented in neotest.
242-------------------------------------------------------------------
243
244-- Customize this command to work as you like
245cmd("TestNodejs", function()
246 vim.cmd ":ProjectRoot" -- cd the project root (requires project.nvim)
247 vim.cmd ":TermExec cmd='npm run test'" -- convention to run tests on nodejs
248 -- You can generate code coverage by add this to your project's packages.json
249 -- "tests": "jest --coverage"
250end, { desc = "Run all unit tests for the current nodejs project" })
251
252-- Customize this command to work as you like
253cmd("TestNodejsE2e", function()
254 vim.cmd ":ProjectRoot" -- cd the project root (requires project.nvim)
255 vim.cmd ":TermExec cmd='npm run e2e'" -- Conventional way to call e2e in nodejs (requires ToggleTerm)
256end, { desc = "Run e2e tests for the current nodejs project" })
257
258-- Extra commands
259----------------------------------------------
260
261-- Change working directory
262cmd("Cwd", function()
263 vim.cmd ":cd %:p:h"
264 vim.cmd ":pwd"
265end, { desc = "cd current file's directory" })
266
267-- Set working directory (alias)
268cmd("Swd", function()
269 vim.cmd ":cd %:p:h"
270 vim.cmd ":pwd"
271end, { desc = "cd current file's directory" })
272
273-- Write all buffers
274cmd("WriteAllBuffers", function()
275 vim.cmd "wa"
276end, { desc = "Write all changed buffers" })
277
278-- Close all notifications
279cmd("CloseNotifications", function()
280 require("notify").dismiss({ pending = true, silent = true })
281end, { desc = "Dismiss all notifications" })