From ffe9fdfa6398f379c9aa4768327839423897a853 Mon Sep 17 00:00:00 2001 From: zegervdv Date: Fri, 14 Oct 2022 17:34:49 +0200 Subject: [PATCH 01/71] refactor: abstract the git module into a generic vcs module --- lua/diffview/scene/file_entry.lua | 4 ++-- lua/diffview/scene/layout.lua | 2 +- lua/diffview/scene/layouts/diff_1.lua | 6 +++--- lua/diffview/scene/layouts/diff_2.lua | 8 ++++---- lua/diffview/scene/layouts/diff_2_hor.lua | 4 ++-- lua/diffview/scene/layouts/diff_2_ver.lua | 4 ++-- lua/diffview/scene/layouts/diff_3.lua | 16 ++++++++-------- lua/diffview/scene/layouts/diff_4.lua | 16 ++++++++-------- lua/diffview/scene/view.lua | 2 +- lua/diffview/scene/window.lua | 10 +++++----- lua/diffview/{git => vcs}/file.lua | 16 ++++++++-------- lua/diffview/vcs/init.lua | 0 12 files changed, 44 insertions(+), 44 deletions(-) rename lua/diffview/{git => vcs}/file.lua (97%) create mode 100644 lua/diffview/vcs/init.lua diff --git a/lua/diffview/scene/file_entry.lua b/lua/diffview/scene/file_entry.lua index 7fa5c074..8619c6e0 100644 --- a/lua/diffview/scene/file_entry.lua +++ b/lua/diffview/scene/file_entry.lua @@ -1,7 +1,7 @@ local lazy = require("diffview.lazy") local oop = require("diffview.oop") ----@type git.File|LazyModule +---@type vcs.File|LazyModule local File = lazy.access("diffview.git.file", "File") ---@type RevType|LazyModule local RevType = lazy.access("diffview.git.rev", "RevType") @@ -313,7 +313,7 @@ function FileEntry.with_layout(layout_class, opt) commit = opt.commit, get_data = opt.get_data, rev = opt.rev_main, - }) --[[@as git.File ]] + }) --[[@as vcs.File ]] if layout_class:instanceof(Diff1.__get()) then main_file.nulled = layout_class.should_null(main_file.rev, opt.status, "a") diff --git a/lua/diffview/scene/layout.lua b/lua/diffview/scene/layout.lua index 60bbe94d..f887194c 100644 --- a/lua/diffview/scene/layout.lua +++ b/lua/diffview/scene/layout.lua @@ -139,7 +139,7 @@ function Layout:find_pivot() return pivot end ----@return git.File[] +---@return vcs.File[] function Layout:files() return utils.tbl_fmap(self.windows, function(v) return v.file diff --git a/lua/diffview/scene/layouts/diff_1.lua b/lua/diffview/scene/layouts/diff_1.lua index ae58d08f..0047ab20 100644 --- a/lua/diffview/scene/layouts/diff_1.lua +++ b/lua/diffview/scene/layouts/diff_1.lua @@ -4,7 +4,7 @@ local oop = require("diffview.oop") local Diff3 = lazy.access("diffview.scene.layouts.diff_3", "Diff3") ---@type Diff3|LazyModule local Diff4 = lazy.access("diffview.scene.layouts.diff_4", "Diff4") ---@type Diff4|LazyModule -local File = lazy.access("diffview.git.file", "File") ---@type git.File|LazyModule +local File = lazy.access("diffview.vcs.file", "File") ---@type vcs.File|LazyModule local Rev = lazy.access("diffview.git.rev", "Rev") ---@type Rev|LazyModule local RevType = lazy.access("diffview.git.rev", "RevType") ---@type RevType|LazyModule local Window = lazy.access("diffview.scene.window", "Window") ---@type Window|LazyModule @@ -19,7 +19,7 @@ local Diff1 = oop.create_class("Diff1", Layout) ---@alias Diff1.WindowSymbol "a" ---@class Diff1.init.Opt ----@field a git.File +---@field a vcs.File ---@field winid_a integer ---@param opt Diff1.init.Opt @@ -60,7 +60,7 @@ function Diff1:create(pivot) self.emitter:emit("create_post", self) end ----@param file git.File +---@param file vcs.File function Diff1:set_file_a(file) self.a:set_file(file) file.symbol = "a" diff --git a/lua/diffview/scene/layouts/diff_2.lua b/lua/diffview/scene/layouts/diff_2.lua index 1b18cc94..ab2940ae 100644 --- a/lua/diffview/scene/layouts/diff_2.lua +++ b/lua/diffview/scene/layouts/diff_2.lua @@ -13,8 +13,8 @@ local Diff2 = oop.create_class("Diff2", Layout) ---@alias Diff2.WindowSymbol "a"|"b" ---@class Diff2.init.Opt ----@field a git.File ----@field b git.File +---@field a vcs.File +---@field b vcs.File ---@field winid_a integer ---@field winid_b integer @@ -26,13 +26,13 @@ function Diff2:init(opt) self:use_windows(self.a, self.b) end ----@param file git.File +---@param file vcs.File function Diff2:set_file_a(file) self.a:set_file(file) file.symbol = "a" end ----@param file git.File +---@param file vcs.File function Diff2:set_file_b(file) self.b:set_file(file) file.symbol = "b" diff --git a/lua/diffview/scene/layouts/diff_2_hor.lua b/lua/diffview/scene/layouts/diff_2_hor.lua index 11569184..ea90c649 100644 --- a/lua/diffview/scene/layouts/diff_2_hor.lua +++ b/lua/diffview/scene/layouts/diff_2_hor.lua @@ -9,8 +9,8 @@ local M = {} local Diff2Hor = oop.create_class("Diff2Hor", Diff2) ---@class Diff2Hor.init.Opt ----@field a git.File ----@field b git.File +---@field a vcs.File +---@field b vcs.File ---@field winid_a integer ---@field winid_b integer diff --git a/lua/diffview/scene/layouts/diff_2_ver.lua b/lua/diffview/scene/layouts/diff_2_ver.lua index aabb89d5..a1ef9997 100644 --- a/lua/diffview/scene/layouts/diff_2_ver.lua +++ b/lua/diffview/scene/layouts/diff_2_ver.lua @@ -11,8 +11,8 @@ local M = {} local Diff2Ver = oop.create_class("Diff2Ver", Diff2) ---@class Diff2Hor.init.Opt ----@field a git.File ----@field b git.File +---@field a vcs.File +---@field b vcs.File ---@field winid_a integer ---@field winid_b integer diff --git a/lua/diffview/scene/layouts/diff_3.lua b/lua/diffview/scene/layouts/diff_3.lua index 834bf0ef..3cd96f0f 100644 --- a/lua/diffview/scene/layouts/diff_3.lua +++ b/lua/diffview/scene/layouts/diff_3.lua @@ -7,8 +7,8 @@ local oop = require("diffview.oop") local Diff1 = lazy.access("diffview.scene.layouts.diff_1", "Diff1") ---@type Diff4|LazyModule local Diff4 = lazy.access("diffview.scene.layouts.diff_4", "Diff4") ----@type git.File|LazyModule -local File = lazy.access("diffview.git.file", "File") +---@type vcs.File|LazyModule +local File = lazy.access("diffview.vcs.file", "File") ---@type Rev|LazyModule local Rev = lazy.access("diffview.git.rev", "Rev") ---@type ERevType|LazyModule @@ -25,9 +25,9 @@ local Diff3 = oop.create_class("Diff3", Layout) ---@alias Diff3.WindowSymbol "a"|"b"|"c" ---@class Diff3.init.Opt ----@field a git.File ----@field b git.File ----@field c git.File +---@field a vcs.File +---@field b vcs.File +---@field c vcs.File ---@field winid_a integer ---@field winid_b integer ---@field winid_c integer @@ -41,19 +41,19 @@ function Diff3:init(opt) self:use_windows(self.a, self.b, self.c) end ----@param file git.File +---@param file vcs.File function Diff3:set_file_a(file) self.a:set_file(file) file.symbol = "a" end ----@param file git.File +---@param file vcs.File function Diff3:set_file_b(file) self.b:set_file(file) file.symbol = "b" end ----@param file git.File +---@param file vcs.File function Diff3:set_file_c(file) self.c:set_file(file) file.symbol = "c" diff --git a/lua/diffview/scene/layouts/diff_4.lua b/lua/diffview/scene/layouts/diff_4.lua index 854ec6b9..8d1c3615 100644 --- a/lua/diffview/scene/layouts/diff_4.lua +++ b/lua/diffview/scene/layouts/diff_4.lua @@ -20,10 +20,10 @@ local Diff4 = oop.create_class("Diff4", Layout) ---@alias Diff4.WindowSymbol "a"|"b"|"c"|"d" ---@class Diff4.init.Opt ----@field a git.File ----@field b git.File ----@field c git.File ----@field d git.File +---@field a vcs.File +---@field b vcs.File +---@field c vcs.File +---@field d vcs.File ---@field winid_a integer ---@field winid_b integer ---@field winid_c integer @@ -39,25 +39,25 @@ function Diff4:init(opt) self:use_windows(self.a, self.b, self.c, self.d) end ----@param file git.File +---@param file vcs.File function Diff4:set_file_a(file) self.a:set_file(file) file.symbol = "a" end ----@param file git.File +---@param file vcs.File function Diff4:set_file_b(file) self.b:set_file(file) file.symbol = "b" end ----@param file git.File +---@param file vcs.File function Diff4:set_file_c(file) self.c:set_file(file) file.symbol = "c" end ----@param file git.File +---@param file vcs.File function Diff4:set_file_d(file) self.d:set_file(file) file.symbol = "d" diff --git a/lua/diffview/scene/view.lua b/lua/diffview/scene/view.lua index b17e4c38..74554bf2 100644 --- a/lua/diffview/scene/view.lua +++ b/lua/diffview/scene/view.lua @@ -7,7 +7,7 @@ local Diff3Hor = lazy.access("diffview.scene.layouts.diff_3_hor", "Diff3Hor") -- local Diff3Ver = lazy.access("diffview.scene.layouts.diff_3_ver", "Diff3Ver") --[[@as Diff3Ver|LazyModule ]] local Diff4Mixed = lazy.access("diffview.scene.layouts.diff_4_mixed", "Diff4Mixed") --[[@as Diff4Mixed|LazyModule ]] local EventEmitter = lazy.access("diffview.events", "EventEmitter") --[[@as EventEmitter|LazyModule ]] -local File = lazy.access("diffview.git.file", "File") --[[@as git.File|LazyModule ]] +local File = lazy.access("diffview.vcs.file", "File") --[[@as vcs.File|LazyModule ]] local config = lazy.require("diffview.config") ---@module "diffview.config" local oop = lazy.require("diffview.oop") ---@module "diffview.oop" local utils = lazy.require("diffview.utils") ---@module "diffview.utils" diff --git a/lua/diffview/scene/window.lua b/lua/diffview/scene/window.lua index 24ee2c1f..b65b59e7 100644 --- a/lua/diffview/scene/window.lua +++ b/lua/diffview/scene/window.lua @@ -1,7 +1,7 @@ local lazy = require("diffview.lazy") local oop = require("diffview.oop") -local File = lazy.access("diffview.git.file", "File") ---@type git.File|LazyModule +local File = lazy.access("diffview.vcs.file", "File") ---@type vcs.File|LazyModule local RevType = lazy.access("diffview.git.rev", "RevType") ---@type RevType|LazyModule local config = lazy.require("diffview.config") ---@module "diffview.config" local lib = lazy.require("diffview.lib") ---@module "diffview.lib" @@ -12,7 +12,7 @@ local M = {} ---@class Window : diffview.Object ---@field id integer ----@field file git.File +---@field file vcs.File ---@field parent Layout local Window = oop.create_class("Window") @@ -20,7 +20,7 @@ Window.winopt_store = {} ---@class Window.init.opt ---@field id integer ----@field file git.File +---@field file vcs.File ---@field parent Layout ---@param opt Window.init.opt @@ -62,7 +62,7 @@ function Window:is_focused() return self:is_valid() and api.nvim_get_current_win() == self.id end ----@param callback fun(file: git.File) +---@param callback fun(file: vcs.File) function Window:load_file(callback) assert(self.file) @@ -75,7 +75,7 @@ function Window:load_file(callback) end) end ----@param callback? fun(file: git.File) +---@param callback? fun(file: vcs.File) function Window:open_file(callback) assert(self.file) diff --git a/lua/diffview/git/file.lua b/lua/diffview/vcs/file.lua similarity index 97% rename from lua/diffview/git/file.lua rename to lua/diffview/vcs/file.lua index f8a01c0f..8b0cbda0 100644 --- a/lua/diffview/git/file.lua +++ b/lua/diffview/vcs/file.lua @@ -22,7 +22,7 @@ local M = {} ---@alias git.FileDataProducer fun(kind: git.FileKind, path: string, pos: "left"|"right"): string[] ----@class git.File : diffview.Object +---@class vcs.File : diffview.Object ---@field git_ctx GitContext ---@filed path string ---@field absolute_path string @@ -40,9 +40,9 @@ local M = {} ---@field active boolean ---@field ready boolean ---@field winopts WindowOptions -local File = oop.create_class("git.File") +local File = oop.create_class("vcs.File") ----@type table +---@type table File.attached = {} ---@static @@ -230,7 +230,7 @@ function File:is_valid() end ---@param force? boolean ----@param opt? git.File.AttachState +---@param opt? vcs.File.AttachState function File:attach_buffer(force, opt) if self.bufnr then File._attach_buffer(self.bufnr, force, opt) @@ -253,7 +253,7 @@ end ---@param t1 table ---@param t2 table ----@return git.File.AttachState +---@return vcs.File.AttachState local function prepare_attach_opt(t1, t2) local res = vim.tbl_extend("keep", t1, { keymaps = {}, @@ -275,14 +275,14 @@ local function prepare_attach_opt(t1, t2) return res end ----@class git.File.AttachState +---@class vcs.File.AttachState ---@field keymaps table ---@field disable_diagnostics boolean ---@static ---@param bufnr integer ---@param force? boolean ----@param opt? git.File.AttachState +---@param opt? vcs.File.AttachState function File._attach_buffer(bufnr, force, opt) local new_opt = false local cur_state = File.attached[bufnr] or {} @@ -385,7 +385,7 @@ function File.load_null_buffer(winid) File._attach_buffer(bn) end ----@type git.File +---@type vcs.File File.NULL_FILE = File({ git_ctx = { toplevel = "diffview://", diff --git a/lua/diffview/vcs/init.lua b/lua/diffview/vcs/init.lua new file mode 100644 index 00000000..e69de29b From b9275e198e8f5ff308ad028c7cf2555b52c5f944 Mon Sep 17 00:00:00 2001 From: zegervdv Date: Fri, 14 Oct 2022 17:38:37 +0200 Subject: [PATCH 02/71] fixup! refactor: abstract the git module into a generic vcs module --- lua/diffview/git/utils.lua | 2 +- lua/diffview/scene/views/file_history/file_history_panel.lua | 2 +- lua/diffview/{git => vcs}/log_entry.lua | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename lua/diffview/{git => vcs}/log_entry.lua (100%) diff --git a/lua/diffview/git/utils.lua b/lua/diffview/git/utils.lua index 18180f72..12b6d111 100644 --- a/lua/diffview/git/utils.lua +++ b/lua/diffview/git/utils.lua @@ -4,7 +4,7 @@ local Diff2Hor = require("diffview.scene.layouts.diff_2_hor").Diff2Hor local FileDict = require("diffview.git.file_dict").FileDict local FileEntry = require("diffview.scene.file_entry").FileEntry local Job = require("plenary.job") -local LogEntry = require("diffview.git.log_entry").LogEntry +local LogEntry = require("diffview.vcs.log_entry").LogEntry local Rev = require("diffview.git.rev").Rev local RevType = require("diffview.git.rev").RevType local Semaphore = require("diffview.control").Semaphore diff --git a/lua/diffview/scene/views/file_history/file_history_panel.lua b/lua/diffview/scene/views/file_history/file_history_panel.lua index 545fc985..083a99cb 100644 --- a/lua/diffview/scene/views/file_history/file_history_panel.lua +++ b/lua/diffview/scene/views/file_history/file_history_panel.lua @@ -1,7 +1,7 @@ local lazy = require("diffview.lazy") local FHOptionPanel = lazy.access("diffview.scene.views.file_history.option_panel", "FHOptionPanel") ---@type FHOptionPanel|LazyModule -local LogEntry = lazy.access("diffview.git.log_entry", "LogEntry") ---@type LogEntry|LazyModule +local LogEntry = lazy.access("diffview.vcs.log_entry", "LogEntry") ---@type LogEntry|LazyModule local Panel = lazy.access("diffview.ui.panel", "Panel") ---@type Panel|LazyModule local PerfTimer = lazy.access("diffview.perf", "PerfTimer") ---@type PerfTimer|LazyModule local config = lazy.require("diffview.config") ---@module "diffview.config" diff --git a/lua/diffview/git/log_entry.lua b/lua/diffview/vcs/log_entry.lua similarity index 100% rename from lua/diffview/git/log_entry.lua rename to lua/diffview/vcs/log_entry.lua From 736e431a1bb18a640dddf77cd453ce77e0c43d9d Mon Sep 17 00:00:00 2001 From: zegervdv Date: Fri, 14 Oct 2022 17:40:52 +0200 Subject: [PATCH 03/71] fixup! refactor: abstract the git module into a generic vcs module --- lua/diffview/git/utils.lua | 2 +- lua/diffview/scene/views/diff/diff_view.lua | 2 +- lua/diffview/{git => vcs}/file_dict.lua | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename lua/diffview/{git => vcs}/file_dict.lua (100%) diff --git a/lua/diffview/git/utils.lua b/lua/diffview/git/utils.lua index 12b6d111..14717c42 100644 --- a/lua/diffview/git/utils.lua +++ b/lua/diffview/git/utils.lua @@ -1,7 +1,7 @@ local Commit = require("diffview.git.commit").Commit local CountDownLatch = require("diffview.control").CountDownLatch local Diff2Hor = require("diffview.scene.layouts.diff_2_hor").Diff2Hor -local FileDict = require("diffview.git.file_dict").FileDict +local FileDict = require("diffview.vcs.file_dict").FileDict local FileEntry = require("diffview.scene.file_entry").FileEntry local Job = require("plenary.job") local LogEntry = require("diffview.vcs.log_entry").LogEntry diff --git a/lua/diffview/scene/views/diff/diff_view.lua b/lua/diffview/scene/views/diff/diff_view.lua index 5446574a..619f9d40 100644 --- a/lua/diffview/scene/views/diff/diff_view.lua +++ b/lua/diffview/scene/views/diff/diff_view.lua @@ -5,7 +5,7 @@ local CommitLogPanel = lazy.access("diffview.ui.panels.commit_log_panel", "Commi local Diff = lazy.access("diffview.diff", "Diff") ---@type Diff|LazyModule local EditToken = lazy.access("diffview.diff", "EditToken") ---@type EditToken|LazyModule local Event = lazy.access("diffview.events", "Event") ---@type Event|LazyModule -local FileDict = lazy.access("diffview.git.file_dict", "FileDict") ---@type FileDict|LazyModule +local FileDict = lazy.access("diffview.vcs.file_dict", "FileDict") ---@type FileDict|LazyModule local FileEntry = lazy.access("diffview.scene.file_entry", "FileEntry") ---@type FileEntry|LazyModule local FilePanel = lazy.access("diffview.scene.views.diff.file_panel", "FilePanel") ---@type FilePanel|LazyModule local PerfTimer = lazy.access("diffview.perf", "PerfTimer") ---@type PerfTimer|LazyModule diff --git a/lua/diffview/git/file_dict.lua b/lua/diffview/vcs/file_dict.lua similarity index 100% rename from lua/diffview/git/file_dict.lua rename to lua/diffview/vcs/file_dict.lua From d522de46a3f90bf94f64b836185deaad45e8d640 Mon Sep 17 00:00:00 2001 From: zegervdv Date: Fri, 14 Oct 2022 17:44:34 +0200 Subject: [PATCH 04/71] fixup! refactor: abstract the git module into a generic vcs module --- lua/diffview/scene/file_entry.lua | 2 +- lua/diffview/ui/panel.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lua/diffview/scene/file_entry.lua b/lua/diffview/scene/file_entry.lua index 8619c6e0..92c688f8 100644 --- a/lua/diffview/scene/file_entry.lua +++ b/lua/diffview/scene/file_entry.lua @@ -2,7 +2,7 @@ local lazy = require("diffview.lazy") local oop = require("diffview.oop") ---@type vcs.File|LazyModule -local File = lazy.access("diffview.git.file", "File") +local File = lazy.access("diffview.vcs.file", "File") ---@type RevType|LazyModule local RevType = lazy.access("diffview.git.rev", "RevType") ---@type Diff1|LazyModule diff --git a/lua/diffview/ui/panel.lua b/lua/diffview/ui/panel.lua index 8c35c605..b17008f1 100644 --- a/lua/diffview/ui/panel.lua +++ b/lua/diffview/ui/panel.lua @@ -1,5 +1,5 @@ local EventEmitter = require("diffview.events").EventEmitter -local File = require("diffview.git.file").File +local File = require("diffview.vcs.file").File local PerfTimer = require("diffview.perf").PerfTimer local logger = require("diffview.logger") local oop = require("diffview.oop") From b892e9ed22bc13cd809c956257669a8d61bd473b Mon Sep 17 00:00:00 2001 From: zegervdv Date: Sat, 15 Oct 2022 10:04:22 +0200 Subject: [PATCH 05/71] fixup! refactor: abstract the git module into a generic vcs module --- lua/diffview/api/views/diff/diff_view.lua | 4 +-- lua/diffview/git/utils.lua | 4 +-- lua/diffview/lib.lua | 4 +-- lua/diffview/scene/file_entry.lua | 2 +- lua/diffview/scene/layouts/diff_1.lua | 4 +-- lua/diffview/scene/layouts/diff_2.lua | 2 +- lua/diffview/scene/layouts/diff_3.lua | 4 +-- lua/diffview/scene/views/diff/diff_view.lua | 2 +- lua/diffview/scene/views/diff/listeners.lua | 2 +- lua/diffview/scene/window.lua | 2 +- lua/diffview/{git => vcs}/commit.lua | 39 ++++----------------- lua/diffview/vcs/file.lua | 4 +-- lua/diffview/{git => vcs}/rev.lua | 0 13 files changed, 24 insertions(+), 49 deletions(-) rename lua/diffview/{git => vcs}/commit.lua (68%) rename lua/diffview/{git => vcs}/rev.lua (100%) diff --git a/lua/diffview/api/views/diff/diff_view.lua b/lua/diffview/api/views/diff/diff_view.lua index d7e59638..845b844e 100644 --- a/lua/diffview/api/views/diff/diff_view.lua +++ b/lua/diffview/api/views/diff/diff_view.lua @@ -3,8 +3,8 @@ local lazy = require("diffview.lazy") local DiffView = lazy.access("diffview.scene.views.diff.diff_view", "DiffView") ---@type DiffView|LazyModule local FileEntry = lazy.access("diffview.scene.file_entry", "FileEntry") ---@type FileEntry|LazyModule local FilePanel = lazy.access("diffview.scene.views.diff.file_panel", "FilePanel") ---@type FilePanel|LazyModule -local Rev = lazy.access("diffview.git.rev", "Rev") ---@type Rev|LazyModule -local RevType = lazy.access("diffview.git.rev", "RevType") ---@type RevType|LazyModule +local Rev = lazy.access("diffview.vcs.rev", "Rev") ---@type Rev|LazyModule +local RevType = lazy.access("diffview.vcs.rev", "RevType") ---@type RevType|LazyModule local async = lazy.require("plenary.async") ---@module "plenary.async" local git = lazy.require("diffview.git.utils") ---@module "diffview.git.utils" local logger = lazy.require("diffview.logger") ---@module "diffview.logger" diff --git a/lua/diffview/git/utils.lua b/lua/diffview/git/utils.lua index 14717c42..92eeb944 100644 --- a/lua/diffview/git/utils.lua +++ b/lua/diffview/git/utils.lua @@ -5,8 +5,8 @@ local FileDict = require("diffview.vcs.file_dict").FileDict local FileEntry = require("diffview.scene.file_entry").FileEntry local Job = require("plenary.job") local LogEntry = require("diffview.vcs.log_entry").LogEntry -local Rev = require("diffview.git.rev").Rev -local RevType = require("diffview.git.rev").RevType +local Rev = require("diffview.vcs.rev").Rev +local RevType = require("diffview.vcs.rev").RevType local Semaphore = require("diffview.control").Semaphore local async = require("plenary.async") local config = require("diffview.config") diff --git a/lua/diffview/lib.lua b/lua/diffview/lib.lua index e644a401..24fdf457 100644 --- a/lua/diffview/lib.lua +++ b/lua/diffview/lib.lua @@ -2,8 +2,8 @@ local lazy = require("diffview.lazy") local DiffView = lazy.access("diffview.scene.views.diff.diff_view", "DiffView") ---@type DiffView|LazyModule local FileHistoryView = lazy.access("diffview.scene.views.file_history.file_history_view", "FileHistoryView") ---@type FileHistoryView|LazyModule -local Rev = lazy.access("diffview.git.rev", "Rev") ---@type Rev|LazyModule -local RevType = lazy.access("diffview.git.rev", "RevType") ---@type ERevType|LazyModule +local Rev = lazy.access("diffview.vcs.rev", "Rev") ---@type Rev|LazyModule +local RevType = lazy.access("diffview.vcs.rev", "RevType") ---@type ERevType|LazyModule local StandardView = lazy.access("diffview.scene.views.standard.standard_view", "StandardView") ---@type StandardView|LazyModule local arg_parser = lazy.require("diffview.arg_parser") ---@module "diffview.arg_parser" local config = lazy.require("diffview.config") ---@module "diffview.config" diff --git a/lua/diffview/scene/file_entry.lua b/lua/diffview/scene/file_entry.lua index 92c688f8..47a8fdc6 100644 --- a/lua/diffview/scene/file_entry.lua +++ b/lua/diffview/scene/file_entry.lua @@ -4,7 +4,7 @@ local oop = require("diffview.oop") ---@type vcs.File|LazyModule local File = lazy.access("diffview.vcs.file", "File") ---@type RevType|LazyModule -local RevType = lazy.access("diffview.git.rev", "RevType") +local RevType = lazy.access("diffview.vcs.rev", "RevType") ---@type Diff1|LazyModule local Diff1 = lazy.access("diffview.scene.layouts.diff_1", "Diff1") ---@type Diff2|LazyModule diff --git a/lua/diffview/scene/layouts/diff_1.lua b/lua/diffview/scene/layouts/diff_1.lua index 0047ab20..b2b15193 100644 --- a/lua/diffview/scene/layouts/diff_1.lua +++ b/lua/diffview/scene/layouts/diff_1.lua @@ -5,8 +5,8 @@ local oop = require("diffview.oop") local Diff3 = lazy.access("diffview.scene.layouts.diff_3", "Diff3") ---@type Diff3|LazyModule local Diff4 = lazy.access("diffview.scene.layouts.diff_4", "Diff4") ---@type Diff4|LazyModule local File = lazy.access("diffview.vcs.file", "File") ---@type vcs.File|LazyModule -local Rev = lazy.access("diffview.git.rev", "Rev") ---@type Rev|LazyModule -local RevType = lazy.access("diffview.git.rev", "RevType") ---@type RevType|LazyModule +local Rev = lazy.access("diffview.vcs.rev", "Rev") ---@type Rev|LazyModule +local RevType = lazy.access("diffview.vcs.rev", "RevType") ---@type RevType|LazyModule local Window = lazy.access("diffview.scene.window", "Window") ---@type Window|LazyModule local api = vim.api diff --git a/lua/diffview/scene/layouts/diff_2.lua b/lua/diffview/scene/layouts/diff_2.lua index ab2940ae..0c364115 100644 --- a/lua/diffview/scene/layouts/diff_2.lua +++ b/lua/diffview/scene/layouts/diff_2.lua @@ -1,4 +1,4 @@ -local RevType = require("diffview.git.rev").RevType +local RevType = require("diffview.vcs.rev").RevType local Window = require("diffview.scene.window").Window local Layout = require("diffview.scene.layout").Layout local oop = require("diffview.oop") diff --git a/lua/diffview/scene/layouts/diff_3.lua b/lua/diffview/scene/layouts/diff_3.lua index 3cd96f0f..80c06927 100644 --- a/lua/diffview/scene/layouts/diff_3.lua +++ b/lua/diffview/scene/layouts/diff_3.lua @@ -10,9 +10,9 @@ local Diff4 = lazy.access("diffview.scene.layouts.diff_4", "Diff4") ---@type vcs.File|LazyModule local File = lazy.access("diffview.vcs.file", "File") ---@type Rev|LazyModule -local Rev = lazy.access("diffview.git.rev", "Rev") +local Rev = lazy.access("diffview.vcs.rev", "Rev") ---@type ERevType|LazyModule -local RevType = lazy.access("diffview.git.rev", "RevType") +local RevType = lazy.access("diffview.vcs.rev", "RevType") local M = {} diff --git a/lua/diffview/scene/views/diff/diff_view.lua b/lua/diffview/scene/views/diff/diff_view.lua index 619f9d40..d5ebaf14 100644 --- a/lua/diffview/scene/views/diff/diff_view.lua +++ b/lua/diffview/scene/views/diff/diff_view.lua @@ -9,7 +9,7 @@ local FileDict = lazy.access("diffview.vcs.file_dict", "FileDict") ---@type File local FileEntry = lazy.access("diffview.scene.file_entry", "FileEntry") ---@type FileEntry|LazyModule local FilePanel = lazy.access("diffview.scene.views.diff.file_panel", "FilePanel") ---@type FilePanel|LazyModule local PerfTimer = lazy.access("diffview.perf", "PerfTimer") ---@type PerfTimer|LazyModule -local RevType = lazy.access("diffview.git.rev", "RevType") ---@type RevType|LazyModule +local RevType = lazy.access("diffview.vcs.rev", "RevType") ---@type RevType|LazyModule local StandardView = lazy.access("diffview.scene.views.standard.standard_view", "StandardView") ---@type StandardView|LazyModule local async = lazy.require("plenary.async") ---@module "plenary.async" local debounce = lazy.require("diffview.debounce") ---@module "diffview.debounce" diff --git a/lua/diffview/scene/views/diff/listeners.lua b/lua/diffview/scene/views/diff/listeners.lua index dcc13cec..baaf25bf 100644 --- a/lua/diffview/scene/views/diff/listeners.lua +++ b/lua/diffview/scene/views/diff/listeners.lua @@ -2,7 +2,7 @@ local lazy = require("diffview.lazy") local actions = lazy.require("diffview.actions") ---@module "diffview.actions" local Event = lazy.access("diffview.events", "Event") ---@type EEvent -local RevType = lazy.access("diffview.git.rev", "RevType") ---@type ERevType +local RevType = lazy.access("diffview.vcs.rev", "RevType") ---@type ERevType local async = lazy.require("plenary.async") ---@module "plenary.async" local git = lazy.require("diffview.git.utils") ---@module "diffview.git.utils" local utils = lazy.require("diffview.utils") ---@module "diffview.utils" diff --git a/lua/diffview/scene/window.lua b/lua/diffview/scene/window.lua index b65b59e7..5ce40efb 100644 --- a/lua/diffview/scene/window.lua +++ b/lua/diffview/scene/window.lua @@ -2,7 +2,7 @@ local lazy = require("diffview.lazy") local oop = require("diffview.oop") local File = lazy.access("diffview.vcs.file", "File") ---@type vcs.File|LazyModule -local RevType = lazy.access("diffview.git.rev", "RevType") ---@type RevType|LazyModule +local RevType = lazy.access("diffview.vcs.rev", "RevType") ---@type RevType|LazyModule local config = lazy.require("diffview.config") ---@module "diffview.config" local lib = lazy.require("diffview.lib") ---@module "diffview.lib" local utils = lazy.require("diffview.utils") ---@module "diffview.utils" diff --git a/lua/diffview/git/commit.lua b/lua/diffview/vcs/commit.lua similarity index 68% rename from lua/diffview/git/commit.lua rename to lua/diffview/vcs/commit.lua index cae553a4..6ff85662 100644 --- a/lua/diffview/git/commit.lua +++ b/lua/diffview/vcs/commit.lua @@ -3,9 +3,7 @@ local oop = require("diffview.oop") local utils = require("diffview.utils") ---@type ERevType|LazyModule -local RevType = lazy.access("diffview.git.rev", "RevType") ----@module "diffview.git.utils" -local git = lazy.require("diffview.git.utils") +local RevType = lazy.access("diffview.vcs.rev", "RevType") local M = {} @@ -41,42 +39,19 @@ function Commit:init(opt) end ---@param rev_arg string ----@param git_toplevel string +---@param toplevel string ---@return Commit? -function Commit.from_rev_arg(rev_arg, git_toplevel) - local out, code = git.exec_sync({ - "show", - "--pretty=format:%H %P%n%an%n%ad%n%ar%n %s", - "--date=raw", - "--name-status", - rev_arg, - "--", - }, git_toplevel) - - if code ~= 0 then - return - end - - local right_hash, _, _ = unpack(utils.str_split(out[1])) - local time, time_offset = unpack(utils.str_split(out[3])) - - return Commit({ - hash = right_hash, - author = out[2], - time = tonumber(time), - time_offset = time_offset, - rel_date = out[4], - subject = out[5]:sub(3), - }) +function Commit.from_rev_arg(rev_arg, toplevel) + return end ---@param rev Rev ----@param git_toplevel string +---@param toplevel string ---@return Commit? -function Commit.from_rev(rev, git_toplevel) +function Commit.from_rev(rev, toplevel) assert(rev.type == RevType.COMMIT, "Rev must be of type COMMIT!") - return Commit.from_rev_arg(rev.commit, git_toplevel) + return Commit.from_rev_arg(rev.commit, toplevel) end function Commit.parse_time_offset(iso_date) diff --git a/lua/diffview/vcs/file.lua b/lua/diffview/vcs/file.lua index 8b0cbda0..6b84aed8 100644 --- a/lua/diffview/vcs/file.lua +++ b/lua/diffview/vcs/file.lua @@ -4,9 +4,9 @@ local oop = require("diffview.oop") ---@module "plenary.async" local async = lazy.require("plenary.async") ---@type Rev -local Rev = lazy.access("diffview.git.rev", "Rev") +local Rev = lazy.access("diffview.vcs.rev", "Rev") ---@type ERevType -local RevType = lazy.access("diffview.git.rev", "RevType") +local RevType = lazy.access("diffview.vcs.rev", "RevType") ---@module "diffview.config" local config = lazy.require("diffview.config") ---@module "diffview.git.utils" diff --git a/lua/diffview/git/rev.lua b/lua/diffview/vcs/rev.lua similarity index 100% rename from lua/diffview/git/rev.lua rename to lua/diffview/vcs/rev.lua From 1176adc2ff31d9452cf686e41b0c43bbfbe5f205 Mon Sep 17 00:00:00 2001 From: zegervdv Date: Sat, 15 Oct 2022 10:10:49 +0200 Subject: [PATCH 06/71] fixup! refactor: abstract the git module into a generic vcs module --- lua/diffview/git/utils.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua/diffview/git/utils.lua b/lua/diffview/git/utils.lua index 92eeb944..cdf17b24 100644 --- a/lua/diffview/git/utils.lua +++ b/lua/diffview/git/utils.lua @@ -1,4 +1,4 @@ -local Commit = require("diffview.git.commit").Commit +local Commit = require("diffview.vcs.adapters.git").GitCommit local CountDownLatch = require("diffview.control").CountDownLatch local Diff2Hor = require("diffview.scene.layouts.diff_2_hor").Diff2Hor local FileDict = require("diffview.vcs.file_dict").FileDict From 989609e5f0da0e68d9bdd500f3dc5ce5f67cd52a Mon Sep 17 00:00:00 2001 From: zegervdv Date: Sat, 15 Oct 2022 10:38:29 +0200 Subject: [PATCH 07/71] fixup! refactor: abstract the git module into a generic vcs module --- lua/diffview/actions.lua | 2 +- lua/diffview/api/views/diff/diff_view.lua | 2 +- lua/diffview/init.lua | 4 +- lua/diffview/lib.lua | 2 +- lua/diffview/scene/views/diff/diff_view.lua | 2 +- lua/diffview/scene/views/diff/listeners.lua | 2 +- .../views/file_history/file_history_panel.lua | 4 +- .../views/file_history/file_history_view.lua | 4 +- .../scene/views/file_history/listeners.lua | 4 +- .../scene/views/file_history/option_panel.lua | 2 +- lua/diffview/vcs/adapters/git/commit.lua | 83 +++++++++++++++++++ lua/diffview/{ => vcs/adapters}/git/utils.lua | 13 +-- lua/diffview/vcs/file.lua | 4 +- lua/diffview/vcs/rev.lua | 4 +- lua/diffview/vcs/utils.lua | 15 ++++ 15 files changed, 118 insertions(+), 29 deletions(-) create mode 100644 lua/diffview/vcs/adapters/git/commit.lua rename lua/diffview/{ => vcs/adapters}/git/utils.lua (99%) create mode 100644 lua/diffview/vcs/utils.lua diff --git a/lua/diffview/actions.lua b/lua/diffview/actions.lua index 3511c2ff..21815749 100644 --- a/lua/diffview/actions.lua +++ b/lua/diffview/actions.lua @@ -3,7 +3,7 @@ local lazy = require("diffview.lazy") local DiffView = lazy.access("diffview.scene.views.diff.diff_view", "DiffView") ---@type DiffView|LazyModule local FileHistoryView = lazy.access("diffview.scene.views.file_history.file_history_view", "FileHistoryView") ---@type FileHistoryView|LazyModule local StandardView = lazy.access("diffview.scene.views.standard.standard_view", "StandardView") ---@type StandardView|LazyModule -local git = lazy.require("diffview.git.utils") ---@module "diffview.git.utils" +local git = lazy.require("diffview.vcs.adapters.git.utils") ---@module "diffview.vcs.adapters.git.utils" local lib = lazy.require("diffview.lib") ---@module "diffview.lib" local utils = lazy.require("diffview.utils") ---@module "diffview.utils" diff --git a/lua/diffview/api/views/diff/diff_view.lua b/lua/diffview/api/views/diff/diff_view.lua index 845b844e..8ca61f75 100644 --- a/lua/diffview/api/views/diff/diff_view.lua +++ b/lua/diffview/api/views/diff/diff_view.lua @@ -6,7 +6,7 @@ local FilePanel = lazy.access("diffview.scene.views.diff.file_panel", "FilePanel local Rev = lazy.access("diffview.vcs.rev", "Rev") ---@type Rev|LazyModule local RevType = lazy.access("diffview.vcs.rev", "RevType") ---@type RevType|LazyModule local async = lazy.require("plenary.async") ---@module "plenary.async" -local git = lazy.require("diffview.git.utils") ---@module "diffview.git.utils" +local git = lazy.require("diffview.vcs.adapters.git.utils") ---@module "diffview.vcs.adapters.git.utils" local logger = lazy.require("diffview.logger") ---@module "diffview.logger" local oop = lazy.require("diffview.oop") ---@module "diffview.oop" local utils = lazy.require("diffview.utils") ---@module "diffview.utils" diff --git a/lua/diffview/init.lua b/lua/diffview/init.lua index 36ba999e..7a646167 100644 --- a/lua/diffview/init.lua +++ b/lua/diffview/init.lua @@ -9,8 +9,8 @@ local lazy = require("diffview.lazy") local arg_parser = lazy.require("diffview.arg_parser") ---@module "diffview.config" local config = lazy.require("diffview.config") ----@module "diffview.git.utils" -local git = lazy.require("diffview.git.utils") +---@module "diffview.vcs.adapters.git.utils" +local git = lazy.require("diffview.vcs.adapters.git.utils") ---@module "diffview.lib" local lib = lazy.require("diffview.lib") ---@module "diffview.logger" diff --git a/lua/diffview/lib.lua b/lua/diffview/lib.lua index 24fdf457..c9f16d13 100644 --- a/lua/diffview/lib.lua +++ b/lua/diffview/lib.lua @@ -7,7 +7,7 @@ local RevType = lazy.access("diffview.vcs.rev", "RevType") ---@type ERevType|Laz local StandardView = lazy.access("diffview.scene.views.standard.standard_view", "StandardView") ---@type StandardView|LazyModule local arg_parser = lazy.require("diffview.arg_parser") ---@module "diffview.arg_parser" local config = lazy.require("diffview.config") ---@module "diffview.config" -local git = lazy.require("diffview.git.utils") ---@module "diffview.git.utils" +local git = lazy.require("diffview.vcs.adapters.git.utils") ---@module "diffview.vcs.adapters.git.utils" local logger = lazy.require("diffview.logger") ---@module "diffview.logger" local utils = lazy.require("diffview.utils") ---@module "diffview.utils" diff --git a/lua/diffview/scene/views/diff/diff_view.lua b/lua/diffview/scene/views/diff/diff_view.lua index d5ebaf14..fcf9e361 100644 --- a/lua/diffview/scene/views/diff/diff_view.lua +++ b/lua/diffview/scene/views/diff/diff_view.lua @@ -13,7 +13,7 @@ local RevType = lazy.access("diffview.vcs.rev", "RevType") ---@type RevType|Lazy local StandardView = lazy.access("diffview.scene.views.standard.standard_view", "StandardView") ---@type StandardView|LazyModule local async = lazy.require("plenary.async") ---@module "plenary.async" local debounce = lazy.require("diffview.debounce") ---@module "diffview.debounce" -local git = lazy.require("diffview.git.utils") ---@module "diffview.git.utils" +local git = lazy.require("diffview.vcs.adapters.git.utils") ---@module "diffview.vcs.adapters.git.utils" local logger = lazy.require("diffview.logger") ---@module "diffview.logger" local utils = lazy.require("diffview.utils") ---@module "diffview.utils" local config = lazy.require("diffview.config") ---@module "diffview.config" diff --git a/lua/diffview/scene/views/diff/listeners.lua b/lua/diffview/scene/views/diff/listeners.lua index baaf25bf..45939f8b 100644 --- a/lua/diffview/scene/views/diff/listeners.lua +++ b/lua/diffview/scene/views/diff/listeners.lua @@ -4,7 +4,7 @@ local actions = lazy.require("diffview.actions") ---@module "diffview.actions" local Event = lazy.access("diffview.events", "Event") ---@type EEvent local RevType = lazy.access("diffview.vcs.rev", "RevType") ---@type ERevType local async = lazy.require("plenary.async") ---@module "plenary.async" -local git = lazy.require("diffview.git.utils") ---@module "diffview.git.utils" +local git = lazy.require("diffview.vcs.adapters.git.utils") ---@module "diffview.vcs.adapters.git.utils" local utils = lazy.require("diffview.utils") ---@module "diffview.utils" local api = vim.api diff --git a/lua/diffview/scene/views/file_history/file_history_panel.lua b/lua/diffview/scene/views/file_history/file_history_panel.lua index 083a99cb..1a7b1c9a 100644 --- a/lua/diffview/scene/views/file_history/file_history_panel.lua +++ b/lua/diffview/scene/views/file_history/file_history_panel.lua @@ -6,14 +6,14 @@ local Panel = lazy.access("diffview.ui.panel", "Panel") ---@type Panel|LazyModul local PerfTimer = lazy.access("diffview.perf", "PerfTimer") ---@type PerfTimer|LazyModule local config = lazy.require("diffview.config") ---@module "diffview.config" local debounce = lazy.require("diffview.debounce") ---@module "diffview.debounce" -local git = lazy.require("diffview.git.utils") ---@module "diffview.git.utils" +local git = lazy.require("diffview.vcs.adapters.git.utils") ---@module "diffview.vcs.adapters.git.utils" local logger = lazy.require("diffview.logger") ---@module "diffview.logger" local oop = lazy.require("diffview.oop") ---@module "diffview.oop" local panel_renderer = lazy.require("diffview.scene.views.file_history.render") ---@module "diffview.scene.views.file_history.render" local renderer = lazy.require("diffview.renderer") ---@module "diffview.renderer" local utils = lazy.require("diffview.utils") ---@module "diffview.utils" -local JobStatus = git.JobStatus +local JobStatus = lazy.access('diffview.vcs.utils', 'JobStatus') ---@type JobStatus|LazyModule local api = vim.api local M = {} diff --git a/lua/diffview/scene/views/file_history/file_history_view.lua b/lua/diffview/scene/views/file_history/file_history_view.lua index f72f0769..cfe88c55 100644 --- a/lua/diffview/scene/views/file_history/file_history_view.lua +++ b/lua/diffview/scene/views/file_history/file_history_view.lua @@ -7,9 +7,9 @@ local FileEntry = lazy.access("diffview.scene.file_entry", "FileEntry") ---@type local FileHistoryPanel = lazy.access("diffview.scene.views.file_history.file_history_panel", "FileHistoryPanel") ---@type FileHistoryPanel|LazyModule local StandardView = lazy.access("diffview.scene.views.standard.standard_view", "StandardView") ---@type StandardView|LazyModule local config = lazy.require("diffview.config") ---@module "diffview.config" -local git = lazy.require("diffview.git.utils") ---@module "diffview.git.utils" +local git = lazy.require("diffview.vcs.adapters.git.utils") ---@module "diffview.vcs.adapters.git.utils" -local JobStatus = lazy.access(git, "JobStatus") ---@type JobStatus|LazyModule +local JobStatus = lazy.access("diffview.vcs.utils", "JobStatus") ---@type JobStatus|LazyModule local api = vim.api local M = {} diff --git a/lua/diffview/scene/views/file_history/listeners.lua b/lua/diffview/scene/views/file_history/listeners.lua index e1502c5e..551a7d6d 100644 --- a/lua/diffview/scene/views/file_history/listeners.lua +++ b/lua/diffview/scene/views/file_history/listeners.lua @@ -1,8 +1,8 @@ local lazy = require("diffview.lazy") local DiffView = lazy.access("diffview.scene.views.diff.diff_view", "DiffView") ---@type DiffView|LazyModule -local JobStatus = lazy.access("diffview.git.utils", "JobStatus") ---@type JobStatus|LazyModule -local git = lazy.require("diffview.git.utils") ---@module "diffview.git.utils" +local JobStatus = lazy.access("diffview.vcs.utils", "JobStatus") ---@type JobStatus|LazyModule +local git = lazy.require("diffview.vcs.adapters.git.utils") ---@module "diffview.vcs.adapters.git.utils" local lib = lazy.require("diffview.lib") ---@module "diffview.lib" local utils = lazy.require("diffview.utils") ---@module "diffview.utils" diff --git a/lua/diffview/scene/views/file_history/option_panel.lua b/lua/diffview/scene/views/file_history/option_panel.lua index 7b755077..40566295 100644 --- a/lua/diffview/scene/views/file_history/option_panel.lua +++ b/lua/diffview/scene/views/file_history/option_panel.lua @@ -1,7 +1,7 @@ local lazy = require("diffview.lazy") local EventEmitter = lazy.access("diffview.events", "EventEmitter") ---@type EventEmitter|LazyModule -local JobStatus = lazy.access("diffview.git.utils", "JobStatus") ---@type JobStatus|LazyModule +local JobStatus = lazy.access("diffview.vcs.utils", "JobStatus") ---@type JobStatus|LazyModule local Panel = lazy.access("diffview.ui.panel", "Panel") ---@type Panel|LazyModule local arg_parser = lazy.require("diffview.arg_parser") ---@module "diffview.arg_parser" local config = lazy.require("diffview.config") ---@module "diffview.config" diff --git a/lua/diffview/vcs/adapters/git/commit.lua b/lua/diffview/vcs/adapters/git/commit.lua new file mode 100644 index 00000000..8d45b1df --- /dev/null +++ b/lua/diffview/vcs/adapters/git/commit.lua @@ -0,0 +1,83 @@ +local lazy = require("diffview.lazy") +local oop = require('diffview.oop') +local utils = require("diffview.utils") +local Commit = require('diffview.vcs.commit').Commit + +---@module "diffview.vcs.utils" +local git = lazy.require("diffview.vcs.utils") + +---@type ERevType|LazyModule +local RevType = lazy.access("diffview.vcs.rev", "RevType") + + +local M = {} + + +---@class GitCommit : Commit +---@field hash string +---@field author string +---@field time number +---@field time_offset number +---@field date string +---@field rel_date string +---@field ref_names string +---@field subject string +---@field body string +local GitCommit = oop.create_class('GitCommit', Commit) + +function GitCommit:init(opt) + GitCommit:super().init(self, opt) +end + +---@param rev_arg string +---@param git_toplevel string +---@return GitCommit? +function GitCommit.from_rev_arg(rev_arg, git_toplevel) + local out, code = git.exec_sync({ + "show", + "--pretty=format:%H %P%n%an%n%ad%n%ar%n %s", + "--date=raw", + "--name-status", + rev_arg, + "--", + }, git_toplevel) + + if code ~= 0 then + return + end + + local right_hash, _, _ = unpack(utils.str_split(out[1])) + local time, time_offset = unpack(utils.str_split(out[3])) + + return GitCommit({ + hash = right_hash, + author = out[2], + time = tonumber(time), + time_offset = time_offset, + rel_date = out[4], + subject = out[5]:sub(3), + }) +end + +---@param rev Rev +---@param git_toplevel string +---@return GitCommit? +function GitCommit.from_rev(rev, git_toplevel) + assert(rev.type == RevType.COMMIT, "Rev must be of type COMMIT!") + + return GitCommit.from_rev_arg(rev.commit, git_toplevel) +end + +function GitCommit.parse_time_offset(iso_date) + local sign, h, m = vim.trim(iso_date):match("([+-])(%d%d):?(%d%d)$") + local offset = tonumber(h) * 60 * 60 + tonumber(m) * 60 + + if sign == "-" then + offset = -offset + end + + return offset +end + +M.GitCommit = GitCommit +return M diff --git a/lua/diffview/git/utils.lua b/lua/diffview/vcs/adapters/git/utils.lua similarity index 99% rename from lua/diffview/git/utils.lua rename to lua/diffview/vcs/adapters/git/utils.lua index cdf17b24..e9731c49 100644 --- a/lua/diffview/git/utils.lua +++ b/lua/diffview/vcs/adapters/git/utils.lua @@ -1,4 +1,4 @@ -local Commit = require("diffview.vcs.adapters.git").GitCommit +local Commit = require("diffview.vcs.adapters.git.commit").GitCommit local CountDownLatch = require("diffview.control").CountDownLatch local Diff2Hor = require("diffview.scene.layouts.diff_2_hor").Diff2Hor local FileDict = require("diffview.vcs.file_dict").FileDict @@ -12,6 +12,7 @@ local async = require("plenary.async") local config = require("diffview.config") local logger = require("diffview.logger") local utils = require("diffview.utils") +local JobStatus = require("diffview.vcs.utils").JobStatus local api = vim.api @@ -34,15 +35,6 @@ local bootstrap = { }, } ----@enum JobStatus -local JobStatus = { - SUCCESS = 1, - PROGRESS = 2, - ERROR = 3, - KILLED = 4, - FATAL = 5, -} - ---@type Job[] local sync_jobs = {} ---@type Semaphore @@ -1728,5 +1720,4 @@ M.restore_file = async.wrap(function(toplevel, path, kind, commit, callback) callback() end, 5) -M.JobStatus = JobStatus return M diff --git a/lua/diffview/vcs/file.lua b/lua/diffview/vcs/file.lua index 6b84aed8..e1cd2ffa 100644 --- a/lua/diffview/vcs/file.lua +++ b/lua/diffview/vcs/file.lua @@ -9,8 +9,8 @@ local Rev = lazy.access("diffview.vcs.rev", "Rev") local RevType = lazy.access("diffview.vcs.rev", "RevType") ---@module "diffview.config" local config = lazy.require("diffview.config") ----@module "diffview.git.utils" -local git = lazy.require("diffview.git.utils") +---@module "diffview.vcs.adapters.git.utils" +local git = lazy.require("diffview.vcs.adapters.git.utils") ---@module "diffview.utils" local utils = lazy.require("diffview.utils") diff --git a/lua/diffview/vcs/rev.lua b/lua/diffview/vcs/rev.lua index 1fa1287a..782e9104 100644 --- a/lua/diffview/vcs/rev.lua +++ b/lua/diffview/vcs/rev.lua @@ -1,8 +1,8 @@ local lazy = require("diffview.lazy") local oop = require("diffview.oop") ----@module "diffview.git.utils" -local git = lazy.require("diffview.git.utils") +---@module "diffview.vcs.adapters.git.utils" +local git = lazy.require("diffview.vcs.adapters.git.utils") local M = {} diff --git a/lua/diffview/vcs/utils.lua b/lua/diffview/vcs/utils.lua new file mode 100644 index 00000000..a6f05c19 --- /dev/null +++ b/lua/diffview/vcs/utils.lua @@ -0,0 +1,15 @@ +local utils = require("diffview.utils") + +local M = {} + +---@enum JobStatus +local JobStatus = { + SUCCESS = 1, + PROGRESS = 2, + ERROR = 3, + KILLED = 4, + FATAL = 5, +} + +M.JobStatus = JobStatus +return M From 4e0b8ab8ec39dd8368cce7680177854b79e64e02 Mon Sep 17 00:00:00 2001 From: zegervdv Date: Sat, 15 Oct 2022 10:44:13 +0200 Subject: [PATCH 08/71] fixup! refactor: abstract the git module into a generic vcs module --- lua/diffview/actions.lua | 2 +- lua/diffview/api/views/diff/diff_view.lua | 2 +- lua/diffview/init.lua | 4 ++-- lua/diffview/lib.lua | 2 +- lua/diffview/scene/views/diff/diff_view.lua | 2 +- lua/diffview/scene/views/diff/listeners.lua | 2 +- lua/diffview/scene/views/file_history/file_history_panel.lua | 2 +- lua/diffview/scene/views/file_history/file_history_view.lua | 2 +- lua/diffview/scene/views/file_history/listeners.lua | 2 +- lua/diffview/vcs/file.lua | 4 ++-- lua/diffview/vcs/init.lua | 3 +++ lua/diffview/vcs/rev.lua | 4 ++-- 12 files changed, 17 insertions(+), 14 deletions(-) diff --git a/lua/diffview/actions.lua b/lua/diffview/actions.lua index 21815749..60b5bed1 100644 --- a/lua/diffview/actions.lua +++ b/lua/diffview/actions.lua @@ -3,7 +3,7 @@ local lazy = require("diffview.lazy") local DiffView = lazy.access("diffview.scene.views.diff.diff_view", "DiffView") ---@type DiffView|LazyModule local FileHistoryView = lazy.access("diffview.scene.views.file_history.file_history_view", "FileHistoryView") ---@type FileHistoryView|LazyModule local StandardView = lazy.access("diffview.scene.views.standard.standard_view", "StandardView") ---@type StandardView|LazyModule -local git = lazy.require("diffview.vcs.adapters.git.utils") ---@module "diffview.vcs.adapters.git.utils" +local git = lazy.require("diffview.vcs") ---@module "diffview.vcs" local lib = lazy.require("diffview.lib") ---@module "diffview.lib" local utils = lazy.require("diffview.utils") ---@module "diffview.utils" diff --git a/lua/diffview/api/views/diff/diff_view.lua b/lua/diffview/api/views/diff/diff_view.lua index 8ca61f75..1e2e37c4 100644 --- a/lua/diffview/api/views/diff/diff_view.lua +++ b/lua/diffview/api/views/diff/diff_view.lua @@ -6,7 +6,7 @@ local FilePanel = lazy.access("diffview.scene.views.diff.file_panel", "FilePanel local Rev = lazy.access("diffview.vcs.rev", "Rev") ---@type Rev|LazyModule local RevType = lazy.access("diffview.vcs.rev", "RevType") ---@type RevType|LazyModule local async = lazy.require("plenary.async") ---@module "plenary.async" -local git = lazy.require("diffview.vcs.adapters.git.utils") ---@module "diffview.vcs.adapters.git.utils" +local git = lazy.require("diffview.vcs") ---@module "diffview.vcs" local logger = lazy.require("diffview.logger") ---@module "diffview.logger" local oop = lazy.require("diffview.oop") ---@module "diffview.oop" local utils = lazy.require("diffview.utils") ---@module "diffview.utils" diff --git a/lua/diffview/init.lua b/lua/diffview/init.lua index 7a646167..030d5085 100644 --- a/lua/diffview/init.lua +++ b/lua/diffview/init.lua @@ -9,8 +9,8 @@ local lazy = require("diffview.lazy") local arg_parser = lazy.require("diffview.arg_parser") ---@module "diffview.config" local config = lazy.require("diffview.config") ----@module "diffview.vcs.adapters.git.utils" -local git = lazy.require("diffview.vcs.adapters.git.utils") +---@module "diffview.vcs" +local git = lazy.require("diffview.vcs") ---@module "diffview.lib" local lib = lazy.require("diffview.lib") ---@module "diffview.logger" diff --git a/lua/diffview/lib.lua b/lua/diffview/lib.lua index c9f16d13..da729c74 100644 --- a/lua/diffview/lib.lua +++ b/lua/diffview/lib.lua @@ -7,7 +7,7 @@ local RevType = lazy.access("diffview.vcs.rev", "RevType") ---@type ERevType|Laz local StandardView = lazy.access("diffview.scene.views.standard.standard_view", "StandardView") ---@type StandardView|LazyModule local arg_parser = lazy.require("diffview.arg_parser") ---@module "diffview.arg_parser" local config = lazy.require("diffview.config") ---@module "diffview.config" -local git = lazy.require("diffview.vcs.adapters.git.utils") ---@module "diffview.vcs.adapters.git.utils" +local git = lazy.require("diffview.vcs") ---@module "diffview.vcs" local logger = lazy.require("diffview.logger") ---@module "diffview.logger" local utils = lazy.require("diffview.utils") ---@module "diffview.utils" diff --git a/lua/diffview/scene/views/diff/diff_view.lua b/lua/diffview/scene/views/diff/diff_view.lua index fcf9e361..de565e3a 100644 --- a/lua/diffview/scene/views/diff/diff_view.lua +++ b/lua/diffview/scene/views/diff/diff_view.lua @@ -13,7 +13,7 @@ local RevType = lazy.access("diffview.vcs.rev", "RevType") ---@type RevType|Lazy local StandardView = lazy.access("diffview.scene.views.standard.standard_view", "StandardView") ---@type StandardView|LazyModule local async = lazy.require("plenary.async") ---@module "plenary.async" local debounce = lazy.require("diffview.debounce") ---@module "diffview.debounce" -local git = lazy.require("diffview.vcs.adapters.git.utils") ---@module "diffview.vcs.adapters.git.utils" +local git = lazy.require("diffview.vcs") ---@module "diffview.vcs" local logger = lazy.require("diffview.logger") ---@module "diffview.logger" local utils = lazy.require("diffview.utils") ---@module "diffview.utils" local config = lazy.require("diffview.config") ---@module "diffview.config" diff --git a/lua/diffview/scene/views/diff/listeners.lua b/lua/diffview/scene/views/diff/listeners.lua index 45939f8b..c061e128 100644 --- a/lua/diffview/scene/views/diff/listeners.lua +++ b/lua/diffview/scene/views/diff/listeners.lua @@ -4,7 +4,7 @@ local actions = lazy.require("diffview.actions") ---@module "diffview.actions" local Event = lazy.access("diffview.events", "Event") ---@type EEvent local RevType = lazy.access("diffview.vcs.rev", "RevType") ---@type ERevType local async = lazy.require("plenary.async") ---@module "plenary.async" -local git = lazy.require("diffview.vcs.adapters.git.utils") ---@module "diffview.vcs.adapters.git.utils" +local git = lazy.require("diffview.vcs") ---@module "diffview.vcs" local utils = lazy.require("diffview.utils") ---@module "diffview.utils" local api = vim.api diff --git a/lua/diffview/scene/views/file_history/file_history_panel.lua b/lua/diffview/scene/views/file_history/file_history_panel.lua index 1a7b1c9a..622709ed 100644 --- a/lua/diffview/scene/views/file_history/file_history_panel.lua +++ b/lua/diffview/scene/views/file_history/file_history_panel.lua @@ -6,7 +6,7 @@ local Panel = lazy.access("diffview.ui.panel", "Panel") ---@type Panel|LazyModul local PerfTimer = lazy.access("diffview.perf", "PerfTimer") ---@type PerfTimer|LazyModule local config = lazy.require("diffview.config") ---@module "diffview.config" local debounce = lazy.require("diffview.debounce") ---@module "diffview.debounce" -local git = lazy.require("diffview.vcs.adapters.git.utils") ---@module "diffview.vcs.adapters.git.utils" +local git = lazy.require("diffview.vcs") ---@module "diffview.vcs" local logger = lazy.require("diffview.logger") ---@module "diffview.logger" local oop = lazy.require("diffview.oop") ---@module "diffview.oop" local panel_renderer = lazy.require("diffview.scene.views.file_history.render") ---@module "diffview.scene.views.file_history.render" diff --git a/lua/diffview/scene/views/file_history/file_history_view.lua b/lua/diffview/scene/views/file_history/file_history_view.lua index cfe88c55..a3240ba6 100644 --- a/lua/diffview/scene/views/file_history/file_history_view.lua +++ b/lua/diffview/scene/views/file_history/file_history_view.lua @@ -7,7 +7,7 @@ local FileEntry = lazy.access("diffview.scene.file_entry", "FileEntry") ---@type local FileHistoryPanel = lazy.access("diffview.scene.views.file_history.file_history_panel", "FileHistoryPanel") ---@type FileHistoryPanel|LazyModule local StandardView = lazy.access("diffview.scene.views.standard.standard_view", "StandardView") ---@type StandardView|LazyModule local config = lazy.require("diffview.config") ---@module "diffview.config" -local git = lazy.require("diffview.vcs.adapters.git.utils") ---@module "diffview.vcs.adapters.git.utils" +local git = lazy.require("diffview.vcs") ---@module "diffview.vcs" local JobStatus = lazy.access("diffview.vcs.utils", "JobStatus") ---@type JobStatus|LazyModule local api = vim.api diff --git a/lua/diffview/scene/views/file_history/listeners.lua b/lua/diffview/scene/views/file_history/listeners.lua index 551a7d6d..f4afce1e 100644 --- a/lua/diffview/scene/views/file_history/listeners.lua +++ b/lua/diffview/scene/views/file_history/listeners.lua @@ -2,7 +2,7 @@ local lazy = require("diffview.lazy") local DiffView = lazy.access("diffview.scene.views.diff.diff_view", "DiffView") ---@type DiffView|LazyModule local JobStatus = lazy.access("diffview.vcs.utils", "JobStatus") ---@type JobStatus|LazyModule -local git = lazy.require("diffview.vcs.adapters.git.utils") ---@module "diffview.vcs.adapters.git.utils" +local git = lazy.require("diffview.vcs") ---@module "diffview.vcs" local lib = lazy.require("diffview.lib") ---@module "diffview.lib" local utils = lazy.require("diffview.utils") ---@module "diffview.utils" diff --git a/lua/diffview/vcs/file.lua b/lua/diffview/vcs/file.lua index e1cd2ffa..8ffb018c 100644 --- a/lua/diffview/vcs/file.lua +++ b/lua/diffview/vcs/file.lua @@ -9,8 +9,8 @@ local Rev = lazy.access("diffview.vcs.rev", "Rev") local RevType = lazy.access("diffview.vcs.rev", "RevType") ---@module "diffview.config" local config = lazy.require("diffview.config") ----@module "diffview.vcs.adapters.git.utils" -local git = lazy.require("diffview.vcs.adapters.git.utils") +---@module "diffview.vcs" +local git = lazy.require("diffview.vcs") ---@module "diffview.utils" local utils = lazy.require("diffview.utils") diff --git a/lua/diffview/vcs/init.lua b/lua/diffview/vcs/init.lua index e69de29b..f27913a1 100644 --- a/lua/diffview/vcs/init.lua +++ b/lua/diffview/vcs/init.lua @@ -0,0 +1,3 @@ +local git = require('diffview.vcs.adapters.git.utils') + +return git diff --git a/lua/diffview/vcs/rev.lua b/lua/diffview/vcs/rev.lua index 782e9104..7e6ba8b7 100644 --- a/lua/diffview/vcs/rev.lua +++ b/lua/diffview/vcs/rev.lua @@ -1,8 +1,8 @@ local lazy = require("diffview.lazy") local oop = require("diffview.oop") ----@module "diffview.vcs.adapters.git.utils" -local git = lazy.require("diffview.vcs.adapters.git.utils") +---@module "diffview.vcs" +local git = lazy.require("diffview.vcs") local M = {} From 1bb63b6718407a50626f2eaecad4f1f0ee787f4c Mon Sep 17 00:00:00 2001 From: zegervdv Date: Sat, 15 Oct 2022 10:53:56 +0200 Subject: [PATCH 09/71] fixup! refactor: abstract the git module into a generic vcs module --- lua/diffview/actions.lua | 8 ++--- lua/diffview/api/views/diff/diff_view.lua | 8 ++--- lua/diffview/init.lua | 10 +++---- lua/diffview/lib.lua | 30 +++++++++---------- lua/diffview/scene/views/diff/diff_view.lua | 10 +++---- lua/diffview/scene/views/diff/listeners.lua | 14 ++++----- .../views/file_history/file_history_panel.lua | 4 +-- .../views/file_history/file_history_view.lua | 2 +- .../scene/views/file_history/listeners.lua | 4 +-- lua/diffview/vcs/adapters/git/commit.lua | 4 +-- lua/diffview/vcs/file.lua | 6 ++-- 11 files changed, 50 insertions(+), 50 deletions(-) diff --git a/lua/diffview/actions.lua b/lua/diffview/actions.lua index 60b5bed1..ab8da8d1 100644 --- a/lua/diffview/actions.lua +++ b/lua/diffview/actions.lua @@ -3,7 +3,7 @@ local lazy = require("diffview.lazy") local DiffView = lazy.access("diffview.scene.views.diff.diff_view", "DiffView") ---@type DiffView|LazyModule local FileHistoryView = lazy.access("diffview.scene.views.file_history.file_history_view", "FileHistoryView") ---@type FileHistoryView|LazyModule local StandardView = lazy.access("diffview.scene.views.standard.standard_view", "StandardView") ---@type StandardView|LazyModule -local git = lazy.require("diffview.vcs") ---@module "diffview.vcs" +local vcs = lazy.require("diffview.vcs") ---@module "diffview.vcs" local lib = lazy.require("diffview.lib") ---@module "diffview.lib" local utils = lazy.require("diffview.utils") ---@module "diffview.utils" @@ -166,7 +166,7 @@ function M.next_conflict() local curfile = main.file if main:is_valid() and curfile:is_valid() then - local conflicts, _, cur_idx = git.parse_conflicts( + local conflicts, _, cur_idx = vcs.parse_conflicts( api.nvim_buf_get_lines(curfile.bufnr, 0, -1, false), main.id ) @@ -197,7 +197,7 @@ function M.prev_conflict() local curfile = main.file if main:is_valid() and curfile:is_valid() then - local conflicts, _, cur_idx = git.parse_conflicts( + local conflicts, _, cur_idx = vcs.parse_conflicts( api.nvim_buf_get_lines(curfile.bufnr, 0, -1, false), main.id ) @@ -334,7 +334,7 @@ function M.conflict_choose(target) local curfile = main.file if main:is_valid() and curfile:is_valid() then - local _, cur = git.parse_conflicts( + local _, cur = vcs.parse_conflicts( api.nvim_buf_get_lines(curfile.bufnr, 0, -1, false), main.id ) diff --git a/lua/diffview/api/views/diff/diff_view.lua b/lua/diffview/api/views/diff/diff_view.lua index 1e2e37c4..acdb418c 100644 --- a/lua/diffview/api/views/diff/diff_view.lua +++ b/lua/diffview/api/views/diff/diff_view.lua @@ -6,7 +6,7 @@ local FilePanel = lazy.access("diffview.scene.views.diff.file_panel", "FilePanel local Rev = lazy.access("diffview.vcs.rev", "Rev") ---@type Rev|LazyModule local RevType = lazy.access("diffview.vcs.rev", "RevType") ---@type RevType|LazyModule local async = lazy.require("plenary.async") ---@module "plenary.async" -local git = lazy.require("diffview.vcs") ---@module "diffview.vcs" +local vcs = lazy.require("diffview.vcs") ---@module "diffview.vcs" local logger = lazy.require("diffview.logger") ---@module "diffview.logger" local oop = lazy.require("diffview.oop") ---@module "diffview.oop" local utils = lazy.require("diffview.utils") ---@module "diffview.utils" @@ -33,7 +33,7 @@ local CDiffView = oop.create_class("CDiffView", DiffView.__get()) function CDiffView:init(opt) logger.info("[api] Creating a new Custom DiffView.") self.valid = false - local git_dir = git.git_dir(opt.git_root) + local git_dir = vcs.git_dir(opt.git_root) if not git_dir then utils.err( @@ -65,7 +65,7 @@ function CDiffView:init(opt) git_ctx, self.files, self.path_args, - self.rev_arg or git.rev_to_pretty_string(opt.left, opt.right) + self.rev_arg or vcs.rev_to_pretty_string(opt.left, opt.right) ), })) @@ -128,7 +128,7 @@ function CDiffView:create_file_entries(files) { kind = "staged", files = files.staged or {}, - left = git.head_rev(self.git_ctx.toplevel), + left = vcs.head_rev(self.git_ctx.toplevel), right = Rev(RevType.STAGE, 0), }, } diff --git a/lua/diffview/init.lua b/lua/diffview/init.lua index 030d5085..3ed6e4b1 100644 --- a/lua/diffview/init.lua +++ b/lua/diffview/init.lua @@ -10,7 +10,7 @@ local arg_parser = lazy.require("diffview.arg_parser") ---@module "diffview.config" local config = lazy.require("diffview.config") ---@module "diffview.vcs" -local git = lazy.require("diffview.vcs") +local vcs = lazy.require("diffview.vcs") ---@module "diffview.lib" local lib = lazy.require("diffview.lib") ---@module "diffview.logger" @@ -229,7 +229,7 @@ function M.rev_candidates(git_toplevel, git_dir) end ---@cast git_toplevel string - git_dir = git.git_dir(git_toplevel) + git_dir = vcs.git_dir(git_toplevel) end if not (git_toplevel and git_dir) then @@ -250,11 +250,11 @@ function M.rev_candidates(git_toplevel, git_dir) vim.fn.glob(git_dir .. "/*", false, true) ) ) - local revs = git.exec_sync( + local revs = vcs.exec_sync( { "rev-parse", "--symbolic", "--branches", "--tags", "--remotes" }, { cwd = git_toplevel, silent = true } ) - local stashes = git.exec_sync( + local stashes = vcs.exec_sync( { "stash", "list", "--pretty=format:%gd" }, { cwd = git_toplevel, silent = true } ) @@ -327,7 +327,7 @@ M.completers = { if not err then ---@cast git_toplevel string - git_dir = git.git_dir(git_toplevel) + git_dir = vcs.git_dir(git_toplevel) end for i = 2, math.min(#ctx.args, ctx.divideridx) do diff --git a/lua/diffview/lib.lua b/lua/diffview/lib.lua index da729c74..a27dbb75 100644 --- a/lua/diffview/lib.lua +++ b/lua/diffview/lib.lua @@ -7,7 +7,7 @@ local RevType = lazy.access("diffview.vcs.rev", "RevType") ---@type ERevType|Laz local StandardView = lazy.access("diffview.scene.views.standard.standard_view", "StandardView") ---@type StandardView|LazyModule local arg_parser = lazy.require("diffview.arg_parser") ---@module "diffview.arg_parser" local config = lazy.require("diffview.config") ---@module "diffview.config" -local git = lazy.require("diffview.vcs") ---@module "diffview.vcs" +local vcs = lazy.require("diffview.vcs") ---@module "diffview.vcs" local logger = lazy.require("diffview.logger") ---@module "diffview.logger" local utils = lazy.require("diffview.utils") ---@module "diffview.utils" @@ -34,7 +34,7 @@ function M.diffview_open(args) for _, path_arg in ipairs(argo.post_args) do for _, path in ipairs(pl:vim_expand(path_arg, false, true)) do - local magic, pattern = git.pathspec_split(path) + local magic, pattern = vcs.pathspec_split(path) pattern = pl:readlink(pattern) or pattern table.insert(paths, magic .. pattern) end @@ -69,7 +69,7 @@ function M.diffview_open(args) local cwd = cpath or vim.loop.cwd() paths = vim.tbl_map(function(pathspec) - return git.pathspec_expand(git_toplevel, cwd, pathspec) + return vcs.pathspec_expand(git_toplevel, cwd, pathspec) end, paths) --[[@as string[] ]] local left, right = M.parse_revs(git_toplevel, rev_arg, { @@ -98,7 +98,7 @@ function M.diffview_open(args) local git_ctx = { toplevel = git_toplevel, - dir = git.git_dir(git_toplevel), + dir = vcs.git_dir(git_toplevel), } if not git_ctx.dir then @@ -144,7 +144,7 @@ function M.file_history(range, args) for _, path_arg in ipairs(argo.args) do for _, path in ipairs(pl:vim_expand(path_arg, false, true)) do - local magic, pattern = git.pathspec_split(path) + local magic, pattern = vcs.pathspec_split(path) pattern = pl:readlink(pattern) or pattern table.insert(paths, magic .. pattern) end @@ -157,7 +157,7 @@ function M.file_history(range, args) local top_indicators = {} for _, path in ipairs(paths) do - if git.pathspec_split(path) == "" then + if vcs.pathspec_split(path) == "" then table.insert(top_indicators, pl:absolute(path, cpath)) break end @@ -189,13 +189,13 @@ function M.file_history(range, args) local cwd = cpath or vim.loop.cwd() paths = vim.tbl_map(function(pathspec) - return git.pathspec_expand(git_toplevel, cwd, pathspec) + return vcs.pathspec_expand(git_toplevel, cwd, pathspec) end, paths) --[[@as string[] ]] ---@type string local range_arg = argo:get_flag("range", { no_empty = true }) if range_arg then - local ok = git.verify_rev_arg(git_toplevel, range_arg) + local ok = vcs.verify_rev_arg(git_toplevel, range_arg) if not ok then utils.err(("Bad revision: %s"):format(utils.str_quote(range_arg))) return @@ -241,7 +241,7 @@ function M.file_history(range, args) log_options.path_args = paths - local ok, opt_description = git.file_history_dry_run(git_toplevel, log_options) + local ok, opt_description = vcs.file_history_dry_run(git_toplevel, log_options) if not ok then utils.info({ @@ -256,7 +256,7 @@ function M.file_history(range, args) local git_ctx = { toplevel = git_toplevel, - dir = git.git_dir(git_toplevel), + dir = vcs.git_dir(git_toplevel), } if not git_ctx.dir then @@ -296,7 +296,7 @@ function M.find_git_toplevel(top_indicators) end if p and pl:readable(p) then - toplevel = git.toplevel(p) + toplevel = vcs.toplevel(p) if toplevel then return nil, toplevel @@ -325,7 +325,7 @@ function M.parse_revs(git_toplevel, rev_arg, opt) ---@type Rev? local right - local head = git.head_rev(git_toplevel) + local head = vcs.head_rev(git_toplevel) ---@cast head Rev if not rev_arg then @@ -337,7 +337,7 @@ function M.parse_revs(git_toplevel, rev_arg, opt) right = Rev(RevType.LOCAL) end elseif rev_arg:match("%.%.%.") then - left, right = git.symmetric_diff_revs(git_toplevel, rev_arg) + left, right = vcs.symmetric_diff_revs(git_toplevel, rev_arg) if not (left or right) then return elseif opt.imply_local then @@ -346,7 +346,7 @@ function M.parse_revs(git_toplevel, rev_arg, opt) left, right = M.imply_local(left, right, head) end else - local rev_strings, code, stderr = git.exec_sync( + local rev_strings, code, stderr = vcs.exec_sync( { "rev-parse", "--revs-only", rev_arg }, git_toplevel ) if code ~= 0 then @@ -361,7 +361,7 @@ function M.parse_revs(git_toplevel, rev_arg, opt) return end - local is_range = git.is_rev_arg_range(rev_arg) + local is_range = vcs.is_rev_arg_range(rev_arg) if is_range then local right_hash = rev_strings[1]:gsub("^%^", "") diff --git a/lua/diffview/scene/views/diff/diff_view.lua b/lua/diffview/scene/views/diff/diff_view.lua index de565e3a..d1a7e95c 100644 --- a/lua/diffview/scene/views/diff/diff_view.lua +++ b/lua/diffview/scene/views/diff/diff_view.lua @@ -13,7 +13,7 @@ local RevType = lazy.access("diffview.vcs.rev", "RevType") ---@type RevType|Lazy local StandardView = lazy.access("diffview.scene.views.standard.standard_view", "StandardView") ---@type StandardView|LazyModule local async = lazy.require("plenary.async") ---@module "plenary.async" local debounce = lazy.require("diffview.debounce") ---@module "diffview.debounce" -local git = lazy.require("diffview.vcs") ---@module "diffview.vcs" +local vcs = lazy.require("diffview.vcs") ---@module "diffview.vcs" local logger = lazy.require("diffview.logger") ---@module "diffview.logger" local utils = lazy.require("diffview.utils") ---@module "diffview.utils" local config = lazy.require("diffview.config") ---@module "diffview.config" @@ -63,7 +63,7 @@ function DiffView:init(opt) self.git_ctx, self.files, self.path_args, - self.rev_arg or git.rev_to_pretty_string(self.left, self.right) + self.rev_arg or vcs.rev_to_pretty_string(self.left, self.right) ), }) @@ -75,7 +75,7 @@ function DiffView:init(opt) local file = entry.layout:get_main_win().file local count_conflicts = vim.schedule_wrap(function() - local conflicts = git.parse_conflicts(api.nvim_buf_get_lines(file.bufnr, 0, -1, false)) + local conflicts = vcs.parse_conflicts(api.nvim_buf_get_lines(file.bufnr, 0, -1, false)) entry.stats = entry.stats or {} entry.stats.conflicts = #conflicts @@ -275,7 +275,7 @@ end ---@return string[] err ---@return FileDict DiffView.get_updated_files = async.wrap(function(self, callback) - git.diff_file_list( + vcs.diff_file_list( self.git_ctx, self.left, self.right, @@ -302,7 +302,7 @@ DiffView.update_files = debounce.debounce_trailing(100, true, vim.schedule_wrap( -- If left is tracking HEAD and right is LOCAL: Update HEAD rev. local new_head if self.left.track_head and self.right.type == RevType.LOCAL then - new_head = git.head_rev(self.git_ctx.toplevel) + new_head = vcs.head_rev(self.git_ctx.toplevel) if new_head and self.left.commit ~= new_head.commit then self.left = new_head else diff --git a/lua/diffview/scene/views/diff/listeners.lua b/lua/diffview/scene/views/diff/listeners.lua index c061e128..892e5699 100644 --- a/lua/diffview/scene/views/diff/listeners.lua +++ b/lua/diffview/scene/views/diff/listeners.lua @@ -4,7 +4,7 @@ local actions = lazy.require("diffview.actions") ---@module "diffview.actions" local Event = lazy.access("diffview.events", "Event") ---@type EEvent local RevType = lazy.access("diffview.vcs.rev", "RevType") ---@type ERevType local async = lazy.require("plenary.async") ---@module "plenary.async" -local git = lazy.require("diffview.vcs") ---@module "diffview.vcs" +local vcs = lazy.require("diffview.vcs") ---@module "diffview.vcs" local utils = lazy.require("diffview.utils") ---@module "diffview.utils" local api = vim.api @@ -34,7 +34,7 @@ return function(view) end end, buf_write_post = function() - if git.has_local(view.left, view.right) then + if vcs.has_local(view.left, view.right) then view.update_needed = true if api.nvim_get_current_tabpage() == view.tabpage then view:update_files() @@ -123,9 +123,9 @@ return function(view) if item then local code if item.kind == "working" or item.kind == "conflicting" then - _, code = git.exec_sync({ "add", item.path }, view.git_ctx.toplevel) + _, code = vcs.exec_sync({ "add", item.path }, view.git_ctx.toplevel) elseif item.kind == "staged" then - _, code = git.exec_sync({ "reset", "--", item.path }, view.git_ctx.toplevel) + _, code = vcs.exec_sync({ "reset", "--", item.path }, view.git_ctx.toplevel) end if code ~= 0 then @@ -180,7 +180,7 @@ return function(view) end, view.files.working) if #args > 0 then - local _, code = git.exec_sync({ "add", args }, view.git_ctx.toplevel) + local _, code = vcs.exec_sync({ "add", args }, view.git_ctx.toplevel) if code ~= 0 then utils.err("Failed to stage files!") @@ -194,7 +194,7 @@ return function(view) end end, unstage_all = function() - local _, code = git.exec_sync({ "reset" }, view.git_ctx.toplevel) + local _, code = vcs.exec_sync({ "reset" }, view.git_ctx.toplevel) if code ~= 0 then utils.err("Failed to unstage files!") @@ -220,7 +220,7 @@ return function(view) utils.err("The file is open with unsaved changes! Aborting file restoration.") return end - git.restore_file(view.git_ctx.toplevel, file.path, file.kind, commit, function() + vcs.restore_file(view.git_ctx.toplevel, file.path, file.kind, commit, function() async.util.scheduler() view:update_files() end) diff --git a/lua/diffview/scene/views/file_history/file_history_panel.lua b/lua/diffview/scene/views/file_history/file_history_panel.lua index 622709ed..22b720dd 100644 --- a/lua/diffview/scene/views/file_history/file_history_panel.lua +++ b/lua/diffview/scene/views/file_history/file_history_panel.lua @@ -6,7 +6,7 @@ local Panel = lazy.access("diffview.ui.panel", "Panel") ---@type Panel|LazyModul local PerfTimer = lazy.access("diffview.perf", "PerfTimer") ---@type PerfTimer|LazyModule local config = lazy.require("diffview.config") ---@module "diffview.config" local debounce = lazy.require("diffview.debounce") ---@module "diffview.debounce" -local git = lazy.require("diffview.vcs") ---@module "diffview.vcs" +local vcs = lazy.require("diffview.vcs") ---@module "diffview.vcs" local logger = lazy.require("diffview.logger") ---@module "diffview.logger" local oop = lazy.require("diffview.oop") ---@module "diffview.oop" local panel_renderer = lazy.require("diffview.scene.views.file_history.render") ---@module "diffview.scene.views.file_history.render" @@ -279,7 +279,7 @@ function FileHistoryPanel:update_entries(callback) self.entries = {} self.updating = true - finalizer = git.file_history( + finalizer = vcs.file_history( self.git_ctx, self.log_options, { default_layout = self.parent.get_default_diff2(), }, diff --git a/lua/diffview/scene/views/file_history/file_history_view.lua b/lua/diffview/scene/views/file_history/file_history_view.lua index a3240ba6..a9407831 100644 --- a/lua/diffview/scene/views/file_history/file_history_view.lua +++ b/lua/diffview/scene/views/file_history/file_history_view.lua @@ -7,7 +7,7 @@ local FileEntry = lazy.access("diffview.scene.file_entry", "FileEntry") ---@type local FileHistoryPanel = lazy.access("diffview.scene.views.file_history.file_history_panel", "FileHistoryPanel") ---@type FileHistoryPanel|LazyModule local StandardView = lazy.access("diffview.scene.views.standard.standard_view", "StandardView") ---@type StandardView|LazyModule local config = lazy.require("diffview.config") ---@module "diffview.config" -local git = lazy.require("diffview.vcs") ---@module "diffview.vcs" +local vcs = lazy.require("diffview.vcs") ---@module "diffview.vcs" local JobStatus = lazy.access("diffview.vcs.utils", "JobStatus") ---@type JobStatus|LazyModule local api = vim.api diff --git a/lua/diffview/scene/views/file_history/listeners.lua b/lua/diffview/scene/views/file_history/listeners.lua index f4afce1e..c0951aa8 100644 --- a/lua/diffview/scene/views/file_history/listeners.lua +++ b/lua/diffview/scene/views/file_history/listeners.lua @@ -2,7 +2,7 @@ local lazy = require("diffview.lazy") local DiffView = lazy.access("diffview.scene.views.diff.diff_view", "DiffView") ---@type DiffView|LazyModule local JobStatus = lazy.access("diffview.vcs.utils", "JobStatus") ---@type JobStatus|LazyModule -local git = lazy.require("diffview.vcs") ---@module "diffview.vcs" +local vcs = lazy.require("diffview.vcs") ---@module "diffview.vcs" local lib = lazy.require("diffview.lib") ---@module "diffview.lib" local utils = lazy.require("diffview.utils") ---@module "diffview.utils" @@ -73,7 +73,7 @@ return function(view) local new_view = DiffView({ git_ctx = view.git_ctx, - rev_arg = git.rev_to_pretty_string(layout.a.file.rev, layout.b.file.rev), + rev_arg = vcs.rev_to_pretty_string(layout.a.file.rev, layout.b.file.rev), left = layout.a.file.rev, right = layout.b.file.rev, options = {}, diff --git a/lua/diffview/vcs/adapters/git/commit.lua b/lua/diffview/vcs/adapters/git/commit.lua index 8d45b1df..ff9a10e4 100644 --- a/lua/diffview/vcs/adapters/git/commit.lua +++ b/lua/diffview/vcs/adapters/git/commit.lua @@ -3,8 +3,8 @@ local oop = require('diffview.oop') local utils = require("diffview.utils") local Commit = require('diffview.vcs.commit').Commit ----@module "diffview.vcs.utils" -local git = lazy.require("diffview.vcs.utils") +---@module "diffview.vcs.adapters.git.utils" +local git = lazy.require("diffview.vcs.adapters.git.utils") ---@type ERevType|LazyModule local RevType = lazy.access("diffview.vcs.rev", "RevType") diff --git a/lua/diffview/vcs/file.lua b/lua/diffview/vcs/file.lua index 8ffb018c..696cc0ab 100644 --- a/lua/diffview/vcs/file.lua +++ b/lua/diffview/vcs/file.lua @@ -10,7 +10,7 @@ local RevType = lazy.access("diffview.vcs.rev", "RevType") ---@module "diffview.config" local config = lazy.require("diffview.config") ---@module "diffview.vcs" -local git = lazy.require("diffview.vcs") +local vcs = lazy.require("diffview.vcs") ---@module "diffview.utils" local utils = lazy.require("diffview.utils") @@ -122,7 +122,7 @@ function File:create_buffer(callback) end if self.binary == nil and not config.get_config().diff_binaries then - self.binary = git.is_binary(self.git_ctx.toplevel, self.path, self.rev) + self.binary = vcs.is_binary(self.git_ctx.toplevel, self.path, self.rev) end if self.nulled or self.binary then @@ -208,7 +208,7 @@ function File:create_buffer(callback) end, nil) else - git.show( + vcs.show( self.git_ctx.toplevel, { ("%s:%s"):format(self.rev:object_name() or "", self.path) }, function(err, result) From 66b7d2dd2687b1511b68ce182141be8730ba4d76 Mon Sep 17 00:00:00 2001 From: zegervdv Date: Sat, 15 Oct 2022 16:05:00 +0200 Subject: [PATCH 10/71] refactor: create adapter base-class and implementations for git and hg --- lua/diffview/vcs/adapter.lua | 8 ++++++++ lua/diffview/vcs/adapters/git/init.lua | 9 +++++++++ lua/diffview/vcs/adapters/hg/init.lua | 9 +++++++++ lua/diffview/vcs/init.lua | 8 ++++++-- 4 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 lua/diffview/vcs/adapter.lua create mode 100644 lua/diffview/vcs/adapters/git/init.lua create mode 100644 lua/diffview/vcs/adapters/hg/init.lua diff --git a/lua/diffview/vcs/adapter.lua b/lua/diffview/vcs/adapter.lua new file mode 100644 index 00000000..ddf3f32d --- /dev/null +++ b/lua/diffview/vcs/adapter.lua @@ -0,0 +1,8 @@ +local oop = require('diffview.oop') + +local M = {} + +local VCSAdapter = oop.create_class('VCSAdapter') + +M.VCSAdapter = VCSAdapter +return M diff --git a/lua/diffview/vcs/adapters/git/init.lua b/lua/diffview/vcs/adapters/git/init.lua new file mode 100644 index 00000000..c71c4152 --- /dev/null +++ b/lua/diffview/vcs/adapters/git/init.lua @@ -0,0 +1,9 @@ +local oop = require('diffview.oop') +local VCSAdapter = require('diffview.vcs.adapter').VCSAdapter + +local M = {} + +local GitAdapter = oop.create_class('GitAdapter', VCSAdapter) + +M.GitAdapter = GitAdapter +return M diff --git a/lua/diffview/vcs/adapters/hg/init.lua b/lua/diffview/vcs/adapters/hg/init.lua new file mode 100644 index 00000000..29a579d6 --- /dev/null +++ b/lua/diffview/vcs/adapters/hg/init.lua @@ -0,0 +1,9 @@ +local oop = require('diffview.oop') +local VCSAdapter = require('diffview.vcs.adapter').VCSAdapter + +local M = {} + +local HgAdapter = oop.create_class('HgAdapter', VCSAdapter) + +M.HgAdapter = HgAdapter +return M diff --git a/lua/diffview/vcs/init.lua b/lua/diffview/vcs/init.lua index f27913a1..7fef70de 100644 --- a/lua/diffview/vcs/init.lua +++ b/lua/diffview/vcs/init.lua @@ -1,3 +1,7 @@ -local git = require('diffview.vcs.adapters.git.utils') +local M = {} -return git +function M.get_adapter(path) + -- TODO: return adapter based on path specification +end + +return M From dce8133b7d45bc593595d0d24624b5f2615bc251 Mon Sep 17 00:00:00 2001 From: zegervdv Date: Sat, 15 Oct 2022 16:10:45 +0200 Subject: [PATCH 11/71] fixup! refactor: create adapter base-class and implementations for git and hg --- lua/diffview/vcs/init.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lua/diffview/vcs/init.lua b/lua/diffview/vcs/init.lua index 7fef70de..80d70b43 100644 --- a/lua/diffview/vcs/init.lua +++ b/lua/diffview/vcs/init.lua @@ -1,7 +1,9 @@ +local git = require('diffview.vcs.adapters.git').GitAdapter + local M = {} function M.get_adapter(path) - -- TODO: return adapter based on path specification + return git end return M From 41420d1885e27bec75246b21bf7a8dab88720f5e Mon Sep 17 00:00:00 2001 From: zegervdv Date: Sat, 15 Oct 2022 17:11:11 +0200 Subject: [PATCH 12/71] fixup! refactor: create adapter base-class and implementations for git and hg --- lua/diffview/vcs/adapter.lua | 40 +++++++++++++++ lua/diffview/vcs/adapters/git/init.lua | 71 ++++++++++++++++++++++++++ lua/diffview/vcs/init.lua | 2 +- 3 files changed, 112 insertions(+), 1 deletion(-) diff --git a/lua/diffview/vcs/adapter.lua b/lua/diffview/vcs/adapter.lua index ddf3f32d..4f8a29e3 100644 --- a/lua/diffview/vcs/adapter.lua +++ b/lua/diffview/vcs/adapter.lua @@ -1,8 +1,48 @@ local oop = require('diffview.oop') +local utils = require('diffview.utils') +local logger = require('diffview.logger') local M = {} local VCSAdapter = oop.create_class('VCSAdapter') +function VCSAdapter:init(path) + self.path = path + self.bootstrap = { + done = false, + ok = false, + } +end + +function VCSAdapter:run_bootstrap() + oop.abstract_stub() +end + +function VCSAdapter:get_command() + oop.abstract_stub() +end + +---@return string cmd The VCS binary. +function VCSAdapter:bin() + return self:get_command()[1] +end + +---@return string[] args The default VCS args. +function VCSAdapter:args() + return utils.vec_slice(self:get_command(), 2) +end + +function VCSAdapter:exec_sync(args, cwd_or_opt) + if not self.bootstrap.done then + self:run_bootstrap() + end + + return utils.system_list( + vim.tbl_flatten({ self:get_command(), args }), + cwd_or_opt + ) +end + + M.VCSAdapter = VCSAdapter return M diff --git a/lua/diffview/vcs/adapters/git/init.lua b/lua/diffview/vcs/adapters/git/init.lua index c71c4152..c6b6f800 100644 --- a/lua/diffview/vcs/adapters/git/init.lua +++ b/lua/diffview/vcs/adapters/git/init.lua @@ -1,9 +1,80 @@ local oop = require('diffview.oop') +local logger = require('diffview.logger') +local utils = require('diffview.utils') +local config = require('diffview.config') local VCSAdapter = require('diffview.vcs.adapter').VCSAdapter local M = {} local GitAdapter = oop.create_class('GitAdapter', VCSAdapter) +function GitAdapter:init(path) + self.super:init(path) + + local bootstrap = { + version_string = nil, + version = {}, + target_version_string = nil, + target_version = { + major = 2, + minor = 31, + patch = 0, + }, + } + self.bootstrap = vim.tbl_extend('force', self.bootstrap, bootstrap) +end + +function GitAdapter:run_bootstrap() + local msg + self.bootstrap.done = true + + local out, code = utils.system_list(vim.tbl_flatten({ config.get_config().git_cmd, "version" })) + if code ~= 0 or not out[1] then + msg = "Could not run `git_cmd`!" + logger.error(msg) + utils.err(msg) + return + end + + self.bootstrap.version_string = out[1]:match("git version (%S+)") + + if not self.bootstrap.version_string then + msg = "Could not get git version!" + logger.error(msg) + utils.err(msg) + return + end + + -- Parse git version + local v, target = self.bootstrap.version, self.bootstrap.target_version + self.bootstrap.target_version_string = ("%d.%d.%d"):format(target.major, target.minor, target.patch) + local parts = vim.split(self.bootstrap.version_string, "%.") + v.major = tonumber(parts[1]) + v.minor = tonumber(parts[2]) + v.patch = tonumber(parts[3]) or 0 + + local vs = ("%08d%08d%08d"):format(v.major, v.minor, v.patch) + local ts = ("%08d%08d%08d"):format(target.major, target.minor, target.patch) + + if vs < ts then + msg = ( + "Git version is outdated! Some functionality might not work as expected, " + .. "or not at all! Target: %s, current: %s" + ):format( + self.bootstrap.target_version_string, + self.bootstrap.version_string + ) + logger.error(msg) + utils.err(msg) + return + end + + self.bootstrap.ok = true +end + +function GitAdapter:get_command() + return config.get_config().git_cmd +end + M.GitAdapter = GitAdapter return M diff --git a/lua/diffview/vcs/init.lua b/lua/diffview/vcs/init.lua index 80d70b43..72438cc0 100644 --- a/lua/diffview/vcs/init.lua +++ b/lua/diffview/vcs/init.lua @@ -3,7 +3,7 @@ local git = require('diffview.vcs.adapters.git').GitAdapter local M = {} function M.get_adapter(path) - return git + return git(path) end return M From d26351095067561b4f88ea2c5875d5f153565fac Mon Sep 17 00:00:00 2001 From: zegervdv Date: Sat, 15 Oct 2022 22:48:31 +0200 Subject: [PATCH 13/71] fixup! refactor: create adapter base-class and implementations for git and hg --- lua/diffview/vcs/adapter.lua | 5 ++++ lua/diffview/vcs/adapters/git/init.lua | 35 ++++++++++++++++++-------- 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/lua/diffview/vcs/adapter.lua b/lua/diffview/vcs/adapter.lua index 4f8a29e3..66986149 100644 --- a/lua/diffview/vcs/adapter.lua +++ b/lua/diffview/vcs/adapter.lua @@ -12,6 +12,7 @@ function VCSAdapter:init(path) done = false, ok = false, } + self.context = self:get_context(path) end function VCSAdapter:run_bootstrap() @@ -22,6 +23,10 @@ function VCSAdapter:get_command() oop.abstract_stub() end +function VCSAdapter:get_context(path) + return {} +end + ---@return string cmd The VCS binary. function VCSAdapter:bin() return self:get_command()[1] diff --git a/lua/diffview/vcs/adapters/git/init.lua b/lua/diffview/vcs/adapters/git/init.lua index c6b6f800..3a8eb162 100644 --- a/lua/diffview/vcs/adapters/git/init.lua +++ b/lua/diffview/vcs/adapters/git/init.lua @@ -11,17 +11,16 @@ local GitAdapter = oop.create_class('GitAdapter', VCSAdapter) function GitAdapter:init(path) self.super:init(path) - local bootstrap = { - version_string = nil, - version = {}, - target_version_string = nil, - target_version = { - major = 2, - minor = 31, - patch = 0, - }, + self.bootstrap.version_string = nil + self.bootstrap.version = {} + self.bootstrap.target_version_string = nil + self.bootstrap.target_version = { + major = 2, + minor = 31, + patch = 0, } - self.bootstrap = vim.tbl_extend('force', self.bootstrap, bootstrap) + + self.context = self:get_context(path) end function GitAdapter:run_bootstrap() @@ -76,5 +75,21 @@ function GitAdapter:get_command() return config.get_config().git_cmd end +function GitAdapter:get_context(path) + local context = {} + local out, code = self:exec_sync({ "rev-parse", "--path-format=absolute", "--show-toplevel" }, path) + if code ~= 0 then + return nil + end + context.toplevel = out[1] and vim.trim(out[1]) + + out, code = self:exec_sync({ "rev-parse", "--path-format=absolute", "--git-dir" }, path) + if code ~= 0 then + return nil + end + context.dir = out[1] and vim.trim(out[1]) + return context +end + M.GitAdapter = GitAdapter return M From 60da691c530fa9b053aef4c6e1dc8b38a3ba5695 Mon Sep 17 00:00:00 2001 From: zegervdv Date: Sun, 16 Oct 2022 11:32:16 +0200 Subject: [PATCH 14/71] fixup! refactor: create adapter base-class and implementations for git and hg --- lua/diffview/lib.lua | 130 +---------------- lua/diffview/vcs/adapter.lua | 3 + lua/diffview/vcs/adapters/git/init.lua | 193 +++++++++++++++++++++++++ 3 files changed, 202 insertions(+), 124 deletions(-) diff --git a/lua/diffview/lib.lua b/lua/diffview/lib.lua index a27dbb75..788f885f 100644 --- a/lua/diffview/lib.lua +++ b/lua/diffview/lib.lua @@ -131,7 +131,8 @@ end ---@param range? { [1]: integer, [2]: integer } ---@param args string[] -function M.file_history(range, args) +function M.file_history(adapter, range, args) + -- TODO: Arg handling is git-specific too? local default_args = config.get_config().default_args.DiffviewFileHistory local argo = arg_parser.parse(vim.tbl_flatten({ default_args, args })) local paths = {} @@ -142,134 +143,15 @@ function M.file_history(range, args) args, }), " ")) - for _, path_arg in ipairs(argo.args) do - for _, path in ipairs(pl:vim_expand(path_arg, false, true)) do - local magic, pattern = vcs.pathspec_split(path) - pattern = pl:readlink(pattern) or pattern - table.insert(paths, magic .. pattern) - end - end - - ---@type string - local cpath = argo:get_flag("C", { no_empty = true, expand = true }) - local cfile = pl:vim_expand("%") - cfile = pl:readlink(cfile) or cfile - - local top_indicators = {} - for _, path in ipairs(paths) do - if vcs.pathspec_split(path) == "" then - table.insert(top_indicators, pl:absolute(path, cpath)) - break - end - end - - table.insert(top_indicators, cpath and pl:realpath(cpath) or ( - vim.bo.buftype == "" - and pl:absolute(cfile) - or nil - )) + local log_options = adapter:file_history_options(range, args) - if not cpath then - table.insert(top_indicators, pl:realpath(".")) - end - - local err, git_toplevel = M.find_git_toplevel(top_indicators) - - if err then - utils.err(err) - return - end - - ---@cast git_toplevel string - logger.lvl(1).s_debug(("Found git top-level: %s"):format(utils.str_quote(git_toplevel))) - - rel_paths = vim.tbl_map(function(v) - return v == "." and "." or pl:relative(v, ".") - end, paths) - - local cwd = cpath or vim.loop.cwd() - paths = vim.tbl_map(function(pathspec) - return vcs.pathspec_expand(git_toplevel, cwd, pathspec) - end, paths) --[[@as string[] ]] - - ---@type string - local range_arg = argo:get_flag("range", { no_empty = true }) - if range_arg then - local ok = vcs.verify_rev_arg(git_toplevel, range_arg) - if not ok then - utils.err(("Bad revision: %s"):format(utils.str_quote(range_arg))) - return - end - - logger.lvl(1).s_debug(("Verified range rev: %s"):format(range_arg)) - end - - local log_flag_names = { - { "follow" }, - { "first-parent" }, - { "show-pulls" }, - { "reflog" }, - { "all" }, - { "merges" }, - { "no-merges" }, - { "reverse" }, - { "max-count", "n" }, - { "L" }, - { "diff-merges" }, - { "author" }, - { "grep" }, - { "base" }, - } - - ---@type LogOptions - local log_options = { rev_range = range_arg } - for _, names in ipairs(log_flag_names) do - local key, _ = names[1]:gsub("%-", "_") - local v = argo:get_flag(names, { - expect_string = type(config.log_option_defaults[key]) ~= "boolean", - expect_list = names[1] == "L", - }) - log_options[key] = v - end - - if range then - paths, rel_paths = {}, {} - log_options.L = { - ("%d,%d:%s"):format(range[1], range[2], pl:relative(pl:absolute(cfile), git_toplevel)) - } - end - - log_options.path_args = paths - - local ok, opt_description = vcs.file_history_dry_run(git_toplevel, log_options) - - if not ok then - utils.info({ - ("No git history for the target(s) given the current options! Targets: %s") - :format(#rel_paths == 0 and "':(top)'" or table.concat(vim.tbl_map(function(v) - return "'" .. v .. "'" - end, rel_paths) --[[@as vector ]], ", ")), - ("Current options: [ %s ]"):format(opt_description) - }) - return - end - - local git_ctx = { - toplevel = git_toplevel, - dir = vcs.git_dir(git_toplevel), - } - - if not git_ctx.dir then - utils.err( - ("Failed to find the git dir for the repository: %s") - :format(utils.str_quote(git_ctx.toplevel)) - ) - return + if log_options == nil then + utils.err('Failed to create log options for file_history') end ---@type FileHistoryView local v = FileHistoryView({ - git_ctx = git_ctx, + git_ctx = adapter, log_options = log_options, }) diff --git a/lua/diffview/vcs/adapter.lua b/lua/diffview/vcs/adapter.lua index 66986149..42b156a4 100644 --- a/lua/diffview/vcs/adapter.lua +++ b/lua/diffview/vcs/adapter.lua @@ -48,6 +48,9 @@ function VCSAdapter:exec_sync(args, cwd_or_opt) ) end +function VCSAdapter:file_history_options(range, args) +end + M.VCSAdapter = VCSAdapter return M diff --git a/lua/diffview/vcs/adapters/git/init.lua b/lua/diffview/vcs/adapters/git/init.lua index 3a8eb162..ed72b28e 100644 --- a/lua/diffview/vcs/adapters/git/init.lua +++ b/lua/diffview/vcs/adapters/git/init.lua @@ -1,9 +1,14 @@ local oop = require('diffview.oop') +local arg_parser = require('diffview.arg_parser') local logger = require('diffview.logger') local utils = require('diffview.utils') local config = require('diffview.config') +local lazy = require('diffview.lazy') local VCSAdapter = require('diffview.vcs.adapter').VCSAdapter +---@type PathLib +local pl = lazy.access(utils, "path") + local M = {} local GitAdapter = oop.create_class('GitAdapter', VCSAdapter) @@ -91,5 +96,193 @@ function GitAdapter:get_context(path) return context end +---@return string, string +local function pathspec_split(pathspec) + local magic = pathspec:match("^:[/!^]*:?") or pathspec:match("^:%b()") or "" + local pattern = pathspec:sub(1 + #magic, -1) + return magic or "", pattern or "" +end + +local function pathspec_expand(toplevel, cwd, pathspec) + local magic, pattern = pathspec_split(pathspec) + if not utils.path:is_abs(pattern) then + pattern = utils.path:join(utils.path:relative(cwd, toplevel), pattern) + end + return magic .. utils.path:convert(pattern) +end + +local function pathspec_modify(pathspec, mods) + local magic, pattern = pathspec_split(pathspec) + return magic .. utils.path:vim_fnamemodify(pattern, mods) +end + +function GitAdapter:find_git_toplevel(top_indicators) + local toplevel + for _, p in ipairs(top_indicators) do + if not pl:is_dir(p) then + p = pl:parent(p) + end + + if p and pl:readable(p) then + local ctxt = self:get_context(p) + toplevel = ctxt.toplevel + + if toplevel then + return nil, toplevel + end + end + end + + return ( + ("Path not a git repo (or any parent): %s") + :format(table.concat(vim.tbl_map(function(v) + local rel_path = pl:relative(v, ".") + return utils.str_quote(rel_path == "" and "." or rel_path) + end, top_indicators) --[[@as vector ]], ", ")) + ) + +end + +---@param range? { [1]: integer, [2]: integer } +---@param args string[] +function GitAdapter:file_history_options(range, args) + local default_args = config.get_config().default_args.DiffviewFileHistory + local argo = arg_parser.parse(vim.tbl_flatten({ default_args, args })) + local paths = {} + local rel_paths + + logger.info("[command call] :DiffviewFileHistory " .. table.concat(vim.tbl_flatten({ + default_args, + args, + }), " ")) + + for _, path_arg in ipairs(argo.args) do + for _, path in ipairs(pl:vim_expand(path_arg, false, true)) do + local magic, pattern = pathspec_split(path) + pattern = pl:readlink(pattern) or pattern + table.insert(paths, magic .. pattern) + end + end + + ---@type string + local cpath = argo:get_flag("C", { no_empty = true, expand = true }) + local cfile = pl:vim_expand("%") + cfile = pl:readlink(cfile) or cfile + + local top_indicators = {} + for _, path in ipairs(paths) do + if pathspec_split(path) == "" then + table.insert(top_indicators, pl:absolute(path, cpath)) + break + end + end + + table.insert(top_indicators, cpath and pl:realpath(cpath) or ( + vim.bo.buftype == "" + and pl:absolute(cfile) + or nil + )) + + if not cpath then + table.insert(top_indicators, pl:realpath(".")) + end + + local err, git_toplevel = self:find_git_toplevel(top_indicators) + + if err then + utils.err(err) + return + end + + ---@cast git_toplevel string + logger.lvl(1).s_debug(("Found git top-level: %s"):format(utils.str_quote(git_toplevel))) + + rel_paths = vim.tbl_map(function(v) + return v == "." and "." or pl:relative(v, ".") + end, paths) + + local cwd = cpath or vim.loop.cwd() + paths = vim.tbl_map(function(pathspec) + return pathspec_expand(git_toplevel, cwd, pathspec) + end, paths) --[[@as string[] ]] + + ---@type string + local range_arg = argo:get_flag("range", { no_empty = true }) + if range_arg then + local ok = self:verify_rev_arg(git_toplevel, range_arg) + if not ok then + utils.err(("Bad revision: %s"):format(utils.str_quote(range_arg))) + return + end + + logger.lvl(1).s_debug(("Verified range rev: %s"):format(range_arg)) + end + + local log_flag_names = { + { "follow" }, + { "first-parent" }, + { "show-pulls" }, + { "reflog" }, + { "all" }, + { "merges" }, + { "no-merges" }, + { "reverse" }, + { "max-count", "n" }, + { "L" }, + { "diff-merges" }, + { "author" }, + { "grep" }, + { "base" }, + } + + ---@type LogOptions + local log_options = { rev_range = range_arg } + for _, names in ipairs(log_flag_names) do + local key, _ = names[1]:gsub("%-", "_") + local v = argo:get_flag(names, { + expect_string = type(config.log_option_defaults[key]) ~= "boolean", + expect_list = names[1] == "L", + }) + log_options[key] = v + end + + if range then + paths, rel_paths = {}, {} + log_options.L = { + ("%d,%d:%s"):format(range[1], range[2], pl:relative(pl:absolute(cfile), git_toplevel)) + } + end + + log_options.path_args = paths + + local ok, opt_description = self:file_history_dry_run(git_toplevel, log_options) + + if not ok then + utils.info({ + ("No git history for the target(s) given the current options! Targets: %s") + :format(#rel_paths == 0 and "':(top)'" or table.concat(vim.tbl_map(function(v) + return "'" .. v .. "'" + end, rel_paths) --[[@as vector ]], ", ")), + ("Current options: [ %s ]"):format(opt_description) + }) + return + end + + local git_ctx = { + toplevel = git_toplevel, + dir = self.context.dir, + } + + if not git_ctx.dir then + utils.err( + ("Failed to find the git dir for the repository: %s") + :format(utils.str_quote(git_ctx.toplevel)) + ) + return + end + + return log_options +end + M.GitAdapter = GitAdapter return M From 22cd83f6be61dd3a422a8daa80a62efc9e7ef0a0 Mon Sep 17 00:00:00 2001 From: zegervdv Date: Mon, 17 Oct 2022 18:54:17 +0200 Subject: [PATCH 15/71] fixup! refactor: create adapter base-class and implementations for git and hg --- lua/diffview/vcs/adapters/git/init.lua | 379 +++++++++++++++++++++++++ 1 file changed, 379 insertions(+) diff --git a/lua/diffview/vcs/adapters/git/init.lua b/lua/diffview/vcs/adapters/git/init.lua index ed72b28e..fa0167ce 100644 --- a/lua/diffview/vcs/adapters/git/init.lua +++ b/lua/diffview/vcs/adapters/git/init.lua @@ -2,6 +2,7 @@ local oop = require('diffview.oop') local arg_parser = require('diffview.arg_parser') local logger = require('diffview.logger') local utils = require('diffview.utils') +local async = require("plenary.async") local config = require('diffview.config') local lazy = require('diffview.lazy') local VCSAdapter = require('diffview.vcs.adapter').VCSAdapter @@ -143,6 +144,384 @@ function GitAdapter:find_git_toplevel(top_indicators) end +---@class git.utils.PreparedLogOpts +---@field rev_range string +---@field base Rev +---@field path_args string[] +---@field flags string[] + +---@param toplevel string +---@param log_options LogOptions +---@param single_file boolean +---@return git.utils.PreparedLogOpts +local function prepare_fh_options(toplevel, log_options, single_file) + local o = log_options + local line_trace = vim.tbl_map(function(v) + if not v:match("^-L") then + return "-L" .. v + end + return v + end, o.L or {}) + + local rev_range, base + + if log_options.rev_range then + local ok, _ = M.verify_rev_arg(toplevel, log_options.rev_range) + + if not ok then + utils.warn(("Bad range revision, ignoring: %s"):format(utils.str_quote(log_options.rev_range))) + else + rev_range = log_options.rev_range + end + end + + if log_options.base then + if log_options.base == "LOCAL" then + base = Rev(RevType.LOCAL) + else + local ok, out = M.verify_rev_arg(toplevel, log_options.base) + + if not ok then + utils.warn(("Bad base revision, ignoring: %s"):format(utils.str_quote(log_options.base))) + else + base = Rev(RevType.COMMIT, out[1]) + end + end + end + + return { + rev_range = rev_range, + base = base, + path_args = log_options.path_args, + flags = utils.vec_join( + line_trace, + (o.follow and single_file) and { "--follow" } or nil, + o.first_parent and { "--first-parent" } or nil, + o.show_pulls and { "--show-pulls" } or nil, + o.reflog and { "--reflog" } or nil, + o.all and { "--all" } or nil, + o.merges and { "--merges" } or nil, + o.no_merges and { "--no-merges" } or nil, + o.reverse and { "--reverse" } or nil, + o.max_count and { "-n" .. o.max_count } or nil, + o.diff_merges and { "--diff-merges=" .. o.diff_merges } or nil, + o.author and { "-E", "--author=" .. o.author } or nil, + o.grep and { "-E", "--grep=" .. o.grep } or nil + ) + } +end + +local function structure_fh_data(namestat_data, numstat_data) + local right_hash, left_hash, merge_hash = unpack(utils.str_split(namestat_data[1])) + local time, time_offset = unpack(utils.str_split(namestat_data[3])) + + return { + left_hash = left_hash ~= "" and left_hash or nil, + right_hash = right_hash, + merge_hash = merge_hash, + author = namestat_data[2], + time = tonumber(time), + time_offset = time_offset, + rel_date = namestat_data[4], + ref_names = namestat_data[5]:sub(3), + subject = namestat_data[6]:sub(3), + namestat = utils.vec_slice(namestat_data, 7), + numstat = numstat_data, + } +end + +---@param state git.utils.FHState +---@param callback fun(status: JobStatus, data?: table, msg?: string[]) +local incremental_fh_data = async.void(function(state, callback) + local raw = {} + local namestat_job, numstat_job, shutdown + + local namestat_state = { + data = {}, + key = "namestat", + idx = 0, + } + local numstat_state = { + data = {}, + key = "numstat", + idx = 0, + } + + local function on_stdout(_, line, j) + local handler_state = j == namestat_job and namestat_state or numstat_state + + if line == "\0" then + if handler_state.idx > 0 then + if not raw[handler_state.idx] then + raw[handler_state.idx] = {} + end + + raw[handler_state.idx][handler_state.key] = handler_state.data + + if not shutdown and raw[handler_state.idx].namestat and raw[handler_state.idx].numstat then + shutdown = callback( + JobStatus.PROGRESS, + structure_fh_data(raw[handler_state.idx].namestat, raw[handler_state.idx].numstat) + ) + + if shutdown then + logger.lvl(1).debug("Killing file history jobs...") + -- NOTE: The default `Job:shutdown` methods use `vim.wait` which + -- causes a segfault when called here. + namestat_job:_shutdown(64) + numstat_job:_shutdown(64) + end + end + end + handler_state.idx = handler_state.idx + 1 + handler_state.data = {} + elseif line ~= "" then + table.insert(handler_state.data, line) + end + end + + ---@type CountDownLatch + local latch = CountDownLatch(2) + + local function on_exit(j, code) + if code == 0 then + on_stdout(nil, "\0", j) + end + latch:count_down() + end + + local rev_range = state.prepared_log_opts.rev_range + + namestat_job = Job:new({ + command = git_bin(), + args = utils.vec_join( + git_args(), + "log", + rev_range, + "--pretty=format:%x00%n%H %P%n%an%n%ad%n%ar%n %D%n %s", + "--date=raw", + "--name-status", + state.prepared_log_opts.flags, + "--", + state.path_args + ), + cwd = state.ctx.toplevel, + on_stdout = on_stdout, + on_exit = on_exit, + }) + + numstat_job = Job:new({ + command = git_bin(), + args = utils.vec_join( + git_args(), + "log", + rev_range, + "--pretty=format:%x00", + "--date=raw", + "--numstat", + state.prepared_log_opts.flags, + "--", + state.path_args + ), + cwd = state.ctx.toplevel, + on_stdout = on_stdout, + on_exit = on_exit, + }) + + namestat_job:start() + numstat_job:start() + + latch:await() + + local debug_opt = { + context = "git.utils>incremental_fh_data()", + func = "s_info", + no_stdout = true, + } + utils.handle_job(namestat_job, { debug_opt = debug_opt }) + utils.handle_job(numstat_job, { debug_opt = debug_opt }) + + if shutdown then + callback(JobStatus.KILLED) + elseif namestat_job.code ~= 0 or numstat_job.code ~= 0 then + callback(JobStatus.ERROR, nil, utils.vec_join( + namestat_job:stderr_result(), + numstat_job:stderr_result()) + ) + else + callback(JobStatus.SUCCESS) + end +end) + +---@param state git.utils.FHState +---@param callback fun(status: JobStatus, data?: table, msg?: string[]) +local incremental_line_trace_data = async.void(function(state, callback) + local raw = {} + local trace_job, shutdown + + local trace_state = { + data = {}, + key = "trace", + idx = 0, + } + + local function on_stdout(_, line) + if line == "\0" then + if trace_state.idx > 0 then + if not raw[trace_state.idx] then + raw[trace_state.idx] = {} + end + + raw[trace_state.idx] = trace_state.data + + if not shutdown then + shutdown = callback( + JobStatus.PROGRESS, + structure_fh_data(raw[trace_state.idx]) + ) + + if shutdown then + logger.lvl(1).debug("Killing file history jobs...") + -- NOTE: The default `Job:shutdown` methods use `vim.wait` which + -- causes a segfault when called here. + trace_job:_shutdown(64) + end + end + end + trace_state.idx = trace_state.idx + 1 + trace_state.data = {} + elseif line ~= "" then + table.insert(trace_state.data, line) + end + end + + ---@type CountDownLatch + local latch = CountDownLatch(1) + + local function on_exit(_, code) + if code == 0 then + on_stdout(nil, "\0") + end + latch:count_down() + end + + local rev_range = state.prepared_log_opts.rev_range + + trace_job = Job:new({ + command = git_bin(), + args = utils.vec_join( + git_args(), + "-P", + "log", + rev_range, + "--color=never", + "--no-ext-diff", + "--pretty=format:%x00%n%H %P%n%an%n%ad%n%ar%n %D%n %s", + "--date=raw", + state.prepared_log_opts.flags, + "--" + ), + cwd = state.ctx.toplevel, + on_stdout = on_stdout, + on_exit = on_exit, + }) + + trace_job:start() + + latch:await() + + utils.handle_job(trace_job, { + debug_opt = { + context = "git.utils>incremental_line_trace_data()", + func = "s_debug", + debug_level = 1, + no_stdout = true, + } + }) + + if shutdown then + callback(JobStatus.KILLED) + elseif trace_job.code ~= 0 then + callback(JobStatus.ERROR, nil, trace_job:stderr_result()) + else + callback(JobStatus.SUCCESS) + end +end) + +---@param toplevel string +---@param path_args string[] +---@param lflags string[] +---@return boolean +function GitAdapter:is_single_file(toplevel, path_args, lflags) + if lflags and #lflags > 0 then + local seen = {} + for i, v in ipairs(lflags) do + local path = v:match(".*:(.*)") + if i > 1 and not seen[path] then + return false + end + seen[path] = true + end + + elseif path_args and toplevel then + return #path_args == 1 + and not utils.path:is_dir(path_args[1]) + and #self:exec_sync({ "ls-files", "--", path_args }, toplevel) < 2 + end + + return true +end + +---@param toplevel string +---@param log_opt LogOptions +---@return boolean ok, string description +function GitAdapter:file_history_dry_run(toplevel, log_opt) + local single_file = self:is_single_file(toplevel, log_opt.path_args, log_opt.L) + local log_options = config.get_log_options(single_file, log_opt) + + local options = vim.tbl_map(function(v) + return vim.fn.shellescape(v) + end, prepare_fh_options(toplevel, log_options, single_file).flags) --[[@as vector ]] + + local description = utils.vec_join( + ("Top-level path: '%s'"):format(utils.path:vim_fnamemodify(toplevel, ":~")), + log_options.rev_range and ("Revision range: '%s'"):format(log_options.rev_range) or nil, + ("Flags: %s"):format(table.concat(options, " ")) + ) + + log_options = utils.tbl_clone(log_options) --[[@as LogOptions ]] + log_options.max_count = 1 + options = prepare_fh_options(toplevel, log_options, single_file).flags + + local context = "git.utils.file_history_dry_run()" + local cmd + + if #log_options.L > 0 then + -- cmd = utils.vec_join("-P", "log", log_options.rev_range, "--no-ext-diff", "--color=never", "--pretty=format:%H", "-s", options, "--") + -- NOTE: Running the dry-run for line tracing is slow. Just skip for now. + return true, table.concat(description, ", ") + else + cmd = utils.vec_join("log", log_options.rev_range, "--pretty=format:%H", "--name-status", options, "--", log_options.path_args) + end + + local out, code = self:exec_sync(cmd, { + cwd = toplevel, + debug_opt = { + context = context, + no_stdout = true, + }, + }) + + local ok = code == 0 and #out > 0 + + if not ok then + logger.lvl(1).s_debug(("[%s] Dry run failed."):format(context)) + end + + return ok, table.concat(description, ", ") + +end + ---@param range? { [1]: integer, [2]: integer } ---@param args string[] function GitAdapter:file_history_options(range, args) From 28e39aaffde860ef4b4cef6ced050ad47bba2657 Mon Sep 17 00:00:00 2001 From: zegervdv Date: Mon, 17 Oct 2022 22:09:25 +0200 Subject: [PATCH 16/71] fixup! refactor: create adapter base-class and implementations for git and hg --- lua/diffview/scene/file_entry.lua | 30 +- .../views/file_history/file_history_panel.lua | 3 +- lua/diffview/vcs/adapter.lua | 53 ++++ lua/diffview/vcs/adapters/git/init.lua | 299 +++++++++++++++++- lua/diffview/vcs/file.lua | 20 +- 5 files changed, 373 insertions(+), 32 deletions(-) diff --git a/lua/diffview/scene/file_entry.lua b/lua/diffview/scene/file_entry.lua index 47a8fdc6..b94f57d9 100644 --- a/lua/diffview/scene/file_entry.lua +++ b/lua/diffview/scene/file_entry.lua @@ -26,7 +26,7 @@ local fstat_cache = {} ---@field conflicts integer ---@class FileEntry : diffview.Object ----@field git_ctx GitContext +---@field adapter GitAdapter ---@field path string ---@field oldpath string ---@field absolute_path string @@ -42,7 +42,7 @@ local fstat_cache = {} local FileEntry = oop.create_class("FileEntry") ---@class FileEntry.init.Opt ----@field git_ctx GitContext +---@field adapter GitAdapter ---@field path string ---@field oldpath string ---@field layout Layout @@ -56,7 +56,7 @@ local FileEntry = oop.create_class("FileEntry") function FileEntry:init(opt) self.path = opt.path self.oldpath = opt.oldpath - self.absolute_path = utils.path:absolute(opt.path, opt.git_ctx.toplevel) + self.absolute_path = utils.path:absolute(opt.path, opt.adapter.context.toplevel) self.parent_path = utils.path:parent(opt.path) or "" self.basename = utils.path:basename(opt.path) self.extension = utils.path:extension(opt.path) @@ -211,7 +211,7 @@ end ---@return FileEntry function FileEntry.for_d2(layout_class, opt) return FileEntry({ - git_ctx = opt.git_ctx, + adapter = opt.adapter, path = opt.path, oldpath = opt.oldpath, status = opt.status, @@ -220,7 +220,7 @@ function FileEntry.for_d2(layout_class, opt) commit = opt.commit, layout = layout_class({ a = File({ - git_ctx = opt.git_ctx, + adapter = opt.adapter, path = opt.oldpath or opt.path, kind = opt.kind, commit = opt.commit, @@ -229,7 +229,7 @@ function FileEntry.for_d2(layout_class, opt) nulled = utils.sate(opt.nulled, layout_class.should_null(opt.rev_a, opt.status, "a")), }), b = File({ - git_ctx = opt.git_ctx, + adapter = opt.adapter, path = opt.path, kind = opt.kind, commit = opt.commit, @@ -254,7 +254,7 @@ end ---@return FileEntry function FileEntry.for_d3(layout_class, opt) return FileEntry({ - git_ctx = opt.git_ctx, + adapter = opt.adapter, path = opt.path, oldpath = opt.oldpath, status = opt.status, @@ -263,7 +263,7 @@ function FileEntry.for_d3(layout_class, opt) commit = opt.commit, layout = layout_class({ a = File({ - git_ctx = opt.git_ctx, + adapter = opt.adapter, path = opt.oldpath or opt.path, kind = opt.kind, commit = opt.commit, @@ -272,7 +272,7 @@ function FileEntry.for_d3(layout_class, opt) nulled = utils.sate(opt.nulled, layout_class.should_null(opt.rev_a, opt.status, "a")), }), b = File({ - git_ctx = opt.git_ctx, + adapter = opt.adapter, path = opt.path, kind = opt.kind, commit = opt.commit, @@ -281,7 +281,7 @@ function FileEntry.for_d3(layout_class, opt) nulled = utils.sate(opt.nulled, layout_class.should_null(opt.rev_b, opt.status, "b")), }), c = File({ - git_ctx = opt.git_ctx, + adapter = opt.adapter, path = opt.path, kind = opt.kind, commit = opt.commit, @@ -307,7 +307,7 @@ end function FileEntry.with_layout(layout_class, opt) local new_layout local main_file = File({ - git_ctx = opt.git_ctx, + adapter = opt.adapter, path = opt.path, kind = opt.kind, commit = opt.commit, @@ -324,7 +324,7 @@ function FileEntry.with_layout(layout_class, opt) main_file.nulled = layout_class.should_null(main_file.rev, opt.status, "b") new_layout = layout_class({ a = File({ - git_ctx = opt.git_ctx, + adapter = opt.adapter, path = opt.oldpath or opt.path, kind = opt.kind, commit = opt.commit, @@ -334,7 +334,7 @@ function FileEntry.with_layout(layout_class, opt) }), b = main_file, c = File({ - git_ctx = opt.git_ctx, + adapter = opt.adapter, path = opt.path, kind = opt.kind, commit = opt.commit, @@ -343,7 +343,7 @@ function FileEntry.with_layout(layout_class, opt) nulled = utils.sate(opt.nulled, layout_class.should_null(opt.rev_theirs, opt.status, "c")), }), d = File({ - git_ctx = opt.git_ctx, + adapter = opt.adapter, path = opt.path, kind = opt.kind, commit = opt.commit, @@ -355,7 +355,7 @@ function FileEntry.with_layout(layout_class, opt) end return FileEntry({ - git_ctx = opt.git_ctx, + adapter = opt.adapter, path = opt.path, oldpath = opt.oldpath, status = opt.status, diff --git a/lua/diffview/scene/views/file_history/file_history_panel.lua b/lua/diffview/scene/views/file_history/file_history_panel.lua index 22b720dd..ccd1d56e 100644 --- a/lua/diffview/scene/views/file_history/file_history_panel.lua +++ b/lua/diffview/scene/views/file_history/file_history_panel.lua @@ -279,8 +279,7 @@ function FileHistoryPanel:update_entries(callback) self.entries = {} self.updating = true - finalizer = vcs.file_history( - self.git_ctx, + finalizer = self.git_ctx:file_history( self.log_options, { default_layout = self.parent.get_default_diff2(), }, update diff --git a/lua/diffview/vcs/adapter.lua b/lua/diffview/vcs/adapter.lua index 42b156a4..172edeeb 100644 --- a/lua/diffview/vcs/adapter.lua +++ b/lua/diffview/vcs/adapter.lua @@ -49,6 +49,59 @@ function VCSAdapter:exec_sync(args, cwd_or_opt) end function VCSAdapter:file_history_options(range, args) + oop.abstract_stub() +end + +function VCSAdapter:file_history_worker(thread, log_opt, opt, co_state, callback) + oop.abstract_stub() +end + + +---@param log_opt ConfigLogOptions +---@param opt git.utils.FileHistoryWorkerSpec +---@param callback function +---@return fun() finalizer +function VCSAdapter:file_history(log_opt, opt, callback) + local thread + + local co_state = { + shutdown = false, + } + + thread = coroutine.create(function() + self:file_history_worker(thread, log_opt, opt, co_state, callback) + end) + + self:handle_co(thread, coroutine.resume(thread)) + + return function() + co_state.shutdown = true + end +end + +---@param thread thread +---@param ok boolean +---@param result any +---@return boolean ok +---@return any result +function VCSAdapter:handle_co(thread, ok, result) + if not ok then + local err_msg = utils.vec_join( + "Coroutine failed!", + debug.traceback(thread, result, 1) + ) + utils.err(err_msg, true) + logger.s_error(table.concat(err_msg, "\n")) + end + return ok, result +end + + +---@param path string +---@param rev Rev +---@return boolean -- True if the file was binary for the given rev, or it didn't exist. +function VCSAdapter:is_binary(path, rev) + oop.abstract_stub() end diff --git a/lua/diffview/vcs/adapters/git/init.lua b/lua/diffview/vcs/adapters/git/init.lua index fa0167ce..ee8ae8dc 100644 --- a/lua/diffview/vcs/adapters/git/init.lua +++ b/lua/diffview/vcs/adapters/git/init.lua @@ -1,11 +1,20 @@ local oop = require('diffview.oop') +local CountDownLatch = require("diffview.control").CountDownLatch local arg_parser = require('diffview.arg_parser') local logger = require('diffview.logger') local utils = require('diffview.utils') local async = require("plenary.async") local config = require('diffview.config') local lazy = require('diffview.lazy') +local FileEntry = require("diffview.scene.file_entry").FileEntry +local Diff2Hor = require("diffview.scene.layouts.diff_2_hor").Diff2Hor +local LogEntry = require("diffview.vcs.log_entry").LogEntry +local RevType = require("diffview.vcs.rev").RevType +local Rev = require("diffview.vcs.rev").Rev local VCSAdapter = require('diffview.vcs.adapter').VCSAdapter +local Job = require("plenary.job") +local JobStatus = require('diffview.vcs.utils').JobStatus +local Commit = require('diffview.vcs.adapters.git.commit').GitCommit ---@type PathLib local pl = lazy.access(utils, "path") @@ -293,9 +302,9 @@ local incremental_fh_data = async.void(function(state, callback) local rev_range = state.prepared_log_opts.rev_range namestat_job = Job:new({ - command = git_bin(), + command = state.adapter:bin(), args = utils.vec_join( - git_args(), + state.adapter:args(), "log", rev_range, "--pretty=format:%x00%n%H %P%n%an%n%ad%n%ar%n %D%n %s", @@ -305,15 +314,15 @@ local incremental_fh_data = async.void(function(state, callback) "--", state.path_args ), - cwd = state.ctx.toplevel, + cwd = state.adapter.context.toplevel, on_stdout = on_stdout, on_exit = on_exit, }) numstat_job = Job:new({ - command = git_bin(), + command = state.adapter:bin(), args = utils.vec_join( - git_args(), + state.adapter:args(), "log", rev_range, "--pretty=format:%x00", @@ -323,7 +332,7 @@ local incremental_fh_data = async.void(function(state, callback) "--", state.path_args ), - cwd = state.ctx.toplevel, + cwd = state.adapter.context.toplevel, on_stdout = on_stdout, on_exit = on_exit, }) @@ -663,5 +672,283 @@ function GitAdapter:file_history_options(range, args) return log_options end +---@class git.utils.FHState +---@field thread thread +---@field adapter GitAdapter +---@field path_args string[] +---@field log_options LogOptions +---@field prepared_log_opts git.utils.PreparedLogOpts +---@field opt git.utils.FileHistoryWorkerSpec +---@field single_file boolean +---@field resume_lock boolean +---@field cur table +---@field commit Commit +---@field entries LogEntry[] +---@field callback function + +---@param state git.utils.FHState +---@return boolean ok, JobStatus? status +local function parse_fh_data(state) + local cur = state.cur + + -- 'git log --name-status' doesn't work properly for merge commits. It + -- lists only an incomplete list of files at best. We need to use 'git + -- show' to get file statuses for merge commits. And merges do not always + -- have changes. + if cur.merge_hash and cur.numstat[1] and #cur.numstat ~= #cur.namestat then + local job + local job_spec = { + command = state.adapter:bin(), + args = utils.vec_join( + state.adapter:args(), + "show", + "--format=", + "--diff-merges=first-parent", + "--name-status", + (state.single_file and state.log_options.follow) and "--follow" or nil, + cur.right_hash, + "--", + state.old_path or state.path_args + ), + cwd = state.ctx.toplevel, + on_exit = function(j) + if j.code == 0 then + cur.namestat = j:result() + end + handle_co(state.thread, coroutine.resume(state.thread)) + end, + } + + local max_retries = 2 + local context = "git.utils.file_history_worker()" + state.resume_lock = true + + for i = 0, max_retries do + -- Git sometimes fails this job silently (exit code 0). Not sure why, + -- possibly because we are running multiple git opeartions on the same + -- repo concurrently. Retrying the job usually solves this. + job = Job:new(job_spec) + job:start() + coroutine.yield() + utils.handle_job(job, { fail_on_empty = true, context = context, log_func = logger.warn }) + + if #cur.namestat == 0 then + if i < max_retries then + logger.warn(("[%s] Retrying %d more time(s)."):format(context, max_retries - i)) + end + else + if i > 0 then + logger.info(("[%s] Retry successful!"):format(context)) + end + break + end + end + + state.resume_lock = false + + if job.code ~= 0 then + state.callback({}, JobStatus.ERROR, job:stderr_result()) + return false, JobStatus.FATAL + end + + if #cur.namestat == 0 then + -- Give up: something has been renamed. We can no longer track the + -- history. + logger.warn(("[%s] Giving up."):format(context)) + utils.warn("Displayed history may be incomplete. Check ':DiffviewLog' for details.", true) + return false + end + end + + local files = {} + for i = 1, #cur.numstat do + local status = cur.namestat[i]:sub(1, 1):gsub("%s", " ") + local name = cur.namestat[i]:match("[%a%s][^%s]*\t(.*)") + local oldname + + if name:match("\t") ~= nil then + oldname = name:match("(.*)\t") + name = name:gsub("^.*\t", "") + if state.single_file then + state.old_path = oldname + end + end + + local stats = { + additions = tonumber(cur.numstat[i]:match("^%d+")), + deletions = tonumber(cur.numstat[i]:match("^%d+%s+(%d+)")), + } + + if not stats.additions or not stats.deletions then + stats = nil + end + + table.insert(files, FileEntry.for_d2(state.opt.default_layout or Diff2Hor, { + adapter = state.adapter, + path = name, + oldpath = oldname, + status = status, + stats = stats, + kind = "working", + commit = state.commit, + rev_a = cur.left_hash and Rev(RevType.COMMIT, cur.left_hash) or Rev.new_null_tree(), + rev_b = state.prepared_log_opts.base or Rev(RevType.COMMIT, cur.right_hash), + })) + end + + if files[1] then + table.insert( + state.entries, + LogEntry({ + path_args = state.path_args, + commit = state.commit, + files = files, + single_file = state.single_file, + }) + ) + + state.callback(state.entries, JobStatus.PROGRESS) + end + + return true +end + + +---@class git.utils.FileHistoryWorkerSpec : git.utils.LayoutOpt + +---@param thread thread +---@param log_opt ConfigLogOptions +---@param opt git.utils.FileHistoryWorkerSpec +---@param co_state table +---@param callback function +function GitAdapter:file_history_worker(thread, log_opt, opt, co_state, callback) + ---@type LogEntry[] + local entries = {} + local data = {} + local data_idx = 1 + local last_status + local err_msg + + local single_file = self:is_single_file(self.context.toplevel, log_opt.single_file.path_args, log_opt.single_file.L) + + ---@type LogOptions + local log_options = config.get_log_options( + single_file, + single_file and log_opt.single_file or log_opt.multi_file + ) + + local is_trace = #log_options.L > 0 + + ---@type git.utils.FHState + local state = { + thread = thread, + adapter = self, + path_args = log_opt.single_file.path_args, + log_options = log_options, + prepared_log_opts = prepare_fh_options(self.context.toplevel, log_options, single_file), + opt = opt, + callback = callback, + entries = entries, + single_file = single_file, + resume_lock = false, + } + + local function data_callback(status, d, msg) + if status == JobStatus.PROGRESS then + data[#data+1] = d + end + + last_status = status + if msg then + err_msg = msg + end + if not state.resume_lock and coroutine.status(thread) == "suspended" then + self:handle_co(thread, coroutine.resume(thread)) + end + + if co_state.shutdown then + return true + end + end + + if is_trace then + incremental_line_trace_data(state, data_callback) + else + incremental_fh_data(state, data_callback) + end + + while true do + if not vim.tbl_contains({ JobStatus.SUCCESS, JobStatus.ERROR, JobStatus.KILLED }, last_status) + and not data[data_idx] then + coroutine.yield() + end + + if last_status == JobStatus.KILLED then + logger.warn("File history processing was killed.") + return + elseif last_status == JobStatus.ERROR then + callback(entries, JobStatus.ERROR, err_msg) + return + elseif last_status == JobStatus.SUCCESS and data_idx > #data then + break + end + + state.cur = data[data_idx] + + state.commit = Commit({ + hash = state.cur.right_hash, + author = state.cur.author, + time = tonumber(state.cur.time), + time_offset = state.cur.time_offset, + rel_date = state.cur.rel_date, + ref_names = state.cur.ref_names, + subject = state.cur.subject, + }) + + local ok, status + if log_options.L[1] then + ok, status = parse_fh_line_trace_data(state) + else + ok, status = parse_fh_data(state) + end + + if not ok then + if status == JobStatus.FATAL then + return + end + break + end + + data_idx = data_idx + 1 + end + + callback(entries, JobStatus.SUCCESS) +end + +---Strange trick to check if a file is binary using only git. +---@param path string +---@param rev Rev +---@return boolean -- True if the file was binary for the given rev, or it didn't exist. +function GitAdapter:is_binary(path, rev) + if rev.type == RevType.STAGE and rev.stage > 0 then + return false + end + + local cmd = { "-c", "submodule.recurse=false", "grep", "-I", "--name-only", "-e", "." } + if rev.type == RevType.LOCAL then + cmd[#cmd+1] = "--untracked" + elseif rev.type == RevType.STAGE then + cmd[#cmd+1] = "--cached" + else + cmd[#cmd+1] = rev.commit + end + + utils.vec_push(cmd, "--", path) + + local _, code = self:exec_sync(cmd, { cwd = self.context.toplevel, silent = true }) + return code ~= 0 +end + + M.GitAdapter = GitAdapter return M diff --git a/lua/diffview/vcs/file.lua b/lua/diffview/vcs/file.lua index 696cc0ab..263ce931 100644 --- a/lua/diffview/vcs/file.lua +++ b/lua/diffview/vcs/file.lua @@ -23,7 +23,7 @@ local M = {} ---@alias git.FileDataProducer fun(kind: git.FileKind, path: string, pos: "left"|"right"): string[] ---@class vcs.File : diffview.Object ----@field git_ctx GitContext +---@field adapter GitAdapter ---@filed path string ---@field absolute_path string ---@field parent_path string @@ -57,9 +57,9 @@ File.bufopts = { ---File constructor ---@param opt table function File:init(opt) - self.git_ctx = opt.git_ctx + self.adapter = opt.adapter self.path = opt.path - self.absolute_path = pl:absolute(opt.path, opt.git_ctx.toplevel) + self.absolute_path = pl:absolute(opt.path, opt.adapter.context.toplevel) self.parent_path = pl:parent(opt.path) or "" self.basename = pl:basename(opt.path) self.extension = pl:extension(opt.path) @@ -122,7 +122,7 @@ function File:create_buffer(callback) end if self.binary == nil and not config.get_config().diff_binaries then - self.binary = vcs.is_binary(self.git_ctx.toplevel, self.path, self.rev) + self.binary = self.adapter:is_binary(self.path, self.rev) end if self.nulled or self.binary then @@ -172,13 +172,13 @@ function File:create_buffer(callback) api.nvim_buf_set_option(self.bufnr, option, value) end - local fullname = pl:join("diffview://", self.git_ctx.dir, context, self.path) + local fullname = pl:join("diffview://", self.adapter.context.dir, context, self.path) local ok = pcall(api.nvim_buf_set_name, self.bufnr, fullname) if not ok then -- Resolve name conflict local i = 1 repeat - fullname = pl:join("diffview://", self.git_ctx.dir, context, i, self.path) + fullname = pl:join("diffview://", self.adapter.context.dir, context, i, self.path) ok = pcall(api.nvim_buf_set_name, self.bufnr, fullname) i = i + 1 until ok @@ -209,7 +209,7 @@ function File:create_buffer(callback) else vcs.show( - self.git_ctx.toplevel, + self.adapter.context.toplevel, { ("%s:%s"):format(self.rev:object_name() or "", self.path) }, function(err, result) if err then @@ -387,8 +387,10 @@ end ---@type vcs.File File.NULL_FILE = File({ - git_ctx = { - toplevel = "diffview://", + adapter = { + context = { + toplevel = "diffview://", + } }, path = "null", kind = "working", From 50305437611caf07f2eb5a005edffd86bcda9f39 Mon Sep 17 00:00:00 2001 From: zegervdv Date: Tue, 18 Oct 2022 07:32:23 +0200 Subject: [PATCH 17/71] fixup! refactor: create adapter base-class and implementations for git and hg --- lua/diffview/vcs/adapters/git/init.lua | 45 ++++++++++++++++++++++++++ lua/diffview/vcs/file.lua | 2 +- 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/lua/diffview/vcs/adapters/git/init.lua b/lua/diffview/vcs/adapters/git/init.lua index ee8ae8dc..f3913697 100644 --- a/lua/diffview/vcs/adapters/git/init.lua +++ b/lua/diffview/vcs/adapters/git/init.lua @@ -457,6 +457,51 @@ local incremental_line_trace_data = async.void(function(state, callback) end end) + +GitAdapter.show = async.wrap(function(toplevel, args, callback) + local job = Job:new({ + command = self:bin(), + args = utils.vec_join( + self:args(), + "show", + args + ), + cwd = toplevel, + ---@type Job + on_exit = async.void(function(j) + local context = "git.utils.show()" + utils.handle_job(j, { + fail_on_empty = true, + context = context, + debug_opt = { no_stdout = true, context = context }, + }) + + if j.code ~= 0 then + callback(j:stderr_result() or {}, nil) + return + end + + local out_status + + if #j:result() == 0 then + async.util.scheduler() + out_status = ensure_output(2, { j }, context) + end + + if out_status == JobStatus.ERROR then + callback(j:stderr_result() or {}, nil) + return + end + + callback(nil, j:result()) + end), + }) + -- Problem: Running multiple 'show' jobs simultaneously may cause them to fail + -- silently. + -- Solution: queue them and run them one after another. + queue_sync_job(job) +end, 3) + ---@param toplevel string ---@param path_args string[] ---@param lflags string[] diff --git a/lua/diffview/vcs/file.lua b/lua/diffview/vcs/file.lua index 263ce931..ebe586ee 100644 --- a/lua/diffview/vcs/file.lua +++ b/lua/diffview/vcs/file.lua @@ -208,7 +208,7 @@ function File:create_buffer(callback) end, nil) else - vcs.show( + self.adapter:show( self.adapter.context.toplevel, { ("%s:%s"):format(self.rev:object_name() or "", self.path) }, function(err, result) From 66851ac1511af80836555680cf4e1b51c36c9cf9 Mon Sep 17 00:00:00 2001 From: zegervdv Date: Tue, 18 Oct 2022 07:49:24 +0200 Subject: [PATCH 18/71] fixup! refactor: create adapter base-class and implementations for git and hg --- lua/diffview/vcs/adapters/git/init.lua | 44 ------------------------ lua/diffview/vcs/file.lua | 6 ++-- lua/diffview/vcs/utils.lua | 47 ++++++++++++++++++++++++++ 3 files changed, 50 insertions(+), 47 deletions(-) diff --git a/lua/diffview/vcs/adapters/git/init.lua b/lua/diffview/vcs/adapters/git/init.lua index f3913697..09708120 100644 --- a/lua/diffview/vcs/adapters/git/init.lua +++ b/lua/diffview/vcs/adapters/git/init.lua @@ -458,50 +458,6 @@ local incremental_line_trace_data = async.void(function(state, callback) end) -GitAdapter.show = async.wrap(function(toplevel, args, callback) - local job = Job:new({ - command = self:bin(), - args = utils.vec_join( - self:args(), - "show", - args - ), - cwd = toplevel, - ---@type Job - on_exit = async.void(function(j) - local context = "git.utils.show()" - utils.handle_job(j, { - fail_on_empty = true, - context = context, - debug_opt = { no_stdout = true, context = context }, - }) - - if j.code ~= 0 then - callback(j:stderr_result() or {}, nil) - return - end - - local out_status - - if #j:result() == 0 then - async.util.scheduler() - out_status = ensure_output(2, { j }, context) - end - - if out_status == JobStatus.ERROR then - callback(j:stderr_result() or {}, nil) - return - end - - callback(nil, j:result()) - end), - }) - -- Problem: Running multiple 'show' jobs simultaneously may cause them to fail - -- silently. - -- Solution: queue them and run them one after another. - queue_sync_job(job) -end, 3) - ---@param toplevel string ---@param path_args string[] ---@param lflags string[] diff --git a/lua/diffview/vcs/file.lua b/lua/diffview/vcs/file.lua index ebe586ee..855be158 100644 --- a/lua/diffview/vcs/file.lua +++ b/lua/diffview/vcs/file.lua @@ -10,7 +10,7 @@ local RevType = lazy.access("diffview.vcs.rev", "RevType") ---@module "diffview.config" local config = lazy.require("diffview.config") ---@module "diffview.vcs" -local vcs = lazy.require("diffview.vcs") +local vcs = lazy.require("diffview.vcs.utils") ---@module "diffview.utils" local utils = lazy.require("diffview.utils") @@ -208,8 +208,8 @@ function File:create_buffer(callback) end, nil) else - self.adapter:show( - self.adapter.context.toplevel, + vcs.show( + self.adapter, { ("%s:%s"):format(self.rev:object_name() or "", self.path) }, function(err, result) if err then diff --git a/lua/diffview/vcs/utils.lua b/lua/diffview/vcs/utils.lua index a6f05c19..a97210df 100644 --- a/lua/diffview/vcs/utils.lua +++ b/lua/diffview/vcs/utils.lua @@ -1,4 +1,6 @@ local utils = require("diffview.utils") +local async = require("plenary.async") +local Job = require("plenary.job") local M = {} @@ -11,5 +13,50 @@ local JobStatus = { FATAL = 5, } +M.show = async.wrap(function(adapter, args, callback) + local job = Job:new({ + command = adapter:bin(), + args = utils.vec_join( + adapter:args(), + "show", + args + ), + cwd = adapter.context.toplevel, + ---@type Job + on_exit = async.void(function(j) + local context = "git.utils.show()" + utils.handle_job(j, { + fail_on_empty = true, + context = context, + debug_opt = { no_stdout = true, context = context }, + }) + + if j.code ~= 0 then + callback(j:stderr_result() or {}, nil) + return + end + + local out_status + + if #j:result() == 0 then + async.util.scheduler() + out_status = ensure_output(2, { j }, context) + end + + if out_status == JobStatus.ERROR then + callback(j:stderr_result() or {}, nil) + return + end + + callback(nil, j:result()) + end), + }) + -- Problem: Running multiple 'show' jobs simultaneously may cause them to fail + -- silently. + -- Solution: queue them and run them one after another. + queue_sync_job(job) + +end, 3) + M.JobStatus = JobStatus return M From 1070789604e8e5779846abfeb104366193cce7ba Mon Sep 17 00:00:00 2001 From: zegervdv Date: Wed, 19 Oct 2022 07:28:34 +0200 Subject: [PATCH 19/71] fixup! refactor: create adapter base-class and implementations for git and hg --- lua/diffview/vcs/utils.lua | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/lua/diffview/vcs/utils.lua b/lua/diffview/vcs/utils.lua index a97210df..81493bb8 100644 --- a/lua/diffview/vcs/utils.lua +++ b/lua/diffview/vcs/utils.lua @@ -1,6 +1,7 @@ local utils = require("diffview.utils") local async = require("plenary.async") local Job = require("plenary.job") +local Semaphore = require('diffview.control').Semaphore local M = {} @@ -13,6 +14,40 @@ local JobStatus = { FATAL = 5, } +---@type Job[] +local sync_jobs = {} +---@type Semaphore +local job_queue_sem = Semaphore.new(1) + +---@param job Job +local resume_sync_queue = async.void(function(job) + local permit = job_queue_sem:acquire() + local idx = utils.vec_indexof(sync_jobs, job) + if idx > -1 then + table.remove(sync_jobs, idx) + end + permit:forget() + + if sync_jobs[1] and not sync_jobs[1].handle then + sync_jobs[1]:start() + end +end) + +---@param job Job +local queue_sync_job = async.void(function(job) + job:add_on_exit_callback(function() + resume_sync_queue(job) + end) + + local permit = job_queue_sem:acquire() + table.insert(sync_jobs, job) + permit:forget() + + if #sync_jobs == 1 then + job:start() + end +end) + M.show = async.wrap(function(adapter, args, callback) local job = Job:new({ command = adapter:bin(), From a6c872505a73c4302cc43d8427c671a305e1c7cb Mon Sep 17 00:00:00 2001 From: zegervdv Date: Wed, 19 Oct 2022 07:31:17 +0200 Subject: [PATCH 20/71] fixup! refactor: create adapter base-class and implementations for git and hg --- lua/diffview/vcs/adapters/git/init.lua | 4 ++++ lua/diffview/vcs/utils.lua | 8 ++------ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lua/diffview/vcs/adapters/git/init.lua b/lua/diffview/vcs/adapters/git/init.lua index 09708120..3f243747 100644 --- a/lua/diffview/vcs/adapters/git/init.lua +++ b/lua/diffview/vcs/adapters/git/init.lua @@ -90,6 +90,10 @@ function GitAdapter:get_command() return config.get_config().git_cmd end +function GitAdapter:get_show_args(args) + return utils.vec_join(self:args(), "show", args) +end + function GitAdapter:get_context(path) local context = {} local out, code = self:exec_sync({ "rev-parse", "--path-format=absolute", "--show-toplevel" }, path) diff --git a/lua/diffview/vcs/utils.lua b/lua/diffview/vcs/utils.lua index 81493bb8..aad05714 100644 --- a/lua/diffview/vcs/utils.lua +++ b/lua/diffview/vcs/utils.lua @@ -51,15 +51,11 @@ end) M.show = async.wrap(function(adapter, args, callback) local job = Job:new({ command = adapter:bin(), - args = utils.vec_join( - adapter:args(), - "show", - args - ), + args = adapter:get_show_args(args), cwd = adapter.context.toplevel, ---@type Job on_exit = async.void(function(j) - local context = "git.utils.show()" + local context = "vcs.utils.show()" utils.handle_job(j, { fail_on_empty = true, context = context, From 3552f9f338e5a1b0f38b58e1891fd292b62f7dce Mon Sep 17 00:00:00 2001 From: zegervdv Date: Wed, 19 Oct 2022 07:34:06 +0200 Subject: [PATCH 21/71] fixup! refactor: create adapter base-class and implementations for git and hg --- lua/diffview/vcs/adapter.lua | 6 ++++++ lua/diffview/vcs/adapters/git/init.lua | 1 + 2 files changed, 7 insertions(+) diff --git a/lua/diffview/vcs/adapter.lua b/lua/diffview/vcs/adapter.lua index 172edeeb..d10a2874 100644 --- a/lua/diffview/vcs/adapter.lua +++ b/lua/diffview/vcs/adapter.lua @@ -23,6 +23,12 @@ function VCSAdapter:get_command() oop.abstract_stub() end +---@param args string[] +---@return string[] args to show commit content +function VCSAdapter:get_show_args(args) + oop.abstract_stub() +end + function VCSAdapter:get_context(path) return {} end diff --git a/lua/diffview/vcs/adapters/git/init.lua b/lua/diffview/vcs/adapters/git/init.lua index 3f243747..05e9d9cf 100644 --- a/lua/diffview/vcs/adapters/git/init.lua +++ b/lua/diffview/vcs/adapters/git/init.lua @@ -90,6 +90,7 @@ function GitAdapter:get_command() return config.get_config().git_cmd end +---@param args table function GitAdapter:get_show_args(args) return utils.vec_join(self:args(), "show", args) end From 4760ccc2f1d0042bf9dd56a3e61d8906c64ddb85 Mon Sep 17 00:00:00 2001 From: zegervdv Date: Wed, 19 Oct 2022 08:09:11 +0200 Subject: [PATCH 22/71] fixup! refactor: create adapter base-class and implementations for git and hg --- lua/diffview/init.lua | 1 + lua/diffview/lib.lua | 35 +++++++- lua/diffview/vcs/adapters/git/init.lua | 118 ++++++++++++++----------- lua/diffview/vcs/adapters/hg/init.lua | 5 ++ lua/diffview/vcs/init.lua | 22 ++++- 5 files changed, 124 insertions(+), 57 deletions(-) diff --git a/lua/diffview/init.lua b/lua/diffview/init.lua index 3ed6e4b1..e16dad97 100644 --- a/lua/diffview/init.lua +++ b/lua/diffview/init.lua @@ -167,6 +167,7 @@ end ---@param range? { [1]: integer, [2]: integer } function M.file_history(range, ...) + local adapter = vcs.get_adapter() local view = lib.file_history(range, utils.tbl_pack(...)) if view then view:open() diff --git a/lua/diffview/lib.lua b/lua/diffview/lib.lua index 788f885f..2bd23f7f 100644 --- a/lua/diffview/lib.lua +++ b/lua/diffview/lib.lua @@ -131,18 +131,49 @@ end ---@param range? { [1]: integer, [2]: integer } ---@param args string[] -function M.file_history(adapter, range, args) +function M.file_history(range, args) -- TODO: Arg handling is git-specific too? local default_args = config.get_config().default_args.DiffviewFileHistory local argo = arg_parser.parse(vim.tbl_flatten({ default_args, args })) local paths = {} - local rel_paths logger.info("[command call] :DiffviewFileHistory " .. table.concat(vim.tbl_flatten({ default_args, args, }), " ")) + for _, path_arg in ipairs(argo.args) do + for _, path in ipairs(pl:vim_expand(path_arg, false, true)) do + local magic, pattern = pathspec_split(path) + pattern = pl:readlink(pattern) or pattern + table.insert(paths, magic .. pattern) + end + end + + ---@type string + local cpath = argo:get_flag("C", { no_empty = true, expand = true }) + local cfile = pl:vim_expand("%") + cfile = pl:readlink(cfile) or cfile + + local top_indicators = {} + for _, path in ipairs(paths) do + if pathspec_split(path) == "" then + table.insert(top_indicators, pl:absolute(path, cpath)) + break + end + end + + table.insert(top_indicators, cpath and pl:realpath(cpath) or ( + vim.bo.buftype == "" + and pl:absolute(cfile) + or nil + )) + + if not cpath then + table.insert(top_indicators, pl:realpath(".")) + end + + local adapter = vcs.get_adapter(top_indicators) local log_options = adapter:file_history_options(range, args) if log_options == nil then diff --git a/lua/diffview/vcs/adapters/git/init.lua b/lua/diffview/vcs/adapters/git/init.lua index 05e9d9cf..368082e2 100644 --- a/lua/diffview/vcs/adapters/git/init.lua +++ b/lua/diffview/vcs/adapters/git/init.lua @@ -23,6 +23,72 @@ local M = {} local GitAdapter = oop.create_class('GitAdapter', VCSAdapter) +---@return string, string +local function pathspec_split(pathspec) + local magic = pathspec:match("^:[/!^]*:?") or pathspec:match("^:%b()") or "" + local pattern = pathspec:sub(1 + #magic, -1) + return magic or "", pattern or "" +end + +local function pathspec_expand(toplevel, cwd, pathspec) + local magic, pattern = pathspec_split(pathspec) + if not utils.path:is_abs(pattern) then + pattern = utils.path:join(utils.path:relative(cwd, toplevel), pattern) + end + return magic .. utils.path:convert(pattern) +end + +local function pathspec_modify(pathspec, mods) + local magic, pattern = pathspec_split(pathspec) + return magic .. utils.path:vim_fnamemodify(pattern, mods) +end + +function M.get_repo_paths(args) + local default_args = config.get_config().default_args.DiffviewFileHistory + local argo = arg_parser.parse(vim.tbl_flatten({ default_args, args })) + local paths = {} + + for _, path_arg in ipairs(argo.args) do + for _, path in ipairs(pl:vim_expand(path_arg, false, true)) do + local magic, pattern = pathspec_split(path) + pattern = pl:readlink(pattern) or pattern + table.insert(paths, magic .. pattern) + end + end + + ---@type string + local cpath = argo:get_flag("C", { no_empty = true, expand = true }) + local cfile = pl:vim_expand("%") + cfile = pl:readlink(cfile) or cfile + + local top_indicators = {} + for _, path in ipairs(paths) do + if pathspec_split(path) == "" then + table.insert(top_indicators, pl:absolute(path, cpath)) + break + end + end + + table.insert(top_indicators, cpath and pl:realpath(cpath) or ( + vim.bo.buftype == "" + and pl:absolute(cfile) + or nil + )) + + if not cpath then + table.insert(top_indicators, pl:realpath(".")) + end + + local out, code = utils.system_list(vim.tbl_flatten({ config.get_config().git_cmd, "-C", cpath, "rev-parse" })) + + -- Not in a Git repo + if code ~= 0 then + return false, {} + end + + return true, top_indicators +end + function GitAdapter:init(path) self.super:init(path) @@ -110,27 +176,6 @@ function GitAdapter:get_context(path) context.dir = out[1] and vim.trim(out[1]) return context end - ----@return string, string -local function pathspec_split(pathspec) - local magic = pathspec:match("^:[/!^]*:?") or pathspec:match("^:%b()") or "" - local pattern = pathspec:sub(1 + #magic, -1) - return magic or "", pattern or "" -end - -local function pathspec_expand(toplevel, cwd, pathspec) - local magic, pattern = pathspec_split(pathspec) - if not utils.path:is_abs(pattern) then - pattern = utils.path:join(utils.path:relative(cwd, toplevel), pattern) - end - return magic .. utils.path:convert(pattern) -end - -local function pathspec_modify(pathspec, mods) - local magic, pattern = pathspec_split(pathspec) - return magic .. utils.path:vim_fnamemodify(pattern, mods) -end - function GitAdapter:find_git_toplevel(top_indicators) local toplevel for _, p in ipairs(top_indicators) do @@ -550,37 +595,6 @@ function GitAdapter:file_history_options(range, args) args, }), " ")) - for _, path_arg in ipairs(argo.args) do - for _, path in ipairs(pl:vim_expand(path_arg, false, true)) do - local magic, pattern = pathspec_split(path) - pattern = pl:readlink(pattern) or pattern - table.insert(paths, magic .. pattern) - end - end - - ---@type string - local cpath = argo:get_flag("C", { no_empty = true, expand = true }) - local cfile = pl:vim_expand("%") - cfile = pl:readlink(cfile) or cfile - - local top_indicators = {} - for _, path in ipairs(paths) do - if pathspec_split(path) == "" then - table.insert(top_indicators, pl:absolute(path, cpath)) - break - end - end - - table.insert(top_indicators, cpath and pl:realpath(cpath) or ( - vim.bo.buftype == "" - and pl:absolute(cfile) - or nil - )) - - if not cpath then - table.insert(top_indicators, pl:realpath(".")) - end - local err, git_toplevel = self:find_git_toplevel(top_indicators) if err then diff --git a/lua/diffview/vcs/adapters/hg/init.lua b/lua/diffview/vcs/adapters/hg/init.lua index 29a579d6..a2c56e0f 100644 --- a/lua/diffview/vcs/adapters/hg/init.lua +++ b/lua/diffview/vcs/adapters/hg/init.lua @@ -5,5 +5,10 @@ local M = {} local HgAdapter = oop.create_class('HgAdapter', VCSAdapter) +function M.get_repo_paths(args) + -- TODO: implement + return false, {} +end + M.HgAdapter = HgAdapter return M diff --git a/lua/diffview/vcs/init.lua b/lua/diffview/vcs/init.lua index 72438cc0..5b2faafc 100644 --- a/lua/diffview/vcs/init.lua +++ b/lua/diffview/vcs/init.lua @@ -1,9 +1,25 @@ -local git = require('diffview.vcs.adapters.git').GitAdapter +local utils = require('diffview.utils') +local git = require('diffview.vcs.adapters.git') +local hg = require('diffview.vcs.adapters.hg') local M = {} -function M.get_adapter(path) - return git(path) +-- Try to extract paths from arguments to determine VCS type +function M.get_adapter(args) + local ok = false + local paths + + ok, paths = git.get_repo_paths(args) + if ok then + return git.GitAdapter(paths) + end + + ok, paths = hg.get_repo_paths(args) + if ok then + return hg.HgAdapter(paths) + end + + utils.err("No valid VCS found for current workspace") end return M From b30586ef8efc4472991ed4e9bf5570afd9561e4f Mon Sep 17 00:00:00 2001 From: zegervdv Date: Wed, 19 Oct 2022 08:45:44 +0200 Subject: [PATCH 23/71] fixup! refactor: create adapter base-class and implementations for git and hg --- lua/diffview/init.lua | 1 - lua/diffview/lib.lua | 39 +-------- lua/diffview/vcs/adapter.lua | 3 +- lua/diffview/vcs/adapters/git/init.lua | 109 +++++++++++++------------ lua/diffview/vcs/init.lua | 13 +-- 5 files changed, 66 insertions(+), 99 deletions(-) diff --git a/lua/diffview/init.lua b/lua/diffview/init.lua index e16dad97..3ed6e4b1 100644 --- a/lua/diffview/init.lua +++ b/lua/diffview/init.lua @@ -167,7 +167,6 @@ end ---@param range? { [1]: integer, [2]: integer } function M.file_history(range, ...) - local adapter = vcs.get_adapter() local view = lib.file_history(range, utils.tbl_pack(...)) if view then view:open() diff --git a/lua/diffview/lib.lua b/lua/diffview/lib.lua index 2bd23f7f..63042eca 100644 --- a/lua/diffview/lib.lua +++ b/lua/diffview/lib.lua @@ -132,49 +132,16 @@ end ---@param range? { [1]: integer, [2]: integer } ---@param args string[] function M.file_history(range, args) - -- TODO: Arg handling is git-specific too? local default_args = config.get_config().default_args.DiffviewFileHistory - local argo = arg_parser.parse(vim.tbl_flatten({ default_args, args })) - local paths = {} logger.info("[command call] :DiffviewFileHistory " .. table.concat(vim.tbl_flatten({ default_args, args, }), " ")) - for _, path_arg in ipairs(argo.args) do - for _, path in ipairs(pl:vim_expand(path_arg, false, true)) do - local magic, pattern = pathspec_split(path) - pattern = pl:readlink(pattern) or pattern - table.insert(paths, magic .. pattern) - end - end - - ---@type string - local cpath = argo:get_flag("C", { no_empty = true, expand = true }) - local cfile = pl:vim_expand("%") - cfile = pl:readlink(cfile) or cfile - - local top_indicators = {} - for _, path in ipairs(paths) do - if pathspec_split(path) == "" then - table.insert(top_indicators, pl:absolute(path, cpath)) - break - end - end - - table.insert(top_indicators, cpath and pl:realpath(cpath) or ( - vim.bo.buftype == "" - and pl:absolute(cfile) - or nil - )) - - if not cpath then - table.insert(top_indicators, pl:realpath(".")) - end - - local adapter = vcs.get_adapter(top_indicators) - local log_options = adapter:file_history_options(range, args) + local adapter, paths = vcs.get_adapter(args) + print('received: ', vim.inspect(paths)) + local log_options = adapter:file_history_options(range, paths, args) if log_options == nil then utils.err('Failed to create log options for file_history') diff --git a/lua/diffview/vcs/adapter.lua b/lua/diffview/vcs/adapter.lua index d10a2874..3a47c53d 100644 --- a/lua/diffview/vcs/adapter.lua +++ b/lua/diffview/vcs/adapter.lua @@ -7,12 +7,11 @@ local M = {} local VCSAdapter = oop.create_class('VCSAdapter') function VCSAdapter:init(path) - self.path = path self.bootstrap = { done = false, ok = false, } - self.context = self:get_context(path) + self.context = {} end function VCSAdapter:run_bootstrap() diff --git a/lua/diffview/vcs/adapters/git/init.lua b/lua/diffview/vcs/adapters/git/init.lua index 368082e2..8299844d 100644 --- a/lua/diffview/vcs/adapters/git/init.lua +++ b/lua/diffview/vcs/adapters/git/init.lua @@ -75,22 +75,61 @@ function M.get_repo_paths(args) or nil )) + local test_args = {} if not cpath then table.insert(top_indicators, pl:realpath(".")) + else + table.insert(test_args, '-C') + table.insert(test_args, cpath) end - local out, code = utils.system_list(vim.tbl_flatten({ config.get_config().git_cmd, "-C", cpath, "rev-parse" })) + + local out, code = utils.system_list(vim.tbl_flatten({ config.get_config().git_cmd, test_args, "rev-parse" })) -- Not in a Git repo if code ~= 0 then - return false, {} + return nil end - return true, top_indicators + return top_indicators end -function GitAdapter:init(path) - self.super:init(path) +local function get_toplevel(path) + local out, code = utils.system_list(vim.tbl_flatten({config.get_config().git_cmd, {"rev-parse", "--path-format=absolute", "--show-toplevel"}, path})) + if code ~= 0 then + return nil + end + return out[1] and vim.trim(out[1]) +end + +local function find_git_toplevel(top_indicators) + local toplevel + for _, p in ipairs(top_indicators) do + if not pl:is_dir(p) then + p = pl:parent(p) + end + + if p and pl:readable(p) then + toplevel = get_toplevel(p) + + if toplevel then + return nil, toplevel + end + end + end + + return ( + ("Path not a git repo (or any parent): %s") + :format(table.concat(vim.tbl_map(function(v) + local rel_path = pl:relative(v, ".") + return utils.str_quote(rel_path == "" and "." or rel_path) + end, top_indicators) --[[@as vector ]], ", ")) + ) + +end + +function GitAdapter:init(paths) + self.super:init(paths) self.bootstrap.version_string = nil self.bootstrap.version = {} @@ -101,7 +140,8 @@ function GitAdapter:init(path) patch = 0, } - self.context = self:get_context(path) + self.context.toplevel = find_git_toplevel(paths) + self.context.dir = self:get_dir(self.context.toplevel) end function GitAdapter:run_bootstrap() @@ -161,47 +201,14 @@ function GitAdapter:get_show_args(args) return utils.vec_join(self:args(), "show", args) end -function GitAdapter:get_context(path) - local context = {} - local out, code = self:exec_sync({ "rev-parse", "--path-format=absolute", "--show-toplevel" }, path) +function GitAdapter:get_dir(path) + local out, code = self:exec_sync({ "rev-parse", "--path-format=absolute", "--git-dir" }, path) if code ~= 0 then return nil end - context.toplevel = out[1] and vim.trim(out[1]) - - out, code = self:exec_sync({ "rev-parse", "--path-format=absolute", "--git-dir" }, path) - if code ~= 0 then - return nil - end - context.dir = out[1] and vim.trim(out[1]) - return context + return out[1] and vim.trim(out[1]) end -function GitAdapter:find_git_toplevel(top_indicators) - local toplevel - for _, p in ipairs(top_indicators) do - if not pl:is_dir(p) then - p = pl:parent(p) - end - - if p and pl:readable(p) then - local ctxt = self:get_context(p) - toplevel = ctxt.toplevel - - if toplevel then - return nil, toplevel - end - end - end - return ( - ("Path not a git repo (or any parent): %s") - :format(table.concat(vim.tbl_map(function(v) - local rel_path = pl:relative(v, ".") - return utils.str_quote(rel_path == "" and "." or rel_path) - end, top_indicators) --[[@as vector ]], ", ")) - ) - -end ---@class git.utils.PreparedLogOpts ---@field rev_range string @@ -584,23 +591,17 @@ end ---@param range? { [1]: integer, [2]: integer } ---@param args string[] -function GitAdapter:file_history_options(range, args) +function GitAdapter:file_history_options(range, paths, args) local default_args = config.get_config().default_args.DiffviewFileHistory local argo = arg_parser.parse(vim.tbl_flatten({ default_args, args })) - local paths = {} local rel_paths - logger.info("[command call] :DiffviewFileHistory " .. table.concat(vim.tbl_flatten({ - default_args, - args, - }), " ")) - - local err, git_toplevel = self:find_git_toplevel(top_indicators) + local cpath = argo:get_flag("C", { no_empty = true, expand = true }) + local cfile = pl:vim_expand("%") + cfile = pl:readlink(cfile) or cfile - if err then - utils.err(err) - return - end + print(vim.inspect(paths)) + local git_toplevel = self.context.toplevel ---@cast git_toplevel string logger.lvl(1).s_debug(("Found git top-level: %s"):format(utils.str_quote(git_toplevel))) diff --git a/lua/diffview/vcs/init.lua b/lua/diffview/vcs/init.lua index 5b2faafc..5f98b695 100644 --- a/lua/diffview/vcs/init.lua +++ b/lua/diffview/vcs/init.lua @@ -9,14 +9,15 @@ function M.get_adapter(args) local ok = false local paths - ok, paths = git.get_repo_paths(args) - if ok then - return git.GitAdapter(paths) + paths = git.get_repo_paths(args) + if paths then + print('toplevel: ', vim.inspect(paths)) + return git.GitAdapter(paths), paths end - ok, paths = hg.get_repo_paths(args) - if ok then - return hg.HgAdapter(paths) + paths = hg.get_repo_paths(args) + if paths then + return hg.HgAdapter(paths), paths end utils.err("No valid VCS found for current workspace") From 00148d860771693c5eee4066dd5d15dcb07d235b Mon Sep 17 00:00:00 2001 From: zegervdv Date: Wed, 19 Oct 2022 08:50:44 +0200 Subject: [PATCH 24/71] fixup! refactor: create adapter base-class and implementations for git and hg --- lua/diffview/vcs/adapters/git/init.lua | 2 +- lua/diffview/vcs/init.lua | 11 +++++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/lua/diffview/vcs/adapters/git/init.lua b/lua/diffview/vcs/adapters/git/init.lua index 8299844d..fabc64f0 100644 --- a/lua/diffview/vcs/adapters/git/init.lua +++ b/lua/diffview/vcs/adapters/git/init.lua @@ -91,7 +91,7 @@ function M.get_repo_paths(args) return nil end - return top_indicators + return paths, top_indicators end local function get_toplevel(path) diff --git a/lua/diffview/vcs/init.lua b/lua/diffview/vcs/init.lua index 5f98b695..0785b8f4 100644 --- a/lua/diffview/vcs/init.lua +++ b/lua/diffview/vcs/init.lua @@ -6,18 +6,17 @@ local M = {} -- Try to extract paths from arguments to determine VCS type function M.get_adapter(args) - local ok = false local paths - paths = git.get_repo_paths(args) + paths, toplevel_indicators = git.get_repo_paths(args) if paths then - print('toplevel: ', vim.inspect(paths)) - return git.GitAdapter(paths), paths + print('toplevel: ', vim.inspect(toplevel_indicators)) + return git.GitAdapter(toplevel_indicators), paths end - paths = hg.get_repo_paths(args) + paths, toplevel_indicators = hg.get_repo_paths(args) if paths then - return hg.HgAdapter(paths), paths + return hg.HgAdapter(toplevel_indicators), paths end utils.err("No valid VCS found for current workspace") From 0049d380967d9e98a56183d44a7bdb3d059706bb Mon Sep 17 00:00:00 2001 From: zegervdv Date: Wed, 19 Oct 2022 08:55:53 +0200 Subject: [PATCH 25/71] fixup! refactor: create adapter base-class and implementations for git and hg --- lua/diffview/vcs/adapters/git/init.lua | 4 +++- lua/diffview/vcs/init.lua | 1 - 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lua/diffview/vcs/adapters/git/init.lua b/lua/diffview/vcs/adapters/git/init.lua index fabc64f0..74e755df 100644 --- a/lua/diffview/vcs/adapters/git/init.lua +++ b/lua/diffview/vcs/adapters/git/init.lua @@ -140,7 +140,8 @@ function GitAdapter:init(paths) patch = 0, } - self.context.toplevel = find_git_toplevel(paths) + local err, toplevel = find_git_toplevel(paths) + self.context.toplevel = toplevel self.context.dir = self:get_dir(self.context.toplevel) end @@ -603,6 +604,7 @@ function GitAdapter:file_history_options(range, paths, args) print(vim.inspect(paths)) local git_toplevel = self.context.toplevel + print('git_toplevel = ', vim.inspect(git_toplevel)) ---@cast git_toplevel string logger.lvl(1).s_debug(("Found git top-level: %s"):format(utils.str_quote(git_toplevel))) diff --git a/lua/diffview/vcs/init.lua b/lua/diffview/vcs/init.lua index 0785b8f4..f34548fa 100644 --- a/lua/diffview/vcs/init.lua +++ b/lua/diffview/vcs/init.lua @@ -10,7 +10,6 @@ function M.get_adapter(args) paths, toplevel_indicators = git.get_repo_paths(args) if paths then - print('toplevel: ', vim.inspect(toplevel_indicators)) return git.GitAdapter(toplevel_indicators), paths end From 0ddfde9185ba004d19f87272d7988608cc6aa0c5 Mon Sep 17 00:00:00 2001 From: zegervdv Date: Wed, 19 Oct 2022 17:19:19 +0200 Subject: [PATCH 26/71] fixup! refactor: create adapter base-class and implementations for git and hg --- lua/diffview/vcs/adapters/hg/init.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua/diffview/vcs/adapters/hg/init.lua b/lua/diffview/vcs/adapters/hg/init.lua index a2c56e0f..9b34874f 100644 --- a/lua/diffview/vcs/adapters/hg/init.lua +++ b/lua/diffview/vcs/adapters/hg/init.lua @@ -7,7 +7,7 @@ local HgAdapter = oop.create_class('HgAdapter', VCSAdapter) function M.get_repo_paths(args) -- TODO: implement - return false, {} + return false end M.HgAdapter = HgAdapter From 855da1f90dd6fafa387d3ddca601210015bc850a Mon Sep 17 00:00:00 2001 From: zegervdv Date: Fri, 21 Oct 2022 04:15:25 +0000 Subject: [PATCH 27/71] fixup! refactor: create adapter base-class and implementations for git and hg --- lua/diffview/lib.lua | 1 - lua/diffview/vcs/adapters/git/init.lua | 2 -- 2 files changed, 3 deletions(-) diff --git a/lua/diffview/lib.lua b/lua/diffview/lib.lua index 63042eca..5a79649b 100644 --- a/lua/diffview/lib.lua +++ b/lua/diffview/lib.lua @@ -140,7 +140,6 @@ function M.file_history(range, args) }), " ")) local adapter, paths = vcs.get_adapter(args) - print('received: ', vim.inspect(paths)) local log_options = adapter:file_history_options(range, paths, args) if log_options == nil then diff --git a/lua/diffview/vcs/adapters/git/init.lua b/lua/diffview/vcs/adapters/git/init.lua index 74e755df..ce846bb4 100644 --- a/lua/diffview/vcs/adapters/git/init.lua +++ b/lua/diffview/vcs/adapters/git/init.lua @@ -601,10 +601,8 @@ function GitAdapter:file_history_options(range, paths, args) local cfile = pl:vim_expand("%") cfile = pl:readlink(cfile) or cfile - print(vim.inspect(paths)) local git_toplevel = self.context.toplevel - print('git_toplevel = ', vim.inspect(git_toplevel)) ---@cast git_toplevel string logger.lvl(1).s_debug(("Found git top-level: %s"):format(utils.str_quote(git_toplevel))) From 9ab91f1cbaa8172e9bb9fe3960f2920ef9b050bd Mon Sep 17 00:00:00 2001 From: zegervdv Date: Fri, 21 Oct 2022 04:26:06 +0000 Subject: [PATCH 28/71] fixup! refactor: create adapter base-class and implementations for git and hg --- lua/diffview/lib.lua | 30 -------------------------- lua/diffview/vcs/adapter.lua | 3 +++ lua/diffview/vcs/adapters/git/init.lua | 11 ++++++++-- lua/diffview/vcs/file.lua | 2 +- 4 files changed, 13 insertions(+), 33 deletions(-) diff --git a/lua/diffview/lib.lua b/lua/diffview/lib.lua index 5a79649b..60e8d70e 100644 --- a/lua/diffview/lib.lua +++ b/lua/diffview/lib.lua @@ -162,36 +162,6 @@ function M.file_history(range, args) return v end ----Try to find the top-level of a working tree by using the given indicative ----paths. ----@param top_indicators string[] A list of paths that might indicate what working tree we are in. ----@return string? err ----@return string? toplevel # The absolute path to the git top-level. -function M.find_git_toplevel(top_indicators) - local toplevel - for _, p in ipairs(top_indicators) do - if not pl:is_dir(p) then - p = pl:parent(p) - end - - if p and pl:readable(p) then - toplevel = vcs.toplevel(p) - - if toplevel then - return nil, toplevel - end - end - end - - return ( - ("Path not a git repo (or any parent): %s") - :format(table.concat(vim.tbl_map(function(v) - local rel_path = pl:relative(v, ".") - return utils.str_quote(rel_path == "" and "." or rel_path) - end, top_indicators) --[[@as vector ]], ", ")) - ) -end - ---Parse a given rev arg. ---@param git_toplevel string ---@param rev_arg string diff --git a/lua/diffview/vcs/adapter.lua b/lua/diffview/vcs/adapter.lua index 3a47c53d..fc91f595 100644 --- a/lua/diffview/vcs/adapter.lua +++ b/lua/diffview/vcs/adapter.lua @@ -4,6 +4,9 @@ local logger = require('diffview.logger') local M = {} +---@class VCSAdapter: diffview.Object +---@field bootstrap boolean[] +---@field context string[] local VCSAdapter = oop.create_class('VCSAdapter') function VCSAdapter:init(path) diff --git a/lua/diffview/vcs/adapters/git/init.lua b/lua/diffview/vcs/adapters/git/init.lua index ce846bb4..ec602888 100644 --- a/lua/diffview/vcs/adapters/git/init.lua +++ b/lua/diffview/vcs/adapters/git/init.lua @@ -11,6 +11,7 @@ local Diff2Hor = require("diffview.scene.layouts.diff_2_hor").Diff2Hor local LogEntry = require("diffview.vcs.log_entry").LogEntry local RevType = require("diffview.vcs.rev").RevType local Rev = require("diffview.vcs.rev").Rev +---@class VCSAdapter local VCSAdapter = require('diffview.vcs.adapter').VCSAdapter local Job = require("plenary.job") local JobStatus = require('diffview.vcs.utils').JobStatus @@ -21,6 +22,7 @@ local pl = lazy.access(utils, "path") local M = {} +---@class GitAdapter : VCSAdapter local GitAdapter = oop.create_class('GitAdapter', VCSAdapter) ---@return string, string @@ -88,7 +90,7 @@ function M.get_repo_paths(args) -- Not in a Git repo if code ~= 0 then - return nil + return nil, nil end return paths, top_indicators @@ -102,6 +104,11 @@ local function get_toplevel(path) return out[1] and vim.trim(out[1]) end +---Try to find the top-level of a working tree by using the given indicative +---paths. +---@param top_indicators string[] A list of paths that might indicate what working tree we are in. +---@return string? err +---@return string? toplevel # as an absolute path local function find_git_toplevel(top_indicators) local toplevel for _, p in ipairs(top_indicators) do @@ -125,7 +132,6 @@ local function find_git_toplevel(top_indicators) return utils.str_quote(rel_path == "" and "." or rel_path) end, top_indicators) --[[@as vector ]], ", ")) ) - end function GitAdapter:init(paths) @@ -140,6 +146,7 @@ function GitAdapter:init(paths) patch = 0, } + -- TODO: Handler error here local err, toplevel = find_git_toplevel(paths) self.context.toplevel = toplevel self.context.dir = self:get_dir(self.context.toplevel) diff --git a/lua/diffview/vcs/file.lua b/lua/diffview/vcs/file.lua index 855be158..15e39f66 100644 --- a/lua/diffview/vcs/file.lua +++ b/lua/diffview/vcs/file.lua @@ -24,7 +24,7 @@ local M = {} ---@class vcs.File : diffview.Object ---@field adapter GitAdapter ----@filed path string +---@field path string ---@field absolute_path string ---@field parent_path string ---@field basename string From 0c09f75b1b14b9b8fbbbd4a1dfdfa4beb673a50f Mon Sep 17 00:00:00 2001 From: zegervdv Date: Fri, 21 Oct 2022 04:45:18 +0000 Subject: [PATCH 29/71] fixup! refactor: create adapter base-class and implementations for git and hg --- lua/diffview/vcs/adapter.lua | 21 +- lua/diffview/vcs/adapters/git/init.lua | 3 +- lua/diffview/vcs/adapters/git/utils.lua | 384 ------------------------ lua/diffview/vcs/utils.lua | 57 ++++ 4 files changed, 78 insertions(+), 387 deletions(-) diff --git a/lua/diffview/vcs/adapter.lua b/lua/diffview/vcs/adapter.lua index fc91f595..0f64770b 100644 --- a/lua/diffview/vcs/adapter.lua +++ b/lua/diffview/vcs/adapter.lua @@ -4,6 +4,10 @@ local logger = require('diffview.logger') local M = {} +---@class vcs.adapter.LayoutOpt +---@field default_layout Diff2 +---@field merge_layout Layout + ---@class VCSAdapter: diffview.Object ---@field bootstrap boolean[] ---@field context string[] @@ -45,6 +49,14 @@ function VCSAdapter:args() return utils.vec_slice(self:get_command(), 2) end +---Execute a VCS command synchronously. +---@param args string[] +---@param cwd_or_opt? string|utils.system_list.Opt +---@return string[] stdout +---@return integer code +---@return string[] stderr +---@overload fun(args: string[], cwd: string?) +---@overload fun(args: string[], opt: utils.system_list.Opt?) function VCSAdapter:exec_sync(args, cwd_or_opt) if not self.bootstrap.done then self:run_bootstrap() @@ -60,13 +72,20 @@ function VCSAdapter:file_history_options(range, args) oop.abstract_stub() end +---@class vcs.adapter.FileHistoryWorkerSpec : git.utils.LayoutOpt + +---@param thread thread +---@param log_opt ConfigLogOptions +---@param opt vcs.adapter.FileHistoryWorkerSpec +---@param co_state table +---@param callback function function VCSAdapter:file_history_worker(thread, log_opt, opt, co_state, callback) oop.abstract_stub() end ---@param log_opt ConfigLogOptions ----@param opt git.utils.FileHistoryWorkerSpec +---@param opt vcs.adapter.FileHistoryWorkerSpec ---@param callback function ---@return fun() finalizer function VCSAdapter:file_history(log_opt, opt, callback) diff --git a/lua/diffview/vcs/adapters/git/init.lua b/lua/diffview/vcs/adapters/git/init.lua index ec602888..73e0a3c3 100644 --- a/lua/diffview/vcs/adapters/git/init.lua +++ b/lua/diffview/vcs/adapters/git/init.lua @@ -842,11 +842,10 @@ local function parse_fh_data(state) end ----@class git.utils.FileHistoryWorkerSpec : git.utils.LayoutOpt ---@param thread thread ---@param log_opt ConfigLogOptions ----@param opt git.utils.FileHistoryWorkerSpec +---@param opt vcs.adapter.FileHistoryWorkerSpec ---@param co_state table ---@param callback function function GitAdapter:file_history_worker(thread, log_opt, opt, co_state, callback) diff --git a/lua/diffview/vcs/adapters/git/utils.lua b/lua/diffview/vcs/adapters/git/utils.lua index e9731c49..9d837933 100644 --- a/lua/diffview/vcs/adapters/git/utils.lua +++ b/lua/diffview/vcs/adapters/git/utils.lua @@ -22,206 +22,6 @@ local M = {} ---@field toplevel string Path to the top-level directory of the working tree. ---@field dir string Path to the .git directory. -local bootstrap = { - done = false, - ok = false, - version_string = nil, - version = {}, - target_version_string = nil, - target_version = { - major = 2, - minor = 31, - patch = 0, - }, -} - ----@type Job[] -local sync_jobs = {} ----@type Semaphore -local job_queue_sem = Semaphore.new(1) - ----Ensure that the configured git binary meets the version requirement. -local function run_bootstrap() - bootstrap.done = true - local msg - - local out, code = utils.system_list( - vim.tbl_flatten({ config.get_config().git_cmd, "version" }) - ) - - if code ~= 0 or not out[1] then - msg = "Could not run `git_cmd`!" - logger.error(msg) - utils.err(msg) - return - end - - bootstrap.version_string = out[1]:match("git version (%S+)") - - if not bootstrap.version_string then - msg = "Could not get git version!" - logger.error(msg) - utils.err(msg) - return - end - - -- Parse git version - local v, target = bootstrap.version, bootstrap.target_version - bootstrap.target_version_string = ("%d.%d.%d"):format(target.major, target.minor, target.patch) - local parts = vim.split(bootstrap.version_string, "%.") - v.major = tonumber(parts[1]) - v.minor = tonumber(parts[2]) - v.patch = tonumber(parts[3]) or 0 - - local vs = ("%08d%08d%08d"):format(v.major, v.minor, v.patch) - local ts = ("%08d%08d%08d"):format(target.major, target.minor, target.patch) - - if vs < ts then - msg = ( - "Git version is outdated! Some functionality might not work as expected, " - .. "or not at all! Target: %s, current: %s" - ):format( - bootstrap.target_version_string, - bootstrap.version_string - ) - logger.error(msg) - utils.err(msg) - return - end - - bootstrap.ok = true -end - ----@return string cmd The git binary. -local function git_bin() - return config.get_config().git_cmd[1] -end - ----@return string[] args The default git args. -local function git_args() - return utils.vec_slice(config.get_config().git_cmd, 2) -end - ----Execute a git command synchronously. ----@param args string[] ----@param cwd_or_opt? string|utils.system_list.Opt ----@return string[] stdout ----@return integer code ----@return string[] stderr ----@overload fun(args: string[], cwd: string?) ----@overload fun(args: string[], opt: utils.system_list.Opt?) -function M.exec_sync(args, cwd_or_opt) - if not bootstrap.done then - run_bootstrap() - end - - return utils.system_list( - vim.tbl_flatten({ config.get_config().git_cmd, args }), - cwd_or_opt - ) -end - ----@param job Job -local resume_sync_queue = async.void(function(job) - local permit = job_queue_sem:acquire() - local idx = utils.vec_indexof(sync_jobs, job) - if idx > -1 then - table.remove(sync_jobs, idx) - end - permit:forget() - - if sync_jobs[1] and not sync_jobs[1].handle then - sync_jobs[1]:start() - end -end) - ----@param job Job -local queue_sync_job = async.void(function(job) - job:add_on_exit_callback(function() - resume_sync_queue(job) - end) - - local permit = job_queue_sem:acquire() - table.insert(sync_jobs, job) - permit:forget() - - if #sync_jobs == 1 then - job:start() - end -end) - ----@param max_retries integer ----@vararg Job -local ensure_output = async.wrap(function(max_retries, jobs, log_context, callback) - local num_bad_jobs - local num_retries = 0 - local new_jobs = {} - local context = log_context and ("[%s] "):format(log_context) or "" - - for n = 0, max_retries - 1 do - num_bad_jobs = 0 - for i, job in ipairs(jobs) do - - if job.code == 0 and #job:result() == 0 then - logger.warn( - ("%sJob expected output, but returned nothing! Retrying %d more times(s)...") - :format(context, max_retries - n) - ) - logger.log_job(job, { func = logger.warn, context = log_context }) - num_retries = n + 1 - - new_jobs[i] = Job:new({ - command = job.command, - args = job.args, - cwd = job._raw_cwd, - env = job.env, - }) - new_jobs[i]:start() - if vim.in_fast_event() then - async.util.scheduler() - end - Job.join(new_jobs[i]) - - job._stdout_results = new_jobs[i]._stdout_results - job._stderr_results = new_jobs[i]._stderr_results - - if new_jobs[i].code ~= 0 then - job.code = new_jobs[i].code - utils.handle_job(new_jobs[i], { context = log_context }) - elseif #job._stdout_results == 0 then - num_bad_jobs = num_bad_jobs + 1 - end - end - end - - if num_bad_jobs == 0 then - if num_retries > 0 then - logger.s_info(("%sRetry was successful!"):format(context)) - end - callback(JobStatus.SUCCESS) - return - end - end - - callback(JobStatus.ERROR) -end, 4) - ----@param thread thread ----@param ok boolean ----@param result any ----@return boolean ok ----@return any result -local function handle_co(thread, ok, result) - if not ok then - local err_msg = utils.vec_join( - "Coroutine failed!", - debug.traceback(thread, result, 1) - ) - utils.err(err_msg, true) - logger.s_error(table.concat(err_msg, "\n")) - end - return ok, result -end ---@class git.utils.LayoutOpt ---@field default_layout Diff2 @@ -1017,190 +817,6 @@ local function parse_fh_line_trace_data(state) return true end ----@class git.utils.FileHistoryWorkerSpec : git.utils.LayoutOpt - ----@param thread thread ----@param ctx GitContext ----@param log_opt ConfigLogOptions ----@param opt git.utils.FileHistoryWorkerSpec ----@param co_state table ----@param callback function -local function file_history_worker(thread, ctx, log_opt, opt, co_state, callback) - ---@type LogEntry[] - local entries = {} - local data = {} - local data_idx = 1 - local last_status - local err_msg - - local single_file = is_single_file(ctx.toplevel, log_opt.single_file.path_args, log_opt.single_file.L) - - ---@type LogOptions - local log_options = config.get_log_options( - single_file, - single_file and log_opt.single_file or log_opt.multi_file - ) - - local is_trace = #log_options.L > 0 - - ---@type git.utils.FHState - local state = { - thread = thread, - ctx = ctx, - path_args = log_opt.single_file.path_args, - log_options = log_options, - prepared_log_opts = prepare_fh_options(ctx.toplevel, log_options, single_file), - opt = opt, - callback = callback, - entries = entries, - single_file = single_file, - resume_lock = false, - } - - local function data_callback(status, d, msg) - if status == JobStatus.PROGRESS then - data[#data+1] = d - end - - last_status = status - if msg then - err_msg = msg - end - if not state.resume_lock and coroutine.status(thread) == "suspended" then - handle_co(thread, coroutine.resume(thread)) - end - - if co_state.shutdown then - return true - end - end - - if is_trace then - incremental_line_trace_data(state, data_callback) - else - incremental_fh_data(state, data_callback) - end - - while true do - if not vim.tbl_contains({ JobStatus.SUCCESS, JobStatus.ERROR, JobStatus.KILLED }, last_status) - and not data[data_idx] then - coroutine.yield() - end - - if last_status == JobStatus.KILLED then - logger.warn("File history processing was killed.") - return - elseif last_status == JobStatus.ERROR then - callback(entries, JobStatus.ERROR, err_msg) - return - elseif last_status == JobStatus.SUCCESS and data_idx > #data then - break - end - - state.cur = data[data_idx] - - state.commit = Commit({ - hash = state.cur.right_hash, - author = state.cur.author, - time = tonumber(state.cur.time), - time_offset = state.cur.time_offset, - rel_date = state.cur.rel_date, - ref_names = state.cur.ref_names, - subject = state.cur.subject, - }) - - local ok, status - if log_options.L[1] then - ok, status = parse_fh_line_trace_data(state) - else - ok, status = parse_fh_data(state) - end - - if not ok then - if status == JobStatus.FATAL then - return - end - break - end - - data_idx = data_idx + 1 - end - - callback(entries, JobStatus.SUCCESS) -end - ----@param ctx GitContext ----@param log_opt ConfigLogOptions ----@param opt git.utils.FileHistoryWorkerSpec ----@param callback function ----@return fun() finalizer -function M.file_history(ctx, log_opt, opt, callback) - local thread - - local co_state = { - shutdown = false, - } - - thread = coroutine.create(function() - file_history_worker(thread, ctx, log_opt, opt, co_state, callback) - end) - - handle_co(thread, coroutine.resume(thread)) - - return function() - co_state.shutdown = true - end -end - ----@param toplevel string ----@param log_opt LogOptions ----@return boolean ok, string description -function M.file_history_dry_run(toplevel, log_opt) - local single_file = is_single_file(toplevel, log_opt.path_args, log_opt.L) - local log_options = config.get_log_options(single_file, log_opt) - - local options = vim.tbl_map(function(v) - return vim.fn.shellescape(v) - end, prepare_fh_options(toplevel, log_options, single_file).flags) --[[@as vector ]] - - local description = utils.vec_join( - ("Top-level path: '%s'"):format(utils.path:vim_fnamemodify(toplevel, ":~")), - log_options.rev_range and ("Revision range: '%s'"):format(log_options.rev_range) or nil, - ("Flags: %s"):format(table.concat(options, " ")) - ) - - log_options = utils.tbl_clone(log_options) --[[@as LogOptions ]] - log_options.max_count = 1 - options = prepare_fh_options(toplevel, log_options, single_file).flags - - local context = "git.utils.file_history_dry_run()" - local cmd - - if #log_options.L > 0 then - -- cmd = utils.vec_join("-P", "log", log_options.rev_range, "--no-ext-diff", "--color=never", "--pretty=format:%H", "-s", options, "--") - -- NOTE: Running the dry-run for line tracing is slow. Just skip for now. - return true, table.concat(description, ", ") - else - cmd = utils.vec_join("log", log_options.rev_range, "--pretty=format:%H", "--name-status", options, "--", log_options.path_args) - end - - local out, code = M.exec_sync(cmd, { - cwd = toplevel, - debug_opt = { - context = context, - no_stdout = true, - }, - }) - - local ok = code == 0 and #out > 0 - - if not ok then - logger.lvl(1).s_debug(("[%s] Dry run failed."):format(context)) - end - - return ok, table.concat(description, ", ") -end - ---Determine whether a rev arg is a range. ---@param rev_arg string ---@return boolean diff --git a/lua/diffview/vcs/utils.lua b/lua/diffview/vcs/utils.lua index aad05714..8656fb03 100644 --- a/lua/diffview/vcs/utils.lua +++ b/lua/diffview/vcs/utils.lua @@ -1,5 +1,6 @@ local utils = require("diffview.utils") local async = require("plenary.async") +local logger = require("diffview.logger") local Job = require("plenary.job") local Semaphore = require('diffview.control').Semaphore @@ -48,6 +49,62 @@ local queue_sync_job = async.void(function(job) end end) +---@param max_retries integer +---@vararg Job +local ensure_output = async.wrap(function(max_retries, jobs, log_context, callback) + local num_bad_jobs + local num_retries = 0 + local new_jobs = {} + local context = log_context and ("[%s] "):format(log_context) or "" + + for n = 0, max_retries - 1 do + num_bad_jobs = 0 + for i, job in ipairs(jobs) do + + if job.code == 0 and #job:result() == 0 then + logger.warn( + ("%sJob expected output, but returned nothing! Retrying %d more times(s)...") + :format(context, max_retries - n) + ) + logger.log_job(job, { func = logger.warn, context = log_context }) + num_retries = n + 1 + + new_jobs[i] = Job:new({ + command = job.command, + args = job.args, + cwd = job._raw_cwd, + env = job.env, + }) + new_jobs[i]:start() + if vim.in_fast_event() then + async.util.scheduler() + end + Job.join(new_jobs[i]) + + job._stdout_results = new_jobs[i]._stdout_results + job._stderr_results = new_jobs[i]._stderr_results + + if new_jobs[i].code ~= 0 then + job.code = new_jobs[i].code + utils.handle_job(new_jobs[i], { context = log_context }) + elseif #job._stdout_results == 0 then + num_bad_jobs = num_bad_jobs + 1 + end + end + end + + if num_bad_jobs == 0 then + if num_retries > 0 then + logger.s_info(("%sRetry was successful!"):format(context)) + end + callback(JobStatus.SUCCESS) + return + end + end + + callback(JobStatus.ERROR) +end, 4) + M.show = async.wrap(function(adapter, args, callback) local job = Job:new({ command = adapter:bin(), From 89d14a6625d234ce36cf95bb879d513b1286c8c1 Mon Sep 17 00:00:00 2001 From: zegervdv Date: Fri, 21 Oct 2022 05:05:36 +0000 Subject: [PATCH 30/71] fixup! refactor: create adapter base-class and implementations for git and hg --- lua/diffview/vcs/adapters/git/utils.lua | 56 ------------------------- 1 file changed, 56 deletions(-) diff --git a/lua/diffview/vcs/adapters/git/utils.lua b/lua/diffview/vcs/adapters/git/utils.lua index 9d837933..2154354a 100644 --- a/lua/diffview/vcs/adapters/git/utils.lua +++ b/lua/diffview/vcs/adapters/git/utils.lua @@ -1,4 +1,3 @@ -local Commit = require("diffview.vcs.adapters.git.commit").GitCommit local CountDownLatch = require("diffview.control").CountDownLatch local Diff2Hor = require("diffview.scene.layouts.diff_2_hor").Diff2Hor local FileDict = require("diffview.vcs.file_dict").FileDict @@ -7,9 +6,7 @@ local Job = require("plenary.job") local LogEntry = require("diffview.vcs.log_entry").LogEntry local Rev = require("diffview.vcs.rev").Rev local RevType = require("diffview.vcs.rev").RevType -local Semaphore = require("diffview.control").Semaphore local async = require("plenary.async") -local config = require("diffview.config") local logger = require("diffview.logger") local utils = require("diffview.utils") local JobStatus = require("diffview.vcs.utils").JobStatus @@ -18,15 +15,6 @@ local api = vim.api local M = {} ----@class GitContext ----@field toplevel string Path to the top-level directory of the working tree. ----@field dir string Path to the .git directory. - - ----@class git.utils.LayoutOpt ----@field default_layout Diff2 ----@field merge_layout Layout - ---@param ctx GitContext ---@param left Rev ---@param right Rev @@ -952,50 +940,6 @@ function M.git_context(path) end end -M.show = async.wrap(function(toplevel, args, callback) - local job = Job:new({ - command = git_bin(), - args = utils.vec_join( - git_args(), - "show", - args - ), - cwd = toplevel, - ---@type Job - on_exit = async.void(function(j) - local context = "git.utils.show()" - utils.handle_job(j, { - fail_on_empty = true, - context = context, - debug_opt = { no_stdout = true, context = context }, - }) - - if j.code ~= 0 then - callback(j:stderr_result() or {}, nil) - return - end - - local out_status - - if #j:result() == 0 then - async.util.scheduler() - out_status = ensure_output(2, { j }, context) - end - - if out_status == JobStatus.ERROR then - callback(j:stderr_result() or {}, nil) - return - end - - callback(nil, j:result()) - end), - }) - -- Problem: Running multiple 'show' jobs simultaneously may cause them to fail - -- silently. - -- Solution: queue them and run them one after another. - queue_sync_job(job) -end, 3) - local CONFLICT_START = [[^<<<<<<< ]] local CONFLICT_BASE = [[^||||||| ]] local CONFLICT_SEP = [[^=======$]] From 76f3af7bf728c8f273affa6d5d27a2ce958e97a5 Mon Sep 17 00:00:00 2001 From: zegervdv Date: Fri, 21 Oct 2022 05:28:39 +0000 Subject: [PATCH 31/71] fixup! refactor: create adapter base-class and implementations for git and hg --- lua/diffview/vcs/adapter.lua | 3 ++- lua/diffview/vcs/adapters/git/init.lua | 7 +++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/lua/diffview/vcs/adapter.lua b/lua/diffview/vcs/adapter.lua index 0f64770b..61d29473 100644 --- a/lua/diffview/vcs/adapter.lua +++ b/lua/diffview/vcs/adapter.lua @@ -22,7 +22,8 @@ function VCSAdapter:init(path) end function VCSAdapter:run_bootstrap() - oop.abstract_stub() + self.bootstrap.done = true + self.bootstrap.ok = true end function VCSAdapter:get_command() diff --git a/lua/diffview/vcs/adapters/git/init.lua b/lua/diffview/vcs/adapters/git/init.lua index 73e0a3c3..807f6ef6 100644 --- a/lua/diffview/vcs/adapters/git/init.lua +++ b/lua/diffview/vcs/adapters/git/init.lua @@ -45,6 +45,10 @@ local function pathspec_modify(pathspec, mods) return magic .. utils.path:vim_fnamemodify(pattern, mods) end +---Parse arguments to detect repository type +---@param args string[] +---@return string[] paths # All paths parsed from arguments +---@return string[] top_indicators # Paths to consider for finding the toplevel function M.get_repo_paths(args) local default_args = config.get_config().default_args.DiffviewFileHistory local argo = arg_parser.parse(vim.tbl_flatten({ default_args, args })) @@ -96,6 +100,9 @@ function M.get_repo_paths(args) return paths, top_indicators end +---Get the git toplevel directory from a path to file or directory +---@param path string +---@return string? local function get_toplevel(path) local out, code = utils.system_list(vim.tbl_flatten({config.get_config().git_cmd, {"rev-parse", "--path-format=absolute", "--show-toplevel"}, path})) if code ~= 0 then From dfb733fc901668c39a8b8f4a6da58fec901a2a9f Mon Sep 17 00:00:00 2001 From: zegervdv Date: Sat, 22 Oct 2022 08:35:38 +0000 Subject: [PATCH 32/71] refactor: move FHOptions to GitAdapter --- .../views/file_history/file_history_panel.lua | 2 +- .../scene/views/file_history/option_panel.lua | 215 +----------------- lua/diffview/vcs/adapters/git/init.lua | 195 ++++++++++++++++ 3 files changed, 206 insertions(+), 206 deletions(-) diff --git a/lua/diffview/scene/views/file_history/file_history_panel.lua b/lua/diffview/scene/views/file_history/file_history_panel.lua index ccd1d56e..98271aa7 100644 --- a/lua/diffview/scene/views/file_history/file_history_panel.lua +++ b/lua/diffview/scene/views/file_history/file_history_panel.lua @@ -77,7 +77,7 @@ function FileHistoryPanel:init(opt) self.entries = opt.entries self.cur_item = {} self.single_file = opt.entries[1] and opt.entries[1].single_file - self.option_panel = FHOptionPanel(self) + self.option_panel = FHOptionPanel(self, self.git_ctx.flags) self.log_options = { single_file = vim.tbl_extend( "force", diff --git a/lua/diffview/scene/views/file_history/option_panel.lua b/lua/diffview/scene/views/file_history/option_panel.lua index 40566295..28fdc218 100644 --- a/lua/diffview/scene/views/file_history/option_panel.lua +++ b/lua/diffview/scene/views/file_history/option_panel.lua @@ -52,228 +52,33 @@ FHOptionPanel.bufopts = { ---@field render_value fun(option: FlagOption, value: string|string[]): boolean, string # Render the flag value in the panel. ---@field render_default fun(options: FlagOption, value: string|string[]): string # Render the default text for the input(). -FHOptionPanel.flags = { - ---@type FlagOption[] - switches = { - { "-f", "--follow", "Follow renames (only for single file)" }, - { "-p", "--first-parent", "Follow only the first parent upon seeing a merge commit" }, - { "-s", "--show-pulls", "Show merge commits the first introduced a change to a branch" }, - { "-R", "--reflog", "Include all reachable objects mentioned by reflogs" }, - { "-a", "--all", "Include all refs" }, - { "-m", "--merges", "List only merge commits" }, - { "-n", "--no-merges", "List no merge commits" }, - { "-r", "--reverse", "List commits in reverse order" }, - }, - ---@type FlagOption[] - options = { - { - "=r", "++rev-range=", "Show only commits in the specified revision range", - ---@param panel FHOptionPanel - completion = function(panel) - return function(arg_lead, _, _) - local view = panel.parent.parent - return diffview.rev_completion(arg_lead, { - accept_range = true, - git_toplevel = view.git_ctx.toplevel, - git_dir = view.git_ctx.dir, - }) - end - end, - }, - { - "=b", "++base=", "Set the base revision", - ---@param panel FHOptionPanel - completion = function(panel) - return function(arg_lead, _, _) - local view = panel.parent.parent - return utils.vec_join("LOCAL", diffview.rev_completion(arg_lead, { - git_toplevel = view.git_ctx.toplevel, - git_dir = view.git_ctx.dir, - })) - end - end, - }, - { "=n", "--max-count=", "Limit the number of commits" }, - { - "=L", "-L", "Trace line evolution", - prompt_label = "(Accepts multiple values)", - prompt_fmt = "${label} ", - completion = function(_) - return function(arg_lead, _, _) - return diffview.line_trace_completion(arg_lead) - end - end, - transform = function(values) - return utils.tbl_fmap(values, function(v) - v = utils.str_match(v, { "^-L(.*)", ".*" }) - if v == "" then return nil end - return v - end) - end, - ---@param self FlagOption - ---@param value string|string[] - render_value = function(self, value) - if #value == 0 then - -- Just render the flag name - return true, self[2] - end - - -- Render a string of quoted args - return false, table.concat(vim.tbl_map(function(v) - if not v:match("^-L") then - -- Prepend the flag if it wasn't specified by the user. - v = "-L" .. v - end - return utils.str_quote(v, { only_if_whitespace = true }) - end, value), " ") - end, - render_default = function(_, value) - if #value == 0 then - -- Just render the flag name - return "-L" - end - - -- Render a string of quoted args - return table.concat(vim.tbl_map(function(v) - v = select(1, v:gsub("\\", "\\\\")) - return utils.str_quote("-L" .. v, { only_if_whitespace = true }) - end, value), " ") - end, - }, - { - "=d", "--diff-merges=", "Determines how merge commits are treated", - select = { - "", - "off", - "on", - "first-parent", - "separate", - "combined", - "dense-combined", - "remerge", - }, - }, - { "=a", "--author=", "List only commits from a given author", prompt_label = "(Extended regular expression)" }, - { "=g", "--grep=", "Filter commit messages", prompt_label = "(Extended regular expression)" }, - { - "--", "--", "Limit to files", - key = "path_args", - prompt_label = "(Path arguments)", - prompt_fmt = "${label}${flag_name} ", - transform = function(values) - return utils.tbl_fmap(values, function(v) - if v == "" then return nil end - return v - end) - end, - render_value = function(_, value) - if #value == 0 then - -- Just render the flag name - return true, "--" - end - - -- Render a string of quoted args - return false, table.concat(utils.vec_join( - "--", - vim.tbl_map(function(v) - v = v:gsub("\\", "\\\\") - return utils.str_quote(v, { only_if_whitespace = true }) - end, value) - ), " ") - end, - completion = function(_) - return function(_, cmd_line, cur_pos) - local ok, ctx = pcall(arg_parser.scan_sh_args, cmd_line, cur_pos) - - if ok then - local quoted = vim.tbl_map(function(v) - return utils.str_quote(v, { only_if_whitespace = true }) - end, ctx.args) - - return vim.tbl_map(function(v) - return table.concat(utils.vec_join( - utils.vec_slice(quoted, 1, ctx.argidx - 1), - utils.str_quote(v, { only_if_whitespace = true }) - ), " ") - end, vim.fn.getcompletion(ctx.arg_lead, "file")) - end - end - end, - }, - }, -} - -for _, list in pairs(FHOptionPanel.flags) do - for i, option in ipairs(list) do - option = vim.tbl_extend("keep", option, { - prompt_fmt = "${label}${flag_name}", - - key = option.key or utils.str_match(option[2], { - "^%-%-?([^=]+)=?", - "^%+%+?([^=]+)=?", - }):gsub("%-", "_"), - - ---@param self FlagOption - ---@param value string|string[] - render_value = function(self, value) - local quoted - - if type(value) == "table" then - quoted = table.concat(vim.tbl_map(function(v) - return self[2] .. utils.str_quote(v, { only_if_whitespace = true }) - end, value), " ") - else - quoted = self[2] .. utils.str_quote(value, { only_if_whitespace = true }) - end - - return value == "", quoted - end, - - ---@param value string|string[] - render_default = function(_, value) - if value == nil then - return "" - elseif type(value) == "table" then - return table.concat(vim.tbl_map(function(v) - v = select(1, v:gsub("\\", "\\\\")) - return utils.str_quote(v, { only_if_whitespace = true }) - end, value), " ") - end - return utils.str_quote(value, { only_if_whitespace = true }) - end, - }) - - list[i] = option - list[option.key] = option - end -end - ---FHOptionPanel constructor. ---@param parent FileHistoryPanel -function FHOptionPanel:init(parent) +function FHOptionPanel:init(parent, flags) FHOptionPanel:super().init(self, { ---@type PanelSplitSpec config = { position = "bottom", - height = #FHOptionPanel.flags.switches + #FHOptionPanel.flags.options + 4, + height = #flags.switches + #flags.options + 4, }, bufname = "DiffviewFHOptionPanel", }) self.parent = parent self.emitter = EventEmitter() + self.flags = flags ---@param option_name string self.emitter:on("set_option", function(option_name) local log_options = self.parent:get_log_options() local cur_value = log_options[option_name] - if FHOptionPanel.flags.switches[option_name] then + if flags.switches[option_name] then self:_set_option(option_name, not cur_value) self:render() self:redraw() - elseif FHOptionPanel.flags.options[option_name] then - local o = FHOptionPanel.flags.options[option_name] + elseif flags.options[option_name] then + local o = flags.options[option_name] local prompt = utils.str_template(o.prompt_fmt, { label = o.prompt_label and o.prompt_label .. " " or "", flag_name = o[2] .. " ", @@ -387,8 +192,8 @@ function FHOptionPanel:setup_buffer() end end - for group, _ in pairs(FHOptionPanel.flags) do - for option_name, v in pairs(FHOptionPanel.flags[group]) do + for group, _ in pairs(self.flags) do + for option_name, v in pairs(self.flags[group]) do vim.keymap.set( "n", v[1], @@ -404,10 +209,10 @@ end function FHOptionPanel:update_components() local switch_schema = {} local option_schema = {} - for _, option in ipairs(FHOptionPanel.flags.switches) do + for _, option in ipairs(self.flags.switches) do table.insert(switch_schema, { name = "switch", context = { option.key, option } }) end - for _, option in ipairs(FHOptionPanel.flags.options) do + for _, option in ipairs(self.flags.options) do table.insert(option_schema, { name = "option", context = { option.key, option } }) end diff --git a/lua/diffview/vcs/adapters/git/init.lua b/lua/diffview/vcs/adapters/git/init.lua index 807f6ef6..f411b7a1 100644 --- a/lua/diffview/vcs/adapters/git/init.lua +++ b/lua/diffview/vcs/adapters/git/init.lua @@ -983,6 +983,201 @@ function GitAdapter:is_binary(path, rev) return code ~= 0 end +GitAdapter.flags = { + ---@type FlagOption[] + switches = { + { "-f", "--follow", "Follow renames (only for single file)" }, + { "-p", "--first-parent", "Follow only the first parent upon seeing a merge commit" }, + { "-s", "--show-pulls", "Show merge commits the first introduced a change to a branch" }, + { "-R", "--reflog", "Include all reachable objects mentioned by reflogs" }, + { "-a", "--all", "Include all refs" }, + { "-m", "--merges", "List only merge commits" }, + { "-n", "--no-merges", "List no merge commits" }, + { "-r", "--reverse", "List commits in reverse order" }, + }, + ---@type FlagOption[] + options = { + { + "=r", "++rev-range=", "Show only commits in the specified revision range", + ---@param panel FHOptionPanel + completion = function(panel) + return function(arg_lead, _, _) + local view = panel.parent.parent + return diffview.rev_completion(arg_lead, { + accept_range = true, + git_toplevel = view.git_ctx.toplevel, + git_dir = view.git_ctx.dir, + }) + end + end, + }, + { + "=b", "++base=", "Set the base revision", + ---@param panel FHOptionPanel + completion = function(panel) + return function(arg_lead, _, _) + local view = panel.parent.parent + return utils.vec_join("LOCAL", diffview.rev_completion(arg_lead, { + git_toplevel = view.git_ctx.toplevel, + git_dir = view.git_ctx.dir, + })) + end + end, + }, + { "=n", "--max-count=", "Limit the number of commits" }, + { + "=L", "-L", "Trace line evolution", + prompt_label = "(Accepts multiple values)", + prompt_fmt = "${label} ", + completion = function(_) + return function(arg_lead, _, _) + return diffview.line_trace_completion(arg_lead) + end + end, + transform = function(values) + return utils.tbl_fmap(values, function(v) + v = utils.str_match(v, { "^-L(.*)", ".*" }) + if v == "" then return nil end + return v + end) + end, + ---@param self FlagOption + ---@param value string|string[] + render_value = function(self, value) + if #value == 0 then + -- Just render the flag name + return true, self[2] + end + + -- Render a string of quoted args + return false, table.concat(vim.tbl_map(function(v) + if not v:match("^-L") then + -- Prepend the flag if it wasn't specified by the user. + v = "-L" .. v + end + return utils.str_quote(v, { only_if_whitespace = true }) + end, value), " ") + end, + render_default = function(_, value) + if #value == 0 then + -- Just render the flag name + return "-L" + end + + -- Render a string of quoted args + return table.concat(vim.tbl_map(function(v) + v = select(1, v:gsub("\\", "\\\\")) + return utils.str_quote("-L" .. v, { only_if_whitespace = true }) + end, value), " ") + end, + }, + { + "=d", "--diff-merges=", "Determines how merge commits are treated", + select = { + "", + "off", + "on", + "first-parent", + "separate", + "combined", + "dense-combined", + "remerge", + }, + }, + { "=a", "--author=", "List only commits from a given author", prompt_label = "(Extended regular expression)" }, + { "=g", "--grep=", "Filter commit messages", prompt_label = "(Extended regular expression)" }, + { + "--", "--", "Limit to files", + key = "path_args", + prompt_label = "(Path arguments)", + prompt_fmt = "${label}${flag_name} ", + transform = function(values) + return utils.tbl_fmap(values, function(v) + if v == "" then return nil end + return v + end) + end, + render_value = function(_, value) + if #value == 0 then + -- Just render the flag name + return true, "--" + end + + -- Render a string of quoted args + return false, table.concat(utils.vec_join( + "--", + vim.tbl_map(function(v) + v = v:gsub("\\", "\\\\") + return utils.str_quote(v, { only_if_whitespace = true }) + end, value) + ), " ") + end, + completion = function(_) + return function(_, cmd_line, cur_pos) + local ok, ctx = pcall(arg_parser.scan_sh_args, cmd_line, cur_pos) + + if ok then + local quoted = vim.tbl_map(function(v) + return utils.str_quote(v, { only_if_whitespace = true }) + end, ctx.args) + + return vim.tbl_map(function(v) + return table.concat(utils.vec_join( + utils.vec_slice(quoted, 1, ctx.argidx - 1), + utils.str_quote(v, { only_if_whitespace = true }) + ), " ") + end, vim.fn.getcompletion(ctx.arg_lead, "file")) + end + end + end, + }, + }, +} + +for _, list in pairs(GitAdapter.flags) do + for i, option in ipairs(list) do + option = vim.tbl_extend("keep", option, { + prompt_fmt = "${label}${flag_name}", + + key = option.key or utils.str_match(option[2], { + "^%-%-?([^=]+)=?", + "^%+%+?([^=]+)=?", + }):gsub("%-", "_"), + + ---@param self FlagOption + ---@param value string|string[] + render_value = function(self, value) + local quoted + + if type(value) == "table" then + quoted = table.concat(vim.tbl_map(function(v) + return self[2] .. utils.str_quote(v, { only_if_whitespace = true }) + end, value), " ") + else + quoted = self[2] .. utils.str_quote(value, { only_if_whitespace = true }) + end + + return value == "", quoted + end, + + ---@param value string|string[] + render_default = function(_, value) + if value == nil then + return "" + elseif type(value) == "table" then + return table.concat(vim.tbl_map(function(v) + v = select(1, v:gsub("\\", "\\\\")) + return utils.str_quote(v, { only_if_whitespace = true }) + end, value), " ") + end + return utils.str_quote(value, { only_if_whitespace = true }) + end, + }) + + list[i] = option + list[option.key] = option + end +end M.GitAdapter = GitAdapter return M From 52423802de4000a4b054bf1c527931ae3272e4ac Mon Sep 17 00:00:00 2001 From: zegervdv Date: Sun, 23 Oct 2022 09:02:43 +0000 Subject: [PATCH 33/71] integrate suggested changes --- lua/diffview/lib.lua | 24 ++++++- lua/diffview/vcs/adapter.lua | 2 + lua/diffview/vcs/adapters/git/init.lua | 97 ++++++++++++-------------- lua/diffview/vcs/adapters/hg/init.lua | 7 +- lua/diffview/vcs/init.lua | 44 +++++++++--- 5 files changed, 107 insertions(+), 67 deletions(-) diff --git a/lua/diffview/lib.lua b/lua/diffview/lib.lua index 60e8d70e..d7fe9a46 100644 --- a/lua/diffview/lib.lua +++ b/lua/diffview/lib.lua @@ -133,14 +133,34 @@ end ---@param args string[] function M.file_history(range, args) local default_args = config.get_config().default_args.DiffviewFileHistory + local argo = arg_parser.parse(vim.tbl_flatten({ default_args, args })) + local rel_paths + + local err, adapter = vcs.get_adapter({ + cmd_ctx = { + path_args = argo.args, + cpath = argo:get_flag("C", { no_empty = true, expand = true }), + }, + }) + + if err then + utils.err(err) + return + end + + rel_paths = vim.tbl_map(function(v) + return v == "." and "." or pl:relative(v, ".") + end, adapter.ctx.path_args) + + print('path_args: ', vim.inspect(adapter.ctx.path_args)) + print('rel_paths:', vim.inspect(rel_paths)) logger.info("[command call] :DiffviewFileHistory " .. table.concat(vim.tbl_flatten({ default_args, args, }), " ")) - local adapter, paths = vcs.get_adapter(args) - local log_options = adapter:file_history_options(range, paths, args) + local log_options = adapter:file_history_options(range, rel_paths, args) if log_options == nil then utils.err('Failed to create log options for file_history') diff --git a/lua/diffview/vcs/adapter.lua b/lua/diffview/vcs/adapter.lua index 61d29473..4b77f444 100644 --- a/lua/diffview/vcs/adapter.lua +++ b/lua/diffview/vcs/adapter.lua @@ -17,6 +17,8 @@ function VCSAdapter:init(path) self.bootstrap = { done = false, ok = false, + version = {}, + version_string = {}, } self.context = {} end diff --git a/lua/diffview/vcs/adapters/git/init.lua b/lua/diffview/vcs/adapters/git/init.lua index f411b7a1..0e6d9037 100644 --- a/lua/diffview/vcs/adapters/git/init.lua +++ b/lua/diffview/vcs/adapters/git/init.lua @@ -45,16 +45,15 @@ local function pathspec_modify(pathspec, mods) return magic .. utils.path:vim_fnamemodify(pattern, mods) end ----Parse arguments to detect repository type ----@param args string[] ----@return string[] paths # All paths parsed from arguments ----@return string[] top_indicators # Paths to consider for finding the toplevel -function M.get_repo_paths(args) - local default_args = config.get_config().default_args.DiffviewFileHistory - local argo = arg_parser.parse(vim.tbl_flatten({ default_args, args })) +---@param path_args string[] # Raw path args +---@param cpath string? # Cwd path given by the `-C` flag option +---@return string[] path_args # Resolved path args +---@return string[] top_indicators # Top-level indicators +function M.get_repo_paths(path_args, cpath) local paths = {} + local top_indicators = {} - for _, path_arg in ipairs(argo.args) do + for _, path_arg in ipairs(path_args) do for _, path in ipairs(pl:vim_expand(path_arg, false, true)) do local magic, pattern = pathspec_split(path) pattern = pl:readlink(pattern) or pattern @@ -62,12 +61,9 @@ function M.get_repo_paths(args) end end - ---@type string - local cpath = argo:get_flag("C", { no_empty = true, expand = true }) local cfile = pl:vim_expand("%") cfile = pl:readlink(cfile) or cfile - local top_indicators = {} for _, path in ipairs(paths) do if pathspec_split(path) == "" then table.insert(top_indicators, pl:absolute(path, cpath)) @@ -76,25 +72,13 @@ function M.get_repo_paths(args) end table.insert(top_indicators, cpath and pl:realpath(cpath) or ( - vim.bo.buftype == "" - and pl:absolute(cfile) - or nil - )) + vim.bo.buftype == "" + and pl:absolute(cfile) + or nil + )) - local test_args = {} if not cpath then table.insert(top_indicators, pl:realpath(".")) - else - table.insert(test_args, '-C') - table.insert(test_args, cpath) - end - - - local out, code = utils.system_list(vim.tbl_flatten({ config.get_config().git_cmd, test_args, "rev-parse" })) - - -- Not in a Git repo - if code ~= 0 then - return nil, nil end return paths, top_indicators @@ -105,6 +89,7 @@ end ---@return string? local function get_toplevel(path) local out, code = utils.system_list(vim.tbl_flatten({config.get_config().git_cmd, {"rev-parse", "--path-format=absolute", "--show-toplevel"}, path})) + print('Checking', path, ' -> ', vim.inspect(code), ' : ', vim.inspect(out)) if code ~= 0 then return nil end @@ -116,7 +101,7 @@ end ---@param top_indicators string[] A list of paths that might indicate what working tree we are in. ---@return string? err ---@return string? toplevel # as an absolute path -local function find_git_toplevel(top_indicators) +function M.find_toplevel(top_indicators) local toplevel for _, p in ipairs(top_indicators) do if not pl:is_dir(p) then @@ -125,9 +110,8 @@ local function find_git_toplevel(top_indicators) if p and pl:readable(p) then toplevel = get_toplevel(p) - if toplevel then - return nil, toplevel + return toplevel end end end @@ -141,22 +125,36 @@ local function find_git_toplevel(top_indicators) ) end -function GitAdapter:init(paths) - self.super:init(paths) +---@param toplevel string +---@param path_args string? +---@param cpath string? +function M.create(toplevel, path_args, cpath) + return GitAdapter({ + toplevel = toplevel, + path_args = path_args, + cpath = cpath, + }) +end + +function GitAdapter:init(opt) + opt = opt or {} + GitAdapter:super().init(self, opt) - self.bootstrap.version_string = nil - self.bootstrap.version = {} - self.bootstrap.target_version_string = nil self.bootstrap.target_version = { major = 2, minor = 31, patch = 0, } - -- TODO: Handler error here - local err, toplevel = find_git_toplevel(paths) - self.context.toplevel = toplevel - self.context.dir = self:get_dir(self.context.toplevel) + local cwd = opt.cpath or vim.loop.cwd() + + self.ctx = { + toplevel = opt.toplevel, + git_dir = self:git_dir(opt.toplevel), + path_args = vim.tbl_map(function(pathspec) + return pathspec_expand(opt.toplevel, cwd, pathspec) + end, opt.path_args or {}) --[[@as string[] ]] + } end function GitAdapter:run_bootstrap() @@ -216,7 +214,7 @@ function GitAdapter:get_show_args(args) return utils.vec_join(self:args(), "show", args) end -function GitAdapter:get_dir(path) +function GitAdapter:git_dir(path) local out, code = self:exec_sync({ "rev-parse", "--path-format=absolute", "--git-dir" }, path) if code ~= 0 then return nil @@ -615,8 +613,6 @@ function GitAdapter:file_history_options(range, paths, args) local cfile = pl:vim_expand("%") cfile = pl:readlink(cfile) or cfile - local git_toplevel = self.context.toplevel - ---@cast git_toplevel string logger.lvl(1).s_debug(("Found git top-level: %s"):format(utils.str_quote(git_toplevel))) @@ -626,13 +622,13 @@ function GitAdapter:file_history_options(range, paths, args) local cwd = cpath or vim.loop.cwd() paths = vim.tbl_map(function(pathspec) - return pathspec_expand(git_toplevel, cwd, pathspec) + return pathspec_expand(self.ctx.toplevel, cwd, pathspec) end, paths) --[[@as string[] ]] ---@type string local range_arg = argo:get_flag("range", { no_empty = true }) if range_arg then - local ok = self:verify_rev_arg(git_toplevel, range_arg) + local ok = self:verify_rev_arg(self.ctx.toplevel, range_arg) if not ok then utils.err(("Bad revision: %s"):format(utils.str_quote(range_arg))) return @@ -672,13 +668,13 @@ function GitAdapter:file_history_options(range, paths, args) if range then paths, rel_paths = {}, {} log_options.L = { - ("%d,%d:%s"):format(range[1], range[2], pl:relative(pl:absolute(cfile), git_toplevel)) + ("%d,%d:%s"):format(range[1], range[2], pl:relative(pl:absolute(cfile), self.ctx.toplevel)) } end log_options.path_args = paths - local ok, opt_description = self:file_history_dry_run(git_toplevel, log_options) + local ok, opt_description = self:file_history_dry_run(self.ctx.toplevel, log_options) if not ok then utils.info({ @@ -691,15 +687,10 @@ function GitAdapter:file_history_options(range, paths, args) return end - local git_ctx = { - toplevel = git_toplevel, - dir = self.context.dir, - } - - if not git_ctx.dir then + if not self.ctx.git_dir then utils.err( ("Failed to find the git dir for the repository: %s") - :format(utils.str_quote(git_ctx.toplevel)) + :format(utils.str_quote(self.ctx.toplevel)) ) return end diff --git a/lua/diffview/vcs/adapters/hg/init.lua b/lua/diffview/vcs/adapters/hg/init.lua index 9b34874f..f12479a5 100644 --- a/lua/diffview/vcs/adapters/hg/init.lua +++ b/lua/diffview/vcs/adapters/hg/init.lua @@ -7,7 +7,12 @@ local HgAdapter = oop.create_class('HgAdapter', VCSAdapter) function M.get_repo_paths(args) -- TODO: implement - return false + return nil +end + +function M.find_toplevel(top_indicators) + -- TODO: implement + return nil end M.HgAdapter = HgAdapter diff --git a/lua/diffview/vcs/init.lua b/lua/diffview/vcs/init.lua index f34548fa..b7b52d65 100644 --- a/lua/diffview/vcs/init.lua +++ b/lua/diffview/vcs/init.lua @@ -4,21 +4,43 @@ local hg = require('diffview.vcs.adapters.hg') local M = {} --- Try to extract paths from arguments to determine VCS type -function M.get_adapter(args) - local paths +---@class vcs.init.get_adapter.Opt +---@field top_indicators string[]? +---@field cmd_ctx vcs.init.get_adapter.Opt.Cmd_Ctx? # Context data from a command call. - paths, toplevel_indicators = git.get_repo_paths(args) - if paths then - return git.GitAdapter(toplevel_indicators), paths - end +---@class vcs.init.get_adapter.Opt.Cmd_Ctx +---@field path_args string[] # Raw path args +---@field cpath string? # Cwd path given by the `-C` flag option + +---@param opt vcs.init.get_adapter.Opt +---@return err string? +---@return adapter VCSAdapter? +function M.get_adapter(opt) + local adapters = { git, hg } + + for _, adapter in ipairs(adapters) do + local path_args + local top_indicators = opt.top_indicators + + print('before', vim.inspect(top_indicators)) + + if not top_indicators then + path_args, top_indicators = adapter.get_repo_paths(opt.cmd_ctx.path_args, opt.cmd_ctx.cpath) + end + print('after', vim.inspect(top_indicators)) + + local toplevel = adapter.find_toplevel(top_indicators) + + print('toplevel: ', vim.inspect(toplevel)) - paths, toplevel_indicators = hg.get_repo_paths(args) - if paths then - return hg.HgAdapter(toplevel_indicators), paths + if toplevel then + -- Create a new adapter instance. Store the resolved path args and the + -- cpath in the adapter context. + return nil, adapter.create(toplevel, path_args, opt.cmd_ctx.cpath) + end end - utils.err("No valid VCS found for current workspace") + return "Not a repo (or any parent), or no supported VCS adapter!" end return M From 87d3c3674f7efe8a55030f238822fe0c5847bc36 Mon Sep 17 00:00:00 2001 From: zegervdv Date: Sun, 23 Oct 2022 09:56:03 +0000 Subject: [PATCH 34/71] fixup! integrate suggested changes --- lua/diffview/api/views/diff/diff_view.lua | 2 +- lua/diffview/scene/file_entry.lua | 12 +++---- lua/diffview/scene/views/diff/diff_view.lua | 12 +++---- lua/diffview/scene/views/diff/listeners.lua | 12 +++---- lua/diffview/scene/views/diff/render.lua | 4 +-- .../views/file_history/file_history_view.lua | 4 +-- .../scene/views/file_history/listeners.lua | 4 +-- .../scene/views/file_history/render.lua | 4 +-- lua/diffview/vcs/adapters/git/init.lua | 36 +++++++++---------- 9 files changed, 44 insertions(+), 46 deletions(-) diff --git a/lua/diffview/api/views/diff/diff_view.lua b/lua/diffview/api/views/diff/diff_view.lua index acdb418c..49dc9606 100644 --- a/lua/diffview/api/views/diff/diff_view.lua +++ b/lua/diffview/api/views/diff/diff_view.lua @@ -128,7 +128,7 @@ function CDiffView:create_file_entries(files) { kind = "staged", files = files.staged or {}, - left = vcs.head_rev(self.git_ctx.toplevel), + left = vcs.head_rev(self.git_ctx.ctx.toplevel), right = Rev(RevType.STAGE, 0), }, } diff --git a/lua/diffview/scene/file_entry.lua b/lua/diffview/scene/file_entry.lua index b94f57d9..82655b21 100644 --- a/lua/diffview/scene/file_entry.lua +++ b/lua/diffview/scene/file_entry.lua @@ -169,8 +169,8 @@ end ---@param git_ctx GitContext ---@param stat? table function FileEntry:validate_stage_buffers(git_ctx, stat) - stat = stat or utils.path:stat(utils.path:join(git_ctx.dir, "index")) - local cached_stat = utils.tbl_access(fstat_cache, { git_ctx.toplevel, "index" }) + stat = stat or utils.path:stat(utils.path:join(git_ctx.ctx.dir, "index")) + local cached_stat = utils.tbl_access(fstat_cache, { git_ctx.ctx.toplevel, "index" }) if stat then if not cached_stat or cached_stat.mtime < stat.mtime.sec then @@ -186,14 +186,14 @@ end ---@static ---@param git_ctx GitContext function FileEntry.update_index_stat(git_ctx, stat) - stat = stat or utils.path:stat(utils.path:join(git_ctx.toplevel, "index")) + stat = stat or utils.path:stat(utils.path:join(git_ctx.ctx.toplevel, "index")) if stat then - if not fstat_cache[git_ctx.toplevel] then - fstat_cache[git_ctx.toplevel] = {} + if not fstat_cache[git_ctx.ctx.toplevel] then + fstat_cache[git_ctx.ctx.toplevel] = {} end - fstat_cache[git_ctx.toplevel].index = { + fstat_cache[git_ctx.ctx.toplevel].index = { mtime = stat.mtime.sec, } end diff --git a/lua/diffview/scene/views/diff/diff_view.lua b/lua/diffview/scene/views/diff/diff_view.lua index d1a7e95c..fa0358ba 100644 --- a/lua/diffview/scene/views/diff/diff_view.lua +++ b/lua/diffview/scene/views/diff/diff_view.lua @@ -55,7 +55,7 @@ function DiffView:init(opt) self.options.selected_file = self.options.selected_file and utils.path:chain(self.options.selected_file) :absolute() - :relative(self.git_ctx.toplevel) + :relative(self.git_ctx.ctx.toplevel) :get() DiffView:super().init(self, { @@ -119,14 +119,14 @@ end function DiffView:post_open() vim.cmd("redraw") - self.commit_log_panel = CommitLogPanel(self.git_ctx.toplevel, { - name = ("diffview://%s/log/%d/%s"):format(self.git_ctx.dir, self.tabpage, "commit_log"), + self.commit_log_panel = CommitLogPanel(self.git_ctx.ctx.toplevel, { + name = ("diffview://%s/log/%d/%s"):format(self.git_ctx.ctx.dir, self.tabpage, "commit_log"), }) if config.get_config().watch_index then self.watcher = vim.loop.new_fs_poll() ---@diagnostic disable-next-line: unused-local - self.watcher:start(self.git_ctx.dir .. "/index", 1000, function(err, prev, cur) + self.watcher:start(self.git_ctx.ctx.dir .. "/index", 1000, function(err, prev, cur) if not err then vim.schedule(function() if self:is_cur_tabpage() then @@ -302,7 +302,7 @@ DiffView.update_files = debounce.debounce_trailing(100, true, vim.schedule_wrap( -- If left is tracking HEAD and right is LOCAL: Update HEAD rev. local new_head if self.left.track_head and self.right.type == RevType.LOCAL then - new_head = vcs.head_rev(self.git_ctx.toplevel) + new_head = vcs.head_rev(self.git_ctx.ctx.toplevel) if new_head and self.left.commit ~= new_head.commit then self.left = new_head else @@ -311,7 +311,7 @@ DiffView.update_files = debounce.debounce_trailing(100, true, vim.schedule_wrap( perf:lap("updated head rev") end - local index_stat = utils.path:stat(utils.path:join(self.git_ctx.dir, "index")) + local index_stat = utils.path:stat(utils.path:join(self.git_ctx.ctx.dir, "index")) local last_winid = api.nvim_get_current_win() self:get_updated_files(function(err, new_files) if err then diff --git a/lua/diffview/scene/views/diff/listeners.lua b/lua/diffview/scene/views/diff/listeners.lua index 892e5699..3b679940 100644 --- a/lua/diffview/scene/views/diff/listeners.lua +++ b/lua/diffview/scene/views/diff/listeners.lua @@ -105,7 +105,7 @@ return function(view) or ( view.left.type == RevType.COMMIT and vim.tbl_contains({ RevType.STAGE, RevType.LOCAL }, view.right.type) - and view.left:is_head(view.git_ctx.toplevel) + and view.left:is_head(view.git_ctx.ctx.toplevel) ) then utils.info("Changes not commited yet. No log available for these changes.") return @@ -123,9 +123,9 @@ return function(view) if item then local code if item.kind == "working" or item.kind == "conflicting" then - _, code = vcs.exec_sync({ "add", item.path }, view.git_ctx.toplevel) + _, code = vcs.exec_sync({ "add", item.path }, view.git_ctx.ctx.toplevel) elseif item.kind == "staged" then - _, code = vcs.exec_sync({ "reset", "--", item.path }, view.git_ctx.toplevel) + _, code = vcs.exec_sync({ "reset", "--", item.path }, view.git_ctx.ctx.toplevel) end if code ~= 0 then @@ -180,7 +180,7 @@ return function(view) end, view.files.working) if #args > 0 then - local _, code = vcs.exec_sync({ "add", args }, view.git_ctx.toplevel) + local _, code = vcs.exec_sync({ "add", args }, view.git_ctx.ctx.toplevel) if code ~= 0 then utils.err("Failed to stage files!") @@ -194,7 +194,7 @@ return function(view) end end, unstage_all = function() - local _, code = vcs.exec_sync({ "reset" }, view.git_ctx.toplevel) + local _, code = vcs.exec_sync({ "reset" }, view.git_ctx.ctx.toplevel) if code ~= 0 then utils.err("Failed to unstage files!") @@ -220,7 +220,7 @@ return function(view) utils.err("The file is open with unsaved changes! Aborting file restoration.") return end - vcs.restore_file(view.git_ctx.toplevel, file.path, file.kind, commit, function() + vcs.restore_file(view.git_ctx.ctx.toplevel, file.path, file.kind, commit, function() async.util.scheduler() view:update_files() end) diff --git a/lua/diffview/scene/views/diff/render.lua b/lua/diffview/scene/views/diff/render.lua index ed2ff033..f9fe59c2 100644 --- a/lua/diffview/scene/views/diff/render.lua +++ b/lua/diffview/scene/views/diff/render.lua @@ -155,7 +155,7 @@ return function(panel) ---@type RenderComponent local comp = panel.components.path.comp local line_idx = 0 - local s = utils.path:shorten(utils.path:vim_fnamemodify(panel.git_ctx.toplevel, ":~"), width - 6) + local s = utils.path:shorten(utils.path:vim_fnamemodify(panel.git_ctx.ctx.toplevel, ":~"), width - 6) comp:add_hl("DiffviewFilePanelRootPath", line_idx, 0, #s) comp:add_line(s) @@ -218,7 +218,7 @@ return function(panel) comp = panel.components.info.entries.comp line_idx = 0 for _, arg in ipairs(extra_info) do - local relpath = utils.path:relative(arg, panel.git_ctx.toplevel) + local relpath = utils.path:relative(arg, panel.git_ctx.ctx.toplevel) if relpath == "" then relpath = "." end diff --git a/lua/diffview/scene/views/file_history/file_history_view.lua b/lua/diffview/scene/views/file_history/file_history_view.lua index a9407831..bd659a0c 100644 --- a/lua/diffview/scene/views/file_history/file_history_view.lua +++ b/lua/diffview/scene/views/file_history/file_history_view.lua @@ -38,8 +38,8 @@ function FileHistoryView:init(opt) end function FileHistoryView:post_open() - self.commit_log_panel = CommitLogPanel(self.git_ctx.toplevel, { - name = ("diffview://%s/log/%d/%s"):format(self.git_ctx.dir, self.tabpage, "commit_log"), + self.commit_log_panel = CommitLogPanel(self.git_ctx.ctx.toplevel, { + name = ("diffview://%s/log/%d/%s"):format(self.git_ctx.ctx.dir, self.tabpage, "commit_log"), }) self:init_event_listeners() diff --git a/lua/diffview/scene/views/file_history/listeners.lua b/lua/diffview/scene/views/file_history/listeners.lua index c0951aa8..7e30b5b6 100644 --- a/lua/diffview/scene/views/file_history/listeners.lua +++ b/lua/diffview/scene/views/file_history/listeners.lua @@ -41,8 +41,8 @@ return function(view) if l1 then l1 = tonumber(l1) lpath = utils.path:chain(lpath) - :normalize({ cwd = view.git_ctx.toplevel, absolute = true }) - :relative(view.git_ctx.toplevel) + :normalize({ cwd = view.git_ctx.ctx.toplevel, absolute = true }) + :relative(view.git_ctx.ctx.toplevel) :get() if lpath == cur.path then diff --git a/lua/diffview/scene/views/file_history/render.lua b/lua/diffview/scene/views/file_history/render.lua index 173d967a..a2ef7853 100644 --- a/lua/diffview/scene/views/file_history/render.lua +++ b/lua/diffview/scene/views/file_history/render.lua @@ -196,10 +196,10 @@ local function prepare_panel_cache(panel) cache[panel] = c c.root_path = panel.state.form == "column" and utils.path:shorten( - utils.path:vim_fnamemodify(panel.git_ctx.toplevel, ":~"), + utils.path:vim_fnamemodify(panel.git_ctx.ctx.toplevel, ":~"), panel:get_config().width - 6 ) - or utils.path:vim_fnamemodify(panel.git_ctx.toplevel, ":~") + or utils.path:vim_fnamemodify(panel.git_ctx.ctx.toplevel, ":~") c.args = table.concat(panel.log_options.single_file.path_args, " ") end diff --git a/lua/diffview/vcs/adapters/git/init.lua b/lua/diffview/vcs/adapters/git/init.lua index 0e6d9037..3ecbf3eb 100644 --- a/lua/diffview/vcs/adapters/git/init.lua +++ b/lua/diffview/vcs/adapters/git/init.lua @@ -150,7 +150,7 @@ function GitAdapter:init(opt) self.ctx = { toplevel = opt.toplevel, - git_dir = self:git_dir(opt.toplevel), + dir = self:get_dir(opt.toplevel), path_args = vim.tbl_map(function(pathspec) return pathspec_expand(opt.toplevel, cwd, pathspec) end, opt.path_args or {}) --[[@as string[] ]] @@ -214,7 +214,7 @@ function GitAdapter:get_show_args(args) return utils.vec_join(self:args(), "show", args) end -function GitAdapter:git_dir(path) +function GitAdapter:get_dir(path) local out, code = self:exec_sync({ "rev-parse", "--path-format=absolute", "--git-dir" }, path) if code ~= 0 then return nil @@ -528,11 +528,10 @@ local incremental_line_trace_data = async.void(function(state, callback) end) ----@param toplevel string ---@param path_args string[] ---@param lflags string[] ---@return boolean -function GitAdapter:is_single_file(toplevel, path_args, lflags) +function GitAdapter:is_single_file(path_args, lflags) if lflags and #lflags > 0 then local seen = {} for i, v in ipairs(lflags) do @@ -543,35 +542,34 @@ function GitAdapter:is_single_file(toplevel, path_args, lflags) seen[path] = true end - elseif path_args and toplevel then + elseif path_args and self.ctx.toplevel then return #path_args == 1 and not utils.path:is_dir(path_args[1]) - and #self:exec_sync({ "ls-files", "--", path_args }, toplevel) < 2 + and #self:exec_sync({ "ls-files", "--", path_args }, self.ctx.toplevel) < 2 end return true end ----@param toplevel string ---@param log_opt LogOptions ---@return boolean ok, string description -function GitAdapter:file_history_dry_run(toplevel, log_opt) - local single_file = self:is_single_file(toplevel, log_opt.path_args, log_opt.L) +function GitAdapter:file_history_dry_run(log_opt) + local single_file = self:is_single_file(log_opt.path_args, log_opt.L) local log_options = config.get_log_options(single_file, log_opt) local options = vim.tbl_map(function(v) return vim.fn.shellescape(v) - end, prepare_fh_options(toplevel, log_options, single_file).flags) --[[@as vector ]] + end, prepare_fh_options(self.ctx.toplevel, log_options, single_file).flags) --[[@as vector ]] local description = utils.vec_join( - ("Top-level path: '%s'"):format(utils.path:vim_fnamemodify(toplevel, ":~")), + ("Top-level path: '%s'"):format(utils.path:vim_fnamemodify(self.ctx.toplevel, ":~")), log_options.rev_range and ("Revision range: '%s'"):format(log_options.rev_range) or nil, ("Flags: %s"):format(table.concat(options, " ")) ) log_options = utils.tbl_clone(log_options) --[[@as LogOptions ]] log_options.max_count = 1 - options = prepare_fh_options(toplevel, log_options, single_file).flags + options = prepare_fh_options(self.ctx.toplevel, log_options, single_file).flags local context = "git.utils.file_history_dry_run()" local cmd @@ -585,7 +583,7 @@ function GitAdapter:file_history_dry_run(toplevel, log_opt) end local out, code = self:exec_sync(cmd, { - cwd = toplevel, + cwd = self.ctx.toplevel, debug_opt = { context = context, no_stdout = true, @@ -674,7 +672,7 @@ function GitAdapter:file_history_options(range, paths, args) log_options.path_args = paths - local ok, opt_description = self:file_history_dry_run(self.ctx.toplevel, log_options) + local ok, opt_description = self:file_history_dry_run(log_options) if not ok then utils.info({ @@ -687,7 +685,7 @@ function GitAdapter:file_history_options(range, paths, args) return end - if not self.ctx.git_dir then + if not self.ctx.dir then utils.err( ("Failed to find the git dir for the repository: %s") :format(utils.str_quote(self.ctx.toplevel)) @@ -996,8 +994,8 @@ GitAdapter.flags = { local view = panel.parent.parent return diffview.rev_completion(arg_lead, { accept_range = true, - git_toplevel = view.git_ctx.toplevel, - git_dir = view.git_ctx.dir, + git_toplevel = view.git_ctx.ctx.toplevel, + git_dir = view.git_ctx.ctx.dir, }) end end, @@ -1009,8 +1007,8 @@ GitAdapter.flags = { return function(arg_lead, _, _) local view = panel.parent.parent return utils.vec_join("LOCAL", diffview.rev_completion(arg_lead, { - git_toplevel = view.git_ctx.toplevel, - git_dir = view.git_ctx.dir, + git_toplevel = view.git_ctx.ctx.toplevel, + git_dir = view.git_ctx.ctx.dir, })) end end, From aea0d676006c06caf0c346003e22fe9f2a7aa240 Mon Sep 17 00:00:00 2001 From: zegervdv Date: Sun, 23 Oct 2022 15:04:33 +0000 Subject: [PATCH 35/71] fixup! integrate suggested changes --- lua/diffview/lib.lua | 3 --- lua/diffview/scene/file_entry.lua | 2 +- lua/diffview/scene/views/file_history/render.lua | 1 + lua/diffview/vcs/adapter.lua | 2 +- lua/diffview/vcs/adapters/git/init.lua | 11 +++++------ lua/diffview/vcs/file.lua | 8 ++++---- lua/diffview/vcs/init.lua | 6 ------ lua/diffview/vcs/utils.lua | 2 +- 8 files changed, 13 insertions(+), 22 deletions(-) diff --git a/lua/diffview/lib.lua b/lua/diffview/lib.lua index d7fe9a46..8c8d19c0 100644 --- a/lua/diffview/lib.lua +++ b/lua/diffview/lib.lua @@ -152,9 +152,6 @@ function M.file_history(range, args) return v == "." and "." or pl:relative(v, ".") end, adapter.ctx.path_args) - print('path_args: ', vim.inspect(adapter.ctx.path_args)) - print('rel_paths:', vim.inspect(rel_paths)) - logger.info("[command call] :DiffviewFileHistory " .. table.concat(vim.tbl_flatten({ default_args, args, diff --git a/lua/diffview/scene/file_entry.lua b/lua/diffview/scene/file_entry.lua index 82655b21..487df96b 100644 --- a/lua/diffview/scene/file_entry.lua +++ b/lua/diffview/scene/file_entry.lua @@ -56,7 +56,7 @@ local FileEntry = oop.create_class("FileEntry") function FileEntry:init(opt) self.path = opt.path self.oldpath = opt.oldpath - self.absolute_path = utils.path:absolute(opt.path, opt.adapter.context.toplevel) + self.absolute_path = utils.path:absolute(opt.path, opt.adapter.ctx.toplevel) self.parent_path = utils.path:parent(opt.path) or "" self.basename = utils.path:basename(opt.path) self.extension = utils.path:extension(opt.path) diff --git a/lua/diffview/scene/views/file_history/render.lua b/lua/diffview/scene/views/file_history/render.lua index a2ef7853..af1deee8 100644 --- a/lua/diffview/scene/views/file_history/render.lua +++ b/lua/diffview/scene/views/file_history/render.lua @@ -227,6 +227,7 @@ return { comp:add_line(cached.root_path) local offset + vim.inspect(panel) if panel.single_file then line_idx = line_idx + 1 if #panel.entries > 0 then diff --git a/lua/diffview/vcs/adapter.lua b/lua/diffview/vcs/adapter.lua index 4b77f444..d61a7431 100644 --- a/lua/diffview/vcs/adapter.lua +++ b/lua/diffview/vcs/adapter.lua @@ -20,7 +20,7 @@ function VCSAdapter:init(path) version = {}, version_string = {}, } - self.context = {} + self.ctx = {} end function VCSAdapter:run_bootstrap() diff --git a/lua/diffview/vcs/adapters/git/init.lua b/lua/diffview/vcs/adapters/git/init.lua index 3ecbf3eb..30a5c214 100644 --- a/lua/diffview/vcs/adapters/git/init.lua +++ b/lua/diffview/vcs/adapters/git/init.lua @@ -89,7 +89,6 @@ end ---@return string? local function get_toplevel(path) local out, code = utils.system_list(vim.tbl_flatten({config.get_config().git_cmd, {"rev-parse", "--path-format=absolute", "--show-toplevel"}, path})) - print('Checking', path, ' -> ', vim.inspect(code), ' : ', vim.inspect(out)) if code ~= 0 then return nil end @@ -384,7 +383,7 @@ local incremental_fh_data = async.void(function(state, callback) "--", state.path_args ), - cwd = state.adapter.context.toplevel, + cwd = state.adapter.ctx.toplevel, on_stdout = on_stdout, on_exit = on_exit, }) @@ -402,7 +401,7 @@ local incremental_fh_data = async.void(function(state, callback) "--", state.path_args ), - cwd = state.adapter.context.toplevel, + cwd = state.adapter.ctx.toplevel, on_stdout = on_stdout, on_exit = on_exit, }) @@ -852,7 +851,7 @@ function GitAdapter:file_history_worker(thread, log_opt, opt, co_state, callback local last_status local err_msg - local single_file = self:is_single_file(self.context.toplevel, log_opt.single_file.path_args, log_opt.single_file.L) + local single_file = self:is_single_file(self.ctx.toplevel, log_opt.single_file.path_args, log_opt.single_file.L) ---@type LogOptions local log_options = config.get_log_options( @@ -868,7 +867,7 @@ function GitAdapter:file_history_worker(thread, log_opt, opt, co_state, callback adapter = self, path_args = log_opt.single_file.path_args, log_options = log_options, - prepared_log_opts = prepare_fh_options(self.context.toplevel, log_options, single_file), + prepared_log_opts = prepare_fh_options(self.ctx.toplevel, log_options, single_file), opt = opt, callback = callback, entries = entries, @@ -968,7 +967,7 @@ function GitAdapter:is_binary(path, rev) utils.vec_push(cmd, "--", path) - local _, code = self:exec_sync(cmd, { cwd = self.context.toplevel, silent = true }) + local _, code = self:exec_sync(cmd, { cwd = self.ctx.toplevel, silent = true }) return code ~= 0 end diff --git a/lua/diffview/vcs/file.lua b/lua/diffview/vcs/file.lua index 15e39f66..108e3682 100644 --- a/lua/diffview/vcs/file.lua +++ b/lua/diffview/vcs/file.lua @@ -59,7 +59,7 @@ File.bufopts = { function File:init(opt) self.adapter = opt.adapter self.path = opt.path - self.absolute_path = pl:absolute(opt.path, opt.adapter.context.toplevel) + self.absolute_path = pl:absolute(opt.path, opt.adapter.ctx.toplevel) self.parent_path = pl:parent(opt.path) or "" self.basename = pl:basename(opt.path) self.extension = pl:extension(opt.path) @@ -172,13 +172,13 @@ function File:create_buffer(callback) api.nvim_buf_set_option(self.bufnr, option, value) end - local fullname = pl:join("diffview://", self.adapter.context.dir, context, self.path) + local fullname = pl:join("diffview://", self.adapter.ctx.dir, context, self.path) local ok = pcall(api.nvim_buf_set_name, self.bufnr, fullname) if not ok then -- Resolve name conflict local i = 1 repeat - fullname = pl:join("diffview://", self.adapter.context.dir, context, i, self.path) + fullname = pl:join("diffview://", self.adapter.ctx.dir, context, i, self.path) ok = pcall(api.nvim_buf_set_name, self.bufnr, fullname) i = i + 1 until ok @@ -388,7 +388,7 @@ end ---@type vcs.File File.NULL_FILE = File({ adapter = { - context = { + ctx = { toplevel = "diffview://", } }, diff --git a/lua/diffview/vcs/init.lua b/lua/diffview/vcs/init.lua index b7b52d65..5299c374 100644 --- a/lua/diffview/vcs/init.lua +++ b/lua/diffview/vcs/init.lua @@ -22,17 +22,11 @@ function M.get_adapter(opt) local path_args local top_indicators = opt.top_indicators - print('before', vim.inspect(top_indicators)) - if not top_indicators then path_args, top_indicators = adapter.get_repo_paths(opt.cmd_ctx.path_args, opt.cmd_ctx.cpath) end - print('after', vim.inspect(top_indicators)) - local toplevel = adapter.find_toplevel(top_indicators) - print('toplevel: ', vim.inspect(toplevel)) - if toplevel then -- Create a new adapter instance. Store the resolved path args and the -- cpath in the adapter context. diff --git a/lua/diffview/vcs/utils.lua b/lua/diffview/vcs/utils.lua index 8656fb03..dade6769 100644 --- a/lua/diffview/vcs/utils.lua +++ b/lua/diffview/vcs/utils.lua @@ -109,7 +109,7 @@ M.show = async.wrap(function(adapter, args, callback) local job = Job:new({ command = adapter:bin(), args = adapter:get_show_args(args), - cwd = adapter.context.toplevel, + cwd = adapter.ctx.toplevel, ---@type Job on_exit = async.void(function(j) local context = "vcs.utils.show()" From c80ab1fe1c47cb58e30d8aaffe57dce576e607cd Mon Sep 17 00:00:00 2001 From: zegervdv Date: Sun, 23 Oct 2022 15:15:52 +0000 Subject: [PATCH 36/71] fixup! integrate suggested changes --- lua/diffview/vcs/adapters/git/init.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua/diffview/vcs/adapters/git/init.lua b/lua/diffview/vcs/adapters/git/init.lua index 30a5c214..d2402503 100644 --- a/lua/diffview/vcs/adapters/git/init.lua +++ b/lua/diffview/vcs/adapters/git/init.lua @@ -851,7 +851,7 @@ function GitAdapter:file_history_worker(thread, log_opt, opt, co_state, callback local last_status local err_msg - local single_file = self:is_single_file(self.ctx.toplevel, log_opt.single_file.path_args, log_opt.single_file.L) + local single_file = self:is_single_file(log_opt.single_file.path_args, log_opt.single_file.L) ---@type LogOptions local log_options = config.get_log_options( From 59e150378613c15b501e31cbb2b3456200f21233 Mon Sep 17 00:00:00 2001 From: zegervdv Date: Sun, 23 Oct 2022 15:53:11 +0000 Subject: [PATCH 37/71] Move completion to adapter --- lua/diffview/init.lua | 126 ++++++------------------- lua/diffview/vcs/adapters/git/init.lua | 123 +++++++++++++++++++++++- lua/diffview/vcs/init.lua | 8 +- lua/diffview/vcs/utils.lua | 11 +++ 4 files changed, 167 insertions(+), 101 deletions(-) diff --git a/lua/diffview/init.lua b/lua/diffview/init.lua index 3ed6e4b1..3eb0a121 100644 --- a/lua/diffview/init.lua +++ b/lua/diffview/init.lua @@ -207,91 +207,6 @@ function M.completion(_, cmd_line, cur_pos) end end -function M.rev_candidates(git_toplevel, git_dir) - logger.lvl(1).debug("[completion] Revision candidates requested.") - local top_indicators - if not (git_toplevel and git_dir) then - local cfile = utils.path:vim_expand("%") - top_indicators = utils.vec_join( - vim.bo.buftype == "" - and utils.path:absolute(cfile) - or nil, - utils.path:realpath(".") - ) - end - - if not (git_toplevel and git_dir) then - local err - err, git_toplevel = lib.find_git_toplevel(top_indicators) - - if err then - return {} - end - - ---@cast git_toplevel string - git_dir = vcs.git_dir(git_toplevel) - end - - if not (git_toplevel and git_dir) then - return {} - end - - -- stylua: ignore start - local targets = { - "HEAD", "FETCH_HEAD", "ORIG_HEAD", "MERGE_HEAD", - "REBASE_HEAD", "CHERRY_PICK_HEAD", "REVERT_HEAD" - } - -- stylua: ignore end - - local heads = vim.tbl_filter( - function(name) return vim.tbl_contains(targets, name) end, - vim.tbl_map( - function(v) return utils.path:basename(v) end, - vim.fn.glob(git_dir .. "/*", false, true) - ) - ) - local revs = vcs.exec_sync( - { "rev-parse", "--symbolic", "--branches", "--tags", "--remotes" }, - { cwd = git_toplevel, silent = true } - ) - local stashes = vcs.exec_sync( - { "stash", "list", "--pretty=format:%gd" }, - { cwd = git_toplevel, silent = true } - ) - - return utils.vec_join(heads, revs, stashes) -end - ----@class RevCompletionSpec ----@field accept_range boolean ----@field git_toplevel string ----@field git_dir string - ----Completion for git revisions. ----@param arg_lead string ----@param opt? RevCompletionSpec ----@return string[] -function M.rev_completion(arg_lead, opt) - ---@type RevCompletionSpec - opt = vim.tbl_extend("keep", opt or {}, { accept_range = false }) - local candidates = M.rev_candidates(opt.git_toplevel, opt.git_dir) - local _, range_end = utils.str_match(arg_lead, { - "^(%.%.%.?)()$", - "^(%.%.%.?)()[^.]", - "[^.](%.%.%.?)()$", - "[^.](%.%.%.?)()[^.]", - }) - - if opt.accept_range and range_end then - local range_lead = arg_lead:sub(1, range_end - 1) - candidates = vim.tbl_map(function(v) - return range_lead .. v - end, candidates) - end - - return M.filter_completion(arg_lead, candidates) -end - ---Completion for the git-log `-L` flag. ---@param arg_lead string ---@return string[]? @@ -310,9 +225,9 @@ function M.line_trace_completion(arg_lead) end end -M.completers = { - ---@param ctx CmdLineContext - DiffviewOpen = function(ctx) +---Create a temporary adapter to get relevant completions +---@return VCSAdapter? +function M.get_adapter() local cfile = utils.path:vim_expand("%") local top_indicators = utils.vec_join( vim.bo.buftype == "" @@ -321,13 +236,27 @@ M.completers = { utils.path:realpath(".") ) + print('toplevels = ', vim.inspect(top_indicators)) + -- Create a short-lived adapter such that we can get relevant completion + local err, adapter = vcs.get_adapter({ top_indicators = top_indicators }) + + -- TODO: Can we flag an error here? + if err then + utils.err(err) + return nil + end + + return adapter +end + +M.completers = { + ---@param ctx CmdLineContext + DiffviewOpen = function(ctx) local has_rev_arg = false - local git_dir - local err, git_toplevel = lib.find_git_toplevel(top_indicators) + local adapter = M.get_adapter() - if not err then - ---@cast git_toplevel string - git_dir = vcs.git_dir(git_toplevel) + if not adapter then + return nil end for i = 2, math.min(#ctx.args, ctx.divideridx) do @@ -341,12 +270,12 @@ M.completers = { if ctx.argidx > ctx.divideridx then utils.vec_push(candidates, unpack(vim.fn.getcompletion(ctx.arg_lead, "file", 0))) - elseif not has_rev_arg and ctx.arg_lead:sub(1, 1) ~= "-" and git_dir and git_toplevel then + elseif not has_rev_arg and ctx.arg_lead:sub(1, 1) ~= "-" and adapter.ctx.dir and adapter.ctx.toplevel then utils.vec_push(candidates, unpack(comp_open:get_all_names())) utils.vec_push(candidates, unpack(M.rev_completion(ctx.arg_lead, { accept_range= true, - git_toplevel = git_toplevel, - git_dir = git_dir, + git_toplevel = adapter.ctx.toplevel, + git_dir = adapter.ctx.dir, }))) else utils.vec_push(candidates, unpack( @@ -359,11 +288,12 @@ M.completers = { end, ---@param ctx CmdLineContext DiffviewFileHistory = function(ctx) + local adapter = M.get_adapter() local candidates = {} utils.vec_push(candidates, unpack( - comp_file_history:get_completion(ctx.arg_lead) - or comp_file_history:get_all_names() + adapter.comp_file_history:get_completion(ctx.arg_lead) + or adapter.comp_file_history:get_all_names() )) utils.vec_push(candidates, unpack(vim.fn.getcompletion(ctx.arg_lead, "file", 0))) diff --git a/lua/diffview/vcs/adapters/git/init.lua b/lua/diffview/vcs/adapters/git/init.lua index d2402503..5ddf35eb 100644 --- a/lua/diffview/vcs/adapters/git/init.lua +++ b/lua/diffview/vcs/adapters/git/init.lua @@ -14,7 +14,8 @@ local Rev = require("diffview.vcs.rev").Rev ---@class VCSAdapter local VCSAdapter = require('diffview.vcs.adapter').VCSAdapter local Job = require("plenary.job") -local JobStatus = require('diffview.vcs.utils').JobStatus +local vcs_utils = require('diffview.vcs.utils') +local JobStatus = vcs_utils.JobStatus local Commit = require('diffview.vcs.adapters.git.commit').GitCommit ---@type PathLib @@ -1167,5 +1168,125 @@ for _, list in pairs(GitAdapter.flags) do end end +function M.rev_candidates(git_toplevel, git_dir) + logger.lvl(1).debug("[completion] Revision candidates requested.") + local top_indicators + if not (git_toplevel and git_dir) then + local cfile = utils.path:vim_expand("%") + top_indicators = utils.vec_join( + vim.bo.buftype == "" + and utils.path:absolute(cfile) + or nil, + utils.path:realpath(".") + ) + end + + if not (git_toplevel and git_dir) then + local err + err, git_toplevel = lib.find_git_toplevel(top_indicators) + + if err then + return {} + end + + ---@cast git_toplevel string + git_dir = vcs.git_dir(git_toplevel) + end + + if not (git_toplevel and git_dir) then + return {} + end + + -- stylua: ignore start + local targets = { + "HEAD", "FETCH_HEAD", "ORIG_HEAD", "MERGE_HEAD", + "REBASE_HEAD", "CHERRY_PICK_HEAD", "REVERT_HEAD" + } + -- stylua: ignore end + + local heads = vim.tbl_filter( + function(name) return vim.tbl_contains(targets, name) end, + vim.tbl_map( + function(v) return utils.path:basename(v) end, + vim.fn.glob(git_dir .. "/*", false, true) + ) + ) + local revs = vcs.exec_sync( + { "rev-parse", "--symbolic", "--branches", "--tags", "--remotes" }, + { cwd = git_toplevel, silent = true } + ) + local stashes = vcs.exec_sync( + { "stash", "list", "--pretty=format:%gd" }, + { cwd = git_toplevel, silent = true } + ) + + return utils.vec_join(heads, revs, stashes) +end + +---@class RevCompletionSpec +---@field accept_range boolean +---@field git_toplevel string +---@field git_dir string + +---Completion for git revisions. +---@param arg_lead string +---@param opt? RevCompletionSpec +---@return string[] +function M.rev_completion(arg_lead, opt) + ---@type RevCompletionSpec + opt = vim.tbl_extend("keep", opt or {}, { accept_range = false }) + local candidates = M.rev_candidates(opt.git_toplevel, opt.git_dir) + local _, range_end = utils.str_match(arg_lead, { + "^(%.%.%.?)()$", + "^(%.%.%.?)()[^.]", + "[^.](%.%.%.?)()$", + "[^.](%.%.%.?)()[^.]", + }) + + if opt.accept_range and range_end then + local range_lead = arg_lead:sub(1, range_end - 1) + candidates = vim.tbl_map(function(v) + return range_lead .. v + end, candidates) + end + + return vcs_utils.filter_completion(arg_lead, candidates) +end + +---@type FlagValueMap +GitAdapter.comp_file_history = arg_parser.FlagValueMap() +GitAdapter.comp_file_history:put({ "base" }, function(_, arg_lead) + return utils.vec_join("LOCAL", M.rev_completion(arg_lead)) +end) +GitAdapter.comp_file_history:put({ "range" }, function(_, arg_lead) + return M.rev_completion(arg_lead, { accept_range = true }) +end) +GitAdapter.comp_file_history:put({ "C" }, function(_, arg_lead) + return vim.fn.getcompletion(arg_lead, "dir") +end) +GitAdapter.comp_file_history:put({ "--follow" }) +GitAdapter.comp_file_history:put({ "--first-parent" }) +GitAdapter.comp_file_history:put({ "--show-pulls" }) +GitAdapter.comp_file_history:put({ "--reflog" }) +GitAdapter.comp_file_history:put({ "--all" }) +GitAdapter.comp_file_history:put({ "--merges" }) +GitAdapter.comp_file_history:put({ "--no-merges" }) +GitAdapter.comp_file_history:put({ "--reverse" }) +GitAdapter.comp_file_history:put({ "--max-count", "-n" }, {}) +GitAdapter.comp_file_history:put({ "-L" }, function (_, arg_lead) + return M.line_trace_completion(arg_lead) +end) +GitAdapter.comp_file_history:put({ "--diff-merges" }, { + "off", + "on", + "first-parent", + "separate", + "combined", + "dense-combined", + "remerge", +}) +GitAdapter.comp_file_history:put({ "--author" }, {}) +GitAdapter.comp_file_history:put({ "--grep" }, {}) + M.GitAdapter = GitAdapter return M diff --git a/lua/diffview/vcs/init.lua b/lua/diffview/vcs/init.lua index 5299c374..6b3e67b5 100644 --- a/lua/diffview/vcs/init.lua +++ b/lua/diffview/vcs/init.lua @@ -13,11 +13,15 @@ local M = {} ---@field cpath string? # Cwd path given by the `-C` flag option ---@param opt vcs.init.get_adapter.Opt ----@return err string? ----@return adapter VCSAdapter? +---@return string? err +---@return VCSAdapter? adapter function M.get_adapter(opt) local adapters = { git, hg } + if not opt.cmd_ctx then + opt.cmd_ctx = {} + end + for _, adapter in ipairs(adapters) do local path_args local top_indicators = opt.top_indicators diff --git a/lua/diffview/vcs/utils.lua b/lua/diffview/vcs/utils.lua index dade6769..f299a444 100644 --- a/lua/diffview/vcs/utils.lua +++ b/lua/diffview/vcs/utils.lua @@ -146,5 +146,16 @@ M.show = async.wrap(function(adapter, args, callback) end, 3) + +---@param arg_lead string +---@param items string[] +---@return string[] +function M.filter_completion(arg_lead, items) + arg_lead, _ = vim.pesc(arg_lead) + return vim.tbl_filter(function(item) + return item:match(arg_lead) + end, items) +end + M.JobStatus = JobStatus return M From ade181d888435cb9fc20b55da0d16b8de45e5abf Mon Sep 17 00:00:00 2001 From: zegervdv Date: Sun, 23 Oct 2022 16:16:11 +0000 Subject: [PATCH 38/71] fixup! Move completion to adapter --- lua/diffview/init.lua | 8 +- lua/diffview/vcs/adapter.lua | 8 ++ lua/diffview/vcs/adapters/git/init.lua | 140 +++++++++++++------------ 3 files changed, 86 insertions(+), 70 deletions(-) diff --git a/lua/diffview/init.lua b/lua/diffview/init.lua index 3eb0a121..32876e7e 100644 --- a/lua/diffview/init.lua +++ b/lua/diffview/init.lua @@ -291,9 +291,13 @@ M.completers = { local adapter = M.get_adapter() local candidates = {} + if not adapter then + return nil + end + utils.vec_push(candidates, unpack( - adapter.comp_file_history:get_completion(ctx.arg_lead) - or adapter.comp_file_history:get_all_names() + adapter.comp.file_history:get_completion(ctx.arg_lead) + or adapter.comp.file_history:get_all_names() )) utils.vec_push(candidates, unpack(vim.fn.getcompletion(ctx.arg_lead, "file", 0))) diff --git a/lua/diffview/vcs/adapter.lua b/lua/diffview/vcs/adapter.lua index d61a7431..275141b9 100644 --- a/lua/diffview/vcs/adapter.lua +++ b/lua/diffview/vcs/adapter.lua @@ -1,6 +1,7 @@ local oop = require('diffview.oop') local utils = require('diffview.utils') local logger = require('diffview.logger') +local arg_parser = require('diffview.arg_parser') local M = {} @@ -21,6 +22,10 @@ function VCSAdapter:init(path) version_string = {}, } self.ctx = {} + + self.comp = { + file_history = arg_parser.FlagValueMap() + } end function VCSAdapter:run_bootstrap() @@ -134,6 +139,9 @@ function VCSAdapter:is_binary(path, rev) oop.abstract_stub() end +function VCSAdapter:init_completion() + oop.abstract_stub() +end M.VCSAdapter = VCSAdapter return M diff --git a/lua/diffview/vcs/adapters/git/init.lua b/lua/diffview/vcs/adapters/git/init.lua index 5ddf35eb..62ad6d78 100644 --- a/lua/diffview/vcs/adapters/git/init.lua +++ b/lua/diffview/vcs/adapters/git/init.lua @@ -155,6 +155,8 @@ function GitAdapter:init(opt) return pathspec_expand(opt.toplevel, cwd, pathspec) end, opt.path_args or {}) --[[@as string[] ]] } + + self:init_completion() end function GitAdapter:run_bootstrap() @@ -1168,34 +1170,36 @@ for _, list in pairs(GitAdapter.flags) do end end -function M.rev_candidates(git_toplevel, git_dir) - logger.lvl(1).debug("[completion] Revision candidates requested.") - local top_indicators - if not (git_toplevel and git_dir) then - local cfile = utils.path:vim_expand("%") - top_indicators = utils.vec_join( - vim.bo.buftype == "" - and utils.path:absolute(cfile) - or nil, - utils.path:realpath(".") - ) - end - - if not (git_toplevel and git_dir) then - local err - err, git_toplevel = lib.find_git_toplevel(top_indicators) - - if err then - return {} - end - - ---@cast git_toplevel string - git_dir = vcs.git_dir(git_toplevel) - end +-- Completion - if not (git_toplevel and git_dir) then - return {} - end +function GitAdapter:rev_candidates() + logger.lvl(1).debug("[completion] Revision candidates requested.") + -- local top_indicators + -- if not (git_toplevel and git_dir) then + -- local cfile = utils.path:vim_expand("%") + -- top_indicators = utils.vec_join( + -- vim.bo.buftype == "" + -- and utils.path:absolute(cfile) + -- or nil, + -- utils.path:realpath(".") + -- ) + -- end + + -- if not (git_toplevel and git_dir) then + -- local err + -- err, git_toplevel = lib.find_git_toplevel(top_indicators) + + -- if err then + -- return {} + -- end + + -- ---@cast git_toplevel string + -- git_dir = vcs.git_dir(git_toplevel) + -- end + + -- if not (git_toplevel and git_dir) then + -- return {} + -- end -- stylua: ignore start local targets = { @@ -1208,16 +1212,16 @@ function M.rev_candidates(git_toplevel, git_dir) function(name) return vim.tbl_contains(targets, name) end, vim.tbl_map( function(v) return utils.path:basename(v) end, - vim.fn.glob(git_dir .. "/*", false, true) + vim.fn.glob(self.ctx.dir .. "/*", false, true) ) ) - local revs = vcs.exec_sync( + local revs = self:exec_sync( { "rev-parse", "--symbolic", "--branches", "--tags", "--remotes" }, - { cwd = git_toplevel, silent = true } + { cwd = self.ctx.toplevel, silent = true } ) - local stashes = vcs.exec_sync( + local stashes = self:exec_sync( { "stash", "list", "--pretty=format:%gd" }, - { cwd = git_toplevel, silent = true } + { cwd = self.ctx.toplevel, silent = true } ) return utils.vec_join(heads, revs, stashes) @@ -1232,10 +1236,10 @@ end ---@param arg_lead string ---@param opt? RevCompletionSpec ---@return string[] -function M.rev_completion(arg_lead, opt) +function GitAdapter:rev_completion(arg_lead, opt) ---@type RevCompletionSpec opt = vim.tbl_extend("keep", opt or {}, { accept_range = false }) - local candidates = M.rev_candidates(opt.git_toplevel, opt.git_dir) + local candidates = self:rev_candidates() local _, range_end = utils.str_match(arg_lead, { "^(%.%.%.?)()$", "^(%.%.%.?)()[^.]", @@ -1253,40 +1257,40 @@ function M.rev_completion(arg_lead, opt) return vcs_utils.filter_completion(arg_lead, candidates) end ----@type FlagValueMap -GitAdapter.comp_file_history = arg_parser.FlagValueMap() -GitAdapter.comp_file_history:put({ "base" }, function(_, arg_lead) - return utils.vec_join("LOCAL", M.rev_completion(arg_lead)) -end) -GitAdapter.comp_file_history:put({ "range" }, function(_, arg_lead) - return M.rev_completion(arg_lead, { accept_range = true }) -end) -GitAdapter.comp_file_history:put({ "C" }, function(_, arg_lead) - return vim.fn.getcompletion(arg_lead, "dir") -end) -GitAdapter.comp_file_history:put({ "--follow" }) -GitAdapter.comp_file_history:put({ "--first-parent" }) -GitAdapter.comp_file_history:put({ "--show-pulls" }) -GitAdapter.comp_file_history:put({ "--reflog" }) -GitAdapter.comp_file_history:put({ "--all" }) -GitAdapter.comp_file_history:put({ "--merges" }) -GitAdapter.comp_file_history:put({ "--no-merges" }) -GitAdapter.comp_file_history:put({ "--reverse" }) -GitAdapter.comp_file_history:put({ "--max-count", "-n" }, {}) -GitAdapter.comp_file_history:put({ "-L" }, function (_, arg_lead) - return M.line_trace_completion(arg_lead) -end) -GitAdapter.comp_file_history:put({ "--diff-merges" }, { - "off", - "on", - "first-parent", - "separate", - "combined", - "dense-combined", - "remerge", -}) -GitAdapter.comp_file_history:put({ "--author" }, {}) -GitAdapter.comp_file_history:put({ "--grep" }, {}) +function GitAdapter:init_completion() + self.comp.file_history:put({ "base" }, function(_, arg_lead) + return utils.vec_join("LOCAL", self:rev_completion(arg_lead)) + end) + self.comp.file_history:put({ "range" }, function(_, arg_lead) + return self:rev_completion(arg_lead, { accept_range = true }) + end) + self.comp.file_history:put({ "C" }, function(_, arg_lead) + return vim.fn.getcompletion(arg_lead, "dir") + end) + self.comp.file_history:put({ "--follow" }) + self.comp.file_history:put({ "--first-parent" }) + self.comp.file_history:put({ "--show-pulls" }) + self.comp.file_history:put({ "--reflog" }) + self.comp.file_history:put({ "--all" }) + self.comp.file_history:put({ "--merges" }) + self.comp.file_history:put({ "--no-merges" }) + self.comp.file_history:put({ "--reverse" }) + self.comp.file_history:put({ "--max-count", "-n" }, {}) + self.comp.file_history:put({ "-L" }, function (_, arg_lead) + return M.line_trace_completion(arg_lead) + end) + self.comp.file_history:put({ "--diff-merges" }, { + "off", + "on", + "first-parent", + "separate", + "combined", + "dense-combined", + "remerge", + }) + self.comp.file_history:put({ "--author" }, {}) + self.comp.file_history:put({ "--grep" }, {}) +end M.GitAdapter = GitAdapter return M From c175d9433133ebad3bfff170fb8b509aa3fb74cc Mon Sep 17 00:00:00 2001 From: zegervdv Date: Sun, 23 Oct 2022 16:31:50 +0000 Subject: [PATCH 39/71] fixup! Move completion to adapter --- lua/diffview/init.lua | 77 ++------------------------ lua/diffview/vcs/adapter.lua | 3 +- lua/diffview/vcs/adapters/git/init.lua | 46 +++++++++++---- 3 files changed, 41 insertions(+), 85 deletions(-) diff --git a/lua/diffview/init.lua b/lua/diffview/init.lua index 32876e7e..8d82d02a 100644 --- a/lua/diffview/init.lua +++ b/lua/diffview/init.lua @@ -22,53 +22,6 @@ local api = vim.api local M = {} ----@type FlagValueMap -local comp_open = arg_parser.FlagValueMap() -comp_open:put({ "u", "untracked-files" }, { "true", "normal", "all", "false", "no" }) -comp_open:put({ "cached", "staged" }) -comp_open:put({ "imply-local" }) -comp_open:put({ "C" }, function(_, arg_lead) - return vim.fn.getcompletion(arg_lead, "dir") -end) -comp_open:put({ "selected-file" }, function (_, arg_lead) - return vim.fn.getcompletion(arg_lead, "file") -end) - ----@type FlagValueMap -local comp_file_history = arg_parser.FlagValueMap() -comp_file_history:put({ "base" }, function(_, arg_lead) - return utils.vec_join("LOCAL", M.rev_completion(arg_lead)) -end) -comp_file_history:put({ "range" }, function(_, arg_lead) - return M.rev_completion(arg_lead, { accept_range = true }) -end) -comp_file_history:put({ "C" }, function(_, arg_lead) - return vim.fn.getcompletion(arg_lead, "dir") -end) -comp_file_history:put({ "--follow" }) -comp_file_history:put({ "--first-parent" }) -comp_file_history:put({ "--show-pulls" }) -comp_file_history:put({ "--reflog" }) -comp_file_history:put({ "--all" }) -comp_file_history:put({ "--merges" }) -comp_file_history:put({ "--no-merges" }) -comp_file_history:put({ "--reverse" }) -comp_file_history:put({ "--max-count", "-n" }, {}) -comp_file_history:put({ "-L" }, function (_, arg_lead) - return M.line_trace_completion(arg_lead) -end) -comp_file_history:put({ "--diff-merges" }, { - "off", - "on", - "first-parent", - "separate", - "combined", - "dense-combined", - "remerge", -}) -comp_file_history:put({ "--author" }, {}) -comp_file_history:put({ "--grep" }, {}) - function M.setup(user_config) config.setup(user_config or {}) end @@ -207,24 +160,6 @@ function M.completion(_, cmd_line, cur_pos) end end ----Completion for the git-log `-L` flag. ----@param arg_lead string ----@return string[]? -function M.line_trace_completion(arg_lead) - local range_end = arg_lead:match(".*:()") - - if not range_end then - return - else - local lead = arg_lead:sub(1, range_end - 1) - local path_lead = arg_lead:sub(range_end) - - return vim.tbl_map(function(v) - return lead .. v - end, vim.fn.getcompletion(path_lead, "file")) - end -end - ---Create a temporary adapter to get relevant completions ---@return VCSAdapter? function M.get_adapter() @@ -236,8 +171,6 @@ function M.get_adapter() utils.path:realpath(".") ) - print('toplevels = ', vim.inspect(top_indicators)) - -- Create a short-lived adapter such that we can get relevant completion local err, adapter = vcs.get_adapter({ top_indicators = top_indicators }) -- TODO: Can we flag an error here? @@ -271,16 +204,14 @@ M.completers = { if ctx.argidx > ctx.divideridx then utils.vec_push(candidates, unpack(vim.fn.getcompletion(ctx.arg_lead, "file", 0))) elseif not has_rev_arg and ctx.arg_lead:sub(1, 1) ~= "-" and adapter.ctx.dir and adapter.ctx.toplevel then - utils.vec_push(candidates, unpack(comp_open:get_all_names())) - utils.vec_push(candidates, unpack(M.rev_completion(ctx.arg_lead, { + utils.vec_push(candidates, unpack(adapter.comp.open:get_all_names())) + utils.vec_push(candidates, unpack(adapter:rev_completion(ctx.arg_lead, { accept_range= true, - git_toplevel = adapter.ctx.toplevel, - git_dir = adapter.ctx.dir, }))) else utils.vec_push(candidates, unpack( - comp_open:get_completion(ctx.arg_lead) - or comp_open:get_all_names() + adapter.comp.open:get_completion(ctx.arg_lead) + or adapter.comp.open:get_all_names() )) end diff --git a/lua/diffview/vcs/adapter.lua b/lua/diffview/vcs/adapter.lua index 275141b9..540afef0 100644 --- a/lua/diffview/vcs/adapter.lua +++ b/lua/diffview/vcs/adapter.lua @@ -24,7 +24,8 @@ function VCSAdapter:init(path) self.ctx = {} self.comp = { - file_history = arg_parser.FlagValueMap() + file_history = arg_parser.FlagValueMap(), + open = arg_parser.FlagValueMap(), } end diff --git a/lua/diffview/vcs/adapters/git/init.lua b/lua/diffview/vcs/adapters/git/init.lua index 62ad6d78..7ba5e98c 100644 --- a/lua/diffview/vcs/adapters/git/init.lua +++ b/lua/diffview/vcs/adapters/git/init.lua @@ -14,8 +14,8 @@ local Rev = require("diffview.vcs.rev").Rev ---@class VCSAdapter local VCSAdapter = require('diffview.vcs.adapter').VCSAdapter local Job = require("plenary.job") -local vcs_utils = require('diffview.vcs.utils') -local JobStatus = vcs_utils.JobStatus +local JobStatus = require('diffview.vcs.utils').JobStatus +local diffview = require('diffview') local Commit = require('diffview.vcs.adapters.git.commit').GitCommit ---@type PathLib @@ -994,10 +994,8 @@ GitAdapter.flags = { completion = function(panel) return function(arg_lead, _, _) local view = panel.parent.parent - return diffview.rev_completion(arg_lead, { + return view.git_ctx:rev_completion(arg_lead, { accept_range = true, - git_toplevel = view.git_ctx.ctx.toplevel, - git_dir = view.git_ctx.ctx.dir, }) end end, @@ -1008,10 +1006,7 @@ GitAdapter.flags = { completion = function(panel) return function(arg_lead, _, _) local view = panel.parent.parent - return utils.vec_join("LOCAL", diffview.rev_completion(arg_lead, { - git_toplevel = view.git_ctx.ctx.toplevel, - git_dir = view.git_ctx.ctx.dir, - })) + return utils.vec_join("LOCAL", view.git_ctx:rev_completion(arg_lead, {})) end end, }, @@ -1022,7 +1017,7 @@ GitAdapter.flags = { prompt_fmt = "${label} ", completion = function(_) return function(arg_lead, _, _) - return diffview.line_trace_completion(arg_lead) + return M.line_trace_completion(arg_lead) end end, transform = function(values) @@ -1254,10 +1249,39 @@ function GitAdapter:rev_completion(arg_lead, opt) end, candidates) end - return vcs_utils.filter_completion(arg_lead, candidates) + return diffview.filter_completion(arg_lead, candidates) end +---Completion for the git-log `-L` flag. +---@param arg_lead string +---@return string[]? +function M.line_trace_completion(arg_lead) + local range_end = arg_lead:match(".*:()") + + if not range_end then + return + else + local lead = arg_lead:sub(1, range_end - 1) + local path_lead = arg_lead:sub(range_end) + + return vim.tbl_map(function(v) + return lead .. v + end, vim.fn.getcompletion(path_lead, "file")) + end +end + + function GitAdapter:init_completion() + self.comp.open:put({ "u", "untracked-files" }, { "true", "normal", "all", "false", "no" }) + self.comp.open:put({ "cached", "staged" }) + self.comp.open:put({ "imply-local" }) + self.comp.open:put({ "C" }, function(_, arg_lead) + return vim.fn.getcompletion(arg_lead, "dir") + end) + self.comp.open:put({ "selected-file" }, function (_, arg_lead) + return vim.fn.getcompletion(arg_lead, "file") + end) + self.comp.file_history:put({ "base" }, function(_, arg_lead) return utils.vec_join("LOCAL", self:rev_completion(arg_lead)) end) From 7811846743f71cd4cdf203fc74116d8215d0cbbe Mon Sep 17 00:00:00 2001 From: zegervdv Date: Mon, 24 Oct 2022 18:12:44 +0000 Subject: [PATCH 40/71] refactor: port DiffviewOPen command --- lua/diffview/lib.lua | 98 +++++--------------------- lua/diffview/vcs/adapters/git/init.lua | 34 +++++++++ 2 files changed, 51 insertions(+), 81 deletions(-) diff --git a/lua/diffview/lib.lua b/lua/diffview/lib.lua index 8c8d19c0..ab9c2c46 100644 --- a/lua/diffview/lib.lua +++ b/lua/diffview/lib.lua @@ -25,98 +25,34 @@ function M.diffview_open(args) local default_args = config.get_config().default_args.DiffviewOpen local argo = arg_parser.parse(vim.tbl_flatten({ default_args, args })) local rev_arg = argo.args[1] - local paths = {} logger.info("[command call] :DiffviewOpen " .. table.concat(vim.tbl_flatten({ default_args, args, }), " ")) - for _, path_arg in ipairs(argo.post_args) do - for _, path in ipairs(pl:vim_expand(path_arg, false, true)) do - local magic, pattern = vcs.pathspec_split(path) - pattern = pl:readlink(pattern) or pattern - table.insert(paths, magic .. pattern) - end - end - - local cfile = pl:vim_expand("%") - cfile = pl:readlink(cfile) or cfile - ---@type string - local cpath = argo:get_flag("C", { no_empty = true, expand = true }) - - local top_indicators = { - cpath and pl:realpath(cpath) or ( - vim.bo.buftype == "" - and pl:absolute(cfile) - or nil - ), - } - - if not cpath then - table.insert(top_indicators, pl:realpath(".")) - end - - local err, git_toplevel = M.find_git_toplevel(top_indicators) + local err, adapter = vcs.get_adapter({ + cmd_ctx = { + path_args = argo.args, + cpath = argo:get_flag("C", { no_empty = true, expand = true }), + }, + }) if err then utils.err(err) return end - ---@cast git_toplevel string - logger.lvl(1).s_debug(("Found git top-level: %s"):format(utils.str_quote(git_toplevel))) - - local cwd = cpath or vim.loop.cwd() - paths = vim.tbl_map(function(pathspec) - return vcs.pathspec_expand(git_toplevel, cwd, pathspec) - end, paths) --[[@as string[] ]] - - local left, right = M.parse_revs(git_toplevel, rev_arg, { - cached = argo:get_flag({ "cached", "staged" }), - imply_local = argo:get_flag("imply-local"), - }) - - if not (left and right) then - return - end - - logger.lvl(1).s_debug(("Parsed revs: left = %s, right = %s"):format(left, right)) - - ---@type DiffViewOptions - local options = { - show_untracked = arg_parser.ambiguous_bool( - argo:get_flag({ "u", "untracked-files" }, { plain = true }), - nil, - { "all", "normal", "true" }, - { "no", "false" } - ), - selected_file = argo:get_flag("selected-file", { no_empty = true, expand = true }) - or (vim.bo.buftype == "" and pl:vim_expand("%:p")) - or nil, - } - - local git_ctx = { - toplevel = git_toplevel, - dir = vcs.git_dir(git_toplevel), - } - - if not git_ctx.dir then - utils.err( - ("Failed to find the git dir for the repository: %s") - :format(utils.str_quote(git_ctx.toplevel)) - ) - return - end + local opts = adapter:diffview_options(args) ---@type DiffView local v = DiffView({ - git_ctx = git_ctx, + git_ctx = adapter, rev_arg = rev_arg, - path_args = paths, - left = left, - right = right, - options = options, + path_args = adapter.ctx.path_args, + left = opts.left, + right = opts.right, + options = opts.options, }) if not v:is_valid() then @@ -136,6 +72,11 @@ function M.file_history(range, args) local argo = arg_parser.parse(vim.tbl_flatten({ default_args, args })) local rel_paths + logger.info("[command call] :DiffviewFileHistory " .. table.concat(vim.tbl_flatten({ + default_args, + args, + }), " ")) + local err, adapter = vcs.get_adapter({ cmd_ctx = { path_args = argo.args, @@ -152,11 +93,6 @@ function M.file_history(range, args) return v == "." and "." or pl:relative(v, ".") end, adapter.ctx.path_args) - logger.info("[command call] :DiffviewFileHistory " .. table.concat(vim.tbl_flatten({ - default_args, - args, - }), " ")) - local log_options = adapter:file_history_options(range, rel_paths, args) if log_options == nil then diff --git a/lua/diffview/vcs/adapters/git/init.lua b/lua/diffview/vcs/adapters/git/init.lua index 7ba5e98c..4e01ec31 100644 --- a/lua/diffview/vcs/adapters/git/init.lua +++ b/lua/diffview/vcs/adapters/git/init.lua @@ -950,6 +950,40 @@ function GitAdapter:file_history_worker(thread, log_opt, opt, co_state, callback callback(entries, JobStatus.SUCCESS) end + +function GitAdapter:diffview_options(args) + local default_args = config.get_config().default_args.DiffviewOpen + local argo = arg_parser.parse(vim.tbl_flatten({ default_args, args })) + local rev_arg = argo.args[1] + + local left, right = M.parse_revs(self.ctx.toplevel, rev_arg, { + cached = argo:get_flag({ "cached", "staged" }), + imply_local = argo:get_flag("imply-local"), + }) + + if not (left and right) then + return + end + + logger.lvl(1).s_debug(("Parsed revs: left = %s, right = %s"):format(left, right)) + + ---@type DiffViewOptions + local options = { + show_untracked = arg_parser.ambiguous_bool( + argo:get_flag({ "u", "untracked-files" }, { plain = true }), + nil, + { "all", "normal", "true" }, + { "no", "false" } + ), + selected_file = argo:get_flag("selected-file", { no_empty = true, expand = true }) + or (vim.bo.buftype == "" and pl:vim_expand("%:p")) + or nil, + } + + return {left = left, right = right, options = options} +end + + ---Strange trick to check if a file is binary using only git. ---@param path string ---@param rev Rev From 8a5d5941ad89225824b9bccbb0af4b5678833c81 Mon Sep 17 00:00:00 2001 From: zegervdv Date: Tue, 25 Oct 2022 11:56:29 +0000 Subject: [PATCH 41/71] fixup! refactor: port DiffviewOPen command --- lua/diffview/lib.lua | 91 -------------- lua/diffview/vcs/adapters/git/init.lua | 155 +++++++++++++++++++++++- lua/diffview/vcs/adapters/git/utils.lua | 65 ---------- 3 files changed, 154 insertions(+), 157 deletions(-) diff --git a/lua/diffview/lib.lua b/lua/diffview/lib.lua index ab9c2c46..9e618d61 100644 --- a/lua/diffview/lib.lua +++ b/lua/diffview/lib.lua @@ -115,97 +115,6 @@ function M.file_history(range, args) return v end ----Parse a given rev arg. ----@param git_toplevel string ----@param rev_arg string ----@param opt table ----@return Rev? left ----@return Rev? right -function M.parse_revs(git_toplevel, rev_arg, opt) - ---@type Rev? - local left - ---@type Rev? - local right - - local head = vcs.head_rev(git_toplevel) - ---@cast head Rev - - if not rev_arg then - if opt.cached then - left = head or Rev.new_null_tree() - right = Rev(RevType.STAGE, 0) - else - left = Rev(RevType.STAGE, 0) - right = Rev(RevType.LOCAL) - end - elseif rev_arg:match("%.%.%.") then - left, right = vcs.symmetric_diff_revs(git_toplevel, rev_arg) - if not (left or right) then - return - elseif opt.imply_local then - ---@cast left Rev - ---@cast right Rev - left, right = M.imply_local(left, right, head) - end - else - local rev_strings, code, stderr = vcs.exec_sync( - { "rev-parse", "--revs-only", rev_arg }, git_toplevel - ) - if code ~= 0 then - utils.err(utils.vec_join( - ("Failed to parse rev %s!"):format(utils.str_quote(rev_arg)), - "Git output: ", - stderr - )) - return - elseif #rev_strings == 0 then - utils.err("Bad revision: " .. utils.str_quote(rev_arg)) - return - end - - local is_range = vcs.is_rev_arg_range(rev_arg) - - if is_range then - local right_hash = rev_strings[1]:gsub("^%^", "") - right = Rev(RevType.COMMIT, right_hash) - if #rev_strings > 1 then - local left_hash = rev_strings[2]:gsub("^%^", "") - left = Rev(RevType.COMMIT, left_hash) - else - left = Rev.new_null_tree() - end - - if opt.imply_local then - left, right = M.imply_local(left, right, head) - end - else - local hash = rev_strings[1]:gsub("^%^", "") - left = Rev(RevType.COMMIT, hash) - if opt.cached then - right = Rev(RevType.STAGE, 0) - else - right = Rev(RevType.LOCAL) - end - end - end - - return left, right -end - ----@param left Rev ----@param right Rev ----@param head Rev ----@return Rev, Rev -function M.imply_local(left, right, head) - if left.commit == head.commit then - left = Rev(RevType.LOCAL) - end - if right.commit == head.commit then - right = Rev(RevType.LOCAL) - end - return left, right -end - ---@param view View function M.add_view(view) table.insert(M.views, view) diff --git a/lua/diffview/vcs/adapters/git/init.lua b/lua/diffview/vcs/adapters/git/init.lua index 4e01ec31..fdcfa1f1 100644 --- a/lua/diffview/vcs/adapters/git/init.lua +++ b/lua/diffview/vcs/adapters/git/init.lua @@ -956,7 +956,7 @@ function GitAdapter:diffview_options(args) local argo = arg_parser.parse(vim.tbl_flatten({ default_args, args })) local rev_arg = argo.args[1] - local left, right = M.parse_revs(self.ctx.toplevel, rev_arg, { + local left, right = self:parse_revs(rev_arg, { cached = argo:get_flag({ "cached", "staged" }), imply_local = argo:get_flag("imply-local"), }) @@ -983,6 +983,159 @@ function GitAdapter:diffview_options(args) return {left = left, right = right, options = options} end +---@return Rev? +function GitAdapter:head_rev() + local out, code = self:exec_sync( + { "rev-parse", "HEAD", "--" }, + { cwd = self.ctx.toplevel, retry_on_empty = 2 } + ) + + if code ~= 0 then + return + end + + local s = vim.trim(out[1]):gsub("^%^", "") + + return Rev(RevType.COMMIT, s, true) +end + +---Parse two endpoint, commit revs from a symmetric difference notated rev arg. +---@param rev_arg string +---@return Rev? left The left rev. +---@return Rev? right The right rev. +function GitAdapter:symmetric_diff_revs(rev_arg) + local r1 = rev_arg:match("(.+)%.%.%.") or "HEAD" + local r2 = rev_arg:match("%.%.%.(.+)") or "HEAD" + local out, code, stderr + + local function err() + utils.err(utils.vec_join( + ("Failed to parse rev '%s'!"):format(rev_arg), + "Git output: ", + stderr + )) + end + + out, code, stderr = self:exec_sync({ "merge-base", r1, r2 }, self.ctx.toplevel) + if code ~= 0 then + return err() + end + local left_hash = out[1]:gsub("^%^", "") + + out, code, stderr = self:exec_sync({ "rev-parse", "--revs-only", r2 }, self.ctx.toplevel) + if code ~= 0 then + return err() + end + local right_hash = out[1]:gsub("^%^", "") + + return Rev(RevType.COMMIT, left_hash), Rev(RevType.COMMIT, right_hash) +end + +---Determine whether a rev arg is a range. +---@param rev_arg string +---@return boolean +function GitAdapter:is_rev_arg_range(rev_arg) + return utils.str_match(rev_arg, { + "^%.%.%.?$", + "^%.%.%.?[^.]", + "[^.]%.%.%.?$", + "[^.]%.%.%.?[^.]", + "^.-%^@", + "^.-%^!", + "^.-%^%-%d?", + }) ~= nil +end + +---Parse a given rev arg. +---@param git_toplevel string +---@param rev_arg string +---@param opt table +---@return Rev? left +---@return Rev? right +function GitAdapter:parse_revs(rev_arg, opt) + ---@type Rev? + local left + ---@type Rev? + local right + + local head = self:head_rev() + ---@cast head Rev + + if not rev_arg then + if opt.cached then + left = head or Rev.new_null_tree() + right = Rev(RevType.STAGE, 0) + else + left = Rev(RevType.STAGE, 0) + right = Rev(RevType.LOCAL) + end + elseif rev_arg:match("%.%.%.") then + left, right = self:symmetric_diff_revs(rev_arg) + if not (left or right) then + return + elseif opt.imply_local then + ---@cast left Rev + ---@cast right Rev + left, right = self:imply_local(left, right, head) + end + else + local rev_strings, code, stderr = self:exec_sync( + { "rev-parse", "--revs-only", rev_arg }, self.ctx.toplevel + ) + if code ~= 0 then + utils.err(utils.vec_join( + ("Failed to parse rev %s!"):format(utils.str_quote(rev_arg)), + "Git output: ", + stderr + )) + return + elseif #rev_strings == 0 then + utils.err("Bad revision: " .. utils.str_quote(rev_arg)) + return + end + + local is_range = self:is_rev_arg_range(rev_arg) + + if is_range then + local right_hash = rev_strings[1]:gsub("^%^", "") + right = Rev(RevType.COMMIT, right_hash) + if #rev_strings > 1 then + local left_hash = rev_strings[2]:gsub("^%^", "") + left = Rev(RevType.COMMIT, left_hash) + else + left = Rev.new_null_tree() + end + + if opt.imply_local then + left, right = self:imply_local(left, right, head) + end + else + local hash = rev_strings[1]:gsub("^%^", "") + left = Rev(RevType.COMMIT, hash) + if opt.cached then + right = Rev(RevType.STAGE, 0) + else + right = Rev(RevType.LOCAL) + end + end + end + + return left, right +end + +---@param left Rev +---@param right Rev +---@param head Rev +---@return Rev, Rev +function GitAdapter:imply_local(left, right, head) + if left.commit == head.commit then + left = Rev(RevType.LOCAL) + end + if right.commit == head.commit then + right = Rev(RevType.LOCAL) + end + return left, right +end ---Strange trick to check if a file is binary using only git. ---@param path string diff --git a/lua/diffview/vcs/adapters/git/utils.lua b/lua/diffview/vcs/adapters/git/utils.lua index 2154354a..df977389 100644 --- a/lua/diffview/vcs/adapters/git/utils.lua +++ b/lua/diffview/vcs/adapters/git/utils.lua @@ -805,21 +805,6 @@ local function parse_fh_line_trace_data(state) return true end ----Determine whether a rev arg is a range. ----@param rev_arg string ----@return boolean -function M.is_rev_arg_range(rev_arg) - return utils.str_match(rev_arg, { - "^%.%.%.?$", - "^%.%.%.?[^.]", - "[^.]%.%.%.?$", - "[^.]%.%.%.?[^.]", - "^.-%^@", - "^.-%^!", - "^.-%^%-%d?", - }) ~= nil -end - ---Convert revs to git rev args. ---@param left Rev ---@param right Rev @@ -856,56 +841,6 @@ function M.rev_to_pretty_string(left, right) return nil end ----@param toplevel string ----@return Rev? -function M.head_rev(toplevel) - local out, code = M.exec_sync( - { "rev-parse", "HEAD", "--" }, - { cwd = toplevel, retry_on_empty = 2 } - ) - - if code ~= 0 then - return - end - - local s = vim.trim(out[1]):gsub("^%^", "") - - return Rev(RevType.COMMIT, s, true) -end - ----Parse two endpoint, commit revs from a symmetric difference notated rev arg. ----@param toplevel string ----@param rev_arg string ----@return Rev? left The left rev. ----@return Rev? right The right rev. -function M.symmetric_diff_revs(toplevel, rev_arg) - local r1 = rev_arg:match("(.+)%.%.%.") or "HEAD" - local r2 = rev_arg:match("%.%.%.(.+)") or "HEAD" - local out, code, stderr - - local function err() - utils.err(utils.vec_join( - ("Failed to parse rev '%s'!"):format(rev_arg), - "Git output: ", - stderr - )) - end - - out, code, stderr = M.exec_sync({ "merge-base", r1, r2 }, toplevel) - if code ~= 0 then - return err() - end - local left_hash = out[1]:gsub("^%^", "") - - out, code, stderr = M.exec_sync({ "rev-parse", "--revs-only", r2 }, toplevel) - if code ~= 0 then - return err() - end - local right_hash = out[1]:gsub("^%^", "") - - return Rev(RevType.COMMIT, left_hash), Rev(RevType.COMMIT, right_hash) -end - ---Derive the top-level path of the working tree of the given path. ---@param path string ---@return string? From 415d0b805249f117d89520473a2d13dfc4fafb02 Mon Sep 17 00:00:00 2001 From: zegervdv Date: Tue, 25 Oct 2022 12:27:29 +0000 Subject: [PATCH 42/71] fixup! refactor: port DiffviewOPen command --- lua/diffview/scene/views/diff/diff_view.lua | 2 +- lua/diffview/vcs/adapters/git/init.lua | 52 ++++ lua/diffview/vcs/adapters/git/utils.lua | 314 -------------------- lua/diffview/vcs/utils.lua | 277 +++++++++++++++++ 4 files changed, 330 insertions(+), 315 deletions(-) diff --git a/lua/diffview/scene/views/diff/diff_view.lua b/lua/diffview/scene/views/diff/diff_view.lua index fa0358ba..b8eecd02 100644 --- a/lua/diffview/scene/views/diff/diff_view.lua +++ b/lua/diffview/scene/views/diff/diff_view.lua @@ -13,7 +13,7 @@ local RevType = lazy.access("diffview.vcs.rev", "RevType") ---@type RevType|Lazy local StandardView = lazy.access("diffview.scene.views.standard.standard_view", "StandardView") ---@type StandardView|LazyModule local async = lazy.require("plenary.async") ---@module "plenary.async" local debounce = lazy.require("diffview.debounce") ---@module "diffview.debounce" -local vcs = lazy.require("diffview.vcs") ---@module "diffview.vcs" +local vcs = lazy.require("diffview.vcs.utils") ---@module "diffview.vcs" local logger = lazy.require("diffview.logger") ---@module "diffview.logger" local utils = lazy.require("diffview.utils") ---@module "diffview.utils" local config = lazy.require("diffview.config") ---@module "diffview.config" diff --git a/lua/diffview/vcs/adapters/git/init.lua b/lua/diffview/vcs/adapters/git/init.lua index fdcfa1f1..1cba4273 100644 --- a/lua/diffview/vcs/adapters/git/init.lua +++ b/lua/diffview/vcs/adapters/git/init.lua @@ -1137,6 +1137,57 @@ function GitAdapter:imply_local(left, right, head) return left, right end +---Convert revs to git rev args. +---@param left Rev +---@param right Rev +---@return string[] +function GitAdapter:rev_to_args(left, right) + assert( + not (left.type == RevType.LOCAL and right.type == RevType.LOCAL), + "Can't diff LOCAL against LOCAL!" + ) + + if left.type == RevType.COMMIT and right.type == RevType.COMMIT then + return { left.commit .. ".." .. right.commit } + elseif left.type == RevType.STAGE and right.type == RevType.LOCAL then + return {} + elseif left.type == RevType.COMMIT and right.type == RevType.STAGE then + return { "--cached", left.commit } + else + return { left.commit } + end +end + +function GitAdapter:get_namestat_args(args) + return utils.vec_join(self:args(), "diff", "--ignore-submodules", "--name-status", args) +end + +function GitAdapter:get_numstat_args(args) + return utils.vec_join(self:args(), "diff", "--ignore-submodules", "--numstat", args) +end + +function GitAdapter:get_files_args(args) + return utils.vec_join(self:args(), "ls-files", "--others", "--exclude-standard", args) +end + +---Check if status for untracked files is disabled for a given git repo. +---@return boolean +function GitAdapter:show_untracked() + local out = self:exec_sync( + { "config", "status.showUntrackedFiles" }, + { cwd = self.ctx.toplevel, silent = true } + ) + return vim.trim(out[1] or "") ~= "no" +end + +---Check if any of the given revs are LOCAL. +---@param left Rev +---@param right Rev +---@return boolean +function GitAdapter:has_local(left, right) + return left.type == RevType.LOCAL or right.type == RevType.LOCAL +end + ---Strange trick to check if a file is binary using only git. ---@param path string ---@param rev Rev @@ -1161,6 +1212,7 @@ function GitAdapter:is_binary(path, rev) return code ~= 0 end + GitAdapter.flags = { ---@type FlagOption[] switches = { diff --git a/lua/diffview/vcs/adapters/git/utils.lua b/lua/diffview/vcs/adapters/git/utils.lua index df977389..3b31f8b4 100644 --- a/lua/diffview/vcs/adapters/git/utils.lua +++ b/lua/diffview/vcs/adapters/git/utils.lua @@ -15,281 +15,6 @@ local api = vim.api local M = {} ----@param ctx GitContext ----@param left Rev ----@param right Rev ----@param args string[] ----@param kind git.FileKind ----@param opt git.utils.LayoutOpt ----@param callback function -local tracked_files = async.wrap(function(ctx, left, right, args, kind, opt, callback) - ---@type FileEntry[] - local files = {} - ---@type FileEntry[] - local conflicts = {} - ---@type CountDownLatch - local latch = CountDownLatch(2) - local debug_opt = { - context = "git.utils>tracked_files()", - func = "s_debug", - debug_level = 1, - no_stdout = true, - } - - ---@param job Job - local function on_exit(job) - utils.handle_job(job, { debug_opt = debug_opt }) - latch:count_down() - end - - local namestat_job = Job:new({ - command = git_bin(), - args = utils.vec_join(git_args(), "diff", "--ignore-submodules", "--name-status", args), - cwd = ctx.toplevel, - on_exit = on_exit - }) - local numstat_job = Job:new({ - command = git_bin(), - args = utils.vec_join(git_args(), "diff", "--ignore-submodules", "--numstat", args), - cwd = ctx.toplevel, - on_exit = on_exit - }) - - namestat_job:start() - numstat_job:start() - latch:await() - local out_status - if not (#namestat_job:result() == #numstat_job:result()) then - out_status = ensure_output(2, { namestat_job, numstat_job }, "git.utils>tracked_files()") - end - - if out_status == JobStatus.ERROR or not (namestat_job.code == 0 and numstat_job.code == 0) then - callback(utils.vec_join(namestat_job:stderr_result(), numstat_job:stderr_result()), nil) - return - end - - local numstat_out = numstat_job:result() - local data = {} - local conflict_map = {} - - for i, s in ipairs(namestat_job:result()) do - local status = s:sub(1, 1):gsub("%s", " ") - local name = s:match("[%a%s][^%s]*\t(.*)") - local oldname - - if name:match("\t") ~= nil then - oldname = name:match("(.*)\t") - name = name:gsub("^.*\t", "") - end - - local stats = { - additions = tonumber(numstat_out[i]:match("^%d+")), - deletions = tonumber(numstat_out[i]:match("^%d+%s+(%d+)")), - } - - if not stats.additions or not stats.deletions then - stats = nil - end - - if not (status == "U" and kind == "staged") then - table.insert(data, { - status = status, - name = name, - oldname = oldname, - stats = stats, - }) - end - - if status == "U" then - conflict_map[name] = data[#data] - end - end - - if kind == "working" and next(conflict_map) then - data = vim.tbl_filter(function(v) - return not conflict_map[v.name] - end, data) - - for _, v in pairs(conflict_map) do - table.insert(conflicts, FileEntry.with_layout(opt.merge_layout, { - git_ctx = ctx, - path = v.name, - oldpath = v.oldname, - status = "U", - kind = "conflicting", - rev_ours = Rev(RevType.STAGE, 2), - rev_main = Rev(RevType.LOCAL), - rev_theirs = Rev(RevType.STAGE, 3), - rev_base = Rev(RevType.STAGE, 1), - })) - end - end - - for _, v in ipairs(data) do - table.insert(files, FileEntry.for_d2(opt.default_layout, { - git_ctx = ctx, - path = v.name, - oldpath = v.oldname, - status = v.status, - stats = v.stats, - kind = kind, - rev_a = left, - rev_b = right, - })) - end - - callback(nil, files, conflicts) -end, 7) - ----@param ctx GitContext ----@param left Rev ----@param right Rev ----@param opt git.utils.LayoutOpt ----@param callback function -local untracked_files = async.wrap(function(ctx, left, right, opt, callback) - Job:new({ - command = git_bin(), - args = utils.vec_join(git_args(), "ls-files", "--others", "--exclude-standard" ), - cwd = ctx.toplevel, - ---@type Job - on_exit = function(j) - utils.handle_job(j, { - debug_opt = { - context = "git.utils>untracked_files()", - func = "s_debug", - debug_level = 1, - no_stdout = true, - } - }) - - if j.code ~= 0 then - callback(j:stderr_result() or {}, nil) - return - end - - local files = {} - for _, s in ipairs(j:result()) do - table.insert(files, FileEntry.for_d2(opt.default_layout, { - git_ctx = ctx, - path = s, - status = "?", - kind = "working", - rev_a = left, - rev_b = right, - })) - end - callback(nil, files) - end - }):start() -end, 5) - ----Get a list of files modified between two revs. ----@param ctx GitContext ----@param left Rev ----@param right Rev ----@param path_args string[]|nil ----@param dv_opt DiffViewOptions ----@param opt git.utils.LayoutOpt ----@param callback function ----@return string[]? err ----@return FileDict? -M.diff_file_list = async.wrap(function(ctx, left, right, path_args, dv_opt, opt, callback) - ---@type FileDict - local files = FileDict() - ---@type CountDownLatch - local latch = CountDownLatch(2) - local rev_args = M.rev_to_args(left, right) - local errors = {} - - tracked_files( - ctx, - left, - right, - utils.vec_join( - rev_args, - "--", - path_args - ), - "working", - opt, - function (err, tfiles, tconflicts) - if err then - errors[#errors+1] = err - utils.err("Failed to get git status for tracked files!", true) - latch:count_down() - return - end - - files:set_working(tfiles) - files:set_conflicting(tconflicts) - local show_untracked = dv_opt.show_untracked - - if show_untracked == nil then - show_untracked = M.show_untracked(ctx.toplevel) - end - - if not (show_untracked and M.has_local(left, right)) then - latch:count_down() - return - end - - ---@diagnostic disable-next-line: redefined-local - local err, ufiles = untracked_files(ctx, left, right, opt) - if err then - errors[#errors+1] = err - utils.err("Failed to get git status for untracked files!", true) - latch:count_down() - else - files:set_working(utils.vec_join(files.working, ufiles)) - - utils.merge_sort(files.working, function(a, b) - return a.path:lower() < b.path:lower() - end) - latch:count_down() - end - end - ) - - if not (left.type == RevType.STAGE and right.type == RevType.LOCAL) then - latch:count_down() - else - local left_rev = M.head_rev(ctx.toplevel) or Rev.new_null_tree() - local right_rev = Rev(RevType.STAGE, 0) - tracked_files( - ctx, - left_rev, - right_rev, - utils.vec_join( - "--cached", - left_rev.commit, - "--", - path_args - ), - "staged", - opt, - function(err, tfiles) - if err then - errors[#errors+1] = err - utils.err("Failed to get git status for staged files!", true) - latch:count_down() - return - end - files:set_staged(tfiles) - latch:count_down() - end - ) - end - - latch:await() - if #errors > 0 then - callback(utils.vec_join(unpack(errors)), nil) - return - end - - files:update_file_trees() - callback(nil, files) -end, 7) - ---@class git.utils.PreparedLogOpts ---@field rev_range string ---@field base Rev @@ -805,26 +530,6 @@ local function parse_fh_line_trace_data(state) return true end ----Convert revs to git rev args. ----@param left Rev ----@param right Rev ----@return string[] -function M.rev_to_args(left, right) - assert( - not (left.type == RevType.LOCAL and right.type == RevType.LOCAL), - "Can't diff LOCAL against LOCAL!" - ) - - if left.type == RevType.COMMIT and right.type == RevType.COMMIT then - return { left.commit .. ".." .. right.commit } - elseif left.type == RevType.STAGE and right.type == RevType.LOCAL then - return {} - elseif left.type == RevType.COMMIT and right.type == RevType.STAGE then - return { "--cached", left.commit } - else - return { left.commit } - end -end ---Convert revs to string representation. ---@param left Rev @@ -1034,14 +739,6 @@ function M.pathspec_modify(pathspec, mods) return magic .. utils.path:vim_fnamemodify(pattern, mods) end ----Check if any of the given revs are LOCAL. ----@param left Rev ----@param right Rev ----@return boolean -function M.has_local(left, right) - return left.type == RevType.LOCAL or right.type == RevType.LOCAL -end - ---Strange trick to check if a file is binary using only git. ---@param toplevel string ---@param path string @@ -1067,17 +764,6 @@ function M.is_binary(toplevel, path, rev) return code ~= 0 end ----Check if status for untracked files is disabled for a given git repo. ----@param toplevel string ----@return boolean -function M.show_untracked(toplevel) - local out = M.exec_sync( - { "config", "status.showUntrackedFiles" }, - { cwd = toplevel, silent = true } - ) - return vim.trim(out[1] or "") ~= "no" -end - ---Get the diff status letter for a file for a given rev. ---@param toplevel string ---@param path string diff --git a/lua/diffview/vcs/utils.lua b/lua/diffview/vcs/utils.lua index f299a444..b0dd7238 100644 --- a/lua/diffview/vcs/utils.lua +++ b/lua/diffview/vcs/utils.lua @@ -1,6 +1,8 @@ +local CountDownLatch = require("diffview.control").CountDownLatch local utils = require("diffview.utils") local async = require("plenary.async") local logger = require("diffview.logger") +local FileDict = require("diffview.vcs.file_dict").FileDict local Job = require("plenary.job") local Semaphore = require('diffview.control').Semaphore @@ -146,6 +148,281 @@ M.show = async.wrap(function(adapter, args, callback) end, 3) +---@param adapter VCSAdapter +---@param left Rev +---@param right Rev +---@param args string[] +---@param kind git.FileKind +---@param opt git.utils.LayoutOpt +---@param callback function +local tracked_files = async.wrap(function(adapter, left, right, args, kind, opt, callback) + ---@type FileEntry[] + local files = {} + ---@type FileEntry[] + local conflicts = {} + ---@type CountDownLatch + local latch = CountDownLatch(2) + local debug_opt = { + context = "git.utils>tracked_files()", + func = "s_debug", + debug_level = 1, + no_stdout = true, + } + + ---@param job Job + local function on_exit(job) + utils.handle_job(job, { debug_opt = debug_opt }) + latch:count_down() + end + + local namestat_job = Job:new({ + command = adapter:bin(), + args = adapter:get_namestat_args(args), + cwd = adapter.ctx.toplevel, + on_exit = on_exit + }) + local numstat_job = Job:new({ + command = adapter:bin(), + args = adapter:get_numstat_args(args), + cwd = adapter.ctx.toplevel, + on_exit = on_exit + }) + + namestat_job:start() + numstat_job:start() + latch:await() + local out_status + if not (#namestat_job:result() == #numstat_job:result()) then + out_status = ensure_output(2, { namestat_job, numstat_job }, "git.utils>tracked_files()") + end + + if out_status == JobStatus.ERROR or not (namestat_job.code == 0 and numstat_job.code == 0) then + callback(utils.vec_join(namestat_job:stderr_result(), numstat_job:stderr_result()), nil) + return + end + + local numstat_out = numstat_job:result() + local data = {} + local conflict_map = {} + + for i, s in ipairs(namestat_job:result()) do + local status = s:sub(1, 1):gsub("%s", " ") + local name = s:match("[%a%s][^%s]*\t(.*)") + local oldname + + if name:match("\t") ~= nil then + oldname = name:match("(.*)\t") + name = name:gsub("^.*\t", "") + end + + local stats = { + additions = tonumber(numstat_out[i]:match("^%d+")), + deletions = tonumber(numstat_out[i]:match("^%d+%s+(%d+)")), + } + + if not stats.additions or not stats.deletions then + stats = nil + end + + if not (status == "U" and kind == "staged") then + table.insert(data, { + status = status, + name = name, + oldname = oldname, + stats = stats, + }) + end + + if status == "U" then + conflict_map[name] = data[#data] + end + end + + if kind == "working" and next(conflict_map) then + data = vim.tbl_filter(function(v) + return not conflict_map[v.name] + end, data) + + for _, v in pairs(conflict_map) do + table.insert(conflicts, FileEntry.with_layout(opt.merge_layout, { + git_ctx = adapter, + path = v.name, + oldpath = v.oldname, + status = "U", + kind = "conflicting", + rev_ours = Rev(RevType.STAGE, 2), + rev_main = Rev(RevType.LOCAL), + rev_theirs = Rev(RevType.STAGE, 3), + rev_base = Rev(RevType.STAGE, 1), + })) + end + end + + for _, v in ipairs(data) do + table.insert(files, FileEntry.for_d2(opt.default_layout, { + git_ctx = adapter, + path = v.name, + oldpath = v.oldname, + status = v.status, + stats = v.stats, + kind = kind, + rev_a = left, + rev_b = right, + })) + end + + callback(nil, files, conflicts) +end, 7) + +---@param adapter VCSAdapter +---@param left Rev +---@param right Rev +---@param opt git.utils.LayoutOpt +---@param callback function +local untracked_files = async.wrap(function(adapter, left, right, opt, callback) + Job:new({ + command = adapter:bin(), + args = adapter:get_files_args(), + cwd = adapter.ctx.toplevel, + ---@type Job + on_exit = function(j) + utils.handle_job(j, { + debug_opt = { + context = "git.utils>untracked_files()", + func = "s_debug", + debug_level = 1, + no_stdout = true, + } + }) + + if j.code ~= 0 then + callback(j:stderr_result() or {}, nil) + return + end + + local files = {} + for _, s in ipairs(j:result()) do + table.insert(files, FileEntry.for_d2(opt.default_layout, { + git_ctx = adapter, + path = s, + status = "?", + kind = "working", + rev_a = left, + rev_b = right, + })) + end + callback(nil, files) + end + }):start() +end, 5) + +---Get a list of files modified between two revs. +---@param adapter VCSAdapter +---@param left Rev +---@param right Rev +---@param path_args string[]|nil +---@param dv_opt DiffViewOptions +---@param opt git.utils.LayoutOpt +---@param callback function +---@return string[]? err +---@return FileDict? +M.diff_file_list = async.wrap(function(adapter, left, right, path_args, dv_opt, opt, callback) + ---@type FileDict + local files = FileDict() + ---@type CountDownLatch + local latch = CountDownLatch(2) + local rev_args = adapter:rev_to_args(left, right) + local errors = {} + + tracked_files( + adapter, + left, + right, + utils.vec_join( + rev_args, + "--", + path_args + ), + "working", + opt, + function (err, tfiles, tconflicts) + if err then + errors[#errors+1] = err + utils.err("Failed to get git status for tracked files!", true) + latch:count_down() + return + end + + files:set_working(tfiles) + files:set_conflicting(tconflicts) + local show_untracked = dv_opt.show_untracked + + if show_untracked == nil then + show_untracked = adapter:show_untracked() + end + + if not (show_untracked and adapter:has_local(left, right)) then + latch:count_down() + return + end + + ---@diagnostic disable-next-line: redefined-local + local err, ufiles = untracked_files(adapter, left, right, opt) + if err then + errors[#errors+1] = err + utils.err("Failed to get git status for untracked files!", true) + latch:count_down() + else + files:set_working(utils.vec_join(files.working, ufiles)) + + utils.merge_sort(files.working, function(a, b) + return a.path:lower() < b.path:lower() + end) + latch:count_down() + end + end + ) + + if not (left.type == RevType.STAGE and right.type == RevType.LOCAL) then + latch:count_down() + else + local left_rev = M.head_rev(ctx.toplevel) or Rev.new_null_tree() + local right_rev = Rev(RevType.STAGE, 0) + tracked_files( + adapter, + left_rev, + right_rev, + utils.vec_join( + "--cached", + left_rev.commit, + "--", + path_args + ), + "staged", + opt, + function(err, tfiles) + if err then + errors[#errors+1] = err + utils.err("Failed to get git status for staged files!", true) + latch:count_down() + return + end + files:set_staged(tfiles) + latch:count_down() + end + ) + end + + latch:await() + if #errors > 0 then + callback(utils.vec_join(unpack(errors)), nil) + return + end + + files:update_file_trees() + callback(nil, files) +end, 7) + ---@param arg_lead string ---@param items string[] From 30328ffedc044019f53e0f2e6bf53680f5d648af Mon Sep 17 00:00:00 2001 From: zegervdv Date: Wed, 26 Oct 2022 17:39:00 +0000 Subject: [PATCH 43/71] fixup! fixup! refactor: port DiffviewOPen command --- lua/diffview/vcs/utils.lua | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lua/diffview/vcs/utils.lua b/lua/diffview/vcs/utils.lua index b0dd7238..587075d0 100644 --- a/lua/diffview/vcs/utils.lua +++ b/lua/diffview/vcs/utils.lua @@ -3,6 +3,8 @@ local utils = require("diffview.utils") local async = require("plenary.async") local logger = require("diffview.logger") local FileDict = require("diffview.vcs.file_dict").FileDict +local FileEntry = require("diffview.scene.file_entry").FileEntry +local RevType = require("diffview.vcs.rev").RevType local Job = require("plenary.job") local Semaphore = require('diffview.control').Semaphore @@ -245,7 +247,7 @@ local tracked_files = async.wrap(function(adapter, left, right, args, kind, opt, for _, v in pairs(conflict_map) do table.insert(conflicts, FileEntry.with_layout(opt.merge_layout, { - git_ctx = adapter, + adapter = adapter, path = v.name, oldpath = v.oldname, status = "U", @@ -260,7 +262,7 @@ local tracked_files = async.wrap(function(adapter, left, right, args, kind, opt, for _, v in ipairs(data) do table.insert(files, FileEntry.for_d2(opt.default_layout, { - git_ctx = adapter, + adapter = adapter, path = v.name, oldpath = v.oldname, status = v.status, @@ -303,7 +305,7 @@ local untracked_files = async.wrap(function(adapter, left, right, opt, callback) local files = {} for _, s in ipairs(j:result()) do table.insert(files, FileEntry.for_d2(opt.default_layout, { - git_ctx = adapter, + adapter = adapter, path = s, status = "?", kind = "working", From 24331ec2934d24059a39c7d1a35f877de750bfaf Mon Sep 17 00:00:00 2001 From: zegervdv Date: Wed, 26 Oct 2022 18:03:33 +0000 Subject: [PATCH 44/71] fixup! fixup! refactor: port DiffviewOPen command --- lua/diffview/scene/views/diff/diff_view.lua | 2 +- lua/diffview/vcs/adapters/git/init.lua | 55 ++++++---- lua/diffview/vcs/adapters/git/rev.lua | 107 ++++++++++++++++++++ lua/diffview/vcs/adapters/git/utils.lua | 15 --- lua/diffview/vcs/rev.lua | 49 ++------- lua/diffview/vcs/utils.lua | 12 +-- 6 files changed, 159 insertions(+), 81 deletions(-) create mode 100644 lua/diffview/vcs/adapters/git/rev.lua diff --git a/lua/diffview/scene/views/diff/diff_view.lua b/lua/diffview/scene/views/diff/diff_view.lua index b8eecd02..c2687bb6 100644 --- a/lua/diffview/scene/views/diff/diff_view.lua +++ b/lua/diffview/scene/views/diff/diff_view.lua @@ -63,7 +63,7 @@ function DiffView:init(opt) self.git_ctx, self.files, self.path_args, - self.rev_arg or vcs.rev_to_pretty_string(self.left, self.right) + self.rev_arg or self.git_ctx:rev_to_pretty_string(self.left, self.right) ), }) diff --git a/lua/diffview/vcs/adapters/git/init.lua b/lua/diffview/vcs/adapters/git/init.lua index 1cba4273..e5191a72 100644 --- a/lua/diffview/vcs/adapters/git/init.lua +++ b/lua/diffview/vcs/adapters/git/init.lua @@ -10,7 +10,7 @@ local FileEntry = require("diffview.scene.file_entry").FileEntry local Diff2Hor = require("diffview.scene.layouts.diff_2_hor").Diff2Hor local LogEntry = require("diffview.vcs.log_entry").LogEntry local RevType = require("diffview.vcs.rev").RevType -local Rev = require("diffview.vcs.rev").Rev +local GitRev = require("diffview.vcs.adapters.git.rev").GitRev ---@class VCSAdapter local VCSAdapter = require('diffview.vcs.adapter').VCSAdapter local Job = require("plenary.job") @@ -26,6 +26,8 @@ local M = {} ---@class GitAdapter : VCSAdapter local GitAdapter = oop.create_class('GitAdapter', VCSAdapter) +GitAdapter.Rev = GitRev + ---@return string, string local function pathspec_split(pathspec) local magic = pathspec:match("^:[/!^]*:?") or pathspec:match("^:%b()") or "" @@ -258,14 +260,14 @@ local function prepare_fh_options(toplevel, log_options, single_file) if log_options.base then if log_options.base == "LOCAL" then - base = Rev(RevType.LOCAL) + base = GitRev(RevType.LOCAL) else local ok, out = M.verify_rev_arg(toplevel, log_options.base) if not ok then utils.warn(("Bad base revision, ignoring: %s"):format(utils.str_quote(log_options.base))) else - base = Rev(RevType.COMMIT, out[1]) + base = GitRev(RevType.COMMIT, out[1]) end end end @@ -817,8 +819,8 @@ local function parse_fh_data(state) stats = stats, kind = "working", commit = state.commit, - rev_a = cur.left_hash and Rev(RevType.COMMIT, cur.left_hash) or Rev.new_null_tree(), - rev_b = state.prepared_log_opts.base or Rev(RevType.COMMIT, cur.right_hash), + rev_a = cur.left_hash and GitRev(RevType.COMMIT, cur.left_hash) or GitRev.new_null_tree(), + rev_b = state.prepared_log_opts.base or GitRev(RevType.COMMIT, cur.right_hash), })) end @@ -996,7 +998,7 @@ function GitAdapter:head_rev() local s = vim.trim(out[1]):gsub("^%^", "") - return Rev(RevType.COMMIT, s, true) + return GitRev(RevType.COMMIT, s, true) end ---Parse two endpoint, commit revs from a symmetric difference notated rev arg. @@ -1028,7 +1030,7 @@ function GitAdapter:symmetric_diff_revs(rev_arg) end local right_hash = out[1]:gsub("^%^", "") - return Rev(RevType.COMMIT, left_hash), Rev(RevType.COMMIT, right_hash) + return GitRev(RevType.COMMIT, left_hash), GitRev(RevType.COMMIT, right_hash) end ---Determine whether a rev arg is a range. @@ -1063,11 +1065,11 @@ function GitAdapter:parse_revs(rev_arg, opt) if not rev_arg then if opt.cached then - left = head or Rev.new_null_tree() - right = Rev(RevType.STAGE, 0) + left = head or GitRev.new_null_tree() + right = GitRev(RevType.STAGE, 0) else - left = Rev(RevType.STAGE, 0) - right = Rev(RevType.LOCAL) + left = GitRev(RevType.STAGE, 0) + right = GitRev(RevType.LOCAL) end elseif rev_arg:match("%.%.%.") then left, right = self:symmetric_diff_revs(rev_arg) @@ -1098,12 +1100,12 @@ function GitAdapter:parse_revs(rev_arg, opt) if is_range then local right_hash = rev_strings[1]:gsub("^%^", "") - right = Rev(RevType.COMMIT, right_hash) + right = GitRev(RevType.COMMIT, right_hash) if #rev_strings > 1 then local left_hash = rev_strings[2]:gsub("^%^", "") - left = Rev(RevType.COMMIT, left_hash) + left = GitRev(RevType.COMMIT, left_hash) else - left = Rev.new_null_tree() + left = GitRev.new_null_tree() end if opt.imply_local then @@ -1111,11 +1113,11 @@ function GitAdapter:parse_revs(rev_arg, opt) end else local hash = rev_strings[1]:gsub("^%^", "") - left = Rev(RevType.COMMIT, hash) + left = GitRev(RevType.COMMIT, hash) if opt.cached then - right = Rev(RevType.STAGE, 0) + right = GitRev(RevType.STAGE, 0) else - right = Rev(RevType.LOCAL) + right = GitRev(RevType.LOCAL) end end end @@ -1129,10 +1131,10 @@ end ---@return Rev, Rev function GitAdapter:imply_local(left, right, head) if left.commit == head.commit then - left = Rev(RevType.LOCAL) + left = GitRev(RevType.LOCAL) end if right.commit == head.commit then - right = Rev(RevType.LOCAL) + right = GitRev(RevType.LOCAL) end return left, right end @@ -1180,6 +1182,21 @@ function GitAdapter:show_untracked() return vim.trim(out[1] or "") ~= "no" end +---Convert revs to string representation. +---@param left Rev +---@param right Rev +---@return string|nil +function GitAdapter:rev_to_pretty_string(left, right) + if left.track_head and right.type == RevType.LOCAL then + return nil + elseif left.commit and right.type == RevType.LOCAL then + return left:abbrev() + elseif left.commit and right.commit then + return left:abbrev() .. ".." .. right:abbrev() + end + return nil +end + ---Check if any of the given revs are LOCAL. ---@param left Rev ---@param right Rev diff --git a/lua/diffview/vcs/adapters/git/rev.lua b/lua/diffview/vcs/adapters/git/rev.lua new file mode 100644 index 00000000..ffcecb75 --- /dev/null +++ b/lua/diffview/vcs/adapters/git/rev.lua @@ -0,0 +1,107 @@ +local oop = require("diffview.oop") +local Rev = require('diffview.vcs.rev').Rev +local RevType = require('diffview.vcs.rev').RevType + +local M = {} + +---@class GitRev : Rev +local GitRev = oop.create_class("GitRev", Rev) + +-- The special SHA for git's empty tree. +GitRev.NULL_TREE_SHA = "4b825dc642cb6eb9a060e54bf8d69288fbee4904" + +---GitRev constructor +---@param rev_type RevType +---@param revision string|number Commit SHA or stage number. +---@param track_head? boolean +function GitRev:init(rev_type, revision, track_head) + local t = type(revision) + + assert( + revision == nil or t == "string" or t == "number", + "'revision' must be one of: nil, string, number!" + ) + if t == "string" then + assert(revision ~= "", "'revision' cannot be an empty string!") + elseif t == "number" then + assert( + revision >= 0 and revision <= 3, + "'revision' must be a valid stage number ([0-3])!" + ) + end + + t = type(track_head) + assert(t == "boolean" or t == "nil", "'track_head' must be of type boolean!") + + self.type = rev_type + self.track_head = track_head or false + + if type(revision) == "string" then + ---@cast revision string + self.commit = revision + elseif type(revision) == "number" then + ---@cast revision number + self.stage = revision + end +end + +---@param name string +---@param adapter? GitAdapter +---@return Rev? +function GitRev.from_name(name, adapter) + local out, code = adapter:exec_sync({ "rev-parse", "--revs-only", name }, adapter.ctx.toplevel) + + if code ~= 0 then + return + end + + return GitRev(RevType.COMMIT, out[1]:gsub("^%^", "")) +end + +---@param adapter VCSAdapter +---@return Rev? +function GitRev.earliest_commit(adapter) + local out, code = adapter:exec_sync({ + "rev-list", "--max-parents=0", "--first-parent", "HEAD" + }, adapter.ctx.toplevel) + + if code ~= 0 then + return + end + + return GitRev(RevType.COMMIT, ({ out[1]:gsub("^%^", "") })[1]) +end + +function GitRev:object_name() + if self.type == RevType.COMMIT then + return self.commit + elseif self.type == RevType.STAGE then + return ":" .. self.stage + end +end + +---Determine if this rev is currently the head. +---@param adapter GitAdapter +---@return boolean? +function Rev:is_head(adapter) + if not self.type == RevType.COMMIT then + return false + end + + local out, code = adapter:exec_sync({ "rev-parse", "HEAD", "--" }, adapter.ctx.toplevel) + + if code ~= 0 or not (out[2] ~= nil or out[1] and out[1] ~= "") then + return + end + + return self.commit == vim.trim(out[1]):gsub("^%^", "") +end + +---Create a new commit rev with the special empty tree SHA. +---@return Rev +function GitRev.new_null_tree() + return GitRev(RevType.COMMIT, GitRev.NULL_TREE_SHA) +end + +M.GitRev = GitRev +return M diff --git a/lua/diffview/vcs/adapters/git/utils.lua b/lua/diffview/vcs/adapters/git/utils.lua index 3b31f8b4..0ef921bb 100644 --- a/lua/diffview/vcs/adapters/git/utils.lua +++ b/lua/diffview/vcs/adapters/git/utils.lua @@ -531,21 +531,6 @@ local function parse_fh_line_trace_data(state) end ----Convert revs to string representation. ----@param left Rev ----@param right Rev ----@return string|nil -function M.rev_to_pretty_string(left, right) - if left.track_head and right.type == RevType.LOCAL then - return nil - elseif left.commit and right.type == RevType.LOCAL then - return left:abbrev() - elseif left.commit and right.commit then - return left:abbrev() .. ".." .. right:abbrev() - end - return nil -end - ---Derive the top-level path of the working tree of the given path. ---@param path string ---@return string? diff --git a/lua/diffview/vcs/rev.lua b/lua/diffview/vcs/rev.lua index 7e6ba8b7..46ad3108 100644 --- a/lua/diffview/vcs/rev.lua +++ b/lua/diffview/vcs/rev.lua @@ -29,9 +29,6 @@ local RevType = oop.enum({ ---@field track_head boolean If true, indicates that the rev should be updated when HEAD changes. local Rev = oop.create_class("Rev") --- The special SHA for git's empty tree. -Rev.NULL_TREE_SHA = "4b825dc642cb6eb9a060e54bf8d69288fbee4904" - ---Rev constructor ---@param rev_type RevType ---@param revision string|number Commit SHA or stage number. @@ -78,38 +75,20 @@ function Rev:__tostring() end ---@param name string ----@param git_toplevel? string +---@param adapter? VCSAdapter ---@return Rev? -function Rev.from_name(name, git_toplevel) - local out, code = git.exec_sync({ "rev-parse", "--revs-only", name }, git_toplevel) - - if code ~= 0 then - return - end - - return Rev(RevType.COMMIT, out[1]:gsub("^%^", "")) +function Rev.from_name(name, adapter) + oop.abstract_stub() end ---@param git_toplevel string ---@return Rev? function Rev.earliest_commit(git_toplevel) - local out, code = git.exec_sync({ - "rev-list", "--max-parents=0", "--first-parent", "HEAD" - }, git_toplevel) - - if code ~= 0 then - return - end - - return Rev(RevType.COMMIT, ({ out[1]:gsub("^%^", "") })[1]) + oop.abstract_stub() end function Rev:object_name() - if self.type == RevType.COMMIT then - return self.commit - elseif self.type == RevType.STAGE then - return ":" .. self.stage - end + oop.abstract_stub() end ---Get an abbreviated commit SHA. Returns `nil` if this Rev is not a commit. @@ -123,26 +102,16 @@ function Rev:abbrev(length) end ---Determine if this rev is currently the head. ----@param git_toplevel string +---@param adapter VCSAdapter ---@return boolean? -function Rev:is_head(git_toplevel) - if not self.type == RevType.COMMIT then - return false - end - - local out, code = git.exec_sync({ "rev-parse", "HEAD", "--" }, git_toplevel) - - if code ~= 0 or not (out[2] ~= nil or out[1] and out[1] ~= "") then - return - end - - return self.commit == vim.trim(out[1]):gsub("^%^", "") +function Rev:is_head(adapter) + oop.abstract_stub() end ---Create a new commit rev with the special empty tree SHA. ---@return Rev function Rev.new_null_tree() - return Rev(RevType.COMMIT, Rev.NULL_TREE_SHA) + return nil end M.RevType = RevType diff --git a/lua/diffview/vcs/utils.lua b/lua/diffview/vcs/utils.lua index 587075d0..18ce5e56 100644 --- a/lua/diffview/vcs/utils.lua +++ b/lua/diffview/vcs/utils.lua @@ -252,10 +252,10 @@ local tracked_files = async.wrap(function(adapter, left, right, args, kind, opt, oldpath = v.oldname, status = "U", kind = "conflicting", - rev_ours = Rev(RevType.STAGE, 2), - rev_main = Rev(RevType.LOCAL), - rev_theirs = Rev(RevType.STAGE, 3), - rev_base = Rev(RevType.STAGE, 1), + rev_ours = adapter.Rev(RevType.STAGE, 2), + rev_main = adapter.Rev(RevType.LOCAL), + rev_theirs = adapter.Rev(RevType.STAGE, 3), + rev_base = adapter.Rev(RevType.STAGE, 1), })) end end @@ -388,8 +388,8 @@ M.diff_file_list = async.wrap(function(adapter, left, right, path_args, dv_opt, if not (left.type == RevType.STAGE and right.type == RevType.LOCAL) then latch:count_down() else - local left_rev = M.head_rev(ctx.toplevel) or Rev.new_null_tree() - local right_rev = Rev(RevType.STAGE, 0) + local left_rev = adapter:head_rev() or adapter.Rev.new_null_tree() + local right_rev = adapter.Rev(RevType.STAGE, 0) tracked_files( adapter, left_rev, From 94c41526cf9cf76cf912c5603296a835c756ba7a Mon Sep 17 00:00:00 2001 From: zegervdv Date: Sat, 29 Oct 2022 09:16:48 +0000 Subject: [PATCH 45/71] fixup! fixup! fixup! refactor: port DiffviewOPen command --- lua/diffview/lib.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua/diffview/lib.lua b/lua/diffview/lib.lua index 9e618d61..893b02ed 100644 --- a/lua/diffview/lib.lua +++ b/lua/diffview/lib.lua @@ -33,7 +33,7 @@ function M.diffview_open(args) local err, adapter = vcs.get_adapter({ cmd_ctx = { - path_args = argo.args, + path_args = argo.post_args, cpath = argo:get_flag("C", { no_empty = true, expand = true }), }, }) From c3faf1997813450901f7ec757a29d73e27bae0ad Mon Sep 17 00:00:00 2001 From: zegervdv Date: Sat, 29 Oct 2022 09:21:35 +0000 Subject: [PATCH 46/71] refactor: rename git_ctx to adapter --- lua/diffview/api/views/diff/diff_view.lua | 12 ++++----- lua/diffview/lib.lua | 4 +-- lua/diffview/scene/file_entry.lua | 20 +++++++------- lua/diffview/scene/layouts/diff_1.lua | 10 +++---- lua/diffview/scene/layouts/diff_3.lua | 2 +- lua/diffview/scene/views/diff/diff_view.lua | 26 +++++++++---------- lua/diffview/scene/views/diff/file_panel.lua | 8 +++--- lua/diffview/scene/views/diff/listeners.lua | 12 ++++----- lua/diffview/scene/views/diff/render.lua | 4 +-- .../views/file_history/file_history_panel.lua | 10 +++---- .../views/file_history/file_history_view.lua | 10 +++---- .../scene/views/file_history/listeners.lua | 6 ++--- .../scene/views/file_history/render.lua | 4 +-- lua/diffview/vcs/adapters/git/init.lua | 4 +-- lua/diffview/vcs/adapters/git/utils.lua | 16 ++++++------ 15 files changed, 74 insertions(+), 74 deletions(-) diff --git a/lua/diffview/api/views/diff/diff_view.lua b/lua/diffview/api/views/diff/diff_view.lua index 49dc9606..430b5c1b 100644 --- a/lua/diffview/api/views/diff/diff_view.lua +++ b/lua/diffview/api/views/diff/diff_view.lua @@ -54,15 +54,15 @@ function CDiffView:init(opt) self.fetch_files = opt.update_files self.get_file_data = opt.get_file_data - local git_ctx = { + local adapter = { toplevel = opt.git_root, dir = git_dir, } CDiffView:super().init(self, vim.tbl_extend("force", opt, { - git_ctx = git_ctx, + adapter = adapter, panel = FilePanel( - git_ctx, + adapter, self.files, self.path_args, self.rev_arg or vcs.rev_to_pretty_string(opt.left, opt.right) @@ -128,7 +128,7 @@ function CDiffView:create_file_entries(files) { kind = "staged", files = files.staged or {}, - left = vcs.head_rev(self.git_ctx.ctx.toplevel), + left = vcs.head_rev(self.adapter.ctx.toplevel), right = Rev(RevType.STAGE, 0), }, } @@ -139,7 +139,7 @@ function CDiffView:create_file_entries(files) for _, file_data in ipairs(v.files) do if v.kind == "conflicting" then table.insert(entries[v.kind], FileEntry.with_layout(CDiffView.get_default_merge_layout(), { - git_ctx = self.git_ctx, + adapter = self.adapter, path = file_data.path, oldpath = file_data.oldpath, status = "U", @@ -152,7 +152,7 @@ function CDiffView:create_file_entries(files) })) else table.insert(entries[v.kind], FileEntry.for_d2(CDiffView.get_default_diff2(), { - git_ctx = self.git_ctx, + adapter = self.adapter, path = file_data.path, oldpath = file_data.oldpath, status = file_data.status, diff --git a/lua/diffview/lib.lua b/lua/diffview/lib.lua index 893b02ed..5314f583 100644 --- a/lua/diffview/lib.lua +++ b/lua/diffview/lib.lua @@ -47,7 +47,7 @@ function M.diffview_open(args) ---@type DiffView local v = DiffView({ - git_ctx = adapter, + adapter = adapter, rev_arg = rev_arg, path_args = adapter.ctx.path_args, left = opts.left, @@ -101,7 +101,7 @@ function M.file_history(range, args) ---@type FileHistoryView local v = FileHistoryView({ - git_ctx = adapter, + adapter = adapter, log_options = log_options, }) diff --git a/lua/diffview/scene/file_entry.lua b/lua/diffview/scene/file_entry.lua index 487df96b..c9e9ce17 100644 --- a/lua/diffview/scene/file_entry.lua +++ b/lua/diffview/scene/file_entry.lua @@ -166,11 +166,11 @@ function FileEntry:convert_layout(target_layout) )) end ----@param git_ctx GitContext +---@param adapter VCSAdapter ---@param stat? table -function FileEntry:validate_stage_buffers(git_ctx, stat) - stat = stat or utils.path:stat(utils.path:join(git_ctx.ctx.dir, "index")) - local cached_stat = utils.tbl_access(fstat_cache, { git_ctx.ctx.toplevel, "index" }) +function FileEntry:validate_stage_buffers(adapter, stat) + stat = stat or utils.path:stat(utils.path:join(adapter.ctx.dir, "index")) + local cached_stat = utils.tbl_access(fstat_cache, { adapter.ctx.toplevel, "index" }) if stat then if not cached_stat or cached_stat.mtime < stat.mtime.sec then @@ -184,16 +184,16 @@ function FileEntry:validate_stage_buffers(git_ctx, stat) end ---@static ----@param git_ctx GitContext -function FileEntry.update_index_stat(git_ctx, stat) - stat = stat or utils.path:stat(utils.path:join(git_ctx.ctx.toplevel, "index")) +---@param adapter VCSAdapter +function FileEntry.update_index_stat(adapter, stat) + stat = stat or utils.path:stat(utils.path:join(adapter.ctx.toplevel, "index")) if stat then - if not fstat_cache[git_ctx.ctx.toplevel] then - fstat_cache[git_ctx.ctx.toplevel] = {} + if not fstat_cache[adapter.ctx.toplevel] then + fstat_cache[adapter.ctx.toplevel] = {} end - fstat_cache[git_ctx.ctx.toplevel].index = { + fstat_cache[adapter.ctx.toplevel].index = { mtime = stat.mtime.sec, } end diff --git a/lua/diffview/scene/layouts/diff_1.lua b/lua/diffview/scene/layouts/diff_1.lua index b2b15193..4eded996 100644 --- a/lua/diffview/scene/layouts/diff_1.lua +++ b/lua/diffview/scene/layouts/diff_1.lua @@ -90,7 +90,7 @@ function Diff1:to_diff3(layout) return layout({ a = File({ - git_ctx = main.git_ctx, + adapter = main.adapter, path = main.path, kind = main.kind, commit = main.commit, @@ -100,7 +100,7 @@ function Diff1:to_diff3(layout) }), b = self.a.file, c = File({ - git_ctx = main.git_ctx, + adapter = main.adapter, path = main.path, kind = main.kind, commit = main.commit, @@ -119,7 +119,7 @@ function Diff1:to_diff4(layout) return layout({ a = File({ - git_ctx = main.git_ctx, + adapter = main.adapter, path = main.path, kind = main.kind, commit = main.commit, @@ -129,7 +129,7 @@ function Diff1:to_diff4(layout) }), b = self.a.file, c = File({ - git_ctx = main.git_ctx, + adapter = main.adapter, path = main.path, kind = main.kind, commit = main.commit, @@ -138,7 +138,7 @@ function Diff1:to_diff4(layout) nulled = false, -- FIXME }), d = File({ - git_ctx = main.git_ctx, + adapter = main.adapter, path = main.path, kind = main.kind, commit = main.commit, diff --git a/lua/diffview/scene/layouts/diff_3.lua b/lua/diffview/scene/layouts/diff_3.lua index 80c06927..5a58921a 100644 --- a/lua/diffview/scene/layouts/diff_3.lua +++ b/lua/diffview/scene/layouts/diff_3.lua @@ -96,7 +96,7 @@ function Diff3:to_diff4(layout) b = self.b.file, c = self.c.file, d = File({ - git_ctx = main.git_ctx, + adapter = main.adapter, path = main.path, kind = main.kind, commit = main.commit, diff --git a/lua/diffview/scene/views/diff/diff_view.lua b/lua/diffview/scene/views/diff/diff_view.lua index c2687bb6..e5ac73c1 100644 --- a/lua/diffview/scene/views/diff/diff_view.lua +++ b/lua/diffview/scene/views/diff/diff_view.lua @@ -26,7 +26,7 @@ local M = {} ---@field selected_file? string Path to the preferred initially selected file. ---@class DiffView : StandardView ----@field git_ctx GitContext +---@field adapter VCSAdapter ---@field rev_arg string ---@field path_args string[] ---@field left Rev @@ -45,7 +45,7 @@ local DiffView = oop.create_class("DiffView", StandardView.__get()) function DiffView:init(opt) self.valid = false self.files = FileDict() - self.git_ctx = opt.git_ctx + self.adapter = opt.adapter self.path_args = opt.path_args self.rev_arg = opt.rev_arg self.left = opt.left @@ -55,15 +55,15 @@ function DiffView:init(opt) self.options.selected_file = self.options.selected_file and utils.path:chain(self.options.selected_file) :absolute() - :relative(self.git_ctx.ctx.toplevel) + :relative(self.adapter.ctx.toplevel) :get() DiffView:super().init(self, { panel = FilePanel( - self.git_ctx, + self.adapter, self.files, self.path_args, - self.rev_arg or self.git_ctx:rev_to_pretty_string(self.left, self.right) + self.rev_arg or self.adapter:rev_to_pretty_string(self.left, self.right) ), }) @@ -119,14 +119,14 @@ end function DiffView:post_open() vim.cmd("redraw") - self.commit_log_panel = CommitLogPanel(self.git_ctx.ctx.toplevel, { - name = ("diffview://%s/log/%d/%s"):format(self.git_ctx.ctx.dir, self.tabpage, "commit_log"), + self.commit_log_panel = CommitLogPanel(self.adapter.ctx.toplevel, { + name = ("diffview://%s/log/%d/%s"):format(self.adapter.ctx.dir, self.tabpage, "commit_log"), }) if config.get_config().watch_index then self.watcher = vim.loop.new_fs_poll() ---@diagnostic disable-next-line: unused-local - self.watcher:start(self.git_ctx.ctx.dir .. "/index", 1000, function(err, prev, cur) + self.watcher:start(self.adapter.ctx.dir .. "/index", 1000, function(err, prev, cur) if not err then vim.schedule(function() if self:is_cur_tabpage() then @@ -276,7 +276,7 @@ end ---@return FileDict DiffView.get_updated_files = async.wrap(function(self, callback) vcs.diff_file_list( - self.git_ctx, + self.adapter, self.left, self.right, self.path_args, @@ -302,7 +302,7 @@ DiffView.update_files = debounce.debounce_trailing(100, true, vim.schedule_wrap( -- If left is tracking HEAD and right is LOCAL: Update HEAD rev. local new_head if self.left.track_head and self.right.type == RevType.LOCAL then - new_head = vcs.head_rev(self.git_ctx.ctx.toplevel) + new_head = vcs.head_rev(self.adapter.ctx.toplevel) if new_head and self.left.commit ~= new_head.commit then self.left = new_head else @@ -311,7 +311,7 @@ DiffView.update_files = debounce.debounce_trailing(100, true, vim.schedule_wrap( perf:lap("updated head rev") end - local index_stat = utils.path:stat(utils.path:join(self.git_ctx.ctx.dir, "index")) + local index_stat = utils.path:stat(utils.path:join(self.adapter.ctx.dir, "index")) local last_winid = api.nvim_get_current_win() self:get_updated_files(function(err, new_files) if err then @@ -355,7 +355,7 @@ DiffView.update_files = debounce.debounce_trailing(100, true, vim.schedule_wrap( end v.cur_files[ai].status = v.new_files[bi].status - v.cur_files[ai]:validate_stage_buffers(self.git_ctx, index_stat) + v.cur_files[ai]:validate_stage_buffers(self.adapter, index_stat) if new_head then v.cur_files[ai]:update_heads(new_head) @@ -402,7 +402,7 @@ DiffView.update_files = debounce.debounce_trailing(100, true, vim.schedule_wrap( end perf:lap("updated file list") - FileEntry.update_index_stat(self.git_ctx, index_stat) + FileEntry.update_index_stat(self.adapter, index_stat) self.files:update_file_trees() self.panel:update_components() self.panel:render() diff --git a/lua/diffview/scene/views/diff/file_panel.lua b/lua/diffview/scene/views/diff/file_panel.lua index c5c2a6bf..4dc38921 100644 --- a/lua/diffview/scene/views/diff/file_panel.lua +++ b/lua/diffview/scene/views/diff/file_panel.lua @@ -11,7 +11,7 @@ local M = {} ---@field folder_statuses "never"|"only_folded"|"always" ---@class FilePanel : Panel ----@field git_ctx GitContext +---@field adapter VCSAdapter ---@field files FileDict ---@field path_args string[] ---@field rev_pretty_name string|nil @@ -42,16 +42,16 @@ FilePanel.bufopts = vim.tbl_extend("force", Panel.bufopts, { }) ---FilePanel constructor. ----@param git_ctx GitContext +---@param adapter VCSAdapter ---@param files FileEntry[] ---@param path_args string[] -function FilePanel:init(git_ctx, files, path_args, rev_pretty_name) +function FilePanel:init(adapter, files, path_args, rev_pretty_name) local conf = config.get_config() FilePanel:super().init(self, { config = conf.file_panel.win_config, bufname = "DiffviewFilePanel", }) - self.git_ctx = git_ctx + self.adapter = adapter self.files = files self.path_args = path_args self.rev_pretty_name = rev_pretty_name diff --git a/lua/diffview/scene/views/diff/listeners.lua b/lua/diffview/scene/views/diff/listeners.lua index 3b679940..fc422224 100644 --- a/lua/diffview/scene/views/diff/listeners.lua +++ b/lua/diffview/scene/views/diff/listeners.lua @@ -105,7 +105,7 @@ return function(view) or ( view.left.type == RevType.COMMIT and vim.tbl_contains({ RevType.STAGE, RevType.LOCAL }, view.right.type) - and view.left:is_head(view.git_ctx.ctx.toplevel) + and view.left:is_head(view.adapter.ctx.toplevel) ) then utils.info("Changes not commited yet. No log available for these changes.") return @@ -123,9 +123,9 @@ return function(view) if item then local code if item.kind == "working" or item.kind == "conflicting" then - _, code = vcs.exec_sync({ "add", item.path }, view.git_ctx.ctx.toplevel) + _, code = vcs.exec_sync({ "add", item.path }, view.adapter.ctx.toplevel) elseif item.kind == "staged" then - _, code = vcs.exec_sync({ "reset", "--", item.path }, view.git_ctx.ctx.toplevel) + _, code = vcs.exec_sync({ "reset", "--", item.path }, view.adapter.ctx.toplevel) end if code ~= 0 then @@ -180,7 +180,7 @@ return function(view) end, view.files.working) if #args > 0 then - local _, code = vcs.exec_sync({ "add", args }, view.git_ctx.ctx.toplevel) + local _, code = vcs.exec_sync({ "add", args }, view.adapter.ctx.toplevel) if code ~= 0 then utils.err("Failed to stage files!") @@ -194,7 +194,7 @@ return function(view) end end, unstage_all = function() - local _, code = vcs.exec_sync({ "reset" }, view.git_ctx.ctx.toplevel) + local _, code = vcs.exec_sync({ "reset" }, view.adapter.ctx.toplevel) if code ~= 0 then utils.err("Failed to unstage files!") @@ -220,7 +220,7 @@ return function(view) utils.err("The file is open with unsaved changes! Aborting file restoration.") return end - vcs.restore_file(view.git_ctx.ctx.toplevel, file.path, file.kind, commit, function() + vcs.restore_file(view.adapter.ctx.toplevel, file.path, file.kind, commit, function() async.util.scheduler() view:update_files() end) diff --git a/lua/diffview/scene/views/diff/render.lua b/lua/diffview/scene/views/diff/render.lua index f9fe59c2..82bfeb7e 100644 --- a/lua/diffview/scene/views/diff/render.lua +++ b/lua/diffview/scene/views/diff/render.lua @@ -155,7 +155,7 @@ return function(panel) ---@type RenderComponent local comp = panel.components.path.comp local line_idx = 0 - local s = utils.path:shorten(utils.path:vim_fnamemodify(panel.git_ctx.ctx.toplevel, ":~"), width - 6) + local s = utils.path:shorten(utils.path:vim_fnamemodify(panel.adapter.ctx.toplevel, ":~"), width - 6) comp:add_hl("DiffviewFilePanelRootPath", line_idx, 0, #s) comp:add_line(s) @@ -218,7 +218,7 @@ return function(panel) comp = panel.components.info.entries.comp line_idx = 0 for _, arg in ipairs(extra_info) do - local relpath = utils.path:relative(arg, panel.git_ctx.ctx.toplevel) + local relpath = utils.path:relative(arg, panel.adapter.ctx.toplevel) if relpath == "" then relpath = "." end diff --git a/lua/diffview/scene/views/file_history/file_history_panel.lua b/lua/diffview/scene/views/file_history/file_history_panel.lua index 98271aa7..242e5590 100644 --- a/lua/diffview/scene/views/file_history/file_history_panel.lua +++ b/lua/diffview/scene/views/file_history/file_history_panel.lua @@ -24,7 +24,7 @@ local perf_update = PerfTimer("[FileHistoryPanel] update") ---@class FileHistoryPanel : Panel ---@field parent FileHistoryView ----@field git_ctx GitContext +---@field adapter VCSAdapter ---@field entries LogEntry[] ---@field rev_range RevRange ---@field log_options ConfigLogOptions @@ -58,7 +58,7 @@ FileHistoryPanel.bufopts = vim.tbl_extend("force", Panel.bufopts, { ---@class FileHistoryPanel.init.Opt ---@field parent FileHistoryView ----@field git_ctx GitContext +---@field adapter VCSAdapter ---@field entries LogEntry[] ---@field log_options LogOptions @@ -73,11 +73,11 @@ function FileHistoryPanel:init(opt) }) self.parent = opt.parent - self.git_ctx = opt.git_ctx + self.adapter = opt.adapter self.entries = opt.entries self.cur_item = {} self.single_file = opt.entries[1] and opt.entries[1].single_file - self.option_panel = FHOptionPanel(self, self.git_ctx.flags) + self.option_panel = FHOptionPanel(self, self.adapter.flags) self.log_options = { single_file = vim.tbl_extend( "force", @@ -279,7 +279,7 @@ function FileHistoryPanel:update_entries(callback) self.entries = {} self.updating = true - finalizer = self.git_ctx:file_history( + finalizer = self.adapter:file_history( self.log_options, { default_layout = self.parent.get_default_diff2(), }, update diff --git a/lua/diffview/scene/views/file_history/file_history_view.lua b/lua/diffview/scene/views/file_history/file_history_view.lua index bd659a0c..fa5dd34a 100644 --- a/lua/diffview/scene/views/file_history/file_history_view.lua +++ b/lua/diffview/scene/views/file_history/file_history_view.lua @@ -15,7 +15,7 @@ local api = vim.api local M = {} ---@class FileHistoryView : StandardView ----@field git_ctx GitContext +---@field adapter VCSAdapter ---@field panel FileHistoryPanel ---@field commit_log_panel CommitLogPanel ---@field valid boolean @@ -23,12 +23,12 @@ local FileHistoryView = oop.create_class("FileHistoryView", StandardView.__get() function FileHistoryView:init(opt) self.valid = false - self.git_ctx = opt.git_ctx + self.adapter = opt.adapter FileHistoryView:super().init(self, { panel = FileHistoryPanel({ parent = self, - git_ctx = self.git_ctx, + adapter = self.adapter, entries = {}, log_options = opt.log_options, }), @@ -38,8 +38,8 @@ function FileHistoryView:init(opt) end function FileHistoryView:post_open() - self.commit_log_panel = CommitLogPanel(self.git_ctx.ctx.toplevel, { - name = ("diffview://%s/log/%d/%s"):format(self.git_ctx.ctx.dir, self.tabpage, "commit_log"), + self.commit_log_panel = CommitLogPanel(self.adapter.ctx.toplevel, { + name = ("diffview://%s/log/%d/%s"):format(self.adapter.ctx.dir, self.tabpage, "commit_log"), }) self:init_event_listeners() diff --git a/lua/diffview/scene/views/file_history/listeners.lua b/lua/diffview/scene/views/file_history/listeners.lua index 7e30b5b6..5df9602c 100644 --- a/lua/diffview/scene/views/file_history/listeners.lua +++ b/lua/diffview/scene/views/file_history/listeners.lua @@ -41,8 +41,8 @@ return function(view) if l1 then l1 = tonumber(l1) lpath = utils.path:chain(lpath) - :normalize({ cwd = view.git_ctx.ctx.toplevel, absolute = true }) - :relative(view.git_ctx.ctx.toplevel) + :normalize({ cwd = view.adapter.ctx.toplevel, absolute = true }) + :relative(view.adapter.ctx.toplevel) :get() if lpath == cur.path then @@ -72,7 +72,7 @@ return function(view) local layout = file.layout --[[@as Diff2 ]] local new_view = DiffView({ - git_ctx = view.git_ctx, + adapter = view.adapter, rev_arg = vcs.rev_to_pretty_string(layout.a.file.rev, layout.b.file.rev), left = layout.a.file.rev, right = layout.b.file.rev, diff --git a/lua/diffview/scene/views/file_history/render.lua b/lua/diffview/scene/views/file_history/render.lua index af1deee8..85dd63bd 100644 --- a/lua/diffview/scene/views/file_history/render.lua +++ b/lua/diffview/scene/views/file_history/render.lua @@ -196,10 +196,10 @@ local function prepare_panel_cache(panel) cache[panel] = c c.root_path = panel.state.form == "column" and utils.path:shorten( - utils.path:vim_fnamemodify(panel.git_ctx.ctx.toplevel, ":~"), + utils.path:vim_fnamemodify(panel.adapter.ctx.toplevel, ":~"), panel:get_config().width - 6 ) - or utils.path:vim_fnamemodify(panel.git_ctx.ctx.toplevel, ":~") + or utils.path:vim_fnamemodify(panel.adapter.ctx.toplevel, ":~") c.args = table.concat(panel.log_options.single_file.path_args, " ") end diff --git a/lua/diffview/vcs/adapters/git/init.lua b/lua/diffview/vcs/adapters/git/init.lua index e5191a72..58fe6e6f 100644 --- a/lua/diffview/vcs/adapters/git/init.lua +++ b/lua/diffview/vcs/adapters/git/init.lua @@ -1250,7 +1250,7 @@ GitAdapter.flags = { completion = function(panel) return function(arg_lead, _, _) local view = panel.parent.parent - return view.git_ctx:rev_completion(arg_lead, { + return view.adapter:rev_completion(arg_lead, { accept_range = true, }) end @@ -1262,7 +1262,7 @@ GitAdapter.flags = { completion = function(panel) return function(arg_lead, _, _) local view = panel.parent.parent - return utils.vec_join("LOCAL", view.git_ctx:rev_completion(arg_lead, {})) + return utils.vec_join("LOCAL", view.adapter:rev_completion(arg_lead, {})) end end, }, diff --git a/lua/diffview/vcs/adapters/git/utils.lua b/lua/diffview/vcs/adapters/git/utils.lua index 0ef921bb..5d585ad7 100644 --- a/lua/diffview/vcs/adapters/git/utils.lua +++ b/lua/diffview/vcs/adapters/git/utils.lua @@ -176,7 +176,7 @@ local incremental_fh_data = async.void(function(state, callback) "--", state.path_args ), - cwd = state.ctx.toplevel, + cwd = state.adapter.ctx.toplevel, on_stdout = on_stdout, on_exit = on_exit, }) @@ -194,7 +194,7 @@ local incremental_fh_data = async.void(function(state, callback) "--", state.path_args ), - cwd = state.ctx.toplevel, + cwd = state.adapter.ctx.toplevel, on_stdout = on_stdout, on_exit = on_exit, }) @@ -292,7 +292,7 @@ local incremental_line_trace_data = async.void(function(state, callback) state.prepared_log_opts.flags, "--" ), - cwd = state.ctx.toplevel, + cwd = state.adapter.ctx.toplevel, on_stdout = on_stdout, on_exit = on_exit, }) @@ -345,7 +345,7 @@ end ---@class git.utils.FHState ---@field thread thread ----@field ctx GitContext +---@field adapter VCSAdapter ---@field path_args string[] ---@field log_options LogOptions ---@field prepared_log_opts git.utils.PreparedLogOpts @@ -381,7 +381,7 @@ local function parse_fh_data(state) "--", state.old_path or state.path_args ), - cwd = state.ctx.toplevel, + cwd = state.adapter.ctx.toplevel, on_exit = function(j) if j.code == 0 then cur.namestat = j:result() @@ -455,7 +455,7 @@ local function parse_fh_data(state) end table.insert(files, FileEntry.for_d2(state.opt.default_layout or Diff2Hor, { - git_ctx = state.ctx, + adapter = state.adapter, path = name, oldpath = oldname, status = status, @@ -502,7 +502,7 @@ local function parse_fh_line_trace_data(state) end table.insert(files, FileEntry.for_d2(Diff2Hor, { - git_ctx = state.ctx, + adapter = state.adapter, path = b_path, oldpath = oldpath, kind = "working", @@ -554,7 +554,7 @@ function M.git_dir(path) end ---@param path string ----@return GitContext? +---@return VCSAdapter? function M.git_context(path) local toplevel = M.toplevel(path) if toplevel then From ffd9a9cc9d8d0a896444b524bbe4f634c0cef477 Mon Sep 17 00:00:00 2001 From: zegervdv Date: Sat, 29 Oct 2022 09:46:27 +0000 Subject: [PATCH 47/71] refactor: add public methods to VCSAdapter class --- lua/diffview/vcs/adapter.lua | 133 +++++++++++++++++++------ lua/diffview/vcs/adapters/git/init.lua | 3 +- 2 files changed, 106 insertions(+), 30 deletions(-) diff --git a/lua/diffview/vcs/adapter.lua b/lua/diffview/vcs/adapter.lua index 540afef0..b9bc3a72 100644 --- a/lua/diffview/vcs/adapter.lua +++ b/lua/diffview/vcs/adapter.lua @@ -2,6 +2,7 @@ local oop = require('diffview.oop') local utils = require('diffview.utils') local logger = require('diffview.logger') local arg_parser = require('diffview.arg_parser') +local RevType = require("diffview.vcs.rev").RevType local M = {} @@ -14,7 +15,13 @@ local M = {} ---@field context string[] local VCSAdapter = oop.create_class('VCSAdapter') -function VCSAdapter:init(path) +---@class vcs.adapter.VCSAdapter.Opt +---@field cpath string? # CWD path +---@field toplevel string # VCS toplevel path +---@field path_args string[] # Extra path arguments + +---@param opt vcs.adapter.VCSAdapter.Opt +function VCSAdapter:init(opt) self.bootstrap = { done = false, ok = false, @@ -34,20 +41,11 @@ function VCSAdapter:run_bootstrap() self.bootstrap.ok = true end +---@return string # path to binary for VCS command function VCSAdapter:get_command() oop.abstract_stub() end ----@param args string[] ----@return string[] args to show commit content -function VCSAdapter:get_show_args(args) - oop.abstract_stub() -end - -function VCSAdapter:get_context(path) - return {} -end - ---@return string cmd The VCS binary. function VCSAdapter:bin() return self:get_command()[1] @@ -58,6 +56,7 @@ function VCSAdapter:args() return utils.vec_slice(self:get_command(), 2) end + ---Execute a VCS command synchronously. ---@param args string[] ---@param cwd_or_opt? string|utils.system_list.Opt @@ -77,11 +76,41 @@ function VCSAdapter:exec_sync(args, cwd_or_opt) ) end -function VCSAdapter:file_history_options(range, args) + +---@param thread thread +---@param ok boolean +---@param result any +---@return boolean ok +---@return any result +function VCSAdapter:handle_co(thread, ok, result) + if not ok then + local err_msg = utils.vec_join( + "Coroutine failed!", + debug.traceback(thread, result, 1) + ) + utils.err(err_msg, true) + logger.s_error(table.concat(err_msg, "\n")) + end + return ok, result +end + +-- File History + +---@param args string[] +---@return string[] args to show commit content +function VCSAdapter:get_show_args(args) + oop.abstract_stub() +end + +---@param range? { [1]: integer, [2]: integer } +---@param paths string[] +---@param args string[] +---@return string[] # Options to show file history +function VCSAdapter:file_history_options(range, paths, args) oop.abstract_stub() end ----@class vcs.adapter.FileHistoryWorkerSpec : git.utils.LayoutOpt +---@class vcs.adapter.FileHistoryWorkerSpec : vcs.adapter.LayoutOpt ---@param thread thread ---@param log_opt ConfigLogOptions @@ -92,7 +121,6 @@ function VCSAdapter:file_history_worker(thread, log_opt, opt, co_state, callback oop.abstract_stub() end - ---@param log_opt ConfigLogOptions ---@param opt vcs.adapter.FileHistoryWorkerSpec ---@param callback function @@ -115,21 +143,70 @@ function VCSAdapter:file_history(log_opt, opt, callback) end end ----@param thread thread ----@param ok boolean ----@param result any ----@return boolean ok ----@return any result -function VCSAdapter:handle_co(thread, ok, result) - if not ok then - local err_msg = utils.vec_join( - "Coroutine failed!", - debug.traceback(thread, result, 1) - ) - utils.err(err_msg, true) - logger.s_error(table.concat(err_msg, "\n")) +-- Diff View + +---Convert revs to rev args. +---@param left Rev +---@param right Rev +---@return string[] +function VCSAdapter:rev_to_args(left, right) + oop.abstract_stub() +end + +---Arguments to show name and status of files +---@param args string[] Extra args +---@return string[] +function VCSAdapter:get_namestat_args(args) + oop.abstract_stub() +end + +---Arguments to show number of changes to files +---@param args string[] Extra args +---@return string[] +function VCSAdapter:get_numstat_args(args) + oop.abstract_stub() +end + +---Arguments to list all files +---@param args string[] Extra args +---@return string[] +function VCSAdapter:get_files_args(args) + oop.abstract_stub() +end + +---@param args string[] +---@return {left: string, right: string, options: string[]} +function VCSAdapter:diffview_options(args) + oop.abstract_stub() +end + +---Check if status for untracked files is disabled +---@return boolean +function VCSAdapter:show_untracked() + return true +end + +---Convert revs to string representation. +---@param left Rev +---@param right Rev +---@return string|nil +function VCSAdapter:rev_to_pretty_string(left, right) + if left.track_head and right.type == RevType.LOCAL then + return nil + elseif left.commit and right.type == RevType.LOCAL then + return left:abbrev() + elseif left.commit and right.commit then + return left:abbrev() .. ".." .. right:abbrev() end - return ok, result + return nil +end + +---Check if any of the given revs are LOCAL. +---@param left Rev +---@param right Rev +---@return boolean +function VCSAdapter:has_local(left, right) + return left.type == RevType.LOCAL or right.type == RevType.LOCAL end diff --git a/lua/diffview/vcs/adapters/git/init.lua b/lua/diffview/vcs/adapters/git/init.lua index 58fe6e6f..5b275d73 100644 --- a/lua/diffview/vcs/adapters/git/init.lua +++ b/lua/diffview/vcs/adapters/git/init.lua @@ -138,6 +138,7 @@ function M.create(toplevel, path_args, cpath) }) end +---@param opt vcs.adapter.VCSAdapter.Opt function GitAdapter:init(opt) opt = opt or {} GitAdapter:super().init(self, opt) @@ -604,8 +605,6 @@ function GitAdapter:file_history_dry_run(log_opt) end ----@param range? { [1]: integer, [2]: integer } ----@param args string[] function GitAdapter:file_history_options(range, paths, args) local default_args = config.get_config().default_args.DiffviewFileHistory local argo = arg_parser.parse(vim.tbl_flatten({ default_args, args })) From d982b83a53175d2330ed816c36280362b92f7013 Mon Sep 17 00:00:00 2001 From: zegervdv Date: Sat, 29 Oct 2022 09:52:07 +0000 Subject: [PATCH 48/71] refactor: clean up duplicate code --- lua/diffview/vcs/adapters/git/init.lua | 47 ++ lua/diffview/vcs/adapters/git/utils.lua | 594 ------------------------ 2 files changed, 47 insertions(+), 594 deletions(-) diff --git a/lua/diffview/vcs/adapters/git/init.lua b/lua/diffview/vcs/adapters/git/init.lua index 5b275d73..d45a04dc 100644 --- a/lua/diffview/vcs/adapters/git/init.lua +++ b/lua/diffview/vcs/adapters/git/init.lua @@ -699,6 +699,53 @@ function GitAdapter:file_history_options(range, paths, args) return log_options end +---@param state git.utils.FHState +---@return boolean ok +local function parse_fh_line_trace_data(state) + local cur = state.cur + + local files = {} + + for _, line in ipairs(cur.namestat) do + if line:match("^diff %-%-git ") then + local a_path = line:match('^diff %-%-git "?a/(.-)"? "?b/') + local b_path = line:match('.*"? "?b/(.-)"?$') + local oldpath = a_path ~= b_path and a_path or nil + + if state.single_file and oldpath then + state.old_path = oldpath + end + + table.insert(files, FileEntry.for_d2(Diff2Hor, { + adapter = state.adapter, + path = b_path, + oldpath = oldpath, + kind = "working", + commit = state.commit, + rev_a = cur.left_hash and GitRev(RevType.COMMIT, cur.left_hash) or GitRev.new_null_tree(), + rev_b = state.prepared_log_opts.base or GitRev(RevType.COMMIT, cur.right_hash), + })) + end + end + + if files[1] then + table.insert( + state.entries, + LogEntry({ + path_args = state.path_args, + commit = state.commit, + files = files, + single_file = state.single_file, + }) + ) + + state.callback(state.entries, JobStatus.PROGRESS) + end + + return true +end + + ---@class git.utils.FHState ---@field thread thread ---@field adapter GitAdapter diff --git a/lua/diffview/vcs/adapters/git/utils.lua b/lua/diffview/vcs/adapters/git/utils.lua index 5d585ad7..836d649c 100644 --- a/lua/diffview/vcs/adapters/git/utils.lua +++ b/lua/diffview/vcs/adapters/git/utils.lua @@ -15,555 +15,6 @@ local api = vim.api local M = {} ----@class git.utils.PreparedLogOpts ----@field rev_range string ----@field base Rev ----@field path_args string[] ----@field flags string[] - ----@param toplevel string ----@param log_options LogOptions ----@param single_file boolean ----@return git.utils.PreparedLogOpts -local function prepare_fh_options(toplevel, log_options, single_file) - local o = log_options - local line_trace = vim.tbl_map(function(v) - if not v:match("^-L") then - return "-L" .. v - end - return v - end, o.L or {}) - - local rev_range, base - - if log_options.rev_range then - local ok, _ = M.verify_rev_arg(toplevel, log_options.rev_range) - - if not ok then - utils.warn(("Bad range revision, ignoring: %s"):format(utils.str_quote(log_options.rev_range))) - else - rev_range = log_options.rev_range - end - end - - if log_options.base then - if log_options.base == "LOCAL" then - base = Rev(RevType.LOCAL) - else - local ok, out = M.verify_rev_arg(toplevel, log_options.base) - - if not ok then - utils.warn(("Bad base revision, ignoring: %s"):format(utils.str_quote(log_options.base))) - else - base = Rev(RevType.COMMIT, out[1]) - end - end - end - - return { - rev_range = rev_range, - base = base, - path_args = log_options.path_args, - flags = utils.vec_join( - line_trace, - (o.follow and single_file) and { "--follow" } or nil, - o.first_parent and { "--first-parent" } or nil, - o.show_pulls and { "--show-pulls" } or nil, - o.reflog and { "--reflog" } or nil, - o.all and { "--all" } or nil, - o.merges and { "--merges" } or nil, - o.no_merges and { "--no-merges" } or nil, - o.reverse and { "--reverse" } or nil, - o.max_count and { "-n" .. o.max_count } or nil, - o.diff_merges and { "--diff-merges=" .. o.diff_merges } or nil, - o.author and { "-E", "--author=" .. o.author } or nil, - o.grep and { "-E", "--grep=" .. o.grep } or nil - ) - } -end - -local function structure_fh_data(namestat_data, numstat_data) - local right_hash, left_hash, merge_hash = unpack(utils.str_split(namestat_data[1])) - local time, time_offset = unpack(utils.str_split(namestat_data[3])) - - return { - left_hash = left_hash ~= "" and left_hash or nil, - right_hash = right_hash, - merge_hash = merge_hash, - author = namestat_data[2], - time = tonumber(time), - time_offset = time_offset, - rel_date = namestat_data[4], - ref_names = namestat_data[5]:sub(3), - subject = namestat_data[6]:sub(3), - namestat = utils.vec_slice(namestat_data, 7), - numstat = numstat_data, - } -end - ----@param state git.utils.FHState ----@param callback fun(status: JobStatus, data?: table, msg?: string[]) -local incremental_fh_data = async.void(function(state, callback) - local raw = {} - local namestat_job, numstat_job, shutdown - - local namestat_state = { - data = {}, - key = "namestat", - idx = 0, - } - local numstat_state = { - data = {}, - key = "numstat", - idx = 0, - } - - local function on_stdout(_, line, j) - local handler_state = j == namestat_job and namestat_state or numstat_state - - if line == "\0" then - if handler_state.idx > 0 then - if not raw[handler_state.idx] then - raw[handler_state.idx] = {} - end - - raw[handler_state.idx][handler_state.key] = handler_state.data - - if not shutdown and raw[handler_state.idx].namestat and raw[handler_state.idx].numstat then - shutdown = callback( - JobStatus.PROGRESS, - structure_fh_data(raw[handler_state.idx].namestat, raw[handler_state.idx].numstat) - ) - - if shutdown then - logger.lvl(1).debug("Killing file history jobs...") - -- NOTE: The default `Job:shutdown` methods use `vim.wait` which - -- causes a segfault when called here. - namestat_job:_shutdown(64) - numstat_job:_shutdown(64) - end - end - end - handler_state.idx = handler_state.idx + 1 - handler_state.data = {} - elseif line ~= "" then - table.insert(handler_state.data, line) - end - end - - ---@type CountDownLatch - local latch = CountDownLatch(2) - - local function on_exit(j, code) - if code == 0 then - on_stdout(nil, "\0", j) - end - latch:count_down() - end - - local rev_range = state.prepared_log_opts.rev_range - - namestat_job = Job:new({ - command = git_bin(), - args = utils.vec_join( - git_args(), - "log", - rev_range, - "--pretty=format:%x00%n%H %P%n%an%n%ad%n%ar%n %D%n %s", - "--date=raw", - "--name-status", - state.prepared_log_opts.flags, - "--", - state.path_args - ), - cwd = state.adapter.ctx.toplevel, - on_stdout = on_stdout, - on_exit = on_exit, - }) - - numstat_job = Job:new({ - command = git_bin(), - args = utils.vec_join( - git_args(), - "log", - rev_range, - "--pretty=format:%x00", - "--date=raw", - "--numstat", - state.prepared_log_opts.flags, - "--", - state.path_args - ), - cwd = state.adapter.ctx.toplevel, - on_stdout = on_stdout, - on_exit = on_exit, - }) - - namestat_job:start() - numstat_job:start() - - latch:await() - - local debug_opt = { - context = "git.utils>incremental_fh_data()", - func = "s_info", - no_stdout = true, - } - utils.handle_job(namestat_job, { debug_opt = debug_opt }) - utils.handle_job(numstat_job, { debug_opt = debug_opt }) - - if shutdown then - callback(JobStatus.KILLED) - elseif namestat_job.code ~= 0 or numstat_job.code ~= 0 then - callback(JobStatus.ERROR, nil, utils.vec_join( - namestat_job:stderr_result(), - numstat_job:stderr_result()) - ) - else - callback(JobStatus.SUCCESS) - end -end) - ----@param state git.utils.FHState ----@param callback fun(status: JobStatus, data?: table, msg?: string[]) -local incremental_line_trace_data = async.void(function(state, callback) - local raw = {} - local trace_job, shutdown - - local trace_state = { - data = {}, - key = "trace", - idx = 0, - } - - local function on_stdout(_, line) - if line == "\0" then - if trace_state.idx > 0 then - if not raw[trace_state.idx] then - raw[trace_state.idx] = {} - end - - raw[trace_state.idx] = trace_state.data - - if not shutdown then - shutdown = callback( - JobStatus.PROGRESS, - structure_fh_data(raw[trace_state.idx]) - ) - - if shutdown then - logger.lvl(1).debug("Killing file history jobs...") - -- NOTE: The default `Job:shutdown` methods use `vim.wait` which - -- causes a segfault when called here. - trace_job:_shutdown(64) - end - end - end - trace_state.idx = trace_state.idx + 1 - trace_state.data = {} - elseif line ~= "" then - table.insert(trace_state.data, line) - end - end - - ---@type CountDownLatch - local latch = CountDownLatch(1) - - local function on_exit(_, code) - if code == 0 then - on_stdout(nil, "\0") - end - latch:count_down() - end - - local rev_range = state.prepared_log_opts.rev_range - - trace_job = Job:new({ - command = git_bin(), - args = utils.vec_join( - git_args(), - "-P", - "log", - rev_range, - "--color=never", - "--no-ext-diff", - "--pretty=format:%x00%n%H %P%n%an%n%ad%n%ar%n %D%n %s", - "--date=raw", - state.prepared_log_opts.flags, - "--" - ), - cwd = state.adapter.ctx.toplevel, - on_stdout = on_stdout, - on_exit = on_exit, - }) - - trace_job:start() - - latch:await() - - utils.handle_job(trace_job, { - debug_opt = { - context = "git.utils>incremental_line_trace_data()", - func = "s_debug", - debug_level = 1, - no_stdout = true, - } - }) - - if shutdown then - callback(JobStatus.KILLED) - elseif trace_job.code ~= 0 then - callback(JobStatus.ERROR, nil, trace_job:stderr_result()) - else - callback(JobStatus.SUCCESS) - end -end) - ----@param toplevel string ----@param path_args string[] ----@param lflags string[] ----@return boolean -local function is_single_file(toplevel, path_args, lflags) - if lflags and #lflags > 0 then - local seen = {} - for i, v in ipairs(lflags) do - local path = v:match(".*:(.*)") - if i > 1 and not seen[path] then - return false - end - seen[path] = true - end - - elseif path_args and toplevel then - return #path_args == 1 - and not utils.path:is_dir(path_args[1]) - and #M.exec_sync({ "ls-files", "--", path_args }, toplevel) < 2 - end - - return true -end - ----@class git.utils.FHState ----@field thread thread ----@field adapter VCSAdapter ----@field path_args string[] ----@field log_options LogOptions ----@field prepared_log_opts git.utils.PreparedLogOpts ----@field opt git.utils.FileHistoryWorkerSpec ----@field single_file boolean ----@field resume_lock boolean ----@field cur table ----@field commit Commit ----@field entries LogEntry[] ----@field callback function - ----@param state git.utils.FHState ----@return boolean ok, JobStatus? status -local function parse_fh_data(state) - local cur = state.cur - - -- 'git log --name-status' doesn't work properly for merge commits. It - -- lists only an incomplete list of files at best. We need to use 'git - -- show' to get file statuses for merge commits. And merges do not always - -- have changes. - if cur.merge_hash and cur.numstat[1] and #cur.numstat ~= #cur.namestat then - local job - local job_spec = { - command = git_bin(), - args = utils.vec_join( - git_args(), - "show", - "--format=", - "--diff-merges=first-parent", - "--name-status", - (state.single_file and state.log_options.follow) and "--follow" or nil, - cur.right_hash, - "--", - state.old_path or state.path_args - ), - cwd = state.adapter.ctx.toplevel, - on_exit = function(j) - if j.code == 0 then - cur.namestat = j:result() - end - handle_co(state.thread, coroutine.resume(state.thread)) - end, - } - - local max_retries = 2 - local context = "git.utils.file_history_worker()" - state.resume_lock = true - - for i = 0, max_retries do - -- Git sometimes fails this job silently (exit code 0). Not sure why, - -- possibly because we are running multiple git opeartions on the same - -- repo concurrently. Retrying the job usually solves this. - job = Job:new(job_spec) - job:start() - coroutine.yield() - utils.handle_job(job, { fail_on_empty = true, context = context, log_func = logger.warn }) - - if #cur.namestat == 0 then - if i < max_retries then - logger.warn(("[%s] Retrying %d more time(s)."):format(context, max_retries - i)) - end - else - if i > 0 then - logger.info(("[%s] Retry successful!"):format(context)) - end - break - end - end - - state.resume_lock = false - - if job.code ~= 0 then - state.callback({}, JobStatus.ERROR, job:stderr_result()) - return false, JobStatus.FATAL - end - - if #cur.namestat == 0 then - -- Give up: something has been renamed. We can no longer track the - -- history. - logger.warn(("[%s] Giving up."):format(context)) - utils.warn("Displayed history may be incomplete. Check ':DiffviewLog' for details.", true) - return false - end - end - - local files = {} - for i = 1, #cur.numstat do - local status = cur.namestat[i]:sub(1, 1):gsub("%s", " ") - local name = cur.namestat[i]:match("[%a%s][^%s]*\t(.*)") - local oldname - - if name:match("\t") ~= nil then - oldname = name:match("(.*)\t") - name = name:gsub("^.*\t", "") - if state.single_file then - state.old_path = oldname - end - end - - local stats = { - additions = tonumber(cur.numstat[i]:match("^%d+")), - deletions = tonumber(cur.numstat[i]:match("^%d+%s+(%d+)")), - } - - if not stats.additions or not stats.deletions then - stats = nil - end - - table.insert(files, FileEntry.for_d2(state.opt.default_layout or Diff2Hor, { - adapter = state.adapter, - path = name, - oldpath = oldname, - status = status, - stats = stats, - kind = "working", - commit = state.commit, - rev_a = cur.left_hash and Rev(RevType.COMMIT, cur.left_hash) or Rev.new_null_tree(), - rev_b = state.prepared_log_opts.base or Rev(RevType.COMMIT, cur.right_hash), - })) - end - - if files[1] then - table.insert( - state.entries, - LogEntry({ - path_args = state.path_args, - commit = state.commit, - files = files, - single_file = state.single_file, - }) - ) - - state.callback(state.entries, JobStatus.PROGRESS) - end - - return true -end - ----@param state git.utils.FHState ----@return boolean ok -local function parse_fh_line_trace_data(state) - local cur = state.cur - - local files = {} - - for _, line in ipairs(cur.namestat) do - if line:match("^diff %-%-git ") then - local a_path = line:match('^diff %-%-git "?a/(.-)"? "?b/') - local b_path = line:match('.*"? "?b/(.-)"?$') - local oldpath = a_path ~= b_path and a_path or nil - - if state.single_file and oldpath then - state.old_path = oldpath - end - - table.insert(files, FileEntry.for_d2(Diff2Hor, { - adapter = state.adapter, - path = b_path, - oldpath = oldpath, - kind = "working", - commit = state.commit, - rev_a = cur.left_hash and Rev(RevType.COMMIT, cur.left_hash) or Rev.new_null_tree(), - rev_b = state.prepared_log_opts.base or Rev(RevType.COMMIT, cur.right_hash), - })) - end - end - - if files[1] then - table.insert( - state.entries, - LogEntry({ - path_args = state.path_args, - commit = state.commit, - files = files, - single_file = state.single_file, - }) - ) - - state.callback(state.entries, JobStatus.PROGRESS) - end - - return true -end - - ----Derive the top-level path of the working tree of the given path. ----@param path string ----@return string? -function M.toplevel(path) - local out, code = M.exec_sync({ "rev-parse", "--path-format=absolute", "--show-toplevel" }, path) - if code ~= 0 then - return nil - end - return out[1] and vim.trim(out[1]) -end - ----Get the path to the .git directory. ----@param path string ----@return string|nil -function M.git_dir(path) - local out, code = M.exec_sync({ "rev-parse", "--path-format=absolute", "--git-dir" }, path) - if code ~= 0 then - return nil - end - return out[1] and vim.trim(out[1]) -end - ----@param path string ----@return VCSAdapter? -function M.git_context(path) - local toplevel = M.toplevel(path) - if toplevel then - return { - toplevel = toplevel, - dir = M.git_dir(toplevel), - } - end -end local CONFLICT_START = [[^<<<<<<< ]] local CONFLICT_BASE = [[^||||||| ]] @@ -704,51 +155,6 @@ function M.parse_conflicts(lines, winid) return ret, cur_conflict, cur_idx or 0 end ----@return string, string -function M.pathspec_split(pathspec) - local magic = pathspec:match("^:[/!^]*:?") or pathspec:match("^:%b()") or "" - local pattern = pathspec:sub(1 + #magic, -1) - return magic or "", pattern or "" -end - -function M.pathspec_expand(toplevel, cwd, pathspec) - local magic, pattern = M.pathspec_split(pathspec) - if not utils.path:is_abs(pattern) then - pattern = utils.path:join(utils.path:relative(cwd, toplevel), pattern) - end - return magic .. utils.path:convert(pattern) -end - -function M.pathspec_modify(pathspec, mods) - local magic, pattern = M.pathspec_split(pathspec) - return magic .. utils.path:vim_fnamemodify(pattern, mods) -end - ----Strange trick to check if a file is binary using only git. ----@param toplevel string ----@param path string ----@param rev Rev ----@return boolean -- True if the file was binary for the given rev, or it didn't exist. -function M.is_binary(toplevel, path, rev) - if rev.type == RevType.STAGE and rev.stage > 0 then - return false - end - - local cmd = { "-c", "submodule.recurse=false", "grep", "-I", "--name-only", "-e", "." } - if rev.type == RevType.LOCAL then - cmd[#cmd+1] = "--untracked" - elseif rev.type == RevType.STAGE then - cmd[#cmd+1] = "--cached" - else - cmd[#cmd+1] = rev.commit - end - - utils.vec_push(cmd, "--", path) - - local _, code = M.exec_sync(cmd, { cwd = toplevel, silent = true }) - return code ~= 0 -end - ---Get the diff status letter for a file for a given rev. ---@param toplevel string ---@param path string From 089fb7ec4145c332a30bdc20c13986053a0be573 Mon Sep 17 00:00:00 2001 From: zegervdv Date: Sat, 29 Oct 2022 09:56:34 +0000 Subject: [PATCH 49/71] refactor: port verify_rev_arg --- lua/diffview/vcs/adapters/git/init.lua | 25 ++++++++++++++++++------- lua/diffview/vcs/adapters/git/utils.lua | 12 ------------ 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/lua/diffview/vcs/adapters/git/init.lua b/lua/diffview/vcs/adapters/git/init.lua index d45a04dc..837751c0 100644 --- a/lua/diffview/vcs/adapters/git/init.lua +++ b/lua/diffview/vcs/adapters/git/init.lua @@ -227,6 +227,17 @@ function GitAdapter:get_dir(path) return out[1] and vim.trim(out[1]) end +---Verify that a given git rev is valid. +---@param rev_arg string +---@return boolean ok, string[] output +function GitAdapter:verify_rev_arg(rev_arg) + local out, code = self:exec_sync({ "rev-parse", "--revs-only", rev_arg }, { + context = "git.utils.verify_rev_arg()", + cwd = self.ctx.toplevel, + }) + return code == 0 and (out[2] ~= nil or out[1] and out[1] ~= ""), out +end + ---@class git.utils.PreparedLogOpts ---@field rev_range string @@ -234,11 +245,11 @@ end ---@field path_args string[] ---@field flags string[] ----@param toplevel string +---@param adapter VCSAdapter ---@param log_options LogOptions ---@param single_file boolean ---@return git.utils.PreparedLogOpts -local function prepare_fh_options(toplevel, log_options, single_file) +local function prepare_fh_options(adapter, log_options, single_file) local o = log_options local line_trace = vim.tbl_map(function(v) if not v:match("^-L") then @@ -250,7 +261,7 @@ local function prepare_fh_options(toplevel, log_options, single_file) local rev_range, base if log_options.rev_range then - local ok, _ = M.verify_rev_arg(toplevel, log_options.rev_range) + local ok, _ = adapter:verify_rev_arg(log_options.rev_range) if not ok then utils.warn(("Bad range revision, ignoring: %s"):format(utils.str_quote(log_options.rev_range))) @@ -263,7 +274,7 @@ local function prepare_fh_options(toplevel, log_options, single_file) if log_options.base == "LOCAL" then base = GitRev(RevType.LOCAL) else - local ok, out = M.verify_rev_arg(toplevel, log_options.base) + local ok, out = adapter:verify_rev_arg(log_options.base) if not ok then utils.warn(("Bad base revision, ignoring: %s"):format(utils.str_quote(log_options.base))) @@ -564,7 +575,7 @@ function GitAdapter:file_history_dry_run(log_opt) local options = vim.tbl_map(function(v) return vim.fn.shellescape(v) - end, prepare_fh_options(self.ctx.toplevel, log_options, single_file).flags) --[[@as vector ]] + end, prepare_fh_options(self, log_options, single_file).flags) --[[@as vector ]] local description = utils.vec_join( ("Top-level path: '%s'"):format(utils.path:vim_fnamemodify(self.ctx.toplevel, ":~")), @@ -574,7 +585,7 @@ function GitAdapter:file_history_dry_run(log_opt) log_options = utils.tbl_clone(log_options) --[[@as LogOptions ]] log_options.max_count = 1 - options = prepare_fh_options(self.ctx.toplevel, log_options, single_file).flags + options = prepare_fh_options(self, log_options, single_file).flags local context = "git.utils.file_history_dry_run()" local cmd @@ -918,7 +929,7 @@ function GitAdapter:file_history_worker(thread, log_opt, opt, co_state, callback adapter = self, path_args = log_opt.single_file.path_args, log_options = log_options, - prepared_log_opts = prepare_fh_options(self.ctx.toplevel, log_options, single_file), + prepared_log_opts = prepare_fh_options(self, log_options, single_file), opt = opt, callback = callback, entries = entries, diff --git a/lua/diffview/vcs/adapters/git/utils.lua b/lua/diffview/vcs/adapters/git/utils.lua index 836d649c..e1150936 100644 --- a/lua/diffview/vcs/adapters/git/utils.lua +++ b/lua/diffview/vcs/adapters/git/utils.lua @@ -192,18 +192,6 @@ function M.get_file_stats(toplevel, path, rev_arg) end end ----Verify that a given git rev is valid. ----@param toplevel string ----@param rev_arg string ----@return boolean ok, string[] output -function M.verify_rev_arg(toplevel, rev_arg) - local out, code = M.exec_sync({ "rev-parse", "--revs-only", rev_arg }, { - context = "git.utils.verify_rev_arg()", - cwd = toplevel, - }) - return code == 0 and (out[2] ~= nil or out[1] and out[1] ~= ""), out -end - ---Restore a file to the state it was in, in a given commit / rev. If no commit ---is given, unstaged files are restored to the state in index, and staged files ---are restored to the state in HEAD. The file will also be written into the From 6c2d1b7cf45c3e60d70fcf5b79722d6c2d31d6ff Mon Sep 17 00:00:00 2001 From: zegervdv Date: Sat, 29 Oct 2022 09:58:28 +0000 Subject: [PATCH 50/71] refactor: remove unused code --- lua/diffview/vcs/adapters/git/utils.lua | 37 ------------------------- 1 file changed, 37 deletions(-) diff --git a/lua/diffview/vcs/adapters/git/utils.lua b/lua/diffview/vcs/adapters/git/utils.lua index e1150936..5219d467 100644 --- a/lua/diffview/vcs/adapters/git/utils.lua +++ b/lua/diffview/vcs/adapters/git/utils.lua @@ -155,43 +155,6 @@ function M.parse_conflicts(lines, winid) return ret, cur_conflict, cur_idx or 0 end ----Get the diff status letter for a file for a given rev. ----@param toplevel string ----@param path string ----@param rev_arg string ----@return string? -function M.get_file_status(toplevel, path, rev_arg) - local out, code = M.exec_sync( - { "diff", "--name-status", rev_arg, "--", path }, - toplevel - ) - if code == 0 and (out[1] and #out[1] > 0) then - return out[1]:sub(1, 1) - end -end - ----Get diff stats for a file for a given rev. ----@param toplevel string ----@param path string ----@param rev_arg string ----@return GitStats? -function M.get_file_stats(toplevel, path, rev_arg) - local out, code = M.exec_sync({ "diff", "--numstat", rev_arg, "--", path }, toplevel) - - if code == 0 and (out[1] and #out[1] > 0) then - local stats = { - additions = tonumber(out[1]:match("^%d+")), - deletions = tonumber(out[1]:match("^%d+%s+(%d+)")), - } - - if not stats.additions or not stats.deletions then - return - end - - return stats - end -end - ---Restore a file to the state it was in, in a given commit / rev. If no commit ---is given, unstaged files are restored to the state in index, and staged files ---are restored to the state in HEAD. The file will also be written into the From 6a57fb204a23b37a715e8e6c3013762ef7633e5a Mon Sep 17 00:00:00 2001 From: zegervdv Date: Sat, 29 Oct 2022 10:00:40 +0000 Subject: [PATCH 51/71] refactor: port incremental_line_trace_data --- lua/diffview/vcs/adapters/git/init.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lua/diffview/vcs/adapters/git/init.lua b/lua/diffview/vcs/adapters/git/init.lua index 837751c0..46e1844a 100644 --- a/lua/diffview/vcs/adapters/git/init.lua +++ b/lua/diffview/vcs/adapters/git/init.lua @@ -503,9 +503,9 @@ local incremental_line_trace_data = async.void(function(state, callback) local rev_range = state.prepared_log_opts.rev_range trace_job = Job:new({ - command = git_bin(), + command = state.adapter:bin(), args = utils.vec_join( - git_args(), + state.adapter:args(), "-P", "log", rev_range, @@ -516,7 +516,7 @@ local incremental_line_trace_data = async.void(function(state, callback) state.prepared_log_opts.flags, "--" ), - cwd = state.ctx.toplevel, + cwd = state.adapter.ctx.toplevel, on_stdout = on_stdout, on_exit = on_exit, }) From f6b921828c5cddf8a0cc621b0b6a79ca8ddc95e1 Mon Sep 17 00:00:00 2001 From: zegervdv Date: Sat, 29 Oct 2022 10:02:23 +0000 Subject: [PATCH 52/71] refactor: rename git.utils --- lua/diffview/vcs/adapters/git/init.lua | 30 +++++++++++++------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/lua/diffview/vcs/adapters/git/init.lua b/lua/diffview/vcs/adapters/git/init.lua index 46e1844a..dcfecd31 100644 --- a/lua/diffview/vcs/adapters/git/init.lua +++ b/lua/diffview/vcs/adapters/git/init.lua @@ -232,14 +232,14 @@ end ---@return boolean ok, string[] output function GitAdapter:verify_rev_arg(rev_arg) local out, code = self:exec_sync({ "rev-parse", "--revs-only", rev_arg }, { - context = "git.utils.verify_rev_arg()", + context = "GitAdapter.verify_rev_arg()", cwd = self.ctx.toplevel, }) return code == 0 and (out[2] ~= nil or out[1] and out[1] ~= ""), out end ----@class git.utils.PreparedLogOpts +---@class GitAdapter.PreparedLogOpts ---@field rev_range string ---@field base Rev ---@field path_args string[] @@ -248,7 +248,7 @@ end ---@param adapter VCSAdapter ---@param log_options LogOptions ---@param single_file boolean ----@return git.utils.PreparedLogOpts +---@return GitAdapter.PreparedLogOpts local function prepare_fh_options(adapter, log_options, single_file) local o = log_options local line_trace = vim.tbl_map(function(v) @@ -325,7 +325,7 @@ local function structure_fh_data(namestat_data, numstat_data) } end ----@param state git.utils.FHState +---@param state GitAdapter.FHState ---@param callback fun(status: JobStatus, data?: table, msg?: string[]) local incremental_fh_data = async.void(function(state, callback) local raw = {} @@ -429,7 +429,7 @@ local incremental_fh_data = async.void(function(state, callback) latch:await() local debug_opt = { - context = "git.utils>incremental_fh_data()", + context = "GitAdapter>incremental_fh_data()", func = "s_info", no_stdout = true, } @@ -448,7 +448,7 @@ local incremental_fh_data = async.void(function(state, callback) end end) ----@param state git.utils.FHState +---@param state GitAdapter.FHState ---@param callback fun(status: JobStatus, data?: table, msg?: string[]) local incremental_line_trace_data = async.void(function(state, callback) local raw = {} @@ -527,7 +527,7 @@ local incremental_line_trace_data = async.void(function(state, callback) utils.handle_job(trace_job, { debug_opt = { - context = "git.utils>incremental_line_trace_data()", + context = "GitAdapter>incremental_line_trace_data()", func = "s_debug", debug_level = 1, no_stdout = true, @@ -587,7 +587,7 @@ function GitAdapter:file_history_dry_run(log_opt) log_options.max_count = 1 options = prepare_fh_options(self, log_options, single_file).flags - local context = "git.utils.file_history_dry_run()" + local context = "GitAdapter.file_history_dry_run()" local cmd if #log_options.L > 0 then @@ -710,7 +710,7 @@ function GitAdapter:file_history_options(range, paths, args) return log_options end ----@param state git.utils.FHState +---@param state GitAdapter.FHState ---@return boolean ok local function parse_fh_line_trace_data(state) local cur = state.cur @@ -757,13 +757,13 @@ local function parse_fh_line_trace_data(state) end ----@class git.utils.FHState +---@class GitAdapter.FHState ---@field thread thread ---@field adapter GitAdapter ---@field path_args string[] ---@field log_options LogOptions ----@field prepared_log_opts git.utils.PreparedLogOpts ----@field opt git.utils.FileHistoryWorkerSpec +---@field prepared_log_opts GitAdapter.PreparedLogOpts +---@field opt GitAdapter.FileHistoryWorkerSpec ---@field single_file boolean ---@field resume_lock boolean ---@field cur table @@ -771,7 +771,7 @@ end ---@field entries LogEntry[] ---@field callback function ----@param state git.utils.FHState +---@param state GitAdapter.FHState ---@return boolean ok, JobStatus? status local function parse_fh_data(state) local cur = state.cur @@ -805,7 +805,7 @@ local function parse_fh_data(state) } local max_retries = 2 - local context = "git.utils.file_history_worker()" + local context = "GitAdapter.file_history_worker()" state.resume_lock = true for i = 0, max_retries do @@ -923,7 +923,7 @@ function GitAdapter:file_history_worker(thread, log_opt, opt, co_state, callback local is_trace = #log_options.L > 0 - ---@type git.utils.FHState + ---@type GitAdapter.FHState local state = { thread = thread, adapter = self, From 15e5759489fa7bf8896930e01db83fbb5ec0bf76 Mon Sep 17 00:00:00 2001 From: zegervdv Date: Sun, 30 Oct 2022 10:33:35 +0000 Subject: [PATCH 53/71] refactor: implement restore --- lua/diffview/vcs/adapter.lua | 12 ++++ lua/diffview/vcs/adapters/git/init.lua | 75 +++++++++++++++++++++ lua/diffview/vcs/adapters/git/utils.lua | 87 ------------------------- lua/diffview/vcs/utils.lua | 21 ++++++ 4 files changed, 108 insertions(+), 87 deletions(-) diff --git a/lua/diffview/vcs/adapter.lua b/lua/diffview/vcs/adapter.lua index b9bc3a72..cf0f9c53 100644 --- a/lua/diffview/vcs/adapter.lua +++ b/lua/diffview/vcs/adapter.lua @@ -15,6 +15,9 @@ local M = {} ---@field context string[] local VCSAdapter = oop.create_class('VCSAdapter') +---The VCS implements a database to store file states +VCSAdapter.has_database = false + ---@class vcs.adapter.VCSAdapter.Opt ---@field cpath string? # CWD path ---@field toplevel string # VCS toplevel path @@ -174,6 +177,15 @@ function VCSAdapter:get_files_args(args) oop.abstract_stub() end +---Restore a file to the requested state +---@param path string # file to restore +---@param kind '"staged"'|'"working"' +---@param commit string +---@return string? Command to undo the restore +function VCSAdapter:restore_file(path, kind, commit) + oop.abstract_stub() +end + ---@param args string[] ---@return {left: string, right: string, options: string[]} function VCSAdapter:diffview_options(args) diff --git a/lua/diffview/vcs/adapters/git/init.lua b/lua/diffview/vcs/adapters/git/init.lua index dcfecd31..27b721f1 100644 --- a/lua/diffview/vcs/adapters/git/init.lua +++ b/lua/diffview/vcs/adapters/git/init.lua @@ -27,6 +27,7 @@ local M = {} local GitAdapter = oop.create_class('GitAdapter', VCSAdapter) GitAdapter.Rev = GitRev +GitAdapter.has_database = true ---@return string, string local function pathspec_split(pathspec) @@ -1229,6 +1230,80 @@ function GitAdapter:get_files_args(args) return utils.vec_join(self:args(), "ls-files", "--others", "--exclude-standard", args) end +function GitAdapter:file_restore(path, kind, commit) + local out, code + local abs_path = utils.path:join(self.ctx.toplevel, path) + local rel_path = utils.path:vim_fnamemodify(abs_path, ":~") + + -- Check if file exists in history + _, code = self:exec_sync( + { "cat-file", "-e", ("%s:%s"):format(kind == "staged" and "HEAD" or "", path) }, + self.ctx.toplevel + ) + local exists_git = code == 0 + local exists_local = utils.path:readable(abs_path) + + if exists_local then + -- Wite file blob into db + out, code = self:exec_sync({ "hash-object", "-w", "--", path }, self.ctx.toplevel) + if code ~= 0 then + utils.err("Failed to write file blob into the object database. Aborting file restoration.", true) + return nil + end + end + + local undo + if exists_local then + undo = (":sp %s | %%!git show %s"):format(vim.fn.fnameescape(rel_path), out[1]:sub(1, 11)) + else + undo = (":!git rm %s"):format(vim.fn.fnameescape(path)) + end + + -- Revert file + if not exists_git then + local bn = utils.find_file_buffer(abs_path) + if bn then + async.util.scheduler() + local ok, err = utils.remove_buffer(false, bn) + if not ok then + utils.err({ + ("Failed to delete buffer '%d'! Aborting file restoration. Error message:") + :format(bn), + err + }, true) + return false + end + end + + if kind == "working" then + -- File is untracked and has no history: delete it from fs. + local ok, err = utils.path:unlink(abs_path) + if not ok then + utils.err({ + ("Failed to delete file '%s'! Aborting file restoration. Error message:") + :format(abs_path), + err + }, true) + return nil + end + else + -- File only exists in index + out, code = self:exec_sync( + { "rm", "-f", "--", path }, + self.ctx.toplevel + ) + end + else + -- File exists in history: checkout + out, code = self:exec_sync( + utils.vec_join("checkout", commit or (kind == "staged" and "HEAD" or nil), "--", path), + self.ctx.toplevel + ) + end + + return undo +end + ---Check if status for untracked files is disabled for a given git repo. ---@return boolean function GitAdapter:show_untracked() diff --git a/lua/diffview/vcs/adapters/git/utils.lua b/lua/diffview/vcs/adapters/git/utils.lua index 5219d467..0971e513 100644 --- a/lua/diffview/vcs/adapters/git/utils.lua +++ b/lua/diffview/vcs/adapters/git/utils.lua @@ -155,92 +155,5 @@ function M.parse_conflicts(lines, winid) return ret, cur_conflict, cur_idx or 0 end ----Restore a file to the state it was in, in a given commit / rev. If no commit ----is given, unstaged files are restored to the state in index, and staged files ----are restored to the state in HEAD. The file will also be written into the ----object database such that the action can be undone. ----@param toplevel string ----@param path string ----@param kind '"staged"'|'"working"' ----@param commit string -M.restore_file = async.wrap(function(toplevel, path, kind, commit, callback) - local out, code - local abs_path = utils.path:join(toplevel, path) - local rel_path = utils.path:vim_fnamemodify(abs_path, ":~") - - -- Check if file exists in history - _, code = M.exec_sync( - { "cat-file", "-e", ("%s:%s"):format(kind == "staged" and "HEAD" or "", path) }, - toplevel - ) - local exists_git = code == 0 - local exists_local = utils.path:readable(abs_path) - - if exists_local then - -- Wite file blob into db - out, code = M.exec_sync({ "hash-object", "-w", "--", path }, toplevel) - if code ~= 0 then - utils.err("Failed to write file blob into the object database. Aborting file restoration.", true) - return callback() - end - end - - local undo - if exists_local then - undo = (":sp %s | %%!git show %s"):format(vim.fn.fnameescape(rel_path), out[1]:sub(1, 11)) - else - undo = (":!git rm %s"):format(vim.fn.fnameescape(path)) - end - - -- Revert file - if not exists_git then - local bn = utils.find_file_buffer(abs_path) - if bn then - async.util.scheduler() - local ok, err = utils.remove_buffer(false, bn) - if not ok then - utils.err({ - ("Failed to delete buffer '%d'! Aborting file restoration. Error message:") - :format(bn), - err - }, true) - return callback() - end - end - - if kind == "working" then - -- File is untracked and has no history: delete it from fs. - local ok, err = utils.path:unlink(abs_path) - if not ok then - utils.err({ - ("Failed to delete file '%s'! Aborting file restoration. Error message:") - :format(abs_path), - err - }, true) - return callback() - end - else - -- File only exists in index - out, code = M.exec_sync( - { "rm", "-f", "--", path }, - toplevel - ) - end - else - -- File exists in history: checkout - out, code = M.exec_sync( - utils.vec_join("checkout", commit or (kind == "staged" and "HEAD" or nil), "--", path), - toplevel - ) - end - if code ~= 0 then - utils.err("Failed to revert file! See ':DiffviewLog' for details.", true) - return callback() - end - - local rev_name = (commit and commit:sub(1, 11)) or (kind == "staged" and "HEAD" or "index") - utils.info(("File restored from %s. Undo with %s"):format(rev_name, undo), true) - callback() -end, 5) return M diff --git a/lua/diffview/vcs/utils.lua b/lua/diffview/vcs/utils.lua index 18ce5e56..fa842cbe 100644 --- a/lua/diffview/vcs/utils.lua +++ b/lua/diffview/vcs/utils.lua @@ -436,5 +436,26 @@ function M.filter_completion(arg_lead, items) end, items) end +---Restore a file to the state it was in, in a given commit / rev. If no commit +---is given, unstaged files are restored to the state in index, and staged files +---are restored to the state in HEAD. The file will also be written into the +---object database such that the action can be undone. +---@param adapter VCSAdapter +---@param path string +---@param kind '"staged"'|'"working"' +---@param commit string +M.restore_file = async.wrap(function(adapter, path, kind, commit, callback) + local undo = adapter:file_restore(path, kind, commit) + if not undo then + utils.err("Failed to revert file! See ':DiffviewLog' for details.", true) + return callback() + end + + local rev_name = (commit and commit:sub(1, 11)) or (kind == "staged" and "HEAD" or "index") + utils.info(("File restored from %s. Undo with %s"):format(rev_name, undo), true) + callback() +end, 5) + + M.JobStatus = JobStatus return M From 7ecfbc12aac0e757ef8fb7e7391dc63fcd41319c Mon Sep 17 00:00:00 2001 From: zegervdv Date: Sun, 30 Oct 2022 10:36:04 +0000 Subject: [PATCH 54/71] fixup! refactor: implement restore --- lua/diffview/scene/views/diff/listeners.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lua/diffview/scene/views/diff/listeners.lua b/lua/diffview/scene/views/diff/listeners.lua index fc422224..a1a64124 100644 --- a/lua/diffview/scene/views/diff/listeners.lua +++ b/lua/diffview/scene/views/diff/listeners.lua @@ -4,7 +4,7 @@ local actions = lazy.require("diffview.actions") ---@module "diffview.actions" local Event = lazy.access("diffview.events", "Event") ---@type EEvent local RevType = lazy.access("diffview.vcs.rev", "RevType") ---@type ERevType local async = lazy.require("plenary.async") ---@module "plenary.async" -local vcs = lazy.require("diffview.vcs") ---@module "diffview.vcs" +local vcs = lazy.require("diffview.vcs.utils") ---@module "diffview.vcs.utils" local utils = lazy.require("diffview.utils") ---@module "diffview.utils" local api = vim.api @@ -34,7 +34,7 @@ return function(view) end end, buf_write_post = function() - if vcs.has_local(view.left, view.right) then + if view.adapter:has_local(view.left, view.right) then view.update_needed = true if api.nvim_get_current_tabpage() == view.tabpage then view:update_files() @@ -220,7 +220,7 @@ return function(view) utils.err("The file is open with unsaved changes! Aborting file restoration.") return end - vcs.restore_file(view.adapter.ctx.toplevel, file.path, file.kind, commit, function() + vcs.restore_file(view.adapter, file.path, file.kind, commit, function() async.util.scheduler() view:update_files() end) From 5d8bd98b8a412f47bbe9f578543a83c533cabf7c Mon Sep 17 00:00:00 2001 From: zegervdv Date: Sun, 30 Oct 2022 10:43:20 +0000 Subject: [PATCH 55/71] fixup! fixup! refactor: implement restore --- lua/diffview/scene/views/diff/listeners.lua | 16 ++++++++-------- lua/diffview/vcs/adapter.lua | 14 ++++++++++++++ lua/diffview/vcs/adapters/git/init.lua | 10 ++++++++++ 3 files changed, 32 insertions(+), 8 deletions(-) diff --git a/lua/diffview/scene/views/diff/listeners.lua b/lua/diffview/scene/views/diff/listeners.lua index a1a64124..821e1273 100644 --- a/lua/diffview/scene/views/diff/listeners.lua +++ b/lua/diffview/scene/views/diff/listeners.lua @@ -121,14 +121,14 @@ return function(view) local item = view:infer_cur_file(true) if item then - local code + local success if item.kind == "working" or item.kind == "conflicting" then - _, code = vcs.exec_sync({ "add", item.path }, view.adapter.ctx.toplevel) + success = view.adapter:add_file(item.path) elseif item.kind == "staged" then - _, code = vcs.exec_sync({ "reset", "--", item.path }, view.adapter.ctx.toplevel) + success = view.adapter:reset_file(item.path) end - if code ~= 0 then + if not success then utils.err(("Failed to stage/unstage file: '%s'"):format(item.path)) return end @@ -180,9 +180,9 @@ return function(view) end, view.files.working) if #args > 0 then - local _, code = vcs.exec_sync({ "add", args }, view.adapter.ctx.toplevel) + local success = view.adapter:add_file(args) - if code ~= 0 then + if not success then utils.err("Failed to stage files!") return end @@ -194,9 +194,9 @@ return function(view) end end, unstage_all = function() - local _, code = vcs.exec_sync({ "reset" }, view.adapter.ctx.toplevel) + local success = view.adapter:reset_file() - if code ~= 0 then + if not success then utils.err("Failed to unstage files!") return end diff --git a/lua/diffview/vcs/adapter.lua b/lua/diffview/vcs/adapter.lua index cf0f9c53..bb8ee7ec 100644 --- a/lua/diffview/vcs/adapter.lua +++ b/lua/diffview/vcs/adapter.lua @@ -186,6 +186,20 @@ function VCSAdapter:restore_file(path, kind, commit) oop.abstract_stub() end +---Add file(s) +---@param paths string[] +---@return boolean # add was successful +function VCSAdapter:add_file(paths) + oop.abstract_stub() +end + +---Reset file(s) +---@param paths string[] +---@return boolean # reset was successful +function VCSAdapter:reset_file(paths) + oop.abstract_stub() +end + ---@param args string[] ---@return {left: string, right: string, options: string[]} function VCSAdapter:diffview_options(args) diff --git a/lua/diffview/vcs/adapters/git/init.lua b/lua/diffview/vcs/adapters/git/init.lua index 27b721f1..b1c6ec6f 100644 --- a/lua/diffview/vcs/adapters/git/init.lua +++ b/lua/diffview/vcs/adapters/git/init.lua @@ -1304,6 +1304,16 @@ function GitAdapter:file_restore(path, kind, commit) return undo end +function GitAdapter:reset_file(paths) + local _, code = self:exec_sync({"reset", "--", paths}, self.ctx.toplevel) + return code == 0 +end + +function GitAdapter:add_file(paths) + local _, code = self:exec_sync({"add", "--", paths}, self.ctx.toplevel) + return code == 0 +end + ---Check if status for untracked files is disabled for a given git repo. ---@return boolean function GitAdapter:show_untracked() From 214784645bff3e00c78d9ba2677a357f2ddf544d Mon Sep 17 00:00:00 2001 From: zegervdv Date: Sun, 30 Oct 2022 17:51:50 +0000 Subject: [PATCH 56/71] refactor: enable merge resolution --- lua/diffview/actions.lua | 2 +- lua/diffview/vcs/adapters/git/utils.lua | 140 ----------------------- lua/diffview/vcs/utils.lua | 141 ++++++++++++++++++++++++ 3 files changed, 142 insertions(+), 141 deletions(-) diff --git a/lua/diffview/actions.lua b/lua/diffview/actions.lua index ab8da8d1..04c49a33 100644 --- a/lua/diffview/actions.lua +++ b/lua/diffview/actions.lua @@ -3,7 +3,7 @@ local lazy = require("diffview.lazy") local DiffView = lazy.access("diffview.scene.views.diff.diff_view", "DiffView") ---@type DiffView|LazyModule local FileHistoryView = lazy.access("diffview.scene.views.file_history.file_history_view", "FileHistoryView") ---@type FileHistoryView|LazyModule local StandardView = lazy.access("diffview.scene.views.standard.standard_view", "StandardView") ---@type StandardView|LazyModule -local vcs = lazy.require("diffview.vcs") ---@module "diffview.vcs" +local vcs = lazy.require("diffview.vcs.utils") ---@module "diffview.vcs.utils" local lib = lazy.require("diffview.lib") ---@module "diffview.lib" local utils = lazy.require("diffview.utils") ---@module "diffview.utils" diff --git a/lua/diffview/vcs/adapters/git/utils.lua b/lua/diffview/vcs/adapters/git/utils.lua index 0971e513..4ea9338e 100644 --- a/lua/diffview/vcs/adapters/git/utils.lua +++ b/lua/diffview/vcs/adapters/git/utils.lua @@ -16,144 +16,4 @@ local api = vim.api local M = {} -local CONFLICT_START = [[^<<<<<<< ]] -local CONFLICT_BASE = [[^||||||| ]] -local CONFLICT_SEP = [[^=======$]] -local CONFLICT_END = [[^>>>>>>> ]] - ----@class ConflictRegion ----@field first integer ----@field last integer ----@field ours { first: integer, last: integer, content?: string[] } ----@field base { first: integer, last: integer, content?: string[] } ----@field theirs { first: integer, last: integer, content?: string[] } - ----@param lines string[] ----@param winid? integer ----@return ConflictRegion[] conflicts ----@return ConflictRegion? cur_conflict The conflict under the cursor in the given window. ----@return integer cur_conflict_idx Index of the current conflict. Will be 0 if the cursor if before the first conflict, and `#conflicts + 1` if the cursor is after the last conflict. -function M.parse_conflicts(lines, winid) - local ret = {} - local has_start, has_base, has_sep = false, false, false - local cur, cursor, cur_conflict, cur_idx - - if winid and api.nvim_win_is_valid(winid) then - cursor = api.nvim_win_get_cursor(winid) - end - - local function handle(data) - local first = math.huge - local last = -1 - - first = math.min(data.ours.first or math.huge, first) - first = math.min(data.base.first or math.huge, first) - first = math.min(data.theirs.first or math.huge, first) - - if first == math.huge then return end - - last = math.max(data.ours.last or -1, -1) - last = math.max(data.base.last or -1, -1) - last = math.max(data.theirs.last or -1, -1) - - if last == -1 then return end - - if data.ours.first and data.ours.last and data.ours.first < data.ours.last then - data.ours.content = utils.vec_slice(lines, data.ours.first + 1, data.ours.last) - end - - if data.base.first and data.base.last and data.base.first < data.base.last then - data.base.content = utils.vec_slice(lines, data.base.first + 1, data.base.last) - end - - if data.theirs.first and data.theirs.last and data.theirs.first < data.theirs.last - 1 then - data.theirs.content = utils.vec_slice(lines, data.theirs.first + 1, data.theirs.last - 1) - end - - if cursor then - if not cur_conflict and cursor[1] >= first and cursor[1] <= last then - cur_conflict = data - cur_idx = #ret + 1 - elseif cursor[1] > last then - cur_idx = (cur_idx or 0) + 1 - end - end - - data.first = first - data.last = last - ret[#ret + 1] = data - end - - local function new_cur() - return { - ours = {}, - base = {}, - theirs = {}, - } - end - - cur = new_cur() - - for i, line in ipairs(lines) do - if line:match(CONFLICT_START) then - if has_start then - handle(cur) - cur, has_start, has_base, has_sep = new_cur(), false, false, false - end - - has_start = true - cur.ours.first = i - cur.ours.last = i - elseif line:match(CONFLICT_BASE) then - if has_base then - handle(cur) - cur, has_start, has_base, has_sep = new_cur(), false, false, false - end - - has_base = true - cur.base.first = i - cur.ours.last = i - 1 - elseif line:match(CONFLICT_SEP) then - if has_sep then - handle(cur) - cur, has_start, has_base, has_sep = new_cur(), false, false, false - end - - has_sep = true - cur.theirs.first = i - cur.theirs.last = i - - if has_base then - cur.base.last = i - 1 - else - cur.ours.last = i - 1 - end - elseif line:match(CONFLICT_END) then - if not has_sep then - if has_base then - cur.base.last = i - 1 - elseif has_start then - cur.ours.last = i - 1 - end - end - - cur.theirs.first = cur.theirs.first or i - cur.theirs.last = i - handle(cur) - cur, has_start, has_base, has_sep = new_cur(), false, false, false - end - end - - handle(cur) - - if cursor and cur_idx then - if cursor[1] > ret[#ret].last then - cur_idx = #ret + 1 - end - end - - return ret, cur_conflict, cur_idx or 0 -end - - return M diff --git a/lua/diffview/vcs/utils.lua b/lua/diffview/vcs/utils.lua index fa842cbe..d35ef947 100644 --- a/lua/diffview/vcs/utils.lua +++ b/lua/diffview/vcs/utils.lua @@ -8,6 +8,8 @@ local RevType = require("diffview.vcs.rev").RevType local Job = require("plenary.job") local Semaphore = require('diffview.control').Semaphore +local api = vim.api + local M = {} ---@enum JobStatus @@ -456,6 +458,145 @@ M.restore_file = async.wrap(function(adapter, path, kind, commit, callback) callback() end, 5) +local CONFLICT_START = [[^<<<<<<< ]] +local CONFLICT_BASE = [[^||||||| ]] +local CONFLICT_SEP = [[^=======$]] +local CONFLICT_END = [[^>>>>>>> ]] + +---@class ConflictRegion +---@field first integer +---@field last integer +---@field ours { first: integer, last: integer, content?: string[] } +---@field base { first: integer, last: integer, content?: string[] } +---@field theirs { first: integer, last: integer, content?: string[] } + +---@param lines string[] +---@param winid? integer +---@return ConflictRegion[] conflicts +---@return ConflictRegion? cur_conflict The conflict under the cursor in the given window. +---@return integer cur_conflict_idx Index of the current conflict. Will be 0 if the cursor if before the first conflict, and `#conflicts + 1` if the cursor is after the last conflict. +function M.parse_conflicts(lines, winid) + local ret = {} + local has_start, has_base, has_sep = false, false, false + local cur, cursor, cur_conflict, cur_idx + + if winid and api.nvim_win_is_valid(winid) then + cursor = api.nvim_win_get_cursor(winid) + end + + local function handle(data) + local first = math.huge + local last = -1 + + first = math.min(data.ours.first or math.huge, first) + first = math.min(data.base.first or math.huge, first) + first = math.min(data.theirs.first or math.huge, first) + + if first == math.huge then return end + + last = math.max(data.ours.last or -1, -1) + last = math.max(data.base.last or -1, -1) + last = math.max(data.theirs.last or -1, -1) + + if last == -1 then return end + + if data.ours.first and data.ours.last and data.ours.first < data.ours.last then + data.ours.content = utils.vec_slice(lines, data.ours.first + 1, data.ours.last) + end + + if data.base.first and data.base.last and data.base.first < data.base.last then + data.base.content = utils.vec_slice(lines, data.base.first + 1, data.base.last) + end + + if data.theirs.first and data.theirs.last and data.theirs.first < data.theirs.last - 1 then + data.theirs.content = utils.vec_slice(lines, data.theirs.first + 1, data.theirs.last - 1) + end + + if cursor then + if not cur_conflict and cursor[1] >= first and cursor[1] <= last then + cur_conflict = data + cur_idx = #ret + 1 + elseif cursor[1] > last then + cur_idx = (cur_idx or 0) + 1 + end + end + + data.first = first + data.last = last + ret[#ret + 1] = data + end + + local function new_cur() + return { + ours = {}, + base = {}, + theirs = {}, + } + end + + cur = new_cur() + + for i, line in ipairs(lines) do + if line:match(CONFLICT_START) then + if has_start then + handle(cur) + cur, has_start, has_base, has_sep = new_cur(), false, false, false + end + + has_start = true + cur.ours.first = i + cur.ours.last = i + elseif line:match(CONFLICT_BASE) then + if has_base then + handle(cur) + cur, has_start, has_base, has_sep = new_cur(), false, false, false + end + + has_base = true + cur.base.first = i + cur.ours.last = i - 1 + elseif line:match(CONFLICT_SEP) then + if has_sep then + handle(cur) + cur, has_start, has_base, has_sep = new_cur(), false, false, false + end + + has_sep = true + cur.theirs.first = i + cur.theirs.last = i + + if has_base then + cur.base.last = i - 1 + else + cur.ours.last = i - 1 + end + elseif line:match(CONFLICT_END) then + if not has_sep then + if has_base then + cur.base.last = i - 1 + elseif has_start then + cur.ours.last = i - 1 + end + end + + cur.theirs.first = cur.theirs.first or i + cur.theirs.last = i + handle(cur) + cur, has_start, has_base, has_sep = new_cur(), false, false, false + end + end + + handle(cur) + + if cursor and cur_idx then + if cursor[1] > ret[#ret].last then + cur_idx = #ret + 1 + end + end + + return ret, cur_conflict, cur_idx or 0 +end + M.JobStatus = JobStatus return M From 6d7ac927f8b959678d0e20ae68bf5bb0dbeec4e9 Mon Sep 17 00:00:00 2001 From: zegervdv Date: Sun, 30 Oct 2022 17:52:33 +0000 Subject: [PATCH 57/71] refactor: delete unused file --- lua/diffview/vcs/adapters/git/utils.lua | 19 ------------------- 1 file changed, 19 deletions(-) delete mode 100644 lua/diffview/vcs/adapters/git/utils.lua diff --git a/lua/diffview/vcs/adapters/git/utils.lua b/lua/diffview/vcs/adapters/git/utils.lua deleted file mode 100644 index 4ea9338e..00000000 --- a/lua/diffview/vcs/adapters/git/utils.lua +++ /dev/null @@ -1,19 +0,0 @@ -local CountDownLatch = require("diffview.control").CountDownLatch -local Diff2Hor = require("diffview.scene.layouts.diff_2_hor").Diff2Hor -local FileDict = require("diffview.vcs.file_dict").FileDict -local FileEntry = require("diffview.scene.file_entry").FileEntry -local Job = require("plenary.job") -local LogEntry = require("diffview.vcs.log_entry").LogEntry -local Rev = require("diffview.vcs.rev").Rev -local RevType = require("diffview.vcs.rev").RevType -local async = require("plenary.async") -local logger = require("diffview.logger") -local utils = require("diffview.utils") -local JobStatus = require("diffview.vcs.utils").JobStatus - -local api = vim.api - -local M = {} - - -return M From d5cb5fd5b83791fca2c5bd513a262058b5632cc8 Mon Sep 17 00:00:00 2001 From: zegervdv Date: Mon, 31 Oct 2022 09:37:28 +0000 Subject: [PATCH 58/71] refactor: clean up last references to vcs module --- lua/diffview/scene/views/diff/diff_view.lua | 2 +- lua/diffview/scene/views/file_history/file_history_panel.lua | 1 - lua/diffview/scene/views/file_history/file_history_view.lua | 1 - lua/diffview/scene/views/file_history/listeners.lua | 3 +-- lua/diffview/vcs/adapter.lua | 5 +++-- lua/diffview/vcs/adapters/git/init.lua | 1 - lua/diffview/vcs/file.lua | 2 +- lua/diffview/vcs/rev.lua | 4 ---- 8 files changed, 6 insertions(+), 13 deletions(-) diff --git a/lua/diffview/scene/views/diff/diff_view.lua b/lua/diffview/scene/views/diff/diff_view.lua index e5ac73c1..494b79d4 100644 --- a/lua/diffview/scene/views/diff/diff_view.lua +++ b/lua/diffview/scene/views/diff/diff_view.lua @@ -13,7 +13,7 @@ local RevType = lazy.access("diffview.vcs.rev", "RevType") ---@type RevType|Lazy local StandardView = lazy.access("diffview.scene.views.standard.standard_view", "StandardView") ---@type StandardView|LazyModule local async = lazy.require("plenary.async") ---@module "plenary.async" local debounce = lazy.require("diffview.debounce") ---@module "diffview.debounce" -local vcs = lazy.require("diffview.vcs.utils") ---@module "diffview.vcs" +local vcs = lazy.require("diffview.vcs.utils") ---@module "diffview.vcs.utils" local logger = lazy.require("diffview.logger") ---@module "diffview.logger" local utils = lazy.require("diffview.utils") ---@module "diffview.utils" local config = lazy.require("diffview.config") ---@module "diffview.config" diff --git a/lua/diffview/scene/views/file_history/file_history_panel.lua b/lua/diffview/scene/views/file_history/file_history_panel.lua index 242e5590..c3608193 100644 --- a/lua/diffview/scene/views/file_history/file_history_panel.lua +++ b/lua/diffview/scene/views/file_history/file_history_panel.lua @@ -6,7 +6,6 @@ local Panel = lazy.access("diffview.ui.panel", "Panel") ---@type Panel|LazyModul local PerfTimer = lazy.access("diffview.perf", "PerfTimer") ---@type PerfTimer|LazyModule local config = lazy.require("diffview.config") ---@module "diffview.config" local debounce = lazy.require("diffview.debounce") ---@module "diffview.debounce" -local vcs = lazy.require("diffview.vcs") ---@module "diffview.vcs" local logger = lazy.require("diffview.logger") ---@module "diffview.logger" local oop = lazy.require("diffview.oop") ---@module "diffview.oop" local panel_renderer = lazy.require("diffview.scene.views.file_history.render") ---@module "diffview.scene.views.file_history.render" diff --git a/lua/diffview/scene/views/file_history/file_history_view.lua b/lua/diffview/scene/views/file_history/file_history_view.lua index fa5dd34a..ecef4f16 100644 --- a/lua/diffview/scene/views/file_history/file_history_view.lua +++ b/lua/diffview/scene/views/file_history/file_history_view.lua @@ -7,7 +7,6 @@ local FileEntry = lazy.access("diffview.scene.file_entry", "FileEntry") ---@type local FileHistoryPanel = lazy.access("diffview.scene.views.file_history.file_history_panel", "FileHistoryPanel") ---@type FileHistoryPanel|LazyModule local StandardView = lazy.access("diffview.scene.views.standard.standard_view", "StandardView") ---@type StandardView|LazyModule local config = lazy.require("diffview.config") ---@module "diffview.config" -local vcs = lazy.require("diffview.vcs") ---@module "diffview.vcs" local JobStatus = lazy.access("diffview.vcs.utils", "JobStatus") ---@type JobStatus|LazyModule local api = vim.api diff --git a/lua/diffview/scene/views/file_history/listeners.lua b/lua/diffview/scene/views/file_history/listeners.lua index 5df9602c..5f1d38fe 100644 --- a/lua/diffview/scene/views/file_history/listeners.lua +++ b/lua/diffview/scene/views/file_history/listeners.lua @@ -2,7 +2,6 @@ local lazy = require("diffview.lazy") local DiffView = lazy.access("diffview.scene.views.diff.diff_view", "DiffView") ---@type DiffView|LazyModule local JobStatus = lazy.access("diffview.vcs.utils", "JobStatus") ---@type JobStatus|LazyModule -local vcs = lazy.require("diffview.vcs") ---@module "diffview.vcs" local lib = lazy.require("diffview.lib") ---@module "diffview.lib" local utils = lazy.require("diffview.utils") ---@module "diffview.utils" @@ -73,7 +72,7 @@ return function(view) local new_view = DiffView({ adapter = view.adapter, - rev_arg = vcs.rev_to_pretty_string(layout.a.file.rev, layout.b.file.rev), + rev_arg = view.adapter:rev_to_pretty_string(layout.a.file.rev, layout.b.file.rev), left = layout.a.file.rev, right = layout.b.file.rev, options = {}, diff --git a/lua/diffview/vcs/adapter.lua b/lua/diffview/vcs/adapter.lua index bb8ee7ec..9870818d 100644 --- a/lua/diffview/vcs/adapter.lua +++ b/lua/diffview/vcs/adapter.lua @@ -3,6 +3,7 @@ local utils = require('diffview.utils') local logger = require('diffview.logger') local arg_parser = require('diffview.arg_parser') local RevType = require("diffview.vcs.rev").RevType +local Rev = require("diffview.vcs.rev").Rev local M = {} @@ -15,8 +16,7 @@ local M = {} ---@field context string[] local VCSAdapter = oop.create_class('VCSAdapter') ----The VCS implements a database to store file states -VCSAdapter.has_database = false +VCSAdapter.Rev = Rev ---@class vcs.adapter.VCSAdapter.Opt ---@field cpath string? # CWD path @@ -243,6 +243,7 @@ function VCSAdapter:is_binary(path, rev) oop.abstract_stub() end +---Initialize completion parameters function VCSAdapter:init_completion() oop.abstract_stub() end diff --git a/lua/diffview/vcs/adapters/git/init.lua b/lua/diffview/vcs/adapters/git/init.lua index b1c6ec6f..196b622f 100644 --- a/lua/diffview/vcs/adapters/git/init.lua +++ b/lua/diffview/vcs/adapters/git/init.lua @@ -27,7 +27,6 @@ local M = {} local GitAdapter = oop.create_class('GitAdapter', VCSAdapter) GitAdapter.Rev = GitRev -GitAdapter.has_database = true ---@return string, string local function pathspec_split(pathspec) diff --git a/lua/diffview/vcs/file.lua b/lua/diffview/vcs/file.lua index 108e3682..1e42dfbe 100644 --- a/lua/diffview/vcs/file.lua +++ b/lua/diffview/vcs/file.lua @@ -9,7 +9,7 @@ local Rev = lazy.access("diffview.vcs.rev", "Rev") local RevType = lazy.access("diffview.vcs.rev", "RevType") ---@module "diffview.config" local config = lazy.require("diffview.config") ----@module "diffview.vcs" +---@module "diffview.vcs.utils" local vcs = lazy.require("diffview.vcs.utils") ---@module "diffview.utils" local utils = lazy.require("diffview.utils") diff --git a/lua/diffview/vcs/rev.lua b/lua/diffview/vcs/rev.lua index 46ad3108..41895181 100644 --- a/lua/diffview/vcs/rev.lua +++ b/lua/diffview/vcs/rev.lua @@ -1,9 +1,5 @@ -local lazy = require("diffview.lazy") local oop = require("diffview.oop") ----@module "diffview.vcs" -local git = lazy.require("diffview.vcs") - local M = {} ---@class RevType : EnumValue From 326eb7448acb8315709ddaa513309888108b4c6a Mon Sep 17 00:00:00 2001 From: zegervdv Date: Tue, 1 Nov 2022 09:26:32 +0000 Subject: [PATCH 59/71] fix: return err and toplevel from find_toplevel() --- lua/diffview/vcs/adapters/git/init.lua | 4 ++-- lua/diffview/vcs/init.lua | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lua/diffview/vcs/adapters/git/init.lua b/lua/diffview/vcs/adapters/git/init.lua index 196b622f..ba3ade5d 100644 --- a/lua/diffview/vcs/adapters/git/init.lua +++ b/lua/diffview/vcs/adapters/git/init.lua @@ -113,7 +113,7 @@ function M.find_toplevel(top_indicators) if p and pl:readable(p) then toplevel = get_toplevel(p) if toplevel then - return toplevel + return nil, toplevel end end end @@ -124,7 +124,7 @@ function M.find_toplevel(top_indicators) local rel_path = pl:relative(v, ".") return utils.str_quote(rel_path == "" and "." or rel_path) end, top_indicators) --[[@as vector ]], ", ")) - ) + ), nil end ---@param toplevel string diff --git a/lua/diffview/vcs/init.lua b/lua/diffview/vcs/init.lua index 6b3e67b5..dac19f13 100644 --- a/lua/diffview/vcs/init.lua +++ b/lua/diffview/vcs/init.lua @@ -29,9 +29,9 @@ function M.get_adapter(opt) if not top_indicators then path_args, top_indicators = adapter.get_repo_paths(opt.cmd_ctx.path_args, opt.cmd_ctx.cpath) end - local toplevel = adapter.find_toplevel(top_indicators) + local err, toplevel = adapter.find_toplevel(top_indicators) - if toplevel then + if not err then -- Create a new adapter instance. Store the resolved path args and the -- cpath in the adapter context. return nil, adapter.create(toplevel, path_args, opt.cmd_ctx.cpath) From e8681023cf7cb488a73a26870171c133615f0f0c Mon Sep 17 00:00:00 2001 From: zegervdv Date: Wed, 2 Nov 2022 17:45:38 +0000 Subject: [PATCH 60/71] refactor: apply review comments --- lua/diffview/scene/views/diff/listeners.lua | 10 +- .../scene/views/file_history/option_panel.lua | 23 ++-- .../scene/views/file_history/render.lua | 1 - lua/diffview/vcs/adapter.lua | 100 ++++++++++++++---- lua/diffview/vcs/adapters/git/init.lua | 64 ++++------- lua/diffview/vcs/adapters/git/rev.lua | 2 +- lua/diffview/vcs/init.lua | 1 - lua/diffview/vcs/utils.lua | 6 +- 8 files changed, 110 insertions(+), 97 deletions(-) diff --git a/lua/diffview/scene/views/diff/listeners.lua b/lua/diffview/scene/views/diff/listeners.lua index 821e1273..25405bdb 100644 --- a/lua/diffview/scene/views/diff/listeners.lua +++ b/lua/diffview/scene/views/diff/listeners.lua @@ -105,7 +105,7 @@ return function(view) or ( view.left.type == RevType.COMMIT and vim.tbl_contains({ RevType.STAGE, RevType.LOCAL }, view.right.type) - and view.left:is_head(view.adapter.ctx.toplevel) + and view.left:is_head(view.adapter) ) then utils.info("Changes not commited yet. No log available for these changes.") return @@ -123,9 +123,9 @@ return function(view) if item then local success if item.kind == "working" or item.kind == "conflicting" then - success = view.adapter:add_file(item.path) + success = view.adapter:add_files({item.path}) elseif item.kind == "staged" then - success = view.adapter:reset_file(item.path) + success = view.adapter:reset_files({item.path}) end if not success then @@ -180,7 +180,7 @@ return function(view) end, view.files.working) if #args > 0 then - local success = view.adapter:add_file(args) + local success = view.adapter:add_files(args) if not success then utils.err("Failed to stage files!") @@ -194,7 +194,7 @@ return function(view) end end, unstage_all = function() - local success = view.adapter:reset_file() + local success = view.adapter:reset_files() if not success then utils.err("Failed to unstage files!") diff --git a/lua/diffview/scene/views/file_history/option_panel.lua b/lua/diffview/scene/views/file_history/option_panel.lua index 28fdc218..e6f01579 100644 --- a/lua/diffview/scene/views/file_history/option_panel.lua +++ b/lua/diffview/scene/views/file_history/option_panel.lua @@ -5,7 +5,6 @@ local JobStatus = lazy.access("diffview.vcs.utils", "JobStatus") ---@type JobSta local Panel = lazy.access("diffview.ui.panel", "Panel") ---@type Panel|LazyModule local arg_parser = lazy.require("diffview.arg_parser") ---@module "diffview.arg_parser" local config = lazy.require("diffview.config") ---@module "diffview.config" -local diffview = lazy.require("diffview") ---@module "diffview" local oop = lazy.require("diffview.oop") ---@module "diffview.oop" local panel_renderer = lazy.require("diffview.scene.views.file_history.render") ---@module "diffview.scene.views.file_history.render" local utils = lazy.require("diffview.utils") ---@module "diffview.utils" @@ -42,43 +41,33 @@ FHOptionPanel.bufopts = { bufhidden = "hide", } ----@class FlagOption : string[] ----@field key string ----@field prompt_label string ----@field prompt_fmt string ----@field select string[] ----@field completion string|fun(panel: FHOptionPanel): function ----@field transform fun(values: string[]): any # Transform the values given by the user. ----@field render_value fun(option: FlagOption, value: string|string[]): boolean, string # Render the flag value in the panel. ----@field render_default fun(options: FlagOption, value: string|string[]): string # Render the default text for the input(). - ---FHOptionPanel constructor. ---@param parent FileHistoryPanel -function FHOptionPanel:init(parent, flags) +function FHOptionPanel:init(parent) FHOptionPanel:super().init(self, { ---@type PanelSplitSpec config = { position = "bottom", - height = #flags.switches + #flags.options + 4, + height = #parent.adapter.flags.switches + #parent.adapter.flags.options + 4, }, bufname = "DiffviewFHOptionPanel", }) self.parent = parent self.emitter = EventEmitter() - self.flags = flags + self.flags = parent.adapter.flags ---@param option_name string self.emitter:on("set_option", function(option_name) local log_options = self.parent:get_log_options() local cur_value = log_options[option_name] - if flags.switches[option_name] then + if self.flags.switches[option_name] then self:_set_option(option_name, not cur_value) self:render() self:redraw() - elseif flags.options[option_name] then - local o = flags.options[option_name] + elseif self.flags.options[option_name] then + local o = self.flags.options[option_name] local prompt = utils.str_template(o.prompt_fmt, { label = o.prompt_label and o.prompt_label .. " " or "", flag_name = o[2] .. " ", diff --git a/lua/diffview/scene/views/file_history/render.lua b/lua/diffview/scene/views/file_history/render.lua index 85dd63bd..278f2539 100644 --- a/lua/diffview/scene/views/file_history/render.lua +++ b/lua/diffview/scene/views/file_history/render.lua @@ -227,7 +227,6 @@ return { comp:add_line(cached.root_path) local offset - vim.inspect(panel) if panel.single_file then line_idx = line_idx + 1 if #panel.entries > 0 then diff --git a/lua/diffview/vcs/adapter.lua b/lua/diffview/vcs/adapter.lua index 9870818d..1de6b8be 100644 --- a/lua/diffview/vcs/adapter.lua +++ b/lua/diffview/vcs/adapter.lua @@ -12,8 +12,9 @@ local M = {} ---@field merge_layout Layout ---@class VCSAdapter: diffview.Object ----@field bootstrap boolean[] ----@field context string[] +---@field bootstrap table +---@field ctx string[] +---@field flags table local VCSAdapter = oop.create_class('VCSAdapter') VCSAdapter.Rev = Rev @@ -44,11 +45,43 @@ function VCSAdapter:run_bootstrap() self.bootstrap.ok = true end ----@return string # path to binary for VCS command +---@diagnostic disable: unused-local, missing-return + +---@param path string +---@param rev Rev +---@return boolean -- True if the file was binary for the given rev, or it didn't exist. +function VCSAdapter:is_binary(path, rev) + oop.abstract_stub() +end + +---Initialize completion parameters +function VCSAdapter:init_completion() + oop.abstract_stub() +end + +---@class RevCompletionSpec +---@field accept_range boolean + +---Completion for revisions. +---@param arg_lead string +---@param opt? RevCompletionSpec +---@return string[] +function VCSAdapter:rev_completion(arg_lead, opt) + oop.abstract_stub() +end + +---@return Rev? +function VCSAdapter:head_rev() + oop.abstract_stub() +end + +---@return string[] # path to binary for VCS command function VCSAdapter:get_command() oop.abstract_stub() end +---@diagnostic enable: unused-local, missing-return + ---@return string cmd The VCS binary. function VCSAdapter:bin() return self:get_command()[1] @@ -99,8 +132,10 @@ end -- File History +---@diagnostic disable: unused-local, missing-return + ---@param args string[] ----@return string[] args to show commit content +---@return string?[] args to show commit content function VCSAdapter:get_show_args(args) oop.abstract_stub() end @@ -124,6 +159,8 @@ function VCSAdapter:file_history_worker(thread, log_opt, opt, co_state, callback oop.abstract_stub() end +---@diagnostic enable: unused-local, missing-return + ---@param log_opt ConfigLogOptions ---@param opt vcs.adapter.FileHistoryWorkerSpec ---@param callback function @@ -148,6 +185,8 @@ end -- Diff View +---@diagnostic disable: unused-local, missing-return + ---Convert revs to rev args. ---@param left Rev ---@param right Rev @@ -157,21 +196,21 @@ function VCSAdapter:rev_to_args(left, right) end ---Arguments to show name and status of files ----@param args string[] Extra args +---@param args string?[] Extra args ---@return string[] function VCSAdapter:get_namestat_args(args) oop.abstract_stub() end ---Arguments to show number of changes to files ----@param args string[] Extra args +---@param args string?[] Extra args ---@return string[] function VCSAdapter:get_numstat_args(args) oop.abstract_stub() end ---Arguments to list all files ----@param args string[] Extra args +---@param args string?[] Extra args ---@return string[] function VCSAdapter:get_files_args(args) oop.abstract_stub() @@ -189,14 +228,14 @@ end ---Add file(s) ---@param paths string[] ---@return boolean # add was successful -function VCSAdapter:add_file(paths) +function VCSAdapter:add_files(paths) oop.abstract_stub() end ---Reset file(s) ----@param paths string[] +---@param paths string?[] ---@return boolean # reset was successful -function VCSAdapter:reset_file(paths) +function VCSAdapter:reset_files(paths) oop.abstract_stub() end @@ -209,9 +248,20 @@ end ---Check if status for untracked files is disabled ---@return boolean function VCSAdapter:show_untracked() - return true + oop.abstract_stub() end +---Restore file +---@param path string +---@param kind '"staged"' | '"working"' +---@param commit string? +---@return boolean # Restore was successful +function VCSAdapter:file_restore(path, kind, commit) + oop.abstract_stub() +end + +---@diagnostic enable: unused-local, missing-return + ---Convert revs to string representation. ---@param left Rev ---@param right Rev @@ -235,18 +285,22 @@ function VCSAdapter:has_local(left, right) return left.type == RevType.LOCAL or right.type == RevType.LOCAL end - ----@param path string ----@param rev Rev ----@return boolean -- True if the file was binary for the given rev, or it didn't exist. -function VCSAdapter:is_binary(path, rev) - oop.abstract_stub() -end - ----Initialize completion parameters -function VCSAdapter:init_completion() - oop.abstract_stub() -end +---@class FlagOption : string[] +---@field key string +---@field prompt_label string +---@field prompt_fmt string +---@field select string[] +---@field completion string|fun(panel: FHOptionPanel): function +---@field transform fun(values: string[]): any # Transform the values given by the user. +---@field render_value fun(option: FlagOption, value: string|string[]): boolean, string # Render the flag value in the panel. +---@field render_default fun(options: FlagOption, value: string|string[]): string # Render the default text for the input(). + +VCSAdapter.flags = { + ---@type FlagOption[] + switches = {}, + ---@type FlagOption[] + options = {}, +} M.VCSAdapter = VCSAdapter return M diff --git a/lua/diffview/vcs/adapters/git/init.lua b/lua/diffview/vcs/adapters/git/init.lua index ba3ade5d..4fa0c4d4 100644 --- a/lua/diffview/vcs/adapters/git/init.lua +++ b/lua/diffview/vcs/adapters/git/init.lua @@ -11,7 +11,6 @@ local Diff2Hor = require("diffview.scene.layouts.diff_2_hor").Diff2Hor local LogEntry = require("diffview.vcs.log_entry").LogEntry local RevType = require("diffview.vcs.rev").RevType local GitRev = require("diffview.vcs.adapters.git.rev").GitRev ----@class VCSAdapter local VCSAdapter = require('diffview.vcs.adapter').VCSAdapter local Job = require("plenary.job") local JobStatus = require('diffview.vcs.utils').JobStatus @@ -245,7 +244,7 @@ end ---@field path_args string[] ---@field flags string[] ----@param adapter VCSAdapter +---@param adapter GitAdapter ---@param log_options LogOptions ---@param single_file boolean ---@return GitAdapter.PreparedLogOpts @@ -625,8 +624,7 @@ function GitAdapter:file_history_options(range, paths, args) local cfile = pl:vim_expand("%") cfile = pl:readlink(cfile) or cfile - ---@cast git_toplevel string - logger.lvl(1).s_debug(("Found git top-level: %s"):format(utils.str_quote(git_toplevel))) + logger.lvl(1).s_debug(("Found git top-level: %s"):format(utils.str_quote(self.ctx.toplevel))) rel_paths = vim.tbl_map(function(v) return v == "." and "." or pl:relative(v, ".") @@ -640,7 +638,7 @@ function GitAdapter:file_history_options(range, paths, args) ---@type string local range_arg = argo:get_flag("range", { no_empty = true }) if range_arg then - local ok = self:verify_rev_arg(self.ctx.toplevel, range_arg) + local ok = self:verify_rev_arg(range_arg) if not ok then utils.err(("Bad revision: %s"):format(utils.str_quote(range_arg))) return @@ -763,7 +761,7 @@ end ---@field path_args string[] ---@field log_options LogOptions ---@field prepared_log_opts GitAdapter.PreparedLogOpts ----@field opt GitAdapter.FileHistoryWorkerSpec +---@field opt vcs.adapter.FileHistoryWorkerSpec ---@field single_file boolean ---@field resume_lock boolean ---@field cur table @@ -795,12 +793,12 @@ local function parse_fh_data(state) "--", state.old_path or state.path_args ), - cwd = state.ctx.toplevel, + cwd = state.adapter.ctx.toplevel, on_exit = function(j) if j.code == 0 then cur.namestat = j:result() end - handle_co(state.thread, coroutine.resume(state.thread)) + state.adapter:handle_co(state.thread, coroutine.resume(state.thread)) end, } @@ -1106,7 +1104,6 @@ function GitAdapter:is_rev_arg_range(rev_arg) end ---Parse a given rev arg. ----@param git_toplevel string ---@param rev_arg string ---@param opt table ---@return Rev? left @@ -1303,13 +1300,21 @@ function GitAdapter:file_restore(path, kind, commit) return undo end -function GitAdapter:reset_file(paths) - local _, code = self:exec_sync({"reset", "--", paths}, self.ctx.toplevel) +function GitAdapter:reset_files(paths) + local arg_paths = {} + if #paths > 0 then + arg_paths = vim.tbl_extend("force", { "--" }, paths) + end + local _, code = self:exec_sync({"reset", arg_paths}, self.ctx.toplevel) return code == 0 end -function GitAdapter:add_file(paths) - local _, code = self:exec_sync({"add", "--", paths}, self.ctx.toplevel) +function GitAdapter:add_files(paths) + local arg_paths = {} + if #paths > 0 then + arg_paths = vim.tbl_extend("force", { "--" }, paths) + end + local _, code = self:exec_sync({"add", arg_paths}, self.ctx.toplevel) return code == 0 end @@ -1370,7 +1375,6 @@ function GitAdapter:is_binary(path, rev) return code ~= 0 end - GitAdapter.flags = { ---@type FlagOption[] switches = { @@ -1566,33 +1570,6 @@ end function GitAdapter:rev_candidates() logger.lvl(1).debug("[completion] Revision candidates requested.") - -- local top_indicators - -- if not (git_toplevel and git_dir) then - -- local cfile = utils.path:vim_expand("%") - -- top_indicators = utils.vec_join( - -- vim.bo.buftype == "" - -- and utils.path:absolute(cfile) - -- or nil, - -- utils.path:realpath(".") - -- ) - -- end - - -- if not (git_toplevel and git_dir) then - -- local err - -- err, git_toplevel = lib.find_git_toplevel(top_indicators) - - -- if err then - -- return {} - -- end - - -- ---@cast git_toplevel string - -- git_dir = vcs.git_dir(git_toplevel) - -- end - - -- if not (git_toplevel and git_dir) then - -- return {} - -- end - -- stylua: ignore start local targets = { "HEAD", "FETCH_HEAD", "ORIG_HEAD", "MERGE_HEAD", @@ -1619,11 +1596,6 @@ function GitAdapter:rev_candidates() return utils.vec_join(heads, revs, stashes) end ----@class RevCompletionSpec ----@field accept_range boolean ----@field git_toplevel string ----@field git_dir string - ---Completion for git revisions. ---@param arg_lead string ---@param opt? RevCompletionSpec diff --git a/lua/diffview/vcs/adapters/git/rev.lua b/lua/diffview/vcs/adapters/git/rev.lua index ffcecb75..6bc043b4 100644 --- a/lua/diffview/vcs/adapters/git/rev.lua +++ b/lua/diffview/vcs/adapters/git/rev.lua @@ -46,7 +46,7 @@ function GitRev:init(rev_type, revision, track_head) end ---@param name string ----@param adapter? GitAdapter +---@param adapter GitAdapter ---@return Rev? function GitRev.from_name(name, adapter) local out, code = adapter:exec_sync({ "rev-parse", "--revs-only", name }, adapter.ctx.toplevel) diff --git a/lua/diffview/vcs/init.lua b/lua/diffview/vcs/init.lua index dac19f13..eb1bfa02 100644 --- a/lua/diffview/vcs/init.lua +++ b/lua/diffview/vcs/init.lua @@ -1,4 +1,3 @@ -local utils = require('diffview.utils') local git = require('diffview.vcs.adapters.git') local hg = require('diffview.vcs.adapters.hg') diff --git a/lua/diffview/vcs/utils.lua b/lua/diffview/vcs/utils.lua index d35ef947..f9862b88 100644 --- a/lua/diffview/vcs/utils.lua +++ b/lua/diffview/vcs/utils.lua @@ -157,7 +157,7 @@ end, 3) ---@param right Rev ---@param args string[] ---@param kind git.FileKind ----@param opt git.utils.LayoutOpt +---@param opt vcs.adapter.LayoutOpt ---@param callback function local tracked_files = async.wrap(function(adapter, left, right, args, kind, opt, callback) ---@type FileEntry[] @@ -281,7 +281,7 @@ end, 7) ---@param adapter VCSAdapter ---@param left Rev ---@param right Rev ----@param opt git.utils.LayoutOpt +---@param opt vcs.adapter.LayoutOpt ---@param callback function local untracked_files = async.wrap(function(adapter, left, right, opt, callback) Job:new({ @@ -326,7 +326,7 @@ end, 5) ---@param right Rev ---@param path_args string[]|nil ---@param dv_opt DiffViewOptions ----@param opt git.utils.LayoutOpt +---@param opt vcs.adapter.LayoutOpt ---@param callback function ---@return string[]? err ---@return FileDict? From 9c51e5d1fb394ea65d71daded20ef0aa5e4879c8 Mon Sep 17 00:00:00 2001 From: zegervdv Date: Thu, 3 Nov 2022 17:16:56 +0000 Subject: [PATCH 61/71] fixup! refactor: apply review comments --- lua/diffview/vcs/adapters/git/init.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lua/diffview/vcs/adapters/git/init.lua b/lua/diffview/vcs/adapters/git/init.lua index 4fa0c4d4..d7ca3e5b 100644 --- a/lua/diffview/vcs/adapters/git/init.lua +++ b/lua/diffview/vcs/adapters/git/init.lua @@ -101,7 +101,7 @@ end ---paths. ---@param top_indicators string[] A list of paths that might indicate what working tree we are in. ---@return string? err ----@return string? toplevel # as an absolute path +---@return string toplevel # as an absolute path function M.find_toplevel(top_indicators) local toplevel for _, p in ipairs(top_indicators) do @@ -123,11 +123,11 @@ function M.find_toplevel(top_indicators) local rel_path = pl:relative(v, ".") return utils.str_quote(rel_path == "" and "." or rel_path) end, top_indicators) --[[@as vector ]], ", ")) - ), nil + ), "" end ---@param toplevel string ----@param path_args string? +---@param path_args string[] ---@param cpath string? function M.create(toplevel, path_args, cpath) return GitAdapter({ From 02f178d63a070a051e42fd49e083838749be7dc2 Mon Sep 17 00:00:00 2001 From: zegervdv Date: Thu, 3 Nov 2022 17:23:12 +0000 Subject: [PATCH 62/71] fixup! refactor: apply review comments --- lua/diffview/vcs/adapter.lua | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/lua/diffview/vcs/adapter.lua b/lua/diffview/vcs/adapter.lua index 1de6b8be..ce6aec9a 100644 --- a/lua/diffview/vcs/adapter.lua +++ b/lua/diffview/vcs/adapter.lua @@ -11,10 +11,19 @@ local M = {} ---@field default_layout Diff2 ---@field merge_layout Layout +---@class vcs.adapter.VCSAdapter.Flags +---@field switches FlagOption[] +---@field options FlagOption[] + +---@class vcs.adapter.VCSAdapter.Ctx +---@field toplevel string # VCS repository toplevel directory +---@field dir string # VCS directory +---@field path_args string[] # Extra path arguments + ---@class VCSAdapter: diffview.Object ---@field bootstrap table ----@field ctx string[] ----@field flags table +---@field ctx vcs.adapter.VCSAdapter.Ctx +---@field flags vcs.adapter.VCSAdapter.Flags local VCSAdapter = oop.create_class('VCSAdapter') VCSAdapter.Rev = Rev From f6bc23c88d352a22223b75aefa882f0e2b8c6adb Mon Sep 17 00:00:00 2001 From: zegervdv Date: Thu, 3 Nov 2022 17:26:57 +0000 Subject: [PATCH 63/71] fixup! refactor: apply review comments --- lua/diffview/vcs/adapter.lua | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lua/diffview/vcs/adapter.lua b/lua/diffview/vcs/adapter.lua index ce6aec9a..a81b153d 100644 --- a/lua/diffview/vcs/adapter.lua +++ b/lua/diffview/vcs/adapter.lua @@ -11,6 +11,13 @@ local M = {} ---@field default_layout Diff2 ---@field merge_layout Layout + +---@class vcs.adapter.VCSAdapter.Bootstrap +---@field done boolean # Did the bootstrapping +---@field ok boolean # Bootstrapping was successful +---@field version table +---@field version_string string[] + ---@class vcs.adapter.VCSAdapter.Flags ---@field switches FlagOption[] ---@field options FlagOption[] @@ -21,7 +28,7 @@ local M = {} ---@field path_args string[] # Extra path arguments ---@class VCSAdapter: diffview.Object ----@field bootstrap table +---@field bootstrap vcs.adapter.VCSAdapter.Bootstrap ---@field ctx vcs.adapter.VCSAdapter.Ctx ---@field flags vcs.adapter.VCSAdapter.Flags local VCSAdapter = oop.create_class('VCSAdapter') From b4ce9b15e2e6f21110d2a02fd10dd8ecba8d9f01 Mon Sep 17 00:00:00 2001 From: zegervdv Date: Thu, 3 Nov 2022 17:30:25 +0000 Subject: [PATCH 64/71] fixup! refactor: apply review comments --- lua/diffview/vcs/adapter.lua | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lua/diffview/vcs/adapter.lua b/lua/diffview/vcs/adapter.lua index a81b153d..7e2f0acc 100644 --- a/lua/diffview/vcs/adapter.lua +++ b/lua/diffview/vcs/adapter.lua @@ -16,7 +16,9 @@ local M = {} ---@field done boolean # Did the bootstrapping ---@field ok boolean # Bootstrapping was successful ---@field version table ----@field version_string string[] +---@field version_string string +---@field target_version table +---@field target_version_string string ---@class vcs.adapter.VCSAdapter.Flags ---@field switches FlagOption[] @@ -46,7 +48,7 @@ function VCSAdapter:init(opt) done = false, ok = false, version = {}, - version_string = {}, + version_string = "", } self.ctx = {} From 589d233777f53a47b7cb4c3fad7331a817a66666 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sindre=20T=2E=20Str=C3=B8m?= Date: Fri, 4 Nov 2022 21:22:00 +0100 Subject: [PATCH 65/71] refactor: Update CDiffView to use adapter model. --- lua/diffview/api/views/diff/diff_view.lua | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/lua/diffview/api/views/diff/diff_view.lua b/lua/diffview/api/views/diff/diff_view.lua index 430b5c1b..7ad66b77 100644 --- a/lua/diffview/api/views/diff/diff_view.lua +++ b/lua/diffview/api/views/diff/diff_view.lua @@ -33,16 +33,19 @@ local CDiffView = oop.create_class("CDiffView", DiffView.__get()) function CDiffView:init(opt) logger.info("[api] Creating a new Custom DiffView.") self.valid = false - local git_dir = vcs.git_dir(opt.git_root) - if not git_dir then + local err, adapter = vcs.get_adapter({ top_indicators = { opt.git_root } }) + + if err then utils.err( - ("Failed to find the git dir for the repository: %s") + ("Failed to create an adapter for the repository: %s") :format(utils.str_quote(opt.git_root)) ) return end + ---@cast adapter -? + -- Fix malformed revs for _, v in ipairs({ "left", "right" }) do local rev = opt[v] @@ -54,18 +57,13 @@ function CDiffView:init(opt) self.fetch_files = opt.update_files self.get_file_data = opt.get_file_data - local adapter = { - toplevel = opt.git_root, - dir = git_dir, - } - CDiffView:super().init(self, vim.tbl_extend("force", opt, { adapter = adapter, panel = FilePanel( adapter, self.files, self.path_args, - self.rev_arg or vcs.rev_to_pretty_string(opt.left, opt.right) + self.rev_arg or adapter:rev_to_pretty_string(opt.left, opt.right) ), })) @@ -128,7 +126,7 @@ function CDiffView:create_file_entries(files) { kind = "staged", files = files.staged or {}, - left = vcs.head_rev(self.adapter.ctx.toplevel), + left = self.adapter:head_rev(), right = Rev(RevType.STAGE, 0), }, } From cd42ab0d6f769528adbd489d7e56bc73f79e597c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sindre=20T=2E=20Str=C3=B8m?= Date: Fri, 4 Nov 2022 21:23:55 +0100 Subject: [PATCH 66/71] refactor: Simplify `{reset|add}_files()`. --- lua/diffview/vcs/adapters/git/init.lua | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/lua/diffview/vcs/adapters/git/init.lua b/lua/diffview/vcs/adapters/git/init.lua index d7ca3e5b..1a229216 100644 --- a/lua/diffview/vcs/adapters/git/init.lua +++ b/lua/diffview/vcs/adapters/git/init.lua @@ -1301,20 +1301,12 @@ function GitAdapter:file_restore(path, kind, commit) end function GitAdapter:reset_files(paths) - local arg_paths = {} - if #paths > 0 then - arg_paths = vim.tbl_extend("force", { "--" }, paths) - end - local _, code = self:exec_sync({"reset", arg_paths}, self.ctx.toplevel) + local _, code = self:exec_sync(utils.vec_join("reset", "--", paths), self.ctx.toplevel) return code == 0 end function GitAdapter:add_files(paths) - local arg_paths = {} - if #paths > 0 then - arg_paths = vim.tbl_extend("force", { "--" }, paths) - end - local _, code = self:exec_sync({"add", arg_paths}, self.ctx.toplevel) + local _, code = self:exec_sync(utils.vec_join("add", "--", paths), self.ctx.toplevel) return code == 0 end From e3e07655541ea80b6d75d795ea4e1199d51a52b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sindre=20T=2E=20Str=C3=B8m?= Date: Fri, 4 Nov 2022 21:35:25 +0100 Subject: [PATCH 67/71] fix: Annotations for optional array parameters. --- lua/diffview/vcs/adapter.lua | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lua/diffview/vcs/adapter.lua b/lua/diffview/vcs/adapter.lua index 7e2f0acc..d60d39ad 100644 --- a/lua/diffview/vcs/adapter.lua +++ b/lua/diffview/vcs/adapter.lua @@ -153,7 +153,7 @@ end ---@diagnostic disable: unused-local, missing-return ---@param args string[] ----@return string?[] args to show commit content +---@return string[]? args to show commit content function VCSAdapter:get_show_args(args) oop.abstract_stub() end @@ -214,21 +214,21 @@ function VCSAdapter:rev_to_args(left, right) end ---Arguments to show name and status of files ----@param args string?[] Extra args +---@param args string[]? Extra args ---@return string[] function VCSAdapter:get_namestat_args(args) oop.abstract_stub() end ---Arguments to show number of changes to files ----@param args string?[] Extra args +---@param args string[]? Extra args ---@return string[] function VCSAdapter:get_numstat_args(args) oop.abstract_stub() end ---Arguments to list all files ----@param args string?[] Extra args +---@param args string[]? Extra args ---@return string[] function VCSAdapter:get_files_args(args) oop.abstract_stub() @@ -251,7 +251,7 @@ function VCSAdapter:add_files(paths) end ---Reset file(s) ----@param paths string?[] +---@param paths string[]? ---@return boolean # reset was successful function VCSAdapter:reset_files(paths) oop.abstract_stub() From 24b91d445ab4e0c7dfd86e810d6c9ea7ab66ed3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sindre=20T=2E=20Str=C3=B8m?= Date: Fri, 4 Nov 2022 21:56:30 +0100 Subject: [PATCH 68/71] feat: Make completion handle failure more gracefully. --- lua/diffview/init.lua | 42 ++++++++++++--------------- lua/diffview/vcs/adapters/hg/init.lua | 2 +- 2 files changed, 19 insertions(+), 25 deletions(-) diff --git a/lua/diffview/init.lua b/lua/diffview/init.lua index 8d82d02a..671dfc81 100644 --- a/lua/diffview/init.lua +++ b/lua/diffview/init.lua @@ -173,10 +173,8 @@ function M.get_adapter() local err, adapter = vcs.get_adapter({ top_indicators = top_indicators }) - -- TODO: Can we flag an error here? if err then - utils.err(err) - return nil + logger.s_warn("[completion] Failed to create adapter: " .. err) end return adapter @@ -188,10 +186,6 @@ M.completers = { local has_rev_arg = false local adapter = M.get_adapter() - if not adapter then - return nil - end - for i = 2, math.min(#ctx.args, ctx.divideridx) do if ctx.args[i]:sub(1, 1) ~= "-" and i ~= ctx.argidx then has_rev_arg = true @@ -203,16 +197,18 @@ M.completers = { if ctx.argidx > ctx.divideridx then utils.vec_push(candidates, unpack(vim.fn.getcompletion(ctx.arg_lead, "file", 0))) - elseif not has_rev_arg and ctx.arg_lead:sub(1, 1) ~= "-" and adapter.ctx.dir and adapter.ctx.toplevel then - utils.vec_push(candidates, unpack(adapter.comp.open:get_all_names())) - utils.vec_push(candidates, unpack(adapter:rev_completion(ctx.arg_lead, { - accept_range= true, - }))) - else - utils.vec_push(candidates, unpack( - adapter.comp.open:get_completion(ctx.arg_lead) - or adapter.comp.open:get_all_names() - )) + elseif adapter then + if not has_rev_arg and ctx.arg_lead:sub(1, 1) ~= "-" then + utils.vec_push(candidates, unpack(adapter.comp.open:get_all_names())) + utils.vec_push(candidates, unpack(adapter:rev_completion(ctx.arg_lead, { + accept_range = true, + }))) + else + utils.vec_push(candidates, unpack( + adapter.comp.open:get_completion(ctx.arg_lead) + or adapter.comp.open:get_all_names() + )) + end end return candidates @@ -222,15 +218,13 @@ M.completers = { local adapter = M.get_adapter() local candidates = {} - if not adapter then - return nil + if adapter then + utils.vec_push(candidates, unpack( + adapter.comp.file_history:get_completion(ctx.arg_lead) + or adapter.comp.file_history:get_all_names() + )) end - utils.vec_push(candidates, unpack( - adapter.comp.file_history:get_completion(ctx.arg_lead) - or adapter.comp.file_history:get_all_names() - )) - utils.vec_push(candidates, unpack(vim.fn.getcompletion(ctx.arg_lead, "file", 0))) return candidates diff --git a/lua/diffview/vcs/adapters/hg/init.lua b/lua/diffview/vcs/adapters/hg/init.lua index f12479a5..8866427e 100644 --- a/lua/diffview/vcs/adapters/hg/init.lua +++ b/lua/diffview/vcs/adapters/hg/init.lua @@ -12,7 +12,7 @@ end function M.find_toplevel(top_indicators) -- TODO: implement - return nil + return "", nil end M.HgAdapter = HgAdapter From 18ad9193982afaf7f101b0bdd04fdd563995dc7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sindre=20T=2E=20Str=C3=B8m?= Date: Tue, 8 Nov 2022 15:00:08 +0100 Subject: [PATCH 69/71] fixup! refactor: Update CDiffView to use adapter model. --- lua/diffview/api/views/diff/diff_view.lua | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lua/diffview/api/views/diff/diff_view.lua b/lua/diffview/api/views/diff/diff_view.lua index 7ad66b77..d0877149 100644 --- a/lua/diffview/api/views/diff/diff_view.lua +++ b/lua/diffview/api/views/diff/diff_view.lua @@ -3,7 +3,7 @@ local lazy = require("diffview.lazy") local DiffView = lazy.access("diffview.scene.views.diff.diff_view", "DiffView") ---@type DiffView|LazyModule local FileEntry = lazy.access("diffview.scene.file_entry", "FileEntry") ---@type FileEntry|LazyModule local FilePanel = lazy.access("diffview.scene.views.diff.file_panel", "FilePanel") ---@type FilePanel|LazyModule -local Rev = lazy.access("diffview.vcs.rev", "Rev") ---@type Rev|LazyModule +local Rev = lazy.access("diffview.vcs.adapters.git.rev", "GitRev") ---@type GitRev|LazyModule local RevType = lazy.access("diffview.vcs.rev", "RevType") ---@type RevType|LazyModule local async = lazy.require("plenary.async") ---@module "plenary.async" local vcs = lazy.require("diffview.vcs") ---@module "diffview.vcs" @@ -146,7 +146,6 @@ function CDiffView:create_file_entries(files) rev_main = Rev(RevType.LOCAL), rev_theirs = Rev(RevType.STAGE, 3), rev_base = Rev(RevType.STAGE, 1), - get_data = self.get_file_data, })) else table.insert(entries[v.kind], FileEntry.for_d2(CDiffView.get_default_diff2(), { From 3b4467dc60ab0702bd4640d162977af8e58298c9 Mon Sep 17 00:00:00 2001 From: zegervdv Date: Tue, 8 Nov 2022 16:56:15 +0000 Subject: [PATCH 70/71] refactor: address review comments --- lua/diffview/lib.lua | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lua/diffview/lib.lua b/lua/diffview/lib.lua index 5314f583..593fb844 100644 --- a/lua/diffview/lib.lua +++ b/lua/diffview/lib.lua @@ -3,7 +3,6 @@ local lazy = require("diffview.lazy") local DiffView = lazy.access("diffview.scene.views.diff.diff_view", "DiffView") ---@type DiffView|LazyModule local FileHistoryView = lazy.access("diffview.scene.views.file_history.file_history_view", "FileHistoryView") ---@type FileHistoryView|LazyModule local Rev = lazy.access("diffview.vcs.rev", "Rev") ---@type Rev|LazyModule -local RevType = lazy.access("diffview.vcs.rev", "RevType") ---@type ERevType|LazyModule local StandardView = lazy.access("diffview.scene.views.standard.standard_view", "StandardView") ---@type StandardView|LazyModule local arg_parser = lazy.require("diffview.arg_parser") ---@module "diffview.arg_parser" local config = lazy.require("diffview.config") ---@module "diffview.config" @@ -45,6 +44,11 @@ function M.diffview_open(args) local opts = adapter:diffview_options(args) + if opts == nil then + utils.err('Failed to create log options for diffview_open') + return + end + ---@type DiffView local v = DiffView({ adapter = adapter, @@ -96,7 +100,7 @@ function M.file_history(range, args) local log_options = adapter:file_history_options(range, rel_paths, args) if log_options == nil then - utils.err('Failed to create log options for file_history') + return end ---@type FileHistoryView From b5d60db04663c2c7ce554dde46b5096e8d7e610f Mon Sep 17 00:00:00 2001 From: zegervdv Date: Fri, 11 Nov 2022 09:50:02 +0000 Subject: [PATCH 71/71] fixup! refactor: address review comments --- lua/diffview/lib.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/lua/diffview/lib.lua b/lua/diffview/lib.lua index 593fb844..d5edad78 100644 --- a/lua/diffview/lib.lua +++ b/lua/diffview/lib.lua @@ -45,7 +45,6 @@ function M.diffview_open(args) local opts = adapter:diffview_options(args) if opts == nil then - utils.err('Failed to create log options for diffview_open') return end