kevinhwang91 / nvim-ufo

Not UFO in the sky, but an ultra fold in Neovim.
BSD 3-Clause "New" or "Revised" License
2.18k stars 38 forks source link

How to get nvim-ufo working with lazy.nvim? #117

Closed poperigby closed 1 year ago

poperigby commented 1 year ago

I'd like to get nvim-ufo working with lazy.nvim.

This is what I currently have for my setup:

return {
    "kevinhwang91/nvim-ufo",
    dependencies = { "kevinhwang91/promise-async", "nvim-treesitter/nvim-treesitter" },
    keys = {
        {
            "<Space>h",
            function()
                -- Either preview fold, or LSP hover
                local winid = require("ufo").peekFoldedLinesUnderCursor()
                if not winid then
                    vim.lsp.buf.hover()
                end
            end,
            desc = "Preview fold or LSP hover"
        },
    },
    opt = {
        provider_selector = function(bufnr, filetype, buftype)
            return { "treesitter", "indent" }
        end,
    }
}

I also have these set:

vim.opt.foldcolumn = "0"
vim.opt.foldlevel = 99
vim.opt.foldlevelstart = 99
vim.opt.foldenable = true

This unfortunately doesn't work. I get this error when pressing zc:

E490: No fold found
kevinhwang91 commented 1 year ago

I don't use lazy.nvim.

poperigby commented 1 year ago

No worries. I thought someone else might know.

SirSilver commented 1 year ago
{
        "kevinhwang91/nvim-ufo",
        dependencies = { "kevinhwang91/promise-async" },
        event = "BufRead",
        keys = {
            { "zR", function() require("ufo").openAllFolds() end },
            { "zM", function() require("ufo").closeAllFolds() end },
            { "K", function()
                local winid = require('ufo').peekFoldedLinesUnderCursor()
                if not winid then
                    vim.lsp.buf.hover()
                end
            end }
        },
        config = function()
            vim.o.foldcolumn = '1'
            vim.o.foldlevel = 99
            vim.o.foldlevelstart = 99
            vim.o.foldenable = true
            require("ufo").setup({
                close_fold_kinds = { "imports" },
            })
        end,
}

This works for me, provider is LSP

poperigby commented 1 year ago

That works perfectly, thanks!

probavee commented 1 year ago

If you want more examples to infer how to customize yourself (here, to get the number of lines folded) :

return {
  "kevinhwang91/nvim-ufo",
  dependencies = { "kevinhwang91/promise-async" },
  event = "BufRead",
  keys = {
    { "zR", function() require("ufo").openAllFolds() end },
    { "zM", function() require("ufo").closeAllFolds() end },
    { "K", function()
      local winid = require('ufo').peekFoldedLinesUnderCursor()
      if not winid then
        vim.lsp.buf.hover()
      end
    end }
  },
  opts = function(_, opts)
    vim.o.foldcolumn = '1'
    vim.o.foldlevel = 99
    vim.o.foldlevelstart = 99
    vim.o.foldenable = true
    -- To show number of folded lines
    local handler = function(virtText, lnum, endLnum, width, truncate)
      local newVirtText = {}
      local suffix = ('  %d '):format(endLnum - lnum)
      local sufWidth = vim.fn.strdisplaywidth(suffix)
      local targetWidth = width - sufWidth
      local curWidth = 0
      for _, chunk in ipairs(virtText) do
        local chunkText = chunk[1]
        local chunkWidth = vim.fn.strdisplaywidth(chunkText)
        if targetWidth > curWidth + chunkWidth then
          table.insert(newVirtText, chunk)
        else
          chunkText = truncate(chunkText, targetWidth - curWidth)
          local hlGroup = chunk[2]
          table.insert(newVirtText, { chunkText, hlGroup })
          chunkWidth = vim.fn.strdisplaywidth(chunkText)
          -- str width returned from truncate() may less than 2nd argument, need padding
          if curWidth + chunkWidth < targetWidth then
            suffix = suffix .. (' '):rep(targetWidth - curWidth - chunkWidth)
          end
          break
        end
        curWidth = curWidth + chunkWidth
      end
      table.insert(newVirtText, { suffix, 'MoreMsg' })
      return newVirtText
    end
    opts.fold_virt_text_handler = handler
  end
}
poperigby commented 1 year ago

Thanks!

suciptoid commented 1 year ago

If you use neovim 0.9 you can use this

return {
  {
    "kevinhwang91/nvim-ufo",
    event = "BufRead",
    dependencies = {
      { "kevinhwang91/promise-async" },
      {
        "luukvbaal/statuscol.nvim",
        config = function()
          local builtin = require("statuscol.builtin")
          require("statuscol").setup({
            -- foldfunc = "builtin",
            -- setopt = true,
            relculright = true,
            segments = {
              { text = { builtin.foldfunc }, click = "v:lua.ScFa" },
              { text = { "%s" }, click = "v:lua.ScSa" },
              { text = { builtin.lnumfunc, " " }, click = "v:lua.ScLa" },
            },
          })
        end,
      },
    },
    config = function()
      -- Fold options
      vim.o.fillchars = [[eob: ,fold: ,foldopen:,foldsep: ,foldclose:]]
      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

      require("ufo").setup()
    end,
  },
}

Result:

Screenshot 2023-03-12 at 14 22 19

Also need to override lsp-config if you want use lsp provider

add:

       -- Add folding capabilities required by ufo.nvim
      capabilities.textDocument.foldingRange = {
        dynamicRegistration = false,
        lineFoldingOnly = true,
      }

full spec

  {
    "neovim/nvim-lspconfig",
    config = function(_, opts)
      -- setup autoformat
      require("lazyvim.plugins.lsp.format").autoformat = opts.autoformat
      -- setup formatting and keymaps
      require("lazyvim.util").on_attach(function(client, buffer)
        require("lazyvim.plugins.lsp.format").on_attach(client, buffer)
        require("lazyvim.plugins.lsp.keymaps").on_attach(client, buffer)
      end)

      -- diagnostics
      for name, icon in pairs(require("lazyvim.config").icons.diagnostics) do
        name = "DiagnosticSign" .. name
        vim.fn.sign_define(name, { text = icon, texthl = name, numhl = "" })
      end
      vim.diagnostic.config(opts.diagnostics)

      local servers = opts.servers
      local capabilities = require("cmp_nvim_lsp").default_capabilities(vim.lsp.protocol.make_client_capabilities())
      -- Add folding capabilities required by ufo.nvim
      capabilities.textDocument.foldingRange = {
        dynamicRegistration = false,
        lineFoldingOnly = true,
      }

      local function setup(server)
        local server_opts = vim.tbl_deep_extend("force", {
          capabilities = vim.deepcopy(capabilities),
        }, servers[server] or {})

        if opts.setup[server] then
          if opts.setup[server](server, server_opts) then
            return
          end
        elseif opts.setup["*"] then
          if opts.setup["*"](server, server_opts) then
            return
          end
        end
        require("lspconfig")[server].setup(server_opts)
      end

      -- temp fix for lspconfig rename
      -- https://github.com/neovim/nvim-lspconfig/pull/2439
      local mappings = require("mason-lspconfig.mappings.server")
      if not mappings.lspconfig_to_package.lua_ls then
        mappings.lspconfig_to_package.lua_ls = "lua-language-server"
        mappings.package_to_lspconfig["lua-language-server"] = "lua_ls"
      end

      local mlsp = require("mason-lspconfig")
      local available = mlsp.get_available_servers()

      local ensure_installed = {} ---@type string[]
      for server, server_opts in pairs(servers) do
        if server_opts then
          server_opts = server_opts == true and {} or server_opts
          -- run manual setup if mason=false or if this is a server that cannot be installed with mason-lspconfig
          if server_opts.mason == false or not vim.tbl_contains(available, server) then
            setup(server)
          else
            ensure_installed[#ensure_installed + 1] = server
          end
        end
      end

      require("mason-lspconfig").setup({ ensure_installed = ensure_installed })
      require("mason-lspconfig").setup_handlers({ setup })
    end,
  },
SamPosh commented 1 year ago

@kevinhwang91 can you add this step also in your read me. Now a days people are using lazy vim plug-in manager . This will be helpful for many. I copied from here and it works.

kevinhwang91 commented 1 year ago

@kevinhwang91 can you add this step also in your read me. Now a days people are using lazy vim plug-in manager . This will be helpful for many. I copied from here and it works.

I hope the user can get rid of the complicated settings for ufo. However, I don't use lazy.nvim and I am not sure whether it works under a simple setting. If anyone can make ufo work under a simple code snippet with lazy.nvim. PR is welcome.

doctorfree commented 1 year ago

This is a minimal lazy.nvim configuration that works for me:

  {
    "kevinhwang91/nvim-ufo",
    event = "BufRead",
    dependencies = { "kevinhwang91/promise-async" },
    config = function()
      require("ufo").setup({})
    end,
  }

Adding keymaps, LSP provider support, status column enhancements, all are not necessary but easy to implement.

jdsutherland commented 11 months ago

@doctorfree if you're just calling setup() in config, you can just do config = true and lazy will call it for you

Zhou-Yicheng commented 11 months ago

opt = {
should be
opts = {
Rolv-Apneseth commented 11 months ago

Thanks @ColleterVi I didn't know opts could accept a function like that. I was having trouble getting the custom fold text to work but that did it (maybe setup needs to be called once before or something but just setting opts to a table was not working for whatever reason).

@kevinhwang91 while @doctorfree provided a minimal working example, I think at least the fold options should be included:

{
    "kevinhwang91/nvim-ufo",
    event = "BufRead",
    dependencies = { "kevinhwang91/promise-async" },
    config = function()
        vim.o.foldcolumn = "1"
        vim.o.foldlevel = 99
        vim.o.foldlevelstart = 99
        vim.o.foldenable = true

        require("ufo").setup()
    end,
}
And in case it helps anyone else, this is what I have working for mine (the full `ufo.lua` file) ```lua local ftMap = { vim = "indent", python = { "indent" }, git = "", } local function custom_selector_handler(bufnr) local function handleFallbackException(err, providerName) if type(err) == "string" and err:match("UfoFallbackException") then return require("ufo").getFolds(bufnr, providerName) else return require("promise").reject(err) end end return require("ufo") .getFolds(bufnr, "lsp") :catch(function(err) return handleFallbackException(err, "treesitter") end) :catch(function(err) return handleFallbackException(err, "indent") end) end local function custom_virt_text_handler(virtText, lnum, endLnum, width, truncate) local newVirtText = {} local suffix = ("  %d"):format(endLnum - lnum) local sufWidth = vim.fn.strdisplaywidth(suffix) local targetWidth = width - sufWidth local curWidth = 0 for _, chunk in ipairs(virtText) do local chunkText = chunk[1] local chunkWidth = vim.fn.strdisplaywidth(chunkText) if targetWidth > curWidth + chunkWidth then table.insert(newVirtText, chunk) else chunkText = truncate(chunkText, targetWidth - curWidth) local hlGroup = chunk[2] table.insert(newVirtText, { chunkText, hlGroup }) chunkWidth = vim.fn.strdisplaywidth(chunkText) if curWidth + chunkWidth < targetWidth then suffix = suffix .. (" "):rep(targetWidth - curWidth - chunkWidth) end break end curWidth = curWidth + chunkWidth end table.insert(newVirtText, { suffix, "Comment" }) return newVirtText end return { "kevinhwang91/nvim-ufo", event = "BufRead", dependencies = { "kevinhwang91/promise-async", }, opts = function(_, opts) vim.o.foldcolumn = "0" vim.o.foldlevel = 99 vim.o.foldlevelstart = 99 vim.o.foldenable = true opts.preview = { win_config = { border = "rounded", winhighlight = "Normal:Folded", winblend = 0, }, mappings = { scrollU = "", scrollD = "", jumpTop = "[", jumpBot = "]", }, } -- Custom fold virtual text, which shows number of folded lines opts.fold_virt_text_handler = custom_virt_text_handler -- Custom provider selector which should go lsp->treesitter->indent opts.provider_selector = function(_, filetype, _) return ftMap[filetype] or custom_selector_handler end end, keys = { { "zR", function() require("ufo").openAllFolds() end, desc = "Open all folds", }, { "zM", function() require("ufo").closeAllFolds() end, desc = "Close all folds", }, { "K", function() local winid = require("ufo").peekFoldedLinesUnderCursor() if not winid then vim.lsp.buf.hover() end end, }, }, } ```