autocmds
Source: autocmds · updated 2026-06-11 · 🔒 secret gist
Synced verbatim from gist.github.com/bl9.
-- Tabline that shows ws_name tab-local variable when set, else falls back to
-- the buffer name. Used by LoadWorkspace to label tabs by folder name.
_G.WsTabline = function()
local s = ""
local ntabs = vim.fn.tabpagenr("$")
for i = 1, ntabs do
local label = vim.fn.gettabvar(i, "ws_name", "")
if label == "" then
local bufs = vim.fn.tabpagebuflist(i)
label = vim.fn.fnamemodify(vim.fn.bufname(bufs[1]), ":t")
if label == "" then label = "[No Name]" end
end
if i == vim.fn.tabpagenr() then
s = s .. "%#TabLineSel# " .. i .. ":" .. label .. " "
else
s = s .. "%#TabLine# " .. i .. ":" .. label .. " "
end
end
return s .. "%#TabLineFill#"
end
vim.opt.tabline = "%!v:lua.WsTabline()"
vim.opt.showtabline = 2
-- Cycle tabs with Alt-l / Alt-h
vim.keymap.set("n", "<A-l>", "<cmd>tabnext<cr>", { desc = "Next tab" })
vim.keymap.set("n", "<A-h>", "<cmd>tabprev<cr>", { desc = "Prev tab" })
-- Fuzzy-pick a tab with <leader>T (Alt+t unreliable on macOS)
vim.keymap.set("n", "<leader>T", function()
local tabs = {}
for i = 1, vim.fn.tabpagenr("$") do
local name = vim.fn.gettabvar(i, "ws_name", "")
if name == "" then
local bufs = vim.fn.tabpagebuflist(i)
name = vim.fn.fnamemodify(vim.fn.bufname(bufs[1]), ":t")
if name == "" then name = "[No Name]" end
end
table.insert(tabs, { text = i .. ": " .. name, idx = i })
end
Snacks.picker.pick({
title = "Tabs",
items = tabs,
format = function(item) return { { item.text } } end,
confirm = function(picker, item)
picker:close()
if item then vim.cmd("tabn " .. item.idx) end
end,
})
end, { desc = "Pick tab" })
local function open_vscode_workspace(workspace_file)
local f = io.open(workspace_file, "r")
if not f then
vim.notify("Could not open workspace file: " .. workspace_file, vim.log.levels.ERROR)
return
end
local content = f:read("*a")
f:close()
local ok, ws = pcall(vim.json.decode, content)
if not ok or not ws.folders then
vim.notify("Failed to parse workspace file", vim.log.levels.ERROR)
return
end
local ws_dir = vim.fn.fnamemodify(workspace_file, ":p:h")
local paths = {}
for _, folder in ipairs(ws.folders) do
local path = folder.path
if not vim.startswith(path, "/") then
path = vim.fn.simplify(ws_dir .. "/" .. path)
end
if vim.fn.isdirectory(path) == 0 then
vim.notify("Skipping missing directory: " .. path, vim.log.levels.WARN)
else
table.insert(paths, path)
end
end
if #paths == 0 then return end
for i, path in ipairs(paths) do
local name = vim.fn.fnamemodify(path, ":t")
if i == 1 then
vim.cmd("tcd " .. vim.fn.fnameescape(path))
else
vim.cmd("tabnew")
vim.cmd("tcd " .. vim.fn.fnameescape(path))
end
vim.t.ws_name = name
end
-- Open tab 1's explorer immediately; lazily open the rest on first TabEnter.
-- Opening multiple Snacks pickers in the same event loop causes BufWinEnter
-- nesting (E218) due to snacks' internal nvim_win_set_buf autocmd cascade.
vim.cmd("tabfirst")
local pending = {}
for i = 2, #paths do
pending[i] = paths[i]
end
local aug = vim.api.nvim_create_augroup("LoadWorkspaceExplorers", { clear = true })
vim.api.nvim_create_autocmd("TabEnter", {
group = aug,
callback = function()
local tabnr = vim.fn.tabpagenr()
if pending[tabnr] then
local p = pending[tabnr]
pending[tabnr] = nil
vim.schedule(function()
Snacks.explorer.open({ cwd = p })
end)
-- Clean up autocmd once all tabs have been visited
if vim.tbl_isempty(pending) then
vim.api.nvim_del_augroup_by_id(aug)
end
end
end,
})
-- Open the first tab's explorer last so it ends up focused
vim.schedule(function()
Snacks.explorer.open({ cwd = paths[1] })
end)
end
vim.api.nvim_create_user_command("LoadWorkspace", function(opts)
open_vscode_workspace(opts.args)
end, { nargs = 1, complete = "file" })