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
marker
provider, 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
marker
provider, 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
marker
mixed withmain
orfallback
provider 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
lsp
provider, should use an async function.Promise.resolve(range):thenCall
should 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