From fcacb831acac0ea12bcd2b9ab3e9397d5ab8fbe6 Mon Sep 17 00:00:00 2001 From: kevinhwang91 Date: Fri, 1 Jul 2022 21:01:29 +0800 Subject: [PATCH 1/3] fix: API is broken need refactor later --- lua/ufo.lua | 3 +++ lua/ufo/fold.lua | 11 +++++++++++ lua/ufo/fold/buffer.lua | 3 ++- lua/ufo/lib/event.lua | 2 ++ lua/ufo/main.lua | 6 +++++- 5 files changed, 23 insertions(+), 2 deletions(-) diff --git a/lua/ufo.lua b/lua/ufo.lua index c9f9c48..90a6aa1 100644 --- a/lua/ufo.lua +++ b/lua/ufo.lua @@ -1,3 +1,5 @@ +local api = vim.api + ---Export methods to the users, `require('ufo').method(...)` ---@class Ufo local M = {} @@ -33,6 +35,7 @@ end ---Inspect ufo information by bufnr ---@param bufnr? number current buffer default function M.inspect(bufnr) + bufnr = bufnr or api.nvim_get_current_buf() local msg = require('ufo.main').inspectBuf(bufnr) if not msg then vim.notify(('Buffer %d has not been attached.'):format(bufnr), vim.log.levels.ERROR) diff --git a/lua/ufo/fold.lua b/lua/ufo/fold.lua index 126e946..604eca3 100644 --- a/lua/ufo/fold.lua +++ b/lua/ufo/fold.lua @@ -115,6 +115,14 @@ function Fold.get(bufnr) return foldbuffer:get(bufnr) end +function Fold.attach(bufnr) + foldbuffer.detachedBufSet[bufnr] = nil +end + +function Fold.detach(bufnr) + foldbuffer.detachedBufSet[bufnr] = true +end + function Fold.setStatus(bufnr, status) local fb = foldbuffer:get(bufnr) local old = '' @@ -147,6 +155,9 @@ end)() local function attach(bufnr) bufnr = bufnr or api.nvim_get_current_buf() + if foldbuffer.detachedBufSet[bufnr] then + return + end log.debug('attach bufnr:', bufnr) local fb = foldbuffer:get(bufnr) if fb then diff --git a/lua/ufo/fold/buffer.lua b/lua/ufo/fold/buffer.lua index 4ad584a..b495f95 100644 --- a/lua/ufo/fold/buffer.lua +++ b/lua/ufo/fold/buffer.lua @@ -20,7 +20,8 @@ local FoldBuffer = { ns = nil, hlNs = nil, openFoldHlTimeout = 0, - pool = {} + pool = {}, + detachedBufSet = {} } ---@class UfoFoldedLine diff --git a/lua/ufo/lib/event.lua b/lua/ufo/lib/event.lua index 722c382..bc65db1 100644 --- a/lua/ufo/lib/event.lua +++ b/lua/ufo/lib/event.lua @@ -1,4 +1,5 @@ local disposable = require('ufo.lib.disposable') +local log = require('ufo.lib.log') ---@class UfoEvent local Event = { @@ -48,6 +49,7 @@ function Event:emit(name, ...) if not listeners then return end + log.trace('event:', name, 'listeners:', listeners, 'args:', ...) for _, listener in ipairs(listeners) do listener(...) end diff --git a/lua/ufo/main.lua b/lua/ufo/main.lua index dfc6c03..8be5b2c 100644 --- a/lua/ufo/main.lua +++ b/lua/ufo/main.lua @@ -10,6 +10,8 @@ local event = require('ufo.lib.event') local disposable = require('ufo.lib.disposable') local enabled + +---@type UfoDisposable[] local disposables = {} local function createEvents() @@ -71,7 +73,7 @@ function M.disable() end deleteCommand() for _, item in ipairs(disposables) do - item:disable() + item:dispose() end enabled = false return true @@ -97,6 +99,7 @@ end function M.attach(bufnr) bufnr = bufnr or api.nvim_get_current_buf() + fold.attach(bufnr) event:emit('BufEnter', bufnr) end @@ -106,6 +109,7 @@ function M.detach(bufnr) if fb then fb:dispose() end + fold.detach(bufnr) end function M.enableFold(bufnr) From a169a9b1c57314b4e110f1762e562d86b9c91ab7 Mon Sep 17 00:00:00 2001 From: kevinhwang91 Date: Fri, 1 Jul 2022 21:11:42 +0800 Subject: [PATCH 2/3] feat(provider): add treesitter module --- lua/ufo/provider.lua | 7 +- lua/ufo/provider/lsp/nvim.lua | 1 - lua/ufo/provider/treesitter.lua | 113 ++++++++++++++++++++++++++++++++ 3 files changed, 118 insertions(+), 3 deletions(-) create mode 100644 lua/ufo/provider/treesitter.lua diff --git a/lua/ufo/provider.lua b/lua/ufo/provider.lua index 83f278f..f4dfa92 100644 --- a/lua/ufo/provider.lua +++ b/lua/ufo/provider.lua @@ -34,10 +34,13 @@ end function Provider.requestFoldingRange(providers, bufnr) local main, fallback = providers[1], providers[2] local mainFunc = getFunction(main) - return promise.resolve(mainFunc(bufnr)):thenCall(function(value) + + return promise(function(resolve) + resolve(mainFunc(bufnr)) + end):thenCall(function(value) return {main, value} end, function(reason) - if reason == 'UfoFallbackException' then + if type(reason) == 'string' and reason:match('UfoFallbackException') then local fallbackFunc = getFunction(fallback) if fallbackFunc then return {fallback, fallbackFunc(bufnr)} diff --git a/lua/ufo/provider/lsp/nvim.lua b/lua/ufo/provider/lsp/nvim.lua index 9d6fa4e..dcf87cb 100644 --- a/lua/ufo/provider/lsp/nvim.lua +++ b/lua/ufo/provider/lsp/nvim.lua @@ -42,7 +42,6 @@ function NvimClient.requestFoldingRange(bufnr, kind) return end local bt = vim.bo[bufnr].bt - -- same behavior with coc.nvim if bt ~= '' and bt ~= 'acwrite' then return end diff --git a/lua/ufo/provider/treesitter.lua b/lua/ufo/provider/treesitter.lua new file mode 100644 index 0000000..4b27f8e --- /dev/null +++ b/lua/ufo/provider/treesitter.lua @@ -0,0 +1,113 @@ +local parsers = require('nvim-treesitter.parsers') +local query = require('nvim-treesitter.query') +local utils = require('ufo.utils') + +local Treesitter = {} + +local function prepare_query(bufnr, parser, queryName, root, rootLang) + if not root then + local firstTree = parser:trees()[1] + if firstTree then + root = firstTree:root() + else + return + end + end + + local range = {root:range()} + + if not rootLang then + local langTree = parser:language_for_range(range) + if langTree then + rootLang = langTree:lang() + else + return + end + end + + return query.get_query(rootLang, queryName), { + root = root, + source = bufnr, + start = range[1], + -- The end row is exclusive so we need to add 1 to it. + stop = range[3] + 1, + } +end + +local function iterFoldMatches(bufnr, parser, root, rootLang) + local q, p = prepare_query(bufnr, parser, 'folds', root, rootLang) + if not q then + return function() end + end + local iter = q:iter_matches(p.root, p.source, p.start, p.stop) + return function() + local pattern, match = iter() + local matches = {} + if pattern == nil then + return pattern + end + for id, node in ipairs(match) do + local name = q.captures[id] -- name of the capture in the query + if name then + table.insert(matches, node) + end + end + return matches + end +end + +local function getFoldMatches(res, bufnr, parser, root, lang) + for matches in iterFoldMatches(bufnr, parser, root, lang) do + for _, node in ipairs(matches) do + table.insert(res, node) + end + end + return res +end + +local function getCpatureMatchesRecursively(bufnr, parser) + local noQuery = true + local res = {} + parser:for_each_tree(function(tree, langTree) + local lang = langTree:lang() + if query.has_folds(lang) then + noQuery = false + getFoldMatches(res, bufnr, parser, tree:root(), lang) + end + end) + if noQuery then + error('UfoFallbackException') + end + return res +end + +function Treesitter.getFolds(bufnr) + local rt = ktime() + if not utils.isBufLoaded(bufnr) then + return + end + local bt = vim.bo[bufnr].bt + if bt ~= '' and bt ~= 'acwrite' then + return + end + local parser = parsers.get_parser(bufnr) + if not parser then + error('UfoFallbackException') + end + + local ranges = {} + local matches = getCpatureMatchesRecursively(bufnr, parser) + for _, node in ipairs(matches) do + local start, _, stop, stop_col = node:range() + if stop_col == 0 then + stop = stop - 1 + end + if stop > start then + table.insert(ranges, {startLine = start, endLine = stop}) + end + end + info(ktime() - rt) + return ranges +end + +return Treesitter From 1cf0e997658976e29bbcc0603cfd2c47c24663c0 Mon Sep 17 00:00:00 2001 From: kevinhwang91 Date: Fri, 1 Jul 2022 21:27:01 +0800 Subject: [PATCH 3/3] fix(provider): remove debug statement for treesitter --- lua/ufo/provider/treesitter.lua | 2 -- 1 file changed, 2 deletions(-) diff --git a/lua/ufo/provider/treesitter.lua b/lua/ufo/provider/treesitter.lua index 4b27f8e..4acd8f7 100644 --- a/lua/ufo/provider/treesitter.lua +++ b/lua/ufo/provider/treesitter.lua @@ -82,7 +82,6 @@ local function getCpatureMatchesRecursively(bufnr, parser) end function Treesitter.getFolds(bufnr) - local rt = ktime() if not utils.isBufLoaded(bufnr) then return end @@ -106,7 +105,6 @@ function Treesitter.getFolds(bufnr) table.insert(ranges, {startLine = start, endLine = stop}) end end - info(ktime() - rt) return ranges end