mirror of
https://github.com/kristoferssolo/telescope-frecency.nvim.git
synced 2025-10-21 20:10:38 +00:00
feat: call init process before telescope loading (#234)
* feat: call init process before telescope loading Fix #231 This changes enable to load frecency without telescope's loading itself. This is needed when you want to load telescope lazily, but want to start registering process as soon as Neovim has started. ```lua { "nvim-telescope/telescope-frecency.nvim", main = "frecency", ---@type FrecencyOpts opts = { db_safe_mode = false, }, }, { "nvim-telescope/telescope.nvim", -- `cmd` opts makes lazy.nvim load telescope.nvim lazily. cmd = { "Telescope" }, config = function() local telescope = require "telescope" telescope.setup { extensions = { other_extension = { foo_bar = true, }, -- Here you need no configuration opts for frecency because -- you've already done. } } -- This is still needed. telescope.load_extension "frecency" end, }, ``` * docs: add note for loading telescope.nvim lazily
This commit is contained in:
parent
cef01dee8b
commit
87ccbae5d2
@ -134,6 +134,9 @@ If no database is found when running Neovim with the plugin installed, a new
|
|||||||
one is created and entries from |shada| and |v:oldfiles| are automatically
|
one is created and entries from |shada| and |v:oldfiles| are automatically
|
||||||
imported.
|
imported.
|
||||||
|
|
||||||
|
NOTE: Even if you want to load |telescope.nvim| lazily, you should NOT load
|
||||||
|
telescope-frecency.nvim lazily. See |telescope-frecency-function-setup|.
|
||||||
|
|
||||||
|
|
||||||
==============================================================================
|
==============================================================================
|
||||||
USAGE *telescope-frecency-usage*
|
USAGE *telescope-frecency-usage*
|
||||||
@ -315,6 +318,46 @@ Options: *telescope-frecency-function-query-options*
|
|||||||
exist below the directory specified this value. See also
|
exist below the directory specified this value. See also
|
||||||
|telescope-frecency-usage|.
|
|telescope-frecency-usage|.
|
||||||
|
|
||||||
|
*telescope-frecency-function-setup*
|
||||||
|
setup() ~
|
||||||
|
|
||||||
|
This will be called by |telescope.nvim| for its initialization. You can also
|
||||||
|
call this to initialize this plugin separated from |telescope.nvim|'s
|
||||||
|
initialization phase.
|
||||||
|
|
||||||
|
This is useful when you want to load |telescope.nvim| lazily, but want to
|
||||||
|
register opened files as soon as Neovim has started. Example configuration for
|
||||||
|
|lazy.nvim| is below.
|
||||||
|
>lua
|
||||||
|
{
|
||||||
|
"nvim-telescope/telescope-frecency.nvim",
|
||||||
|
main = "frecency",
|
||||||
|
---@type FrecencyOpts
|
||||||
|
opts = {
|
||||||
|
db_safe_mode = false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"nvim-telescope/telescope.nvim",
|
||||||
|
-- `cmd` opts makes lazy.nvim load telescope.nvim lazily.
|
||||||
|
cmd = { "Telescope" },
|
||||||
|
config = function()
|
||||||
|
local telescope = require "telescope"
|
||||||
|
telescope.setup {
|
||||||
|
extensions = {
|
||||||
|
other_extension = {
|
||||||
|
foo_bar = true,
|
||||||
|
},
|
||||||
|
-- Here you need no configuration opts for frecency because
|
||||||
|
-- you've already done.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
-- This is still needed.
|
||||||
|
telescope.load_extension "frecency"
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
==============================================================================
|
==============================================================================
|
||||||
CONFIGURATION *telescope-frecency-configuration*
|
CONFIGURATION *telescope-frecency-configuration*
|
||||||
|
|||||||
@ -1,261 +1,81 @@
|
|||||||
local Database = require "frecency.database"
|
---This object is intended to be used as a singleton, and is lazily loaded.
|
||||||
local EntryMaker = require "frecency.entry_maker"
|
---When methods are called at the first time, it calls the constructor and
|
||||||
local FS = require "frecency.fs"
|
---setup() to be initialized.
|
||||||
local Picker = require "frecency.picker"
|
---@class FrecencyInstance
|
||||||
local Recency = require "frecency.recency"
|
---@field complete fun(findstart: 1|0, base: string): integer|''|string[]
|
||||||
local config = require "frecency.config"
|
---@field delete fun(path: string): nil
|
||||||
local log = require "frecency.log"
|
---@field query fun(opts?: FrecencyQueryOpts): FrecencyQueryEntry[]|string[]
|
||||||
|
---@field register fun(bufnr: integer, datetime: string?): nil
|
||||||
---@class Frecency
|
---@field start fun(opts: FrecencyPickerOptions?): nil
|
||||||
---@field private buf_registered table<integer, boolean> flag to indicate the buffer is registered to the database.
|
---@field validate_database fun(force: boolean?): nil
|
||||||
---@field private database FrecencyDatabase
|
local frecency = setmetatable({}, {
|
||||||
---@field private entry_maker FrecencyEntryMaker
|
---@param self FrecencyInstance
|
||||||
---@field private fs FrecencyFS
|
---@param key "complete"|"delete"|"register"|"start"|"validate_database"
|
||||||
---@field private picker FrecencyPicker
|
---@return function
|
||||||
---@field private recency FrecencyRecency
|
__index = function(self, key)
|
||||||
local Frecency = {}
|
---@return Frecency
|
||||||
|
local function instance()
|
||||||
---@return Frecency
|
return rawget(self, "instance")
|
||||||
Frecency.new = function()
|
|
||||||
local self = setmetatable({ buf_registered = {} }, { __index = Frecency }) --[[@as Frecency]]
|
|
||||||
self.fs = FS.new()
|
|
||||||
self.database = Database.new(self.fs)
|
|
||||||
self.entry_maker = EntryMaker.new(self.fs)
|
|
||||||
self.recency = Recency.new()
|
|
||||||
return self
|
|
||||||
end
|
|
||||||
|
|
||||||
---This is called when `:Telescope frecency` is called at the first time.
|
|
||||||
---@return nil
|
|
||||||
function Frecency:setup()
|
|
||||||
-- HACK: Wihout this wrapping, it spoils background color detection.
|
|
||||||
-- See https://github.com/nvim-telescope/telescope-frecency.nvim/issues/210
|
|
||||||
vim.defer_fn(function()
|
|
||||||
self:assert_db_entries()
|
|
||||||
if config.auto_validate then
|
|
||||||
self:validate_database()
|
|
||||||
end
|
end
|
||||||
end, 0)
|
|
||||||
end
|
|
||||||
|
|
||||||
---This can be calledBy `require("telescope").extensions.frecency.frecency`.
|
return function(...)
|
||||||
---@param opts? FrecencyPickerOptions
|
if not instance() then
|
||||||
---@return nil
|
rawset(self, "instance", require("frecency.klass").new())
|
||||||
function Frecency:start(opts)
|
instance():setup()
|
||||||
local start = os.clock()
|
|
||||||
log.debug "Frecency:start"
|
|
||||||
opts = opts or {}
|
|
||||||
if opts.cwd then
|
|
||||||
opts.cwd = vim.fn.expand(opts.cwd)
|
|
||||||
end
|
end
|
||||||
local ignore_filenames
|
return instance()[key](instance(), ...)
|
||||||
if opts.hide_current_buffer or config.hide_current_buffer then
|
|
||||||
ignore_filenames = { vim.api.nvim_buf_get_name(0) }
|
|
||||||
end
|
end
|
||||||
self.picker = Picker.new(self.database, self.entry_maker, self.fs, self.recency, {
|
|
||||||
editing_bufnr = vim.api.nvim_get_current_buf(),
|
|
||||||
ignore_filenames = ignore_filenames,
|
|
||||||
initial_workspace_tag = opts.workspace,
|
|
||||||
})
|
|
||||||
self.picker:start(vim.tbl_extend("force", config.get(), opts))
|
|
||||||
log.debug(("Frecency:start picker:start takes %f seconds"):format(os.clock() - start))
|
|
||||||
end
|
|
||||||
|
|
||||||
---This can be calledBy `require("telescope").extensions.frecency.complete`.
|
|
||||||
---@param findstart 1|0
|
|
||||||
---@param base string
|
|
||||||
---@return integer|''|string[]
|
|
||||||
function Frecency:complete(findstart, base)
|
|
||||||
return self.picker:complete(findstart, base)
|
|
||||||
end
|
|
||||||
|
|
||||||
---@private
|
|
||||||
---@return nil
|
|
||||||
function Frecency:assert_db_entries()
|
|
||||||
if not self.database:has_entry() then
|
|
||||||
self.database:insert_files(vim.v.oldfiles)
|
|
||||||
self:notify("Imported %d entries from oldfiles.", #vim.v.oldfiles)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
---@private
|
|
||||||
---@param force? boolean
|
|
||||||
---@return nil
|
|
||||||
function Frecency:validate_database(force)
|
|
||||||
local unlinked = self.database:unlinked_entries()
|
|
||||||
if #unlinked == 0 or (not force and #unlinked < config.db_validate_threshold) then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
local function remove_entries()
|
|
||||||
self.database:remove_files(unlinked)
|
|
||||||
self:notify("removed %d missing entries.", #unlinked)
|
|
||||||
end
|
|
||||||
if not config.db_safe_mode then
|
|
||||||
remove_entries()
|
|
||||||
return
|
|
||||||
end
|
|
||||||
vim.ui.select({ "y", "n" }, {
|
|
||||||
prompt = self:message("remove %d entries from database?", #unlinked),
|
|
||||||
---@param item "y"|"n"
|
|
||||||
---@return string
|
|
||||||
format_item = function(item)
|
|
||||||
return item == "y" and "Yes. Remove them." or "No. Do nothing."
|
|
||||||
end,
|
end,
|
||||||
}, function(item)
|
})
|
||||||
if item == "y" then
|
|
||||||
remove_entries()
|
|
||||||
else
|
|
||||||
self:notify "validation aborted"
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
---@param bufnr integer
|
local setup_done = false
|
||||||
---@param epoch? integer
|
|
||||||
function Frecency:register(bufnr, epoch)
|
---When this func is called, Frecency instance is NOT created but only
|
||||||
if config.ignore_register and config.ignore_register(bufnr) then
|
---configuration is done.
|
||||||
|
---@param ext_config? FrecencyOpts
|
||||||
|
---@return nil
|
||||||
|
local function setup(ext_config)
|
||||||
|
if setup_done then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local path = vim.api.nvim_buf_get_name(bufnr)
|
|
||||||
if self.buf_registered[bufnr] or not self.fs:is_valid_path(path) then
|
require("frecency.config").setup(ext_config)
|
||||||
return
|
|
||||||
|
vim.api.nvim_set_hl(0, "TelescopeBufferLoaded", { link = "String", default = true })
|
||||||
|
vim.api.nvim_set_hl(0, "TelescopePathSeparator", { link = "Directory", default = true })
|
||||||
|
vim.api.nvim_set_hl(0, "TelescopeFrecencyScores", { link = "Number", default = true })
|
||||||
|
vim.api.nvim_set_hl(0, "TelescopeQueryFilter", { link = "WildMenu", default = true })
|
||||||
|
|
||||||
|
---@param cmd_info { bang: boolean }
|
||||||
|
vim.api.nvim_create_user_command("FrecencyValidate", function(cmd_info)
|
||||||
|
frecency.validate_database(cmd_info.bang)
|
||||||
|
end, { bang = true, desc = "Clean up DB for telescope-frecency" })
|
||||||
|
|
||||||
|
vim.api.nvim_create_user_command("FrecencyDelete", function(info)
|
||||||
|
local path_string = info.args == "" and "%:p" or info.args
|
||||||
|
local path = vim.fn.expand(path_string) --[[@as string]]
|
||||||
|
frecency.delete(path)
|
||||||
|
end, { nargs = "?", complete = "file", desc = "Delete entry from telescope-frecency" })
|
||||||
|
|
||||||
|
local group = vim.api.nvim_create_augroup("TelescopeFrecency", {})
|
||||||
|
vim.api.nvim_create_autocmd({ "BufWinEnter", "BufWritePost" }, {
|
||||||
|
desc = "Update database for telescope-frecency",
|
||||||
|
group = group,
|
||||||
|
---@param args { buf: integer }
|
||||||
|
callback = function(args)
|
||||||
|
local is_floatwin = vim.api.nvim_win_get_config(0).relative ~= ""
|
||||||
|
if not is_floatwin then
|
||||||
|
frecency.register(args.buf)
|
||||||
end
|
end
|
||||||
self.database:update(path, epoch)
|
end,
|
||||||
self.buf_registered[bufnr] = true
|
})
|
||||||
|
|
||||||
|
setup_done = true
|
||||||
end
|
end
|
||||||
|
|
||||||
---@param path string
|
return {
|
||||||
---@return nil
|
start = frecency.start,
|
||||||
function Frecency:delete(path)
|
complete = frecency.complete,
|
||||||
if self.database:remove_entry(path) then
|
query = frecency.query,
|
||||||
self:notify("successfully deleted: %s", path)
|
setup = setup,
|
||||||
else
|
}
|
||||||
self:warn("failed to delete: %s", path)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
---@alias FrecencyQueryOrder "count"|"path"|"score"|"timestamps"
|
|
||||||
---@alias FrecencyQueryDirection "asc"|"desc"
|
|
||||||
|
|
||||||
---@class FrecencyQueryOpts
|
|
||||||
---@field direction? "asc"|"desc" default: "desc"
|
|
||||||
---@field limit? integer default: 100
|
|
||||||
---@field order? FrecencyQueryOrder default: "score"
|
|
||||||
---@field record? boolean default: false
|
|
||||||
---@field workspace? string default: nil
|
|
||||||
|
|
||||||
---@class FrecencyQueryEntry
|
|
||||||
---@field count integer
|
|
||||||
---@field path string
|
|
||||||
---@field score number
|
|
||||||
---@field timestamps integer[]
|
|
||||||
|
|
||||||
---@param opts? FrecencyQueryOpts
|
|
||||||
---@param epoch? integer
|
|
||||||
---@return FrecencyQueryEntry[]|string[]
|
|
||||||
function Frecency:query(opts, epoch)
|
|
||||||
opts = vim.tbl_extend("force", {
|
|
||||||
direction = "desc",
|
|
||||||
limit = 100,
|
|
||||||
order = "score",
|
|
||||||
record = false,
|
|
||||||
}, opts or {})
|
|
||||||
---@param entry FrecencyDatabaseEntry
|
|
||||||
local entries = vim.tbl_map(function(entry)
|
|
||||||
return {
|
|
||||||
count = entry.count,
|
|
||||||
path = entry.path,
|
|
||||||
score = entry.ages and self.recency:calculate(entry.count, entry.ages) or 0,
|
|
||||||
timestamps = entry.timestamps,
|
|
||||||
}
|
|
||||||
end, self.database:get_entries(opts.workspace, epoch))
|
|
||||||
table.sort(entries, self:query_sorter(opts.order, opts.direction))
|
|
||||||
local results = opts.record and entries or vim.tbl_map(function(entry)
|
|
||||||
return entry.path
|
|
||||||
end, entries)
|
|
||||||
if #results > opts.limit then
|
|
||||||
return vim.list_slice(results, 1, opts.limit)
|
|
||||||
end
|
|
||||||
return results
|
|
||||||
end
|
|
||||||
|
|
||||||
---@private
|
|
||||||
---@param order FrecencyQueryOrder
|
|
||||||
---@param direction FrecencyQueryDirection
|
|
||||||
---@return fun(a: FrecencyQueryEntry, b: FrecencyQueryEntry): boolean
|
|
||||||
function Frecency:query_sorter(order, direction)
|
|
||||||
local is_asc = direction == "asc"
|
|
||||||
if order == "count" then
|
|
||||||
if is_asc then
|
|
||||||
return function(a, b)
|
|
||||||
return a.count < b.count or (a.count == b.count and a.path < b.path)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return function(a, b)
|
|
||||||
return a.count > b.count or (a.count == b.count and a.path < b.path)
|
|
||||||
end
|
|
||||||
elseif order == "path" then
|
|
||||||
if is_asc then
|
|
||||||
return function(a, b)
|
|
||||||
return a.path < b.path
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return function(a, b)
|
|
||||||
return a.path > b.path
|
|
||||||
end
|
|
||||||
elseif order == "score" then
|
|
||||||
if is_asc then
|
|
||||||
return function(a, b)
|
|
||||||
return a.score < b.score or (a.score == b.score and a.path < b.path)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return function(a, b)
|
|
||||||
return a.score > b.score or (a.score == b.score and a.path < b.path)
|
|
||||||
end
|
|
||||||
elseif is_asc then
|
|
||||||
return function(a, b)
|
|
||||||
local a_timestamp = a.timestamps[1] or 0
|
|
||||||
local b_timestamp = b.timestamps[1] or 0
|
|
||||||
return a_timestamp < b_timestamp or (a_timestamp == b_timestamp and a.path < b.path)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return function(a, b)
|
|
||||||
local a_timestamp = a.timestamps[1] or 0
|
|
||||||
local b_timestamp = b.timestamps[1] or 0
|
|
||||||
return a_timestamp > b_timestamp or (a_timestamp == b_timestamp and a.path < b.path)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
---@private
|
|
||||||
---@param fmt string
|
|
||||||
---@param ...? any
|
|
||||||
---@return string
|
|
||||||
function Frecency:message(fmt, ...)
|
|
||||||
return ("[Telescope-Frecency] " .. fmt):format(unpack { ... })
|
|
||||||
end
|
|
||||||
|
|
||||||
---@private
|
|
||||||
---@param fmt string
|
|
||||||
---@param ...? any
|
|
||||||
---@return nil
|
|
||||||
function Frecency:notify(fmt, ...)
|
|
||||||
vim.notify(self:message(fmt, ...))
|
|
||||||
end
|
|
||||||
|
|
||||||
---@private
|
|
||||||
---@param fmt string
|
|
||||||
---@param ...? any
|
|
||||||
---@return nil
|
|
||||||
function Frecency:warn(fmt, ...)
|
|
||||||
vim.notify(self:message(fmt, ...), vim.log.levels.WARN)
|
|
||||||
end
|
|
||||||
|
|
||||||
---@private
|
|
||||||
---@param fmt string
|
|
||||||
---@param ...? any
|
|
||||||
---@return nil
|
|
||||||
function Frecency:error(fmt, ...)
|
|
||||||
vim.notify(self:message(fmt, ...), vim.log.levels.ERROR)
|
|
||||||
end
|
|
||||||
|
|
||||||
return Frecency
|
|
||||||
|
|||||||
261
lua/frecency/klass.lua
Normal file
261
lua/frecency/klass.lua
Normal file
@ -0,0 +1,261 @@
|
|||||||
|
local Database = require "frecency.database"
|
||||||
|
local EntryMaker = require "frecency.entry_maker"
|
||||||
|
local FS = require "frecency.fs"
|
||||||
|
local Picker = require "frecency.picker"
|
||||||
|
local Recency = require "frecency.recency"
|
||||||
|
local config = require "frecency.config"
|
||||||
|
local log = require "frecency.log"
|
||||||
|
|
||||||
|
---@class Frecency
|
||||||
|
---@field private buf_registered table<integer, boolean> flag to indicate the buffer is registered to the database.
|
||||||
|
---@field private database FrecencyDatabase
|
||||||
|
---@field private entry_maker FrecencyEntryMaker
|
||||||
|
---@field private fs FrecencyFS
|
||||||
|
---@field private picker FrecencyPicker
|
||||||
|
---@field private recency FrecencyRecency
|
||||||
|
local Frecency = {}
|
||||||
|
|
||||||
|
---@return Frecency
|
||||||
|
Frecency.new = function()
|
||||||
|
local self = setmetatable({ buf_registered = {} }, { __index = Frecency }) --[[@as Frecency]]
|
||||||
|
self.fs = FS.new()
|
||||||
|
self.database = Database.new(self.fs)
|
||||||
|
self.entry_maker = EntryMaker.new(self.fs)
|
||||||
|
self.recency = Recency.new()
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
---This is called when `:Telescope frecency` is called at the first time.
|
||||||
|
---@return nil
|
||||||
|
function Frecency:setup()
|
||||||
|
-- HACK: Wihout this wrapping, it spoils background color detection.
|
||||||
|
-- See https://github.com/nvim-telescope/telescope-frecency.nvim/issues/210
|
||||||
|
vim.defer_fn(function()
|
||||||
|
self:assert_db_entries()
|
||||||
|
if config.auto_validate then
|
||||||
|
self:validate_database()
|
||||||
|
end
|
||||||
|
end, 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
---This can be calledBy `require("telescope").extensions.frecency.frecency`.
|
||||||
|
---@param opts? FrecencyPickerOptions
|
||||||
|
---@return nil
|
||||||
|
function Frecency:start(opts)
|
||||||
|
local start = os.clock()
|
||||||
|
log.debug "Frecency:start"
|
||||||
|
opts = opts or {}
|
||||||
|
if opts.cwd then
|
||||||
|
opts.cwd = vim.fn.expand(opts.cwd)
|
||||||
|
end
|
||||||
|
local ignore_filenames
|
||||||
|
if opts.hide_current_buffer or config.hide_current_buffer then
|
||||||
|
ignore_filenames = { vim.api.nvim_buf_get_name(0) }
|
||||||
|
end
|
||||||
|
self.picker = Picker.new(self.database, self.entry_maker, self.fs, self.recency, {
|
||||||
|
editing_bufnr = vim.api.nvim_get_current_buf(),
|
||||||
|
ignore_filenames = ignore_filenames,
|
||||||
|
initial_workspace_tag = opts.workspace,
|
||||||
|
})
|
||||||
|
self.picker:start(vim.tbl_extend("force", config.get(), opts))
|
||||||
|
log.debug(("Frecency:start picker:start takes %f seconds"):format(os.clock() - start))
|
||||||
|
end
|
||||||
|
|
||||||
|
---This can be calledBy `require("telescope").extensions.frecency.complete`.
|
||||||
|
---@param findstart 1|0
|
||||||
|
---@param base string
|
||||||
|
---@return integer|''|string[]
|
||||||
|
function Frecency:complete(findstart, base)
|
||||||
|
return self.picker:complete(findstart, base)
|
||||||
|
end
|
||||||
|
|
||||||
|
---@private
|
||||||
|
---@return nil
|
||||||
|
function Frecency:assert_db_entries()
|
||||||
|
if not self.database:has_entry() then
|
||||||
|
self.database:insert_files(vim.v.oldfiles)
|
||||||
|
self:notify("Imported %d entries from oldfiles.", #vim.v.oldfiles)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
---@private
|
||||||
|
---@param force? boolean
|
||||||
|
---@return nil
|
||||||
|
function Frecency:validate_database(force)
|
||||||
|
local unlinked = self.database:unlinked_entries()
|
||||||
|
if #unlinked == 0 or (not force and #unlinked < config.db_validate_threshold) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local function remove_entries()
|
||||||
|
self.database:remove_files(unlinked)
|
||||||
|
self:notify("removed %d missing entries.", #unlinked)
|
||||||
|
end
|
||||||
|
if not config.db_safe_mode then
|
||||||
|
remove_entries()
|
||||||
|
return
|
||||||
|
end
|
||||||
|
vim.ui.select({ "y", "n" }, {
|
||||||
|
prompt = self:message("remove %d entries from database?", #unlinked),
|
||||||
|
---@param item "y"|"n"
|
||||||
|
---@return string
|
||||||
|
format_item = function(item)
|
||||||
|
return item == "y" and "Yes. Remove them." or "No. Do nothing."
|
||||||
|
end,
|
||||||
|
}, function(item)
|
||||||
|
if item == "y" then
|
||||||
|
remove_entries()
|
||||||
|
else
|
||||||
|
self:notify "validation aborted"
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param bufnr integer
|
||||||
|
---@param epoch? integer
|
||||||
|
function Frecency:register(bufnr, epoch)
|
||||||
|
if config.ignore_register and config.ignore_register(bufnr) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local path = vim.api.nvim_buf_get_name(bufnr)
|
||||||
|
if self.buf_registered[bufnr] or not self.fs:is_valid_path(path) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
self.database:update(path, epoch)
|
||||||
|
self.buf_registered[bufnr] = true
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param path string
|
||||||
|
---@return nil
|
||||||
|
function Frecency:delete(path)
|
||||||
|
if self.database:remove_entry(path) then
|
||||||
|
self:notify("successfully deleted: %s", path)
|
||||||
|
else
|
||||||
|
self:warn("failed to delete: %s", path)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
---@alias FrecencyQueryOrder "count"|"path"|"score"|"timestamps"
|
||||||
|
---@alias FrecencyQueryDirection "asc"|"desc"
|
||||||
|
|
||||||
|
---@class FrecencyQueryOpts
|
||||||
|
---@field direction? "asc"|"desc" default: "desc"
|
||||||
|
---@field limit? integer default: 100
|
||||||
|
---@field order? FrecencyQueryOrder default: "score"
|
||||||
|
---@field record? boolean default: false
|
||||||
|
---@field workspace? string default: nil
|
||||||
|
|
||||||
|
---@class FrecencyQueryEntry
|
||||||
|
---@field count integer
|
||||||
|
---@field path string
|
||||||
|
---@field score number
|
||||||
|
---@field timestamps integer[]
|
||||||
|
|
||||||
|
---@param opts? FrecencyQueryOpts
|
||||||
|
---@param epoch? integer
|
||||||
|
---@return FrecencyQueryEntry[]|string[]
|
||||||
|
function Frecency:query(opts, epoch)
|
||||||
|
opts = vim.tbl_extend("force", {
|
||||||
|
direction = "desc",
|
||||||
|
limit = 100,
|
||||||
|
order = "score",
|
||||||
|
record = false,
|
||||||
|
}, opts or {})
|
||||||
|
---@param entry FrecencyDatabaseEntry
|
||||||
|
local entries = vim.tbl_map(function(entry)
|
||||||
|
return {
|
||||||
|
count = entry.count,
|
||||||
|
path = entry.path,
|
||||||
|
score = entry.ages and self.recency:calculate(entry.count, entry.ages) or 0,
|
||||||
|
timestamps = entry.timestamps,
|
||||||
|
}
|
||||||
|
end, self.database:get_entries(opts.workspace, epoch))
|
||||||
|
table.sort(entries, self:query_sorter(opts.order, opts.direction))
|
||||||
|
local results = opts.record and entries or vim.tbl_map(function(entry)
|
||||||
|
return entry.path
|
||||||
|
end, entries)
|
||||||
|
if #results > opts.limit then
|
||||||
|
return vim.list_slice(results, 1, opts.limit)
|
||||||
|
end
|
||||||
|
return results
|
||||||
|
end
|
||||||
|
|
||||||
|
---@private
|
||||||
|
---@param order FrecencyQueryOrder
|
||||||
|
---@param direction FrecencyQueryDirection
|
||||||
|
---@return fun(a: FrecencyQueryEntry, b: FrecencyQueryEntry): boolean
|
||||||
|
function Frecency:query_sorter(order, direction)
|
||||||
|
local is_asc = direction == "asc"
|
||||||
|
if order == "count" then
|
||||||
|
if is_asc then
|
||||||
|
return function(a, b)
|
||||||
|
return a.count < b.count or (a.count == b.count and a.path < b.path)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return function(a, b)
|
||||||
|
return a.count > b.count or (a.count == b.count and a.path < b.path)
|
||||||
|
end
|
||||||
|
elseif order == "path" then
|
||||||
|
if is_asc then
|
||||||
|
return function(a, b)
|
||||||
|
return a.path < b.path
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return function(a, b)
|
||||||
|
return a.path > b.path
|
||||||
|
end
|
||||||
|
elseif order == "score" then
|
||||||
|
if is_asc then
|
||||||
|
return function(a, b)
|
||||||
|
return a.score < b.score or (a.score == b.score and a.path < b.path)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return function(a, b)
|
||||||
|
return a.score > b.score or (a.score == b.score and a.path < b.path)
|
||||||
|
end
|
||||||
|
elseif is_asc then
|
||||||
|
return function(a, b)
|
||||||
|
local a_timestamp = a.timestamps[1] or 0
|
||||||
|
local b_timestamp = b.timestamps[1] or 0
|
||||||
|
return a_timestamp < b_timestamp or (a_timestamp == b_timestamp and a.path < b.path)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return function(a, b)
|
||||||
|
local a_timestamp = a.timestamps[1] or 0
|
||||||
|
local b_timestamp = b.timestamps[1] or 0
|
||||||
|
return a_timestamp > b_timestamp or (a_timestamp == b_timestamp and a.path < b.path)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
---@private
|
||||||
|
---@param fmt string
|
||||||
|
---@param ...? any
|
||||||
|
---@return string
|
||||||
|
function Frecency:message(fmt, ...)
|
||||||
|
return ("[Telescope-Frecency] " .. fmt):format(unpack { ... })
|
||||||
|
end
|
||||||
|
|
||||||
|
---@private
|
||||||
|
---@param fmt string
|
||||||
|
---@param ...? any
|
||||||
|
---@return nil
|
||||||
|
function Frecency:notify(fmt, ...)
|
||||||
|
vim.notify(self:message(fmt, ...))
|
||||||
|
end
|
||||||
|
|
||||||
|
---@private
|
||||||
|
---@param fmt string
|
||||||
|
---@param ...? any
|
||||||
|
---@return nil
|
||||||
|
function Frecency:warn(fmt, ...)
|
||||||
|
vim.notify(self:message(fmt, ...), vim.log.levels.WARN)
|
||||||
|
end
|
||||||
|
|
||||||
|
---@private
|
||||||
|
---@param fmt string
|
||||||
|
---@param ...? any
|
||||||
|
---@return nil
|
||||||
|
function Frecency:error(fmt, ...)
|
||||||
|
vim.notify(self:message(fmt, ...), vim.log.levels.ERROR)
|
||||||
|
end
|
||||||
|
|
||||||
|
return Frecency
|
||||||
@ -3,7 +3,7 @@
|
|||||||
vim.opt.runtimepath:append(vim.env.TELESCOPE_PATH)
|
vim.opt.runtimepath:append(vim.env.TELESCOPE_PATH)
|
||||||
|
|
||||||
---@diagnostic disable: invisible, undefined-field
|
---@diagnostic disable: invisible, undefined-field
|
||||||
local Frecency = require "frecency"
|
local Frecency = require "frecency.klass"
|
||||||
local Picker = require "frecency.picker"
|
local Picker = require "frecency.picker"
|
||||||
local util = require "frecency.tests.util"
|
local util = require "frecency.tests.util"
|
||||||
local log = require "plenary.log"
|
local log = require "plenary.log"
|
||||||
|
|||||||
@ -1,32 +1,4 @@
|
|||||||
---This object is intended to be used as a singleton, and is lazily loaded.
|
local frecency = require "frecency"
|
||||||
---When methods are called at the first time, it calls the constructor and
|
|
||||||
---setup() to be initialized.
|
|
||||||
---@class FrecencyInstance
|
|
||||||
---@field complete fun(findstart: 1|0, base: string): integer|''|string[]
|
|
||||||
---@field delete fun(path: string): nil
|
|
||||||
---@field query fun(opts?: FrecencyQueryOpts): FrecencyQueryEntry[]|string[]
|
|
||||||
---@field register fun(bufnr: integer, datetime: string?): nil
|
|
||||||
---@field start fun(opts: FrecencyPickerOptions?): nil
|
|
||||||
---@field validate_database fun(force: boolean?): nil
|
|
||||||
local frecency = setmetatable({}, {
|
|
||||||
---@param self FrecencyInstance
|
|
||||||
---@param key "complete"|"delete"|"register"|"start"|"validate_database"
|
|
||||||
---@return function
|
|
||||||
__index = function(self, key)
|
|
||||||
---@return Frecency
|
|
||||||
local function instance()
|
|
||||||
return rawget(self, "instance")
|
|
||||||
end
|
|
||||||
|
|
||||||
return function(...)
|
|
||||||
if not instance() then
|
|
||||||
rawset(self, "instance", require("frecency").new())
|
|
||||||
instance():setup()
|
|
||||||
end
|
|
||||||
return instance()[key](instance(), ...)
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
})
|
|
||||||
|
|
||||||
return require("telescope").register_extension {
|
return require("telescope").register_extension {
|
||||||
exports = {
|
exports = {
|
||||||
@ -34,42 +6,7 @@ return require("telescope").register_extension {
|
|||||||
complete = frecency.complete,
|
complete = frecency.complete,
|
||||||
query = frecency.query,
|
query = frecency.query,
|
||||||
},
|
},
|
||||||
|
setup = frecency.setup,
|
||||||
---When this func is called, Frecency instance is NOT created but only
|
|
||||||
---configuration is done.
|
|
||||||
setup = function(ext_config)
|
|
||||||
require("frecency.config").setup(ext_config)
|
|
||||||
|
|
||||||
vim.api.nvim_set_hl(0, "TelescopeBufferLoaded", { link = "String", default = true })
|
|
||||||
vim.api.nvim_set_hl(0, "TelescopePathSeparator", { link = "Directory", default = true })
|
|
||||||
vim.api.nvim_set_hl(0, "TelescopeFrecencyScores", { link = "Number", default = true })
|
|
||||||
vim.api.nvim_set_hl(0, "TelescopeQueryFilter", { link = "WildMenu", default = true })
|
|
||||||
|
|
||||||
---@param cmd_info { bang: boolean }
|
|
||||||
vim.api.nvim_create_user_command("FrecencyValidate", function(cmd_info)
|
|
||||||
frecency.validate_database(cmd_info.bang)
|
|
||||||
end, { bang = true, desc = "Clean up DB for telescope-frecency" })
|
|
||||||
|
|
||||||
vim.api.nvim_create_user_command("FrecencyDelete", function(info)
|
|
||||||
local path_string = info.args == "" and "%:p" or info.args
|
|
||||||
local path = vim.fn.expand(path_string) --[[@as string]]
|
|
||||||
frecency.delete(path)
|
|
||||||
end, { nargs = "?", complete = "file", desc = "Delete entry from telescope-frecency" })
|
|
||||||
|
|
||||||
local group = vim.api.nvim_create_augroup("TelescopeFrecency", {})
|
|
||||||
vim.api.nvim_create_autocmd({ "BufWinEnter", "BufWritePost" }, {
|
|
||||||
desc = "Update database for telescope-frecency",
|
|
||||||
group = group,
|
|
||||||
---@param args { buf: integer }
|
|
||||||
callback = function(args)
|
|
||||||
local is_floatwin = vim.api.nvim_win_get_config(0).relative ~= ""
|
|
||||||
if not is_floatwin then
|
|
||||||
frecency.register(args.buf)
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
})
|
|
||||||
end,
|
|
||||||
|
|
||||||
health = function()
|
health = function()
|
||||||
if vim.F.npcall(require, "nvim-web-devicons") then
|
if vim.F.npcall(require, "nvim-web-devicons") then
|
||||||
vim.health.ok "nvim-web-devicons installed."
|
vim.health.ok "nvim-web-devicons installed."
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user