Skip to content

Commit 78a126c

Browse files
committed
fix(treesitter): standalone implementation
Drop hard nvim-treesitter requirement, but assumes parser and `locals` query (using the captures documented here: https://github.com/nvim-treesitter/nvim-treesitter/blob/main/CONTRIBUTING.md#locals) is available on `runtimepath`. Locals module is extracted from https://github.com/nvim-treesitter/nvim-treesitter/blob/53dccb3a77da54a4e428275e8b44dbff77e0d47d/lua/nvim-treesitter/locals.lua (which has since been removed because it is no longer used).
1 parent d143e80 commit 78a126c

File tree

4 files changed

+132
-33
lines changed

4 files changed

+132
-33
lines changed

README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,6 @@ wiki.
6868
### Optional dependencies
6969

7070
- [sharkdp/fd](https://github.com/sharkdp/fd) (finder)
71-
- [nvim-treesitter/nvim-treesitter](https://github.com/nvim-treesitter/nvim-treesitter) (finder/preview)
7271
- [neovim LSP](https://neovim.io/doc/user/lsp.html) (picker)
7372
- [devicons](https://github.com/nvim-tree/nvim-web-devicons) (icons)
7473

lua/telescope/builtin/__files.lua

Lines changed: 11 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -397,50 +397,34 @@ files.find_files = function(opts)
397397
:find()
398398
end
399399

400-
local function prepare_match(entry, kind)
401-
local entries = {}
402-
403-
if entry.node then
404-
table.insert(entries, entry)
405-
else
406-
for name, item in pairs(entry) do
407-
vim.list_extend(entries, prepare_match(item, name))
408-
end
409-
end
410-
411-
return entries
412-
end
413-
414400
-- TODO: finish docs for opts.show_line
415401
files.treesitter = function(opts)
416402
opts.show_line = vim.F.if_nil(opts.show_line, true)
403+
local ft = vim.bo[opts.bufnr].filetype
404+
local lang = vim.treesitter.language.get_lang(ft)
417405

418-
local has_nvim_treesitter, _ = pcall(require, "nvim-treesitter")
419-
if not has_nvim_treesitter then
406+
if not (lang and vim.treesitter.language.add(lang)) then
420407
utils.notify("builtin.treesitter", {
421-
msg = "This picker requires nvim-treesitter",
408+
msg = "No parser for the current buffer",
422409
level = "ERROR",
423410
})
424411
return
425412
end
426413

427-
local parsers = require "nvim-treesitter.parsers"
428-
if not parsers.has_parser(parsers.get_buf_lang(opts.bufnr)) then
414+
if not (lang and vim.treesitter.query.get(lang, "locals")) then
429415
utils.notify("builtin.treesitter", {
430-
msg = "No parser for the current buffer",
416+
msg = "No locals query for the current buffer",
431417
level = "ERROR",
432418
})
433419
return
434420
end
435421

436-
local ts_locals = require "nvim-treesitter.locals"
422+
local definitions, _, _ = require("telescope.ts_locals").get(opts.bufnr)
437423
local results = {}
438-
for _, definition in ipairs(ts_locals.get_definitions(opts.bufnr)) do
439-
local entries = prepare_match(ts_locals.get_local_nodes(definition))
440-
for _, entry in ipairs(entries) do
441-
entry.kind = vim.F.if_nil(entry.kind, "")
442-
table.insert(results, entry)
443-
end
424+
for _, definition in ipairs(definitions) do
425+
local kind = definition.kind:gsub("local%.definition%.", "")
426+
definition.kind = vim.F.if_nil(kind, "")
427+
table.insert(results, definition)
444428
end
445429

446430
results = utils.filter_symbols(results, opts)

lua/telescope/health.lua

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,6 @@ local optional_dependencies = {
3030

3131
local required_plugins = {
3232
{ lib = "plenary", optional = false },
33-
{
34-
lib = "nvim-treesitter",
35-
optional = true,
36-
info = "(Required for `:Telescope treesitter`.)",
37-
},
3833
}
3934

4035
local check_binary_installed = function(package)

lua/telescope/ts_locals.lua

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
local ts = vim.treesitter
2+
3+
local M = {}
4+
5+
---Memoize a function using hash_fn to hash the arguments.
6+
---@generic F: function
7+
---@param fn F
8+
---@param hash_fn fun(...): any
9+
---@return F
10+
local function memoize(fn, hash_fn)
11+
local cache = setmetatable({}, { __mode = "kv" }) ---@type table<any,any[]>
12+
13+
return function(...)
14+
local key = hash_fn(...)
15+
if cache[key] == nil then
16+
local v = { fn(...) } ---@type any[]
17+
18+
for k, value in pairs(v) do
19+
if value == nil then
20+
value[k] = vim.NIL ---@type table
21+
end
22+
end
23+
24+
cache[key] = v
25+
end
26+
27+
local v = cache[key]
28+
29+
for k, value in ipairs(v) do
30+
if value == vim.NIL then
31+
value[k] = nil ---@type table
32+
end
33+
end
34+
35+
return unpack(v)
36+
end
37+
end
38+
---@param bufnr integer: the buffer
39+
---@return TSNode|nil root: root node of the buffer
40+
local function get_root(bufnr)
41+
local parser = ts.get_parser(bufnr)
42+
if not parser then
43+
return
44+
end
45+
parser:parse()
46+
return parser:trees()[1]:root()
47+
end
48+
49+
---@param bufnr integer: the buffer
50+
---@return vim.treesitter.Query|nil query: `locals` query
51+
---@return TSNode|nil root: root node of the bufferocal function get_query(bufnr)
52+
local function get_query(bufnr)
53+
local root = get_root(bufnr)
54+
55+
local ft = vim.bo[bufnr].filetype
56+
local lang = ts.language.get_lang(ft) or ft
57+
58+
local query = (ts.query.get(lang, "locals"))
59+
60+
return query, root
61+
end
62+
63+
---@alias TSScope "parent"|"local"|"global"
64+
65+
---@class TSLocal
66+
---@field kind string
67+
---@field node TSNode
68+
---@field scope TSScope
69+
70+
-- Return all locals for the buffer
71+
--
72+
-- memoized by buffer tick
73+
--
74+
---@param bufnr integer buffer
75+
---@return TSLocal[] definitions
76+
---@return TSLocal[] references
77+
---@return TSNode[] scopes
78+
M.get = memoize(function(bufnr)
79+
local query, root = get_query(bufnr)
80+
if not query or not root then
81+
return {}, {}, {}
82+
end
83+
84+
local definitions = {}
85+
local scopes = {}
86+
local references = {}
87+
for id, node, metadata in query:iter_captures(root, bufnr) do
88+
local kind = query.captures[id]
89+
90+
local scope = "local" ---@type string
91+
for k, v in
92+
pairs(metadata --[[@as {[integer|string]: string}]])
93+
do
94+
if type(k) == "string" and vim.endswith(k, "local.scope") then
95+
scope = v
96+
end
97+
end
98+
99+
if node and vim.startswith(kind, "local.definition") then
100+
table.insert(definitions, { kind = kind, node = node, scope = scope })
101+
end
102+
103+
if node and kind == "local.scope" then
104+
table.insert(scopes, node)
105+
end
106+
107+
if node and kind == "local.reference" then
108+
table.insert(references, { kind = kind, node = node, scope = scope })
109+
end
110+
end
111+
112+
return definitions, references, scopes
113+
end, function(bufnr)
114+
local root = get_root(bufnr)
115+
if not root then
116+
return tostring(bufnr)
117+
end
118+
return tostring(root:id())
119+
end)
120+
121+
return M

0 commit comments

Comments
 (0)