From 028e772f32ca143b40b776df96235dfe103a307f Mon Sep 17 00:00:00 2001 From: hnolan Date: Sun, 11 May 2025 13:51:53 -0400 Subject: [PATCH 1/6] Partial matching for restrict_sources Modified all usages of restrict_sources to consider partial matches. E.g., previously, `restrict_sources={"python"}` would fail to find `python~3.13`, whereas now that will be considered a match. Also added an example remap to the README that shows how this can be used to search the docs for the current file's language automatically. --- lua/apidocs.lua | 11 +++ lua/apidocs/snacks.lua | 14 +++- lua/apidocs/telescope.lua | 140 +++++++++++++++++++++----------------- 3 files changed, 99 insertions(+), 66 deletions(-) diff --git a/lua/apidocs.lua b/lua/apidocs.lua index 9621ff1..85f8e74 100644 --- a/lua/apidocs.lua +++ b/lua/apidocs.lua @@ -39,6 +39,17 @@ local function apidocs_open(opts) end if type == "directory" then if opts and opts.restrict_sources then + for _, source in ipairs(opts.restrict_sources) do + -- check for exact match + if name == source then + table.insert(installed_docs, name) + -- check for partial match + -- e.g. "python" -> "python~3.12" + elseif name:match("^" .. source .. "~%d+(%.%d+)?$") then + table.insert(installed_docs, name) + end + end + if vim.tbl_contains(opts.restrict_sources, name) then table.insert(installed_docs, name) end diff --git a/lua/apidocs/snacks.lua b/lua/apidocs/snacks.lua index 3aa8e9a..a47c911 100644 --- a/lua/apidocs/snacks.lua +++ b/lua/apidocs/snacks.lua @@ -26,10 +26,18 @@ local function get_data_dirs(opts) return { data_dir } end local dirs = {} + local install_dirs = vim.fn.readdir(data_dir) for _, source in ipairs(opts.restrict_sources) do - local dir = data_dir .. source .. "/" - if vim.fn.isdirectory(dir) == 1 then - table.insert(dirs, dir) + for _, entry in ipairs(install_dirs) do + if vim.fn.isdirectory(data_dir .. entry) == 1 then + if entry == source then + table.insert(dirs, data_dir .. entry .. "/") + elseif entry:match("^" .. source .. "~%d+(%.%d+)?$") then + -- check for partial match + -- e.g. "python" -> "python~3.12" + table.insert(dirs, data_dir .. entry .. "/") + end + end end end return dirs diff --git a/lua/apidocs/telescope.lua b/lua/apidocs/telescope.lua index dc3737e..2d3652d 100644 --- a/lua/apidocs/telescope.lua +++ b/lua/apidocs/telescope.lua @@ -2,8 +2,8 @@ local common = require("apidocs.common") local install = require("apidocs.install") local function telescope_attach_mappings(prompt_bufnr, map) - local actions = require('telescope.actions') - map('i', '', function(nr) + local actions = require("telescope.actions") + map("i", "", function(nr) actions.close(prompt_bufnr) local entry = require("telescope.actions.state").get_selected_entry(prompt_bufnr) common.open_doc_in_new_window(entry.filename or entry.value) @@ -11,14 +11,14 @@ local function telescope_attach_mappings(prompt_bufnr, map) vim.cmd(":" .. entry.lnum) vim.cmd("norm! zz") end - end, {buffer = true}) + end, { buffer = true }) return true end local function apidocs_open(params, slugs_to_mtimes, candidates) local docs_path = common.data_folder() - local pickers = require "telescope.pickers" - local finders = require "telescope.finders" + local pickers = require("telescope.pickers") + local finders = require("telescope.finders") local previewers = require("telescope.previewers") local conf = require("telescope.config").values @@ -31,75 +31,89 @@ local function apidocs_open(params, slugs_to_mtimes, candidates) } end - pickers.new({}, { - prompt_title = "API docs", - finder = finders.new_table { - results = candidates, - entry_maker = entry_maker - }, - previewer = previewers.new_buffer_previewer({ - -- messy because of the conceal - setup = function(self) - vim.schedule(function() - local winid = self.state.winid - vim.wo[winid].conceallevel = 2 - vim.wo[winid].concealcursor = "n" - local augroup = vim.api.nvim_create_augroup('TelescopeApiDocsResumeConceal', { clear = true }) - vim.api.nvim_create_autocmd({"User"}, { - group = augroup, - pattern = "TelescopeResumePost", - callback = function() - local action_state = require("telescope.actions.state") - local current_picker = action_state.get_current_picker(vim.api.nvim_get_current_buf()) - if current_picker.prompt_title == "API docs" or current_picker.prompt_title == "API docs search" then - local winid = current_picker.all_previewers[1].state.winid - vim.wo[winid].conceallevel = 2 - vim.wo[winid].concealcursor = "n" - end - end - }) - end) - return {} - end, - define_preview = function(self, entry) - common.load_doc_in_buffer(self.state.bufnr, entry.value) - end, - }), - sorter = conf.generic_sorter({}), - attach_mappings = telescope_attach_mappings, - }):find() + pickers + .new({}, { + prompt_title = "API docs", + finder = finders.new_table({ + results = candidates, + entry_maker = entry_maker, + }), + previewer = previewers.new_buffer_previewer({ + -- messy because of the conceal + setup = function(self) + vim.schedule(function() + local winid = self.state.winid + vim.wo[winid].conceallevel = 2 + vim.wo[winid].concealcursor = "n" + local augroup = vim.api.nvim_create_augroup("TelescopeApiDocsResumeConceal", { clear = true }) + vim.api.nvim_create_autocmd({ "User" }, { + group = augroup, + pattern = "TelescopeResumePost", + callback = function() + local action_state = require("telescope.actions.state") + local current_picker = action_state.get_current_picker(vim.api.nvim_get_current_buf()) + if current_picker.prompt_title == "API docs" or current_picker.prompt_title == "API docs search" then + local winid = current_picker.all_previewers[1].state.winid + vim.wo[winid].conceallevel = 2 + vim.wo[winid].concealcursor = "n" + end + end, + }) + end) + return {} + end, + define_preview = function(self, entry) + common.load_doc_in_buffer(self.state.bufnr, entry.value) + end, + }), + sorter = conf.generic_sorter({}), + attach_mappings = telescope_attach_mappings, + }) + :find() end local function apidocs_search(opts) local previewers = require("telescope.previewers") - local make_entry = require "telescope.make_entry" + local make_entry = require("telescope.make_entry") local folder = common.data_folder() if opts and opts.source then folder = folder .. opts.source .. "/" end - local search_dirs = {folder} + local search_dirs = { folder } if opts and opts.restrict_sources then - search_dirs = vim.tbl_map(function(d) return folder .. d end, opts.restrict_sources) + local install_dirs = vim.fn.readdir(folder) + for _, source in ipairs(opts.restrict_sources) do + for _, entry in ipairs(install_dirs) do + if vim.fn.isdirectory(folder .. entry) == 1 then + if entry == source then + table.insert(search_dirs, folder .. entry .. "/") + elseif entry:match("^" .. source .. "~%d+(%.%d+)?$") then + -- check for partial match + -- e.g. "python" -> "python~3.12" + table.insert(search_dirs, folder .. entry .. "/") + end + end + end + end end - local default_entry_maker = make_entry.gen_from_vimgrep() local function entry_maker(entry) local r = default_entry_maker(entry) - r.display = function(entry) - local entry_components = vim.split(entry.filename:sub(#folder+1), "#") - local source_length = entry_components[1]:find("/") - local hl_group = { - { {0, source_length}, "TelescopeResultsTitle"}, - { {source_length, #entry_components[1]}, "TelescopeResultsMethod" }, - { {#entry_components[1], #entry_components[1] + #(tostring(entry.lnum))+2}, "TelescopeResultsLineNr" }, - } - return string.format("%s:%d: %s", entry_components[1], entry.lnum, entry.text), hl_group - end + r.display = function(entry) + local entry_components = vim.split(entry.filename:sub(#folder + 1), "#") + local source_length = entry_components[1]:find("/") + local hl_group = { + { { 0, source_length }, "TelescopeResultsTitle" }, + { { source_length, #entry_components[1] }, "TelescopeResultsMethod" }, + { { #entry_components[1], #entry_components[1] + #(tostring(entry.lnum)) + 2 }, "TelescopeResultsLineNr" }, + } + return string.format("%s:%d: %s", entry_components[1], entry.lnum, entry.text), hl_group + end return r end - require('telescope.builtin').live_grep({ + require("telescope.builtin").live_grep({ cwd = folder, search_dirs = search_dirs, prompt_title = "API docs search", @@ -111,8 +125,8 @@ local function apidocs_search(opts) local winid = self.state.winid vim.wo[winid].conceallevel = 2 vim.wo[winid].concealcursor = "n" - local augroup = vim.api.nvim_create_augroup('TelescopeApiDocsResumeConceal', { clear = true }) - vim.api.nvim_create_autocmd({"User"}, { + local augroup = vim.api.nvim_create_augroup("TelescopeApiDocsResumeConceal", { clear = true }) + vim.api.nvim_create_autocmd({ "User" }, { group = augroup, pattern = "TelescopeResumePost", callback = function() @@ -123,7 +137,7 @@ local function apidocs_search(opts) vim.wo[winid].conceallevel = 2 vim.wo[winid].concealcursor = "n" end - end + end, }) end) return {} @@ -131,10 +145,10 @@ local function apidocs_search(opts) define_preview = function(self, entry) common.load_doc_in_buffer(self.state.bufnr, entry.filename) - local ns = vim.api.nvim_create_namespace('my_highlights') - vim.api.nvim_buf_set_extmark(self.state.bufnr, ns, entry.lnum-1, 0, { + local ns = vim.api.nvim_create_namespace("my_highlights") + vim.api.nvim_buf_set_extmark(self.state.bufnr, ns, entry.lnum - 1, 0, { end_line = entry.lnum, - hl_group = 'TelescopePreviewMatch', + hl_group = "TelescopePreviewMatch", strict = false, }) vim.schedule(function() From c553ca4b2993204f87953aaba06df7ec6176db74 Mon Sep 17 00:00:00 2001 From: hnolan Date: Sun, 11 May 2025 13:53:12 -0400 Subject: [PATCH 2/6] Add README example --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index 818447b..f57a2f1 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,14 @@ return { end, keys = { { 'sad', 'ApidocsOpen', desc = 'Search Api Doc' }, + { + 'sac', + function() + local lang = vim.bo.filetype + require("apidocs").apidocs_search({ restrict_sources = { lang } }) + end, + desc = 'Search Api Doc (restricted to current file type)' + }, }, } ``` From 0e744c8c2059e11973739b678205b8d4c136a52c Mon Sep 17 00:00:00 2001 From: hnolan Date: Sun, 11 May 2025 15:22:11 -0400 Subject: [PATCH 3/6] Remove duplicate equality check --- lua/apidocs.lua | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/lua/apidocs.lua b/lua/apidocs.lua index 85f8e74..1679d3d 100644 --- a/lua/apidocs.lua +++ b/lua/apidocs.lua @@ -40,19 +40,14 @@ local function apidocs_open(opts) if type == "directory" then if opts and opts.restrict_sources then for _, source in ipairs(opts.restrict_sources) do - -- check for exact match - if name == source then + if vim.tbl_contains(opts.restrict_sources, name) then table.insert(installed_docs, name) - -- check for partial match + -- no exact match, check for partial match -- e.g. "python" -> "python~3.12" elseif name:match("^" .. source .. "~%d+(%.%d+)?$") then table.insert(installed_docs, name) end end - - if vim.tbl_contains(opts.restrict_sources, name) then - table.insert(installed_docs, name) - end else table.insert(installed_docs, name) end From f464e1c4562d2aa106c79ab4a18311d1b5bd7ad5 Mon Sep 17 00:00:00 2001 From: hnolan Date: Sun, 11 May 2025 16:03:51 -0400 Subject: [PATCH 4/6] Refactor get_data_dirs to only initiate extra loop if no exact match is found --- lua/apidocs/snacks.lua | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lua/apidocs/snacks.lua b/lua/apidocs/snacks.lua index a47c911..b9c7abc 100644 --- a/lua/apidocs/snacks.lua +++ b/lua/apidocs/snacks.lua @@ -28,11 +28,12 @@ local function get_data_dirs(opts) local dirs = {} local install_dirs = vim.fn.readdir(data_dir) for _, source in ipairs(opts.restrict_sources) do - for _, entry in ipairs(install_dirs) do - if vim.fn.isdirectory(data_dir .. entry) == 1 then - if entry == source then - table.insert(dirs, data_dir .. entry .. "/") - elseif entry:match("^" .. source .. "~%d+(%.%d+)?$") then + local dir = data_dir .. source .. "/" + if vim.fn.isdirectory(dir) == 1 then + table.insert(dirs, dir) + else + for _, entry in ipairs(install_dirs) do + if entry:match("^" .. source .. "~%d+") then -- check for partial match -- e.g. "python" -> "python~3.12" table.insert(dirs, data_dir .. entry .. "/") From 7fc1c9d8e0374f703f2ef8dc8732e30f86889ce4 Mon Sep 17 00:00:00 2001 From: hnolan Date: Sun, 11 May 2025 16:04:38 -0400 Subject: [PATCH 5/6] Fix restrict_sources implementation for Telescope Note: does not currently function with `telescope.apidocs_open`, only `telescope.apidocs_search`. --- lua/apidocs/telescope.lua | 42 +++++++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/lua/apidocs/telescope.lua b/lua/apidocs/telescope.lua index 2d3652d..3c2e00b 100644 --- a/lua/apidocs/telescope.lua +++ b/lua/apidocs/telescope.lua @@ -72,6 +72,30 @@ local function apidocs_open(params, slugs_to_mtimes, candidates) :find() end +local function get_data_dirs(opts) + local data_dir = common.data_folder() + if not (opts and opts.restrict_sources) then + return { data_dir } + end + local dirs = {} + local install_dirs = vim.fn.readdir(data_dir) + for _, source in ipairs(opts.restrict_sources) do + local dir = data_dir .. source .. "/" + if vim.fn.isdirectory(dir) == 1 then + table.insert(dirs, dir) + else + for _, entry in ipairs(install_dirs) do + if entry:match("^" .. source .. "~%d+") then + -- check for partial match + -- e.g. "python" -> "python~3.12" + table.insert(dirs, data_dir .. entry .. "/") + end + end + end + end + return dirs +end + local function apidocs_search(opts) local previewers = require("telescope.previewers") local make_entry = require("telescope.make_entry") @@ -79,23 +103,7 @@ local function apidocs_search(opts) if opts and opts.source then folder = folder .. opts.source .. "/" end - local search_dirs = { folder } - if opts and opts.restrict_sources then - local install_dirs = vim.fn.readdir(folder) - for _, source in ipairs(opts.restrict_sources) do - for _, entry in ipairs(install_dirs) do - if vim.fn.isdirectory(folder .. entry) == 1 then - if entry == source then - table.insert(search_dirs, folder .. entry .. "/") - elseif entry:match("^" .. source .. "~%d+(%.%d+)?$") then - -- check for partial match - -- e.g. "python" -> "python~3.12" - table.insert(search_dirs, folder .. entry .. "/") - end - end - end - end - end + local search_dirs = get_data_dirs(opts) local default_entry_maker = make_entry.gen_from_vimgrep() local function entry_maker(entry) From 6cbc367f209c8dd663cc46990bb2da5c2d1eaa46 Mon Sep 17 00:00:00 2001 From: hnolan Date: Thu, 22 May 2025 07:42:45 -0400 Subject: [PATCH 6/6] Rework restrict_sources checking logic --- lua/apidocs.lua | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/lua/apidocs.lua b/lua/apidocs.lua index 1679d3d..d7f9f2f 100644 --- a/lua/apidocs.lua +++ b/lua/apidocs.lua @@ -39,13 +39,16 @@ local function apidocs_open(opts) end if type == "directory" then if opts and opts.restrict_sources then - for _, source in ipairs(opts.restrict_sources) do - if vim.tbl_contains(opts.restrict_sources, name) then - table.insert(installed_docs, name) + if vim.tbl_contains(opts.restrict_sources, name) then + table.insert(installed_docs, name) + else -- no exact match, check for partial match -- e.g. "python" -> "python~3.12" - elseif name:match("^" .. source .. "~%d+(%.%d+)?$") then - table.insert(installed_docs, name) + for _, source in ipairs(opts.restrict_sources) do + if name:match("^" .. source .. "~%d+(%.%d+)?$") then + table.insert(installed_docs, name) + break + end end end else