-- Copyright (c) 2020-2021 shadmansaleh -- MIT license, see LICENSE for more details. local Branch = require('lualine.component'):new() local modules = require('lualine_require').lazy_require { utils = 'lualine.utils.utils', } -- vars Branch.git_branch = '' Branch.git_dir = '' -- os specific path separator Branch.sep = package.config:sub(1, 1) -- event watcher to watch head file -- Use file wstch for non windows and poll for windows. -- windows doesn't like file watch for some reason. Branch.file_changed = Branch.sep ~= '\\' and vim.loop.new_fs_event() or vim.loop.new_fs_poll() Branch.active_bufnr = '0' local branch_cache = {} -- stores last known branch for a buffer -- Initilizer Branch.new = function(self, options, child) local new_branch = self._parent:new(options, child or Branch) if not new_branch.options.icon then new_branch.options.icon = '' -- e0a0 end -- run watch head on load so branch is present when component is loaded Branch.find_git_dir() -- update branch state of BufEnter as different Buffer may be on different repos modules.utils.define_autocmd('BufEnter', "lua require'lualine.components.branch'.find_git_dir()") return new_branch end Branch.update_status = function(_, is_focused) if Branch.active_bufnr ~= vim.g.actual_curbuf then -- Workaround for https://github.com/hoob3rt/lualine.nvim/issues/286 -- See upstream issue https://github.com/neovim/neovim/issues/15300 -- Diff is out of sync re sync it. Branch.find_git_dir() end if not is_focused then return branch_cache[vim.fn.bufnr()] or '' end return Branch.git_branch end local git_dir_cache = {} -- Stores git paths that we already know of -- returns full path to git directory for current directory function Branch.find_git_dir() -- get file dir so we can search from that dir local file_dir = vim.fn.expand '%:p:h' local root_dir = file_dir local git_dir -- Search upward for .git file or folder while root_dir do if git_dir_cache[root_dir] then git_dir = git_dir_cache[root_dir] break end local git_path = root_dir .. Branch.sep .. '.git' local git_file_stat = vim.loop.fs_stat(git_path) if git_file_stat then if git_file_stat.type == 'directory' then git_dir = git_path elseif git_file_stat.type == 'file' then -- separate git-dir or submodule is used local file = io.open(git_path) git_dir = file:read() git_dir = git_dir:match 'gitdir: (.+)$' file:close() -- submodule / relative file path if git_dir and git_dir:sub(1, 1) ~= Branch.sep and not git_dir:match '^%a:.*$' then git_dir = git_path:match '(.*).git' .. git_dir end end if git_dir then local head_file_stat = vim.loop.fs_stat(git_dir .. Branch.sep .. 'HEAD') if head_file_stat and head_file_stat.type == 'file' then break else git_dir = nil end end end root_dir = root_dir:match('(.*)' .. Branch.sep .. '.-') end git_dir_cache[file_dir] = git_dir if Branch.git_dir ~= git_dir then Branch.git_dir = git_dir Branch.update_branch() end return git_dir end -- sets git_branch veriable to branch name or commit hash if not on branch function Branch.get_git_head(head_file) local f_head = io.open(head_file) if f_head then local HEAD = f_head:read() f_head:close() local branch = HEAD:match 'ref: refs/heads/(.+)$' if branch then Branch.git_branch = branch else Branch.git_branch = HEAD:sub(1, 6) end end return nil end -- Update branch function Branch.update_branch() Branch.active_bufnr = tostring(vim.fn.bufnr()) Branch.file_changed:stop() local git_dir = Branch.git_dir if git_dir and #git_dir > 0 then local head_file = git_dir .. Branch.sep .. 'HEAD' Branch.get_git_head(head_file) Branch.file_changed:start( head_file, Branch.sep ~= '\\' and {} or 1000, vim.schedule_wrap(function() -- reset file-watch Branch.update_branch() end) ) else -- set to '' when git dir was not found Branch.git_branch = '' end branch_cache[vim.fn.bufnr()] = Branch.git_branch end return Branch