Skip to content

Commit 16ea7dd

Browse files
committed
feat(picker): cache apropos result to speed up
1 parent 0d8b6ea commit 16ea7dd

1 file changed

Lines changed: 60 additions & 6 deletions

File tree

lua/telescope/builtin/__internal.lua

Lines changed: 60 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -828,16 +828,70 @@ internal.man_pages = function(opts)
828828
assert(vim.islist(opts.sections), "sections should be a list")
829829
opts.man_cmd = utils.get_lazy_default(opts.man_cmd, function()
830830
local uname = vim.uv.os_uname()
831-
local sysname = string.lower(uname.sysname)
832-
if sysname == "darwin" then
833-
local major_version = tonumber(vim.fn.matchlist(uname.release, [[^\(\d\+\)\..*]])[2]) or 0
834-
return major_version >= 22 and { "apropos", "." } or { "apropos", " " }
835-
elseif sysname == "freebsd" then
831+
local sysname = uname.sysname:lower()
832+
if sysname == "freebsd" then
836833
return { "apropos", "." }
837-
else
834+
elseif sysname ~= "darwin" then
838835
return { "apropos", "" }
839836
end
837+
838+
-- Cache apropos result on macOS speed up. macOS with SIP (System Integrity
839+
-- Protection) cannot update the whatis database, so makes apropos much
840+
-- slower. We cache results and invalidate it when the mtime's of man
841+
-- directories are changed.
842+
local mansects, cache_filename
843+
if vim.env.MANSECT then
844+
mansects = vim.split(vim.env.MANSECT, ":")
845+
cache_filename = ("telescope-man-pages-%s"):format(vim.env.MANSECT:gsub(":", "-"))
846+
else
847+
-- This MANSECT default value is derived from apropos' man page
848+
mansects = vim.split("1:8:2:3:3lua:n:4:5:6:7:9:l", ":")
849+
cache_filename = "telescope-man-pages"
850+
end
851+
local cache_path = vim.fs.joinpath(vim.fn.stdpath "cache", cache_filename)
852+
local cache_stat = vim.uv.fs_stat(cache_path)
853+
854+
if cache_stat and cache_stat.size > 0 then
855+
local manpath = vim.env.MANPATH or vim.trim(vim.system({ "manpath" }):wait().stdout)
856+
local man_dirs = vim.split(manpath, ":", { trimempty = true })
857+
858+
local mansects_map = vim.iter(mansects):fold({}, function(a, b)
859+
a[b] = true
860+
return a
861+
end)
862+
local function is_man_dir(path)
863+
local s = vim.fs.basename(path):match "^man(.*)$"
864+
return s and mansects_map[s] or false
865+
end
866+
867+
-- Detect mtime of man directories that is newer than cache file
868+
local cache_invalid = vim.iter(man_dirs):any(function(man_dir)
869+
local search = vim.fs.dir(man_dir, {
870+
depth = 2,
871+
skip = function(dir_name)
872+
return not is_man_dir(dir_name)
873+
end,
874+
})
875+
return vim.iter(search):any(function(ent, typ)
876+
if typ ~= "directory" or not is_man_dir(ent) then
877+
return false
878+
end
879+
local dir = vim.fs.joinpath(man_dir, ent)
880+
local stat = vim.uv.fs_stat(dir)
881+
return stat and stat.mtime.sec > cache_stat.mtime.sec or false
882+
end)
883+
end)
884+
885+
if not cache_invalid then
886+
return { "cat", cache_path }
887+
end
888+
end
889+
890+
local major_version = tonumber(vim.fn.matchlist(uname.release, [[^\(\d\+\)\..*]])[2]) or 0
891+
local cmd = major_version >= 22 and "apropos ." or "apropos ' '"
892+
return { "sh", "-c", ("%s | tee %s"):format(cmd, cache_path) }
840893
end)
894+
841895
opts.entry_maker = opts.entry_maker or make_entry.gen_from_apropos(opts)
842896
opts.env = { PATH = vim.env.PATH, MANPATH = vim.env.MANPATH }
843897

0 commit comments

Comments
 (0)