nvim-ufo icon indicating copy to clipboard operation
nvim-ufo copied to clipboard

Support for applying `marker` method in parallel with ufo

Open msva opened this issue 2 years ago • 5 comments

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

msva avatar Oct 27 '22 20:10 msva

It's similar to https://github.com/kevinhwang91/nvim-ufo/issues/22.

Two steps:

  1. Need to write a marker provider, you can refer to indent.lua. TBH, it's not hard, just scan content and parse foldmarker, 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.
  2. Enable marker provider, choose one of the two ways:
    1. 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;
    2. Add a new option to enable ufo's marker mixed with main or fallback provider automatically if foldmethod == 'marker' , AFAIK, foldmethod == 'marker' is always used with modeline, this way is more elegant than first way IMO.

kevinhwang91 avatar Oct 27 '22 21:10 kevinhwang91

thanks

msva avatar Oct 27 '22 23:10 msva

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,
	},

danpf avatar Jan 03 '24 06:01 danpf

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,
	},
  1. No need to handle commentstring;
  2. Use string.find({s}, {pattern}, 0, true]]) to handle plain marker instead of regex;
  3. If use lsp provider, should use an async function. Promise.resolve(range):thenCall should be more robust.

kevinhwang91 avatar Jan 03 '24 10:01 kevinhwang91

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

LucasAVasco avatar May 13 '24 00:05 LucasAVasco