mirror of
https://github.com/kevinhwang91/nvim-ufo
synced 2024-09-16 14:14:02 +02:00
refactor(treesitter): remove nvim-treesitter dependencies (#189)
Replace all the use of nvim-treesitter APIs with core vim.treesitter APIs. No more nvim-treesitter dependency, just core neovim is enough. This fix makes ufo work without the nvim-treesitter plugin as an additional dependency. In fact, nvim-treesitter v1.0 deprecates and removes some APIs that have been migrated to the core neovim APIs `vim.treesitter`, which makes ufo's previous treesitter provider implementation incompatible. Note that this commit does not change the minimum neovim version requirement, should work fine with neovim 0.7.x. Implementation note: There are four APIs that need to be migrated: - `nvim-treesitter.parsers.get_parser()`: The difference to the core API `vim.treesitter.get_parser()` is whether to throw errors when a parser is not available (`has_parser`). We simply mimic the previous behavior by catching errors. - `nvim-treesitter.query.get_query()`: The difference to core API `vim.treesitter.query.get()` is whether the query file is cached or not. This may have a small performance impact; in neovim 0.10.x, this function is memoized and thus very fast, but in neovim <= 0.9.x it might be slightly slow due to the lack of cache. Note: One can consider as well automatically falling back to the old nvim-treesitter (v0.9.x) if available, for neovim < 0.10. - `nvim-treesitter.query.has_folds()` (i.e., `has_query_files()`): can be easily replaced with `vim.treesitter.query.get_files`. Also there might be a subtle performance difference of whether cache is being used (in the old nvim-treesitter implementations) or not. - `nvim-treesitter.tsrange`: The `TSRange` API has gone. Note that this is used only to implement the `#make-range!` directive; it suffices to have `node:range()` only for where it's used. Therefore, `TSRange.from_nodes()` is the only API we'll need, which can be easily backported into the existing `MetaNode` implementation.
This commit is contained in:
parent
dd83ca6f8f
commit
c1e8102e4e
2 changed files with 44 additions and 10 deletions
|
@ -44,7 +44,6 @@ The goal of nvim-ufo is to make Neovim's fold look modern and keep high performa
|
|||
|
||||
- [Neovim](https://github.com/neovim/neovim) 0.7.2 or later
|
||||
- [coc.nvim](https://github.com/neoclide/coc.nvim) (optional)
|
||||
- [nvim-treesitter](https://github.com/nvim-treesitter/nvim-treesitter) (optional)
|
||||
|
||||
### Installation
|
||||
|
||||
|
@ -92,9 +91,9 @@ require('ufo').setup()
|
|||
--
|
||||
|
||||
-- Option 3: treesitter as a main provider instead
|
||||
-- Only depend on `nvim-treesitter/queries/filetype/folds.scm`,
|
||||
-- (Note: the `nvim-treesitter` plugin is *not* needed.)
|
||||
-- ufo uses the same query files for folding (queries/<lang>/folds.scm)
|
||||
-- performance and stability are better than `foldmethod=nvim_treesitter#foldexpr()`
|
||||
use {'nvim-treesitter/nvim-treesitter', run = ':TSUpdate'}
|
||||
require('ufo').setup({
|
||||
provider_selector = function(bufnr, filetype, buftype)
|
||||
return {'treesitter', 'indent'}
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
local parsers = require('nvim-treesitter.parsers')
|
||||
local query = require('nvim-treesitter.query')
|
||||
local tsrange = require('nvim-treesitter.tsrange')
|
||||
local bufmanager = require('ufo.bufmanager')
|
||||
local foldingrange = require('ufo.model.foldingrange')
|
||||
|
||||
|
@ -10,6 +7,26 @@ local Treesitter = {
|
|||
hasProviders = {}
|
||||
}
|
||||
|
||||
---@diagnostic disable: deprecated
|
||||
---@return vim.treesitter.LanguageTree|nil parser for the buffer, or nil if parser is not available
|
||||
local function getParser(bufnr, lang)
|
||||
local ok, parser = pcall(vim.treesitter.get_parser, bufnr, lang)
|
||||
if not ok then
|
||||
return nil
|
||||
end
|
||||
return parser
|
||||
end
|
||||
local get_query = assert(vim.treesitter.query.get or vim.treesitter.query.get_query)
|
||||
local get_query_files = assert(vim.treesitter.query.get_files or vim.treesitter.query.get_query_files)
|
||||
---@diagnostic enable: deprecated
|
||||
|
||||
|
||||
-- Backward compatibility for the dummy directive (#make-range!),
|
||||
-- which no longer exists in nvim-treesitter v1.0+
|
||||
if not vim.tbl_contains(vim.treesitter.query.list_directives(), "make-range!") then
|
||||
vim.treesitter.query.add_directive("make-range!", function() end, {})
|
||||
end
|
||||
|
||||
local MetaNode = {}
|
||||
MetaNode.__index = MetaNode
|
||||
|
||||
|
@ -24,6 +41,19 @@ function MetaNode:range()
|
|||
return range[1], range[2], range[3], range[4]
|
||||
end
|
||||
|
||||
--- Return a meta node that represents a range between two nodes, i.e., (#make-range!),
|
||||
--- that is similar to the legacy TSRange.from_node() from nvim-treesitter.
|
||||
function MetaNode.from_nodes(start_node, end_node)
|
||||
local start_pos = { start_node:start() }
|
||||
local end_pos = { end_node:end_() }
|
||||
return MetaNode:new({
|
||||
[1] = start_pos[1],
|
||||
[2] = start_pos[2],
|
||||
[3] = end_pos[1],
|
||||
[4] = end_pos[2],
|
||||
})
|
||||
end
|
||||
|
||||
local function prepareQuery(bufnr, parser, root, rootLang, queryName)
|
||||
if not root then
|
||||
local firstTree = parser:trees()[1]
|
||||
|
@ -45,7 +75,7 @@ local function prepareQuery(bufnr, parser, root, rootLang, queryName)
|
|||
end
|
||||
end
|
||||
|
||||
return query.get_query(rootLang, queryName), {
|
||||
return get_query(rootLang, queryName), {
|
||||
root = root,
|
||||
source = bufnr,
|
||||
start = range[1],
|
||||
|
@ -67,6 +97,8 @@ local function iterFoldMatches(bufnr, parser, root, rootLang)
|
|||
if pattern == nil then
|
||||
return pattern
|
||||
end
|
||||
|
||||
-- Extract capture names from each match
|
||||
for id, node in pairs(match) do
|
||||
local m = metadata[id]
|
||||
if m and m.range then
|
||||
|
@ -74,11 +106,13 @@ local function iterFoldMatches(bufnr, parser, root, rootLang)
|
|||
end
|
||||
table.insert(matches, node)
|
||||
end
|
||||
|
||||
-- Add some predicates for testing
|
||||
local preds = q.info.patterns[pattern]
|
||||
if preds then
|
||||
for _, pred in pairs(preds) do
|
||||
if pred[1] == 'make-range!' and type(pred[2]) == 'string' and #pred == 4 then
|
||||
local node = tsrange.TSRange.from_nodes(bufnr, match[pred[3]], match[pred[4]])
|
||||
local node = MetaNode.from_nodes(match[pred[3]], match[pred[4]])
|
||||
table.insert(matches, node)
|
||||
end
|
||||
end
|
||||
|
@ -101,7 +135,8 @@ local function getCpatureMatchesRecursively(bufnr, parser)
|
|||
local res = {}
|
||||
parser:for_each_tree(function(tree, langTree)
|
||||
local lang = langTree:lang()
|
||||
if query.has_folds(lang) then
|
||||
local has_folds = #get_query_files(lang, 'folds', nil) > 0
|
||||
if has_folds then
|
||||
noQuery = false
|
||||
getFoldMatches(res, bufnr, parser, tree:root(), lang)
|
||||
end
|
||||
|
@ -126,7 +161,7 @@ function Treesitter.getFolds(bufnr)
|
|||
if self.hasProviders[ft] == false then
|
||||
error('UfoFallbackException')
|
||||
end
|
||||
local parser = parsers.get_parser(bufnr)
|
||||
local parser = getParser(bufnr)
|
||||
if not parser then
|
||||
self.hasProviders[ft] = false
|
||||
error('UfoFallbackException')
|
||||
|
|
Loading…
Reference in a new issue