nvim-ufo/README.md

387 lines
15 KiB
Markdown
Raw Permalink Normal View History

2022-06-01 09:17:08 +02:00
# nvim-ufo
The goal of nvim-ufo is to make Neovim's fold look modern and keep high performance.
<https://user-images.githubusercontent.com/17562139/173796287-9842fb3a-37c2-47fb-8968-6e7600c0fcef.mp4>
2022-06-18 16:33:45 +02:00
> [setup foldcolumn like demo](https://github.com/kevinhwang91/nvim-ufo/issues/4)
2022-06-01 09:17:08 +02:00
---
## Table of contents
- [Table of contents](#table-of-contents)
- [Features](#features)
- [Quickstart](#quickstart)
- [Requirements](#requirements)
- [Installation](#installation)
- [Minimal configuration](#minimal-configuration)
- [Usage](#usage)
- [Documentation](#documentation)
2022-07-23 15:56:40 +02:00
- [How does nvim-ufo get the folds?](#how-does-nvim-ufo-get-the-folds)
- [Setup and description](#setup-and-description)
2022-07-04 11:12:37 +02:00
- [Preview function table](#preview-function-table)
- [Commands](#commands)
- [API](#api)
- [Highlight groups](#highlight-groups)
- [Advanced configuration](#advanced-configuration)
2022-07-04 11:12:37 +02:00
- [Customize configuration](#customize-configuration)
- [Customize fold text](#customize-fold-text)
- [Feedback](#feedback)
- [License](#license)
2022-06-01 09:17:08 +02:00
## Features
- Penetrate color for folded lines like other modern editors/IDEs
- Never block Neovim
- Adding folds high accuracy with Folding Range in LSP
2022-08-12 18:50:07 +02:00
- Support fallback and customize strategy for fold provider
2022-07-04 11:12:37 +02:00
- Peek folded line and jump the desired location with less redraw
2022-06-01 09:17:08 +02:00
## Quickstart
### Requirements
2023-11-23 19:11:57 +01:00
- [Neovim](https://github.com/neovim/neovim) 0.7.2 or later
2022-06-01 09:17:08 +02:00
- [coc.nvim](https://github.com/neoclide/coc.nvim) (optional)
### Installation
Install with [Packer.nvim](https://github.com/wbthomason/packer.nvim):
```lua
use {'kevinhwang91/nvim-ufo', requires = 'kevinhwang91/promise-async'}
```
### Minimal configuration
```lua
use {'kevinhwang91/nvim-ufo', requires = 'kevinhwang91/promise-async'}
vim.o.foldcolumn = '1' -- '0' is not bad
2022-07-06 03:40:10 +02:00
vim.o.foldlevel = 99 -- Using ufo provider need a large value, feel free to decrease the value
vim.o.foldlevelstart = 99
2022-07-06 03:40:10 +02:00
vim.o.foldenable = true
2022-06-01 09:17:08 +02:00
2022-07-04 11:12:37 +02:00
-- 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)
2022-08-12 18:50:07 +02:00
-- Option 1: coc.nvim as LSP client
2022-06-01 09:17:08 +02:00
use {'neoclide/coc.nvim', branch = 'master', run = 'yarn install --frozen-lockfile'}
2022-07-04 11:12:37 +02:00
require('ufo').setup()
2022-06-18 16:33:45 +02:00
--
2022-06-01 09:17:08 +02:00
2022-08-12 18:50:07 +02:00
-- Option 2: nvim lsp as LSP client
-- Tell the server the capability of foldingRange,
-- Neovim hasn't added foldingRange to default capabilities, users must add it manually
2022-06-01 09:17:08 +02:00
local capabilities = vim.lsp.protocol.make_client_capabilities()
capabilities.textDocument.foldingRange = {
dynamicRegistration = false,
lineFoldingOnly = true
}
local language_servers = require("lspconfig").util.available_servers() -- 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
2022-07-04 11:12:37 +02:00
require('ufo').setup()
2022-06-18 16:33:45 +02:00
--
2022-06-01 09:17:08 +02:00
2022-08-12 18:50:07 +02:00
-- Option 3: treesitter as a main provider instead
refactor(treesitter): remove nvim-treesitter dependencies (#189) Replace all the use of nvim-treesitter APIs with core vim.treesitter APIs. No more nvim-treesitter dependency, just core neovim is enough. This fix makes ufo work without the nvim-treesitter plugin as an additional dependency. In fact, nvim-treesitter v1.0 deprecates and removes some APIs that have been migrated to the core neovim APIs `vim.treesitter`, which makes ufo's previous treesitter provider implementation incompatible. Note that this commit does not change the minimum neovim version requirement, should work fine with neovim 0.7.x. Implementation note: There are four APIs that need to be migrated: - `nvim-treesitter.parsers.get_parser()`: The difference to the core API `vim.treesitter.get_parser()` is whether to throw errors when a parser is not available (`has_parser`). We simply mimic the previous behavior by catching errors. - `nvim-treesitter.query.get_query()`: The difference to core API `vim.treesitter.query.get()` is whether the query file is cached or not. This may have a small performance impact; in neovim 0.10.x, this function is memoized and thus very fast, but in neovim <= 0.9.x it might be slightly slow due to the lack of cache. Note: One can consider as well automatically falling back to the old nvim-treesitter (v0.9.x) if available, for neovim < 0.10. - `nvim-treesitter.query.has_folds()` (i.e., `has_query_files()`): can be easily replaced with `vim.treesitter.query.get_files`. Also there might be a subtle performance difference of whether cache is being used (in the old nvim-treesitter implementations) or not. - `nvim-treesitter.tsrange`: The `TSRange` API has gone. Note that this is used only to implement the `#make-range!` directive; it suffices to have `node:range()` only for where it's used. Therefore, `TSRange.from_nodes()` is the only API we'll need, which can be easily backported into the existing `MetaNode` implementation.
2024-03-19 16:53:46 +01:00
-- (Note: the `nvim-treesitter` plugin is *not* needed.)
-- ufo uses the same query files for folding (queries/<lang>/folds.scm)
2022-08-12 18:50:07 +02:00
-- performance and stability are better than `foldmethod=nvim_treesitter#foldexpr()`
2022-07-04 11:12:37 +02:00
require('ufo').setup({
provider_selector = function(bufnr, filetype, buftype)
2022-07-04 11:12:37 +02:00
return {'treesitter', 'indent'}
end
})
--
2022-08-12 18:50:07 +02:00
-- Option 4: disable all providers for all buffers
2022-07-06 03:40:10 +02:00
-- Not recommend, AFAIK, the ufo's providers are the best performance in Neovim
2022-07-04 11:12:37 +02:00
require('ufo').setup({
provider_selector = function(bufnr, filetype, buftype)
2022-07-04 11:12:37 +02:00
return ''
end
})
2022-06-01 09:17:08 +02:00
```
### Usage
Use fold as usual.
2022-08-12 18:50:07 +02:00
Using a provider of ufo, must set a large value for `foldlevel`, this is the limitation of
2022-07-04 11:12:37 +02:00
`foldmethod=manual`. A small value may close fold automatically if the fold ranges updated.
2022-08-12 18:50:07 +02:00
After running `zR` and `zM` normal commands will change the `foldlevel`, ufo provide the APIs
`openAllFolds`/`closeAllFolds` to open/close all folds but keep `foldlevel` value, need to remap
them.
Like `zR` and `zM`, if you used `zr` and `zm` before, please use `closeFoldsWith` API to close folds
like `set foldlevel=n` but keep `foldlevel` value.
2022-07-04 11:12:37 +02:00
2022-06-01 09:17:08 +02:00
## Documentation
### How does nvim-ufo get the folds?
If ufo detect `foldmethod` option is not `diff` or `marker`, it will request the providers to get
2022-06-18 16:33:45 +02:00
the folds, the request strategy is formed by the main and the fallback. The default value of main is
2022-06-01 09:17:08 +02:00
`lsp` and the default value of fallback is `indent` which implemented by ufo.
2022-07-04 11:12:37 +02:00
For example, Changing the text in a buffer will request the providers for folds.
2022-06-01 09:17:08 +02:00
> `foldmethod` option will finally become `manual` if ufo is working.
2022-06-01 09:17:08 +02:00
### Setup and description
```lua
{
open_fold_hl_timeout = {
2022-07-04 11:12:37 +02:00
description = [[Time in millisecond between the range to be highlgihted and to be cleared
2022-06-01 09:17:08 +02:00
while opening the folded line, `0` value will disable the highlight]],
default = 400
},
provider_selector = {
2022-07-04 11:12:37 +02:00
description = [[A function as a selector for fold providers. For now, there are
'lsp' and 'treesitter' as main provider, 'indent' as fallback provider]],
2022-06-01 09:17:08 +02:00
default = nil
},
close_fold_kinds_for_ft = {
description = [[After the buffer is displayed (opened for the first time), close the
2022-10-24 15:16:00 +02:00
folds whose range with `kind` field is included in this option. For now,
'lsp' provider's standardized kinds are 'comment', 'imports' and 'region',
and the 'treesitter' provider exposes the underlying node types.
This option is a table with filetype as key and fold kinds as value. Use a
default value if value of filetype is absent.
Run `UfoInspect` for details if your provider has extended the kinds.]],
default = {default = {}}
},
fold_virt_text_handler = {
2022-07-04 11:12:37 +02:00
description = [[A function customize fold virt text, see ### Customize fold text]],
default = nil
2022-07-04 11:12:37 +02:00
},
enable_get_fold_virt_text = {
description = [[Enable a function with `lnum` as a parameter to capture the virtual text
for the folded lines and export the function to `get_fold_virt_text` field of
ctx table as 6th parameter in `fold_virt_text_handler`]],
2022-07-04 18:49:57 +02:00
default = false
},
2022-07-04 11:12:37 +02:00
preview = {
description = [[Configure the options for preview window and remap the keys for current
buffer and preview buffer if the preview window is displayed.
Never worry about the users's keymaps are overridden by ufo, ufo will save
them and restore them if preview window is closed.]],
win_config = {
border = {
description = [[The border for preview window,
`:h nvim_open_win() | call search('border:')`]],
default = 'rounded',
},
winblend = {
description = [[The winblend for preview window, `:h winblend`]],
default = 12,
},
winhighlight = {
description = [[The winhighlight for preview window, `:h winhighlight`]],
default = 'Normal:Normal',
},
maxheight = {
description = [[The max height of preview window]],
default = 20,
}
2022-07-04 11:12:37 +02:00
},
mappings = {
description = [[The table for {function = key}]],
default = [[see ###Preview function table for detail]],
}
2022-06-01 09:17:08 +02:00
}
}
```
2022-07-04 11:12:37 +02:00
`:h ufo` may help you to get the all default configuration.
### Preview function table
<!-- markdownlint-disable MD013 -->
| Function | Action | Def Key |
| -------- | ---------------------------------------------------------------------------------------------- | ------- |
| scrollB | Type `CTRL-B` in preview window | |
| scrollF | Type `CTRL-F` in preview window | |
| scrollU | Type `CTRL-U` in preview window | |
| scrollD | Type `CTRL-D` in preview window | |
| scrollE | Type `CTRL-E` in preview window | `<C-E>` |
| scrollY | Type `CTRL-Y` in preview window | `<C-Y>` |
| jumpTop | Jump to top region in preview window | |
| jumpBot | Jump to bottom region in preview window | |
| close | In normal window: Close preview window<br>In preview window: Close preview window | `q` |
2022-07-04 11:12:37 +02:00
| switch | In normal window: Go to preview window<br>In preview window: Go to normal window | `<Tab>` |
| trace | In normal window: Trace code based on topline<br>In preview window: Trace code based on cursor | `<CR>` |
<!-- markdownlint-enable MD013-->
Additional mouse supported:
1. `<ScrollWheelUp>` and `<ScrollWheelDown>`: Scroll preview window.
2022-07-04 12:46:52 +02:00
2. `<2-LeftMouse>`: Same as `trace` action in preview window.
> `trace` action will open all fold for the folded lines
2022-07-04 11:12:37 +02:00
### Commands
2022-07-27 02:41:23 +02:00
| Command | Description |
| -------------- | -------------------------------------------------------------- |
| UfoEnable | Enable ufo |
| UfoDisable | Disable ufo |
| UfoInspect | Inspect current buffer information |
| UfoAttach | Attach current buffer to enable all features |
| UfoDetach | Detach current buffer to disable all features |
2022-07-27 02:41:23 +02:00
| UfoEnableFold | Enable to get folds and update them at once for current buffer |
| UfoDisableFold | Disable to get folds for current buffer |
### API
2022-07-04 11:12:37 +02:00
[ufo.lua](./lua/ufo.lua)
2022-06-01 09:17:08 +02:00
### Highlight groups
```vim
" hi default UfoFoldedFg guifg=Normal.foreground
" hi default UfoFoldedBg guibg=Folded.background
2022-07-04 11:12:37 +02:00
hi default link UfoPreviewSbar PmenuSbar
hi default link UfoPreviewThumb PmenuThumb
hi default link UfoPreviewWinBar UfoFoldedBg
hi default link UfoPreviewCursorLine Visual
2022-06-01 09:17:08 +02:00
hi default link UfoFoldedEllipsis Comment
hi default link UfoCursorFoldedLine CursorLine
2022-06-01 09:17:08 +02:00
```
- `UfoFoldedFg`: Foreground for raw text of folded line.
- `UfoFoldedBg`: Background of folded line.
- `UfoPreviewSbar`: Scroll bar of preview window, only take effect if the border is missing right
horizontal line, like `border = 'none'`.
- `UfoPreviewCursorLine`: Highlight current line in preview window if it isn't the start of folded
lines.
- `UfoPreviewWinBar`: Virtual winBar of preview window.
- `UfoPreviewThumb`: Thumb of preview window.
- `UfoFoldedEllipsis`: Ellipsis at the end of folded line, invalid if `fold_virt_text_handler` is
set.
- `UfoCursorFoldedLine`: Highlight the folded line under the cursor
2022-06-01 09:17:08 +02:00
## Advanced configuration
2022-06-18 16:33:45 +02:00
Configuration can be found at [example.lua](./doc/example.lua)
2022-07-04 11:12:37 +02:00
### Customize configuration
```lua
local ftMap = {
vim = 'indent',
python = {'indent'},
git = ''
}
require('ufo').setup({
2022-07-04 11:12:37 +02:00
open_fold_hl_timeout = 150,
close_fold_kinds_for_ft = {
default = {'imports', 'comment'},
json = {'array'},
c = {'comment', 'region'}
},
2022-07-04 11:12:37 +02:00
preview = {
win_config = {
border = {'', '─', '', '', '', '─', '', ''},
winhighlight = 'Normal:Folded',
winblend = 0
},
mappings = {
scrollU = '<C-u>',
scrollD = '<C-d>',
jumpTop = '[',
jumpBot = ']'
2022-07-04 11:12:37 +02:00
}
},
provider_selector = function(bufnr, filetype, buftype)
2022-07-04 11:12:37 +02:00
-- if you prefer treesitter provider rather than lsp,
-- return ftMap[filetype] or {'treesitter', 'indent'}
return ftMap[filetype]
-- refer to ./doc/example.lua for detail
end
})
2022-08-12 18:50:07 +02:00
vim.keymap.set('n', 'zR', require('ufo').openAllFolds)
vim.keymap.set('n', 'zM', require('ufo').closeAllFolds)
vim.keymap.set('n', 'zr', require('ufo').openFoldsExceptKinds)
2022-08-12 18:50:07 +02:00
vim.keymap.set('n', 'zm', require('ufo').closeFoldsWith) -- closeAllFolds == closeFoldsWith(0)
2022-07-04 11:12:37 +02:00
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
2022-07-04 11:12:37 +02:00
vim.lsp.buf.hover()
end
end)
```
### Customize fold text
Adding number suffix of folded lines instead of the default ellipsis, here is the example:
<p align="center">
<img width="864px" src=https://user-images.githubusercontent.com/17562139/174121926-e90a962d-9fc9-428a-bd53-274ed392c68d.png>
</p>
```lua
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)
2022-06-19 09:49:19 +02:00
-- 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
-- global handler
-- `handler` is the 2nd parameter of `setFoldVirtTextHandler`,
-- check out `./lua/ufo.lua` and search `setFoldVirtTextHandler` for detail.
require('ufo').setup({
fold_virt_text_handler = handler
2022-06-19 13:09:15 +02:00
})
-- buffer scope handler
-- will override global handler if it is existed
-- local bufnr = vim.api.nvim_get_current_buf()
-- require('ufo').setFoldVirtTextHandler(bufnr, handler)
```
2022-06-01 09:17:08 +02:00
## Feedback
- If you get an issue or come up with an awesome idea, don't hesitate to open an issue in github.
- If you think this plugin is useful or cool, consider rewarding it a star.
## License
The project is licensed under a BSD-3-clause license. See [LICENSE](./LICENSE) file for details.