aboutsummaryrefslogtreecommitdiff
path: root/runtime
diff options
context:
space:
mode:
Diffstat (limited to 'runtime')
-rw-r--r--runtime/autoload/gzip.vim5
-rw-r--r--runtime/autoload/spotbugs.vim250
-rw-r--r--runtime/compiler/eslint.vim7
-rw-r--r--runtime/compiler/javac.vim8
-rw-r--r--runtime/compiler/maven.vim2
-rw-r--r--runtime/compiler/pytest.vim103
-rw-r--r--runtime/compiler/spotbugs.vim189
-rw-r--r--runtime/doc/diagnostic.txt27
-rw-r--r--runtime/doc/lsp.txt41
-rw-r--r--runtime/doc/lua.txt31
-rw-r--r--runtime/doc/news.txt10
-rw-r--r--runtime/doc/options.txt1
-rw-r--r--runtime/doc/quickfix.txt119
-rw-r--r--runtime/ftplugin/java.vim138
-rw-r--r--runtime/ftplugin/python.vim11
-rw-r--r--runtime/lua/vim/_defaults.lua49
-rw-r--r--runtime/lua/vim/_meta/options.lua1
-rw-r--r--runtime/lua/vim/filetype.lua2
-rw-r--r--runtime/lua/vim/fs.lua12
-rw-r--r--runtime/lua/vim/loader.lua65
-rw-r--r--runtime/lua/vim/lsp.lua34
-rw-r--r--runtime/lua/vim/lsp/_folding_range.lua371
-rw-r--r--runtime/lua/vim/lsp/buf.lua4
-rw-r--r--runtime/lua/vim/lsp/client.lua4
-rw-r--r--runtime/lua/vim/lsp/protocol.lua7
-rw-r--r--runtime/lua/vim/lsp/util.lua6
-rw-r--r--runtime/lua/vim/shared.lua45
-rw-r--r--runtime/lua/vim/text.lua24
-rw-r--r--runtime/lua/vim/treesitter/_fold.lua53
-rw-r--r--runtime/syntax/po.vim56
30 files changed, 1517 insertions, 158 deletions
diff --git a/runtime/autoload/gzip.vim b/runtime/autoload/gzip.vim
index 26b1cda034..a6fbe2c336 100644
--- a/runtime/autoload/gzip.vim
+++ b/runtime/autoload/gzip.vim
@@ -1,6 +1,6 @@
" Vim autoload file for editing compressed files.
" Maintainer: The Vim Project <https://github.com/vim/vim>
-" Last Change: 2023 Aug 10
+" Last Change: 2024 Nov 25
" Former Maintainer: Bram Moolenaar <Bram@vim.org>
" These functions are used by the gzip plugin.
@@ -148,6 +148,9 @@ fun gzip#read(cmd)
else
let fname = escape(expand("%:r"), " \t\n*?[{`$\\%#'\"|!<")
endif
+ if filereadable(undofile(expand("%")))
+ exe "sil rundo " . fnameescape(undofile(expand("%")))
+ endif
if &verbose >= 8
execute "doau BufReadPost " . fname
else
diff --git a/runtime/autoload/spotbugs.vim b/runtime/autoload/spotbugs.vim
new file mode 100644
index 0000000000..9161395794
--- /dev/null
+++ b/runtime/autoload/spotbugs.vim
@@ -0,0 +1,250 @@
+" Default pre- and post-compiler actions for SpotBugs
+" Maintainers: @konfekt and @zzzyxwvut
+" Last Change: 2024 Nov 27
+
+let s:save_cpo = &cpo
+set cpo&vim
+
+if v:version > 900
+
+ function! spotbugs#DeleteClassFiles() abort
+ if !exists('b:spotbugs_class_files')
+ return
+ endif
+
+ for pathname in b:spotbugs_class_files
+ let classname = pathname =~# "^'.\\+\\.class'$"
+ \ ? eval(pathname)
+ \ : pathname
+
+ if classname =~# '\.class$' && filereadable(classname)
+ " Since v9.0.0795.
+ let octad = readblob(classname, 0, 8)
+
+ " Test the magic number and the major version number (45 for v1.0).
+ " Since v9.0.2027.
+ if len(octad) == 8 && octad[0 : 3] == 0zcafe.babe &&
+ \ or((octad[6] << 8), octad[7]) >= 45
+ echomsg printf('Deleting %s: %d', classname, delete(classname))
+ endif
+ endif
+ endfor
+
+ let b:spotbugs_class_files = []
+ endfunction
+
+else
+
+ function! s:DeleteClassFilesWithNewLineCodes(classname) abort
+ " The distribution of "0a"s in class file versions 2560 and 2570:
+ "
+ " 0zca.fe.ba.be.00.00.0a.00 0zca.fe.ba.be.00.00.0a.0a
+ " 0zca.fe.ba.be.00.0a.0a.00 0zca.fe.ba.be.00.0a.0a.0a
+ " 0zca.fe.ba.be.0a.00.0a.00 0zca.fe.ba.be.0a.00.0a.0a
+ " 0zca.fe.ba.be.0a.0a.0a.00 0zca.fe.ba.be.0a.0a.0a.0a
+ let numbers = [0, 0, 0, 0, 0, 0, 0, 0]
+ let offset = 0
+ let lines = readfile(a:classname, 'b', 4)
+
+ " Track NL byte counts to handle files of less than 8 bytes.
+ let nl_cnt = len(lines)
+ " Track non-NL byte counts for "0zca.fe.ba.be.0a.0a.0a.0a".
+ let non_nl_cnt = 0
+
+ for line in lines
+ for idx in range(strlen(line))
+ " Remap NLs to Nuls.
+ let numbers[offset] = (line[idx] == "\n") ? 0 : char2nr(line[idx]) % 256
+ let non_nl_cnt += 1
+ let offset += 1
+
+ if offset > 7
+ break
+ endif
+ endfor
+
+ let nl_cnt -= 1
+
+ if offset > 7 || (nl_cnt < 1 && non_nl_cnt > 4)
+ break
+ endif
+
+ " Reclaim NLs.
+ let numbers[offset] = 10
+ let offset += 1
+
+ if offset > 7
+ break
+ endif
+ endfor
+
+ " Test the magic number and the major version number (45 for v1.0).
+ if offset > 7 && numbers[0] == 0xca && numbers[1] == 0xfe &&
+ \ numbers[2] == 0xba && numbers[3] == 0xbe &&
+ \ (numbers[6] * 256 + numbers[7]) >= 45
+ echomsg printf('Deleting %s: %d', a:classname, delete(a:classname))
+ endif
+ endfunction
+
+ function! spotbugs#DeleteClassFiles() abort
+ if !exists('b:spotbugs_class_files')
+ return
+ endif
+
+ let encoding = &encoding
+
+ try
+ set encoding=latin1
+
+ for pathname in b:spotbugs_class_files
+ let classname = pathname =~# "^'.\\+\\.class'$"
+ \ ? eval(pathname)
+ \ : pathname
+
+ if classname =~# '\.class$' && filereadable(classname)
+ let line = get(readfile(classname, 'b', 1), 0, '')
+ let length = strlen(line)
+
+ " Test the magic number and the major version number (45 for v1.0).
+ if length > 3 && line[0 : 3] == "\xca\xfe\xba\xbe"
+ if length > 7 && ((line[6] == "\n" ? 0 : char2nr(line[6]) % 256) * 256 +
+ \ (line[7] == "\n" ? 0 : char2nr(line[7]) % 256)) >= 45
+ echomsg printf('Deleting %s: %d', classname, delete(classname))
+ else
+ call s:DeleteClassFilesWithNewLineCodes(classname)
+ endif
+ endif
+ endif
+ endfor
+ finally
+ let &encoding = encoding
+ endtry
+
+ let b:spotbugs_class_files = []
+ endfunction
+
+endif
+
+function! spotbugs#DefaultPostCompilerAction() abort
+ " Since v7.4.191.
+ make %:S
+endfunction
+
+" Look for "spotbugs#compiler" in "ftplugin/java.vim".
+let s:compiler = exists('spotbugs#compiler') ? spotbugs#compiler : ''
+let s:readable = filereadable($VIMRUNTIME . '/compiler/' . s:compiler . '.vim')
+
+if s:readable && s:compiler ==# 'maven' && executable('mvn')
+
+ function! spotbugs#DefaultPreCompilerAction() abort
+ call spotbugs#DeleteClassFiles()
+ compiler maven
+ make compile
+ endfunction
+
+ function! spotbugs#DefaultPreCompilerTestAction() abort
+ call spotbugs#DeleteClassFiles()
+ compiler maven
+ make test-compile
+ endfunction
+
+ function! spotbugs#DefaultProperties() abort
+ return {
+ \ 'PreCompilerAction':
+ \ function('spotbugs#DefaultPreCompilerAction'),
+ \ 'PreCompilerTestAction':
+ \ function('spotbugs#DefaultPreCompilerTestAction'),
+ \ 'PostCompilerAction':
+ \ function('spotbugs#DefaultPostCompilerAction'),
+ \ 'sourceDirPath': 'src/main/java',
+ \ 'classDirPath': 'target/classes',
+ \ 'testSourceDirPath': 'src/test/java',
+ \ 'testClassDirPath': 'target/test-classes',
+ \ }
+ endfunction
+
+ unlet s:readable s:compiler
+elseif s:readable && s:compiler ==# 'ant' && executable('ant')
+
+ function! spotbugs#DefaultPreCompilerAction() abort
+ call spotbugs#DeleteClassFiles()
+ compiler ant
+ make compile
+ endfunction
+
+ function! spotbugs#DefaultPreCompilerTestAction() abort
+ call spotbugs#DeleteClassFiles()
+ compiler ant
+ make compile-test
+ endfunction
+
+ function! spotbugs#DefaultProperties() abort
+ return {
+ \ 'PreCompilerAction':
+ \ function('spotbugs#DefaultPreCompilerAction'),
+ \ 'PreCompilerTestAction':
+ \ function('spotbugs#DefaultPreCompilerTestAction'),
+ \ 'PostCompilerAction':
+ \ function('spotbugs#DefaultPostCompilerAction'),
+ \ 'sourceDirPath': 'src',
+ \ 'classDirPath': 'build/classes',
+ \ 'testSourceDirPath': 'test',
+ \ 'testClassDirPath': 'build/test/classes',
+ \ }
+ endfunction
+
+ unlet s:readable s:compiler
+elseif s:readable && s:compiler ==# 'javac' && executable('javac')
+
+ function! spotbugs#DefaultPreCompilerAction() abort
+ call spotbugs#DeleteClassFiles()
+ compiler javac
+
+ if get(b:, 'javac_makeprg_params', get(g:, 'javac_makeprg_params', '')) =~ '\s@\S'
+ " Read options and filenames from @options [@sources ...].
+ make
+ else
+ " Let Javac figure out what files to compile.
+ execute 'make ' . join(map(filter(copy(v:argv),
+ \ "v:val =~# '\\.java\\=$'"),
+ \ 'shellescape(v:val)'), ' ')
+ endif
+ endfunction
+
+ function! spotbugs#DefaultPreCompilerTestAction() abort
+ call spotbugs#DefaultPreCompilerAction()
+ endfunction
+
+ function! spotbugs#DefaultProperties() abort
+ return {
+ \ 'PreCompilerAction':
+ \ function('spotbugs#DefaultPreCompilerAction'),
+ \ 'PreCompilerTestAction':
+ \ function('spotbugs#DefaultPreCompilerTestAction'),
+ \ 'PostCompilerAction':
+ \ function('spotbugs#DefaultPostCompilerAction'),
+ \ }
+ endfunction
+
+ unlet s:readable s:compiler
+else
+
+ function! spotbugs#DefaultPreCompilerAction() abort
+ echomsg printf('Not supported: "%s"', s:compiler)
+ endfunction
+
+ function! spotbugs#DefaultPreCompilerTestAction() abort
+ call spotbugs#DefaultPreCompilerAction()
+ endfunction
+
+ function! spotbugs#DefaultProperties() abort
+ return {}
+ endfunction
+
+ unlet s:readable
+endif
+
+let &cpo = s:save_cpo
+unlet s:save_cpo
+
+" vim: set foldmethod=syntax shiftwidth=2 expandtab:
diff --git a/runtime/compiler/eslint.vim b/runtime/compiler/eslint.vim
index db7a665991..0414817900 100644
--- a/runtime/compiler/eslint.vim
+++ b/runtime/compiler/eslint.vim
@@ -1,13 +1,12 @@
" Vim compiler file
" Compiler: ESLint for JavaScript
" Maintainer: Romain Lafourcade <romainlafourcade@gmail.com>
-" Last Change: 2020 August 20
-" 2024 Apr 03 by The Vim Project (removed :CompilerSet definition)
+" Last Change: 2024 Nov 30
if exists("current_compiler")
finish
endif
let current_compiler = "eslint"
-CompilerSet makeprg=npx\ eslint\ --format\ compact
-CompilerSet errorformat=%f:\ line\ %l\\,\ col\ %c\\,\ %m,%-G%.%#
+CompilerSet makeprg=npx\ eslint\ --format\ stylish
+CompilerSet errorformat=%-P%f,\%\\s%#%l:%c\ %#\ %trror\ \ %m,\%\\s%#%l:%c\ %#\ %tarning\ \ %m,\%-Q,\%-G%.%#,
diff --git a/runtime/compiler/javac.vim b/runtime/compiler/javac.vim
index 9bd4cdf270..53cd772ed8 100644
--- a/runtime/compiler/javac.vim
+++ b/runtime/compiler/javac.vim
@@ -1,7 +1,7 @@
" Vim compiler file
" Compiler: Java Development Kit Compiler
" Maintainer: Doug Kearns <dougkearns@gmail.com>
-" Last Change: 2024 Jun 14
+" Last Change: 2024 Nov 19 (enable local javac_makeprg_params)
if exists("current_compiler")
finish
@@ -11,11 +11,7 @@ let current_compiler = "javac"
let s:cpo_save = &cpo
set cpo&vim
-if exists("g:javac_makeprg_params")
- execute $'CompilerSet makeprg=javac\ {escape(g:javac_makeprg_params, ' \|"')}'
-else
- CompilerSet makeprg=javac
-endif
+execute $'CompilerSet makeprg=javac\ {escape(get(b:, 'javac_makeprg_params', get(g:, 'javac_makeprg_params', '')), ' \|"')}'
CompilerSet errorformat=%E%f:%l:\ error:\ %m,
\%W%f:%l:\ warning:\ %m,
diff --git a/runtime/compiler/maven.vim b/runtime/compiler/maven.vim
index ef8d8a6fb2..72e74e301d 100644
--- a/runtime/compiler/maven.vim
+++ b/runtime/compiler/maven.vim
@@ -14,7 +14,7 @@ if exists("current_compiler")
endif
let current_compiler = "maven"
-CompilerSet makeprg=mvn\ --batch-mode
+execute $'CompilerSet makeprg=mvn\ --batch-mode\ {escape(get(b:, 'maven_makeprg_params', get(g:, 'maven_makeprg_params', '')), ' \|"')}'
" Error message for POM
CompilerSet errorformat=[FATAL]\ Non-parseable\ POM\ %f:\ %m%\\s%\\+@%.%#line\ %l\\,\ column\ %c%.%#,
diff --git a/runtime/compiler/pytest.vim b/runtime/compiler/pytest.vim
new file mode 100644
index 0000000000..7fc189932c
--- /dev/null
+++ b/runtime/compiler/pytest.vim
@@ -0,0 +1,103 @@
+" Vim compiler file
+" Compiler: Pytest (Python testing framework)
+" Maintainer: @Konfekt and @mgedmin
+" Last Change: 2024 Nov 28
+
+if exists("current_compiler") | finish | endif
+let current_compiler = "pytest"
+
+let s:cpo_save = &cpo
+set cpo&vim
+
+" CompilerSet makeprg=pytest
+if has('unix')
+ execute $'CompilerSet makeprg=/usr/bin/env\ PYTHONWARNINGS=ignore\ pytest\ {escape(get(b:, 'pytest_makeprg_params', get(g:, 'pytest_makeprg_params', '--tb=short --quiet')), ' \|"')}'
+elseif has('win32')
+ execute $'CompilerSet makeprg=set\ PYTHONWARNINGS=ignore\ &&\ pytest\ {escape(get(b:, 'pytest_makeprg_params', get(g:, 'pytest_makeprg_params', '--tb=short --quiet')), ' \|"')}'
+else
+ CompilerSet makeprg=pytest\ --tb=short\ --quiet
+ execute $'CompilerSet makeprg=pytest\ {escape(get(b:, 'pytest_makeprg_params', get(g:, 'pytest_makeprg_params', '--tb=short --quiet')), ' \|"')}'
+endif
+
+" Pytest syntax errors {{{2
+
+" Reset error format so that sourcing .vimrc again and again doesn't grow it
+" without bounds
+setlocal errorformat&
+
+" For the record, the default errorformat is this:
+"
+" %*[^"]"%f"%*\D%l: %m
+" "%f"%*\D%l: %m
+" %-G%f:%l: (Each undeclared identifier is reported only once
+" %-G%f:%l: for each function it appears in.)
+" %-GIn file included from %f:%l:%c:
+" %-GIn file included from %f:%l:%c\,
+" %-GIn file included from %f:%l:%c
+" %-GIn file included from %f:%l
+" %-G%*[ ]from %f:%l:%c
+" %-G%*[ ]from %f:%l:
+" %-G%*[ ]from %f:%l\,
+" %-G%*[ ]from %f:%l
+" %f:%l:%c:%m
+" %f(%l):%m
+" %f:%l:%m
+" "%f"\, line %l%*\D%c%*[^ ] %m
+" %D%*\a[%*\d]: Entering directory %*[`']%f'
+" %X%*\a[%*\d]: Leaving directory %*[`']%f'
+" %D%*\a: Entering directory %*[`']%f'
+" %X%*\a: Leaving directory %*[`']%f'
+" %DMaking %*\a in %f
+" %f|%l| %m
+"
+" and sometimes it misfires, so let's fix it up a bit
+" (TBH I don't even know what compiler produces filename(lineno) so why even
+" have it?)
+setlocal errorformat-=%f(%l):%m
+
+" Sometimes Vim gets confused about ISO-8601 timestamps and thinks they're
+" filenames; this is a big hammer that ignores anything filename-like on lines
+" that start with at least two spaces, possibly preceded by a number and
+" optional punctuation
+setlocal errorformat^=%+G%\\d%#%.%\\=\ \ %.%#
+
+" Similar, but when the entire line starts with a date
+setlocal errorformat^=%+G\\d\\d\\d\\d-\\d\\d-\\d\\d\ \\d\\d:\\d\\d%.%#
+
+" make: *** [Makefile:14: target] Error 1
+setlocal errorformat^=%+Gmake:\ ***\ %.%#
+
+" FAILED tests.py::test_with_params[YYYY-MM-DD:HH:MM:SS] - Exception: bla bla
+setlocal errorformat^=%+GFAILED\ %.%#
+
+" AssertionError: assert ...YYYY-MM-DD:HH:MM:SS...
+setlocal errorformat^=%+GAssertionError:\ %.%#
+
+" --- /path/to/file:before YYYY-MM-DD HH:MM:SS.ssssss
+setlocal errorformat^=---%f:%m
+
+" +++ /path/to/file:before YYYY-MM-DD HH:MM:SS.ssssss
+setlocal errorformat^=+++%f:%m
+
+" Sometimes pytest prepends an 'E' marker at the beginning of a traceback line
+setlocal errorformat+=E\ %#File\ \"%f\"\\,\ line\ %l%.%#
+
+" Python tracebacks (unittest + doctest output) {{{2
+
+" This collapses the entire traceback into just the last file+lineno,
+" which is convenient when you want to jump to the line that failed (and not
+" the top-level entry point), but it makes it impossible to see the full
+" traceback, which sucks.
+""setlocal errorformat+=
+"" \File\ \"%f\"\\,\ line\ %l%.%#,
+"" \%C\ %.%#,
+"" \%-A\ \ File\ \"unittest%.py\"\\,\ line\ %.%#,
+"" \%-A\ \ File\ \"%f\"\\,\ line\ 0%.%#,
+"" \%A\ \ File\ \"%f\"\\,\ line\ %l%.%#,
+"" \%Z%[%^\ ]%\\@=%m
+setlocal errorformat+=File\ \"%f\"\\,\ line\ %l\\,%#%m
+
+exe 'CompilerSet errorformat='..escape(&l:errorformat, ' \|"')
+
+let &cpo = s:cpo_save
+unlet s:cpo_save
diff --git a/runtime/compiler/spotbugs.vim b/runtime/compiler/spotbugs.vim
new file mode 100644
index 0000000000..72a5084976
--- /dev/null
+++ b/runtime/compiler/spotbugs.vim
@@ -0,0 +1,189 @@
+" Vim compiler file
+" Compiler: Spotbugs (Java static checker; needs javac compiled classes)
+" Maintainer: @konfekt and @zzzyxwvut
+" Last Change: 2024 Nov 27
+
+if exists('g:current_compiler') || bufname() !~# '\.java\=$' || wordcount().chars < 9
+ finish
+endif
+
+let s:cpo_save = &cpo
+set cpo&vim
+
+" Unfortunately Spotbugs does not output absolute paths, so you need to
+" pass the directory of the files being checked as `-sourcepath` parameter.
+" The regex, auxpath and glob try to include all dependent classes of the
+" current buffer. See https://github.com/spotbugs/spotbugs/issues/856
+
+" FIXME: When "search()" is used with the "e" flag, it makes no _further_
+" progress after claiming an EOL match (i.e. "\_" or "\n", but not "$").
+" XXX: Omit anonymous class declarations
+let s:keywords = '\C\<\%(\.\@1<!class\|@\=interface\|enum\|record\|package\)\%(\s\|$\)'
+let s:type_names = '\C\<\%(\.\@1<!class\|@\=interface\|enum\|record\)\s*\(\K\k*\)\>'
+" Capture ";" for counting a class file directory (see s:package_dir_heads below)
+let s:package_names = '\C\<package\s*\(\K\%(\k*\.\=\)\+;\)'
+let s:package = ''
+
+if has('syntax') && exists('g:syntax_on') && exists('b:current_syntax') &&
+ \ b:current_syntax == 'java' && hlexists('javaClassDecl')
+
+ function! s:GetDeclaredTypeNames() abort
+ if bufname() =~# '\<\%(module\|package\)-info\.java\=$'
+ return [expand('%:t:r')]
+ endif
+ defer execute('silent! normal! g``')
+ call cursor(1, 1)
+ let type_names = []
+ let lnum = search(s:keywords, 'eW')
+ while lnum > 0
+ let name_attr = synIDattr(synID(lnum, (col('.') - 1), 0), 'name')
+ if name_attr ==# 'javaClassDecl'
+ let tokens = matchlist(getline(lnum)..getline(lnum + 1), s:type_names)
+ if !empty(tokens) | call add(type_names, tokens[1]) | endif
+ elseif name_attr ==# 'javaExternal'
+ let tokens = matchlist(getline(lnum)..getline(lnum + 1), s:package_names)
+ if !empty(tokens) | let s:package = tokens[1] | endif
+ endif
+ let lnum = search(s:keywords, 'eW')
+ endwhile
+ return type_names
+ endfunction
+
+else
+ function! s:GetDeclaredTypeNames() abort
+ if bufname() =~# '\<\%(module\|package\)-info\.java\=$'
+ return [expand('%:t:r')]
+ endif
+ " Undo the unsetting of &hls, see below
+ if &hls
+ defer execute('set hls')
+ endif
+ " Possibly restore the current values for registers '"' and "y", see below
+ defer call('setreg', ['"', getreg('"'), getregtype('"')])
+ defer call('setreg', ['y', getreg('y'), getregtype('y')])
+ defer execute('silent bwipeout')
+ " Copy buffer contents for modification
+ silent %y y
+ new
+ " Apply ":help scratch-buffer" effects and match "$" in Java (generated)
+ " type names (see s:type_names)
+ setlocal iskeyword+=$ buftype=nofile bufhidden=hide noswapfile nohls
+ 0put y
+ " Discard text blocks and strings
+ silent keeppatterns %s/\\\@<!"""\_.\{-}\\\@<!"""\|\\"//ge
+ silent keeppatterns %s/".*"//ge
+ " Discard comments
+ silent keeppatterns %s/\/\/.\+$//ge
+ silent keeppatterns %s/\/\*\_.\{-}\*\///ge
+ call cursor(1, 1)
+ let type_names = []
+ let lnum = search(s:keywords, 'eW')
+ while lnum > 0
+ let line = getline(lnum)
+ if line =~# '\<package\>'
+ let tokens = matchlist(line..getline(lnum + 1), s:package_names)
+ if !empty(tokens) | let s:package = tokens[1] | endif
+ else
+ let tokens = matchlist(line..getline(lnum + 1), s:type_names)
+ if !empty(tokens) | call add(type_names, tokens[1]) | endif
+ endif
+ let lnum = search(s:keywords, 'eW')
+ endwhile
+ return type_names
+ endfunction
+endif
+
+if has('win32')
+
+ function! s:GlobClassFiles(src_type_name) abort
+ return glob(a:src_type_name..'$*.class', 1, 1)
+ endfunction
+
+else
+ function! s:GlobClassFiles(src_type_name) abort
+ return glob(a:src_type_name..'\$*.class', 1, 1)
+ endfunction
+endif
+
+if exists('g:spotbugs_properties') &&
+ \ (has_key(g:spotbugs_properties, 'sourceDirPath') &&
+ \ has_key(g:spotbugs_properties, 'classDirPath')) ||
+ \ (has_key(g:spotbugs_properties, 'testSourceDirPath') &&
+ \ has_key(g:spotbugs_properties, 'testClassDirPath'))
+
+function! s:FindClassFiles(src_type_name) abort
+ let class_files = []
+ " Match pairwise the components of source and class pathnames
+ for [src_dir, bin_dir] in filter([
+ \ [get(g:spotbugs_properties, 'sourceDirPath', ''),
+ \ get(g:spotbugs_properties, 'classDirPath', '')],
+ \ [get(g:spotbugs_properties, 'testSourceDirPath', ''),
+ \ get(g:spotbugs_properties, 'testClassDirPath', '')]],
+ \ '!(empty(v:val[0]) || empty(v:val[1]))')
+ " Since only the rightmost "src" is sought, while there can be any number of
+ " such filenames, no "fnamemodify(a:src_type_name, ':p:s?src?bin?')" is used
+ let tail_idx = strridx(a:src_type_name, src_dir)
+ " No such directory or no such inner type (i.e. without "$")
+ if tail_idx < 0 | continue | endif
+ " Substitute "bin_dir" for the rightmost "src_dir"
+ let candidate_type_name = strpart(a:src_type_name, 0, tail_idx)..
+ \ bin_dir..
+ \ strpart(a:src_type_name, (tail_idx + strlen(src_dir)))
+ for candidate in insert(s:GlobClassFiles(candidate_type_name),
+ \ candidate_type_name..'.class')
+ if filereadable(candidate) | call add(class_files, shellescape(candidate)) | endif
+ endfor
+ if !empty(class_files) | break | endif
+ endfor
+ return class_files
+endfunction
+
+else
+function! s:FindClassFiles(src_type_name) abort
+ let class_files = []
+ for candidate in insert(s:GlobClassFiles(a:src_type_name),
+ \ a:src_type_name..'.class')
+ if filereadable(candidate) | call add(class_files, shellescape(candidate)) | endif
+ endfor
+ return class_files
+endfunction
+endif
+
+function! s:CollectClassFiles() abort
+ " Get a platform-independent pathname prefix, cf. "expand('%:p:h')..'/'"
+ let pathname = expand('%:p')
+ let tail_idx = strridx(pathname, expand('%:t'))
+ let src_pathname = strpart(pathname, 0, tail_idx)
+ let all_class_files = []
+ " Get all type names in the current buffer and let the filename globbing
+ " discover inner type names from arbitrary type names
+ for type_name in s:GetDeclaredTypeNames()
+ call extend(all_class_files, s:FindClassFiles(src_pathname..type_name))
+ endfor
+ return all_class_files
+endfunction
+
+" Expose class files for removal etc.
+let b:spotbugs_class_files = s:CollectClassFiles()
+let s:package_dir_heads = repeat(':h', (1 + strlen(substitute(s:package, '[^.;]', '', 'g'))))
+let g:current_compiler = 'spotbugs'
+" CompilerSet makeprg=spotbugs
+let &l:makeprg = 'spotbugs'..(has('win32') ? '.bat' : '')..' '..
+ \ get(b:, 'spotbugs_makeprg_params', get(g:, 'spotbugs_makeprg_params', '-workHard -experimental'))..
+ \ ' -textui -emacs -auxclasspath %:p'..s:package_dir_heads..':S -sourcepath %:p'..s:package_dir_heads..':S '..
+ \ join(b:spotbugs_class_files, ' ')
+" Emacs expects doubled line numbers
+setlocal errorformat=%f:%l:%*[0-9]\ %m,%f:-%*[0-9]:-%*[0-9]\ %m
+
+" " This compiler is meant to be used for a single buffer only
+" exe 'CompilerSet makeprg='..escape(&l:makeprg, ' \|"')
+" exe 'CompilerSet errorformat='..escape(&l:errorformat, ' \|"')
+
+delfunction s:CollectClassFiles
+delfunction s:FindClassFiles
+delfunction s:GlobClassFiles
+delfunction s:GetDeclaredTypeNames
+let &cpo = s:cpo_save
+unlet s:package_dir_heads s:package s:package_names s:type_names s:keywords s:cpo_save
+
+" vim: set foldmethod=syntax shiftwidth=2 expandtab:
diff --git a/runtime/doc/diagnostic.txt b/runtime/doc/diagnostic.txt
index 9ccc3102b6..eaa3681caa 100644
--- a/runtime/doc/diagnostic.txt
+++ b/runtime/doc/diagnostic.txt
@@ -163,6 +163,33 @@ show a sign for the highest severity diagnostic on a given line: >lua
}
<
+ *diagnostic-loclist-example*
+Whenever the |location-list| is opened, the following `show` handler will show
+the most recent diagnostics: >lua
+
+ vim.diagnostic.handlers.loclist = {
+ show = function(_, _, _, opts)
+ -- Generally don't want it to open on every update
+ opts.loclist.open = opts.loclist.open or false
+ local winid = vim.api.nvim_get_current_win()
+ vim.diagnostic.setloclist(opts.loclist)
+ vim.api.nvim_set_current_win(winid)
+ end
+ }
+<
+
+The handler accepts the same options as |vim.diagnostic.setloclist()| and can be
+configured using |vim.diagnostic.config()|: >lua
+
+ -- Open the location list on every diagnostic change (warnings/errors only).
+ vim.diagnostic.config({
+ loclist = {
+ open = true,
+ severity = { min = vim.diagnostic.severity.WARN },
+ }
+ })
+<
+
==============================================================================
HIGHLIGHTS *diagnostic-highlights*
diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt
index 350edc068f..f7157df0f2 100644
--- a/runtime/doc/lsp.txt
+++ b/runtime/doc/lsp.txt
@@ -204,6 +204,7 @@ won't run if your server doesn't support them.
- `'textDocument/diagnostic'`
- `'textDocument/documentHighlight'`
- `'textDocument/documentSymbol'`
+- `'textDocument/foldingRange'`
- `'textDocument/formatting'`
- `'textDocument/hover'`
- `'textDocument/implementation'`
@@ -697,6 +698,35 @@ commands *vim.lsp.commands*
The second argument is the `ctx` of |lsp-handler|
+foldclose({kind}, {winid}) *vim.lsp.foldclose()*
+ Close all {kind} of folds in the the window with {winid}.
+
+ To automatically fold imports when opening a file, you can use an autocmd: >lua
+ vim.api.nvim_create_autocmd('LspNotify', {
+ callback = function(args)
+ if args.data.method == 'textDocument/didOpen' then
+ vim.lsp.foldclose('imports', vim.fn.bufwinid(args.buf))
+ end
+ end,
+ })
+<
+
+ Parameters: ~
+ • {kind} (`lsp.FoldingRangeKind`) Kind to close, one of "comment",
+ "imports" or "region".
+ • {winid} (`integer?`) Defaults to the current window.
+
+foldexpr({lnum}) *vim.lsp.foldexpr()*
+ Provides an interface between the built-in client and a `foldexpr`
+ function.
+
+ Parameters: ~
+ • {lnum} (`integer`) line number
+
+foldtext() *vim.lsp.foldtext()*
+ Provides a `foldtext` function that shows the `collapsedText` retrieved,
+ defaults to the first folded line if `collapsedText` is not provided.
+
formatexpr({opts}) *vim.lsp.formatexpr()*
Provides an interface between the built-in client and a `formatexpr`
function.
@@ -975,7 +1005,7 @@ Lua module: vim.lsp.client *lsp-client*
• {dynamic_capabilities} (`lsp.DynamicCapabilities`)
• {request} (`fun(self: vim.lsp.Client, method: string, params: table?, handler: lsp.Handler?, bufnr: integer?): boolean, integer?`)
See |Client:request()|.
- • {request_sync} (`fun(self: vim.lsp.Client, method: string, params: table, timeout_ms: integer?, bufnr: integer): {err: lsp.ResponseError?, result:any}?, string?`)
+ • {request_sync} (`fun(self: vim.lsp.Client, method: string, params: table, timeout_ms: integer?, bufnr: integer?): {err: lsp.ResponseError?, result:any}?, string?`)
See |Client:request_sync()|.
• {notify} (`fun(self: vim.lsp.Client, method: string, params: table?): boolean`)
See |Client:notify()|.
@@ -1179,7 +1209,7 @@ Client:request({method}, {params}, {handler}, {bufnr})
• {method} (`string`) LSP method name.
• {params} (`table?`) LSP request params.
• {handler} (`lsp.Handler?`) Response |lsp-handler| for this method.
- • {bufnr} (`integer?`) Buffer handle. 0 for current (default).
+ • {bufnr} (`integer?`) (default: 0) Buffer handle, or 0 for current.
Return (multiple): ~
(`boolean`) status indicates whether the request was successful. If it
@@ -1201,7 +1231,8 @@ Client:request_sync({method}, {params}, {timeout_ms}, {bufnr})
• {params} (`table`) LSP request params.
• {timeout_ms} (`integer?`) Maximum time in milliseconds to wait for a
result. Defaults to 1000
- • {bufnr} (`integer`) Buffer handle (0 for current).
+ • {bufnr} (`integer?`) (default: 0) Buffer handle, or 0 for
+ current.
Return (multiple): ~
(`{err: lsp.ResponseError?, result:any}?`) `result` and `err` from the
@@ -1385,7 +1416,7 @@ format({opts}) *vim.lsp.buf.format()*
predicate are included. Example: >lua
-- Never request typescript-language-server for formatting
vim.lsp.buf.format {
- filter = function(client) return client.name ~= "tsserver" end
+ filter = function(client) return client.name ~= "ts_ls" end
}
<
• {async}? (`boolean`, default: false) If true the method
@@ -1448,7 +1479,7 @@ references({context}, {opts}) *vim.lsp.buf.references()*
window.
Parameters: ~
- • {context} (`table?`) Context for the request
+ • {context} (`lsp.ReferenceContext?`) Context for the request
• {opts} (`vim.lsp.ListOpts?`) See |vim.lsp.ListOpts|.
See also: ~
diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt
index 4d4a51872a..c3dddaf888 100644
--- a/runtime/doc/lua.txt
+++ b/runtime/doc/lua.txt
@@ -2485,22 +2485,24 @@ vim.validate({name}, {value}, {validator}, {optional}, {message})
==============================================================================
Lua module: vim.loader *vim.loader*
-vim.loader.disable() *vim.loader.disable()*
+vim.loader.enable({enable}) *vim.loader.enable()*
WARNING: This feature is experimental/unstable.
- Disables the experimental Lua module loader:
- • removes the loaders
- • adds the default Nvim loader
-
-vim.loader.enable() *vim.loader.enable()*
- WARNING: This feature is experimental/unstable.
+ Enables or disables the experimental Lua module loader:
- Enables the experimental Lua module loader:
- • overrides loadfile
+ Enable (`enable=true`):
+ • overrides |loadfile()|
• adds the Lua loader using the byte-compilation cache
• adds the libs loader
• removes the default Nvim loader
+ Disable (`enable=false`):
+ • removes the loaders
+ • adds the default Nvim loader
+
+ Parameters: ~
+ • {enable} (`boolean?`) true/nil to enable, false to disable
+
vim.loader.find({modname}, {opts}) *vim.loader.find()*
WARNING: This feature is experimental/unstable.
@@ -2924,6 +2926,17 @@ vim.keymap.set({mode}, {lhs}, {rhs}, {opts}) *vim.keymap.set()*
==============================================================================
Lua module: vim.fs *vim.fs*
+
+ *vim.fs.exists()*
+Use |uv.fs_stat()| to check a file's type, and whether it exists.
+
+Example: >lua
+ if vim.uv.fs_stat(file) then
+ vim.print("file exists")
+ end
+<
+
+
vim.fs.basename({file}) *vim.fs.basename()*
Return the basename of the given path
diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt
index 58ab7ef44c..ad3f2c0a6a 100644
--- a/runtime/doc/news.txt
+++ b/runtime/doc/news.txt
@@ -11,13 +11,17 @@ For changes in the previous release, see |news-0.10|.
Type |gO| to see the table of contents.
==============================================================================
-BREAKING CHANGES IN HEAD *news-breaking-dev*
+BREAKING CHANGES IN HEAD OR EXPERIMENTAL *news-breaking-dev*
====== Remove this section before release. ======
The following changes to UNRELEASED features were made during the development
cycle (Nvim HEAD, the "master" branch).
+EXPERIMENTS
+
+• Removed `vim.loader.disable()`. Use `vim.loader.enable(false)` instead.
+
OPTIONS
• 'jumpoptions' flag "unload" has been renamed to "clean".
@@ -226,6 +230,8 @@ LSP
• |vim.lsp.buf.hover()| now highlights hover ranges using the
|hl-LspReferenceTarget| highlight group.
• Functions in |vim.lsp.Client| can now be called as methods.
+• Implemented LSP folding: |vim.lsp.foldexpr()|
+ https://microsoft.github.io/language-server-protocol/specification/#textDocument_foldingRange
LUA
@@ -280,6 +286,8 @@ TUI
:lua =vim.api.nvim_get_chan_info(vim.api.nvim_list_uis()[1].chan)
• |log| messages written by the builtin UI client (TUI, |--remote-ui|) are
now prefixed with "ui" instead of "?".
+• The TUI will re-query the terminal's background color when a theme update
+ notification is received and Nvim will update 'background' accordingly.
UI
diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt
index ea56633c77..c5f21c64a2 100644
--- a/runtime/doc/options.txt
+++ b/runtime/doc/options.txt
@@ -5917,6 +5917,7 @@ A jump table for the options with a short description can be found at |Q_op|.
All fields except the {item} are optional. A single percent sign can
be given as "%%".
+ *stl-%!*
When the option starts with "%!" then it is used as an expression,
evaluated and the result is used as the option value. Example: >vim
set statusline=%!MyStatusLine()
diff --git a/runtime/doc/quickfix.txt b/runtime/doc/quickfix.txt
index b3399b2766..a291c0277d 100644
--- a/runtime/doc/quickfix.txt
+++ b/runtime/doc/quickfix.txt
@@ -1317,9 +1317,117 @@ g:compiler_gcc_ignore_unmatched_lines
JAVAC *compiler-javac*
Commonly used compiler options can be added to 'makeprg' by setting the
-g:javac_makeprg_params variable. For example: >
+b/g:javac_makeprg_params variable. For example: >
+
let g:javac_makeprg_params = "-Xlint:all -encoding utf-8"
-<
+
+MAVEN *compiler-maven*
+
+Commonly used compiler options can be added to 'makeprg' by setting the
+b/g:maven_makeprg_params variable. For example: >
+
+ let g:maven_makeprg_params = "-DskipTests -U -X"
+
+SPOTBUGS *compiler-spotbugs*
+
+SpotBugs is a static analysis tool that can be used to find bugs in Java.
+It scans the Java bytecode of all classes in the currently open buffer.
+(Therefore, `:compiler! spotbugs` is not supported.)
+
+Commonly used compiler options can be added to 'makeprg' by setting the
+"b:" or "g:spotbugs_makeprg_params" variable. For example: >
+
+ let b:spotbugs_makeprg_params = "-longBugCodes -effort:max -low"
+
+The global default is "-workHard -experimental".
+
+By default, the class files are searched in the directory where the source
+files are placed. However, typical Java projects use distinct directories
+for source files and class files. To make both known to SpotBugs, assign
+their paths (distinct and relative to their common root directory) to the
+following properties (using the example of a common Maven project): >
+
+ let g:spotbugs_properties = {
+ \ 'sourceDirPath': 'src/main/java',
+ \ 'classDirPath': 'target/classes',
+ \ 'testSourceDirPath': 'src/test/java',
+ \ 'testClassDirPath': 'target/test-classes',
+ \ }
+
+Note that values for the path keys describe only for SpotBugs where to look
+for files; refer to the documentation for particular compiler plugins for more
+information.
+
+The default pre- and post-compiler actions are provided for Ant, Maven, and
+Javac compiler plugins and can be selected by assigning the name of a compiler
+plugin to the "compiler" key: >
+
+ let g:spotbugs_properties = {
+ \ 'compiler': 'maven',
+ \ }
+
+This single setting is essentially equivalent to all the settings below, with
+the exception made for the "PreCompilerAction" and "PreCompilerTestAction"
+values: their listed |Funcref|s will obtain no-op implementations whereas the
+implicit Funcrefs of the "compiler" key will obtain the requested defaults if
+available. >
+
+ let g:spotbugs_properties = {
+ \ 'PreCompilerAction':
+ \ function('spotbugs#DefaultPreCompilerAction'),
+ \ 'PreCompilerTestAction':
+ \ function('spotbugs#DefaultPreCompilerTestAction'),
+ \ 'PostCompilerAction':
+ \ function('spotbugs#DefaultPostCompilerAction'),
+ \ 'sourceDirPath': 'src/main/java',
+ \ 'classDirPath': 'target/classes',
+ \ 'testSourceDirPath': 'src/test/java',
+ \ 'testClassDirPath': 'target/test-classes',
+ \ }
+
+With default actions, the compiler of choice will attempt to rebuild the class
+files for the buffer (and possibly for the whole project) as soon as a Java
+syntax file is loaded; then, `spotbugs` will attempt to analyze the quality of
+the compilation unit of the buffer.
+
+When default actions are not suited to a desired workflow, consider writing
+arbitrary functions yourself and matching their |Funcref|s to the supported
+keys: "PreCompilerAction", "PreCompilerTestAction", and "PostCompilerAction".
+
+The next example re-implements the default pre-compiler actions for a Maven
+project and requests other default Maven settings with the "compiler" entry: >
+
+ function! MavenPreCompilerAction() abort
+ call spotbugs#DeleteClassFiles()
+ compiler maven
+ make compile
+ endfunction
+
+ function! MavenPreCompilerTestAction() abort
+ call spotbugs#DeleteClassFiles()
+ compiler maven
+ make test-compile
+ endfunction
+
+ let g:spotbugs_properties = {
+ \ 'compiler': 'maven',
+ \ 'PreCompilerAction':
+ \ function('MavenPreCompilerAction'),
+ \ 'PreCompilerTestAction':
+ \ function('MavenPreCompilerTestAction'),
+ \ }
+
+Note that all entered custom settings will take precedence over the matching
+default settings in "g:spotbugs_properties".
+
+The "g:spotbugs_properties" variable is consulted by the Java filetype plugin
+(|ft-java-plugin|) to arrange for the described automation, and, therefore, it
+must be defined before |FileType| events can take place for the buffers loaded
+with Java source files. It could, for example, be set in a project-local
+|vimrc| loaded by [0].
+
+[0] https://github.com/MarcWeber/vim-addon-local-vimrc/
+
GNU MAKE *compiler-make*
Since the default make program is "make", the compiler plugin for make,
@@ -1409,6 +1517,13 @@ Useful values for the 'makeprg' options therefore are:
setlocal makeprg=./alltests.py " Run a testsuite
setlocal makeprg=python\ %:S " Run a single testcase
+PYTEST COMPILER *compiler-pytest*
+Commonly used compiler options can be added to 'makeprg' by setting the
+b/g:pytest_makeprg_params variable. For example: >
+
+ let b:pytest_makeprg_params = "--verbose --no-summary --disable-warnings"
+
+The global default is "--tb=short --quiet"; Python warnings are suppressed.
TEX COMPILER *compiler-tex*
diff --git a/runtime/ftplugin/java.vim b/runtime/ftplugin/java.vim
index 55b358374f..6e12fe2fe5 100644
--- a/runtime/ftplugin/java.vim
+++ b/runtime/ftplugin/java.vim
@@ -3,7 +3,7 @@
" Maintainer: Aliaksei Budavei <0x000c70 AT gmail DOT com>
" Former Maintainer: Dan Sharp
" Repository: https://github.com/zzzyxwvut/java-vim.git
-" Last Change: 2024 Sep 26
+" Last Change: 2024 Nov 24
" 2024 Jan 14 by Vim Project (browsefilter)
" 2024 May 23 by Riley Bruins <ribru17@gmail.com> ('commentstring')
@@ -90,10 +90,127 @@ if (has("gui_win32") || has("gui_gtk")) && !exists("b:browsefilter")
endif
endif
+" The support for pre- and post-compiler actions for SpotBugs.
+if exists("g:spotbugs_properties") && has_key(g:spotbugs_properties, 'compiler')
+ try
+ let spotbugs#compiler = g:spotbugs_properties.compiler
+ let g:spotbugs_properties = extend(
+ \ spotbugs#DefaultProperties(),
+ \ g:spotbugs_properties,
+ \ 'force')
+ catch
+ echomsg v:errmsg
+ finally
+ call remove(g:spotbugs_properties, 'compiler')
+ endtry
+endif
+
+if exists("g:spotbugs_properties") &&
+ \ filereadable($VIMRUNTIME . '/compiler/spotbugs.vim')
+ let s:request = 0
+
+ if has_key(g:spotbugs_properties, 'PreCompilerAction')
+ let s:dispatcher = 'call g:spotbugs_properties.PreCompilerAction() | '
+ let s:request += 1
+ endif
+
+ if has_key(g:spotbugs_properties, 'PreCompilerTestAction')
+ let s:dispatcher = 'call g:spotbugs_properties.PreCompilerTestAction() | '
+ let s:request += 2
+ endif
+
+ if has_key(g:spotbugs_properties, 'PostCompilerAction')
+ let s:request += 4
+ endif
+
+ if (s:request == 3 || s:request == 7) &&
+ \ has_key(g:spotbugs_properties, 'sourceDirPath') &&
+ \ has_key(g:spotbugs_properties, 'testSourceDirPath')
+ function! s:DispatchAction(path_action_pairs) abort
+ let name = expand('%:p')
+
+ for [path, Action] in a:path_action_pairs
+ if name =~# (path . '.\{-}\.java\=$')
+ call Action()
+ break
+ endif
+ endfor
+ endfunction
+
+ let s:dispatcher = printf('call s:DispatchAction(%s) | ',
+ \ string([[g:spotbugs_properties.sourceDirPath,
+ \ g:spotbugs_properties.PreCompilerAction],
+ \ [g:spotbugs_properties.testSourceDirPath,
+ \ g:spotbugs_properties.PreCompilerTestAction]]))
+ endif
+
+ if s:request
+ if exists("b:spotbugs_syntax_once")
+ let s:actions = [{'event': 'BufWritePost'}]
+ else
+ " XXX: Handle multiple FileType events when vimrc contains more
+ " than one filetype setting for the language, e.g.:
+ " :filetype plugin indent on
+ " :autocmd BufRead,BufNewFile *.java setlocal filetype=java ...
+ " XXX: DO NOT ADD b:spotbugs_syntax_once TO b:undo_ftplugin !
+ let b:spotbugs_syntax_once = 1
+ let s:actions = [{
+ \ 'event': 'Syntax',
+ \ 'once': 1,
+ \ }, {
+ \ 'event': 'BufWritePost',
+ \ }]
+ endif
+
+ for s:idx in range(len(s:actions))
+ if s:request == 7 || s:request == 6 || s:request == 5
+ let s:actions[s:idx].cmd = s:dispatcher . 'compiler spotbugs | ' .
+ \ 'call g:spotbugs_properties.PostCompilerAction()'
+ elseif s:request == 4
+ let s:actions[s:idx].cmd = 'compiler spotbugs | ' .
+ \ 'call g:spotbugs_properties.PostCompilerAction()'
+ elseif s:request == 3 || s:request == 2 || s:request == 1
+ let s:actions[s:idx].cmd = s:dispatcher . 'compiler spotbugs'
+ else
+ let s:actions[s:idx].cmd = ''
+ endif
+ endfor
+
+ if !exists("#java_spotbugs")
+ augroup java_spotbugs
+ augroup END
+ endif
+
+ " The events are defined in s:actions.
+ silent! autocmd! java_spotbugs BufWritePost <buffer>
+ silent! autocmd! java_spotbugs Syntax <buffer>
+
+ for s:action in s:actions
+ execute printf('autocmd java_spotbugs %s <buffer> %s',
+ \ s:action.event,
+ \ s:action.cmd . (has_key(s:action, 'once')
+ \ ? printf(' | autocmd! java_spotbugs %s <buffer>',
+ \ s:action.event)
+ \ : ''))
+ endfor
+
+ unlet! s:action s:actions s:idx s:dispatcher
+ endif
+
+ unlet s:request
+endif
+
+function! JavaFileTypeCleanUp() abort
+ setlocal suffixes< suffixesadd< formatoptions< comments< commentstring< path< includeexpr<
+ unlet! b:browsefilter
+
+ " The concatenated removals may be misparsed as a BufWritePost autocmd.
+ silent! autocmd! java_spotbugs BufWritePost <buffer>
+ silent! autocmd! java_spotbugs Syntax <buffer>
+endfunction
+
" Undo the stuff we changed.
-let b:undo_ftplugin = "setlocal suffixes< suffixesadd<" .
- \ " formatoptions< comments< commentstring< path< includeexpr<" .
- \ " | unlet! b:browsefilter"
+let b:undo_ftplugin = 'call JavaFileTypeCleanUp() | delfunction JavaFileTypeCleanUp'
" See ":help vim9-mix".
if !has("vim9script")
@@ -114,6 +231,19 @@ if exists("s:zip_func_upgradable")
setlocal suffixesadd<
endif
+if exists("*s:DispatchAction")
+ def! s:DispatchAction(path_action_pairs: list<list<any>>)
+ const name: string = expand('%:p')
+
+ for [path: string, Action: func: any] in path_action_pairs
+ if name =~# (path .. '.\{-}\.java\=$')
+ Action()
+ break
+ endif
+ endfor
+ enddef
+endif
+
" Restore the saved compatibility options.
let &cpo = s:save_cpo
unlet s:save_cpo
diff --git a/runtime/ftplugin/python.vim b/runtime/ftplugin/python.vim
index c000296726..6f20468896 100644
--- a/runtime/ftplugin/python.vim
+++ b/runtime/ftplugin/python.vim
@@ -3,8 +3,9 @@
" Maintainer: Tom Picton <tom@tompicton.com>
" Previous Maintainer: James Sully <sullyj3@gmail.com>
" Previous Maintainer: Johannes Zellner <johannes@zellner.org>
-" Last Change: 2024/05/13
-" https://github.com/tpict/vim-ftplugin-python
+" Repository: https://github.com/tpict/vim-ftplugin-python
+" Last Change: 2024/05/13
+" 2024 Nov 30 use pytest compiler (#16130)
if exists("b:did_ftplugin") | finish | endif
let b:did_ftplugin = 1
@@ -134,6 +135,11 @@ elseif executable('python')
setlocal keywordprg=python\ -m\ pydoc
endif
+if expand('%:t') =~# '\v^test_.*\.py$|_test\.py$' && executable('pytest')
+ compiler pytest
+ let &l:makeprg .= ' %:S'
+endif
+
" Script for filetype switching to undo the local stuff we may have changed
let b:undo_ftplugin = 'setlocal cinkeys<'
\ . '|setlocal comments<'
@@ -148,6 +154,7 @@ let b:undo_ftplugin = 'setlocal cinkeys<'
\ . '|setlocal softtabstop<'
\ . '|setlocal suffixesadd<'
\ . '|setlocal tabstop<'
+ \ . '|setlocal makeprg<'
\ . '|silent! nunmap <buffer> [M'
\ . '|silent! nunmap <buffer> [['
\ . '|silent! nunmap <buffer> []'
diff --git a/runtime/lua/vim/_defaults.lua b/runtime/lua/vim/_defaults.lua
index 2687f34302..6583cf48b3 100644
--- a/runtime/lua/vim/_defaults.lua
+++ b/runtime/lua/vim/_defaults.lua
@@ -546,8 +546,9 @@ do
---
--- @param option string Option name
--- @param value any Option value
- local function setoption(option, value)
- if vim.api.nvim_get_option_info2(option, {}).was_set then
+ --- @param force boolean? Always set the value, even if already set
+ local function setoption(option, value, force)
+ if not force and vim.api.nvim_get_option_info2(option, {}).was_set then
-- Don't do anything if option is already set
return
end
@@ -563,7 +564,7 @@ do
once = true,
nested = true,
callback = function()
- setoption(option, value)
+ setoption(option, value, force)
end,
})
end
@@ -645,11 +646,15 @@ do
return nil, nil, nil
end
- local timer = assert(vim.uv.new_timer())
-
+ -- This autocommand updates the value of 'background' anytime we receive
+ -- an OSC 11 response from the terminal emulator. If the user has set
+ -- 'background' explictly then we will delete this autocommand,
+ -- effectively disabling automatic background setting.
+ local force = false
local id = vim.api.nvim_create_autocmd('TermResponse', {
group = group,
nested = true,
+ desc = "Update the value of 'background' automatically based on the terminal emulator's background color",
callback = function(args)
local resp = args.data ---@type string
local r, g, b = parseosc11(resp)
@@ -661,27 +666,33 @@ do
if rr and gg and bb then
local luminance = (0.299 * rr) + (0.587 * gg) + (0.114 * bb)
local bg = luminance < 0.5 and 'dark' or 'light'
- setoption('background', bg)
+ setoption('background', bg, force)
+
+ -- On the first query response, don't force setting the option in
+ -- case the user has already set it manually. If they have, then
+ -- this autocommand will be deleted. If they haven't, then we do
+ -- want to force setting the option to override the value set by
+ -- this autocommand.
+ if not force then
+ force = true
+ end
end
+ end
+ end,
+ })
- return true
+ vim.api.nvim_create_autocmd('VimEnter', {
+ group = group,
+ nested = true,
+ once = true,
+ callback = function()
+ if vim.api.nvim_get_option_info2('background', {}).was_set then
+ vim.api.nvim_del_autocmd(id)
end
end,
})
io.stdout:write('\027]11;?\007')
-
- timer:start(1000, 0, function()
- -- Delete the autocommand if no response was received
- vim.schedule(function()
- -- Suppress error if autocommand has already been deleted
- pcall(vim.api.nvim_del_autocmd, id)
- end)
-
- if not timer:is_closing() then
- timer:close()
- end
- end)
end
--- If the TUI (term_has_truecolor) was able to determine that the host
diff --git a/runtime/lua/vim/_meta/options.lua b/runtime/lua/vim/_meta/options.lua
index c635d9bd3b..247b464a70 100644
--- a/runtime/lua/vim/_meta/options.lua
+++ b/runtime/lua/vim/_meta/options.lua
@@ -6311,6 +6311,7 @@ vim.wo.stc = vim.wo.statuscolumn
--- All fields except the {item} are optional. A single percent sign can
--- be given as "%%".
---
+--- *stl-%!*
--- When the option starts with "%!" then it is used as an expression,
--- evaluated and the result is used as the option value. Example:
---
diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua
index aa566973b6..5d771c30e9 100644
--- a/runtime/lua/vim/filetype.lua
+++ b/runtime/lua/vim/filetype.lua
@@ -962,7 +962,9 @@ local extension = {
purs = 'purescript',
arr = 'pyret',
pxd = 'pyrex',
+ pxi = 'pyrex',
pyx = 'pyrex',
+ ['pyx+'] = 'pyrex',
pyw = 'python',
py = 'python',
pyi = 'python',
diff --git a/runtime/lua/vim/fs.lua b/runtime/lua/vim/fs.lua
index d91eeaf02f..2f007d97c3 100644
--- a/runtime/lua/vim/fs.lua
+++ b/runtime/lua/vim/fs.lua
@@ -1,3 +1,15 @@
+--- @brief <pre>help
+--- *vim.fs.exists()*
+--- Use |uv.fs_stat()| to check a file's type, and whether it exists.
+---
+--- Example:
+---
+--- >lua
+--- if vim.uv.fs_stat(file) then
+--- vim.print("file exists")
+--- end
+--- <
+
local uv = vim.uv
local M = {}
diff --git a/runtime/lua/vim/loader.lua b/runtime/lua/vim/loader.lua
index 0cce0ab21d..71d0188128 100644
--- a/runtime/lua/vim/loader.lua
+++ b/runtime/lua/vim/loader.lua
@@ -399,50 +399,51 @@ function M.reset(path)
end
end
---- Enables the experimental Lua module loader:
---- * overrides loadfile
+--- Enables or disables the experimental Lua module loader:
+---
+--- Enable (`enable=true`):
+--- * overrides |loadfile()|
--- * adds the Lua loader using the byte-compilation cache
--- * adds the libs loader
--- * removes the default Nvim loader
---
---- @since 0
-function M.enable()
- if M.enabled then
- return
- end
- M.enabled = true
- vim.fn.mkdir(vim.fn.fnamemodify(M.path, ':p'), 'p')
- _G.loadfile = loadfile_cached
- -- add Lua loader
- table.insert(loaders, 2, loader_cached)
- -- add libs loader
- table.insert(loaders, 3, loader_lib_cached)
- -- remove Nvim loader
- for l, loader in ipairs(loaders) do
- if loader == vim._load_package then
- table.remove(loaders, l)
- break
- end
- end
-end
-
---- Disables the experimental Lua module loader:
+--- Disable (`enable=false`):
--- * removes the loaders
--- * adds the default Nvim loader
---
--- @since 0
-function M.disable()
- if not M.enabled then
+---
+--- @param enable? (boolean) true/nil to enable, false to disable
+function M.enable(enable)
+ enable = enable == nil and true or enable
+ if enable == M.enabled then
return
end
- M.enabled = false
- _G.loadfile = _loadfile
- for l, loader in ipairs(loaders) do
- if loader == loader_cached or loader == loader_lib_cached then
- table.remove(loaders, l)
+ M.enabled = enable
+
+ if enable then
+ vim.fn.mkdir(vim.fn.fnamemodify(M.path, ':p'), 'p')
+ _G.loadfile = loadfile_cached
+ -- add Lua loader
+ table.insert(loaders, 2, loader_cached)
+ -- add libs loader
+ table.insert(loaders, 3, loader_lib_cached)
+ -- remove Nvim loader
+ for l, loader in ipairs(loaders) do
+ if loader == vim._load_package then
+ table.remove(loaders, l)
+ break
+ end
+ end
+ else
+ _G.loadfile = _loadfile
+ for l, loader in ipairs(loaders) do
+ if loader == loader_cached or loader == loader_lib_cached then
+ table.remove(loaders, l)
+ end
end
+ table.insert(loaders, 2, vim._load_package)
end
- table.insert(loaders, 2, vim._load_package)
end
--- Tracks the time spent in a function
diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua
index 6d29c9e4df..a3791e15c3 100644
--- a/runtime/lua/vim/lsp.lua
+++ b/runtime/lua/vim/lsp.lua
@@ -3,6 +3,7 @@ local validate = vim.validate
local lsp = vim._defer_require('vim.lsp', {
_changetracking = ..., --- @module 'vim.lsp._changetracking'
+ _folding_range = ..., --- @module 'vim.lsp._folding_range'
_snippet_grammar = ..., --- @module 'vim.lsp._snippet_grammar'
_tagfunc = ..., --- @module 'vim.lsp._tagfunc'
_watchfiles = ..., --- @module 'vim.lsp._watchfiles'
@@ -57,6 +58,7 @@ lsp._request_name_to_capability = {
[ms.textDocument_documentHighlight] = { 'documentHighlightProvider' },
[ms.textDocument_documentLink] = { 'documentLinkProvider' },
[ms.textDocument_documentSymbol] = { 'documentSymbolProvider' },
+ [ms.textDocument_foldingRange] = { 'foldingRangeProvider' },
[ms.textDocument_formatting] = { 'documentFormattingProvider' },
[ms.textDocument_hover] = { 'hoverProvider' },
[ms.textDocument_implementation] = { 'implementationProvider' },
@@ -1094,6 +1096,38 @@ function lsp.tagfunc(pattern, flags)
return vim.lsp._tagfunc(pattern, flags)
end
+--- Provides an interface between the built-in client and a `foldexpr` function.
+---@param lnum integer line number
+function lsp.foldexpr(lnum)
+ return vim.lsp._folding_range.foldexpr(lnum)
+end
+
+--- Close all {kind} of folds in the the window with {winid}.
+---
+--- To automatically fold imports when opening a file, you can use an autocmd:
+---
+--- ```lua
+--- vim.api.nvim_create_autocmd('LspNotify', {
+--- callback = function(args)
+--- if args.data.method == 'textDocument/didOpen' then
+--- vim.lsp.foldclose('imports', vim.fn.bufwinid(args.buf))
+--- end
+--- end,
+--- })
+--- ```
+---
+---@param kind lsp.FoldingRangeKind Kind to close, one of "comment", "imports" or "region".
+---@param winid? integer Defaults to the current window.
+function lsp.foldclose(kind, winid)
+ return vim.lsp._folding_range.foldclose(kind, winid)
+end
+
+--- Provides a `foldtext` function that shows the `collapsedText` retrieved,
+--- defaults to the first folded line if `collapsedText` is not provided.
+function lsp.foldtext()
+ return vim.lsp._folding_range.foldtext()
+end
+
---Checks whether a client is stopped.
---
---@param client_id (integer)
diff --git a/runtime/lua/vim/lsp/_folding_range.lua b/runtime/lua/vim/lsp/_folding_range.lua
new file mode 100644
index 0000000000..6a445017a3
--- /dev/null
+++ b/runtime/lua/vim/lsp/_folding_range.lua
@@ -0,0 +1,371 @@
+local util = require('vim.lsp.util')
+local log = require('vim.lsp.log')
+local ms = require('vim.lsp.protocol').Methods
+local api = vim.api
+
+local M = {}
+
+---@class (private) vim.lsp.folding_range.BufState
+---
+---@field version? integer
+---
+--- Never use this directly, `renew()` the cached foldinfo
+--- then use on demand via `row_*` fields.
+---
+--- Index In the form of client_id -> ranges
+---@field client_ranges table<integer, lsp.FoldingRange[]?>
+---
+--- Index in the form of row -> [foldlevel, mark]
+---@field row_level table<integer, [integer, ">" | "<"?]?>
+---
+--- Index in the form of start_row -> kinds
+---@field row_kinds table<integer, table<lsp.FoldingRangeKind, true?>?>>
+---
+--- Index in the form of start_row -> collapsed_text
+---@field row_text table<integer, string?>
+
+---@type table<integer, vim.lsp.folding_range.BufState?>
+local bufstates = {}
+
+--- Renew the cached foldinfo in the buffer.
+---@param bufnr integer
+local function renew(bufnr)
+ local bufstate = assert(bufstates[bufnr])
+
+ ---@type table<integer, [integer, ">" | "<"?]?>
+ local row_level = {}
+ ---@type table<integer, table<lsp.FoldingRangeKind, true?>?>>
+ local row_kinds = {}
+ ---@type table<integer, string?>
+ local row_text = {}
+
+ for _, ranges in pairs(bufstate.client_ranges) do
+ for _, range in ipairs(ranges) do
+ local start_row = range.startLine
+ local end_row = range.endLine
+ -- Adding folds within a single line is not supported by Nvim.
+ if start_row ~= end_row then
+ row_text[start_row] = range.collapsedText
+
+ local kind = range.kind
+ if kind then
+ local kinds = row_kinds[start_row] or {}
+ kinds[kind] = true
+ row_kinds[start_row] = kinds
+ end
+
+ for row = start_row, end_row do
+ local level = row_level[row] or { 0 }
+ level[1] = level[1] + 1
+ row_level[row] = level
+ end
+ row_level[start_row][2] = '>'
+ row_level[end_row][2] = '<'
+ end
+ end
+ end
+
+ bufstate.row_level = row_level
+ bufstate.row_kinds = row_kinds
+ bufstate.row_text = row_text
+end
+
+--- Renew the cached foldinfo then force `foldexpr()` to be re-evaluated,
+--- without opening folds.
+---@param bufnr integer
+local function foldupdate(bufnr)
+ renew(bufnr)
+ for _, winid in ipairs(vim.fn.win_findbuf(bufnr)) do
+ local wininfo = vim.fn.getwininfo(winid)[1]
+ if wininfo and wininfo.tabnr == vim.fn.tabpagenr() then
+ if vim.wo[winid].foldmethod == 'expr' then
+ vim._foldupdate(winid, 0, api.nvim_buf_line_count(bufnr))
+ end
+ end
+ end
+end
+
+--- Whether `foldupdate()` is scheduled for the buffer with `bufnr`.
+---
+--- Index in the form of bufnr -> true?
+---@type table<integer, true?>
+local scheduled_foldupdate = {}
+
+--- Schedule `foldupdate()` after leaving insert mode.
+---@param bufnr integer
+local function schedule_foldupdate(bufnr)
+ if not scheduled_foldupdate[bufnr] then
+ scheduled_foldupdate[bufnr] = true
+ api.nvim_create_autocmd('InsertLeave', {
+ buffer = bufnr,
+ once = true,
+ callback = function()
+ foldupdate(bufnr)
+ scheduled_foldupdate[bufnr] = nil
+ end,
+ })
+ end
+end
+
+---@param results table<integer,{err: lsp.ResponseError?, result: lsp.FoldingRange[]?}>
+---@type lsp.MultiHandler
+local function multi_handler(results, ctx)
+ local bufnr = assert(ctx.bufnr)
+ -- Handling responses from outdated buffer only causes performance overhead.
+ if util.buf_versions[bufnr] ~= ctx.version then
+ return
+ end
+
+ local bufstate = assert(bufstates[bufnr])
+ for client_id, result in pairs(results) do
+ if result.err then
+ log.error(result.err)
+ else
+ bufstate.client_ranges[client_id] = result.result
+ end
+ end
+ bufstate.version = ctx.version
+
+ if api.nvim_get_mode().mode:match('^i') then
+ -- `foldUpdate()` is guarded in insert mode.
+ schedule_foldupdate(bufnr)
+ else
+ foldupdate(bufnr)
+ end
+end
+
+---@param result lsp.FoldingRange[]?
+---@type lsp.Handler
+local function handler(err, result, ctx)
+ multi_handler({ [ctx.client_id] = { err = err, result = result } }, ctx)
+end
+
+--- Request `textDocument/foldingRange` from the server.
+--- `foldupdate()` is scheduled once after the request is completed.
+---@param bufnr integer
+---@param client? vim.lsp.Client The client whose server supports `foldingRange`.
+local function request(bufnr, client)
+ ---@type lsp.FoldingRangeParams
+ local params = { textDocument = util.make_text_document_params(bufnr) }
+
+ if client then
+ client:request(ms.textDocument_foldingRange, params, handler, bufnr)
+ return
+ end
+
+ if not next(vim.lsp.get_clients({ bufnr = bufnr, method = ms.textDocument_foldingRange })) then
+ return
+ end
+
+ vim.lsp.buf_request_all(bufnr, ms.textDocument_foldingRange, params, multi_handler)
+end
+
+-- NOTE:
+-- `bufstate` and event hooks are interdependent:
+-- * `bufstate` needs event hooks for correctness.
+-- * event hooks require the previous `bufstate` for updates.
+-- Since they are manually created and destroyed,
+-- we ensure their lifecycles are always synchronized.
+--
+-- TODO(ofseed):
+-- 1. Implement clearing `bufstate` and event hooks
+-- when no clients in the buffer support the corresponding method.
+-- 2. Then generalize this state management to other LSP modules.
+local augroup_setup = api.nvim_create_augroup('vim_lsp_folding_range/setup', {})
+
+--- Initialize `bufstate` and event hooks, then request folding ranges.
+--- Manage their lifecycle within this function.
+---@param bufnr integer
+---@return vim.lsp.folding_range.BufState?
+local function setup(bufnr)
+ if not api.nvim_buf_is_loaded(bufnr) then
+ return
+ end
+
+ -- Register the new `bufstate`.
+ bufstates[bufnr] = {
+ client_ranges = {},
+ row_level = {},
+ row_kinds = {},
+ row_text = {},
+ }
+
+ -- Event hooks from `buf_attach` can't be removed externally.
+ -- Hooks and `bufstate` share the same lifecycle;
+ -- they should self-destroy if `bufstate == nil`.
+ api.nvim_buf_attach(bufnr, false, {
+ -- `on_detach` also runs on buffer reload (`:e`).
+ -- Ensure `bufstate` and hooks are cleared to avoid duplication or leftover states.
+ on_detach = function()
+ bufstates[bufnr] = nil
+ api.nvim_clear_autocmds({ buffer = bufnr, group = augroup_setup })
+ end,
+ -- Reset `bufstate` and request folding ranges.
+ on_reload = function()
+ bufstates[bufnr] = {
+ client_ranges = {},
+ row_level = {},
+ row_kinds = {},
+ row_text = {},
+ }
+ request(bufnr)
+ end,
+ --- Sync changed rows with their previous foldlevels before applying new ones.
+ on_bytes = function(_, _, _, start_row, _, _, old_row, _, _, new_row, _, _)
+ if bufstates[bufnr] == nil then
+ return true
+ end
+ local row_level = bufstates[bufnr].row_level
+ if next(row_level) == nil then
+ return
+ end
+ local row = new_row - old_row
+ if row > 0 then
+ vim._list_insert(row_level, start_row, start_row + math.abs(row) - 1, { -1 })
+ -- If the previous row ends a fold,
+ -- Nvim treats the first row after consecutive `-1`s as a new fold start,
+ -- which is not the desired behavior.
+ local prev_level = row_level[start_row - 1]
+ if prev_level and prev_level[2] == '<' then
+ row_level[start_row] = { prev_level[1] - 1 }
+ end
+ elseif row < 0 then
+ vim._list_remove(row_level, start_row, start_row + math.abs(row) - 1)
+ end
+ end,
+ })
+ api.nvim_create_autocmd('LspDetach', {
+ group = augroup_setup,
+ buffer = bufnr,
+ callback = function(args)
+ if not api.nvim_buf_is_loaded(bufnr) then
+ return
+ end
+
+ ---@type integer
+ local client_id = args.data.client_id
+ bufstates[bufnr].client_ranges[client_id] = nil
+
+ ---@type vim.lsp.Client[]
+ local clients = vim
+ .iter(vim.lsp.get_clients({ bufnr = bufnr, method = ms.textDocument_foldingRange }))
+ ---@param client vim.lsp.Client
+ :filter(function(client)
+ return client.id ~= client_id
+ end)
+ :totable()
+ if #clients == 0 then
+ bufstates[bufnr] = {
+ client_ranges = {},
+ row_level = {},
+ row_kinds = {},
+ row_text = {},
+ }
+ end
+
+ foldupdate(bufnr)
+ end,
+ })
+ api.nvim_create_autocmd('LspAttach', {
+ group = augroup_setup,
+ buffer = bufnr,
+ callback = function(args)
+ local client = assert(vim.lsp.get_client_by_id(args.data.client_id))
+ request(bufnr, client)
+ end,
+ })
+ api.nvim_create_autocmd('LspNotify', {
+ group = augroup_setup,
+ buffer = bufnr,
+ callback = function(args)
+ local client = assert(vim.lsp.get_client_by_id(args.data.client_id))
+ if
+ client:supports_method(ms.textDocument_foldingRange, bufnr)
+ and (
+ args.data.method == ms.textDocument_didChange
+ or args.data.method == ms.textDocument_didOpen
+ )
+ then
+ request(bufnr, client)
+ end
+ end,
+ })
+
+ request(bufnr)
+
+ return bufstates[bufnr]
+end
+
+---@param kind lsp.FoldingRangeKind
+---@param winid integer
+local function foldclose(kind, winid)
+ vim._with({ win = winid }, function()
+ local bufnr = api.nvim_win_get_buf(winid)
+ local row_kinds = bufstates[bufnr].row_kinds
+ -- Reverse traverse to ensure that the smallest ranges are closed first.
+ for row = api.nvim_buf_line_count(bufnr) - 1, 0, -1 do
+ local kinds = row_kinds[row]
+ if kinds and kinds[kind] then
+ vim.cmd(row + 1 .. 'foldclose')
+ end
+ end
+ end)
+end
+
+---@param kind lsp.FoldingRangeKind
+---@param winid? integer
+function M.foldclose(kind, winid)
+ vim.validate('kind', kind, 'string')
+ vim.validate('winid', winid, 'number', true)
+
+ winid = winid or api.nvim_get_current_win()
+ local bufnr = api.nvim_win_get_buf(winid)
+ local bufstate = bufstates[bufnr]
+ if not bufstate then
+ return
+ end
+
+ if bufstate.version == util.buf_versions[bufnr] then
+ foldclose(kind, winid)
+ return
+ end
+ -- Schedule `foldclose()` if the buffer is not up-to-date.
+
+ if not next(vim.lsp.get_clients({ bufnr = bufnr, method = ms.textDocument_foldingRange })) then
+ return
+ end
+ ---@type lsp.FoldingRangeParams
+ local params = { textDocument = util.make_text_document_params(bufnr) }
+ vim.lsp.buf_request_all(bufnr, ms.textDocument_foldingRange, params, function(...)
+ multi_handler(...)
+ foldclose(kind, winid)
+ end)
+end
+
+---@return string
+function M.foldtext()
+ local bufnr = api.nvim_get_current_buf()
+ local lnum = vim.v.foldstart
+ local row = lnum - 1
+ local bufstate = bufstates[bufnr]
+ if bufstate and bufstate.row_text[row] then
+ return bufstate.row_text[row]
+ end
+ return vim.fn.getline(lnum)
+end
+
+---@param lnum? integer
+---@return string level
+function M.foldexpr(lnum)
+ local bufnr = api.nvim_get_current_buf()
+ local bufstate = bufstates[bufnr] or setup(bufnr)
+ if not bufstate then
+ return '0'
+ end
+
+ local row = (lnum or vim.v.lnum) - 1
+ local level = bufstate.row_level[row]
+ return level and (level[2] or '') .. (level[1] or '0') or '0'
+end
+
+return M
diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua
index aca6bf27f4..10479807a2 100644
--- a/runtime/lua/vim/lsp/buf.lua
+++ b/runtime/lua/vim/lsp/buf.lua
@@ -487,7 +487,7 @@ end
--- ```lua
--- -- Never request typescript-language-server for formatting
--- vim.lsp.buf.format {
---- filter = function(client) return client.name ~= "tsserver" end
+--- filter = function(client) return client.name ~= "ts_ls" end
--- }
--- ```
--- @field filter? fun(client: vim.lsp.Client): boolean?
@@ -736,7 +736,7 @@ end
--- Lists all the references to the symbol under the cursor in the quickfix window.
---
----@param context (table|nil) Context for the request
+---@param context lsp.ReferenceContext? Context for the request
---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_references
---@param opts? vim.lsp.ListOpts
function M.references(context, opts)
diff --git a/runtime/lua/vim/lsp/client.lua b/runtime/lua/vim/lsp/client.lua
index 7c2b7192f5..a14b6ccda6 100644
--- a/runtime/lua/vim/lsp/client.lua
+++ b/runtime/lua/vim/lsp/client.lua
@@ -639,7 +639,7 @@ end
--- @param method string LSP method name.
--- @param params? table LSP request params.
--- @param handler? lsp.Handler Response |lsp-handler| for this method.
---- @param bufnr? integer Buffer handle. 0 for current (default).
+--- @param bufnr? integer (default: 0) Buffer handle, or 0 for current.
--- @return boolean status indicates whether the request was successful.
--- If it is `false`, then it will always be `false` (the client has shutdown).
--- @return integer? request_id Can be used with |Client:cancel_request()|.
@@ -718,7 +718,7 @@ end
--- @param params table LSP request params.
--- @param timeout_ms integer? Maximum time in milliseconds to wait for
--- a result. Defaults to 1000
---- @param bufnr integer Buffer handle (0 for current).
+--- @param bufnr? integer (default: 0) Buffer handle, or 0 for current.
--- @return {err: lsp.ResponseError?, result:any}? `result` and `err` from the |lsp-handler|.
--- `nil` is the request was unsuccessful
--- @return string? err On timeout, cancel or error, where `err` is a
diff --git a/runtime/lua/vim/lsp/protocol.lua b/runtime/lua/vim/lsp/protocol.lua
index 3d29dad90a..cfd47d8f7c 100644
--- a/runtime/lua/vim/lsp/protocol.lua
+++ b/runtime/lua/vim/lsp/protocol.lua
@@ -440,6 +440,13 @@ function protocol.make_client_capabilities()
properties = { 'command' },
},
},
+ foldingRange = {
+ dynamicRegistration = false,
+ lineFoldingOnly = true,
+ foldingRange = {
+ collapsedText = true,
+ },
+ },
formatting = {
dynamicRegistration = true,
},
diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua
index 3de76f93a6..ced8aa5745 100644
--- a/runtime/lua/vim/lsp/util.lua
+++ b/runtime/lua/vim/lsp/util.lua
@@ -1894,7 +1894,7 @@ function M.make_position_params(window, position_encoding)
local buf = api.nvim_win_get_buf(window)
if position_encoding == nil then
vim.notify_once(
- 'warning: position_encoding is required, using the offset_encoding from the first client',
+ 'position_encoding param is required in vim.lsp.util.make_position_params. Defaulting to position encoding of the first client.',
vim.log.levels.WARN
)
position_encoding = M._get_offset_encoding(buf)
@@ -1950,7 +1950,7 @@ function M.make_range_params(window, position_encoding)
local buf = api.nvim_win_get_buf(window or 0)
if position_encoding == nil then
vim.notify_once(
- 'warning: position_encoding is required, using the offset_encoding from the first client',
+ 'position_encoding param is required in vim.lsp.util.make_range_params. Defaulting to position encoding of the first client.',
vim.log.levels.WARN
)
position_encoding = M._get_offset_encoding(buf)
@@ -1979,7 +1979,7 @@ function M.make_given_range_params(start_pos, end_pos, bufnr, position_encoding)
bufnr = bufnr or api.nvim_get_current_buf()
if position_encoding == nil then
vim.notify_once(
- 'warning: position_encoding is required, using the offset_encoding from the first client',
+ 'position_encoding param is required in vim.lsp.util.make_given_range_params. Defaulting to position encoding of the first client.',
vim.log.levels.WARN
)
position_encoding = M._get_offset_encoding(bufnr)
diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua
index 4f2373b182..2e8edea22a 100644
--- a/runtime/lua/vim/shared.lua
+++ b/runtime/lua/vim/shared.lua
@@ -737,6 +737,51 @@ function vim.list_slice(list, start, finish)
return new_list
end
+--- Efficiently insert items into the middle of a list.
+---
+--- Calling table.insert() in a loop will re-index the tail of the table on
+--- every iteration, instead this function will re-index the table exactly
+--- once.
+---
+--- Based on https://stackoverflow.com/questions/12394841/safely-remove-items-from-an-array-table-while-iterating/53038524#53038524
+---
+---@param t any[]
+---@param first integer
+---@param last integer
+---@param v any
+function vim._list_insert(t, first, last, v)
+ local n = #t
+
+ -- Shift table forward
+ for i = n - first, 0, -1 do
+ t[last + 1 + i] = t[first + i]
+ end
+
+ -- Fill in new values
+ for i = first, last do
+ t[i] = v
+ end
+end
+
+--- Efficiently remove items from middle of a list.
+---
+--- Calling table.remove() in a loop will re-index the tail of the table on
+--- every iteration, instead this function will re-index the table exactly
+--- once.
+---
+--- Based on https://stackoverflow.com/questions/12394841/safely-remove-items-from-an-array-table-while-iterating/53038524#53038524
+---
+---@param t any[]
+---@param first integer
+---@param last integer
+function vim._list_remove(t, first, last)
+ local n = #t
+ for i = 0, n - first do
+ t[first + i] = t[last + 1 + i]
+ t[last + 1 + i] = nil
+ end
+end
+
--- Trim whitespace (Lua pattern "%s") from both sides of a string.
---
---@see |lua-patterns|
diff --git a/runtime/lua/vim/text.lua b/runtime/lua/vim/text.lua
index d45c8021c6..f910ab3a1d 100644
--- a/runtime/lua/vim/text.lua
+++ b/runtime/lua/vim/text.lua
@@ -2,6 +2,18 @@
local M = {}
+local alphabet = '0123456789ABCDEF'
+local atoi = {} ---@type table<string, integer>
+local itoa = {} ---@type table<integer, string>
+do
+ for i = 1, #alphabet do
+ local char = alphabet:sub(i, i)
+ itoa[i - 1] = char
+ atoi[char] = i - 1
+ atoi[char:lower()] = i - 1
+ end
+end
+
--- Hex encode a string.
---
--- @param str string String to encode
@@ -9,7 +21,9 @@ local M = {}
function M.hexencode(str)
local enc = {} ---@type string[]
for i = 1, #str do
- enc[i] = string.format('%02X', str:byte(i, i + 1))
+ local byte = str:byte(i)
+ enc[2 * i - 1] = itoa[math.floor(byte / 16)]
+ enc[2 * i] = itoa[byte % 16]
end
return table.concat(enc)
end
@@ -26,8 +40,12 @@ function M.hexdecode(enc)
local str = {} ---@type string[]
for i = 1, #enc, 2 do
- local n = assert(tonumber(enc:sub(i, i + 1), 16))
- str[#str + 1] = string.char(n)
+ local u = atoi[enc:sub(i, i)]
+ local l = atoi[enc:sub(i + 1, i + 1)]
+ if not u or not l then
+ return nil, 'string must contain only hex characters'
+ end
+ str[(i + 1) / 2] = string.char(u * 16 + l)
end
return table.concat(str), nil
end
diff --git a/runtime/lua/vim/treesitter/_fold.lua b/runtime/lua/vim/treesitter/_fold.lua
index 7237d2e7d4..0cb5b497c7 100644
--- a/runtime/lua/vim/treesitter/_fold.lua
+++ b/runtime/lua/vim/treesitter/_fold.lua
@@ -30,65 +30,20 @@ function FoldInfo.new()
}, FoldInfo)
end
---- Efficiently remove items from middle of a list a list.
----
---- Calling table.remove() in a loop will re-index the tail of the table on
---- every iteration, instead this function will re-index the table exactly
---- once.
----
---- Based on https://stackoverflow.com/questions/12394841/safely-remove-items-from-an-array-table-while-iterating/53038524#53038524
----
----@param t any[]
----@param first integer
----@param last integer
-local function list_remove(t, first, last)
- local n = #t
- for i = 0, n - first do
- t[first + i] = t[last + 1 + i]
- t[last + 1 + i] = nil
- end
-end
-
---@package
---@param srow integer
---@param erow integer 0-indexed, exclusive
function FoldInfo:remove_range(srow, erow)
- list_remove(self.levels, srow + 1, erow)
- list_remove(self.levels0, srow + 1, erow)
-end
-
---- Efficiently insert items into the middle of a list.
----
---- Calling table.insert() in a loop will re-index the tail of the table on
---- every iteration, instead this function will re-index the table exactly
---- once.
----
---- Based on https://stackoverflow.com/questions/12394841/safely-remove-items-from-an-array-table-while-iterating/53038524#53038524
----
----@param t any[]
----@param first integer
----@param last integer
----@param v any
-local function list_insert(t, first, last, v)
- local n = #t
-
- -- Shift table forward
- for i = n - first, 0, -1 do
- t[last + 1 + i] = t[first + i]
- end
-
- -- Fill in new values
- for i = first, last do
- t[i] = v
- end
+ vim._list_remove(self.levels, srow + 1, erow)
+ vim._list_remove(self.levels0, srow + 1, erow)
end
---@package
---@param srow integer
---@param erow integer 0-indexed, exclusive
function FoldInfo:add_range(srow, erow)
- list_insert(self.levels, srow + 1, erow, -1)
- list_insert(self.levels0, srow + 1, erow, -1)
+ vim._list_insert(self.levels, srow + 1, erow, -1)
+ vim._list_insert(self.levels0, srow + 1, erow, -1)
end
---@param range Range2
diff --git a/runtime/syntax/po.vim b/runtime/syntax/po.vim
index 08d6baec27..6da27f639d 100644
--- a/runtime/syntax/po.vim
+++ b/runtime/syntax/po.vim
@@ -1,10 +1,11 @@
" Vim syntax file
" Language: po (gettext)
" Maintainer: Dwayne Bailey <dwayne@translate.org.za>
-" Last Change: 2015 Jun 07
+" Last Change: 2024 Nov 28
" Contributors: Dwayne Bailey (Most advanced syntax highlighting)
" Leonardo Fontenelle (Spell checking)
" Nam SungHyun <namsh@kldp.org> (Original maintainer)
+" Eisuke Kawashima (add format-flags: #16132)
" quit when a syntax file was already loaded
if exists("b:current_syntax")
@@ -32,9 +33,9 @@ syn region poMsgCTxt matchgroup=poStatementMsgCTxt start=+^msgctxt "+rs=e-1
syn region poMsgID matchgroup=poStatementMsgid start=+^msgid "+rs=e-1 matchgroup=poStringID end=+^msgstr\(\|\[[\]0\[]\]\) "+me=s-1 contains=poStringID,poStatementMsgidplural,poStatementMsgid
syn region poMsgSTR matchgroup=poStatementMsgstr start=+^msgstr\(\|\[[\]0\[]\]\) "+rs=e-1 matchgroup=poStringSTR end=+\n\n+me=s-1 contains=poStringSTR,poStatementMsgstr
syn region poStringCTxt start=+"+ skip=+\\\\\|\\"+ end=+"+
-syn region poStringID start=+"+ skip=+\\\\\|\\"+ end=+"+ contained
+syn region poStringID start=+"+ skip=+\\\\\|\\"+ end=+"+ contained
\ contains=poSpecial,poFormat,poCommentKDE,poPluralKDE,poKDEdesktopFile,poHtml,poAcceleratorId,poHtmlNot,poVariable
-syn region poStringSTR start=+"+ skip=+\\\\\|\\"+ end=+"+ contained
+syn region poStringSTR start=+"+ skip=+\\\\\|\\"+ end=+"+ contained
\ contains=@Spell,poSpecial,poFormat,poHeaderItem,poCommentKDEError,poHeaderUndefined,poPluralKDEError,poMsguniqError,poKDEdesktopFile,poHtml,poAcceleratorStr,poHtmlNot,poVariable
" Header and Copyright
@@ -45,13 +46,43 @@ syn match poCopyrightUnset "SOME DESCRIPTIVE TITLE\|FIRST AUTHOR <EMAIL@ADDR
" Translation comment block including: translator comment, automatic comments, flags and locations
syn match poComment "^#.*$"
syn keyword poFlagFuzzy fuzzy contained
+
+syn match poFlagFormat /\<\%(no-\)\?awk-format\>/ contained
+syn match poFlagFormat /\<\%(no-\)\?boost-format\>/ contained
+syn match poFlagFormat /\<\%(no-\)\?c++-format\>/ contained
+syn match poFlagFormat /\<\%(no-\)\?c-format\>/ contained
+syn match poFlagFormat /\<\%(no-\)\?csharp-format\>/ contained
+syn match poFlagFormat /\<\%(no-\)\?elisp-format\>/ contained
+syn match poFlagFormat /\<\%(no-\)\?gcc-internal-format\>/ contained
+syn match poFlagFormat /\<\%(no-\)\?gfc-internal-format\>/ contained
+syn match poFlagFormat /\<\%(no-\)\?java-format\>/ contained
+syn match poFlagFormat /\<\%(no-\)\?java-printf-format\>/ contained
+syn match poFlagFormat /\<\%(no-\)\?javascript-format\>/ contained
+syn match poFlagFormat /\<\%(no-\)\?kde-format\>/ contained
+syn match poFlagFormat /\<\%(no-\)\?librep-format\>/ contained
+syn match poFlagFormat /\<\%(no-\)\?lisp-format\>/ contained
+syn match poFlagFormat /\<\%(no-\)\?lua-format\>/ contained
+syn match poFlagFormat /\<\%(no-\)\?objc-format\>/ contained
+syn match poFlagFormat /\<\%(no-\)\?object-pascal-format\>/ contained
+syn match poFlagFormat /\<\%(no-\)\?perl-brace-format\>/ contained
+syn match poFlagFormat /\<\%(no-\)\?perl-format\>/ contained
+syn match poFlagFormat /\<\%(no-\)\?php-format\>/ contained
+syn match poFlagFormat /\<\%(no-\)\?python-brace-format\>/ contained
+syn match poFlagFormat /\<\%(no-\)\?python-format\>/ contained
+syn match poFlagFormat /\<\%(no-\)\?qt-format\>/ contained
+syn match poFlagFormat /\<\%(no-\)\?qt-plural-format\>/ contained
+syn match poFlagFormat /\<\%(no-\)\?ruby-format\>/ contained
+syn match poFlagFormat /\<\%(no-\)\?scheme-format\>/ contained
+syn match poFlagFormat /\<\%(no-\)\?sh-format\>/ contained
+syn match poFlagFormat /\<\%(no-\)\?smalltalk-format\>/ contained
+syn match poFlagFormat /\<\%(no-\)\?tcl-format\>/ contained
+syn match poFlagFormat /\<\%(no-\)\?ycp-format\>/ contained
+
syn match poCommentTranslator "^# .*$" contains=poCopyrightUnset
-syn match poCommentAutomatic "^#\..*$"
+syn match poCommentAutomatic "^#\..*$"
syn match poCommentSources "^#:.*$"
-syn match poCommentFlags "^#,.*$" contains=poFlagFuzzy
-syn match poDiffOld '\(^#| "[^{]*+}\|{+[^}]*+}\|{+[^}]*\|"$\)' contained
-syn match poDiffNew '\(^#| "[^{]*-}\|{-[^}]*-}\|{-[^}]*\|"$\)' contained
-syn match poCommentDiff "^#|.*$" contains=poDiffOld,poDiffNew
+syn match poCommentFlags "^#,.*$" contains=poFlagFuzzy,poFlagFormat
+syn match poCommentPrevious "^#|.*$"
" Translations (also includes header fields as they appear in a translation msgstr)
syn region poCommentKDE start=+"_: +ms=s+1 end="\\n" end="\"\n^msgstr"me=s-1 contained
@@ -66,13 +97,13 @@ syn match poFormat "%%" contained
syn region poMsguniqError matchgroup=poMsguniqErrorMarkers start="#-#-#-#-#" end='#\("\n"\|\)-\("\n"\|\)#\("\n"\|\)-\("\n"\|\)#\("\n"\|\)-\("\n"\|\)#\("\n"\|\)-\("\n"\|\)#\("\n"\|\)\\n' contained
" Obsolete messages
-syn match poObsolete "^#\~.*$"
+syn match poObsolete "^#\~.*$"
" KDE Name= handling
syn match poKDEdesktopFile "\"\(Name\|Comment\|GenericName\|Description\|Keywords\|About\)="ms=s+1,me=e-1
" Accelerator keys - this messes up if the preceding or following char is a multibyte unicode char
-syn match poAcceleratorId contained "[^&_~][&_~]\(\a\|\d\)[^:]"ms=s+1,me=e-1
+syn match poAcceleratorId contained "[^&_~][&_~]\(\a\|\d\)[^:]"ms=s+1,me=e-1
syn match poAcceleratorStr contained "[^&_~][&_~]\(\a\|\d\)[^:]"ms=s+1,me=e-1 contains=@Spell
" Variables simple
@@ -86,11 +117,10 @@ hi def link poComment Comment
hi def link poCommentAutomatic Comment
hi def link poCommentTranslator Comment
hi def link poCommentFlags Special
-hi def link poCommentDiff Comment
+hi def link poCommentPrevious Comment
hi def link poCopyrightUnset Todo
hi def link poFlagFuzzy Todo
-hi def link poDiffOld Todo
-hi def link poDiffNew Special
+hi def link poFlagFormat Todo
hi def link poObsolete Comment
hi def link poStatementMsgid Statement