From 6494af86e811dca3ccbc94f429b0c0144e97cc2e Mon Sep 17 00:00:00 2001 From: Brian Ryall Date: Thu, 4 Jan 2024 14:58:03 -0500 Subject: [PATCH] refactor: add back in telescope --- flake.lock | 59 ++- flake.nix | 13 +- lua/git-worktree/git.lua | 109 +----- lua/git-worktree/git_spec.lua | 6 - lua/git-worktree/hooks.lua | 29 -- lua/telescope/_extensions/git_worktree.lua | 432 ++++++++++----------- 6 files changed, 291 insertions(+), 357 deletions(-) diff --git a/flake.lock b/flake.lock index c2f2a63..06bcec6 100644 --- a/flake.lock +++ b/flake.lock @@ -38,6 +38,24 @@ "inputs": { "systems": "systems" }, + "locked": { + "lastModified": 1701680307, + "narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "4022d587cbbfd70fe950c1e2083a02621806a725", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "flake-utils_2": { + "inputs": { + "systems": "systems_2" + }, "locked": { "lastModified": 1685518550, "narHash": "sha256-o2d0KcvaXzTrPRIo0kOLV0/QXHhDQ5DTi+OxcjO8xqY=", @@ -89,6 +107,29 @@ "type": "github" } }, + "neovim": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "dir": "contrib", + "lastModified": 1704031853, + "narHash": "sha256-DOxfnhrIdTDWb+b9vKiuXq7zGTIhzC4g0EEP1uh36xs=", + "owner": "neovim", + "repo": "neovim", + "rev": "6fa0f303d7f0823bfc5ba6cc7b4e7a7cd76143ac", + "type": "github" + }, + "original": { + "dir": "contrib", + "owner": "neovim", + "repo": "neovim", + "type": "github" + } + }, "nixpkgs": { "locked": { "lastModified": 1703499205, @@ -158,7 +199,7 @@ "pre-commit-hooks": { "inputs": { "flake-compat": "flake-compat", - "flake-utils": "flake-utils", + "flake-utils": "flake-utils_2", "gitignore": "gitignore", "nixpkgs": [ "nixpkgs" @@ -183,6 +224,7 @@ "inputs": { "flake-parts": "flake-parts", "neodev-nvim": "neodev-nvim", + "neovim": "neovim", "nixpkgs": "nixpkgs", "plenary-nvim": "plenary-nvim", "pre-commit-hooks": "pre-commit-hooks", @@ -204,6 +246,21 @@ "type": "github" } }, + "systems_2": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, "telescope-nvim": { "flake": false, "locked": { diff --git a/flake.nix b/flake.nix index b0e4798..6317377 100644 --- a/flake.nix +++ b/flake.nix @@ -9,6 +9,10 @@ inputs.nixpkgs.follows = "nixpkgs"; }; + neovim = { + url = "github:neovim/neovim?dir=contrib"; + inputs.nixpkgs.follows = "nixpkgs"; + }; neodev-nvim = { url = "github:folke/neodev.nvim"; flake = false; @@ -55,6 +59,9 @@ _module.args.pkgs = import inputs.nixpkgs { inherit system; overlays = [ + (final: _: { + neovim-nightly = inputs.neovim.packages.${final.system}.neovim; + }) ]; }; devShells = { @@ -81,12 +88,6 @@ name = "telescope.nvim"; src = inputs.telescope-nvim; }; - packages.neorocks-test-stable = pkgs.callPackage ./nix/neorocks-test.nix { - name = "git-worktree-stable"; - inherit self; - nvim = pkgs.neovim-unwrapped; - inherit (config.packages) plenary-plugin; - }; checks = { inherit pre-commit-check; diff --git a/lua/git-worktree/git.lua b/lua/git-worktree/git.lua index 1c94fdb..adc9474 100644 --- a/lua/git-worktree/git.lua +++ b/lua/git-worktree/git.lua @@ -57,54 +57,6 @@ function M.has_worktree(path_str, cb) job:start() end --- --- @return boolean --- function M.is_bare_repo() --- local inside_worktree_job = Job:new({ --- "git", --- "rev-parse", --- "--is-bare-repository", --- cwd = vim.loop.cwd(), --- }) --- --- local stdout, code = inside_worktree_job:sync() --- if code ~= 0 then --- status:log().error("Error in determining if we are in a worktree") --- return false --- end --- --- stdout = table.concat(stdout, "") --- --- if stdout == "true" then --- return true --- else --- return false --- end --- end --- --- --- @return boolean --- function M.is_worktree() --- local inside_worktree_job = Job:new({ --- "git", --- "rev-parse", --- "--is-inside-work-tree", --- cwd = vim.loop.cwd(), --- }) --- --- local stdout, code = inside_worktree_job:sync() --- if code ~= 0 then --- status:log().error("Error in determining if we are in a worktree") --- return false --- end --- --- stdout = table.concat(stdout, "") --- --- if stdout == "true" then --- return true --- else --- return false --- end --- end - --- @return string|nil function M.gitroot_dir() local job = Job:new { @@ -113,20 +65,20 @@ function M.gitroot_dir() '--path-format=absolute', '--git-common-dir', cwd = vim.loop.cwd(), - -- on_stderr = function(_, data) - -- status:log().info('ERROR: ' .. data) - -- end, + on_stderr = function(_, data) + Log.error('ERROR: ' .. data) + end, } local stdout, code = job:sync() if code ~= 0 then - -- status:log().error( - -- 'Error in determining the git root dir: code:' - -- .. tostring(code) - -- .. ' out: ' - -- .. table.concat(stdout, '') - -- .. '.' - -- ) + Log.error( + 'Error in determining the git root dir: code:' + .. tostring(code) + .. ' out: ' + .. table.concat(stdout, '') + .. '.' + ) return nil end @@ -141,20 +93,20 @@ function M.toplevel_dir() '--path-format=absolute', '--show-toplevel', cwd = vim.loop.cwd(), - -- on_stderr = function(_, data) - -- status:log().info('ERROR: ' .. data) - -- end, + on_stderr = function(_, data) + Log.error('ERROR: ' .. data) + end, } local stdout, code = job:sync() if code ~= 0 then - -- status:log().error( - -- 'Error in determining the git root dir: code:' - -- .. tostring(code) - -- .. ' out: ' - -- .. table.concat(stdout, '') - -- .. '.' - -- ) + Log.error( + 'Error in determining the git root dir: code:' + .. tostring(code) + .. ' out: ' + .. table.concat(stdout, '') + .. '.' + ) return nil end @@ -180,27 +132,6 @@ function M.has_branch(branch, cb) cb(found) end):start() end --- --- function M.has_origin() --- local found = false --- local job = Job:new({ --- "git", --- "remote", --- "show", --- on_stdout = function(_, data) --- data = vim.trim(data) --- found = found or data == "origin" --- end, --- cwd = vim.loop.cwd(), --- }) --- --- -- TODO: I really don't want status's spread everywhere... seems bad --- job:after(function() --- status:status("found origin: " .. tostring(found)) --- end):sync() --- --- return found --- end --- @param path string --- @param branch string diff --git a/lua/git-worktree/git_spec.lua b/lua/git-worktree/git_spec.lua index 97862dc..6574104 100644 --- a/lua/git-worktree/git_spec.lua +++ b/lua/git-worktree/git_spec.lua @@ -1,8 +1,5 @@ local git_harness = require('git-worktree.test.git_util') local gwt_git = require('git-worktree.git') -local Status = require('git-worktree.status') - -local status = Status:new() -- local wait_for_result = function(job, result) -- if type(result) == 'string' then @@ -20,7 +17,6 @@ describe('git-worktree git operations', function() describe('in normal repo', function() before_each(function() working_dir, master_dir = git_harness.prepare_repo() - status:reset(0) end) after_each(function() vim.api.nvim_command('cd ' .. cwd) @@ -99,7 +95,6 @@ describe('git-worktree git operations', function() describe('in bare repo', function() before_each(function() working_dir = git_harness.prepare_repo_bare() - status:reset(0) end) after_each(function() vim.api.nvim_command('cd ' .. cwd) @@ -177,7 +172,6 @@ describe('git-worktree git operations', function() describe('in worktree repo', function() before_each(function() working_dir, master_dir = git_harness.prepare_repo_bare_worktree(1) - status:reset(0) end) after_each(function() vim.api.nvim_command('cd ' .. cwd) diff --git a/lua/git-worktree/hooks.lua b/lua/git-worktree/hooks.lua index 8cfae90..5815b7c 100644 --- a/lua/git-worktree/hooks.lua +++ b/lua/git-worktree/hooks.lua @@ -1,7 +1,3 @@ --- local Enum = require("git-worktree.enum") --- local Status = require("git-worktree.status") --- local status = Status:new() - --- @class GitWorkTreeHook --- @field SWITCH? fun(...): nil @@ -47,29 +43,4 @@ M.hook_event_names = { SWITCH = 'SWITCH', } --- function M.on_tree_change_handler(op, metadata) --- if M._config.update_on_change then --- if op == Enum.Operations.Switch then --- local changed = M.update_current_buffer(metadata["prev_path"]) --- if not changed then --- status --- :log() --- .debug("Could not change to the file in the new worktree, --- running the `update_on_change_command`") --- vim.cmd(M._config.update_on_change_command) --- end --- end --- end --- end - --- function M.emit_on_change(op, metadata) --- -- TODO: We don't have a way to async update what is running --- status:next_status(string.format("Running post %s callbacks", op)) --- print(metadata) --- -- on_tree_change_handler(op, metadata) --- -- for idx = 1, #on_change_callbacks do --- -- on_change_callbacks[idx](op, metadata) --- -- end --- end - return M diff --git a/lua/telescope/_extensions/git_worktree.lua b/lua/telescope/_extensions/git_worktree.lua index 5cc7862..7d2cf46 100644 --- a/lua/telescope/_extensions/git_worktree.lua +++ b/lua/telescope/_extensions/git_worktree.lua @@ -1,230 +1,210 @@ --- local strings = require("plenary.strings") --- local pickers = require("telescope.pickers") --- local finders = require("telescope.finders") --- local actions = require("telescope.actions") --- local utils = require("telescope.utils") --- local action_set = require("telescope.actions.set") --- local action_state = require("telescope.actions.state") --- local conf = require("telescope.config").values --- local git_worktree = require("git-worktree") - --- local force_next_deletion = false --- --- local get_worktree_path = function(prompt_bufnr) --- local selection = action_state.get_selected_entry(prompt_bufnr) --- return selection.path --- end --- --- local switch_worktree = function(prompt_bufnr) --- local worktree_path = get_worktree_path(prompt_bufnr) --- actions.close(prompt_bufnr) --- if worktree_path ~= nil then --- git_worktree.switch_worktree(worktree_path) --- end --- end --- --- local toggle_forced_deletion = function() --- -- redraw otherwise the message is not displayed when in insert mode --- if force_next_deletion then --- print("The next deletion will not be forced") --- vim.fn.execute("redraw") --- else --- print("The next deletion will be forced") --- vim.fn.execute("redraw") --- force_next_deletion = true --- end --- end --- --- local delete_success_handler = function() --- force_next_deletion = false --- end --- --- local delete_failure_handler = function() --- print("Deletion failed, use to force the next deletion") --- end --- --- local ask_to_confirm_deletion = function(forcing) --- if forcing then --- return vim.fn.input("Force deletion of worktree? [y/n]: ") --- end --- --- return vim.fn.input("Delete worktree? [y/n]: ") --- end --- --- local confirm_deletion = function(forcing) --- if not git_worktree._config.confirm_telescope_deletions then --- return true --- end --- --- local confirmed = ask_to_confirm_deletion(forcing) --- --- if string.sub(string.lower(confirmed), 0, 1) == "y" then --- return true --- end --- --- print("Didn't delete worktree") --- return false --- end --- --- local delete_worktree = function(prompt_bufnr) --- if not confirm_deletion() then --- return --- end --- --- local worktree_path = get_worktree_path(prompt_bufnr) --- actions.close(prompt_bufnr) --- if worktree_path ~= nil then --- git_worktree.delete_worktree(worktree_path, force_next_deletion, { --- on_failure = delete_failure_handler, --- on_success = delete_success_handler, --- }) --- end --- end --- --- local create_input_prompt = function(cb) --- --[[ --- local window = Window.centered({ --- width = 30, --- height = 1 --- }) --- vim.api.nvim_buf_set_option(window.bufnr, "buftype", "prompt") --- vim.fn.prompt_setprompt(window.bufnr, "Worktree Location: ") --- vim.fn.prompt_setcallback(window.bufnr, function(text) --- vim.api.nvim_win_close(window.win_id, true) --- vim.api.nvim_buf_delete(window.bufnr, {force = true}) --- cb(text) --- end) --- --- vim.api.nvim_set_current_win(window.win_id) --- vim.fn.schedule(function() --- vim.nvim_command("startinsert") --- end) --- --]] --- -- --- --- local subtree = vim.fn.input("Path to subtree > ") --- cb(subtree) --- end --- --- local create_worktree = function(opts) --- opts = opts or {} --- opts.attach_mappings = function() --- actions.select_default:replace(function(prompt_bufnr, _) --- local selected_entry = action_state.get_selected_entry() --- local current_line = action_state.get_current_line() --- --- actions.close(prompt_bufnr) --- --- local branch = selected_entry ~= nil and selected_entry.value or current_line --- --- if branch == nil then --- return --- end --- --- create_input_prompt(function(name) --- if name == "" then --- name = branch --- end --- git_worktree.create_worktree(name, branch) --- end) --- end) --- --- -- do we need to replace other default maps? --- --- return true --- end --- require("telescope.builtin").git_branches(opts) --- end --- --- local telescope_git_worktree = function(opts) --- opts = opts or {} --- local output = utils.get_os_command_output({ "git", "worktree", "list" }) --- local results = {} --- local widths = { --- path = 0, --- sha = 0, --- branch = 0, --- } --- --- local parse_line = function(line) --- local fields = vim.split(string.gsub(line, "%s+", " "), " ") --- local entry = { --- path = fields[1], --- sha = fields[2], --- branch = fields[3], --- } --- --- if entry.sha ~= "(bare)" then --- local index = #results + 1 --- for key, val in pairs(widths) do --- if key == "path" then --- local new_path = utils.transform_path(opts, entry[key]) --- local path_len = strings.strdisplaywidth(new_path or "") --- widths[key] = math.max(val, path_len) --- else --- widths[key] = math.max(val, strings.strdisplaywidth(entry[key] or "")) --- end --- end --- --- table.insert(results, index, entry) --- end --- end --- --- for _, line in ipairs(output) do --- parse_line(line) --- end --- --- if #results == 0 then --- return --- end --- --- local displayer = require("telescope.pickers.entry_display").create({ --- separator = " ", --- items = { --- { width = widths.branch }, --- { width = widths.path }, --- { width = widths.sha }, --- }, --- }) --- --- local make_display = function(entry) --- return displayer({ --- { entry.branch, "TelescopeResultsIdentifier" }, --- { utils.transform_path(opts, entry.path) }, --- { entry.sha }, --- }) --- end --- --- pickers --- .new(opts or {}, { --- prompt_title = "Git Worktrees", --- finder = finders.new_table({ --- results = results, --- entry_maker = function(entry) --- entry.value = entry.branch --- entry.ordinal = entry.branch --- entry.display = make_display --- return entry --- end, --- }), --- sorter = conf.generic_sorter(opts), --- attach_mappings = function(_, map) --- action_set.select:replace(switch_worktree) --- --- map("i", "", delete_worktree) --- map("n", "", delete_worktree) --- map("i", "", toggle_forced_deletion) --- map("n", "", toggle_forced_deletion) --- --- return true --- end, --- }) --- :find() --- end --- +local strings = require('plenary.strings') +local pickers = require('telescope.pickers') +local finders = require('telescope.finders') +local actions = require('telescope.actions') +local utils = require('telescope.utils') +local action_set = require('telescope.actions.set') +local action_state = require('telescope.actions.state') +local conf = require('telescope.config').values +local git_worktree = require('git-worktree') + +local force_next_deletion = false + +local get_worktree_path = function(prompt_bufnr) + local selection = action_state.get_selected_entry(prompt_bufnr) + return selection.path +end + +local switch_worktree = function(prompt_bufnr) + local worktree_path = get_worktree_path(prompt_bufnr) + actions.close(prompt_bufnr) + if worktree_path ~= nil then + git_worktree:switch_worktree(worktree_path) + end +end + +local toggle_forced_deletion = function() + -- redraw otherwise the message is not displayed when in insert mode + if force_next_deletion then + print('The next deletion will not be forced') + vim.fn.execute('redraw') + else + print('The next deletion will be forced') + vim.fn.execute('redraw') + force_next_deletion = true + end +end + +local delete_success_handler = function() + force_next_deletion = false +end + +local delete_failure_handler = function() + print('Deletion failed, use to force the next deletion') +end + +local ask_to_confirm_deletion = function(forcing) + if forcing then + return vim.fn.input('Force deletion of worktree? [y/n]: ') + end + + return vim.fn.input('Delete worktree? [y/n]: ') +end + +local confirm_deletion = function(forcing) + if not git_worktree._config.confirm_telescope_deletions then + return true + end + + local confirmed = ask_to_confirm_deletion(forcing) + + if string.sub(string.lower(confirmed), 0, 1) == 'y' then + return true + end + + print("Didn't delete worktree") + return false +end + +local delete_worktree = function(prompt_bufnr) + if not confirm_deletion() then + return + end + + local worktree_path = get_worktree_path(prompt_bufnr) + actions.close(prompt_bufnr) + if worktree_path ~= nil then + git_worktree.delete_worktree(worktree_path, force_next_deletion, { + on_failure = delete_failure_handler, + on_success = delete_success_handler, + }) + end +end + +local telescope_git_worktree = function(opts) + opts = opts or {} + local output = utils.get_os_command_output { 'git', 'worktree', 'list' } + local results = {} + local widths = { + path = 0, + sha = 0, + branch = 0, + } + + local parse_line = function(line) + local fields = vim.split(string.gsub(line, '%s+', ' '), ' ') + local entry = { + path = fields[1], + sha = fields[2], + branch = fields[3], + } + + if entry.sha ~= '(bare)' then + local index = #results + 1 + for key, val in pairs(widths) do + if key == 'path' then + local new_path = utils.transform_path(opts, entry[key]) + local path_len = strings.strdisplaywidth(new_path or '') + widths[key] = math.max(val, path_len) + else + widths[key] = math.max(val, strings.strdisplaywidth(entry[key] or '')) + end + end + + table.insert(results, index, entry) + end + end + + for _, line in ipairs(output) do + parse_line(line) + end + + if #results == 0 then + return + end + + local displayer = require('telescope.pickers.entry_display').create { + separator = ' ', + items = { + { width = widths.branch }, + { width = widths.path }, + { width = widths.sha }, + }, + } + + local make_display = function(entry) + return displayer { + { entry.branch, 'TelescopeResultsIdentifier' }, + { utils.transform_path(opts, entry.path) }, + { entry.sha }, + } + end + + pickers + .new(opts or {}, { + prompt_title = 'Git Worktrees', + finder = finders.new_table { + results = results, + entry_maker = function(entry) + entry.value = entry.branch + entry.ordinal = entry.branch + entry.display = make_display + return entry + end, + }, + sorter = conf.generic_sorter(opts), + attach_mappings = function(_, map) + action_set.select:replace(switch_worktree) + + map('i', '', delete_worktree) + map('n', '', delete_worktree) + map('i', '', toggle_forced_deletion) + map('n', '', toggle_forced_deletion) + + return true + end, + }) + :find() +end + +local create_input_prompt = function(cb) + local subtree = vim.fn.input('Path to subtree > ') + cb(subtree) +end + +local create_worktree = function(opts) + opts = opts or {} + opts.attach_mappings = function() + actions.select_default:replace(function(prompt_bufnr, _) + local selected_entry = action_state.get_selected_entry() + local current_line = action_state.get_current_line() + + actions.close(prompt_bufnr) + + local branch = selected_entry ~= nil and selected_entry.value or current_line + + if branch == nil then + return + end + + create_input_prompt(function(name) + if name == '' then + name = branch + end + git_worktree.create_worktree(name, branch) + end) + end) + + -- do we need to replace other default maps? + + return true + end + require('telescope.builtin').git_branches(opts) +end + return require('telescope').register_extension { exports = { - -- git_worktree = telescope_git_worktree, - -- create_git_worktree = create_worktree, + git_worktree = telescope_git_worktree, + create_git_worktree = create_worktree, }, }