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:
JINNOUCHI Yasushi 2024-03-14 20:28:06 +09:00 committed by GitHub
parent 771726f7d6
commit bd52772bf2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 377 additions and 305 deletions

View File

@ -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
View 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

View File

@ -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

View File

@ -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

View File

@ -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()

View File

@ -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,
},
} }