local M = {} function M.safe_require(name) local ok, mod = pcall(require, name) if ok then return mod end return nil end -- Number of parent directories to show in full in the tabline -- The rest will be shortened according to tabline_shorten_length -- Example: with full_parents = 1, /path/to/my/project/src/file.txt becomes pat/to/my/project/src/file.txt -- Example: with full_parents = 2, it becomes pat/to/my/project/src/file.txt M.tabline_full_parents = 1 -- Number of characters to show for shortened directory names in the tabline -- Example: with shorten_length = 3, /path/to/my becomes pat/to/my -- Example: with shorten_length = 1, /path/to/my becomes p/t/m M.tabline_shorten_length = 3 -- Get all modified, deleted, and untracked Git files with status -- Returns a list suitable for setqflist() or nil on error function M.git_changed_files() -- Use git status --porcelain to get all changes with status indicators -- Format: "XY filename" where X=index status, Y=worktree status -- Status codes: M=modified, D=deleted, A=added, ??=untracked, etc. local handle = io.popen('git status --porcelain 2>/dev/null') if not handle then vim.notify('Failed to run git status', vim.log.levels.ERROR) return nil end local result = handle:read('*a') handle:close() if result == '' then vim.notify('No git changes found', vim.log.levels.INFO) return nil end local qf_list = {} local status_map = { ['M'] = 'Modified', ['A'] = 'Added', ['D'] = 'Deleted', ['R'] = 'Renamed', ['C'] = 'Copied', ['U'] = 'Unmerged', ['?'] = 'Untracked', } for line in result:gmatch('[^\n]+') do -- Parse porcelain format: "XY filename" or "XY original -> renamed" local index_status = line:sub(1, 1) local work_status = line:sub(2, 2) local filename = line:sub(4) -- Skip "XY " prefix -- Determine status text (worktree takes precedence over index) local status_code = work_status ~= ' ' and work_status or index_status local status_text = status_map[status_code] or 'Changed' table.insert(qf_list, { filename = filename, lnum = 1, text = status_text, }) end return qf_list end -- Custom tabline function -- Shows configurable number of full parent directories, shortens the rest function M.custom_tabline() local tabline = '' local num_tabs = vim.fn.tabpagenr('$') for i = 1, num_tabs do local buflist = vim.fn.tabpagebuflist(i) local winnr = vim.fn.tabpagewinnr(i) local bufnr = buflist[winnr] local bufname = vim.fn.bufname(bufnr) local bufmodified = vim.fn.getbufvar(bufnr, "&modified") -- Highlight for the tab if i == vim.fn.tabpagenr() then tabline = tabline .. '%#TabLineSel#' else tabline = tabline .. '%#TabLine#' end -- Tab number tabline = tabline .. ' ' .. i .. ' ' -- Format the filename with smart path shortening local filename if bufname == '' then filename = '[No Name]' else -- Get the full path relative to cwd if possible local path = vim.fn.fnamemodify(bufname, ':~:.') -- Split path into components local parts = vim.split(path, '/', { plain = true }) if #parts > M.tabline_full_parents + 1 then -- We have enough parts to do smart shortening local result = {} -- Shorten the leading directories (all but the last full_parents + filename) local num_to_shorten = #parts - M.tabline_full_parents - 1 for j = 1, num_to_shorten do table.insert(result, parts[j]:sub(1, M.tabline_shorten_length)) end -- Add the full parent directories for j = num_to_shorten + 1, #parts - 1 do table.insert(result, parts[j]) end -- Add the filename table.insert(result, parts[#parts]) filename = table.concat(result, '/') else -- Path is short enough, just use it as-is filename = path end end -- Add modified flag if bufmodified == 1 then filename = filename .. ' [+]' end tabline = tabline .. filename .. ' ' end -- Fill the rest with TabLineFill tabline = tabline .. '%#TabLineFill#%T' -- Right-align: show tab page count if more than one tab if num_tabs > 1 then tabline = tabline .. '%=%#TabLine# ' .. num_tabs .. ' tabs ' end return tabline end return M