nvim-ufo
nvim-ufo copied to clipboard
Support for applying `marker` method in parallel with ufo
Feature description
Hi there! Is it somehow possible to implement support to apply methods, that was selected in ufo (ts/lsp) simultaneously with marker (to support vim's default {{{/}}} at the same time)?
Describe the solution you'd like
I'm not sure, how is it supposed to look like if taking {n,}vim's internal marker method, but, probably, it can be made by writing custom function, that implements same behaviour :shrug:
I'll try to invent something myself, but can't promise anything, and would be glad to get some help/code snippets for that.
Or, if that would "internal" functionality of ufo :grinning:
Thanks in advance!
Additional context
No response
It's similar to https://github.com/kevinhwang91/nvim-ufo/issues/22.
Two steps:
- Need to write a
markerprovider, you can refer to indent.lua. TBH, it's not hard, just scan content and parsefoldmarker, use a stack data struct should be easy enough, I am not very Interested in this feature, so I leave this feature for the someone like you, you needn't to concern the performance for the time being. If there're perf issues I will solve them later. - Enable
markerprovider, choose one of the two ways:- Register a new provider to mix multiple providers, refer to https://github.com/kevinhwang91/nvim-ufo/blob/9d59d713c484d90790f53045a5b4685fb0686ff6/doc/example.lua#L26-L57 , this way is very flexible;
- Add a new option to enable ufo's
markermixed withmainorfallbackprovider automatically iffoldmethod == 'marker', AFAIK,foldmethod == 'marker'is always used withmodeline, this way is more elegant than first way IMO.
thanks
This was my solution to this problem. it is not perfect, but it works well enough for my needs.
{
"kevinhwang91/nvim-ufo",
event = { "BufRead" },
dependencies = "kevinhwang91/promise-async",
config = function()
vim.o.foldcolumn = "1" -- '0' is not bad
vim.o.foldlevel = 99 -- Using ufo provider need a large value, feel free to decrease the value
vim.o.foldlevelstart = 99
vim.o.foldenable = true
-- Using ufo provider need remap `zR` and `zM`. If Neovim is 0.6.1, remap yourself
vim.keymap.set("n", "zR", require("ufo").openAllFolds)
vim.keymap.set("n", "zM", require("ufo").closeAllFolds)
vim.keymap.set("n", "zK", function()
local winid = require("ufo").peekFoldedLinesUnderCursor()
if not winid then
vim.lsp.buf.hover()
end
end, { desc = "Peek Fold" })
local ftMap = {
git = "",
}
local foldingrange = require("ufo.model.foldingrange")
local bufmanager = require("ufo.bufmanager")
local CustomMarkerProvider = {}
function CustomMarkerProvider.getFolds(bufnr)
local buf = bufmanager:get(bufnr)
if not buf then
return
end
local fmrOpen = vim.opt.foldmarker:get()[1]
local fmrClose = vim.opt.foldmarker:get()[2]
local commentstring = vim.opt.commentstring:get()
if commentstring == "/*%s*/" then
-- Hack for c++ and other // and /* */ langs
commentstring = "//%s"
end
local openRegx = commentstring .. "*.-" .. fmrOpen
local closeRegx = commentstring .. "*.-" .. fmrClose
local summaryRegx = openRegx .. "%s*(.*)"
local lines = buf:lines(1, -1)
local ranges = {}
local stack = {}
for lnum, line in ipairs(lines) do
-- Check for start marker
if line:match(openRegx) then
table.insert(stack, lnum)
-- Check for end marker
elseif line:match(closeRegx) then
local startLnum = table.remove(stack)
if startLnum then
local summary = lines[startLnum]:match(summaryRegx)
table.insert(ranges, foldingrange.new(startLnum - 1, lnum - 1, summary))
end
end
end
return ranges
end
local function customizeSelector(bufnr)
local ranges = CustomMarkerProvider.getFolds(bufnr)
local maybe_additional_ranges = require("ufo").getFolds(bufnr, "treesitter")
if next(maybe_additional_ranges) ~= nil then
ranges = vim.list_extend(ranges, maybe_additional_ranges)
else
ranges = vim.list_extend(ranges, require("ufo").getFolds(bufnr, "indent"))
end
return ranges
end
require("ufo").setup({
provider_selector = function(bufnr, filetype, buftype)
return ftMap[filetype] or customizeSelector
end,
})
end,
},
This was my solution to this problem. it is not perfect, but it works well enough for my needs.
{ "kevinhwang91/nvim-ufo", event = { "BufRead" }, dependencies = "kevinhwang91/promise-async", config = function() vim.o.foldcolumn = "1" -- '0' is not bad vim.o.foldlevel = 99 -- Using ufo provider need a large value, feel free to decrease the value vim.o.foldlevelstart = 99 vim.o.foldenable = true -- Using ufo provider need remap `zR` and `zM`. If Neovim is 0.6.1, remap yourself vim.keymap.set("n", "zR", require("ufo").openAllFolds) vim.keymap.set("n", "zM", require("ufo").closeAllFolds) vim.keymap.set("n", "zK", function() local winid = require("ufo").peekFoldedLinesUnderCursor() if not winid then vim.lsp.buf.hover() end end, { desc = "Peek Fold" }) local ftMap = { git = "", } local foldingrange = require("ufo.model.foldingrange") local bufmanager = require("ufo.bufmanager") local CustomMarkerProvider = {} function CustomMarkerProvider.getFolds(bufnr) local buf = bufmanager:get(bufnr) if not buf then return end local fmrOpen = vim.opt.foldmarker:get()[1] local fmrClose = vim.opt.foldmarker:get()[2] local commentstring = vim.opt.commentstring:get() if commentstring == "/*%s*/" then -- Hack for c++ and other // and /* */ langs commentstring = "//%s" end local openRegx = commentstring .. "*.-" .. fmrOpen local closeRegx = commentstring .. "*.-" .. fmrClose local summaryRegx = openRegx .. "%s*(.*)" local lines = buf:lines(1, -1) local ranges = {} local stack = {} for lnum, line in ipairs(lines) do -- Check for start marker if line:match(openRegx) then table.insert(stack, lnum) -- Check for end marker elseif line:match(closeRegx) then local startLnum = table.remove(stack) if startLnum then local summary = lines[startLnum]:match(summaryRegx) table.insert(ranges, foldingrange.new(startLnum - 1, lnum - 1, summary)) end end end return ranges end local function customizeSelector(bufnr) local ranges = CustomMarkerProvider.getFolds(bufnr) local maybe_additional_ranges = require("ufo").getFolds(bufnr, "treesitter") if next(maybe_additional_ranges) ~= nil then ranges = vim.list_extend(ranges, maybe_additional_ranges) else ranges = vim.list_extend(ranges, require("ufo").getFolds(bufnr, "indent")) end return ranges end require("ufo").setup({ provider_selector = function(bufnr, filetype, buftype) return ftMap[filetype] or customizeSelector end, }) end, },
- No need to handle
commentstring; - Use
string.find({s}, {pattern}, 0, true]])to handle plain marker instead of regex; - If use
lspprovider, should use an async function.Promise.resolve(range):thenCallshould be more robust.
I implemented the marker provider in a fork: https://github.com/LucasAVasco/nvim-ufo/tree/main
Pull request: https://github.com/kevinhwang91/nvim-ufo/pull/218
I also implemented a function to merge UFO providers (also can merge functions that returns Foldings): https://github.com/LucasAVasco/nvim-ufo/tree/featMergeProviders
Pull request: https://github.com/kevinhwang91/nvim-ufo/pull/219
If you want to use both (and my pull requests have not yet been accepted), you can use this branch: https://github.com/LucasAVasco/nvim-ufo/tree/markerAndMergeProviders