diff --git a/README.md b/README.md index 4687c91..9fcf8c7 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,8 @@ shallow clones automatically. It aims to provide a similar experience to - [Examples](#examples) - [:closed_book: Close Buffers](#closed_book-close-buffers) - [Parameters](#parameters) + - [:floppy_disk: Persist Repository](#floppy_disk-persist-repository) + - [Parameters](#parameters) - [:toothbrush: Clean](#toothbrush-clean) - [Parameters](#parameters) - [:broom: Clean All](#broom-clean-all) @@ -50,8 +52,8 @@ shallow clones automatically. It aims to provide a similar experience to - [Parameters](#parameters) - [:goggles: Toggle UI](#goggles-toggle-ui) - [Parameters](#parameters) - - [:telescope: Telescope](#telescope-telescope) - - [:bone: Recent Repositories](#bone-recent-repositories) + - [:pick: Pickers](#pick-pickers) + - [:rock: History](#rock-history) - [:gear: Options](#gear-options) - [:spider_web: URL Parsing](#spider_web-url-parsing) - [Supported URLs](#supported-urls) @@ -105,9 +107,9 @@ Lazier (documentation will not be available until first use): "GitDevClean", "GitDevCleanAll", "GitDevCloseBuffers", + "GitDevHistory", "GitDevOpen", "GitDevPersist", - "GitDevRecents", "GitDevToggleUI", "GitDevXDGHandle", }, @@ -238,21 +240,26 @@ override default window configuration. for this call. -### :telescope: Telescope +### :pick: Pickers +Supports `mini.pick`, `snacks`, `telescope` and a fallback to `vim.ui.select`. +By default, it will use the first picker provider it finds. +See `pickers` in [Options](#gear-options) below. -#### :bone: Recent Repositories -Command: `GitDevRecents` -Telescope: `Telescope git_dev recents` +#### :rock: History +API: `require("git-dev").pickers.history(local_opts)` +Command: `GitDevHistory` -Revisit previously opened repositories via a telescope extension. +A picker for previously opened repositories. Opened repositories are tracked in a simple single file KV store. Its only -purpose (currently) is to be queried by the telescope extension for a -convenient way to re-open previously opened repositories. +purpose (currently) is to be queried by the history picker for a convenient way +to re-open previously opened repositories. + See `history` in [Options](#gear-options) below. ## :gear: Options + ```lua M.config = { -- Whether to delete an opened repository when nvim exits. @@ -353,7 +360,7 @@ M.config = { ---@type "always"|"never"|"current" delete_repo_dir = "current", }, - -- XDG handling of `nvim-getdev` URIs. + -- XDG handling of `nvim-gitdev` URIs. -- Requires: `xdg-mime` and `xdg-open`. xdg_handler = { enabled = false, @@ -367,10 +374,45 @@ M.config = { content = '#!/usr/bin/env sh\nnvim -c GitDevXDGHandle\\ "$@"', }, }, + -- Picker configuration + ---@type GitDevPickerOpts + pickers = { + ---Nil for auto detection. + type = nil, + ---A configuration for the `history` picker + history = { + separator = { + text = " │ ", + hl_group = "WinSeparator", + }, + entry = { + -- Defines the width ratios of `repo_utl`, `ref` and `selected_path` + -- in the picker's results / entries buffer. Setting a `width` below + -- will override the ratio defined for the entry part. + ratios = { 5, 2, 3 }, + repo_url = { + -- Fixed width. + -- If `nil`, it will be determined by window width and the ratios + -- array above. + width = nil, + hl_group = "String", + }, + ref = { + width = nil, + hl_group = "Tag", + }, + selected_path = { + width = nil, + hl_group = "LineNr", + }, + }, + }, + }, -- More verbosity. verbose = false, } ``` + ## :spider_web: URL Parsing It is reasonable to assume that browsing arbitrary Git repositories will diff --git a/lua/git-dev/history.lua b/lua/git-dev/history.lua index 77ec54d..8041ab0 100644 --- a/lua/git-dev/history.lua +++ b/lua/git-dev/history.lua @@ -36,7 +36,7 @@ function History:add(repo, ref, opts, parsed_repo) end function History:get() - return self._store:get_all() + return vim.iter(self._store:least_recent_values(self.n)):rev():totable() end ---@param record GitDevHistoryRecord @@ -45,6 +45,7 @@ function History:key(record) end function History:update_opts(key, opts) + ---@type GitDevHistoryRecord local record = self._store:get(key) if not record then return diff --git a/lua/git-dev/init.lua b/lua/git-dev/init.lua index c410c5e..1972b00 100644 --- a/lua/git-dev/init.lua +++ b/lua/git-dev/init.lua @@ -2,6 +2,7 @@ local M = {} local U = require "git-dev.utils" +-- gitdev-config-start M.config = { -- Whether to delete an opened repository when nvim exits. -- If `true`, it will create an auto command for opened repositories @@ -101,7 +102,7 @@ M.config = { ---@type "always"|"never"|"current" delete_repo_dir = "current", }, - -- XDG handling of `nvim-getdev` URIs. + -- XDG handling of `nvim-gitdev` URIs. -- Requires: `xdg-mime` and `xdg-open`. xdg_handler = { enabled = false, @@ -115,12 +116,48 @@ M.config = { content = '#!/usr/bin/env sh\nnvim -c GitDevXDGHandle\\ "$@"', }, }, + -- Picker configuration + ---@type GitDevPickerOpts + pickers = { + ---Nil for auto detection. + type = nil, + ---A configuration for the `history` picker + history = { + separator = { + text = " │ ", + hl_group = "WinSeparator", + }, + entry = { + -- Defines the width ratios of `repo_utl`, `ref` and `selected_path` + -- in the picker's results / entries buffer. Setting a `width` below + -- will override the ratio defined for the entry part. + ratios = { 5, 2, 3 }, + repo_url = { + -- Fixed width. + -- If `nil`, it will be determined by window width and the ratios + -- array above. + width = nil, + hl_group = "String", + }, + ref = { + width = nil, + hl_group = "Tag", + }, + selected_path = { + width = nil, + hl_group = "LineNr", + }, + }, + }, + }, -- More verbosity. verbose = false, } +-- gitdev-config-end M.session = nil M.history = nil +M.pickers = nil local cd_func = { global = U.cmd_to_func "cd", @@ -536,6 +573,9 @@ M.setup = function(opts) ---@type GitDevSession M.session = require("git-dev.session"):init() + -- Configure picker + M.pickers = require("git-dev.pickers").setup(M.config.pickers) + local xdg = require "git-dev.xdg" if M.config.xdg_handler.enabled then xdg.enable(M.config.xdg_handler) @@ -595,11 +635,18 @@ M.setup = function(opts) complete = complete_from_session, }) - vim.api.nvim_create_user_command( - "GitDevRecents", - "Telescope git_dev recents", - { desc = "Revisit previously opened repositories." } - ) + -- TODO: Remove + vim.api.nvim_create_user_command("GitDevRecents", function() + vim.notify( + "'GitDevRecents' has been renamed to 'GitDevHistory' and will be removed in the future.", + vim.log.levels.WARN + ) + M.pickers:history() + end, { desc = "Revisit previously opened repositories." }) + + vim.api.nvim_create_user_command("GitDevHistory", function() + M.pickers:history() + end, { desc = "Revisit previously opened repositories." }) end return M diff --git a/lua/git-dev/parser.lua b/lua/git-dev/parser.lua index e74a7ea..8dc33a5 100644 --- a/lua/git-dev/parser.lua +++ b/lua/git-dev/parser.lua @@ -34,7 +34,6 @@ end ---@field base_uri_format string ---@field default_org? string ---@field extra_domain_to_parser? table ----@field parse function local Parser = {} ---@param o Parser diff --git a/lua/git-dev/pickers/init.lua b/lua/git-dev/pickers/init.lua new file mode 100644 index 0000000..38985bf --- /dev/null +++ b/lua/git-dev/pickers/init.lua @@ -0,0 +1,223 @@ +---@class SeparatorConfig +---@field text? string +---@field hl_group? string + +---@class EntryConfig +---@field width? number +---@field hl_group? string + +---@class HistoryEntryConfig +---@field ratios number[]? +---@field repo_url EntryConfig +---@field ref EntryConfig +---@field selected_path EntryConfig + +---@class HistoryLocalOpts +---@field entry HistoryEntryConfig +---@field separator SeparatorConfig + +---@class GitDevPickerOpts +---@field type nil|"mini"|"snacks"|"telescope"|"uiselect" +---@field history HistoryLocalOpts + +local P = { + config = { + history = { + title = "Recently Opened", + get_entries = function() + return require("git-dev").history:get() + end, + -- Action on selection + select_entry = function(entry) + if entry then + require("git-dev").open( + entry.args.repo, + entry.args.ref, + entry.args.opts + ) + end + end, + -- An array of an entry text parts + label_parts = function(entry) + return { + entry.parsed.repo_url, + entry.parsed.commit or entry.parsed.branch or "", + entry.parsed.selected_path or "", + } + end, + preview = function(buf_id, win_id, item) + if win_id then + vim.api.nvim_set_option_value("wrap", true, { win = win_id }) + end + if buf_id then + vim.api.nvim_set_option_value("ft", "lua", { buf = buf_id }) + vim.api.nvim_buf_set_lines( + buf_id, + 0, + -1, + false, + vim.split(vim.inspect(item), "\n") + ) + end + end, + -- A string for fuzzy finding if supported by picker + ordinal = function(entry) + local ord = entry.args.repo + for _, v in pairs(entry.parsed) do + ord = ord .. ("|" .. v) + end + return ord + end, + }, + }, + utils = {}, +} + +---@param opts GitDevPickerOpts +function P.setup(opts) + P.config = vim.tbl_deep_extend("force", P.config, opts) + -- Pick the first available picker + if not P.config.type then + if pcall(vim.inspect, MiniPick) then + P.config.type = "mini" + elseif pcall(require, "snacks.picker") then + P.config.type = "snacks" + elseif pcall(require, "telescope") then + P.config.type = "telescope" + else + P.config.type = "uiselect" + end + end + -- Register pickers if supported + if P.config.type == "telescope" then + pcall(require("telescope").load_extension, "git_dev") + elseif P.config.type == "mini" then + pcall(function() + for name, picker in pairs(require "git-dev.pickers.mini") do + require("mini.pick").registry["git_dev_" .. name] = picker + end + end) + end + return P +end + +function P.history(local_opts) + local opts = vim.tbl_deep_extend("force", P.config.history, local_opts or {}) + local ok, p = pcall(require, "git-dev.pickers." .. P.config.type) + if ok then + p.history(opts) + else + vim.notify("Unknown picker type: " .. P.config.type, vim.log.levels.ERROR) + end +end + +---@class FitOptions +---@field align? "left"|"right"|"center" +---@field truncate? "left"|"right"|"both" +---@field ellipsis? string + +---Fits a string into a given width by truncating and aligning it. +---@param s string +---@param w number? +---@param opts FitOptions? +function P.utils.fit_string(s, w, opts) + if not w then + return s + end + if not opts then + opts = {} + end + opts.truncate = opts.truncate or "left" + opts.align = opts.align or "left" + opts.ellipsis = opts.ellipsis or "…" + + local s_len = s:len() + local elp_len = vim.fn.strdisplaywidth(opts.ellipsis) + if s_len > w then + if opts.truncate == "left" then + s = opts.ellipsis .. s:sub(-w + elp_len) + elseif opts.truncate == "right" then + s = s:sub(1, w - elp_len) .. opts.ellipsis + elseif opts.truncate == "both" then + s = opts.ellipsis + .. s:sub( + s_len / 2 - w / 2 + elp_len, + s_len / 2 + w / 2 - (w % 2) - elp_len + ) + .. opts.ellipsis + end + end + local pad = w - s:len() + if opts.align == "left" then + s = s .. string.rep(" ", pad) + elseif opts.align == "right" then + s = string.rep(" ", pad) .. s + elseif opts.align == "center" then + s = string.rep(" ", pad / 2) .. s .. string.rep(" ", pad / 2 + (pad % 2)) + end + return s +end + +function P.utils.sum(arr) + return vim.iter(arr):fold(0, function(acc, v) + if v then + return acc + v + end + return acc + end) +end + +---Gets a maximum width, an array of ratios and an array of fixed widths. +---Returns an array of widths within the limit. +---The sum of the fixed widths must be smaller than the maximum width. +---Remaining space will be +---@param max_width number +---@param ratios number[]? Determines the relative ratio of the remaining space. +---@param fixed number[]? Forces a width, non-nil values will ignore ratio. +---@return number[] +function P.utils.normalize_width(max_width, ratios, fixed) + fixed = fixed and vim.deepcopy(fixed) or {} + ratios = ratios and vim.deepcopy(ratios) or {} + + local total_fixed = P.utils.sum(fixed) + if total_fixed > max_width then + return {} + end + + local remaining = max_width + local idxs = {} + local widths = {} + + local len = math.max(#ratios, #fixed) + for i = 1, len do + if fixed[i] then + ratios[i] = nil + widths[i] = fixed[i] + remaining = remaining - fixed[i] + else + idxs[#idxs + 1] = i + widths[i] = 0 + end + end + + local total_ratio = P.utils.sum(ratios) + local remainder = remaining + for _, i in ipairs(idxs) do + if ratios[i] then + -- how to do integer divide (non-fractional)? + widths[i] = math.floor((ratios[i] / total_ratio) * remaining) + remainder = remainder - widths[i] + end + end + + -- Add remainder to the first non-zero width + for i = 1, #widths do + if widths[i] and widths[i] > 0 then + widths[i] = widths[i] + remainder + break + end + end + return widths +end + +return P diff --git a/lua/git-dev/pickers/mini.lua b/lua/git-dev/pickers/mini.lua new file mode 100644 index 0000000..78a93ac --- /dev/null +++ b/lua/git-dev/pickers/mini.lua @@ -0,0 +1,95 @@ +local config = require("git-dev.pickers").config.history +local picker_utils = require("git-dev.pickers").utils + +local pickers = {} + +---Generates a history picker for mini.pick +---@param local_opts HistoryLocalOpts +function pickers.history(local_opts) + local minipick = require "mini.pick" + local ns = vim.api.nvim_create_namespace "GitDevPickers" + minipick.start { + source = { + name = config.title, + items = config.get_entries, + choose = config.select_entry, + show = function(buf_id, items) + local widths = picker_utils.normalize_width( + vim.fn.winwidth(0), -- should deduct separator width + local_opts.entry.ratios, + { + local_opts.entry.repo_url.width, + local_opts.entry.ref.width, + local_opts.entry.selected_path.width, + } + ) + local entries_parts = vim.tbl_map(function(item) + local parts = config.label_parts(item) + + return { + picker_utils.fit_string( + parts[1], + widths[1], + { align = "left", truncate = "left" } + ), + local_opts.separator.text or " ", + picker_utils.fit_string( + parts[2], + widths[2], + { align = "center", truncate = "right" } + ), + local_opts.separator.text or " ", + picker_utils.fit_string( + parts[3], + widths[3], + { align = "left", truncate = "left" } + ), + } + end, items) + vim.api.nvim_buf_set_lines( + buf_id, + 0, + -1, + false, + vim.tbl_map(function(parts) + return vim.iter(parts):join "" + end, entries_parts) + ) + + -- Highlight parts + local hl_groups = { + local_opts.entry.repo_url.hl_group, + local_opts.separator.hl_group, + local_opts.entry.ref.hl_group, + local_opts.separator.hl_group, + local_opts.entry.selected_path.hl_group, + } + vim.api.nvim_buf_clear_namespace(buf_id, ns, 0, -1) + for i, entry in ipairs(entries_parts) do + local col = 0 + for j, hl in ipairs(hl_groups) do + local end_col = col + string.len(entry[j]) + if hl then + vim.api.nvim_buf_set_extmark(buf_id, ns, i - 1, col, { + end_row = i - 1, + end_col = end_col, + hl_group = hl, + priority = 202, + }) + end + col = end_col + end + end + end, + preview = function(buf_id, item) + return config.preview( + buf_id, + minipick.get_picker_state().windows.main, + item + ) + end, + }, + } +end + +return pickers diff --git a/lua/git-dev/pickers/snacks.lua b/lua/git-dev/pickers/snacks.lua new file mode 100644 index 0000000..509e4a9 --- /dev/null +++ b/lua/git-dev/pickers/snacks.lua @@ -0,0 +1,82 @@ +local config = require("git-dev.pickers").config.history +local picker_utils = require("git-dev.pickers").utils + +local pickers = {} + +---@param local_opts HistoryLocalOpts +function pickers.history(local_opts) + local snacks = require "snacks" + + local widths + + return snacks.picker.pick { + source = "git-dev", + title = config.title, + items = config.get_entries(), + confirm = function(_, item) + config.select_entry(item) + end, + format = function(item, p) + if not widths then + widths = picker_utils.normalize_width( + vim.fn.winwidth(p.list.win.win), -- should deduct separator width + local_opts.entry.ratios, + { + local_opts.entry.repo_url.width, + local_opts.entry.ref.width, + local_opts.entry.selected_path.width, + } + ) + end + local parts = config.label_parts(item) + return { + { + picker_utils.fit_string( + parts[1], + widths[1], + { align = "left", truncate = "left" } + ), + local_opts.entry.repo_url.hl_group, + }, + { + local_opts.separator.text or " ", + local_opts.separator.hl_group, + }, + { + picker_utils.fit_string( + parts[2], + widths[2], + { align = "center", truncate = "right" } + ), + local_opts.entry.ref.hl_group, + }, + { + local_opts.separator.text or " ", + local_opts.separator.hl_group, + }, + { + picker_utils.fit_string( + parts[3], + widths[3], + { align = "left", truncate = "left" } + ), + local_opts.entry.selected_path.hl_group, + }, + } + end, + transform = function(item) + item.text = config.ordinal(item) + item.preview = { + ft = "lua", + text = vim.inspect { + args = item.args, + parsed = item.parsed, + }, + } + return item + end, + preview = "preview", + } +end + +return pickers diff --git a/lua/git-dev/pickers/telescope.lua b/lua/git-dev/pickers/telescope.lua new file mode 100644 index 0000000..1ef8918 --- /dev/null +++ b/lua/git-dev/pickers/telescope.lua @@ -0,0 +1,103 @@ +local config = require("git-dev.pickers").config.history +local picker_utils = require("git-dev.pickers").utils + +---@param local_opts HistoryLocalOpts +local history = function(local_opts, opts) + local pickers = require "telescope.pickers" + local finders = require "telescope.finders" + local actions = require "telescope.actions" + local action_state = require "telescope.actions.state" + local entry_display = require "telescope.pickers.entry_display" + local previewers = require "telescope.previewers" + local state = require "telescope.state" + + local display_maker = entry_display.create { + separator = local_opts.separator.text or " ", + separator_hl = local_opts.separator.hl_group, + items = { + { remaining = true }, + { remaining = true }, + { remaining = true }, + }, + } + + local widths + + pickers + .new(opts or {}, { + prompt_title = config.title, + finder = finders.new_table { + results = config.get_entries(), + entry_maker = function(entry) + return { + value = entry, + display = function(_) + if widths == nil then + local status = + state.get_status(vim.F.if_nil(vim.api.nvim_get_current_buf())) + local winid = status.layout.results.winid + widths = picker_utils.normalize_width( + vim.fn.winwidth(winid), -- should deduct separator width + local_opts.entry.ratios, + { + local_opts.entry.repo_url.width, + local_opts.entry.ref.width, + local_opts.entry.selected_path.width, + } + ) + end + local parts = config.label_parts(entry) + return display_maker { + { + picker_utils.fit_string( + parts[1], + widths[1], + { align = "left", truncate = "left" } + ), + local_opts.entry.repo_url.hl_group, + }, + { + picker_utils.fit_string( + parts[2], + widths[2], + { align = "center", truncate = "right" } + ), + local_opts.entry.ref.hl_group, + }, + { + picker_utils.fit_string( + parts[3], + widths[3], + { align = "left", truncate = "left" } + ), + local_opts.entry.selected_path.hl_group, + }, + } + end, + ordinal = config.ordinal(entry), -- used for filtering + } + end, + }, + previewer = previewers.new_buffer_previewer { + define_preview = function(self, entry) + return config.preview(self.state.bufnr, self.state.winid, entry.value) + end, + }, + attach_mappings = function(prompt_bufnr, _) + actions.select_default:replace(function() + actions.close(prompt_bufnr) + local selection = action_state.get_selected_entry() + if not selection then + return + end + config.select_entry(selection.value) + end) + return true + end, + }) + :find() +end + +return { + history = history, +} diff --git a/lua/git-dev/pickers/uiselect.lua b/lua/git-dev/pickers/uiselect.lua new file mode 100644 index 0000000..569cc4d --- /dev/null +++ b/lua/git-dev/pickers/uiselect.lua @@ -0,0 +1,41 @@ +local config = require("git-dev.pickers").config.history +local picker_utils = require("git-dev.pickers").utils + +local pickers = {} + +---@param local_opts HistoryLocalOpts +function pickers.history(local_opts) + vim.ui.select(config.get_entries(), { + prompt = config.title, + format_item = function(item) + local parts = config.label_parts(item) + return vim + .iter({ + picker_utils.fit_string( + parts[1], + local_opts.entry.repo_url.width, + { align = "left", truncate = "left" } + ), + local_opts.separator.text or " ", + picker_utils.fit_string( + parts[2], + local_opts.entry.ref.width, + { align = "center", truncate = "right" } + ), + local_opts.separator.text or " ", + picker_utils.fit_string( + parts[3], + local_opts.entry.selected_path.width, + { align = "left", truncate = "left" } + ), + }) + :join "" + end, + }, function(selected) + if selected then + config.select_entry(selected) + end + end) +end + +return pickers diff --git a/lua/git-dev/store.lua b/lua/git-dev/store.lua index f596d92..7f8d70d 100644 --- a/lua/git-dev/store.lua +++ b/lua/git-dev/store.lua @@ -190,7 +190,7 @@ end ---Get the keys of the `n` least recent items in store. ---Order is not guaranteed. Not very efficient. ----@param n integer Number of least recent records to return. +---@param n? integer Number of least recent records to return. ---@return table function Store:least_recent(n) self:load() @@ -201,6 +201,15 @@ function Store:least_recent(n) return vim.list_slice(keys, 1, n) end +function Store:least_recent_values(n) + return vim + .iter(self:least_recent(n)) + :map(function(k) + return self._db.data[k] + end) + :totable() +end + ---Purges the store. Removes store file if exists. function Store:purge() if self.path then diff --git a/lua/git-dev/xdg.lua b/lua/git-dev/xdg.lua index 5ab5408..8fe8ab1 100644 --- a/lua/git-dev/xdg.lua +++ b/lua/git-dev/xdg.lua @@ -62,7 +62,11 @@ XDG.enable = function(opts) "xdg-mime query default " .. desktop_entry.mime_type, function(code) if code ~= 0 then - vim.api.nvim_err_writeln "Failed to get default handler" + vim.api.nvim_echo( + { { "Failed to get default handler" } }, + true, + { err = true } + ) end end ) @@ -74,7 +78,11 @@ XDG.enable = function(opts) "xdg-mime default " .. entry_name .. " " .. desktop_entry.mime_type, function(code) if code ~= 0 then - vim.api.nvim_err_writeln "Failed to set default handler" + vim.api.nvim_echo( + { { "Failed to set default handler" } }, + true, + { err = true } + ) end end ) diff --git a/lua/telescope/_extensions/git_dev/init.lua b/lua/telescope/_extensions/git_dev/init.lua index 9e30c8c..c9d1619 100644 --- a/lua/telescope/_extensions/git_dev/init.lua +++ b/lua/telescope/_extensions/git_dev/init.lua @@ -1,7 +1,25 @@ local telescope = require "telescope" +local history = function(opts) + local config = vim.tbl_deep_extend( + "force", + require("git-dev").config.pickers, + { type = "telescope" } + ) + require("git-dev").pickers.setup(config).history(opts) +end + return telescope.register_extension { exports = { - recents = require "telescope._extensions.git_dev.recents", + -- TODO: Remove + recents = function(opts) + vim.notify( + "'recents' picker has been renamed to 'history' " + .. "and will be removed in the future", + vim.log.levels.WARN + ) + history(opts) + end, + history = history, }, } diff --git a/lua/telescope/_extensions/git_dev/recents.lua b/lua/telescope/_extensions/git_dev/recents.lua deleted file mode 100644 index aa766cc..0000000 --- a/lua/telescope/_extensions/git_dev/recents.lua +++ /dev/null @@ -1,129 +0,0 @@ -local pickers = require "telescope.pickers" -local finders = require "telescope.finders" -local conf = require("telescope.config").values -local actions = require "telescope.actions" -local action_state = require "telescope.actions.state" -local entry_display = require "telescope.pickers.entry_display" -local utils = require "telescope.utils" -local previewers = require "telescope.previewers" - -local recents = function(opts) - local repo_url_width = 32 - local ref_width = 9 - - local display_maker = entry_display.create { - separator = " │ ", - separator_hl = "TelescopePreviewHyphen", - items = { - { width = repo_url_width }, - { width = ref_width }, - { remaining = true }, - }, - } - - local function make_ordinal(entry) - local ord = entry.args.repo - for _, v in pairs(entry.parsed) do - ord = ord .. ("|" .. v) - end - return ord - end - - local function truncate_left(s, w, lean_right) - local s_len = vim.fn.strdisplaywidth(s) - if s_len <= w then - local m = (w - s_len) / 2 - local r = lean_right and (w - s_len) % 2 or 0 - return string.rep(" ", m + r) .. s - end - local dots = "…" - local dots_w = vim.fn.strdisplaywidth(dots) - return dots .. s:sub(s_len - w + dots_w + 1) - end - - local entry_maker = function(entry) - return { - value = entry, - display = function(ent) - return display_maker { - { - truncate_left(ent.value.parsed.repo_url, repo_url_width, true), - "TelescopePreviewExecute", - }, - { - truncate_left( - ent.value.args.ref.commit - or ent.value.args.ref.tag - or ent.value.args.ref.branch - or ent.value.parsed.commit - or ent.value.parsed.branch - or "", - ref_width - ), - "TelescopeResultsIdentifier", - }, - { - ent.value.parsed.selected_path - and utils.transform_path({ - path_display = { - -- shorten = { len = 2, exclude = { 1, -1 } }, - }, - }, ent.value.parsed.selected_path) - or "", - "TelescopeResultsFunction", - }, - } - end, - ordinal = make_ordinal(entry), -- used for filtering - } - end - - local function make_preview(entry) - return vim.inspect(entry.value) - end - - pickers - .new(opts or {}, { - prompt_title = "Recently Opened", - finder = finders.new_table { - results = require("git-dev").history:get(), - entry_maker = entry_maker, - }, - sorter = conf.generic_sorter(opts), - previewer = previewers.new_buffer_previewer { - define_preview = function(self, entry) - vim.api.nvim_set_option_value("ft", "hcl", { buf = self.state.bufnr }) - vim.api.nvim_set_option_value( - "wrap", - true, - { win = self.state.winid } - ) - vim.api.nvim_buf_set_lines( - self.state.bufnr, - 0, - -1, - false, - vim.split(make_preview(entry), "\n") - ) - end, - }, - attach_mappings = function(prompt_bufnr, _) - actions.select_default:replace(function() - actions.close(prompt_bufnr) - local selection = action_state.get_selected_entry() - if not selection then - return - end - require("git-dev").open( - selection.value.args.repo, - selection.value.args.ref, - selection.value.args.opts - ) - end) - return true - end, - }) - :find() -end - -return recents