diff --git a/doc/gitsigns.txt b/doc/gitsigns.txt index 72926df..befab90 100644 --- a/doc/gitsigns.txt +++ b/doc/gitsigns.txt @@ -298,8 +298,9 @@ get_hunks({bufnr}) *gitsigns.get_hunks()* • `"start"`: Line number (1-based) • `"count"`: Line count -setqflist({target}) *gitsigns.setqflist()* - Populate the quickfix list with hunks. +setqflist({target}, {opts}) *gitsigns.setqflist()* + Populate the quickfix list with hunks. Automatically opens the + quickfix window. Attributes: ~ {async} @@ -312,10 +313,23 @@ setqflist({target}) *gitsigns.setqflist()* number. `0` for current buffer (default). • `"attached"`: All attached buffers. • `"all"`: All modified files for each git - directory of all attached buffers. + directory of all attached buffers in addition + to the current working directory. + {opts} (table|nil): + Additional options: + • {use_location_list}: (boolean) + Populate the location list instead of the + quickfix list. Default to `false`. + • {nr}: (integer) + Window number or ID when using location list. + Expand folds when navigating to a hunk which is + inside a fold. Defaults to `0`. setloclist({nr}, {target}) *gitsigns.setloclist()* - Populate the location list with hunks. + Populate the location list with hunks. Automatically opens the + location list window. + + Alias for: `setqflist({target}, { use_location_list = true, nr = {nr} }` Attributes: ~ {async} diff --git a/lua/gitsigns.lua b/lua/gitsigns.lua index 8830768..120f069 100644 --- a/lua/gitsigns.lua +++ b/lua/gitsigns.lua @@ -53,7 +53,7 @@ local handle_moved = function(bufnr, bcache, old_relpath) end do_update = true elseif git_obj.orig_relpath then - local orig_file = git_obj.toplevel .. util.path_sep .. git_obj.orig_relpath + local orig_file = git_obj.repo.toplevel .. util.path_sep .. git_obj.orig_relpath if git_obj:file_info(orig_file) then dprintf('Moved file reset') git_obj.relpath = git_obj.orig_relpath @@ -65,7 +65,7 @@ local handle_moved = function(bufnr, bcache, old_relpath) end if do_update then - git_obj.file = git_obj.toplevel .. util.path_sep .. git_obj.relpath + git_obj.file = git_obj.repo.toplevel .. util.path_sep .. git_obj.relpath bcache.file = git_obj.file bcache.git_obj:update_file_info() scheduler() @@ -97,10 +97,10 @@ local watch_index = function(bufnr, gitdir) local git_obj = bcache.git_obj - git_obj:update_abbrev_head() + git_obj.repo:update_abbrev_head() scheduler() - Status:update(bufnr, { head = git_obj.abbrev_head }) + Status:update(bufnr, { head = git_obj.repo.abbrev_head }) local was_tracked = git_obj.object_name ~= nil local old_relpath = git_obj.relpath @@ -240,20 +240,21 @@ local attach0 = function(cbuf, aucmd) end local git_obj = git.Obj.new(file) + local repo = git_obj.repo - if not git_obj.gitdir then + if not repo.gitdir then dprint('Not in git repo') return end scheduler() Status:update(cbuf, { - head = git_obj.abbrev_head, - root = git_obj.toplevel, - gitdir = git_obj.gitdir, + head = repo.abbrev_head, + root = repo.toplevel, + gitdir = repo.gitdir, }) - if vim.startswith(file, git_obj.gitdir .. util.path_sep) then + if vim.startswith(file, repo.gitdir .. util.path_sep) then dprint('In non-standard git dir') return end @@ -286,7 +287,7 @@ local attach0 = function(cbuf, aucmd) base = config.base, file = file, commit = commit, - index_watcher = watch_index(cbuf, git_obj.gitdir), + index_watcher = watch_index(cbuf, repo.gitdir), git_obj = git_obj, }) @@ -413,8 +414,9 @@ local _update_cwd_head = function() local cwd = vim.fn.getcwd() local head for _, bcache in pairs(cache) do - if bcache.git_obj.toplevel == cwd then - head = bcache.git_obj.abbrev_head + local repo = bcache.git_obj.repo + if repo.toplevel == cwd then + head = repo.abbrev_head break end end diff --git a/lua/gitsigns/actions.lua b/lua/gitsigns/actions.lua index 4341efe..8aa5da8 100644 --- a/lua/gitsigns/actions.lua +++ b/lua/gitsigns/actions.lua @@ -1,6 +1,5 @@ local void = require('plenary.async.async').void local scheduler = require('plenary.async.util').scheduler -local block_on = require('plenary.async.util').block_on local Status = require("gitsigns.status") local config = require('gitsigns.config').config @@ -28,7 +27,12 @@ local NavHunkOpts = {} -local M = {} +local M = {QFListOpts = {}, } + + + + + @@ -452,13 +456,13 @@ local function run_diff(a, b) return f(a, b, diff_opts.algorithm, diff_opts.indent_heuristic) end -local function get_blame_hunk(git_obj, info) +local function get_blame_hunk(repo, info) local a = {} if info.previous then - a = git_obj:get_show_text(info.previous_sha .. ':' .. info.previous_filename) + a = repo:get_show_text(info.previous_sha .. ':' .. info.previous_filename) end - local b = git_obj:get_show_text(info.sha .. ':' .. info.filename) + local b = repo:get_show_text(info.sha .. ':' .. info.filename) local hunks = run_diff(a, b) local hunk, i = gs_hunks.find_hunk(info.orig_lnum, hunks) return hunk, i, #hunks @@ -516,7 +520,7 @@ M.blame_line = void(function(full) vim.list_extend(lines, commit_message) if full then - hunk, ihunk, nhunk = get_blame_hunk(bcache.git_obj, result) + hunk, ihunk, nhunk = get_blame_hunk(bcache.git_obj.repo, result) end else lines[#lines + 1] = result.author @@ -595,7 +599,7 @@ M.diffthis = void(function(base) local err local comp_obj = bcache:get_compare_obj(calc_base(base)) if base then - text, err = bcache.git_obj:get_show_text(comp_obj) + text, err = bcache.git_obj.repo:get_show_text(comp_obj) if err then print(err) return @@ -607,7 +611,7 @@ M.diffthis = void(function(base) local ft = api.nvim_buf_get_option(bufnr, 'filetype') - local bufname = string.format('gitsigns://%s/%s', bcache.git_obj.gitdir, comp_obj) + local bufname = string.format('gitsigns://%s/%s', bcache.git_obj.repo.gitdir, comp_obj) vim.cmd("keepalt aboveleft vertical split " .. bufname) @@ -647,54 +651,59 @@ local function buildqflist(target) local bufnr = target if not cache[bufnr] then return end hunks_to_qflist(bufnr, cache[bufnr].hunks, qflist) - elseif target == 'attached' or target == 'all' then - local gitdirs_done = {} + elseif target == 'attached' then for bufnr, bcache in pairs(cache) do hunks_to_qflist(bufnr, bcache.hunks, qflist) + end + elseif target == 'all' then + local repos = {} + for _, bcache in pairs(cache) do + local repo = bcache.git_obj.repo + repos[repo] = true + end - if target == 'all' then - local git_obj = bcache.git_obj - if not gitdirs_done[git_obj.gitdir] then - for _, f in ipairs(git_obj:files_changed()) do - local f_abs = git_obj.toplevel .. '/' .. f - local stat = vim.loop.fs_stat(f_abs) - if stat and stat.type == 'file' then - local hunks = run_diff( - git_obj:get_show_text(':0:' .. f), - util.file_lines(f_abs)) + local repo = git.Repo.new(vim.fn.getcwd()) + repos[repo] = true - hunks_to_qflist(f_abs, hunks, qflist) - end - end + for r, _ in pairs(repos) do + for _, f in ipairs(r:files_changed()) do + local f_abs = r.toplevel .. '/' .. f + local stat = vim.loop.fs_stat(f_abs) + if stat and stat.type == 'file' then + local a = r:get_show_text(':0:' .. f) + scheduler() + local hunks = run_diff(a, util.file_lines(f_abs)) + hunks_to_qflist(f_abs, hunks, qflist) end - gitdirs_done[git_obj.gitdir] = true end end + end return qflist end -M.setqflist = function(target) - block_on(function() - local qflist = buildqflist(target) - scheduler() - vim.fn.setqflist({}, ' ', { - items = qflist, - title = 'Hunks', - }) - end) -end +M.setqflist = void(function(target, opts) + opts = opts or {} + local qfopts = { + items = buildqflist(target), + title = 'Hunks', + } + scheduler() + if opts.use_location_list then + local nr = opts.nr or 0 + vim.fn.setloclist(nr, {}, ' ', qfopts) + vim.cmd([[lopen]]) + else + vim.fn.setqflist({}, ' ', qfopts) + vim.cmd([[copen]]) + end +end) M.setloclist = function(nr, target) - block_on(function() - nr = nr or 0 - local qflist = buildqflist(target) - scheduler() - vim.fn.setloclist(nr, {}, ' ', { - items = qflist, - title = 'Hunks', - }) - end) + M.setqflist(target, { + nr = nr, + use_location_list = true, + }) end M.get_actions = function() diff --git a/lua/gitsigns/current_line_blame.lua b/lua/gitsigns/current_line_blame.lua index 5a3e62b..37edba3 100644 --- a/lua/gitsigns/current_line_blame.lua +++ b/lua/gitsigns/current_line_blame.lua @@ -54,7 +54,7 @@ M.update = void(function() api.nvim_buf_set_extmark(bufnr, namespace, lnum - 1, 0, { id = 1, virt_text = config.current_line_blame_formatter( - bcache.git_obj.username, + bcache.git_obj.repo.username, result, config.current_line_blame_formatter_opts), diff --git a/lua/gitsigns/git.lua b/lua/gitsigns/git.lua index ad0112b..de6dc66 100644 --- a/lua/gitsigns/git.lua +++ b/lua/gitsigns/git.lua @@ -21,7 +21,14 @@ local GJobSpec = {} -local M = {BlameInfo = {}, Version = {}, Obj = {}, } +local M = {BlameInfo = {}, Version = {}, Repo = {}, Obj = {}, } + + + + + + + @@ -87,6 +94,7 @@ local M = {BlameInfo = {}, Version = {}, Obj = {}, } local Obj = M.Obj +local Repo = M.Repo local function parse_version(version) assert(version:match('%d+%.%d+%.%w+'), 'Invalid git version: ' .. version) @@ -216,16 +224,60 @@ end -Obj.command = function(self, args, spec) +Repo.command = function(self, args, spec) spec = spec or {} spec.cwd = self.toplevel return M.command({ '--git-dir=' .. self.gitdir, unpack(args) }, spec) end -Obj.update_abbrev_head = function(self) +Repo.files_changed = function(self) + local results = self:command({ 'status', '--porcelain', '--ignore-submodules' }) + + local ret = {} + for _, line in ipairs(results) do + if line:sub(1, 2):match('^.M') then + ret[#ret + 1] = line:sub(4, -1) + end + end + return ret +end + + +Repo.get_show_text = function(self, object) + return self:command({ 'show', object }, { supress_stderr = true }) +end + +Repo.update_abbrev_head = function(self) _, _, self.abbrev_head = M.get_repo_info(self.toplevel) end +Repo.new = function(dir) + local self = setmetatable({}, { __index = Repo }) + + self.username = M.command({ 'config', 'user.name' })[1] + self.toplevel, self.gitdir, self.abbrev_head = M.get_repo_info(dir) + + + if M.enable_yadm and not self.gitdir then + if vim.startswith(dir, os.getenv('HOME')) and + #M.command({ 'ls-files', dir }, { command = 'yadm' }) ~= 0 then + self.toplevel, self.gitdir, self.abbrev_head = + M.get_repo_info(dir, 'yadm') + end + end + + return self +end + + + + + + +Obj.command = function(self, args, spec) + return self.repo:command(args, spec) +end + Obj.update_file_info = function(self) local old_object_name = self.object_name _, self.object_name, self.mode_bits, self.has_conflicts = self:file_info() @@ -270,13 +322,8 @@ Obj.unstage_file = function(self) self:command({ 'reset', self.file }) end - -Obj.get_show_text = function(self, object) - return self:command({ 'show', object }, { supress_stderr = true }) -end - Obj.run_blame = function(self, lines, lnum) - if not self.object_name or self.abbrev_head == '' then + if not self.object_name or self.repo.abbrev_head == '' then @@ -356,43 +403,20 @@ Obj.has_moved = function(self) if orig_relpath == orig then self.orig_relpath = orig_relpath self.relpath = new - self.file = self.toplevel .. '/' .. new + self.file = self.repo.toplevel .. '/' .. new return new end end end end -Obj.files_changed = function(self) - local results = self:command({ 'status', '--porcelain', '--ignore-submodules' }) - - local ret = {} - for _, line in ipairs(results) do - if line:sub(1, 2):match('^.M') then - ret[#ret + 1] = line:sub(4, -1) - end - end - return ret -end - Obj.new = function(file) local self = setmetatable({}, { __index = Obj }) self.file = file - self.username = M.command({ 'config', 'user.name' })[1] - self.toplevel, self.gitdir, self.abbrev_head = - M.get_repo_info(util.dirname(file)) + self.repo = Repo.new(util.dirname(file)) - - if M.enable_yadm and not self.gitdir then - if vim.startswith(file, os.getenv('HOME')) and - #M.command({ 'ls-files', file }, { command = 'yadm' }) ~= 0 then - self.toplevel, self.gitdir, self.abbrev_head = - M.get_repo_info(util.dirname(file), 'yadm') - end - end - - if not self.gitdir then + if not self.repo.gitdir then return self end diff --git a/lua/gitsigns/manager.lua b/lua/gitsigns/manager.lua index b4bf781..c5cd9d2 100644 --- a/lua/gitsigns/manager.lua +++ b/lua/gitsigns/manager.lua @@ -228,7 +228,7 @@ local update0 = function(bufnr, bcache) end if not bcache.compare_text or config._refresh_staged_on_update then - bcache.compare_text = git_obj:get_show_text(compare_object) + bcache.compare_text = git_obj.repo:get_show_text(compare_object) end bcache.hunks = run_diff(bcache.compare_text, buftext, @@ -243,7 +243,7 @@ local update0 = function(bufnr, bcache) M.apply_win_signs(bufnr, bcache.pending_signs) end local summary = gs_hunks.get_summary(bcache.hunks) - summary.head = git_obj.abbrev_head + summary.head = git_obj.repo.abbrev_head Status:update(bufnr, summary) update_cnt = update_cnt + 1 diff --git a/teal/gitsigns.tl b/teal/gitsigns.tl index 0e91b9a..37ea1a3 100644 --- a/teal/gitsigns.tl +++ b/teal/gitsigns.tl @@ -53,7 +53,7 @@ local handle_moved = function(bufnr: integer, bcache: CacheEntry, old_relpath: s end do_update = true elseif git_obj.orig_relpath then - local orig_file = git_obj.toplevel..util.path_sep..git_obj.orig_relpath + local orig_file = git_obj.repo.toplevel..util.path_sep..git_obj.orig_relpath if git_obj:file_info(orig_file) then dprintf('Moved file reset') git_obj.relpath = git_obj.orig_relpath @@ -65,7 +65,7 @@ local handle_moved = function(bufnr: integer, bcache: CacheEntry, old_relpath: s end if do_update then - git_obj.file = git_obj.toplevel..util.path_sep..git_obj.relpath + git_obj.file = git_obj.repo.toplevel..util.path_sep..git_obj.relpath bcache.file = git_obj.file bcache.git_obj:update_file_info() scheduler() @@ -97,10 +97,10 @@ local watch_index = function(bufnr: integer, gitdir: string): uv.FSPollObj local git_obj = bcache.git_obj - git_obj:update_abbrev_head() + git_obj.repo:update_abbrev_head() scheduler() - Status:update(bufnr, { head = git_obj.abbrev_head}) + Status:update(bufnr, { head = git_obj.repo.abbrev_head}) local was_tracked = git_obj.object_name ~= nil local old_relpath = git_obj.relpath @@ -240,20 +240,21 @@ local attach0 = function(cbuf: integer, aucmd: string) end local git_obj = git.Obj.new(file) + local repo = git_obj.repo - if not git_obj.gitdir then + if not repo.gitdir then dprint('Not in git repo') return end scheduler() Status:update(cbuf, { - head = git_obj.abbrev_head, - root = git_obj.toplevel, - gitdir = git_obj.gitdir, + head = repo.abbrev_head, + root = repo.toplevel, + gitdir = repo.gitdir, }) - if vim.startswith(file, git_obj.gitdir..util.path_sep) then + if vim.startswith(file, repo.gitdir..util.path_sep) then dprint('In non-standard git dir') return end @@ -286,7 +287,7 @@ local attach0 = function(cbuf: integer, aucmd: string) base = config.base, file = file, commit = commit, - index_watcher = watch_index(cbuf, git_obj.gitdir), + index_watcher = watch_index(cbuf, repo.gitdir), git_obj = git_obj } @@ -413,8 +414,9 @@ local _update_cwd_head = function() local cwd = vim.fn.getcwd() local head: string for _, bcache in pairs(cache as {number:CacheEntry}) do - if bcache.git_obj.toplevel == cwd then - head = bcache.git_obj.abbrev_head + local repo = bcache.git_obj.repo + if repo.toplevel == cwd then + head = repo.abbrev_head break end end diff --git a/teal/gitsigns/actions.tl b/teal/gitsigns/actions.tl index 7f8c773..84940f3 100644 --- a/teal/gitsigns/actions.tl +++ b/teal/gitsigns/actions.tl @@ -1,6 +1,5 @@ local void = require('plenary.async.async').void local scheduler = require('plenary.async.util').scheduler -local block_on = require('plenary.async.util').block_on local Status = require("gitsigns.status") local config = require('gitsigns.config').config @@ -48,7 +47,12 @@ local record M reset_base : function(global: boolean) diffthis : function -- function(base: string) - setqflist : function(target:integer|string) + record QFListOpts + use_location_list: boolean + nr: integer + end + + setqflist : function(target:integer|string, opts: QFListOpts, callback: function) setloclist : function(nr: integer, target:integer|string) user_range : {integer, integer} @@ -452,13 +456,13 @@ local function run_diff(a: {string}, b: {string}): {Hunk} return f(a, b, diff_opts.algorithm, diff_opts.indent_heuristic) end -local function get_blame_hunk(git_obj: git.Obj, info: git.BlameInfo): Hunk, integer, integer +local function get_blame_hunk(repo: git.Repo, info: git.BlameInfo): Hunk, integer, integer local a = {} -- If no previous so sha of blame added the file if info.previous then - a = git_obj:get_show_text(info.previous_sha..':'..info.previous_filename) + a = repo:get_show_text(info.previous_sha..':'..info.previous_filename) end - local b = git_obj:get_show_text(info.sha..':'..info.filename) + local b = repo:get_show_text(info.sha..':'..info.filename) local hunks = run_diff(a, b) local hunk, i = gs_hunks.find_hunk(info.orig_lnum, hunks) return hunk, i, #hunks @@ -516,7 +520,7 @@ M.blame_line = void(function(full: boolean) vim.list_extend(lines, commit_message) if full then - hunk, ihunk, nhunk = get_blame_hunk(bcache.git_obj, result) + hunk, ihunk, nhunk = get_blame_hunk(bcache.git_obj.repo, result) end else lines[#lines+1] = result.author @@ -595,7 +599,7 @@ M.diffthis = void(function(base: string) local err: string local comp_obj = bcache:get_compare_obj(calc_base(base)) if base then - text, err = bcache.git_obj:get_show_text(comp_obj) + text, err = bcache.git_obj.repo:get_show_text(comp_obj) if err then print(err) return @@ -607,7 +611,7 @@ M.diffthis = void(function(base: string) local ft = api.nvim_buf_get_option(bufnr, 'filetype') - local bufname = string.format('gitsigns://%s/%s', bcache.git_obj.gitdir, comp_obj) + local bufname = string.format('gitsigns://%s/%s', bcache.git_obj.repo.gitdir, comp_obj) -- TDOD lewis6991 (27/05/21): Respect 'vertical' in diffopt vim.cmd("keepalt aboveleft vertical split "..bufname) @@ -647,54 +651,59 @@ local function buildqflist(target: integer|string): {vim.fn.QFItem} local bufnr = target as integer if not cache[bufnr] then return end hunks_to_qflist(bufnr, cache[bufnr].hunks, qflist) - elseif target == 'attached' or target == 'all' then - local gitdirs_done: {string:boolean} = {} + elseif target == 'attached' then for bufnr, bcache in pairs(cache as {integer:CacheEntry}) do hunks_to_qflist(bufnr, bcache.hunks, qflist) + end + elseif target == 'all' then + local repos: {git.Repo:boolean} = {} + for _, bcache in pairs(cache as {integer:CacheEntry}) do + local repo = bcache.git_obj.repo + repos[repo] = true + end - if target == 'all' then - local git_obj = bcache.git_obj - if not gitdirs_done[git_obj.gitdir] then - for _, f in ipairs(git_obj:files_changed()) do - local f_abs = git_obj.toplevel..'/'..f - local stat = vim.loop.fs_stat(f_abs) - if stat and stat.type == 'file' then - local hunks = run_diff( - git_obj:get_show_text(':0:'..f), - util.file_lines(f_abs) - ) - hunks_to_qflist(f_abs, hunks, qflist) - end - end + local repo = git.Repo.new(vim.fn.getcwd()) + repos[repo] = true + + for r, _ in pairs(repos) do + for _, f in ipairs(r:files_changed()) do + local f_abs = r.toplevel..'/'..f + local stat = vim.loop.fs_stat(f_abs) + if stat and stat.type == 'file' then + local a = r:get_show_text(':0:'..f) + scheduler() + local hunks = run_diff(a, util.file_lines(f_abs)) + hunks_to_qflist(f_abs, hunks, qflist) end - gitdirs_done[git_obj.gitdir] = true end end + end return qflist end -M.setqflist = function(target: integer|string) - block_on(function() - local qflist = buildqflist(target) - scheduler() - vim.fn.setqflist({}, ' ', { - items = qflist, - title = 'Hunks' - }) - end) -end +M.setqflist = void(function(target: integer|string, opts: M.QFListOpts) + opts = opts or {} + local qfopts = { + items = buildqflist(target), + title = 'Hunks' + } + scheduler() + if opts.use_location_list then + local nr = opts.nr or 0 + vim.fn.setloclist(nr, {}, ' ', qfopts) + vim.cmd[[lopen]] + else + vim.fn.setqflist({}, ' ', qfopts) + vim.cmd[[copen]] + end +end) M.setloclist = function(nr: integer, target: integer|string) - block_on(function() - nr = nr or 0 - local qflist = buildqflist(target) - scheduler() - vim.fn.setloclist(nr, {}, ' ', { - items = qflist, - title = 'Hunks' - }) - end) + M.setqflist(target, { + nr = nr, + use_location_list = true + }) end M.get_actions = function(): {string:function} diff --git a/teal/gitsigns/current_line_blame.tl b/teal/gitsigns/current_line_blame.tl index 4efd9da..cd8be83 100644 --- a/teal/gitsigns/current_line_blame.tl +++ b/teal/gitsigns/current_line_blame.tl @@ -54,7 +54,7 @@ M.update = void(function() api.nvim_buf_set_extmark(bufnr, namespace, lnum-1, 0, { id = 1, virt_text = config.current_line_blame_formatter( - bcache.git_obj.username, + bcache.git_obj.repo.username, result, config.current_line_blame_formatter_opts ), diff --git a/teal/gitsigns/git.tl b/teal/gitsigns/git.tl index f452890..c46c12f 100644 --- a/teal/gitsigns/git.tl +++ b/teal/gitsigns/git.tl @@ -58,12 +58,22 @@ local record M get_repo_info: function(path: string, cmd: string): string,string,string command : function(args: {string}, spec: GJobSpec): {string}, string + record Repo + toplevel : string + gitdir : string + abbrev_head: string + username : string + + command : function(Repo, {string}, GJobSpec): {string}, string + files_changed : function(Repo): {string} + get_show_text : function(Repo, string): {string}, string + update_abbrev_head : function(Repo) + new : function(string): Repo + end + record Obj - toplevel : string - gitdir : string + repo : Repo file : string - abbrev_head : string - username : string relpath : string orig_relpath : string -- Use for tracking moved files object_name : string @@ -71,22 +81,20 @@ local record M has_conflicts : boolean command : function(Obj, {string}, GJobSpec): {string}, string - update_abbrev_head : function(Obj) update_file_info : function(Obj): boolean unstage_file : function(Obj, string, string) - get_show_text : function(Obj, string): {string}, string run_blame : function(Obj, {string}, number): BlameInfo file_info : function(Obj, string): string, string, string, boolean ensure_file_in_index : function(Obj) stage_hunks : function(Obj, {Hunk}, boolean) has_moved : function(Obj): string - files_changed : function(Obj): {string} new : function(string): Obj end end local Obj = M.Obj +local Repo = M.Repo local function parse_version(version: string): M.Version assert(version:match('%d+%.%d+%.%w+'), 'Invalid git version: '..version) @@ -212,20 +220,64 @@ M.set_version = function(version: string) end -------------------------------------------------------------------------------- --- Git object methods +-- Git repo object methods -------------------------------------------------------------------------------- --- Run git command the with the objects gitdir and toplevel -Obj.command = function(self: Obj, args: {string}, spec: GJobSpec): {string}, string +Repo.command = function(self: Repo, args: {string}, spec: GJobSpec): {string}, string spec = spec or {} spec.cwd = self.toplevel return M.command({'--git-dir='..self.gitdir, unpack(args)}, spec) end -Obj.update_abbrev_head = function(self: Obj) +Repo.files_changed = function(self: Repo): {string} + local results = self:command({ 'status', '--porcelain', '--ignore-submodules' }) + + local ret: {string} = {} + for _, line in ipairs(results) do + if line:sub(1, 2):match('^.M') then + ret[#ret+1] = line:sub(4, -1) + end + end + return ret +end + +--- Get version of file in the index, return array lines +Repo.get_show_text = function(self: Repo, object: string): {string}, string + return self:command({'show', object}, {supress_stderr = true}) +end + +Repo.update_abbrev_head = function(self: Repo) _, _, self.abbrev_head = M.get_repo_info(self.toplevel) end +Repo.new = function(dir: string): Repo + local self = setmetatable({} as Repo, {__index = Repo}) + + self.username = M.command({'config', 'user.name'})[1] + self.toplevel, self.gitdir, self.abbrev_head = M.get_repo_info(dir) + + -- Try yadm + if M.enable_yadm and not self.gitdir then + if vim.startswith(dir, os.getenv('HOME')) + and #M.command({'ls-files', dir}, {command = 'yadm'}) ~= 0 then + self.toplevel, self.gitdir, self.abbrev_head = + M.get_repo_info(dir, 'yadm') + end + end + + return self +end + +-------------------------------------------------------------------------------- +-- Git object methods +-------------------------------------------------------------------------------- + +--- Run git command the with the objects gitdir and toplevel +Obj.command = function(self: Obj, args: {string}, spec: GJobSpec): {string}, string + return self.repo:command(args, spec) +end + Obj.update_file_info = function(self: Obj): boolean local old_object_name = self.object_name _, self.object_name, self.mode_bits, self.has_conflicts = self:file_info() @@ -270,13 +322,8 @@ Obj.unstage_file = function(self: Obj) self:command{'reset', self.file } end ---- Get version of file in the index, return array lines -Obj.get_show_text = function(self: Obj, object: string): {string}, string - return self:command({'show', object}, { supress_stderr = true }) -end - Obj.run_blame = function(self: Obj, lines: {string}, lnum: number): M.BlameInfo - if not self.object_name or self.abbrev_head == '' then + if not self.object_name or self.repo.abbrev_head == '' then -- As we support attaching to untracked files we need to return something if -- the file isn't isn't tracked in git. -- If abbrev_head is empty, then assume the repo has no commits @@ -356,43 +403,20 @@ Obj.has_moved = function(self: Obj): string if orig_relpath == orig then self.orig_relpath = orig_relpath self.relpath = new - self.file = self.toplevel..'/'..new + self.file = self.repo.toplevel..'/'..new return new end end end end -Obj.files_changed = function(self: Obj): {string} - local results = self:command{'status', '--porcelain', '--ignore-submodules'} - - local ret: {string} = {} - for _, line in ipairs(results) do - if line:sub(1, 2):match('^.M') then - ret[#ret+1] = line:sub(4, -1) - end - end - return ret -end - Obj.new = function(file: string): Obj local self = setmetatable({} as Obj, {__index = Obj}) self.file = file - self.username = M.command({'config', 'user.name'})[1] - self.toplevel, self.gitdir, self.abbrev_head = - M.get_repo_info(util.dirname(file)) + self.repo = Repo.new(util.dirname(file)) - -- Try yadm - if M.enable_yadm and not self.gitdir then - if vim.startswith(file, os.getenv('HOME')) - and #M.command({'ls-files', file}, {command = 'yadm'}) ~= 0 then - self.toplevel, self.gitdir, self.abbrev_head = - M.get_repo_info(util.dirname(file), 'yadm') - end - end - - if not self.gitdir then + if not self.repo.gitdir then return self end diff --git a/teal/gitsigns/manager.tl b/teal/gitsigns/manager.tl index cf10433..42c807d 100644 --- a/teal/gitsigns/manager.tl +++ b/teal/gitsigns/manager.tl @@ -228,7 +228,7 @@ local update0 = function(bufnr: integer, bcache: CacheEntry) end if not bcache.compare_text or config._refresh_staged_on_update then - bcache.compare_text = git_obj:get_show_text(compare_object) + bcache.compare_text = git_obj.repo:get_show_text(compare_object) end bcache.hunks = run_diff(bcache.compare_text, buftext, @@ -243,7 +243,7 @@ local update0 = function(bufnr: integer, bcache: CacheEntry) M.apply_win_signs(bufnr, bcache.pending_signs) end local summary = gs_hunks.get_summary(bcache.hunks) - summary.head = git_obj.abbrev_head + summary.head = git_obj.repo.abbrev_head Status:update(bufnr, summary) update_cnt = update_cnt + 1