docs: func/expr options, misc #41102

This commit is contained in:
Justin M. Keyes
2026-08-01 17:48:32 -04:00
committed by GitHub
parent d060d91ef8
commit 98d767cd53
19 changed files with 259 additions and 248 deletions

View File

@@ -1494,25 +1494,24 @@ $VIMRUNTIME/ftplugin directory, sets the 'formatexpr' option to: >
That means, you will find the corresponding script, defining the
xmlformat#Format() function, in the file `$VIMRUNTIME/autoload/xmlformat.vim`
Here is an example script that removes trailing whitespace from the selected
text. Put it in your autoload directory, e.g. ~/.vim/autoload/format.vim:
>vim
func! format#Format()
" only reformat on explicit gq command
if mode() != 'n'
" fall back to Vim's internal reformatting
Here is an example that removes trailing whitespace from the selected text.
Put it in your |ftplugin|:
>lua
vim.bo.formatexpr = function()
-- only reformat on explicit gq command
if vim.fn.mode() ~= 'n' then
-- fall back to the internal reformatting
return 1
endif
let lines = getline(v:lnum, v:lnum + v:count - 1)
call map(lines, {key, val -> substitute(val, '\s\+$', '', 'g')})
call setline('.', lines)
end
local lines = vim.fn.getline(vim.v.lnum, vim.v.lnum + vim.v.count - 1)
for i, val in ipairs(lines) do
lines[i] = (val:gsub('%s+$', ''))
end
vim.fn.setline('.', lines)
" do not run internal formatter!
-- do not run internal formatter!
return 0
endfunc
You can then enable the formatting by executing: >
setlocal formatexpr=format#Format()
end
Note: this function explicitly returns non-zero when called from insert mode
(which basically means, text is inserted beyond the 'textwidth' limit). This

View File

@@ -511,28 +511,22 @@ Additionally, 'diffexpr' should take care of "icase" and "iwhite" in the
The advantage of using a function call without arguments is that it is faster,
see |expr-option-function|.
Example (this does almost the same as 'diffexpr' being empty): >
Example (this does almost the same as 'diffexpr' being empty): >lua
set diffexpr=MyDiff()
function MyDiff()
let opt = ""
if &diffopt =~ "icase"
let opt = opt .. "-i "
endif
if &diffopt =~ "iwhite"
let opt = opt .. "-b "
endif
silent execute "!diff -a --binary " .. opt .. v:fname_in .. " " .. v:fname_new ..
\ " > " .. v:fname_out
redraw!
endfunction
vim.o.diffexpr = function()
local opt = ''
if vim.o.diffopt:find('icase') then
opt = opt .. '-i '
end
if vim.o.diffopt:find('iwhite') then
opt = opt .. '-b '
end
vim.fn.system(('diff -a %s%s %s > %s'):format(
opt, vim.v.fname_in, vim.v.fname_new, vim.v.fname_out))
end
The "-a" argument is used to force comparing the files as text, comparing as
binaries isn't useful. The "--binary" argument makes the files read in binary
mode, so that a CTRL-Z doesn't end the text on DOS.
The `redraw!` command may not be needed, depending on whether executing a
shell command shows something on the display or not.
binaries isn't useful.
If the 'diffexpr' expression starts with s: or |<SID>|, then it is replaced
with the script ID (|local-function|). Example: >
@@ -579,13 +573,12 @@ will have the same effect. These variables are set to the file names used:
The advantage of using a function call without arguments is that it is faster,
see |expr-option-function|.
Example (this does the same as 'patchexpr' being empty): >
Example (this does the same as 'patchexpr' being empty): >lua
set patchexpr=MyPatch()
function MyPatch()
:call system("patch -o " .. v:fname_out .. " " .. v:fname_in ..
\ " < " .. v:fname_diff)
endfunction
vim.o.patchexpr = function()
vim.fn.system(('patch -o %s %s < %s'):format(
vim.v.fname_out, vim.v.fname_in, vim.v.fname_diff))
end
Make sure that using the "patch" program doesn't have unwanted side effects.
For example, watch out for additionally generated files, which should be

View File

@@ -67,17 +67,35 @@ EXPR *fold-expr*
The folds are automatically defined by their foldlevel, like with the "indent"
method. The value of the 'foldexpr' option is evaluated to get the foldlevel
of a line. Examples:
This will create a fold for all consecutive lines that start with a tab: >
This will create a fold for all consecutive lines that start with a tab: >lua
vim.wo.foldexpr = function()
return vim.fn.getline(vim.v.lnum):sub(1, 1) == '\t' and 1 or 0
end
This will make a fold out of paragraphs separated by blank lines: >lua
vim.wo.foldexpr = function()
if vim.fn.getline(vim.v.lnum):match('^%s*$')
and vim.fn.getline(vim.v.lnum + 1):match('%S') then
return '<1'
end
return 1
end
This does the same: >lua
vim.wo.foldexpr = function()
if vim.fn.getline(vim.v.lnum - 1):match('^%s*$')
and vim.fn.getline(vim.v.lnum):match('%S') then
return '>1'
end
return 1
end
When setting an expression string with ":set" instead, backslashes must be
used to escape characters that ":set" handles differently (space, backslash,
double quote, etc., see |option-backslash|): >vim
:set foldexpr=getline(v:lnum)[0]==\"\\t\"
This will make a fold out of paragraphs separated by blank lines: >
:set foldexpr=getline(v:lnum)=~'^\\s*$'&&getline(v:lnum+1)=~'\\S'?'<1':1
This does the same: >
:set foldexpr=getline(v:lnum-1)=~'^\\s*$'&&getline(v:lnum)=~'\\S'?'>1':1
Note that backslashes must be used to escape characters that ":set" handles
differently (space, backslash, double quote, etc., see |option-backslash|).
The most efficient is to call a function without arguments: >
The most efficient is to use a function value (|expr-option-function|): >lua
vim.wo.foldexpr = MyFoldLevel
or to call a Vimscript function without arguments: >vim
:set foldexpr=MyFoldLevel()
The function must use v:lnum. See |expr-option-function|.
@@ -159,20 +177,20 @@ level is found.
If this proves difficult, the next best thing could be to cache all fold
levels in a buffer-local variable (b:foldlevels) that is only updated on
|b:changedtick|:
>vim
func MyFoldFunc()
if b:lasttick == b:changedtick
return b:foldlevels[v:lnum - 1]
endif
let b:lasttick = b:changedtick
let b:foldlevels = []
" compute foldlevels ...
return b:foldlevels[v:lnum - 1]
enddef
set foldexpr=s:MyFoldFunc()
>lua
vim.wo.foldexpr = function()
if vim.b.lasttick == vim.b.changedtick then
return vim.b.foldlevels[vim.v.lnum]
end
vim.b.lasttick = vim.b.changedtick
local levels = {}
-- compute foldlevels ...
vim.b.foldlevels = levels
return levels[vim.v.lnum]
end
<
In above example further speedup was gained by using a function without
arguments (that must still use v:lnum). See |expr-option-function|.
A function value also avoids re-parsing the expression for every line (it must
still use v:lnum). See |expr-option-function|.
SYNTAX *fold-syntax*
@@ -523,11 +541,14 @@ folds will be opened.
FOLDTEXT *fold-foldtext*
'foldtext' is a string option that specifies an expression. This expression
is evaluated to obtain the text displayed for a closed fold. Example: >
is evaluated to obtain the text displayed for a closed fold. Example: >lua
:set foldtext=v:folddashes.substitute(getline(v:foldstart),'/\\*\\\|\\*/\\\|{{{\\d\\=','','g')
vim.wo.foldtext = function()
local line = vim.fn.getline(vim.v.foldstart)
return vim.v.folddashes .. line:gsub('/%*', ''):gsub('%*/', '')
end
This shows the first line of the fold, with "/*", "*/" and "{{{" removed.
This shows the first line of the fold, with "/*" and "*/" removed.
Note the use of backslashes to avoid some characters to be interpreted by the
":set" command. It is much simpler to define a function and call it: >

View File

@@ -696,16 +696,16 @@ had been typed.
For example, the following will map <Tab> to either actually insert a <Tab> if
the current line is currently only whitespace, or start/continue a CTRL-N
completion operation: >
completion operation: >lua
function! CleverTab()
if strpart( getline('.'), 0, col('.')-1 ) =~ '^\s*$'
return "\<Tab>"
else
return "\<C-N>"
endif
endfunction
inoremap <Tab> <C-R>=CleverTab()<CR>
function _G.clever_tab()
if vim.fn.getline('.'):sub(1, vim.fn.col('.') - 1):match('^%s*$') then
return vim.keycode('<Tab>')
else
return vim.keycode('<C-N>')
end
end
vim.keymap.set('i', '<Tab>', '<C-R>=v:lua.clever_tab()<CR>')
@@ -876,29 +876,29 @@ not used. See |complete-functions| for an explanation of how the function is
invoked and what it should return.
Here is an example that uses the "aiksaurus" command (provided by Magnus
Groß): >
Groß): >lua
func Thesaur(findstart, base)
if a:findstart
return searchpos('\<', 'bnW', line('.'))[1] - 1
endif
let res = []
let h = ''
for l in systemlist('aiksaurus ' .. shellescape(a:base))
if l[:3] == '=== '
let h = '(' .. substitute(l[4:], ' =*$', ')', '')
elseif l ==# 'Alphabetically similar known words are: '
let h = "\U0001f52e"
elseif l[0] =~ '\a' || (h ==# "\U0001f52e" && l[0] ==# "\t")
call extend(res, map(split(substitute(l, '^\t', '', ''), ', '), {_, val -> {'word': val, 'menu': h}}))
endif
endfor
vim.o.thesaurusfunc = function(findstart, base)
if findstart == 1 then
return vim.fn.searchpos('\\<', 'bnW', vim.fn.line('.'))[2] - 1
end
local res = {}
local h = ''
for _, l in ipairs(vim.fn.systemlist('aiksaurus '
.. vim.fn.shellescape(base))) do
if l:sub(1, 4) == '=== ' then
h = ('(%s)'):format((l:sub(5):gsub(' =*$', '')))
elseif l == 'Alphabetically similar known words are: ' then
h = '🔮'
elseif l:match('^%a') or (h == '🔮' and l:sub(1, 1) == '\t') then
local words = (l:gsub('^\t', ''))
for _, val in ipairs(vim.split(words, ', ', { plain = true })) do
table.insert(res, { word = val, menu = h })
end
end
end
return res
endfunc
if exists('+thesaurusfunc')
set thesaurusfunc=Thesaur
endif
end
Completing keywords in the current and included files *compl-keyword*
@@ -1291,54 +1291,50 @@ while still searching for matches. Stop searching when it returns non-zero.
The function is allowed to move the cursor, it is restored afterwards.
The function is not allowed to move to another window or delete text.
An example that completes the names of the months: >
fun! CompleteMonths(findstart, base)
if a:findstart
" locate the start of the word
let line = getline('.')
let start = col('.') - 1
while start > 0 && line[start - 1] =~ '\a'
let start -= 1
endwhile
return start
else
" find months matching with "a:base"
let res = []
for m in split("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec")
if m =~ '^' .. a:base
call add(res, m)
endif
endfor
return res
endif
endfun
set completefunc=CompleteMonths
An example that completes the names of the months: >lua
vim.o.completefunc = function(findstart, base)
if findstart == 1 then -- Locate the start of the word.
local line = vim.fn.getline('.')
local start = vim.fn.col('.') - 1
while start > 0 and line:sub(start, start):match('%a') do
start = start - 1
end
return start
else -- Find months matching with "base".
local res = {}
local months = 'Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec'
for m in months:gmatch('%a+') do
if vim.startswith(m, base) then
table.insert(res, m)
end
end
return res
end
end
<
The same, but now pretending searching for matches is slow: >
fun! CompleteMonths(findstart, base)
if a:findstart
" locate the start of the word
let line = getline('.')
let start = col('.') - 1
while start > 0 && line[start - 1] =~ '\a'
let start -= 1
endwhile
return start
else
" find months matching with "a:base"
for m in split("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec")
if m =~ '^' .. a:base
call complete_add(m)
endif
sleep 300m " simulate searching for next match
if complete_check()
break
endif
endfor
return []
endif
endfun
set completefunc=CompleteMonths
The same, but now pretending searching for matches is slow: >lua
vim.o.completefunc = function(findstart, base)
if findstart == 1 then -- Locate the start of the word.
local line = vim.fn.getline('.')
local start = vim.fn.col('.') - 1
while start > 0 and line:sub(start, start):match('%a') do
start = start - 1
end
return start
else -- Find months matching with "base".
local months = 'Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec'
for m in months:gmatch('%a+') do
if vim.startswith(m, base) then
vim.fn.complete_add(m)
end
vim.uv.sleep(300) -- Simulate searching for next match.
if vim.fn.complete_check() ~= 0 then
break
end
end
return {}
end
end
<
INSERT COMPLETION POPUP MENU *ins-completion-menu*

View File

@@ -1210,7 +1210,7 @@ foldexpr({lnum}) *vim.lsp.foldexpr()*
To use, set 'foldmethod' to "expr" and set the value of 'foldexpr': >lua
vim.o.foldmethod = 'expr'
vim.o.foldexpr = 'v:lua.vim.lsp.foldexpr()'
vim.o.foldexpr = vim.lsp.foldexpr
<
Or use it only when supported by checking for the
@@ -1218,14 +1218,14 @@ foldexpr({lnum}) *vim.lsp.foldexpr()*
Example: >lua
vim.o.foldmethod = 'expr'
-- Default to treesitter folding
vim.o.foldexpr = 'v:lua.vim.treesitter.foldexpr()'
vim.o.foldexpr = vim.treesitter.foldexpr
-- Prefer LSP folding if client supports it
vim.api.nvim_create_autocmd('LspAttach', {
callback = function(ev)
local client = vim.lsp.get_client_by_id(ev.data.client_id)
if client:supports_method('textDocument/foldingRange') then
local win = vim.api.nvim_get_current_win()
vim.wo[win][0].foldexpr = 'v:lua.vim.lsp.foldexpr()'
vim.wo[win][0].foldexpr = vim.lsp.foldexpr
end
end,
})
@@ -1248,9 +1248,12 @@ formatexpr({opts}) *vim.lsp.formatexpr()*
function.
Currently only supports a single client. This can be set via
`setlocal formatexpr=v:lua.vim.lsp.formatexpr()` or (more typically) in
`on_attach` via
`vim.bo[bufnr].formatexpr = 'v:lua.vim.lsp.formatexpr(#{timeout_ms:250})'`.
`vim.bo[bufnr].formatexpr = vim.lsp.formatexpr`, or with a wrapper to pass
options: >lua
vim.bo[bufnr].formatexpr = function()
return vim.lsp.formatexpr({ timeout_ms = 250 })
end
<
Parameters: ~
• {opts} (`table?`) A table with the following fields:

View File

@@ -1315,7 +1315,7 @@ vim.lua_omnifunc({find_start}) *vim.lua_omnifunc()*
Omnifunc for completing Lua values from the runtime Lua interpreter,
similar to the builtin completion for the `:lua` command.
Activate using `set omnifunc=v:lua.vim.lua_omnifunc` in a Lua buffer.
Activate using `vim.bo.omnifunc = vim.lua_omnifunc` in a Lua buffer.
Parameters: ~
• {find_start} (`1|0`)
@@ -2644,15 +2644,15 @@ vim.fs.dir({path}, {opts}) *vim.fs.dir()*
Parameters: ~
• {path} (`string`) Directory to iterate over, normalized via
|vim.fs.normalize()| unless `opts.normalize=false`.
• {opts} (`table?`) Optional keyword arguments:
• {opts} (`table?`) A table with the following fields:
• {depth}? (`integer`, default: `1`) How deep to traverse.
• {err}? (`boolean`, default: `false`) Report errors via the
iterator's third value ("err"), instead of silently
skipping.
• {follow}? (`boolean`, default: `false`) Follow symbolic
links.
• {normalize}? (`boolean`, default: `true`) Expand "~" and "$"
in {path} before scanning the directory.
• {plain}? (`boolean`, default: `false`) Do not expand special
forms like "~" and "$" in {path}.
• {skip}? (`fun(dir_name: string): boolean`) Predicate to
control traversal. Return false to stop searching the
current directory. Only useful when depth > 1 Return an

View File

@@ -330,6 +330,9 @@ g<Down> [count] display lines downward. |exclusive| motion.
*-*
`-` <minus> [count] lines upward, on the first non-blank
character |linewise|.
*-_default*
By default "-" is mapped to open the parent directory
of the current buffer. |dir-mappings| |default-mappings|
`+` or *+*
CTRL-M or *CTRL-M* *<CR>*

View File

@@ -320,7 +320,7 @@ LUA
• |vim.fs.abspath()| gained `cwd` and `plain` parameters.
• |vim.fs.dir()| with `opts.err=true`, reports errors. An inaccessible root
dir yields a single (name, nil, err) item.
• |vim.fs.dir()| gained a `normalize` parameter.
• |vim.fs.dir()| gained a `plain` parameter.
• |vim.fs.find()| returns a list of errors as its second return value.
• |vim.fs.mkdir()| creates directories, including parent directories with
`opts.parents=true`.

View File

@@ -1386,14 +1386,13 @@ A jump table for the options with a short description can be found at |Q_op|.
Conversion between "latin1", "unicode", "ucs-2", "ucs-4" and "utf-8"
is done internally by Vim, 'charconvert' is not used for this.
Also used for Unicode conversion.
Example: >vim
set charconvert=CharConvert()
fun CharConvert()
system("recode "
\ .. v:charconvert_from .. ".." .. v:charconvert_to
\ .. " <" .. v:fname_in .. " >" .. v:fname_out)
return v:shell_error
endfun
Example: >lua
vim.o.charconvert = function()
vim.fn.system(('recode %s..%s <%s >%s'):format(
vim.v.charconvert_from, vim.v.charconvert_to,
vim.v.fname_in, vim.v.fname_out))
return vim.v.shell_error
end
< The related Vim variables are:
v:charconvert_from name of the current encoding
v:charconvert_to name of the desired encoding
@@ -2988,20 +2987,20 @@ A jump table for the options with a short description can be found at |Q_op|.
Examples:
>vim
" Use glob()
func FindFuncGlob(cmdarg, cmdcomplete)
let pat = a:cmdcomplete ? $'{a:cmdarg}*' : a:cmdarg
return glob(pat, v:false, v:true)
endfunc
set findfunc=FindFuncGlob
>lua
-- Use vim.fn.glob()
vim.o.findfunc = function(cmdarg, cmdcomplete)
local pat = cmdcomplete and (cmdarg .. '*') or cmdarg
return vim.fn.glob(pat, false, true)
end
" Use the 'git ls-files' output
func FindGitFiles(cmdarg, cmdcomplete)
let fnames = systemlist('git ls-files')
return fnames->filter('v:val =~? a:cmdarg')
endfunc
set findfunc=FindGitFiles
-- Use the "git ls-files" output
vim.o.findfunc = function(cmdarg, cmdcomplete)
local fnames = vim.fn.systemlist('git ls-files')
return vim.tbl_filter(function(v)
return v:lower():find(cmdarg:lower(), 1, true) ~= nil
end, fnames)
end
<
*'fixendofline'* *'fixeol'* *'nofixendofline'* *'nofixeol'*

View File

@@ -967,20 +967,14 @@ It is not allowed to close a window or change window from inside 'tagfunc'.
The following is a hypothetical example of a function used for 'tagfunc'. It
uses the output of |taglist()| to generate the result: a list of tags in the
inverse order of file names.
>vim
function CompareFilenames(item1, item2)
let f1 = a:item1['filename']
let f2 = a:item2['filename']
return f1 >=# f2 ? -1 : f1 <=# f2 ? 1 : 0
endfunction
function TagFunc(pattern, flags, info)
let result = taglist(a:pattern)
call sort(result, "CompareFilenames")
>lua
vim.bo.tagfunc = function(pattern, flags, info)
local result = vim.fn.taglist(pattern)
table.sort(result, function(item1, item2)
return item1.filename > item2.filename
end)
return result
endfunc
set tagfunc=TagFunc
end
<
Note: When executing |taglist()| the 'tagfunc' function won't be called
recursively.

View File

@@ -958,7 +958,7 @@ Lua module: vim.treesitter *lua-treesitter-core*
foldexpr({lnum}) *vim.treesitter.foldexpr()*
Returns the fold level for {lnum} in the current buffer. Can be set
directly to 'foldexpr': >lua
vim.wo.foldexpr = 'v:lua.vim.treesitter.foldexpr()'
vim.wo.foldexpr = vim.treesitter.foldexpr
<
Attributes: ~

View File

@@ -60,7 +60,7 @@ end
---@param cb fun(err?: string, entries?: nvim.dir.Entry[])
function M.list(_, path, cb)
local entries = {} ---@type nvim.dir.Entry[]
for name, type, err in fs.dir(path, { err = true, normalize = false }) do
for name, type, err in fs.dir(path, { err = true, plain = true }) do
if err then
cb(err)
return

View File

@@ -1217,7 +1217,7 @@ do
--- Omnifunc for completing Lua values from the runtime Lua interpreter,
--- similar to the builtin completion for the `:lua` command.
---
--- Activate using `set omnifunc=v:lua.vim.lua_omnifunc` in a Lua buffer.
--- Activate using `vim.bo.omnifunc = vim.lua_omnifunc` in a Lua buffer.
--- @param find_start 1|0
function vim.lua_omnifunc(find_start, _)
if find_start == 1 then

View File

@@ -811,14 +811,13 @@ vim.bo.channel = vim.o.channel
--- Also used for Unicode conversion.
--- Example:
---
--- ```vim
--- set charconvert=CharConvert()
--- fun CharConvert()
--- system("recode "
--- \ .. v:charconvert_from .. ".." .. v:charconvert_to
--- \ .. " <" .. v:fname_in .. " >" .. v:fname_out)
--- return v:shell_error
--- endfun
--- ```lua
--- vim.o.charconvert = function()
--- vim.fn.system(('recode %s..%s <%s >%s'):format(
--- vim.v.charconvert_from, vim.v.charconvert_to,
--- vim.v.fname_in, vim.v.fname_out))
--- return vim.v.shell_error
--- end
--- ```
--- The related Vim variables are:
--- v:charconvert_from name of the current encoding
@@ -2655,20 +2654,20 @@ vim.go.fcs = vim.go.fillchars
---
--- Examples:
---
--- ```vim
--- " Use glob()
--- func FindFuncGlob(cmdarg, cmdcomplete)
--- let pat = a:cmdcomplete ? $'{a:cmdarg}*' : a:cmdarg
--- return glob(pat, v:false, v:true)
--- endfunc
--- set findfunc=FindFuncGlob
--- ```lua
--- -- Use vim.fn.glob()
--- vim.o.findfunc = function(cmdarg, cmdcomplete)
--- local pat = cmdcomplete and (cmdarg .. '*') or cmdarg
--- return vim.fn.glob(pat, false, true)
--- end
---
--- " Use the 'git ls-files' output
--- func FindGitFiles(cmdarg, cmdcomplete)
--- let fnames = systemlist('git ls-files')
--- return fnames->filter('v:val =~? a:cmdarg')
--- endfunc
--- set findfunc=FindGitFiles
--- -- Use the "git ls-files" output
--- vim.o.findfunc = function(cmdarg, cmdcomplete)
--- local fnames = vim.fn.systemlist('git ls-files')
--- return vim.tbl_filter(function(v)
--- return v:lower():find(cmdarg:lower(), 1, true) ~= nil
--- end, fnames)
--- end
--- ```
---
---

View File

@@ -191,9 +191,9 @@ end
--- (default: `false`)
--- @field follow? boolean
---
--- Expand "~" and "$" in {path} before scanning the directory.
--- (default: `true`)
--- @field normalize? boolean
--- Do not expand special forms like "~" and "$" in {path}.
--- (default: `false`)
--- @field plain? boolean
--- Gets an iterator over items found in `path` (normalized via |vim.fs.normalize()|).
---
@@ -210,7 +210,7 @@ end
---@since 10
---@param path (string) Directory to iterate over, normalized via |vim.fs.normalize()| unless
--- `opts.normalize=false`.
---@param opts? vim.fs.dir.Opts Optional keyword arguments:
---@param opts? vim.fs.dir.Opts
---@return fun(): string?, string?, string? # Iterator over items in {path}, yielding (name, type, err):
--- - name: Basename of the item relative to {path}.
--- - type: One of: "file", "directory", "link", "fifo", "socket", "char", "block", "unknown".
@@ -224,9 +224,9 @@ function M.dir(path, opts)
vim.validate('err', opts.err, 'boolean', true)
vim.validate('follow', opts.follow, 'boolean', true)
vim.validate('skip', opts.skip, 'function', true)
vim.validate('normalize', opts.normalize, 'boolean', true)
vim.validate('plain', opts.plain, 'boolean', true)
if opts.normalize ~= false then
if opts.plain ~= true then
path = M.normalize(path)
end

View File

@@ -1374,8 +1374,13 @@ end
--- Provides an interface between the built-in client and a `formatexpr` function.
---
--- Currently only supports a single client. This can be set via
--- `setlocal formatexpr=v:lua.vim.lsp.formatexpr()` or (more typically) in `on_attach`
--- via `vim.bo[bufnr].formatexpr = 'v:lua.vim.lsp.formatexpr(#{timeout_ms:250})'`.
--- `vim.bo[bufnr].formatexpr = vim.lsp.formatexpr`, or with a wrapper to pass options:
---
--- ```lua
--- vim.bo[bufnr].formatexpr = function()
--- return vim.lsp.formatexpr({ timeout_ms = 250 })
--- end
--- ```
---
---@param opts? vim.lsp.formatexpr.Opts
function lsp.formatexpr(opts)
@@ -1445,7 +1450,7 @@ end
---
--- ```lua
--- vim.o.foldmethod = 'expr'
--- vim.o.foldexpr = 'v:lua.vim.lsp.foldexpr()'
--- vim.o.foldexpr = vim.lsp.foldexpr
--- ```
---
--- Or use it only when supported by checking for the "textDocument/foldingRange"
@@ -1454,14 +1459,14 @@ end
--- ```lua
--- vim.o.foldmethod = 'expr'
--- -- Default to treesitter folding
--- vim.o.foldexpr = 'v:lua.vim.treesitter.foldexpr()'
--- vim.o.foldexpr = vim.treesitter.foldexpr
--- -- Prefer LSP folding if client supports it
--- vim.api.nvim_create_autocmd('LspAttach', {
--- callback = function(ev)
--- local client = vim.lsp.get_client_by_id(ev.data.client_id)
--- if client:supports_method('textDocument/foldingRange') then
--- local win = vim.api.nvim_get_current_win()
--- vim.wo[win][0].foldexpr = 'v:lua.vim.lsp.foldexpr()'
--- vim.wo[win][0].foldexpr = vim.lsp.foldexpr
--- end
--- end,
--- })

View File

@@ -502,7 +502,7 @@ end
--- Returns the fold level for {lnum} in the current buffer. Can be set directly to 'foldexpr':
---
--- ```lua
--- vim.wo.foldexpr = 'v:lua.vim.treesitter.foldexpr()'
--- vim.wo.foldexpr = vim.treesitter.foldexpr
--- ```
---
---@since 11

View File

@@ -1195,14 +1195,13 @@ local options = {
Conversion between "latin1", "unicode", "ucs-2", "ucs-4" and "utf-8"
is done internally by Vim, 'charconvert' is not used for this.
Also used for Unicode conversion.
Example: >vim
set charconvert=CharConvert()
fun CharConvert()
system("recode "
\ .. v:charconvert_from .. ".." .. v:charconvert_to
\ .. " <" .. v:fname_in .. " >" .. v:fname_out)
return v:shell_error
endfun
Example: >lua
vim.o.charconvert = function()
vim.fn.system(('recode %s..%s <%s >%s'):format(
vim.v.charconvert_from, vim.v.charconvert_to,
vim.v.fname_in, vim.v.fname_out))
return vim.v.shell_error
end
< The related Vim variables are:
v:charconvert_from name of the current encoding
v:charconvert_to name of the desired encoding
@@ -3449,20 +3448,20 @@ local options = {
Examples:
>vim
" Use glob()
func FindFuncGlob(cmdarg, cmdcomplete)
let pat = a:cmdcomplete ? $'{a:cmdarg}*' : a:cmdarg
return glob(pat, v:false, v:true)
endfunc
set findfunc=FindFuncGlob
>lua
-- Use vim.fn.glob()
vim.o.findfunc = function(cmdarg, cmdcomplete)
local pat = cmdcomplete and (cmdarg .. '*') or cmdarg
return vim.fn.glob(pat, false, true)
end
" Use the 'git ls-files' output
func FindGitFiles(cmdarg, cmdcomplete)
let fnames = systemlist('git ls-files')
return fnames->filter('v:val =~? a:cmdarg')
endfunc
set findfunc=FindGitFiles
-- Use the "git ls-files" output
vim.o.findfunc = function(cmdarg, cmdcomplete)
local fnames = vim.fn.systemlist('git ls-files')
return vim.tbl_filter(function(v)
return v:lower():find(cmdarg:lower(), 1, true) ~= nil
end, fnames)
end
<
]=],
full_name = 'findfunc',

View File

@@ -345,7 +345,7 @@ describe('vim.fs', function()
eq(nil, result['a/noaccess'])
end)
it('opts.normalize=false uses {path} literally', function()
it('plain=true', function()
mkdir('testdir')
mkdir('testdir/$XTEST_FS_DIR')
mkdir('testdir/expanded')
@@ -360,9 +360,9 @@ describe('vim.fs', function()
exec_lua(function()
vim.uv.os_setenv('XTEST_FS_DIR', 'expanded')
local out = {} ---@type table<string, string>[]
for i, normalize in ipairs({ true, false }) do
for i, plain in ipairs({ false, true }) do
out[i] = {}
for name, etype in vim.fs.dir('testdir/$XTEST_FS_DIR', { normalize = normalize }) do
for name, etype in vim.fs.dir('testdir/$XTEST_FS_DIR', { plain = plain }) do
out[i][name] = etype
end
end