mirror of
https://github.com/kristoferssolo/telescope-frecency.nvim.git
synced 2025-10-21 20:10:38 +00:00
fix!: change timing for initialization (#179)
* fix!: change timing for initialization fix #109 fix #59 This fixes problems below. * Auto-validation feature is called at Neovim starting. - → Now it starts at `:Telescope frecency` called at the first time. * `frecency.setup()` is called every when `:Telescope frecency` is called. - → Now it is called only once. * `telescope.setup()` calls `frecency.new()` and it reads the database. This causes time in executing `init.lua`. - → Now it reads the database lazily. It reads at the first time when needed. * test: change logic to initialize config * test: make Neovim version newer in CI
This commit is contained in:
parent
771726f7d6
commit
bd52772bf2
4
.github/workflows/ci.yml
vendored
4
.github/workflows/ci.yml
vendored
@ -12,9 +12,9 @@ jobs:
|
|||||||
- macos-latest
|
- macos-latest
|
||||||
- windows-latest
|
- windows-latest
|
||||||
version:
|
version:
|
||||||
|
- v0.9.5
|
||||||
|
- v0.9.4
|
||||||
- v0.9.2
|
- v0.9.2
|
||||||
- v0.9.1
|
|
||||||
- v0.9.0
|
|
||||||
- nightly
|
- nightly
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
timeout-minutes: 15
|
timeout-minutes: 15
|
||||||
|
|||||||
100
lua/frecency/config.lua
Normal file
100
lua/frecency/config.lua
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
local os_util = require "frecency.os_util"
|
||||||
|
|
||||||
|
---@class FrecencyConfig: FrecencyRawConfig
|
||||||
|
---@field private values FrecencyRawConfig
|
||||||
|
local Config = {}
|
||||||
|
|
||||||
|
---@class FrecencyRawConfig
|
||||||
|
---@field auto_validate boolean default: true
|
||||||
|
---@field db_root string default: vim.fn.stdpath "data"
|
||||||
|
---@field db_safe_mode boolean default: true
|
||||||
|
---@field db_validate_threshold integer default: 10
|
||||||
|
---@field default_workspace string default: nil
|
||||||
|
---@field disable_devicons boolean default: false
|
||||||
|
---@field filter_delimiter string default: ":"
|
||||||
|
---@field hide_current_buffer boolean default: false
|
||||||
|
---@field ignore_patterns string[] default: { "*.git/*", "*/tmp/*", "term://*" }
|
||||||
|
---@field max_timestamps integer default: 10
|
||||||
|
---@field show_filter_column boolean|string[]|nil default: true
|
||||||
|
---@field show_scores boolean default: false
|
||||||
|
---@field show_unindexed boolean default: true
|
||||||
|
---@field workspace_scan_cmd "LUA"|string[]|nil default: nil
|
||||||
|
---@field workspaces table<string, string> default: {}
|
||||||
|
|
||||||
|
---@return FrecencyConfig
|
||||||
|
Config.new = function()
|
||||||
|
local default_values = {
|
||||||
|
auto_validate = true,
|
||||||
|
db_root = vim.fn.stdpath "data",
|
||||||
|
db_safe_mode = true,
|
||||||
|
db_validate_threshold = 10,
|
||||||
|
default_workspace = nil,
|
||||||
|
disable_devicons = false,
|
||||||
|
filter_delimiter = ":",
|
||||||
|
hide_current_buffer = false,
|
||||||
|
ignore_patterns = os_util.is_windows and { [[*.git\*]], [[*\tmp\*]], "term://*" }
|
||||||
|
or { "*.git/*", "*/tmp/*", "term://*" },
|
||||||
|
max_timestamps = 10,
|
||||||
|
show_filter_column = true,
|
||||||
|
show_scores = false,
|
||||||
|
show_unindexed = true,
|
||||||
|
workspace_scan_cmd = nil,
|
||||||
|
workspaces = {},
|
||||||
|
}
|
||||||
|
---@type table<string, boolean>
|
||||||
|
local keys = {}
|
||||||
|
for k, _ in pairs(default_values) do
|
||||||
|
keys[k] = true
|
||||||
|
end
|
||||||
|
return setmetatable({
|
||||||
|
values = default_values,
|
||||||
|
}, {
|
||||||
|
__index = function(self, key)
|
||||||
|
if key == "values" then
|
||||||
|
return rawget(self, key)
|
||||||
|
elseif keys[key] then
|
||||||
|
return rawget(rawget(self, "values"), key)
|
||||||
|
end
|
||||||
|
return rawget(Config, key)
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
local config = Config.new()
|
||||||
|
|
||||||
|
---@return FrecencyRawConfig
|
||||||
|
Config.get = function()
|
||||||
|
return config.values
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param ext_config any
|
||||||
|
---@return nil
|
||||||
|
Config.setup = function(ext_config)
|
||||||
|
local opts = vim.tbl_extend("force", config.values, ext_config or {})
|
||||||
|
vim.validate {
|
||||||
|
auto_validate = { opts.auto_validate, "b" },
|
||||||
|
db_root = { opts.db_root, "s" },
|
||||||
|
db_safe_mode = { opts.db_safe_mode, "b" },
|
||||||
|
db_validate_threshold = { opts.db_validate_threshold, "n" },
|
||||||
|
default_workspace = { opts.default_workspace, "s", true },
|
||||||
|
disable_devicons = { opts.disable_devicons, "b" },
|
||||||
|
filter_delimiter = { opts.filter_delimiter, "s" },
|
||||||
|
hide_current_buffer = { opts.hide_current_buffer, "b" },
|
||||||
|
ignore_patterns = { opts.ignore_patterns, "t" },
|
||||||
|
max_timestamps = {
|
||||||
|
opts.max_timestamps,
|
||||||
|
function(v)
|
||||||
|
return type(v) == "number" and v > 0
|
||||||
|
end,
|
||||||
|
"positive number",
|
||||||
|
},
|
||||||
|
show_filter_column = { opts.show_filter_column, { "b", "t" }, true },
|
||||||
|
show_scores = { opts.show_scores, "b" },
|
||||||
|
show_unindexed = { opts.show_unindexed, "b" },
|
||||||
|
workspace_scan_cmd = { opts.workspace_scan_cmd, { "s", "t" }, true },
|
||||||
|
workspaces = { opts.workspaces, "t" },
|
||||||
|
}
|
||||||
|
config.values = opts
|
||||||
|
end
|
||||||
|
|
||||||
|
return config
|
||||||
@ -1,237 +0,0 @@
|
|||||||
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 WebDevicons = require "frecency.web_devicons"
|
|
||||||
local os_util = require "frecency.os_util"
|
|
||||||
local log = require "plenary.log"
|
|
||||||
|
|
||||||
---@class Frecency
|
|
||||||
---@field config FrecencyConfig
|
|
||||||
---@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 = {}
|
|
||||||
|
|
||||||
---@class FrecencyConfig
|
|
||||||
---@field auto_validate boolean? default: true
|
|
||||||
---@field db_root string? default: vim.fn.stdpath "data"
|
|
||||||
---@field db_safe_mode boolean? default: true
|
|
||||||
---@field db_validate_threshold? integer default: 10
|
|
||||||
---@field default_workspace string? default: nil
|
|
||||||
---@field disable_devicons boolean? default: false
|
|
||||||
---@field filter_delimiter string? default: ":"
|
|
||||||
---@field hide_current_buffer boolean default: false
|
|
||||||
---@field ignore_patterns string[]? default: { "*.git/*", "*/tmp/*", "term://*" }
|
|
||||||
---@field max_timestamps integer? default: 10
|
|
||||||
---@field show_filter_column boolean|string[]|nil default: true
|
|
||||||
---@field show_scores boolean? default: false
|
|
||||||
---@field show_unindexed boolean? default: true
|
|
||||||
---@field workspace_scan_cmd "LUA"|string[]|nil default: nil
|
|
||||||
---@field workspaces table<string, string>? default: {}
|
|
||||||
|
|
||||||
---@param opts FrecencyConfig?
|
|
||||||
---@return Frecency
|
|
||||||
Frecency.new = function(opts)
|
|
||||||
---@type FrecencyConfig
|
|
||||||
local config = vim.tbl_extend("force", {
|
|
||||||
auto_validate = true,
|
|
||||||
db_root = vim.fn.stdpath "data",
|
|
||||||
db_safe_mode = true,
|
|
||||||
db_validate_threshold = 10,
|
|
||||||
default_workspace = nil,
|
|
||||||
disable_devicons = false,
|
|
||||||
filter_delimiter = ":",
|
|
||||||
hide_current_buffer = false,
|
|
||||||
ignore_patterns = os_util.is_windows and { [[*.git\*]], [[*\tmp\*]], "term://*" }
|
|
||||||
or { "*.git/*", "*/tmp/*", "term://*" },
|
|
||||||
max_timestamps = 10,
|
|
||||||
show_filter_column = true,
|
|
||||||
show_scores = false,
|
|
||||||
show_unindexed = true,
|
|
||||||
workspace_scan_cmd = nil,
|
|
||||||
workspaces = {},
|
|
||||||
}, opts or {})
|
|
||||||
local self = setmetatable({ buf_registered = {}, config = config }, { __index = Frecency })--[[@as Frecency]]
|
|
||||||
self.fs = FS.new { ignore_patterns = config.ignore_patterns }
|
|
||||||
|
|
||||||
self.database = Database.new(self.fs, { root = config.db_root })
|
|
||||||
local web_devicons = WebDevicons.new(not config.disable_devicons)
|
|
||||||
self.entry_maker = EntryMaker.new(self.fs, web_devicons, {
|
|
||||||
show_filter_column = config.show_filter_column,
|
|
||||||
show_scores = config.show_scores,
|
|
||||||
})
|
|
||||||
local max_count = config.max_timestamps > 0 and config.max_timestamps or 10
|
|
||||||
self.recency = Recency.new { max_count = max_count }
|
|
||||||
return self
|
|
||||||
end
|
|
||||||
|
|
||||||
---@return nil
|
|
||||||
function Frecency:setup()
|
|
||||||
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 })
|
|
||||||
|
|
||||||
-- TODO: Should we schedule this after loading shada?
|
|
||||||
self:assert_db_entries()
|
|
||||||
|
|
||||||
---@param cmd_info { bang: boolean }
|
|
||||||
vim.api.nvim_create_user_command("FrecencyValidate", function(cmd_info)
|
|
||||||
self:validate_database(cmd_info.bang)
|
|
||||||
end, { bang = true, desc = "Clean up DB for telescope-frecency" })
|
|
||||||
|
|
||||||
if self.config.auto_validate then
|
|
||||||
self:validate_database()
|
|
||||||
end
|
|
||||||
|
|
||||||
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]]
|
|
||||||
self: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)
|
|
||||||
self:register(args.buf)
|
|
||||||
end,
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
---@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 self.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, {
|
|
||||||
default_workspace_tag = self.config.default_workspace,
|
|
||||||
editing_bufnr = vim.api.nvim_get_current_buf(),
|
|
||||||
filter_delimiter = self.config.filter_delimiter,
|
|
||||||
ignore_filenames = ignore_filenames,
|
|
||||||
initial_workspace_tag = opts.workspace,
|
|
||||||
show_unindexed = self.config.show_unindexed,
|
|
||||||
workspace_scan_cmd = self.config.workspace_scan_cmd,
|
|
||||||
workspaces = self.config.workspaces,
|
|
||||||
})
|
|
||||||
self.picker:start(vim.tbl_extend("force", self.config, opts))
|
|
||||||
log.debug(("Frecency:start picker:start takes %f seconds"):format(os.clock() - start))
|
|
||||||
end
|
|
||||||
|
|
||||||
---@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 < self.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 self.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 datetime string? ISO8601 format string
|
|
||||||
function Frecency:register(bufnr, datetime)
|
|
||||||
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, self.recency.config.max_count, datetime)
|
|
||||||
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
|
|
||||||
|
|
||||||
---@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
|
|
||||||
@ -1,29 +1,172 @@
|
|||||||
---@type Frecency?
|
local Database = require "frecency.database"
|
||||||
local frecency
|
local EntryMaker = require "frecency.entry_maker"
|
||||||
|
local FS = require "frecency.fs"
|
||||||
|
local Picker = require "frecency.picker"
|
||||||
|
local Recency = require "frecency.recency"
|
||||||
|
local WebDevicons = require "frecency.web_devicons"
|
||||||
|
local config = require "frecency.config"
|
||||||
|
local log = require "plenary.log"
|
||||||
|
|
||||||
return {
|
---@class Frecency
|
||||||
---@param opts FrecencyConfig?
|
---@field private buf_registered table<integer, boolean> flag to indicate the buffer is registered to the database.
|
||||||
setup = function(opts)
|
---@field private database FrecencyDatabase
|
||||||
frecency = require("frecency.frecency").new(opts)
|
---@field private entry_maker FrecencyEntryMaker
|
||||||
frecency:setup()
|
---@field private fs FrecencyFS
|
||||||
end,
|
---@field private picker FrecencyPicker
|
||||||
---@param opts FrecencyPickerOptions
|
---@field private recency FrecencyRecency
|
||||||
start = function(opts)
|
local Frecency = {}
|
||||||
if frecency then
|
|
||||||
frecency:start(opts)
|
---@return Frecency
|
||||||
|
Frecency.new = function()
|
||||||
|
local self = setmetatable({ buf_registered = {} }, { __index = Frecency }) --[[@as Frecency]]
|
||||||
|
self.fs = FS.new { ignore_patterns = config.ignore_patterns }
|
||||||
|
self.database = Database.new(self.fs, { root = config.db_root })
|
||||||
|
local web_devicons = WebDevicons.new(not config.disable_devicons)
|
||||||
|
self.entry_maker = EntryMaker.new(self.fs, web_devicons, {
|
||||||
|
show_filter_column = config.show_filter_column,
|
||||||
|
show_scores = config.show_scores,
|
||||||
|
})
|
||||||
|
self.recency = Recency.new { max_count = config.max_timestamps }
|
||||||
|
return self
|
||||||
end
|
end
|
||||||
end,
|
|
||||||
|
---This is called when `:Telescope frecency` is called at the first time.
|
||||||
|
---@return nil
|
||||||
|
function Frecency:setup()
|
||||||
|
self:assert_db_entries()
|
||||||
|
if config.auto_validate then
|
||||||
|
self:validate_database()
|
||||||
|
end
|
||||||
|
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, {
|
||||||
|
default_workspace_tag = config.default_workspace,
|
||||||
|
editing_bufnr = vim.api.nvim_get_current_buf(),
|
||||||
|
filter_delimiter = config.filter_delimiter,
|
||||||
|
ignore_filenames = ignore_filenames,
|
||||||
|
initial_workspace_tag = opts.workspace,
|
||||||
|
show_unindexed = config.show_unindexed,
|
||||||
|
workspace_scan_cmd = config.workspace_scan_cmd,
|
||||||
|
workspaces = config.workspaces,
|
||||||
|
})
|
||||||
|
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 findstart 1|0
|
||||||
---@param base string
|
---@param base string
|
||||||
---@return integer|''|string[]
|
---@return integer|''|string[]
|
||||||
complete = function(findstart, base)
|
function Frecency:complete(findstart, base)
|
||||||
if frecency then
|
return self.picker:complete(findstart, base)
|
||||||
return frecency:complete(findstart, base)
|
|
||||||
end
|
end
|
||||||
return ""
|
|
||||||
|
---@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,
|
||||||
---@return Frecency
|
}, function(item)
|
||||||
frecency = function()
|
if item == "y" then
|
||||||
return assert(frecency)
|
remove_entries()
|
||||||
end,
|
else
|
||||||
}
|
self:notify "validation aborted"
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param bufnr integer
|
||||||
|
---@param datetime string? ISO8601 format string
|
||||||
|
function Frecency:register(bufnr, datetime)
|
||||||
|
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, self.recency.config.max_count, datetime)
|
||||||
|
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
|
||||||
|
|
||||||
|
---@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,19 +3,30 @@
|
|||||||
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.frecency"
|
local Frecency = require "frecency"
|
||||||
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"
|
||||||
local Path = require "plenary.path"
|
local Path = require "plenary.path"
|
||||||
|
local config = require "frecency.config"
|
||||||
|
|
||||||
---@param files string[]
|
---@param files string[]
|
||||||
---@param callback fun(frecency: Frecency, finder: FrecencyFinder, dir: PlenaryPath): nil
|
---@param cb_or_config table|fun(frecency: Frecency, finder: FrecencyFinder, dir: PlenaryPath): nil
|
||||||
|
---@param callback? fun(frecency: Frecency, finder: FrecencyFinder, dir: PlenaryPath): nil
|
||||||
---@return nil
|
---@return nil
|
||||||
local function with_files(files, callback)
|
local function with_files(files, cb_or_config, callback)
|
||||||
local dir, close = util.make_tree(files)
|
local dir, close = util.make_tree(files)
|
||||||
log.debug { db_root = dir.filename }
|
local cfg
|
||||||
local frecency = Frecency.new { db_root = dir.filename }
|
if type(cb_or_config) == "table" then
|
||||||
|
cfg = vim.tbl_extend("force", { db_root = dir.filename }, cb_or_config)
|
||||||
|
else
|
||||||
|
cfg = { db_root = dir.filename }
|
||||||
|
callback = cb_or_config
|
||||||
|
end
|
||||||
|
assert(callback)
|
||||||
|
log.debug(cfg)
|
||||||
|
config.setup(cfg)
|
||||||
|
local frecency = Frecency.new()
|
||||||
frecency.picker = Picker.new(
|
frecency.picker = Picker.new(
|
||||||
frecency.database,
|
frecency.database,
|
||||||
frecency.entry_maker,
|
frecency.entry_maker,
|
||||||
@ -232,14 +243,16 @@ describe("frecency", function()
|
|||||||
|
|
||||||
describe("when with not force", function()
|
describe("when with not force", function()
|
||||||
describe("when files are unlinked but it is less than threshold", function()
|
describe("when files are unlinked but it is less than threshold", function()
|
||||||
with_files({ "hoge1.txt", "hoge2.txt", "hoge3.txt", "hoge4.txt", "hoge5.txt" }, function(frecency, finder, dir)
|
with_files(
|
||||||
|
{ "hoge1.txt", "hoge2.txt", "hoge3.txt", "hoge4.txt", "hoge5.txt" },
|
||||||
|
{ db_validate_threshold = 3 },
|
||||||
|
function(frecency, finder, dir)
|
||||||
local register = make_register(frecency, dir)
|
local register = make_register(frecency, dir)
|
||||||
register("hoge1.txt", "2023-07-29T00:00:00+09:00")
|
register("hoge1.txt", "2023-07-29T00:00:00+09:00")
|
||||||
register("hoge2.txt", "2023-07-29T00:01:00+09:00")
|
register("hoge2.txt", "2023-07-29T00:01:00+09:00")
|
||||||
register("hoge3.txt", "2023-07-29T00:02:00+09:00")
|
register("hoge3.txt", "2023-07-29T00:02:00+09:00")
|
||||||
register("hoge4.txt", "2023-07-29T00:03:00+09:00")
|
register("hoge4.txt", "2023-07-29T00:03:00+09:00")
|
||||||
register("hoge5.txt", "2023-07-29T00:04:00+09:00")
|
register("hoge5.txt", "2023-07-29T00:04:00+09:00")
|
||||||
frecency.config.db_validate_threshold = 3
|
|
||||||
dir:joinpath("hoge1.txt"):rm()
|
dir:joinpath("hoge1.txt"):rm()
|
||||||
dir:joinpath("hoge2.txt"):rm()
|
dir:joinpath("hoge2.txt"):rm()
|
||||||
frecency:validate_database()
|
frecency:validate_database()
|
||||||
@ -257,13 +270,15 @@ describe("frecency", function()
|
|||||||
{ count = 1, path = filepath(dir, "hoge5.txt"), score = 10 },
|
{ count = 1, path = filepath(dir, "hoge5.txt"), score = 10 },
|
||||||
}, results)
|
}, results)
|
||||||
end)
|
end)
|
||||||
end)
|
end
|
||||||
|
)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
describe("when files are unlinked and it is more than threshold", function()
|
describe("when files are unlinked and it is more than threshold", function()
|
||||||
describe('when the user response "yes"', function()
|
describe('when the user response "yes"', function()
|
||||||
with_files(
|
with_files(
|
||||||
{ "hoge1.txt", "hoge2.txt", "hoge3.txt", "hoge4.txt", "hoge5.txt" },
|
{ "hoge1.txt", "hoge2.txt", "hoge3.txt", "hoge4.txt", "hoge5.txt" },
|
||||||
|
{ db_validate_threshold = 3 },
|
||||||
function(frecency, finder, dir)
|
function(frecency, finder, dir)
|
||||||
local register = make_register(frecency, dir)
|
local register = make_register(frecency, dir)
|
||||||
register("hoge1.txt", "2023-07-29T00:00:00+09:00")
|
register("hoge1.txt", "2023-07-29T00:00:00+09:00")
|
||||||
@ -271,7 +286,6 @@ describe("frecency", function()
|
|||||||
register("hoge3.txt", "2023-07-29T00:02:00+09:00")
|
register("hoge3.txt", "2023-07-29T00:02:00+09:00")
|
||||||
register("hoge4.txt", "2023-07-29T00:03:00+09:00")
|
register("hoge4.txt", "2023-07-29T00:03:00+09:00")
|
||||||
register("hoge5.txt", "2023-07-29T00:04:00+09:00")
|
register("hoge5.txt", "2023-07-29T00:04:00+09:00")
|
||||||
frecency.config.db_validate_threshold = 3
|
|
||||||
dir:joinpath("hoge1.txt"):rm()
|
dir:joinpath("hoge1.txt"):rm()
|
||||||
dir:joinpath("hoge2.txt"):rm()
|
dir:joinpath("hoge2.txt"):rm()
|
||||||
dir:joinpath("hoge3.txt"):rm()
|
dir:joinpath("hoge3.txt"):rm()
|
||||||
@ -301,6 +315,7 @@ describe("frecency", function()
|
|||||||
describe('when the user response "no"', function()
|
describe('when the user response "no"', function()
|
||||||
with_files(
|
with_files(
|
||||||
{ "hoge1.txt", "hoge2.txt", "hoge3.txt", "hoge4.txt", "hoge5.txt" },
|
{ "hoge1.txt", "hoge2.txt", "hoge3.txt", "hoge4.txt", "hoge5.txt" },
|
||||||
|
{ db_validate_threshold = 3 },
|
||||||
function(frecency, finder, dir)
|
function(frecency, finder, dir)
|
||||||
local register = make_register(frecency, dir)
|
local register = make_register(frecency, dir)
|
||||||
register("hoge1.txt", "2023-07-29T00:00:00+09:00")
|
register("hoge1.txt", "2023-07-29T00:00:00+09:00")
|
||||||
@ -308,7 +323,6 @@ describe("frecency", function()
|
|||||||
register("hoge3.txt", "2023-07-29T00:02:00+09:00")
|
register("hoge3.txt", "2023-07-29T00:02:00+09:00")
|
||||||
register("hoge4.txt", "2023-07-29T00:03:00+09:00")
|
register("hoge4.txt", "2023-07-29T00:03:00+09:00")
|
||||||
register("hoge5.txt", "2023-07-29T00:04:00+09:00")
|
register("hoge5.txt", "2023-07-29T00:04:00+09:00")
|
||||||
frecency.config.db_validate_threshold = 3
|
|
||||||
dir:joinpath("hoge1.txt"):rm()
|
dir:joinpath("hoge1.txt"):rm()
|
||||||
dir:joinpath("hoge2.txt"):rm()
|
dir:joinpath("hoge2.txt"):rm()
|
||||||
dir:joinpath("hoge3.txt"):rm()
|
dir:joinpath("hoge3.txt"):rm()
|
||||||
@ -366,14 +380,13 @@ describe("frecency", function()
|
|||||||
end)
|
end)
|
||||||
|
|
||||||
describe("when db_safe_mode is false", function()
|
describe("when db_safe_mode is false", function()
|
||||||
with_files({ "hoge1.txt", "hoge2.txt" }, function(frecency, finder, dir)
|
with_files({ "hoge1.txt", "hoge2.txt" }, { db_safe_mode = false }, function(frecency, finder, dir)
|
||||||
local register = make_register(frecency, dir)
|
local register = make_register(frecency, dir)
|
||||||
register("hoge1.txt", "2023-07-29T00:00:00+09:00")
|
register("hoge1.txt", "2023-07-29T00:00:00+09:00")
|
||||||
register("hoge2.txt", "2023-07-29T00:01:00+09:00")
|
register("hoge2.txt", "2023-07-29T00:01:00+09:00")
|
||||||
dir:joinpath("hoge1.txt"):rm()
|
dir:joinpath("hoge1.txt"):rm()
|
||||||
|
|
||||||
with_fake_vim_ui_select("y", function(called)
|
with_fake_vim_ui_select("y", function(called)
|
||||||
frecency.config.db_safe_mode = false
|
|
||||||
frecency:validate_database(true)
|
frecency:validate_database(true)
|
||||||
|
|
||||||
it("did not call vim.ui.select()", function()
|
it("did not call vim.ui.select()", function()
|
||||||
|
|||||||
@ -1,7 +1,64 @@
|
|||||||
local frecency = require "frecency"
|
---This object is intended to be used as a singleton, and is lazily loaded.
|
||||||
|
---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 register fun(bufnr: integer, datetime: string?): nil
|
||||||
|
---@field start fun(opts: FrecencyPickerOptions?): nil
|
||||||
|
---@field validate_database fun(force: boolean?): nil
|
||||||
|
local frecency = setmetatable({}, {
|
||||||
|
__index = function(self, key)
|
||||||
|
return function(...)
|
||||||
|
local instance = rawget(self, "instance") --[[@as Frecency?]]
|
||||||
|
if not instance then
|
||||||
|
instance = require("frecency").new()
|
||||||
|
instance:setup()
|
||||||
|
rawset(self, "instance", instance)
|
||||||
|
end
|
||||||
|
return instance[key](instance, ...)
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
|
||||||
return require("telescope").register_extension {
|
return require("telescope").register_extension {
|
||||||
setup = frecency.setup,
|
exports = {
|
||||||
|
frecency = frecency.start,
|
||||||
|
complete = frecency.complete,
|
||||||
|
},
|
||||||
|
|
||||||
|
---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)
|
||||||
|
frecency.register(args.buf)
|
||||||
|
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."
|
||||||
@ -18,8 +75,4 @@ return require("telescope").register_extension {
|
|||||||
vim.health.info "No suitable find executable found."
|
vim.health.info "No suitable find executable found."
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
exports = {
|
|
||||||
frecency = frecency.start,
|
|
||||||
complete = frecency.complete,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user