diff --git a/lua/git/blame.lua b/lua/git/blame.lua index b6cee34..f2ef5d7 100644 --- a/lua/git/blame.lua +++ b/lua/git/blame.lua @@ -56,21 +56,6 @@ function M.state(buf) return states[buf] end ----@param ts integer ----@param tz string ----@return string -local function format_author_time(ts, tz) - local sign, hh, mm = tz:match("^([+-])(%d%d)(%d%d)$") - ---@type number - local offset = 0 - if sign then - local h = tonumber(hh) or 0 - local m = tonumber(mm) or 0 - offset = (h * 3600 + m * 60) * (sign == "-" and -1 or 1) - end - return os.date("!%Y-%m-%d %T ", ts + offset) .. tz -end - ---@param stdout string ---@return ow.Git.Blame.Result local function parse_porcelain(stdout) @@ -534,7 +519,7 @@ local function open_popup(state, lnum, watch_buf) head = { "Not Committed Yet" } else local short = sha:sub(1, 8) - local date = format_author_time(commit.author_time, commit.author_tz) + local date = util.format_git_time(commit.author_time, commit.author_tz) sha_len = #short date_col = sha_len + 2 + #commit.author + 2 head = { diff --git a/lua/git/core/util.lua b/lua/git/core/util.lua index 0c85d41..9e956da 100644 --- a/lua/git/core/util.lua +++ b/lua/git/core/util.lua @@ -120,6 +120,23 @@ function M.split_lines(content) return lines end +---@param ts integer +---@param tz string +---@return string +function M.format_git_time(ts, tz) + local sign, hh, mm = tz:match("^([+-])(%d%d)(%d%d)$") + local offset = 0 + if sign then + local h = tonumber(hh) or 0 + local m = tonumber(mm) or 0 + offset = math.floor(h * 3600 + m * 60) + if sign == "-" then + offset = -offset + end + end + return os.date("!%a %b %e %T %Y ", ts + offset) .. tz +end + ---@param buf integer ---@param ns integer ---@param lines string[] diff --git a/lua/git/object.lua b/lua/git/object.lua index 8898832..d0619ce 100644 --- a/lua/git/object.lua +++ b/lua/git/object.lua @@ -159,6 +159,29 @@ function M.buf_for(r, rev) return buf end +---@param lines string[] +local function format_header_dates(lines) + for i, line in ipairs(lines) do + if line == "" then + return + end + local prefix, ts, tz = line:match("^(author .-) (%d+) ([+-]%d%d%d%d)$") + if not prefix then + prefix, ts, tz = line:match("^(committer .-) (%d+) ([+-]%d%d%d%d)$") + end + if not prefix then + prefix, ts, tz = line:match("^(tagger .-) (%d+) ([+-]%d%d%d%d)$") + end + if prefix then + local n = math.floor(assert(tonumber(ts))) + lines[i] = ("%s %s"):format( + prefix, + util.format_git_time(n, tz --[[@as string]]) + ) + end + end +end + ---@param buf integer ---@param r ow.Git.Repo ---@param rev ow.Git.Revision @@ -189,7 +212,9 @@ local function populate(buf, r, rev, state, rev_sha) end end - util.set_buf_lines(buf, 0, -1, util.split_lines(stdout)) + local lines = util.split_lines(stdout) + format_header_dates(lines) + util.set_buf_lines(buf, 0, -1, lines) state.sha = rev_sha return true end diff --git a/test/git/blame_test.lua b/test/git/blame_test.lua index e031f65..ce8f404 100644 --- a/test/git/blame_test.lua +++ b/test/git/blame_test.lua @@ -86,9 +86,9 @@ t.test("line popup formats the datetime in the author timezone", function() ) t.truthy( (lines[1] or ""):match( - "%d%d%d%d%-%d%d%-%d%d %d%d:%d%d:%d%d [+%-]%d%d%d%d$" + "%u%l%l %u%l%l +%d+ %d%d:%d%d:%d%d %d%d%d%d [+%-]%d%d%d%d$" ), - "the head line ends with an ISO datetime and numeric timezone" + "the head line ends with a human datetime and numeric timezone" ) end) diff --git a/test/git/object_test.lua b/test/git/object_test.lua index 0082d21..b90f9ee 100644 --- a/test/git/object_test.lua +++ b/test/git/object_test.lua @@ -134,6 +134,45 @@ t.test("read_uri opens stage-0 entry as a writable index buffer", function() t.eq(vim.api.nvim_buf_get_lines(buf, 0, -1, false), { "first" }) end) +t.test("M.open renders author/committer dates, not unix timestamps", function() + local dir = h.make_repo({ a = "first\n" }) + local r = assert(require("git.core.repo").resolve(dir)) + + object.open(r, "HEAD", { split = false }) + local author = assert(find_line(0, "author "), "expected an author line") + local committer = + assert(find_line(0, "committer "), "expected a committer line") + local lines = vim.api.nvim_buf_get_lines(0, 0, -1, false) + for _, lnum in ipairs({ author, committer }) do + local line = assert(lines[lnum]) + t.truthy( + line:match( + " %u%l%l %u%l%l +%d+ %d%d:%d%d:%d%d %d%d%d%d [+-]%d%d%d%d$" + ), + "expected formatted date on: " .. line + ) + t.falsy( + line:match(" %d%d%d%d%d%d%d%d%d%d? [+-]%d%d%d%d$"), + "expected no unix timestamp on: " .. line + ) + end +end) + +t.test("M.open renders tagger dates on annotated tags", function() + local dir = h.make_repo({ a = "first\n" }) + h.git(dir, "tag", "-m", "rel", "v1") + local r = assert(require("git.core.repo").resolve(dir)) + + object.open(r, "v1", { split = false }) + local tagger = assert(find_line(0, "tagger "), "expected a tagger line") + local line = + assert(vim.api.nvim_buf_get_lines(0, tagger - 1, tagger, false)[1]) + t.truthy( + line:match(" %u%l%l %u%l%l +%d+ %d%d:%d%d:%d%d %d%d%d%d [+-]%d%d%d%d$"), + "expected formatted date on: " .. line + ) +end) + t.test("open_under_cursor on a 'tree ' line opens the tree", function() local dir = h.make_repo({ a = "first\n" }) local r = assert(require("git.core.repo").resolve(dir))