nvim-ufo

repository·main·Indexed 25 days ago

https://github.com/kevinhwang91/nvim-ufo

A Neovim plugin providing high-performance folding visuals using LSP (Folding Range) or Treesitter. It features customizable fold virtual text, a fold preview window, and API functions to manage fold states, such as openAllFolds and closeAllFolds, without blocking the editor.

Tokens
3.8K
Snippets
10
Records
16
Agent score
34%

What's inside nvim-ufo

  1. Configure nvim-ufo with Neovim LSP

    main

    When using Neovim's built-in LSP, you must manually add the foldingRange capability to your client capabilities, as Neovim does not include it by default. Then, call require('ufo').setup().

    -- Tell the server the capability of foldingRange
    -- Neovim hasn't added foldingRange to default capabilities, users must add it manually
    local capabilities = vim.lsp.protocol.make_client_capabilities()
    capabilities.textDocument.foldingRange = {
        dynamicRegistration = false,
        lineFoldingOnly = true
    }
    local language_servers = vim.lsp.get_clients() -- or list servers manually like {'gopls', 'clangd'}
    for _, ls in ipairs(language_servers) do
        require('lspconfig')[ls].setup({
            capabilities = capabilities
            -- you can add other fields for setting up lsp server in this table
        })
    end
    require('ufo').setup()
  2. Configure nvim-ufo with Treesitter provider

    main

    You can use treesitter as the primary fold provider instead of LSP. This uses the same query files as nvim-treesitter but is managed by ufo for better performance and stability. Note that the nvim-treesitter plugin itself is not strictly required.

    require('ufo').setup({
        provider_selector = function(bufnr, filetype, buftype)
            return {'treesitter', 'indent'}
        end
    })
  3. Configure nvim-ufo minimal settings

    main

    To use nvim-ufo effectively, you must set high foldlevel and foldlevelstart values to prevent folds from closing automatically when ranges update. You should also remap zR and zM to use the ufo API instead of standard Neovim commands to maintain the foldlevel value.

    use {'kevinhwang91/nvim-ufo', requires = 'kevinhwang91/promise-async'}
    
    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)
  4. Configure nvim-ufo via setup()

    main

    Use require('ufo').setup() to customize plugin behavior. Key configuration options include:

    • open_fold_hl_timeout: Timeout for fold highlight.
    • close_fold_kinds_for_ft: Define which fold kinds (e.g., 'imports', 'comment', 'array') should be closed by default for specific filetypes.
    • close_fold_current_line_for_ft: Boolean or table to determine if the current line should be closed for specific filetypes.
    • preview: Configuration for the fold preview window, including win_config (borders, highlights, blend) and mappings (scroll and jump keys).
    • provider_selector: A function to select the fold provider (e.g., 'lsp', 'indent', or 'treesitter') based on bufnr, filetype, and buftype.
    • fold_virt_text_handler: A function to customize how folded text is displayed.

    To use a custom provider selection logic, implement a function that returns the desired provider string or table.

    local ftMap = {
        vim = 'indent',
        python = {'indent'},
        git = ''
    }
    require('ufo').setup({
        open_fold_hl_timeout = 150,
        close_fold_kinds_for_ft = {
            default = {'imports', 'comment'},
            json = {'array'},
            c = {'comment', 'region'}
        },
        close_fold_current_line_for_ft = {
            default = true,
            c = false
        },
        preview = {
            win_config = {
                border = {'', '─', '', '', '', '─', '', ''},
                winhighlight = 'Normal:Folded',
                winblend = 0
            },
            mappings = {
                scrollU = '<C-u>',
                scrollD = '<C-d>',
                jumpTop = '[',
                jumpBot = ']'
            }
        },
        provider_selector = function(bufnr, filetype, buftype)
            return ftMap[filetype]
        end
    })
  5. Configure nvim-ufo setup options

    main

    The require('ufo').setup() function accepts a configuration table. Key options include:

    • open_fold_hl_timeout: Time in ms between highlighting and clearing the range when opening a folded line (default: 400).
    • provider_selector: A function to select fold providers (e.g., {'lsp', 'treesitter', 'indent'}).
    • close_fold_kinds_for_ft: A table mapping filetypes to fold kind values (like 'comment', 'imports', or 'region') to be closed on buffer display.
    • close_fold_current_line_for_ft: A table mapping filetypes to booleans to determine if folds on the current line should be closed on display.
    • fold_virt_text_handler: A function to customize fold virtual text.
    • enable_get_fold_virt_text: Enables capturing virtual text for folded lines.
    • override_foldtext: Whether to override foldtext with a custom virtual text handler (default: true).
    • preview: Configuration for the preview window, including win_config (border, winblend, winhighlight, maxheight) and mappings.
  6. Customize fold text display with fold_virt_text_handler

    main

    You can customize the text displayed for folded lines by providing a handler function to fold_virt_text_handler in the setup() call.

    The handler function receives the following arguments:

    • virtText: The virtual text chunks.
    • lnum: The starting line number of the fold.
    • endLnum: The ending line number of the fold.
    • width: The available width for the text.
    • truncate: A function to truncate text.

    You can apply this globally via require('ufo').setup() or per-buffer using require('ufo').setFoldVirtTextHandler(bufnr, handler).

    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)
                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
    
    require('ufo').setup({
        fold_virt_text_handler = handler
    })
  7. Use nvim-ufo API functions and keybindings

    main

    The following functions are available for managing folds and previewing content:

    • require('ufo').openAllFolds(): Opens all folds.
    • require('ufo').closeAllFolds(): Closes all folds.
    • require('ufo').openFoldsExceptKinds(kinds): Opens all folds except those matching the specified kinds.
    • require('ufo').closeFoldsWith(kinds): Closes folds matching the specified kinds (passing 0 is equivalent to closeAllFolds).
    • require('ufo').peekFoldedLinesUnderCursor(): Peeks at the folded lines under the cursor. Returns a window ID if successful.

    Example keybindings:

    -- Open/Close all folds
    vim.keymap.set('n', 'zR', require('ufo').openAllFolds)
    vim.keymap.set('n', 'zM', require('ufo').closeAllFolds)
    
    -- Open/Close specific kinds
    vim.keymap.set('n', 'zr', require('ufo').openFoldsExceptKinds)
    vim.keymap.set('n', 'zm', require('ufo').closeFoldsWith)
    
    -- Peek folded lines and fallback to LSP/CoC hover
    vim.keymap.set('n', 'K', function()
        local winid = require('ufo').peekFoldedLinesUnderCursor()
        if not winid then
            -- choose one of coc.nvim and nvim lsp
            vim.fn.CocActionAsync('definitionHover') -- coc.nvim
            vim.lsp.buf.hover()
        end
    end)
    -- Example keybindings
    vim.keymap.set('n', 'zR', require('ufo').openAllFolds)
    vim.keymap.set('n', 'zM', require('ufo').closeAllFolds)
    vim.keymap.set('n', 'zr', require('ufo').openFoldsExceptKinds)
    vim.keymap.set('n', 'zm', require('ufo').closeFoldsWith)
    
    vim.keymap.set('n', 'K', function()
        local winid = require('ufo').peekFoldedLinesUnderCursor()
        if not winid then
            vim.lsp.buf.hover()
        end
    end)
  8. Configure nvim-ufo highlight groups

    main

    Customize the appearance of folds and the preview window using these highlight groups:

    • UfoFoldedFg: Foreground for raw text of folded line.
    • UfoFoldedBg: Background of folded line.
    • UfoPreviewSbar: Scroll bar of preview window.
    • UfoPreviewCursorLine: Highlight current line in preview window.
    • UfoPreviewWinBar: Virtual winBar of preview window.
    • UfoPreviewThumb: Thumb of preview window.
    • UfoFoldedEllipsis: Ellipsis at the end of folded line.
    • UfoCursorFoldedLine: Highlight the folded line under the cursor.
  9. Use nvim-ufo commands

    main

    The following commands are available to manage ufo behavior:

    CommandDescription
    UfoEnableEnable ufo
    UfoDisableDisable ufo
    UfoInspectInspect current buffer information
    UfoAttachAttach current buffer to enable all features
    UfoDetachDetach current buffer to disable all features
    UfoEnableFoldEnable to get folds and update them at once for current buffer
    UfoDisableFoldDisable to get folds for current buffer
  10. Configure custom foldtext with foldtext()

    main
    Use this function to implement custom fold text. It calculates virtual text and close folds based on the current fold start and end lines. It handles tab expansion and provides a fallback to the original line text if virtual text is unavailable.
  11. Control folding with enableFold() and disableFold()

    main

    Toggle the folding state for a specific buffer.

    • enableFold(bufnr): Sets the fold status to 'start' and triggers a fold update for the buffer.
    • disableFold(bufnr): Sets the fold status to 'stop' for the buffer.
    require('ufo').enableFold()
    require('ufo').disableFold()