aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.clangd2
-rw-r--r--.github/labeler.yml2
-rw-r--r--.github/workflows/ci.yml4
-rwxr-xr-x.github/workflows/env.sh11
-rw-r--r--.github/workflows/release.yml49
-rw-r--r--CMakeLists.txt16
-rw-r--r--CONTRIBUTING.md14
-rw-r--r--runtime/autoload/dist/ft.vim11
-rw-r--r--runtime/autoload/provider/clipboard.vim12
-rw-r--r--runtime/doc/api.txt2
-rw-r--r--runtime/doc/change.txt17
-rw-r--r--runtime/doc/cmdline.txt3
-rw-r--r--runtime/doc/eval.txt3
-rw-r--r--runtime/doc/index.txt13
-rw-r--r--runtime/doc/lsp.txt15
-rw-r--r--runtime/doc/map.txt2
-rw-r--r--runtime/doc/options.txt12
-rw-r--r--runtime/doc/quickfix.txt5
-rw-r--r--runtime/doc/starting.txt8
-rw-r--r--runtime/doc/tips.txt2
-rw-r--r--runtime/doc/ui.txt14
-rw-r--r--runtime/doc/usr_41.txt13
-rw-r--r--runtime/doc/various.txt3
-rw-r--r--runtime/doc/vim_diff.txt3
-rw-r--r--runtime/filetype.vim16
-rw-r--r--runtime/lua/vim/_meta.lua147
-rw-r--r--runtime/lua/vim/lsp/buf.lua2
-rw-r--r--runtime/lua/vim/lsp/diagnostic.lua6
-rw-r--r--runtime/lua/vim/lsp/handlers.lua4
-rw-r--r--runtime/lua/vim/lsp/rpc.lua13
-rw-r--r--runtime/lua/vim/lsp/util.lua21
-rw-r--r--runtime/syntax/lsp_markdown.vim10
-rw-r--r--runtime/syntax/vim.vim11
-rw-r--r--scripts/genvimvim.lua2
-rwxr-xr-xscripts/vim-patch.sh5
-rw-r--r--src/nvim/api/ui_events.in.h3
-rw-r--r--src/nvim/ascii.h2
-rw-r--r--src/nvim/buffer.c8
-rw-r--r--src/nvim/eval.c77
-rw-r--r--src/nvim/eval/funcs.c31
-rw-r--r--src/nvim/eval/typval.c76
-rw-r--r--src/nvim/eval/typval.h2
-rw-r--r--src/nvim/eval/userfunc.c8
-rw-r--r--src/nvim/event/multiqueue.c6
-rw-r--r--src/nvim/event/multiqueue.h2
-rw-r--r--src/nvim/ex_cmds.c32
-rw-r--r--src/nvim/ex_cmds2.c16
-rw-r--r--src/nvim/ex_docmd.c4
-rw-r--r--src/nvim/lua/executor.c10
-rw-r--r--src/nvim/lua/treesitter.c5
-rw-r--r--src/nvim/marktree.c1
-rw-r--r--src/nvim/ops.c6
-rw-r--r--src/nvim/option.c6
-rw-r--r--src/nvim/options.lua2
-rw-r--r--src/nvim/path.c48
-rw-r--r--src/nvim/quickfix.c153
-rw-r--r--src/nvim/screen.c19
-rw-r--r--src/nvim/testdir/test_autocmd.vim75
-rw-r--r--src/nvim/testdir/test_filetype.vim42
-rw-r--r--src/nvim/testdir/test_global.vim4
-rw-r--r--src/nvim/testdir/test_let.vim10
-rw-r--r--src/nvim/testdir/test_quickfix.vim129
-rw-r--r--src/nvim/testdir/test_visual.vim22
-rw-r--r--src/nvim/tui/tui.c31
-rw-r--r--src/nvim/window.c37
-rw-r--r--test/functional/api/vim_spec.lua3
-rw-r--r--test/functional/autocmd/autocmd_spec.lua11
-rw-r--r--test/functional/core/startup_spec.lua5
-rw-r--r--test/functional/eval/system_spec.lua31
-rw-r--r--test/functional/ex_cmds/make_spec.lua44
-rw-r--r--test/functional/ex_cmds/source_spec.lua25
-rw-r--r--test/functional/fixtures/fake-lsp-server.lua17
-rw-r--r--test/functional/helpers.lua14
-rw-r--r--test/functional/legacy/memory_usage_spec.lua32
-rw-r--r--test/functional/lua/buffer_updates_spec.lua33
-rw-r--r--test/functional/lua/vim_spec.lua248
-rw-r--r--test/functional/plugin/lsp_spec.lua162
-rw-r--r--test/functional/provider/clipboard_spec.lua14
-rw-r--r--test/functional/ui/tabline_spec.lua43
-rw-r--r--test/unit/eval/typval_spec.lua2
-rw-r--r--test/unit/marktree_spec.lua15
-rw-r--r--third-party/CMakeLists.txt5
82 files changed, 1661 insertions, 383 deletions
diff --git a/.clangd b/.clangd
new file mode 100644
index 0000000000..d7911aaf64
--- /dev/null
+++ b/.clangd
@@ -0,0 +1,2 @@
+CompileFlags:
+ CompilationDatabase: build/ # Search build/ directory for compile_commands.json
diff --git a/.github/labeler.yml b/.github/labeler.yml
index 8a7c0639f3..282c6b1e7c 100644
--- a/.github/labeler.yml
+++ b/.github/labeler.yml
@@ -9,7 +9,7 @@
"tui":
- src/nvim/tui/tui.*
-"tree-sitter":
+"treesitter":
- src/nvim/lua/treesitter.*
- runtime/lua/vim/treesitter.lua
- runtime/lua/vim/treesitter/*
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 72a6be304c..aa16a94802 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -29,6 +29,10 @@ jobs:
- cc: clang
runner: macos-10.15
os: osx
+ - flavor: functionaltest-lua
+ cc: gcc
+ runner: ubuntu-20.04
+ os: linux
runs-on: ${{ matrix.runner }}
if: github.event.pull_request.draft == false
env:
diff --git a/.github/workflows/env.sh b/.github/workflows/env.sh
index 459ed669eb..a30e06ae26 100755
--- a/.github/workflows/env.sh
+++ b/.github/workflows/env.sh
@@ -20,13 +20,13 @@ VALGRIND_LOG=$GITHUB_WORKSPACE/build/log/valgrind-%p.log
CACHE_NVIM_DEPS_DIR=$HOME/.cache/nvim-deps
CACHE_MARKER=$HOME/.cache/nvim-deps/.ci_cache_marker
CCACHE_BASEDIR=$GITHUB_WORKSPACE
-DEPS_CMAKE_FLAGS=-DUSE_BUNDLED_GPERF=OFF
-FUNCTIONALTEST=functionaltest
CCACHE_COMPRESS=1
CCACHE_SLOPPINESS=time_macros,file_macro
CCACHE_DIR=$HOME/.ccache
EOF
+DEPS_CMAKE_FLAGS=-DUSE_BUNDLED_GPERF=OFF
+FUNCTIONALTEST=functionaltest
BUILD_FLAGS="CMAKE_FLAGS=-DCI_BUILD=ON -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX:PATH=$HOME/nvim-install -DBUSTED_OUTPUT_TYPE=nvim -DDEPS_PREFIX=$HOME/nvim-deps/usr -DMIN_LOG_LEVEL=3"
case "$FLAVOR" in
@@ -49,10 +49,17 @@ EOF
CI_TARGET=lint
EOF
;;
+ functionaltest-lua)
+ BUILD_FLAGS="$BUILD_FLAGS -DPREFER_LUA=ON"
+ FUNCTIONALTEST=functionaltest-lua
+ DEPS_CMAKE_FLAGS="$DEPS_CMAKE_FLAGS -DUSE_BUNDLED_LUAJIT=OFF"
+ ;;
*)
;;
esac
cat <<EOF >> "$GITHUB_ENV"
$BUILD_FLAGS
+DEPS_CMAKE_FLAGS=$DEPS_CMAKE_FLAGS
+FUNCTIONALTEST=$FUNCTIONALTEST
EOF
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index def934c597..ff7562bf20 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -160,6 +160,38 @@ jobs:
with:
delete_release: true
tag_name: nightly
+ # `sha256sum` outputs <sha> <path>, so we cd into each dir to drop the
+ # containing folder from the output.
+ - name: Generate Linux64 SHA256 checksums
+ run: |
+ cd ./nvim-linux64
+ sha256sum nvim-linux64.tar.gz > nvim-linux64.tar.gz.sha256sum
+ echo "SHA_LINUX_64=$(cat nvim-linux64.tar.gz.sha256sum)" >> $GITHUB_ENV
+ - name: Generate App Image SHA256 checksums
+ run: |
+ cd ./appimage
+ sha256sum nvim.appimage > nvim.appimage.sha256sum
+ echo "SHA_APP_IMAGE=$(cat nvim.appimage.sha256sum)" >> $GITHUB_ENV
+ - name: Generate App Image Zsync SHA256 checksums
+ run: |
+ cd ./appimage
+ sha256sum nvim.appimage.zsync > nvim.appimage.zsync.sha256sum
+ echo "SHA_APP_IMAGE_ZSYNC=$(cat nvim.appimage.zsync.sha256sum)" >> $GITHUB_ENV
+ - name: Generate macOS SHA256 checksums
+ run: |
+ cd ./nvim-macos
+ sha256sum nvim-macos.tar.gz > nvim-macos.tar.gz.sha256sum
+ echo "SHA_MACOS=$(cat nvim-macos.tar.gz.sha256sum)" >> $GITHUB_ENV
+ - name: Generate Win32 SHA256 checksums
+ run: |
+ cd ./nvim-win32
+ sha256sum nvim-win32.zip > nvim-win32.zip.sha256sum
+ echo "SHA_WIN_32=$(cat nvim-win32.zip.sha256sum)" >> $GITHUB_ENV
+ - name: Generate Win64 SHA256 checksums
+ run: |
+ cd ./nvim-win64
+ sha256sum nvim-win64.zip > nvim-win64.zip.sha256sum
+ echo "SHA_WIN_64=$(cat nvim-win64.zip.sha256sum)" >> $GITHUB_ENV
- uses: meeDamian/github-release@2.0
with:
token: ${{ secrets.GITHUB_TOKEN }}
@@ -171,11 +203,17 @@ jobs:
allow_override: ${{ env.TAG_NAME == 'nightly' }}
files: |
nvim-macos.tar.gz:./nvim-macos/nvim-macos.tar.gz
+ nvim-macos.tar.gz.sha256sum:./nvim-macos/nvim-macos.tar.gz.sha256sum
nvim-linux64.tar.gz:./nvim-linux64/nvim-linux64.tar.gz
+ nvim-linux64.tar.gz.sha256sum:./nvim-linux64/nvim-linux64.tar.gz.sha256sum
nvim.appimage:./appimage/nvim.appimage
+ nvim.appimage.sha256sum:./appimage/nvim.appimage.sha256sum
nvim.appimage.zsync:./appimage/nvim.appimage.zsync
+ nvim.appimage.zsync.sha256sum:./appimage/nvim.appimage.zsync.sha256sum
nvim-win32.zip:./nvim-win32/nvim-win32.zip
+ nvim-win32.zip.sha256sum:./nvim-win32/nvim-win32.zip.sha256sum
nvim-win64.zip:./nvim-win64/nvim-win64.zip
+ nvim-win64.zip.sha256sum:./nvim-win64/nvim-win64.zip.sha256sum
body: |
${{ env.SUBJECT }}
```
@@ -207,3 +245,14 @@ jobs:
### Other
- Install by [package manager](https://github.com/neovim/neovim/wiki/Installing-Neovim)
+
+ ## SHA256 Checksums
+
+ ```
+ ${{ env.SHA_LINUX_64 }}
+ ${{ env.SHA_APP_IMAGE }}
+ ${{ env.SHA_APP_IMAGE_ZSYNC }}
+ ${{ env.SHA_MACOS }}
+ ${{ env.SHA_WIN_64 }}
+ ${{ env.SHA_WIN_32 }}
+ ```
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 346600740e..0bb2695cf4 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -393,6 +393,22 @@ include_directories(SYSTEM ${LIBLUV_INCLUDE_DIRS})
find_package(TreeSitter REQUIRED)
include_directories(SYSTEM ${TreeSitter_INCLUDE_DIRS})
+list(APPEND CMAKE_REQUIRED_INCLUDES "${TreeSitter_INCLUDE_DIRS}")
+list(APPEND CMAKE_REQUIRED_LIBRARIES "${TreeSitter_LIBRARIES}")
+check_c_source_compiles("
+#include <tree_sitter/api.h>
+int
+main(void)
+{
+ TSQueryCursor *cursor = ts_query_cursor_new();
+ ts_query_cursor_set_match_limit(cursor, 32);
+ return 0;
+}
+" TS_HAS_SET_MATCH_LIMIT)
+if(TS_HAS_SET_MATCH_LIMIT)
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DNVIM_TS_HAS_SET_MATCH_LIMIT")
+endif()
+
# Note: The test lib requires LuaJIT; it will be skipped if LuaJIT is missing.
option(PREFER_LUA "Prefer Lua over LuaJIT in the nvim executable." OFF)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 94c371b62d..27fd2b97bb 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -20,6 +20,7 @@ Reporting problems
- Update Neovim to the latest version to see if your problem persists.
- Disable plugins incrementally, to narrow down the cause of the issue.
- When reporting a crash, [include a stacktrace](https://github.com/neovim/neovim/wiki/FAQ#backtrace-linux).
+- Use [ASAN/UBSAN](#clang-sanitizers-asan-and-ubsan) to get detailed errors for segfaults and undefined behavior.
- [Bisect][git-bisect] to the cause of a regression, if you are able. This is _extremely_ helpful.
- Check `$NVIM_LOG_FILE`, if it exists.
- Include `cmake --system-information` for build-related issues.
@@ -172,7 +173,20 @@ master build. To view the defects, just request access; you will be approved.
```
git log --oneline --no-merges --grep coverity
```
+
+### Clang sanitizers (ASAN and UBSAN)
+ ASAN/UBSAN can be used to detect memory errors and other common forms of undefined behavior at runtime in debug builds.
+ To build neovim with sanitizers enabled, use
+ ```
+ rm -rf build && CMAKE_EXTRA_FLAGS="-DCMAKE_C_COMPILER=clang -DCLANG_ASAN_UBSAN=1" make
+ ```
+ When running neovim, use
+ ```
+ UBSAN_OPTIONS=print_stacktrace=1 ASAN_OPTIONS=log_path=/tmp/nvim_asan nvim args...
+ ```
+ If neovim exits unexpectedly, check `/tmp/nvim_asan.{PID}` (or your preferred `log_path`) for log files with error messages.
+
Coding
------
diff --git a/runtime/autoload/dist/ft.vim b/runtime/autoload/dist/ft.vim
index 1ac74b5785..ac80659113 100644
--- a/runtime/autoload/dist/ft.vim
+++ b/runtime/autoload/dist/ft.vim
@@ -172,6 +172,17 @@ func dist#ft#FTent()
setf dtd
endfunc
+func dist#ft#ExCheck()
+ let lines = getline(1, min([line("$"), 100]))
+ if exists('g:filetype_euphoria')
+ exe 'setf ' . g:filetype_euphoria
+ elseif match(lines, '^--\|^ifdef\>\|^include\>') > -1
+ setf euphoria3
+ else
+ setf elixir
+ endif
+endfunc
+
func dist#ft#EuphoriaCheck()
if exists('g:filetype_euphoria')
exe 'setf ' . g:filetype_euphoria
diff --git a/runtime/autoload/provider/clipboard.vim b/runtime/autoload/provider/clipboard.vim
index 07f37d604f..dea79f21f0 100644
--- a/runtime/autoload/provider/clipboard.vim
+++ b/runtime/autoload/provider/clipboard.vim
@@ -156,7 +156,14 @@ function! s:clipboard.get(reg) abort
elseif s:selections[a:reg].owner > 0
return s:selections[a:reg].data
end
- return s:try_cmd(s:paste[a:reg])
+
+ let clipboard_data = s:try_cmd(s:paste[a:reg])
+ if match(&clipboard, '\v(unnamed|unnamedplus)') >= 0 && get(s:selections[a:reg].data, 0, []) == clipboard_data
+ " When system clipboard return is same as our cache return the cache
+ " as it contains regtype information
+ return s:selections[a:reg].data
+ end
+ return clipboard_data
endfunction
function! s:clipboard.set(lines, regtype, reg) abort
@@ -175,6 +182,9 @@ function! s:clipboard.set(lines, regtype, reg) abort
if s:cache_enabled == 0
call s:try_cmd(s:copy[a:reg], a:lines)
+ "Cache it anyway we can compare it later to get regtype of the yank
+ let s:selections[a:reg] = copy(s:selection)
+ let s:selections[a:reg].data = [a:lines, a:regtype]
return 0
end
diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt
index 919417f3a0..07c45c9298 100644
--- a/runtime/doc/api.txt
+++ b/runtime/doc/api.txt
@@ -422,7 +422,7 @@ Buffer text can be highlighted by typical mechanisms (syntax highlighting,
options from the current window; specify `style=minimal` in |nvim_open_win()|
to disable various visual features such as the 'number' column.
-Currently, floating windows don't support widgets like border or scrollbar.
+Currently, floating windows don't support some widgets like scrollbar.
Example: create a float with scratch buffer: >
diff --git a/runtime/doc/change.txt b/runtime/doc/change.txt
index 49c6d06fbf..7a63a89986 100644
--- a/runtime/doc/change.txt
+++ b/runtime/doc/change.txt
@@ -913,22 +913,7 @@ This replaces an end-of-line with a new line containing the value of $HOME. >
This replaces each 'E' character with a euro sign. Read more in |<Char->|.
-4.3 Search and replace *search-replace*
-
- *:pro* *:promptfind*
-:promptf[ind] [string]
- Put up a Search dialog. When [string] is given, it is
- used as the initial search string.
- {only for Win32 GUI}
-
- *:promptr* *:promptrepl*
-:promptr[epl] [string]
- Put up a Search/Replace dialog. When [string] is
- given, it is used as the initial search string.
- {only for Win32 GUI}
-
-
-4.4 Changing tabs *change-tabs*
+4.3 Changing tabs *change-tabs*
*:ret* *:retab* *:retab!*
:[range]ret[ab][!] [new_tabstop]
Replace all sequences of white-space containing a
diff --git a/runtime/doc/cmdline.txt b/runtime/doc/cmdline.txt
index dcdc2384dc..2db694cf07 100644
--- a/runtime/doc/cmdline.txt
+++ b/runtime/doc/cmdline.txt
@@ -532,7 +532,6 @@ that see the '"' as part of their argument:
:normal
:ownsyntax
:popup
- :promptfind (and the like)
:registers
:return
:sort
@@ -571,8 +570,6 @@ followed by another Vim command:
:make
:normal
:perlfile
- :promptfind
- :promptrepl
:pyfile
:python
:registers
diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt
index 9f44c016f0..bf7f2b21de 100644
--- a/runtime/doc/eval.txt
+++ b/runtime/doc/eval.txt
@@ -7879,7 +7879,8 @@ setqflist({list} [, {action}[, {what}]]) *setqflist()*
the last quickfix list.
quickfixtextfunc
function to get the text to display in the
- quickfix window. Refer to
+ quickfix window. The value can be the name of
+ a function or a funcref or a lambda. Refer to
|quickfix-window-function| for an explanation
of how to write the function and an example.
title quickfix list title text. See |quickfix-title|
diff --git a/runtime/doc/index.txt b/runtime/doc/index.txt
index c54b588e6b..2aafc075a6 100644
--- a/runtime/doc/index.txt
+++ b/runtime/doc/index.txt
@@ -1201,6 +1201,7 @@ tag command action ~
|:cgetfile| :cg[etfile] read file with error messages
|:changes| :changes print the change list
|:chdir| :chd[ir] change directory
+|:checkhealth| :checkh[ealth] run healthchecks
|:checkpath| :che[ckpath] list included files
|:checktime| :checkt[ime] check timestamp of loaded buffers
|:chistory| :chi[story] list the error lists
@@ -1278,6 +1279,7 @@ tag command action ~
|:endtry| :endt[ry] end previous :try
|:endwhile| :endw[hile] end previous :while
|:enew| :ene[w] edit a new, unnamed buffer
+|:eval| :ev[al] evaluate an expression and discard the result
|:ex| :ex same as ":edit"
|:execute| :exe[cute] execute result of expressions
|:exit| :exi[t] same as ":xit"
@@ -1451,14 +1453,12 @@ tag command action ~
|:packloadall| :packl[oadall] load all packages under 'packpath'
|:pclose| :pc[lose] close preview window
|:pedit| :ped[it] edit file in the preview window
-|:perl| :perl execute perl command
-|:perldo| :perldo execute perl command for each line
-|:perfile| :perlfile execute perl script file
+|:perl| :pe[rl] execute perl command
+|:perldo| :perld[o] execute perl command for each line
+|:perlfile| :perlf[ile] execute perl script file
|:print| :p[rint] print lines
|:profdel| :profd[el] stop profiling a function or script
|:profile| :prof[ile] profiling functions and scripts
-|:promptfind| :pro[mptfind] open GUI dialog for searching
-|:promptrepl| :promptr[epl] open GUI dialog for search/replace
|:pop| :po[p] jump to older entry in tag stack
|:popup| :popu[p] popup a menu by name
|:ppop| :pp[op] ":pop" in preview window
@@ -1537,7 +1537,6 @@ tag command action ~
buffer list
|:scriptnames| :scr[iptnames] list names of all sourced Vim scripts
|:scriptencoding| :scripte[ncoding] encoding used in sourced Vim script
-|:scriptversion| :scriptv[ersion] version of Vim script used
|:scscope| :scs[cope] split window and execute cscope command
|:set| :se[t] show or set options
|:setfiletype| :setf[iletype] set 'filetype', unless it was set already
@@ -1549,8 +1548,6 @@ tag command action ~
|:sign| :sig[n] manipulate signs
|:silent| :sil[ent] run a command silently
|:sleep| :sl[eep] do nothing for a few seconds
-|:sleep!| :sl[eep]! do nothing for a few seconds, without the
- cursor visible
|:slast| :sla[st] split window and go to last file in the
argument list
|:smagic| :sm[agic] :substitute with 'magic'
diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt
index aa95245bd0..d6ef761bcb 100644
--- a/runtime/doc/lsp.txt
+++ b/runtime/doc/lsp.txt
@@ -135,12 +135,6 @@ FAQ *lsp-faq*
autocmd BufWritePre *.rs lua vim.lsp.buf.formatting_sync(nil, 1000)
<
- *vim.lsp.callbacks*
-- Q: What happened to `vim.lsp.callbacks`?
- A: After better defining the interface of |lsp-handler|s, we thought it best
- to remove the generic usage of `callbacks` and transform to `handlers`.
- Due to this, `vim.lsp.callbacks` was renamed to |vim.lsp.handlers|.
-
*lsp-vs-treesitter*
- Q: How do LSP and Treesitter compare?
A: LSP requires a client and language server. The language server uses
@@ -525,6 +519,15 @@ LspDiagnosticsSignHint
Used for "Hint" signs in sign column.
See |vim.lsp.diagnostic.set_signs()|
+ *lsp-highlight-codelens*
+
+Highlight groups related to |lsp-codelens| functionality.
+
+ *hl-LspCodeLens*
+LspCodeLens
+ Used to color the virtual text of the codelens. See
+ |nvim_buf_set_virtual_text()|.
+
==============================================================================
AUTOCOMMANDS *lsp-autocommands*
diff --git a/runtime/doc/map.txt b/runtime/doc/map.txt
index 77cbf7d9b7..10d503e180 100644
--- a/runtime/doc/map.txt
+++ b/runtime/doc/map.txt
@@ -855,7 +855,7 @@ Here is an example that counts the number of spaces with <F4>: >
set clipboard= selection=inclusive
let commands = #{line: "'[V']y", char: "`[v`]y", block: "`[\<c-v>`]y"}
silent exe 'noautocmd keepjumps normal! ' .. get(commands, a:type, '')
- echom getreg('"')->count(' ')
+ echom count(getreg('"'), ' ')
finally
call setreg('"', reg_save)
call setpos("'<", visual_marks_save[0])
diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt
index cc9696e536..791fb8664e 100644
--- a/runtime/doc/options.txt
+++ b/runtime/doc/options.txt
@@ -4607,7 +4607,8 @@ A jump table for the options with a short description can be found at |Q_op|.
customize the information displayed in the quickfix or location window
for each entry in the corresponding quickfix or location list. See
|quickfix-window-function| for an explanation of how to write the
- function and an example.
+ function and an example. The value can be the name of a function or a
+ lambda.
This option cannot be set from a |modeline| or in the |sandbox|, for
security reasons.
@@ -5237,11 +5238,12 @@ A jump table for the options with a short description can be found at |Q_op|.
Note that such processing is done after |:set| did its own round of
unescaping, so to keep yourself sane use |:let-&| like shown above.
*shell-powershell*
- To use powershell: >
+ To use PowerShell: >
let &shell = has('win32') ? 'powershell' : 'pwsh'
- set shellquote= shellpipe=\| shellxquote=
- set shellcmdflag=-NoLogo\ -NoProfile\ -ExecutionPolicy\ RemoteSigned\ -Command
- set shellredir=\|\ Out-File\ -Encoding\ UTF8
+ let &shellcmdflag = '-NoLogo -NoProfile -ExecutionPolicy RemoteSigned -Command [Console]::InputEncoding=[Console]::OutputEncoding=[System.Text.Encoding]::UTF8;'
+ let &shellredir = '2>&1 | Out-File -Encoding UTF8 %s; exit $LastExitCode'
+ let &shellpipe = '2>&1 | Out-File -Encoding UTF8 %s; exit $LastExitCode'
+ set shellquote= shellxquote=
< This option cannot be set from a |modeline| or in the |sandbox|, for
security reasons.
diff --git a/runtime/doc/quickfix.txt b/runtime/doc/quickfix.txt
index ebc7e2a1b4..563fb0c962 100644
--- a/runtime/doc/quickfix.txt
+++ b/runtime/doc/quickfix.txt
@@ -1919,7 +1919,10 @@ The function should return a single line of text to display in the quickfix
window for each entry from start_idx to end_idx. The function can obtain
information about the entries using the |getqflist()| function and specifying
the quickfix list identifier "id". For a location list, getloclist() function
-can be used with the 'winid' argument.
+can be used with the 'winid' argument. If an empty list is returned, then the
+default format is used to display all the entries. If an item in the returned
+list is an empty string, then the default format is used to display the
+corresponding entry.
If a quickfix or location list specific customization is needed, then the
'quickfixtextfunc' attribute of the list can be set using the |setqflist()| or
diff --git a/runtime/doc/starting.txt b/runtime/doc/starting.txt
index 2110b88330..5431ce3bd8 100644
--- a/runtime/doc/starting.txt
+++ b/runtime/doc/starting.txt
@@ -416,11 +416,11 @@ accordingly. Vim proceeds in this order:
the same time. Each line in a "init.vim" is executed as an Ex command
line. See also |vimrc-intro| and |base-directories|.
- The Nvim config file is "init.vim", located at:
- Unix ~/.config/nvim/init.vim
- Windows ~/AppData/Local/nvim/init.vim
+ The config file is located at:
+ Unix ~/.config/nvim/init.vim (or init.lua)
+ Windows ~/AppData/Local/nvim/init.vim (or init.lua)
or if |$XDG_CONFIG_HOME| is defined:
- $XDG_CONFIG_HOME/nvim/init.vim
+ $XDG_CONFIG_HOME/nvim/init.vim (or init.lua)
If Nvim was started with "-u {file}" then {file} is used as the config
and all initializations until 5. are skipped. $MYVIMRC is not set.
diff --git a/runtime/doc/tips.txt b/runtime/doc/tips.txt
index bcb2e48cbf..27b6ca2885 100644
--- a/runtime/doc/tips.txt
+++ b/runtime/doc/tips.txt
@@ -462,7 +462,7 @@ the current window, try this custom `:HelpCurwin` command:
execute mods .. ' helpclose'
let s:did_open_help = v:true
endif
- if !getcompletion(a:subject, 'help')->empty()
+ if !empty(getcompletion(a:subject, 'help'))
execute mods .. ' edit ' .. &helpfile
endif
return 'help ' .. a:subject
diff --git a/runtime/doc/ui.txt b/runtime/doc/ui.txt
index 82406898c8..e7be14e732 100644
--- a/runtime/doc/ui.txt
+++ b/runtime/doc/ui.txt
@@ -219,8 +219,8 @@ the editor.
["busy_start"]
["busy_stop"]
- Nvim started or stopped being busy, and possibly not responsive to
- user input. This could be indicated to the user by hiding the cursor.
+ Indicates to the UI that it must stop rendering the cursor. This event
+ is misnamed and does not actually have anything to do with busyness.
["suspend"]
|:suspend| command or |CTRL-Z| mapping is used. A terminal client (or
@@ -631,11 +631,13 @@ Tabline Events *ui-tabline*
Activated by the `ext_tabline` |ui-option|.
-["tabline_update", curtab, tabs]
+["tabline_update", curtab, tabs, curbuf, buffers]
Tabline was updated. UIs should present this data in a custom tabline
- widget.
- curtab: Current Tabpage
- tabs: List of Dicts [{ "tab": Tabpage, "name": String }, ...]
+ widget. Note: options `curbuf` + `buffers` were added in API7.
+ curtab: Current Tabpage
+ tabs: List of Dicts [{ "tab": Tabpage, "name": String }, ...]
+ curbuf: Current buffer handle.
+ buffers: List of Dicts [{ "buffer": buffer handle, "name": String}, ...]
==============================================================================
Cmdline Events *ui-cmdline*
diff --git a/runtime/doc/usr_41.txt b/runtime/doc/usr_41.txt
index f92cb3c509..a190bf2f27 100644
--- a/runtime/doc/usr_41.txt
+++ b/runtime/doc/usr_41.txt
@@ -99,8 +99,6 @@ and the value of the variable i. Since i is one, this will print:
Then there is the ":let i += 1" command. This does the same thing as
":let i = i + 1". This adds one to the variable i and assigns the new value
to the same variable.
-Note: this is how it works in legacy Vim script, which is what we discuss in
-this file.
The example was given to explain the commands, but would you really want to
make such a loop, it can be written much more compact: >
@@ -120,24 +118,23 @@ Numbers can be decimal, hexadecimal, octal or binary.
A hexadecimal number starts with "0x" or "0X". For example "0x1f" is decimal
31.
-An octal number starts with "0o", "0O" or a zero and another digit. "0o17" is
-decimal 15. Using just a zero prefix is not supported in Vim9 script.
+An octal number starts with a zero and another digit. "017" is decimal 15.
A binary number starts with "0b" or "0B". For example "0b101" is decimal 5.
A decimal number is just digits. Careful: don't put a zero before a decimal
-number, it will be interpreted as an octal number in legacy script!
+number, it will be interpreted as an octal number!
The ":echo" command always prints decimal numbers. Example: >
- :echo 0x7f 0o36
+ :echo 0x7f 036
< 127 30 ~
A number is made negative with a minus sign. This also works for hexadecimal,
octal and binary numbers. A minus sign is also used for subtraction. Compare
this with the previous example: >
- :echo 0x7f -0o36
+ :echo 0x7f -036
< 97 ~
White space in an expression is ignored. However, it's recommended to use it
@@ -145,7 +142,7 @@ for separating items, to make the expression easier to read. For example, to
avoid the confusion with a negative number above, put a space between the
minus sign and the following number: >
- :echo 0x7f - 0o36
+ :echo 0x7f - 036
==============================================================================
*41.2* Variables
diff --git a/runtime/doc/various.txt b/runtime/doc/various.txt
index d5c07d9622..ec91b6e29a 100644
--- a/runtime/doc/various.txt
+++ b/runtime/doc/various.txt
@@ -506,7 +506,8 @@ gO Show a filetype-specific, navigable "outline" of the
Queued messages are processed during the sleep.
*:sl!* *:sleep!*
-:[N]sl[eep]! [N] [m] Same as above, but hide the cursor.
+:[N]sl[eep]! [N] [m] Same as above. Unlike Vim, it does not hide the
+ cursor. |vim-differences|
==============================================================================
2. Using Vim like less or more *less*
diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt
index bb30495d77..27c4b82aca 100644
--- a/runtime/doc/vim_diff.txt
+++ b/runtime/doc/vim_diff.txt
@@ -421,7 +421,10 @@ Commands:
:mode (no longer accepts an argument)
:open
:Print
+ :promptfind
+ :promptrepl
:shell
+ :sleep! (does not hide the cursor; same as :sleep)
:smile
:tearoff
diff --git a/runtime/filetype.vim b/runtime/filetype.vim
index 09a1d1d0e6..2617d8ffc0 100644
--- a/runtime/filetype.vim
+++ b/runtime/filetype.vim
@@ -389,7 +389,7 @@ au BufNewFile,BufRead *.cfm,*.cfi,*.cfc setf cf
" Configure scripts
au BufNewFile,BufRead configure.in,configure.ac setf config
-" CUDA Cumpute Unified Device Architecture
+" CUDA Compute Unified Device Architecture
au BufNewFile,BufRead *.cu,*.cuh setf cuda
" Dockerfilb; Podman uses the same syntax with name Containerfile
@@ -404,8 +404,15 @@ au BufNewFile,BufRead *enlightenment/*.cfg setf c
" Eterm
au BufNewFile,BufRead *Eterm/*.cfg setf eterm
+" Elixir or Euphoria
+au BufNewFile,BufRead *.ex call dist#ft#ExCheck()
+
+" Elixir
+au BufRead,BufNewFile mix.lock,*.exs setf elixir
+au BufRead,BufNewFile *.eex,*.leex setf eelixir
+
" Euphoria 3 or 4
-au BufNewFile,BufRead *.eu,*.ew,*.ex,*.exu,*.exw call dist#ft#EuphoriaCheck()
+au BufNewFile,BufRead *.eu,*.ew,*.exu,*.exw call dist#ft#EuphoriaCheck()
if has("fname_case")
au BufNewFile,BufRead *.EU,*.EW,*.EX,*.EXU,*.EXW call dist#ft#EuphoriaCheck()
endif
@@ -851,6 +858,9 @@ au BufNewFile,BufRead *.jov,*.j73,*.jovial setf jovial
" JSON
au BufNewFile,BufRead *.json,*.jsonp,*.webmanifest setf json
+" JSON Patch (RFC 6902)
+au BufNewFile,BufRead *.json-patch setf json
+
" Jupyter Notebook is also json
au BufNewFile,BufRead *.ipynb setf json
@@ -1495,7 +1505,7 @@ au BufNewFile,BufRead *.sass setf sass
au BufNewFile,BufRead *.sa setf sather
" Scala
-au BufNewFile,BufRead *.scala setf scala
+au BufNewFile,BufRead *.scala,*.sc setf scala
" SBT - Scala Build Tool
au BufNewFile,BufRead *.sbt setf sbt
diff --git a/runtime/lua/vim/_meta.lua b/runtime/lua/vim/_meta.lua
index 02d1154df4..b1f935541c 100644
--- a/runtime/lua/vim/_meta.lua
+++ b/runtime/lua/vim/_meta.lua
@@ -209,12 +209,17 @@ local function get_scoped_option(k, set_type)
end
if is_window_option(info) then
- if vim.api.nvim_get_option_info(k).was_set then
- local was_set, value = pcall(a.nvim_win_get_option, 0, k)
- if was_set then return value end
+ local ok, value = pcall(a.nvim_win_get_option, 0, k)
+ if ok then
+ return value
end
- return a.nvim_get_option(k)
+ local global_ok, global_val = pcall(a.nvim_get_option, k)
+ if global_ok then
+ return global_val
+ end
+
+ error("win_get: This should never happen. File an issue and tag @tjdevries")
end
error("This fallback case should not be possible. " .. k)
@@ -262,7 +267,7 @@ local key_value_options = {
winhl = true,
}
----@class OptionType
+---@class OptionTypes
--- Option Type Enum
local OptionTypes = setmetatable({
BOOLEAN = 0,
@@ -306,6 +311,28 @@ local get_option_type = function(name, info)
end
+-- Check whether the OptionTypes is allowed for vim.opt
+-- If it does not match, throw an error which indicates which option causes the error.
+local function assert_valid_value(name, value, types)
+ local type_of_value = type(value)
+ for _, valid_type in ipairs(types) do
+ if valid_type == type_of_value then
+ return
+ end
+ end
+
+ error(string.format("Invalid option type '%s' for '%s', should be %s", type_of_value, name, table.concat(types, " or ")))
+end
+
+local valid_types = {
+ [OptionTypes.BOOLEAN] = { "boolean" },
+ [OptionTypes.NUMBER] = { "number" },
+ [OptionTypes.STRING] = { "string" },
+ [OptionTypes.SET] = { "string", "table" },
+ [OptionTypes.ARRAY] = { "string", "table" },
+ [OptionTypes.MAP] = { "string", "table" },
+}
+
--- Convert a lua value to a vimoption_T value
local convert_value_to_vim = (function()
-- Map of functions to take a Lua style value and convert to vimoption_T style value.
@@ -315,24 +342,41 @@ local convert_value_to_vim = (function()
[OptionTypes.NUMBER] = function(_, value) return value end,
[OptionTypes.STRING] = function(_, value) return value end,
- [OptionTypes.SET] = function(_, value)
+ [OptionTypes.SET] = function(info, value)
if type(value) == "string" then return value end
- local result = ''
- for k in pairs(value) do
- result = result .. k
- end
- return result
+ if info.flaglist and info.commalist then
+ local keys = {}
+ for k, v in pairs(value) do
+ if v then
+ table.insert(keys, k)
+ end
+ end
+
+ table.sort(keys)
+ return table.concat(keys, ",")
+ else
+ local result = ''
+ for k, v in pairs(value) do
+ if v then
+ result = result .. k
+ end
+ end
+
+ return result
+ end
end,
- [OptionTypes.ARRAY] = function(_, value)
+ [OptionTypes.ARRAY] = function(info, value)
if type(value) == "string" then return value end
- return table.concat(remove_duplicate_values(value), ",")
+ if not info.allows_duplicates then
+ value = remove_duplicate_values(value)
+ end
+ return table.concat(value, ",")
end,
[OptionTypes.MAP] = function(_, value)
if type(value) == "string" then return value end
- if type(value) == "function" then error(debug.traceback("asdf")) end
local result = {}
for opt_key, opt_value in pairs(value) do
@@ -345,7 +389,10 @@ local convert_value_to_vim = (function()
}
return function(name, info, value)
- return to_vim_value[get_option_type(name, info)](info, value)
+ local option_type = get_option_type(name, info)
+ assert_valid_value(name, value, valid_types[option_type])
+
+ return to_vim_value[option_type](info, value)
end
end)()
@@ -358,26 +405,82 @@ local convert_value_to_lua = (function()
[OptionTypes.NUMBER] = function(_, value) return value end,
[OptionTypes.STRING] = function(_, value) return value end,
- [OptionTypes.ARRAY] = function(_, value)
+ [OptionTypes.ARRAY] = function(info, value)
if type(value) == "table" then
- value = remove_duplicate_values(value)
+ if not info.allows_duplicates then
+ value = remove_duplicate_values(value)
+ end
+
return value
end
+ -- Empty strings mean that there is nothing there,
+ -- so empty table should be returned.
+ if value == '' then
+ return {}
+ end
+
+ -- Handles unescaped commas in a list.
+ if string.find(value, ",,,") then
+ local comma_split = vim.split(value, ",,,")
+ local left = comma_split[1]
+ local right = comma_split[2]
+
+ local result = {}
+ vim.list_extend(result, vim.split(left, ","))
+ table.insert(result, ",")
+ vim.list_extend(result, vim.split(right, ","))
+
+ table.sort(result)
+
+ return result
+ end
+
+ if string.find(value, ",^,,", 1, true) then
+ local comma_split = vim.split(value, ",^,,", true)
+ local left = comma_split[1]
+ local right = comma_split[2]
+
+ local result = {}
+ vim.list_extend(result, vim.split(left, ","))
+ table.insert(result, "^,")
+ vim.list_extend(result, vim.split(right, ","))
+
+ table.sort(result)
+
+ return result
+ end
+
return vim.split(value, ",")
end,
[OptionTypes.SET] = function(info, value)
if type(value) == "table" then return value end
+ -- Empty strings mean that there is nothing there,
+ -- so empty table should be returned.
+ if value == '' then
+ return {}
+ end
+
assert(info.flaglist, "That is the only one I know how to handle")
- local result = {}
- for i = 1, #value do
- result[value:sub(i, i)] = true
- end
+ if info.flaglist and info.commalist then
+ local split_value = vim.split(value, ",")
+ local result = {}
+ for _, v in ipairs(split_value) do
+ result[v] = true
+ end
- return result
+ return result
+ else
+ local result = {}
+ for i = 1, #value do
+ result[value:sub(i, i)] = true
+ end
+
+ return result
+ end
end,
[OptionTypes.MAP] = function(info, raw_value)
diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua
index ad5a348d4c..b13d662ccb 100644
--- a/runtime/lua/vim/lsp/buf.lua
+++ b/runtime/lua/vim/lsp/buf.lua
@@ -339,7 +339,7 @@ end
--- Add the folder at path to the workspace folders. If {path} is
--- not provided, the user will be prompted for a path using |input()|.
function M.add_workspace_folder(workspace_folder)
- workspace_folder = workspace_folder or npcall(vfn.input, "Workspace Folder: ", vfn.expand('%:p:h'))
+ workspace_folder = workspace_folder or npcall(vfn.input, "Workspace Folder: ", vfn.expand('%:p:h'), 'dir')
vim.api.nvim_command("redraw")
if not (workspace_folder and #workspace_folder > 0) then return end
if vim.fn.isdirectory(workspace_folder) == 0 then
diff --git a/runtime/lua/vim/lsp/diagnostic.lua b/runtime/lua/vim/lsp/diagnostic.lua
index e4acfd0711..64dde78f17 100644
--- a/runtime/lua/vim/lsp/diagnostic.lua
+++ b/runtime/lua/vim/lsp/diagnostic.lua
@@ -1214,9 +1214,9 @@ function M.set_loclist(opts)
if severity then
return d.severity == severity
end
- severity = to_severity(opts.severity_limit)
- if severity then
- return d.severity == severity
+ local severity_limit = to_severity(opts.severity_limit)
+ if severity_limit then
+ return d.severity <= severity_limit
end
return true
end
diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua
index 5d38150fc0..41852b9d88 100644
--- a/runtime/lua/vim/lsp/handlers.lua
+++ b/runtime/lua/vim/lsp/handlers.lua
@@ -196,7 +196,6 @@ M['textDocument/references'] = function(_, _, result)
if not result then return end
util.set_qflist(util.locations_to_items(result))
api.nvim_command("copen")
- api.nvim_command("wincmd p")
end
--@private
@@ -211,7 +210,6 @@ local symbol_handler = function(_, _, result, _, bufnr)
util.set_qflist(util.symbols_to_items(result, bufnr))
api.nvim_command("copen")
- api.nvim_command("wincmd p")
end
--@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_documentSymbol
M['textDocument/documentSymbol'] = symbol_handler
@@ -302,7 +300,6 @@ local function location_handler(_, method, result)
if #result > 1 then
util.set_qflist(util.locations_to_items(result))
api.nvim_command("copen")
- api.nvim_command("wincmd p")
end
else
util.jump_to_location(result)
@@ -383,7 +380,6 @@ local make_call_hierarchy_handler = function(direction)
end
util.set_qflist(items)
api.nvim_command("copen")
- api.nvim_command("wincmd p")
end
end
diff --git a/runtime/lua/vim/lsp/rpc.lua b/runtime/lua/vim/lsp/rpc.lua
index 98835d6708..4c5f02af9d 100644
--- a/runtime/lua/vim/lsp/rpc.lua
+++ b/runtime/lua/vim/lsp/rpc.lua
@@ -50,8 +50,13 @@ recursive_convert_NIL = function(v, tbl_processed)
return nil
elseif not tbl_processed[v] and type(v) == 'table' then
tbl_processed[v] = true
+ local inside_list = vim.tbl_islist(v)
return vim.tbl_map(function(x)
- return recursive_convert_NIL(x, tbl_processed)
+ if not inside_list or (inside_list and type(x) == "table") then
+ return recursive_convert_NIL(x, tbl_processed)
+ else
+ return x
+ end
end, v)
end
@@ -444,7 +449,7 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params)
method = method;
params = params;
}
- if result then
+ if result and message_callbacks then
message_callbacks[message_id] = schedule_wrap(callback)
return result, message_id
else
@@ -543,14 +548,14 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params)
-- - The server will not send a result callback after this cancellation.
-- - If the server sent this cancellation ACK after sending the result, the user of this RPC
-- client will ignore the result themselves.
- if result_id then
+ if result_id and message_callbacks then
message_callbacks[result_id] = nil
end
return
end
end
- local callback = message_callbacks[result_id]
+ local callback = message_callbacks and message_callbacks[result_id]
if callback then
message_callbacks[result_id] = nil
validate {
diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua
index 7c0132822e..195e3a0e65 100644
--- a/runtime/lua/vim/lsp/util.lua
+++ b/runtime/lua/vim/lsp/util.lua
@@ -1056,6 +1056,21 @@ function M._trim(contents, opts)
return contents
end
+-- Generates a table mapping markdown code block lang to vim syntax,
+-- based on g:markdown_fenced_languages
+-- @return a table of lang -> syntax mappings
+-- @private
+local function get_markdown_fences()
+ local fences = {}
+ for _, fence in pairs(vim.g.markdown_fenced_languages or {}) do
+ local lang, syntax = fence:match("^(.*)=(.*)$")
+ if lang then
+ fences[lang] = syntax
+ end
+ end
+ return fences
+end
+
--- Converts markdown into syntax highlighted regions by stripping the code
--- blocks and converting them into highlighted code.
--- This will by default insert a blank line separator after those code block
@@ -1138,7 +1153,7 @@ function M.stylize_markdown(bufnr, contents, opts)
table.insert(highlights, {
ft = match.ft;
start = start + 1;
- finish = #stripped + 1 - 1;
+ finish = #stripped;
})
else
table.insert(stripped, line)
@@ -1187,11 +1202,13 @@ function M.stylize_markdown(bufnr, contents, opts)
-- keep track of syntaxes we already inlcuded.
-- no need to include the same syntax more than once
local langs = {}
+ local fences = get_markdown_fences()
local function apply_syntax_to_region(ft, start, finish)
if ft == "" then
vim.cmd(string.format("syntax region markdownCode start=+\\%%%dl+ end=+\\%%%dl+ keepend extend", start, finish + 1))
return
end
+ ft = fences[ft] or ft
local name = ft..idx
idx = idx + 1
local lang = "@"..ft:upper()
@@ -1220,7 +1237,7 @@ function M.stylize_markdown(bufnr, contents, opts)
apply_syntax_to_region(h.ft, h.start, h.finish)
last = h.finish + 1
end
- if last < #stripped then
+ if last <= #stripped then
apply_syntax_to_region("lsp_markdown", last, #stripped)
end
end)
diff --git a/runtime/syntax/lsp_markdown.vim b/runtime/syntax/lsp_markdown.vim
index d9b50be54c..90d3185673 100644
--- a/runtime/syntax/lsp_markdown.vim
+++ b/runtime/syntax/lsp_markdown.vim
@@ -10,8 +10,14 @@ execute 'source' expand('<sfile>:p:h') .. '/markdown.vim'
syn cluster mkdNonListItem add=mkdEscape,mkdNbsp
-syntax region mkdEscape matchgroup=mkdEscape start=/\\\ze[\\\x60*{}\[\]()#+\-,.!_>~|"$%&'\/:;<=?@^ ]/ end=/.\zs/ keepend contains=mkdEscapeCh oneline concealends
-syntax match mkdEscapeCh /./ contained
+syn clear markdownEscape
+syntax region markdownEscape matchgroup=markdownEscape start=/\\\ze[\\\x60*{}\[\]()#+\-,.!_>~|"$%&'\/:;<=?@^ ]/ end=/./ containedin=ALL keepend oneline concealends
+
+" conceal html entities
syntax match mkdNbsp /&nbsp;/ conceal cchar=
+syntax match mkdLt /&lt;/ conceal cchar=<
+syntax match mkdGt /&gt;/ conceal cchar=>
+syntax match mkdAmp /&amp;/ conceal cchar=&
+syntax match mkdQuot /&quot;/ conceal cchar="
hi def link mkdEscape special
diff --git a/runtime/syntax/vim.vim b/runtime/syntax/vim.vim
index d395c6b1c3..55c47aa34d 100644
--- a/runtime/syntax/vim.vim
+++ b/runtime/syntax/vim.vim
@@ -149,7 +149,7 @@ syn match vimNumber '\<0[xX]\x\+' skipwhite nextgroup=vimGlobal,vimSubst
syn match vimNumber '\%(^\|\A\)\zs#\x\{6}' skipwhite nextgroup=vimGlobal,vimSubst,vimCommand,vimComment,vim9Comment
syn match vimNumber '\<0[zZ][a-zA-Z0-9.]\+' skipwhite nextgroup=vimGlobal,vimSubst,vimCommand,vimComment,vim9Comment
syn match vimNumber '0[0-7]\+' skipwhite nextgroup=vimGlobal,vimSubst,vimCommand,vimComment,vim9Comment
-syn match vimNumber '0b[01]\+' skipwhite nextgroup=vimGlobal,vimSubst,vimCommand,vimComment,vim9Comment
+syn match vimNumber '0[bB][01]\+' skipwhite nextgroup=vimGlobal,vimSubst,vimCommand,vimComment,vim9Comment
" All vimCommands are contained by vimIsCommand. {{{2
syn match vimCmdSep "[:|]\+" skipwhite nextgroup=vimAddress,vimAutoCmd,vimEcho,vimIsCommand,vimExtCmd,vimFilter,vimLet,vimMap,vimMark,vimSet,vimSyntax,vimUserCmd
@@ -355,8 +355,8 @@ syn match vimCmplxRepeat '[^a-zA-Z_/\\()]q[0-9a-zA-Z"]\>'lc=1
syn match vimCmplxRepeat '@[0-9a-z".=@:]\ze\($\|[^a-zA-Z]\>\)'
" Set command and associated set-options (vimOptions) with comment {{{2
-syn region vimSet matchgroup=vimCommand start="\<\%(setl\%[ocal]\|setg\%[lobal]\|se\%[t]\)\>" skip="\%(\\\\\)*\\." end="$" end="|" matchgroup=vimNotation end="<[cC][rR]>" keepend oneline contains=vimSetEqual,vimOption,vimErrSetting,vimComment,vim9Comment,vimSetString,vimSetMod
-syn region vimSetEqual contained start="[=:]\|[-+^]=" skip="\\\\\|\\\s" end="[| \t]\|$"me=e-1 contains=vimCtrlChar,vimSetSep,vimNotation,vimEnvvar oneline
+syn region vimSet matchgroup=vimCommand start="\<\%(setl\%[ocal]\|setg\%[lobal]\|se\%[t]\)\>" skip="\%(\\\\\)*\\." end="$" end="|" matchgroup=vimNotation end="<[cC][rR]>" oneline keepend contains=vimSetEqual,vimOption,vimErrSetting,vimComment,vim9Comment,vimSetString,vimSetMod
+ syn region vimSetEqual contained start="[=:]\|[-+^]=" skip="\\\\\|\\\s" end="[| \t]"me=e-1 end="$" contains=vimCtrlChar,vimSetSep,vimNotation,vimEnvvar
syn region vimSetString contained start=+="+hs=s+1 skip=+\\\\\|\\"+ end=+"+ contains=vimCtrlChar
syn match vimSetSep contained "[,:]"
syn match vimSetMod contained "&vim\=\|[!&?<]\|all&"
@@ -435,7 +435,8 @@ syn match vimFunc "\%(\%([sSgGbBwWtTlL]:\|<[sS][iI][dD]>\)\=\%(\w\+\.\)*\I[a-zA
syn match vimUserFunc contained "\%(\%([sSgGbBwWtTlL]:\|<[sS][iI][dD]>\)\=\%(\w\+\.\)*\I[a-zA-Z0-9_.]*\)\|\<\u[a-zA-Z0-9.]*\>\|\<if\>" contains=vimNotation
" User Command Highlighting: {{{2
-syn match vimUsrCmd '^\s*\zs\u\w*.*$'
+"syn match vimUsrCmd '^\s*\zs\u\w*.*$'
+syn match vimUsrCmd '^\s*\zs\u\%(\w*\)\@>\%([(#[]\|\s\+\%([-+*/%]\=\|\.\.\)=\)\@!'
" Errors And Warnings: {{{2
" ====================
@@ -573,7 +574,7 @@ syn case match
syn match vimHiAttribList contained "\i\+" contains=vimHiAttrib
syn match vimHiAttribList contained "\i\+,"he=e-1 contains=vimHiAttrib nextgroup=vimHiAttribList
syn case ignore
-syn keyword vimHiCtermColor contained black blue brown cyan darkblue darkcyan darkgray darkgreen darkgrey darkmagenta darkred darkyellow gray green grey lightblue lightcyan lightgray lightgreen lightgrey lightmagenta lightred magenta red white yellow
+syn keyword vimHiCtermColor contained black blue brown cyan darkblue darkcyan darkgray darkgreen darkgrey darkmagenta darkred darkyellow gray green grey grey40 grey50 grey90 lightblue lightcyan lightgray lightgreen lightgrey lightmagenta lightred lightyellow magenta red seagreen white yellow
syn match vimHiCtermColor contained "\<color\d\{1,3}\>"
syn case match
diff --git a/scripts/genvimvim.lua b/scripts/genvimvim.lua
index ccd5489fdc..2c3701bf0c 100644
--- a/scripts/genvimvim.lua
+++ b/scripts/genvimvim.lua
@@ -123,7 +123,7 @@ end
w('\n\nsyn case match')
local vimfun_start = 'syn keyword vimFuncName contained '
w('\n\n' .. vimfun_start)
-funcs = mpack.unpack(io.open(funcs_file):read("*all"))
+funcs = mpack.unpack(io.open(funcs_file, 'rb'):read("*all"))
local started = 0
for name, def in pairs(funcs) do
if name then
diff --git a/scripts/vim-patch.sh b/scripts/vim-patch.sh
index 86552c0c8d..4fd9711619 100755
--- a/scripts/vim-patch.sh
+++ b/scripts/vim-patch.sh
@@ -14,7 +14,8 @@ fi
readonly NVIM_SOURCE_DIR="${NVIM_SOURCE_DIR:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}"
readonly VIM_SOURCE_DIR_DEFAULT="${NVIM_SOURCE_DIR}/.vim-src"
readonly VIM_SOURCE_DIR="${VIM_SOURCE_DIR:-${VIM_SOURCE_DIR_DEFAULT}}"
-readonly BASENAME="$(basename "${0}")"
+BASENAME="$(basename "${0}")"
+readonly BASENAME
readonly BRANCH_PREFIX="vim-"
CREATED_FILES=()
@@ -372,7 +373,7 @@ submit_pr() {
pr_title="${pr_title// /,}" # Replace spaces with commas.
local pr_message
- pr_message="$(printf '[RFC] vim-patch:%s\n\n%s\n' "${pr_title#,}" "${pr_body}")"
+ pr_message="$(printf 'vim-patch:%s\n\n%s\n' "${pr_title#,}" "${pr_body}")"
if [[ $push_first -ne 0 ]]; then
echo "Pushing to 'origin/${checked_out_branch}'."
diff --git a/src/nvim/api/ui_events.in.h b/src/nvim/api/ui_events.in.h
index 11e21a88ea..35d39a34d7 100644
--- a/src/nvim/api/ui_events.in.h
+++ b/src/nvim/api/ui_events.in.h
@@ -130,7 +130,8 @@ void popupmenu_hide(void)
void popupmenu_select(Integer selected)
FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
-void tabline_update(Tabpage current, Array tabs)
+void tabline_update(Tabpage current, Array tabs,
+ Buffer current_buffer, Array buffers)
FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
void cmdline_show(Array content, Integer pos, String firstc, String prompt,
diff --git a/src/nvim/ascii.h b/src/nvim/ascii.h
index 7e4dee3d34..f41068ea70 100644
--- a/src/nvim/ascii.h
+++ b/src/nvim/ascii.h
@@ -31,9 +31,7 @@
#define CSI 0x9b // Control Sequence Introducer
#define CSI_STR "\233"
#define DCS 0x90 // Device Control String
-#define DCS_STR "\033P"
#define STERM 0x9c // String Terminator
-#define STERM_STR "\033\\"
#define POUND 0xA3
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index 6a50264e0f..f1f32076bf 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -840,11 +840,7 @@ static void clear_wininfo(buf_T *buf)
while (buf->b_wininfo != NULL) {
wip = buf->b_wininfo;
buf->b_wininfo = wip->wi_next;
- if (wip->wi_optset) {
- clear_winopt(&wip->wi_opt);
- deleteFoldRecurse(buf, &wip->wi_folds);
- }
- xfree(wip);
+ free_wininfo(wip, buf);
}
}
@@ -5665,7 +5661,7 @@ bool buf_contents_changed(buf_T *buf)
void
wipe_buffer(
buf_T *buf,
- int aucmd // When true trigger autocommands.
+ bool aucmd // When true trigger autocommands.
)
{
if (!aucmd) {
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index a3fa9c986f..ff019d1e07 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -2340,10 +2340,8 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv,
if (get_var_tv((const char *)lp->ll_name, (int)STRLEN(lp->ll_name),
&tv, &di, true, false) == OK) {
if ((di == NULL
- || (!var_check_ro(di->di_flags, (const char *)lp->ll_name,
- TV_CSTRING)
- && !tv_check_lock(di->di_tv.v_lock, (const char *)lp->ll_name,
- TV_CSTRING)))
+ || (!var_check_ro(di->di_flags, lp->ll_name, TV_CSTRING)
+ && !tv_check_lock(&di->di_tv, lp->ll_name, TV_CSTRING)))
&& eexe_mod_op(&tv, rettv, op) == OK) {
set_var(lp->ll_name, lp->ll_name_len, &tv, false);
}
@@ -2353,10 +2351,10 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv,
set_var_const(lp->ll_name, lp->ll_name_len, rettv, copy, is_const);
}
*endp = cc;
- } else if (tv_check_lock(lp->ll_newkey == NULL
- ? lp->ll_tv->v_lock
- : lp->ll_tv->vval.v_dict->dv_lock,
- (const char *)lp->ll_name, TV_CSTRING)) {
+ } else if (var_check_lock(lp->ll_newkey == NULL
+ ? lp->ll_tv->v_lock
+ : lp->ll_tv->vval.v_dict->dv_lock,
+ lp->ll_name, TV_CSTRING)) {
} else if (lp->ll_range) {
listitem_T *ll_li = lp->ll_li;
int ll_n1 = lp->ll_n1;
@@ -2369,9 +2367,8 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv,
// Check whether any of the list items is locked
for (ri = tv_list_first(rettv->vval.v_list);
ri != NULL && ll_li != NULL; ) {
- if (tv_check_lock(TV_LIST_ITEM_TV(ll_li)->v_lock,
- (const char *)lp->ll_name,
- TV_CSTRING)) {
+ if (var_check_lock(TV_LIST_ITEM_TV(ll_li)->v_lock, lp->ll_name,
+ TV_CSTRING)) {
return;
}
ri = TV_LIST_ITEM_NEXT(rettv->vval.v_list, ri);
@@ -2795,13 +2792,13 @@ static int do_unlet_var(lval_T *lp, char_u *name_end, exarg_T *eap,
} else if ((lp->ll_list != NULL
// ll_list is not NULL when lvalue is not in a list, NULL lists
// yield E689.
- && tv_check_lock(tv_list_locked(lp->ll_list),
- (const char *)lp->ll_name,
- lp->ll_name_len))
+ && var_check_lock(tv_list_locked(lp->ll_list),
+ lp->ll_name,
+ lp->ll_name_len))
|| (lp->ll_dict != NULL
- && tv_check_lock(lp->ll_dict->dv_lock,
- (const char *)lp->ll_name,
- lp->ll_name_len))) {
+ && var_check_lock(lp->ll_dict->dv_lock,
+ lp->ll_name,
+ lp->ll_name_len))) {
return FAIL;
} else if (lp->ll_range) {
assert(lp->ll_list != NULL);
@@ -2810,9 +2807,9 @@ static int do_unlet_var(lval_T *lp, char_u *name_end, exarg_T *eap,
listitem_T *last_li = first_li;
for (;;) {
listitem_T *const li = TV_LIST_ITEM_NEXT(lp->ll_list, lp->ll_li);
- if (tv_check_lock(TV_LIST_ITEM_TV(lp->ll_li)->v_lock,
- (const char *)lp->ll_name,
- lp->ll_name_len)) {
+ if (var_check_lock(TV_LIST_ITEM_TV(lp->ll_li)->v_lock,
+ lp->ll_name,
+ lp->ll_name_len)) {
return false;
}
lp->ll_li = li;
@@ -2897,11 +2894,11 @@ int do_unlet(const char *const name, const size_t name_len, const bool forceit)
dictitem_T *const di = TV_DICT_HI2DI(hi);
if (var_check_fixed(di->di_flags, (const char *)name, TV_CSTRING)
|| var_check_ro(di->di_flags, (const char *)name, TV_CSTRING)
- || tv_check_lock(d->dv_lock, (const char *)name, TV_CSTRING)) {
+ || var_check_lock(d->dv_lock, name, TV_CSTRING)) {
return FAIL;
}
- if (tv_check_lock(d->dv_lock, (const char *)name, TV_CSTRING)) {
+ if (var_check_lock(d->dv_lock, name, TV_CSTRING)) {
return FAIL;
}
@@ -5962,14 +5959,14 @@ void filter_map(typval_T *argvars, typval_T *rettv, int map)
if (argvars[0].v_type == VAR_LIST) {
tv_copy(&argvars[0], rettv);
if ((l = argvars[0].vval.v_list) == NULL
- || (!map && tv_check_lock(tv_list_locked(l), arg_errmsg,
- TV_TRANSLATE))) {
+ || (!map
+ && var_check_lock(tv_list_locked(l), arg_errmsg, TV_TRANSLATE))) {
return;
}
} else if (argvars[0].v_type == VAR_DICT) {
tv_copy(&argvars[0], rettv);
if ((d = argvars[0].vval.v_dict) == NULL
- || (!map && tv_check_lock(d->dv_lock, arg_errmsg, TV_TRANSLATE))) {
+ || (!map && var_check_lock(d->dv_lock, arg_errmsg, TV_TRANSLATE))) {
return;
}
} else {
@@ -6002,7 +5999,7 @@ void filter_map(typval_T *argvars, typval_T *rettv, int map)
di = TV_DICT_HI2DI(hi);
if (map
- && (tv_check_lock(di->di_tv.v_lock, arg_errmsg, TV_TRANSLATE)
+ && (var_check_lock(di->di_tv.v_lock, arg_errmsg, TV_TRANSLATE)
|| var_check_ro(di->di_flags, arg_errmsg, TV_TRANSLATE))) {
break;
}
@@ -6029,8 +6026,8 @@ void filter_map(typval_T *argvars, typval_T *rettv, int map)
for (listitem_T *li = tv_list_first(l); li != NULL;) {
if (map
- && tv_check_lock(TV_LIST_ITEM_TV(li)->v_lock, arg_errmsg,
- TV_TRANSLATE)) {
+ && var_check_lock(TV_LIST_ITEM_TV(li)->v_lock, arg_errmsg,
+ TV_TRANSLATE)) {
break;
}
vimvars[VV_KEY].vv_nr = idx;
@@ -7200,12 +7197,15 @@ bool callback_from_typval(Callback *const callback, typval_T *const arg)
r = FAIL;
} else if (arg->v_type == VAR_FUNC || arg->v_type == VAR_STRING) {
char_u *name = arg->vval.v_string;
- if (name != NULL) {
+ if (name == NULL) {
+ r = FAIL;
+ } else if (*name == NUL) {
+ callback->type = kCallbackNone;
+ callback->data.funcref = NULL;
+ } else {
func_ref(name);
callback->data.funcref = vim_strsave(name);
callback->type = kCallbackFuncref;
- } else {
- r = FAIL;
}
} else if (nlua_is_table_from_lua(arg)) {
char_u *name = nlua_register_table_as_callable(arg);
@@ -7216,8 +7216,10 @@ bool callback_from_typval(Callback *const callback, typval_T *const arg)
} else {
r = FAIL;
}
- } else if (arg->v_type == VAR_NUMBER && arg->vval.v_number == 0) {
+ } else if (arg->v_type == VAR_SPECIAL
+ || (arg->v_type == VAR_NUMBER && arg->vval.v_number == 0)) {
callback->type = kCallbackNone;
+ callback->data.funcref = NULL;
} else {
r = FAIL;
}
@@ -7324,14 +7326,7 @@ void add_timer_info(typval_T *rettv, timer_T *timer)
return;
}
- if (timer->callback.type == kCallbackPartial) {
- di->di_tv.v_type = VAR_PARTIAL;
- di->di_tv.vval.v_partial = timer->callback.data.partial;
- timer->callback.data.partial->pt_refcount++;
- } else if (timer->callback.type == kCallbackFuncref) {
- di->di_tv.v_type = VAR_FUNC;
- di->di_tv.vval.v_string = vim_strsave(timer->callback.data.funcref);
- }
+ callback_put(&timer->callback, &di->di_tv);
}
void add_timer_info_all(typval_T *rettv)
@@ -8949,7 +8944,7 @@ static void set_var_const(const char *name, const size_t name_len,
// existing variable, need to clear the value
if (var_check_ro(v->di_flags, name, name_len)
- || tv_check_lock(v->di_tv.v_lock, name, name_len)) {
+ || var_check_lock(v->di_tv.v_lock, name, name_len)) {
return;
}
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index 4f9a9fcd68..1ba31bfe68 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -268,7 +268,8 @@ static void f_add(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_number = 1; // Default: failed.
if (argvars[0].v_type == VAR_LIST) {
list_T *const l = argvars[0].vval.v_list;
- if (!tv_check_lock(tv_list_locked(l), N_("add() argument"), TV_TRANSLATE)) {
+ if (!var_check_lock(tv_list_locked(l), N_("add() argument"),
+ TV_TRANSLATE)) {
tv_list_append_tv(l, &argvars[1]);
tv_copy(&argvars[0], rettv);
}
@@ -2277,9 +2278,9 @@ static void f_flatten(typval_T *argvars, typval_T *rettv, FunPtr fptr)
list = argvars[0].vval.v_list;
if (list != NULL
- && !tv_check_lock(tv_list_locked(list),
- N_("flatten() argument"),
- TV_TRANSLATE)
+ && !var_check_lock(tv_list_locked(list),
+ N_("flatten() argument"),
+ TV_TRANSLATE)
&& tv_list_flatten(list, maxdepth) == OK) {
tv_copy(&argvars[0], rettv);
}
@@ -2299,7 +2300,7 @@ static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr)
list_T *const l1 = argvars[0].vval.v_list;
list_T *const l2 = argvars[1].vval.v_list;
- if (!tv_check_lock(tv_list_locked(l1), arg_errmsg, TV_TRANSLATE)) {
+ if (!var_check_lock(tv_list_locked(l1), arg_errmsg, TV_TRANSLATE)) {
listitem_T *item;
if (argvars[2].v_type != VAR_UNKNOWN) {
before = (long)tv_get_number_chk(&argvars[2], &error);
@@ -2328,13 +2329,13 @@ static void f_extend(typval_T *argvars, typval_T *rettv, FunPtr fptr)
dict_T *const d1 = argvars[0].vval.v_dict;
dict_T *const d2 = argvars[1].vval.v_dict;
if (d1 == NULL) {
- const bool locked = tv_check_lock(VAR_FIXED, arg_errmsg, TV_TRANSLATE);
+ const bool locked = var_check_lock(VAR_FIXED, arg_errmsg, TV_TRANSLATE);
(void)locked;
assert(locked == true);
} else if (d2 == NULL) {
// Do nothing
tv_copy(&argvars[0], rettv);
- } else if (!tv_check_lock(d1->dv_lock, arg_errmsg, TV_TRANSLATE)) {
+ } else if (!var_check_lock(d1->dv_lock, arg_errmsg, TV_TRANSLATE)) {
const char *action = "force";
// Check the third argument.
if (argvars[2].v_type != VAR_UNKNOWN) {
@@ -4845,8 +4846,8 @@ static void f_insert(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (argvars[0].v_type != VAR_LIST) {
EMSG2(_(e_listarg), "insert()");
- } else if (!tv_check_lock(tv_list_locked((l = argvars[0].vval.v_list)),
- N_("insert() argument"), TV_TRANSLATE)) {
+ } else if (!var_check_lock(tv_list_locked((l = argvars[0].vval.v_list)),
+ N_("insert() argument"), TV_TRANSLATE)) {
long before = 0;
if (argvars[2].v_type != VAR_UNKNOWN) {
before = tv_get_number_chk(&argvars[2], &error);
@@ -7079,7 +7080,7 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (argvars[2].v_type != VAR_UNKNOWN) {
EMSG2(_(e_toomanyarg), "remove()");
} else if ((d = argvars[0].vval.v_dict) != NULL
- && !tv_check_lock(d->dv_lock, arg_errmsg, TV_TRANSLATE)) {
+ && !var_check_lock(d->dv_lock, arg_errmsg, TV_TRANSLATE)) {
const char *key = tv_get_string_chk(&argvars[1]);
if (key != NULL) {
di = tv_dict_find(d, key, -1);
@@ -7098,8 +7099,8 @@ static void f_remove(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
} else if (argvars[0].v_type != VAR_LIST) {
EMSG2(_(e_listdictarg), "remove()");
- } else if (!tv_check_lock(tv_list_locked((l = argvars[0].vval.v_list)),
- arg_errmsg, TV_TRANSLATE)) {
+ } else if (!var_check_lock(tv_list_locked((l = argvars[0].vval.v_list)),
+ arg_errmsg, TV_TRANSLATE)) {
bool error = false;
idx = tv_get_number_chk(&argvars[1], &error);
@@ -7374,8 +7375,8 @@ static void f_reverse(typval_T *argvars, typval_T *rettv, FunPtr fptr)
list_T *l;
if (argvars[0].v_type != VAR_LIST) {
EMSG2(_(e_listarg), "reverse()");
- } else if (!tv_check_lock(tv_list_locked((l = argvars[0].vval.v_list)),
- N_("reverse() argument"), TV_TRANSLATE)) {
+ } else if (!var_check_lock(tv_list_locked((l = argvars[0].vval.v_list)),
+ N_("reverse() argument"), TV_TRANSLATE)) {
tv_list_reverse(l);
tv_list_set_ret(rettv, l);
}
@@ -9462,7 +9463,7 @@ static void do_sort_uniq(typval_T *argvars, typval_T *rettv, bool sort)
EMSG2(_(e_listarg), sort ? "sort()" : "uniq()");
} else {
list_T *const l = argvars[0].vval.v_list;
- if (tv_check_lock(tv_list_locked(l), arg_errmsg, TV_TRANSLATE)) {
+ if (var_check_lock(tv_list_locked(l), arg_errmsg, TV_TRANSLATE)) {
goto theend;
}
tv_list_set_ret(rettv, l);
diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c
index 61de83fc21..7221dc8bc9 100644
--- a/src/nvim/eval/typval.c
+++ b/src/nvim/eval/typval.c
@@ -1162,6 +1162,49 @@ void callback_free(Callback *callback)
}
}
callback->type = kCallbackNone;
+ callback->data.funcref = NULL;
+}
+
+/// Copy a callback into a typval_T.
+void callback_put(Callback *cb, typval_T *tv)
+ FUNC_ATTR_NONNULL_ALL
+{
+ switch (cb->type) {
+ case kCallbackPartial:
+ tv->v_type = VAR_PARTIAL;
+ tv->vval.v_partial = cb->data.partial;
+ cb->data.partial->pt_refcount++;
+ break;
+ case kCallbackFuncref:
+ tv->v_type = VAR_FUNC;
+ tv->vval.v_string = vim_strsave(cb->data.funcref);
+ func_ref(cb->data.funcref);
+ break;
+ default:
+ tv->v_type = VAR_SPECIAL;
+ tv->vval.v_special = kSpecialVarNull;
+ break;
+ }
+}
+
+// Copy callback from "src" to "dest", incrementing the refcounts.
+void callback_copy(Callback *dest, Callback *src)
+ FUNC_ATTR_NONNULL_ALL
+{
+ dest->type = src->type;
+ switch (src->type) {
+ case kCallbackPartial:
+ dest->data.partial = src->data.partial;
+ dest->data.partial->pt_refcount++;
+ break;
+ case kCallbackFuncref:
+ dest->data.funcref = vim_strsave(src->data.funcref);
+ func_ref(src->data.funcref);
+ break;
+ default:
+ dest->data.funcref = NULL;
+ break;
+ }
}
/// Remove watcher from a dictionary
@@ -1951,7 +1994,7 @@ void tv_dict_extend(dict_T *const d1, dict_T *const d2,
} else if (*action == 'f' && di2 != di1) {
typval_T oldtv;
- if (tv_check_lock(di1->di_tv.v_lock, arg_errmsg, arg_errmsg_len)
+ if (var_check_lock(di1->di_tv.v_lock, arg_errmsg, arg_errmsg_len)
|| var_check_ro(di1->di_flags, arg_errmsg, arg_errmsg_len)) {
break;
}
@@ -2582,7 +2625,7 @@ bool tv_islocked(const typval_T *const tv)
///
/// Also gives an error message when typval is locked.
///
-/// @param[in] lock Lock status.
+/// @param[in] tv Typval.
/// @param[in] name Variable name, used in the error message.
/// @param[in] name_len Variable name length. Use #TV_TRANSLATE to translate
/// variable name and compute the length. Use #TV_CSTRING
@@ -2596,10 +2639,37 @@ bool tv_islocked(const typval_T *const tv)
/// gettext.
///
/// @return true if variable is locked, false otherwise.
-bool tv_check_lock(const VarLockStatus lock, const char *name,
+bool tv_check_lock(const typval_T *tv, const char *name,
size_t name_len)
FUNC_ATTR_WARN_UNUSED_RESULT
{
+ VarLockStatus lock = VAR_UNLOCKED;
+
+ switch (tv->v_type) {
+ // case VAR_BLOB:
+ // if (tv->vval.v_blob != NULL)
+ // lock = tv->vval.v_blob->bv_lock;
+ // break;
+ case VAR_LIST:
+ if (tv->vval.v_list != NULL) {
+ lock = tv->vval.v_list->lv_lock;
+ }
+ break;
+ case VAR_DICT:
+ if (tv->vval.v_dict != NULL) {
+ lock = tv->vval.v_dict->dv_lock;
+ }
+ break;
+ default:
+ break;
+ }
+ return var_check_lock(tv->v_lock, name, name_len)
+ || (lock != VAR_UNLOCKED && var_check_lock(lock, name, name_len));
+}
+
+/// @return true if variable "name" is locked (immutable)
+bool var_check_lock(VarLockStatus lock, const char *name, size_t name_len)
+{
const char *error_message = NULL;
switch (lock) {
case VAR_UNLOCKED: {
diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h
index 2b4612016b..050b84efec 100644
--- a/src/nvim/eval/typval.h
+++ b/src/nvim/eval/typval.h
@@ -120,7 +120,7 @@ typedef enum {
VAR_DICT, ///< Dictionary, .v_dict is used.
VAR_FLOAT, ///< Floating-point value, .v_float is used.
VAR_BOOL, ///< true, false
- VAR_SPECIAL, ///< Special value (true, false, null), .v_special
+ VAR_SPECIAL, ///< Special value (null), .v_special
///< is used.
VAR_PARTIAL, ///< Partial, .v_partial is used.
} VarType;
diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c
index f5d1b1e870..5ffc06ec44 100644
--- a/src/nvim/eval/userfunc.c
+++ b/src/nvim/eval/userfunc.c
@@ -2455,13 +2455,13 @@ void ex_function(exarg_T *eap)
goto erret;
}
if (fudi.fd_di == NULL) {
- if (tv_check_lock(fudi.fd_dict->dv_lock, (const char *)eap->arg,
- TV_CSTRING)) {
+ if (var_check_lock(fudi.fd_dict->dv_lock, (const char *)eap->arg,
+ TV_CSTRING)) {
// Can't add a function to a locked dictionary
goto erret;
}
- } else if (tv_check_lock(fudi.fd_di->di_tv.v_lock, (const char *)eap->arg,
- TV_CSTRING)) {
+ } else if (var_check_lock(fudi.fd_di->di_tv.v_lock, (const char *)eap->arg,
+ TV_CSTRING)) {
// Can't change an existing function if it is locked
goto erret;
}
diff --git a/src/nvim/event/multiqueue.c b/src/nvim/event/multiqueue.c
index 1e6d62135c..f534fc483f 100644
--- a/src/nvim/event/multiqueue.c
+++ b/src/nvim/event/multiqueue.c
@@ -73,7 +73,7 @@ struct multiqueue_item {
struct multiqueue {
MultiQueue *parent;
QUEUE headtail; // circularly-linked
- put_callback put_cb;
+ PutCallback put_cb;
void *data;
size_t size;
};
@@ -91,7 +91,7 @@ typedef struct {
static Event NILEVENT = { .handler = NULL, .argv = {NULL} };
-MultiQueue *multiqueue_new_parent(put_callback put_cb, void *data)
+MultiQueue *multiqueue_new_parent(PutCallback put_cb, void *data)
{
return multiqueue_new(NULL, put_cb, data);
}
@@ -104,7 +104,7 @@ MultiQueue *multiqueue_new_child(MultiQueue *parent)
return multiqueue_new(parent, NULL, NULL);
}
-static MultiQueue *multiqueue_new(MultiQueue *parent, put_callback put_cb,
+static MultiQueue *multiqueue_new(MultiQueue *parent, PutCallback put_cb,
void *data)
{
MultiQueue *rv = xmalloc(sizeof(MultiQueue));
diff --git a/src/nvim/event/multiqueue.h b/src/nvim/event/multiqueue.h
index a688107665..dc60fbb4c7 100644
--- a/src/nvim/event/multiqueue.h
+++ b/src/nvim/event/multiqueue.h
@@ -7,7 +7,7 @@
#include "nvim/lib/queue.h"
typedef struct multiqueue MultiQueue;
-typedef void (*put_callback)(MultiQueue *multiq, void *data);
+typedef void (*PutCallback)(MultiQueue *multiq, void *data);
#define multiqueue_put(q, h, ...) \
multiqueue_put_event(q, event_create(h, __VA_ARGS__));
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index 6a0a08eee8..4af7794317 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -3344,6 +3344,15 @@ static char_u *sub_parse_flags(char_u *cmd, subflags_T *subflags,
return cmd;
}
+static int check_regexp_delim(int c)
+ FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ if (isalpha(c)) {
+ EMSG(_("E146: Regular expressions can't be delimited by letters"));
+ return FAIL;
+ }
+ return OK;
+}
/// Perform a substitution from line eap->line1 to line eap->line2 using the
/// command pointed to by eap->arg which should be of the form:
@@ -3408,16 +3417,14 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout,
/* new pattern and substitution */
if (eap->cmd[0] == 's' && *cmd != NUL && !ascii_iswhite(*cmd)
&& vim_strchr((char_u *)"0123456789cegriIp|\"", *cmd) == NULL) {
- /* don't accept alphanumeric for separator */
- if (isalpha(*cmd)) {
- EMSG(_("E146: Regular expressions can't be delimited by letters"));
+ // don't accept alphanumeric for separator
+ if (check_regexp_delim(*cmd) == FAIL) {
return NULL;
}
- /*
- * undocumented vi feature:
- * "\/sub/" and "\?sub?" use last used search pattern (almost like
- * //sub/r). "\&sub&" use last substitute pattern (like //sub/).
- */
+
+ // undocumented vi feature:
+ // "\/sub/" and "\?sub?" use last used search pattern (almost like
+ // //sub/r). "\&sub&" use last substitute pattern (like //sub/).
if (*cmd == '\\') {
++cmd;
if (vim_strchr((char_u *)"/?&", *cmd) == NULL) {
@@ -4230,6 +4237,8 @@ skip:
}
}
+ curbuf->deleted_bytes2 = 0;
+
if (first_line != 0) {
/* Need to subtract the number of added lines from "last_line" to get
* the line number before the change (same as adding the number of
@@ -4455,6 +4464,8 @@ void ex_global(exarg_T *eap)
} else if (*cmd == NUL) {
EMSG(_("E148: Regular expression missing from global"));
return;
+ } else if (check_regexp_delim(*cmd) == FAIL) {
+ return;
} else {
delim = *cmd; /* get the delimiter */
if (delim)
@@ -4773,8 +4784,9 @@ void ex_help(exarg_T *eap)
* window. */
if (empty_fnum != 0 && curbuf->b_fnum != empty_fnum) {
buf = buflist_findnr(empty_fnum);
- if (buf != NULL && buf->b_nwindows == 0)
- wipe_buffer(buf, TRUE);
+ if (buf != NULL && buf->b_nwindows == 0) {
+ wipe_buffer(buf, true);
+ }
}
/* keep the previous alternate file */
diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c
index 9abeee47f4..9d500a8ddb 100644
--- a/src/nvim/ex_cmds2.c
+++ b/src/nvim/ex_cmds2.c
@@ -2665,7 +2665,8 @@ static void cmd_source_buffer(const exarg_T *eap)
};
if (curbuf != NULL && curbuf->b_fname
&& path_with_extension((const char *)curbuf->b_fname, "lua")) {
- nlua_source_using_linegetter(get_buffer_line, (void *)&cookie, ":source");
+ nlua_source_using_linegetter(get_buffer_line, (void *)&cookie,
+ ":source (no file)");
} else {
source_using_linegetter((void *)&cookie, get_buffer_line,
":source (no file)");
@@ -3002,14 +3003,23 @@ int do_source(char_u *fname, int check_other, int is_vimrc)
}
if (path_with_extension((const char *)fname, "lua")) {
+ // TODO(shadmansaleh): Properly handle :verbose for lua
+ // For now change currennt_sctx before sourcing lua files
+ // So verbose doesn't say everything was done in line 1 since we don't know
+ const sctx_T current_sctx_backup = current_sctx;
+ const linenr_T sourcing_lnum_backup = sourcing_lnum;
+ current_sctx.sc_lnum = 0;
+ sourcing_lnum = 0;
// Source the file as lua
- retval = (int)nlua_exec_file((const char *)fname);
+ nlua_exec_file((const char *)fname);
+ current_sctx = current_sctx_backup;
+ sourcing_lnum = sourcing_lnum_backup;
} else {
// Call do_cmdline, which will call getsourceline() to get the lines.
do_cmdline(firstline, getsourceline, (void *)&cookie,
DOCMD_VERBOSE|DOCMD_NOWAIT|DOCMD_REPEAT);
- retval = OK;
}
+ retval = OK;
if (l_do_profiling == PROF_YES) {
// Get "si" again, "script_items" may have been reallocated.
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index 29347def4c..57d1339bb0 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -4184,10 +4184,6 @@ static char_u *invalid_range(exarg_T *eap)
}
break;
case ADDR_UNSIGNED:
- if (eap->line2 < 0) {
- return (char_u *)_(e_invrange);
- }
- break;
case ADDR_NONE:
// Will give an error elsewhere.
break;
diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c
index afc387ef38..4d4286354b 100644
--- a/src/nvim/lua/executor.c
+++ b/src/nvim/lua/executor.c
@@ -1164,6 +1164,13 @@ static void nlua_typval_exec(const char *lcmd, size_t lcmd_len,
int nlua_source_using_linegetter(LineGetter fgetline,
void *cookie, char *name)
{
+ const linenr_T save_sourcing_lnum = sourcing_lnum;
+ const sctx_T save_current_sctx = current_sctx;
+ current_sctx.sc_sid = SID_STR;
+ current_sctx.sc_seq = 0;
+ current_sctx.sc_lnum = 0;
+ sourcing_lnum = 0;
+
garray_T ga;
char_u *line = NULL;
@@ -1174,6 +1181,9 @@ int nlua_source_using_linegetter(LineGetter fgetline,
char *code = (char *)ga_concat_strings_sep(&ga, "\n");
size_t len = strlen(code);
nlua_typval_exec(code, len, name, NULL, 0, false, NULL);
+
+ sourcing_lnum = save_sourcing_lnum;
+ current_sctx = save_current_sctx;
ga_clear_strings(&ga);
xfree(code);
return OK;
diff --git a/src/nvim/lua/treesitter.c b/src/nvim/lua/treesitter.c
index c186928ae2..e3fa48f530 100644
--- a/src/nvim/lua/treesitter.c
+++ b/src/nvim/lua/treesitter.c
@@ -1073,6 +1073,11 @@ static int node_rawquery(lua_State *L)
// TODO(bfredl): these are expensive allegedly,
// use a reuse list later on?
TSQueryCursor *cursor = ts_query_cursor_new();
+ // TODO(clason): API introduced after tree-sitter release 0.19.5
+ // remove guard when minimum ts version is bumped to 0.19.6+
+#ifdef NVIM_TS_HAS_SET_MATCH_LIMIT
+ ts_query_cursor_set_match_limit(cursor, 32);
+#endif
ts_query_cursor_exec(cursor, query, node);
bool captures = lua_toboolean(L, 3);
diff --git a/src/nvim/marktree.c b/src/nvim/marktree.c
index 34acf64d83..feb54eae4a 100644
--- a/src/nvim/marktree.c
+++ b/src/nvim/marktree.c
@@ -356,6 +356,7 @@ void marktree_del_itr(MarkTree *b, MarkTreeIter *itr, bool rev)
y = y->level ? y->ptr[0] : NULL;
}
}
+ itr->i--;
}
b->n_keys--;
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index f2f6803665..855f63ba7b 100644
--- a/src/nvim/ops.c
+++ b/src/nvim/ops.c
@@ -2719,10 +2719,13 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append)
// Copy a block range into a register.
// If "exclude_trailing_space" is set, do not copy trailing whitespaces.
-static void yank_copy_line(yankreg_T *reg, const struct block_def *bd,
+static void yank_copy_line(yankreg_T *reg, struct block_def *bd,
size_t y_idx, bool exclude_trailing_space)
FUNC_ATTR_NONNULL_ALL
{
+ if (exclude_trailing_space) {
+ bd->endspaces = 0;
+ }
int size = bd->startspaces + bd->endspaces + bd->textlen;
assert(size >= 0);
char_u *pnew = xmallocz((size_t)size);
@@ -3992,6 +3995,7 @@ int do_join(size_t count,
del_lines((long)count - 1, false);
curwin->w_cursor.lnum = t;
curbuf_splice_pending--;
+ curbuf->deleted_bytes2 = 0;
/*
* Set the cursor column:
diff --git a/src/nvim/option.c b/src/nvim/option.c
index 57b8fe1a2e..388bedc043 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -85,6 +85,7 @@
#include "nvim/api/private/helpers.h"
#include "nvim/os/input.h"
#include "nvim/os/lang.h"
+#include "nvim/quickfix.h"
/*
* The options that are local to a window or buffer have "indir" set to one of
@@ -3182,6 +3183,10 @@ ambw_end:
}
}
}
+ } else if (varp == &p_qftf) {
+ if (!qf_process_qftf_option()) {
+ errmsg = e_invarg;
+ }
} else {
// Options that are a list of flags.
p = NULL;
@@ -7760,6 +7765,7 @@ static Dictionary vimoption2dict(vimoption_T *opt)
}
PUT(dict, "type", CSTR_TO_OBJ(type));
PUT(dict, "default", def);
+ PUT(dict, "allows_duplicates", BOOL(!(opt->flags & P_NODUP)));
return dict;
}
diff --git a/src/nvim/options.lua b/src/nvim/options.lua
index 86dec74f56..0b09686675 100644
--- a/src/nvim/options.lua
+++ b/src/nvim/options.lua
@@ -3152,7 +3152,7 @@ return {
full_name='wildmode', abbreviation='wim',
short_desc=N_("mode for 'wildchar' command-line expansion"),
type='string', list='onecomma', scope={'global'},
- deny_duplicates=true,
+ deny_duplicates=false,
vim=true,
varname='p_wim',
defaults={if_true={vi="", vim="full"}}
diff --git a/src/nvim/path.c b/src/nvim/path.c
index fe50be5ea1..6ac24182cc 100644
--- a/src/nvim/path.c
+++ b/src/nvim/path.c
@@ -180,6 +180,34 @@ const char *path_next_component(const char *fname)
return fname;
}
+/// Returns the length of the path head on the current platform.
+/// @return
+/// - 3 on windows
+/// - 1 otherwise
+int path_head_length(void)
+{
+#ifdef WIN32
+ return 3;
+#else
+ return 1;
+#endif
+}
+
+/// Returns true if path begins with characters denoting the head of a path
+/// (e.g. '/' on linux and 'D:' on windows).
+/// @param path The path to be checked.
+/// @return
+/// - True if path begins with a path head
+/// - False otherwise
+bool is_path_head(const char_u *path)
+{
+#ifdef WIN32
+ return isalpha(path[0]) && path[1] == ':';
+#else
+ return vim_ispathsep(*path);
+#endif
+}
+
/// Get a pointer to one character past the head of a path name.
/// Unix: after "/"; Win: after "c:\"
/// If there is no head, path is returned.
@@ -189,7 +217,7 @@ char_u *get_past_head(const char_u *path)
#ifdef WIN32
// May skip "c:"
- if (isalpha(path[0]) && path[1] == ':') {
+ if (is_path_head(path)) {
retval = path + 2;
}
#endif
@@ -1991,10 +2019,24 @@ char_u *path_shorten_fname(char_u *full_path, char_u *dir_name)
assert(dir_name != NULL);
size_t len = strlen((char *)dir_name);
+
+ // If dir_name is a path head, full_path can always be made relative.
+ if (len == (size_t)path_head_length() && is_path_head(dir_name)) {
+ return full_path + len;
+ }
+
+ // If full_path and dir_name do not match, it's impossible to make one
+ // relative to the other.
+ if (fnamencmp(dir_name, full_path, len) != 0) {
+ return NULL;
+ }
+
char_u *p = full_path + len;
- if (fnamencmp(dir_name, full_path, len) != 0
- || !vim_ispathsep(*p)) {
+ // If *p is not pointing to a path separator, this means that full_path's
+ // last directory name is longer than *dir_name's last directory, so they
+ // don't actually match.
+ if (!vim_ispathsep(*p)) {
return NULL;
}
diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c
index 1a9bbe26f0..71624baaf4 100644
--- a/src/nvim/quickfix.c
+++ b/src/nvim/quickfix.c
@@ -100,7 +100,7 @@ typedef struct qf_list_S {
char_u *qf_title; ///< title derived from the command that created
///< the error list or set by setqflist
typval_T *qf_ctx; ///< context set by setqflist/setloclist
- char_u *qf_qftf; ///< 'quickfixtextfunc' setting for this list
+ Callback qftf_cb; ///< 'quickfixtextfunc' callback function
struct dir_stack_T *qf_dir_stack;
char_u *qf_directory;
@@ -541,6 +541,9 @@ static int efm_to_regpat(const char_u *efm, int len, efm_T *fmt_ptr,
static efm_T *fmt_start = NULL; // cached across qf_parse_line() calls
+// callback function for 'quickfixtextfunc'
+static Callback qftf_cb;
+
static void free_efm_list(efm_T **efm_first)
{
for (efm_T *efm_ptr = *efm_first; efm_ptr != NULL; efm_ptr = *efm_first) {
@@ -1978,7 +1981,7 @@ static int copy_loclist_entries(const qf_list_T *from_qfl, qf_list_T *to_qfl)
}
/// Copy the specified location list 'from_qfl' to 'to_qfl'.
-static int copy_loclist(const qf_list_T *from_qfl, qf_list_T *to_qfl)
+static int copy_loclist(qf_list_T *from_qfl, qf_list_T *to_qfl)
FUNC_ATTR_NONNULL_ALL
{
// Some of the fields are populated by qf_add_entry()
@@ -2000,11 +2003,7 @@ static int copy_loclist(const qf_list_T *from_qfl, qf_list_T *to_qfl)
} else {
to_qfl->qf_ctx = NULL;
}
- if (from_qfl->qf_qftf != NULL) {
- to_qfl->qf_qftf = vim_strsave(from_qfl->qf_qftf);
- } else {
- to_qfl->qf_qftf = NULL;
- }
+ callback_copy(&to_qfl->qftf_cb, &from_qfl->qftf_cb);
if (from_qfl->qf_count) {
if (copy_loclist_entries(from_qfl, to_qfl) == FAIL) {
@@ -3385,7 +3384,7 @@ static void qf_free(qf_list_T *qfl)
XFREE_CLEAR(qfl->qf_title);
tv_free(qfl->qf_ctx);
qfl->qf_ctx = NULL;
- XFREE_CLEAR(qfl->qf_qftf);
+ callback_free(&qfl->qftf_cb);
qfl->qf_id = 0;
qfl->qf_changedtick = 0L;
}
@@ -3860,6 +3859,41 @@ static buf_T *qf_find_buf(qf_info_T *qi)
return NULL;
}
+// Process the 'quickfixtextfunc' option value.
+bool qf_process_qftf_option(void)
+{
+ typval_T *tv;
+ Callback cb;
+
+ if (p_qftf == NULL || *p_qftf == NUL) {
+ callback_free(&qftf_cb);
+ return true;
+ }
+
+ if (*p_qftf == '{') {
+ // Lambda expression
+ tv = eval_expr(p_qftf);
+ if (tv == NULL) {
+ return false;
+ }
+ } else {
+ // treat everything else as a function name string
+ tv = xcalloc(1, sizeof(*tv));
+ tv->v_type = VAR_STRING;
+ tv->vval.v_string = vim_strsave(p_qftf);
+ }
+
+ if (!callback_from_typval(&cb, tv)) {
+ tv_free(tv);
+ return false;
+ }
+
+ callback_free(&qftf_cb);
+ qftf_cb = cb;
+ tv_free(tv);
+ return true;
+}
+
/// Update the w:quickfix_title variable in the quickfix/location list window in
/// all the tab pages.
static void qf_update_win_titlevar(qf_info_T *qi)
@@ -3891,7 +3925,15 @@ static void qf_update_buffer(qf_info_T *qi, qfline_T *old_last)
int qf_winid = 0;
if (IS_LL_STACK(qi)) {
- qf_winid = curwin->handle;
+ if (curwin->w_llist == qi) {
+ win = curwin;
+ } else {
+ win = qf_find_win_with_loclist(qi);
+ if (win == NULL) {
+ return;
+ }
+ }
+ qf_winid = (int)win->handle;
}
if (old_last == NULL) {
@@ -3928,7 +3970,9 @@ static int qf_buf_add_line(qf_list_T *qfl, buf_T *buf, linenr_T lnum,
int len;
buf_T *errbuf;
- if (qftf_str != NULL) {
+ // If the 'quickfixtextfunc' function returned an non-empty custom string
+ // for this entry, then use it.
+ if (qftf_str != NULL && *qftf_str != NUL) {
STRLCPY(IObuff, qftf_str, IOSIZE);
} else {
if (qfp->qf_module != NULL) {
@@ -3997,22 +4041,25 @@ static int qf_buf_add_line(qf_list_T *qfl, buf_T *buf, linenr_T lnum,
return OK;
}
+// Call the 'quickfixtextfunc' function to get the list of lines to display in
+// the quickfix window for the entries 'start_idx' to 'end_idx'.
static list_T *call_qftf_func(qf_list_T *qfl,
int qf_winid,
long start_idx,
long end_idx)
{
- char_u *qftf = p_qftf;
+ Callback *cb = &qftf_cb;
list_T *qftf_list = NULL;
// If 'quickfixtextfunc' is set, then use the user-supplied function to get
// the text to display. Use the local value of 'quickfixtextfunc' if it is
// set.
- if (qfl->qf_qftf != NULL) {
- qftf = qfl->qf_qftf;
+ if (qfl->qftf_cb.type != kCallbackNone) {
+ cb = &qfl->qftf_cb;
}
- if (qftf != NULL && *qftf != NUL) {
+ if (cb != NULL && cb->type != kCallbackNone) {
typval_T args[1];
+ typval_T rettv;
// create the dict argument
dict_T *const dict = tv_dict_alloc_lock(VAR_FIXED);
@@ -4026,8 +4073,16 @@ static list_T *call_qftf_func(qf_list_T *qfl,
args[0].v_type = VAR_DICT;
args[0].vval.v_dict = dict;
- qftf_list = call_func_retlist(qftf, 1, args);
- dict->dv_refcount--;
+ qftf_list = NULL;
+
+ if (callback_call(cb, 1, args, &rettv)) {
+ if (rettv.v_type == VAR_LIST) {
+ qftf_list = rettv.vval.v_list;
+ tv_list_ref(qftf_list);
+ }
+ tv_clear(&rettv);
+ }
+ tv_dict_unref(dict);
}
return qftf_list;
@@ -4064,6 +4119,7 @@ static void qf_fill_buffer(qf_list_T *qfl, buf_T *buf, qfline_T *old_last,
if (qfl != NULL) {
char_u dirname[MAXPATHL];
int prev_bufnr = -1;
+ bool invalid_val = false;
*dirname = NUL;
@@ -4086,10 +4142,15 @@ static void qf_fill_buffer(qf_list_T *qfl, buf_T *buf, qfline_T *old_last,
while (lnum < qfl->qf_count) {
char_u *qftf_str = NULL;
- if (qftf_li != NULL) {
- // Use the text supplied by the user defined function
- qftf_str = (char_u *)tv_get_string_chk(TV_LIST_ITEM_TV(qftf_li));
+ // Use the text supplied by the user defined function (if any).
+ // If the returned value is not string, then ignore the rest
+ // of the returned values and use the default.
+ if (qftf_li != NULL && !invalid_val) {
+ qftf_str = (char_u *)tv_get_string_chk(TV_LIST_ITEM_TV(qftf_li));
+ if (qftf_str == NULL) {
+ invalid_val = true;
}
+ }
if (qf_buf_add_line(qfl, buf, lnum, qfp, dirname, qftf_str,
prev_bufnr != qfp->qf_fnum) == FAIL) {
@@ -5660,7 +5721,7 @@ static void wipe_dummy_buffer(buf_T *buf, char_u *dirname_start)
// work when got_int is set.
enter_cleanup(&cs);
- wipe_buffer(buf, FALSE);
+ wipe_buffer(buf, true);
// Restore the error/interrupt/exception state if not discarded by a
// new aborting error, interrupt, or uncaught exception.
@@ -5796,7 +5857,9 @@ enum {
QF_GETLIST_SIZE = 0x80,
QF_GETLIST_TICK = 0x100,
QF_GETLIST_FILEWINID = 0x200,
- QF_GETLIST_ALL = 0x3FF,
+ QF_GETLIST_QFBUFNR = 0x400,
+ QF_GETLIST_QFTF = 0x800,
+ QF_GETLIST_ALL = 0xFFF,
};
/// Parse text from 'di' and return the quickfix list items.
@@ -5894,6 +5957,9 @@ static int qf_getprop_keys2flags(const dict_T *what, bool loclist)
if (loclist && tv_dict_find(what, S_LEN("filewinid")) != NULL) {
flags |= QF_GETLIST_FILEWINID;
}
+ if (tv_dict_find(what, S_LEN("quickfixtextfunc")) != NULL) {
+ flags |= QF_GETLIST_QFTF;
+ }
return flags;
}
@@ -5985,6 +6051,9 @@ static int qf_getprop_defaults(qf_info_T *qi,
if ((status == OK) && locstack && (flags & QF_GETLIST_FILEWINID)) {
status = tv_dict_add_nr(retdict, S_LEN("filewinid"), 0);
}
+ if ((status == OK) && (flags & QF_GETLIST_QFTF)) {
+ status = tv_dict_add_str(retdict, S_LEN("quickfixtextfunc"), "");
+ }
return status;
}
@@ -6060,6 +6129,26 @@ static int qf_getprop_idx(qf_list_T *qfl, int eidx, dict_T *retdict)
return tv_dict_add_nr(retdict, S_LEN("idx"), eidx);
}
+/// Return the 'quickfixtextfunc' function of a quickfix/location list
+/// @return OK or FAIL
+static int qf_getprop_qftf(qf_list_T *qfl, dict_T *retdict)
+ FUNC_ATTR_NONNULL_ALL
+{
+ int status;
+
+ if (qfl->qftf_cb.type != kCallbackNone) {
+ typval_T tv;
+
+ callback_put(&qfl->qftf_cb, &tv);
+ status = tv_dict_add_tv(retdict, S_LEN("quickfixtextfunc"), &tv);
+ tv_clear(&tv);
+ } else {
+ status = tv_dict_add_str(retdict, S_LEN("quickfixtextfunc"), "");
+ }
+
+ return status;
+}
+
/// Return quickfix/location list details (title) as a dictionary.
/// 'what' contains the details to return. If 'list_idx' is -1,
/// then current list is used. Otherwise the specified list is used.
@@ -6133,19 +6222,25 @@ int qf_get_properties(win_T *wp, dict_T *what, dict_T *retdict)
if ((status == OK) && (wp != NULL) && (flags & QF_GETLIST_FILEWINID)) {
status = qf_getprop_filewinid(wp, qi, retdict);
}
+ if ((status == OK) && (flags & QF_GETLIST_QFTF)) {
+ status = qf_getprop_qftf(qfl, retdict);
+ }
return status;
}
/// Set the current index in the specified quickfix list
-static int qf_setprop_qftf(qf_info_T *qi, qf_list_T *qfl,
- dictitem_T *di)
+/// @return OK
+static int qf_setprop_qftf(qf_list_T *qfl, dictitem_T *di)
+ FUNC_ATTR_NONNULL_ALL
{
- XFREE_CLEAR(qfl->qf_qftf);
- if (di->di_tv.v_type == VAR_STRING && di->di_tv.vval.v_string != NULL) {
- qfl->qf_qftf = vim_strsave(di->di_tv.vval.v_string);
- }
- return OK;
+ Callback cb;
+
+ callback_free(&qfl->qftf_cb);
+ if (callback_from_typval(&cb, &di->di_tv)) {
+ qfl->qftf_cb = cb;
+ }
+ return OK;
}
/// Add a new quickfix entry to list at 'qf_idx' in the stack 'qi' from the
@@ -6514,7 +6609,7 @@ static int qf_set_properties(qf_info_T *qi, const dict_T *what, int action,
retval = qf_setprop_curidx(qi, qfl, di);
}
if ((di = tv_dict_find(what, S_LEN("quickfixtextfunc"))) != NULL) {
- retval = qf_setprop_qftf(qi, qfl, di);
+ retval = qf_setprop_qftf(qfl, di);
}
if (newlist || retval == OK) {
diff --git a/src/nvim/screen.c b/src/nvim/screen.c
index 04157a0154..3446a944cd 100644
--- a/src/nvim/screen.c
+++ b/src/nvim/screen.c
@@ -7211,7 +7211,24 @@ void ui_ext_tabline_update(void)
ADD(tabs, DICTIONARY_OBJ(tab_info));
}
- ui_call_tabline_update(curtab->handle, tabs);
+
+ Array buffers = ARRAY_DICT_INIT;
+ FOR_ALL_BUFFERS(buf) {
+ // Do not include unlisted buffers
+ if (!buf->b_p_bl) {
+ continue;
+ }
+
+ Dictionary buffer_info = ARRAY_DICT_INIT;
+ PUT(buffer_info, "buffer", BUFFER_OBJ(buf->handle));
+
+ get_trans_bufname(buf);
+ PUT(buffer_info, "name", STRING_OBJ(cstr_to_string((char *)NameBuff)));
+
+ ADD(buffers, DICTIONARY_OBJ(buffer_info));
+ }
+
+ ui_call_tabline_update(curtab->handle, tabs, curbuf->handle, buffers);
}
/*
diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim
index bb84fa498e..ad28118f16 100644
--- a/src/nvim/testdir/test_autocmd.vim
+++ b/src/nvim/testdir/test_autocmd.vim
@@ -108,19 +108,19 @@ func Test_bufunload()
autocmd BufWipeout * call add(s:li, "bufwipeout")
augroup END
- let s:li=[]
+ let s:li = []
new
setlocal bufhidden=
bunload
call assert_equal(["bufunload", "bufdelete"], s:li)
- let s:li=[]
+ let s:li = []
new
setlocal bufhidden=delete
bunload
call assert_equal(["bufunload", "bufdelete"], s:li)
- let s:li=[]
+ let s:li = []
new
setlocal bufhidden=unload
bwipeout
@@ -196,6 +196,29 @@ func Test_autocmd_bufunload_avoiding_SEGV_02()
bwipe! a.txt
endfunc
+func Test_autocmd_dummy_wipeout()
+ " prepare files
+ call writefile([''], 'Xdummywipetest1.txt')
+ call writefile([''], 'Xdummywipetest2.txt')
+ augroup test_bufunload_group
+ autocmd!
+ autocmd BufUnload * call add(s:li, "bufunload")
+ autocmd BufDelete * call add(s:li, "bufdelete")
+ autocmd BufWipeout * call add(s:li, "bufwipeout")
+ augroup END
+
+ let s:li = []
+ split Xdummywipetest1.txt
+ silent! vimgrep /notmatched/ Xdummywipetest*
+ call assert_equal(["bufunload", "bufwipeout"], s:li)
+
+ bwipeout
+ call delete('Xdummywipetest1.txt')
+ call delete('Xdummywipetest2.txt')
+ au! test_bufunload_group
+ augroup! test_bufunload_group
+endfunc
+
func Test_win_tab_autocmd()
let g:record = []
@@ -428,7 +451,7 @@ func Test_autocmd_bufwipe_in_SessLoadPost()
let content =<< trim [CODE]
set nocp noswapfile
- let v:swapchoice="e"
+ let v:swapchoice = "e"
augroup test_autocmd_sessionload
autocmd!
autocmd SessionLoadPost * exe bufnr("Xsomething") . "bw!"
@@ -537,92 +560,92 @@ func Test_OptionSet()
au OptionSet * :call s:AutoCommandOptionSet(expand("<amatch>"))
" 1: Setting number option"
- let g:options=[['number', 0, 1, 'global']]
+ let g:options = [['number', 0, 1, 'global']]
set nu
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
" 2: Setting local number option"
- let g:options=[['number', 1, 0, 'local']]
+ let g:options = [['number', 1, 0, 'local']]
setlocal nonu
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
" 3: Setting global number option"
- let g:options=[['number', 1, 0, 'global']]
+ let g:options = [['number', 1, 0, 'global']]
setglobal nonu
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
" 4: Setting local autoindent option"
- let g:options=[['autoindent', 0, 1, 'local']]
+ let g:options = [['autoindent', 0, 1, 'local']]
setlocal ai
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
" 5: Setting global autoindent option"
- let g:options=[['autoindent', 0, 1, 'global']]
+ let g:options = [['autoindent', 0, 1, 'global']]
setglobal ai
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
" 6: Setting global autoindent option"
- let g:options=[['autoindent', 1, 0, 'global']]
+ let g:options = [['autoindent', 1, 0, 'global']]
set ai!
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
" Should not print anything, use :noa
" 7: don't trigger OptionSet"
- let g:options=[['invalid', 1, 1, 'invalid']]
+ let g:options = [['invalid', 1, 1, 'invalid']]
noa set nonu
call assert_equal([['invalid', 1, 1, 'invalid']], g:options)
call assert_equal(g:opt[0], g:opt[1])
" 8: Setting several global list and number option"
- let g:options=[['list', 0, 1, 'global'], ['number', 0, 1, 'global']]
+ let g:options = [['list', 0, 1, 'global'], ['number', 0, 1, 'global']]
set list nu
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
" 9: don't trigger OptionSet"
- let g:options=[['invalid', 1, 1, 'invalid'], ['invalid', 1, 1, 'invalid']]
+ let g:options = [['invalid', 1, 1, 'invalid'], ['invalid', 1, 1, 'invalid']]
noa set nolist nonu
call assert_equal([['invalid', 1, 1, 'invalid'], ['invalid', 1, 1, 'invalid']], g:options)
call assert_equal(g:opt[0], g:opt[1])
" 10: Setting global acd"
- let g:options=[['autochdir', 0, 1, 'local']]
+ let g:options = [['autochdir', 0, 1, 'local']]
setlocal acd
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
" 11: Setting global autoread (also sets local value)"
- let g:options=[['autoread', 0, 1, 'global']]
+ let g:options = [['autoread', 0, 1, 'global']]
set ar
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
" 12: Setting local autoread"
- let g:options=[['autoread', 1, 1, 'local']]
+ let g:options = [['autoread', 1, 1, 'local']]
setlocal ar
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
" 13: Setting global autoread"
- let g:options=[['autoread', 1, 0, 'global']]
+ let g:options = [['autoread', 1, 0, 'global']]
setglobal invar
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
" 14: Setting option backspace through :let"
- let g:options=[['backspace', '', 'eol,indent,start', 'global']]
- let &bs="eol,indent,start"
+ let g:options = [['backspace', '', 'eol,indent,start', 'global']]
+ let &bs = "eol,indent,start"
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
" 15: Setting option backspace through setbufvar()"
- let g:options=[['backup', 0, 1, 'local']]
+ let g:options = [['backup', 0, 1, 'local']]
" try twice, first time, shouldn't trigger because option name is invalid,
" second time, it should trigger
call assert_fails("call setbufvar(1, '&l:bk', 1)", "E355")
@@ -632,13 +655,13 @@ func Test_OptionSet()
call assert_equal(g:opt[0], g:opt[1])
" 16: Setting number option using setwinvar"
- let g:options=[['number', 0, 1, 'local']]
+ let g:options = [['number', 0, 1, 'local']]
call setwinvar(0, '&number', 1)
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
" 17: Setting key option, shouldn't trigger"
- let g:options=[['key', 'invalid', 'invalid1', 'invalid']]
+ let g:options = [['key', 'invalid', 'invalid1', 'invalid']]
setlocal key=blah
setlocal key=
call assert_equal([['key', 'invalid', 'invalid1', 'invalid']], g:options)
@@ -646,13 +669,13 @@ func Test_OptionSet()
" 18: Setting string option"
let oldval = &tags
- let g:options=[['tags', oldval, 'tagpath', 'global']]
+ let g:options = [['tags', oldval, 'tagpath', 'global']]
set tags=tagpath
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
" 1l: Resetting string option"
- let g:options=[['tags', 'tagpath', oldval, 'global']]
+ let g:options = [['tags', 'tagpath', oldval, 'global']]
set tags&
call assert_equal([], g:options)
call assert_equal(g:opt[0], g:opt[1])
@@ -672,7 +695,7 @@ func Test_OptionSet_diffmode()
call test_override('starting', 1)
" 18: Changing an option when entering diff mode
new
- au OptionSet diff :let &l:cul=v:option_new
+ au OptionSet diff :let &l:cul = v:option_new
call setline(1, ['buffer 1', 'line2', 'line3', 'line4'])
call assert_equal(0, &l:cul)
@@ -1754,7 +1777,7 @@ func Test_autocmd_CmdWinEnter()
autocmd CmdWinEnter * quit
let winnr = winnr('$')
END
- let filename='XCmdWinEnter'
+ let filename = 'XCmdWinEnter'
call writefile(lines, filename)
let buf = RunVimInTerminal('-S '.filename, #{rows: 6})
diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim
index 71a7a2cce5..eb6151fbe1 100644
--- a/src/nvim/testdir/test_filetype.vim
+++ b/src/nvim/testdir/test_filetype.vim
@@ -161,6 +161,8 @@ let s:filename_checks = {
\ 'ecd': ['file.ecd'],
\ 'edif': ['file.edf', 'file.edif', 'file.edo'],
\ 'elinks': ['elinks.conf'],
+ \ 'elixir': ['file.ex', 'file.exs', 'mix.lock'],
+ \ 'eelixir': ['file.eex', 'file.leex'],
\ 'elm': ['file.elm'],
\ 'elmfilt': ['filter-rules'],
\ 'epuppet': ['file.epp'],
@@ -257,7 +259,7 @@ let s:filename_checks = {
\ 'jgraph': ['file.jgr'],
\ 'jovial': ['file.jov', 'file.j73', 'file.jovial'],
\ 'jproperties': ['file.properties', 'file.properties_xx', 'file.properties_xx_xx', 'some.properties_xx_xx_file'],
- \ 'json': ['file.json', 'file.jsonp', 'file.webmanifest', 'Pipfile.lock', 'file.ipynb'],
+ \ 'json': ['file.json', 'file.jsonp', 'file.json-patch', 'file.webmanifest', 'Pipfile.lock', 'file.ipynb'],
\ 'jsp': ['file.jsp'],
\ 'kconfig': ['Kconfig', 'Kconfig.debug', 'Kconfig.file'],
\ 'kivy': ['file.kv'],
@@ -422,7 +424,7 @@ let s:filename_checks = {
\ 'sass': ['file.sass'],
\ 'sather': ['file.sa'],
\ 'sbt': ['file.sbt'],
- \ 'scala': ['file.scala'],
+ \ 'scala': ['file.scala', 'file.sc'],
\ 'scheme': ['file.scm', 'file.ss', 'file.rkt'],
\ 'scilab': ['file.sci', 'file.sce'],
\ 'screen': ['.screenrc', 'screenrc'],
@@ -765,5 +767,41 @@ func Test_pp_file()
filetype off
endfunc
+func Test_ex_file()
+ filetype on
+
+ call writefile(['arbitrary content'], 'Xfile.ex')
+ split Xfile.ex
+ call assert_equal('elixir', &filetype)
+ bwipe!
+ let g:filetype_euphoria = 'euphoria4'
+ split Xfile.ex
+ call assert_equal('euphoria4', &filetype)
+ bwipe!
+ unlet g:filetype_euphoria
+
+ call writefile(['-- filetype euphoria comment'], 'Xfile.ex')
+ split Xfile.ex
+ call assert_equal('euphoria3', &filetype)
+ bwipe!
+
+ call writefile(['--filetype euphoria comment'], 'Xfile.ex')
+ split Xfile.ex
+ call assert_equal('euphoria3', &filetype)
+ bwipe!
+
+ call writefile(['ifdef '], 'Xfile.ex')
+ split Xfile.ex
+ call assert_equal('euphoria3', &filetype)
+ bwipe!
+
+ call writefile(['include '], 'Xfile.ex')
+ split Xfile.ex
+ call assert_equal('euphoria3', &filetype)
+ bwipe!
+
+ call delete('Xfile.ex')
+ filetype off
+endfunc
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_global.vim b/src/nvim/testdir/test_global.vim
index 2de2c412de..8edc9c2608 100644
--- a/src/nvim/testdir/test_global.vim
+++ b/src/nvim/testdir/test_global.vim
@@ -36,4 +36,8 @@ func Test_global_error()
call assert_fails('g/\(/y', 'E476:')
endfunc
+func Test_wrong_delimiter()
+ call assert_fails('g x^bxd', 'E146:')
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_let.vim b/src/nvim/testdir/test_let.vim
index a5cbd8f6a6..6cb736a38a 100644
--- a/src/nvim/testdir/test_let.vim
+++ b/src/nvim/testdir/test_let.vim
@@ -126,11 +126,16 @@ endfunction
func s:set_varg7(...) abort
let b = a:000
- call add(b, 1)
+ let b += [1]
endfunction
func s:set_varg8(...) abort
let b = a:000
+ call add(b, 1)
+endfunction
+
+func s:set_varg9(...) abort
+ let b = a:000
let b[0][0] = 1
endfunction
@@ -142,7 +147,8 @@ func Test_let_varg_fail()
call s:set_varg5([0])
call assert_fails('call s:set_varg6(1)', 'E742:')
call assert_fails('call s:set_varg7(1)', 'E742:')
- call s:set_varg8([0])
+ call assert_fails('call s:set_varg8(1)', 'E742:')
+ call s:set_varg9([0])
endfunction
func Test_let_utf8_environment()
diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim
index 14240f0d5f..6bd64caa6c 100644
--- a/src/nvim/testdir/test_quickfix.vim
+++ b/src/nvim/testdir/test_quickfix.vim
@@ -891,7 +891,7 @@ func Test_efm1()
Xtestfile:9: parse error before `asd'
make: *** [vim] Error 1
in file "Xtestfile" linenr 10: there is an error
-
+
2 returned
"Xtestfile", line 11 col 1; this is an error
"Xtestfile", line 12 col 2; this is another error
@@ -914,7 +914,7 @@ func Test_efm1()
x should be a dot
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx line 20
^
-
+
Does anyone know what is the problem and how to correction it?
"Xtestfile", line 21 col 9: What is the title of the quickfix window?
"Xtestfile", line 22 col 9: What is the title of the quickfix window?
@@ -3483,12 +3483,13 @@ func Xgetlist_empty_tests(cchar)
if a:cchar == 'c'
call assert_equal({'context' : '', 'id' : 0, 'idx' : 0,
\ 'items' : [], 'nr' : 0, 'size' : 0,
- \ 'title' : '', 'winid' : 0, 'changedtick': 0},
- \ g:Xgetlist({'all' : 0}))
+ \ 'title' : '', 'winid' : 0, 'changedtick': 0,
+ \ 'quickfixtextfunc' : ''}, g:Xgetlist({'all' : 0}))
else
call assert_equal({'context' : '', 'id' : 0, 'idx' : 0,
\ 'items' : [], 'nr' : 0, 'size' : 0, 'title' : '',
- \ 'winid' : 0, 'changedtick': 0, 'filewinid' : 0},
+ \ 'winid' : 0, 'changedtick': 0, 'filewinid' : 0,
+ \ 'quickfixtextfunc' : ''},
\ g:Xgetlist({'all' : 0}))
endif
@@ -3526,11 +3527,13 @@ func Xgetlist_empty_tests(cchar)
if a:cchar == 'c'
call assert_equal({'context' : '', 'id' : 0, 'idx' : 0, 'items' : [],
\ 'nr' : 0, 'size' : 0, 'title' : '', 'winid' : 0,
+ \ 'quickfixtextfunc' : '',
\ 'changedtick' : 0}, g:Xgetlist({'id' : qfid, 'all' : 0}))
else
call assert_equal({'context' : '', 'id' : 0, 'idx' : 0, 'items' : [],
\ 'nr' : 0, 'size' : 0, 'title' : '', 'winid' : 0,
- \ 'changedtick' : 0, 'filewinid' : 0},
+ \ 'changedtick' : 0, 'filewinid' : 0,
+ \ 'quickfixtextfunc' : ''},
\ g:Xgetlist({'id' : qfid, 'all' : 0}))
endif
@@ -3547,12 +3550,13 @@ func Xgetlist_empty_tests(cchar)
if a:cchar == 'c'
call assert_equal({'context' : '', 'id' : 0, 'idx' : 0, 'items' : [],
\ 'nr' : 0, 'size' : 0, 'title' : '', 'winid' : 0,
- \ 'changedtick' : 0}, g:Xgetlist({'nr' : 5, 'all' : 0}))
+ \ 'changedtick' : 0,
+ \ 'quickfixtextfunc' : ''}, g:Xgetlist({'nr' : 5, 'all' : 0}))
else
call assert_equal({'context' : '', 'id' : 0, 'idx' : 0, 'items' : [],
\ 'nr' : 0, 'size' : 0, 'title' : '', 'winid' : 0,
- \ 'changedtick' : 0, 'filewinid' : 0},
- \ g:Xgetlist({'nr' : 5, 'all' : 0}))
+ \ 'changedtick' : 0, 'filewinid' : 0,
+ \ 'quickfixtextfunc' : ''}, g:Xgetlist({'nr' : 5, 'all' : 0}))
endif
endfunc
@@ -4994,6 +4998,9 @@ func Xtest_qftextfunc(cchar)
set efm=%f:%l:%c:%m
set quickfixtextfunc=Tqfexpr
+ call assert_equal('Tqfexpr', &quickfixtextfunc)
+ call assert_equal('',
+ \ g:Xgetlist({'quickfixtextfunc' : 1}).quickfixtextfunc)
Xexpr ['F1:10:2:green', 'F1:20:4:blue']
Xwindow
call assert_equal('F1-L10C2-green', getline(1))
@@ -5030,12 +5037,15 @@ func Xtest_qftextfunc(cchar)
call assert_equal('Line 10, Col 2', getline(1))
call assert_equal('Line 20, Col 4', getline(2))
Xclose
+ call assert_equal(function('PerQfText'),
+ \ g:Xgetlist({'quickfixtextfunc' : 1}).quickfixtextfunc)
" Add entries to the list when the quickfix buffer is hidden
Xaddexpr ['F1:30:6:red']
Xwindow
call assert_equal('Line 30, Col 6', getline(3))
Xclose
call g:Xsetlist([], 'r', {'quickfixtextfunc' : ''})
+ call assert_equal('', g:Xgetlist({'quickfixtextfunc' : 1}).quickfixtextfunc)
set quickfixtextfunc&
delfunc PerQfText
@@ -5074,12 +5084,53 @@ func Xtest_qftextfunc(cchar)
" \ 'E730:')
Xexpr ['F1:10:2:green', 'F1:20:4:blue', 'F1:30:6:red']
call assert_fails('Xwindow', 'E730:')
- call assert_equal(['one', 'F1|20 col 4| blue', 'two'], getline(1, '$'))
+ call assert_equal(['one', 'F1|20 col 4| blue', 'F1|30 col 6| red'],
+ \ getline(1, '$'))
Xclose
set quickfixtextfunc&
delfunc Xqftext
delfunc Xqftext2
+
+ " set the global option to a lambda function
+ set quickfixtextfunc={d\ ->\ map(g:Xgetlist({'id'\ :\ d.id,\ 'items'\ :\ 1}).items[d.start_idx-1:d.end_idx-1],\ 'v:val.text')}
+ Xexpr ['F1:10:2:green', 'F1:20:4:blue']
+ Xwindow
+ call assert_equal(['green', 'blue'], getline(1, '$'))
+ Xclose
+ call assert_equal("{d -> map(g:Xgetlist({'id' : d.id, 'items' : 1}).items[d.start_idx-1:d.end_idx-1], 'v:val.text')}", &quickfixtextfunc)
+ set quickfixtextfunc&
+
+ " use a lambda function that returns an empty list
+ set quickfixtextfunc={d\ ->\ []}
+ Xexpr ['F1:10:2:green', 'F1:20:4:blue']
+ Xwindow
+ call assert_equal(['F1|10 col 2| green', 'F1|20 col 4| blue'],
+ \ getline(1, '$'))
+ Xclose
+ set quickfixtextfunc&
+
+ " use a lambda function that returns a list with empty strings
+ set quickfixtextfunc={d\ ->\ ['',\ '']}
+ Xexpr ['F1:10:2:green', 'F1:20:4:blue']
+ Xwindow
+ call assert_equal(['F1|10 col 2| green', 'F1|20 col 4| blue'],
+ \ getline(1, '$'))
+ Xclose
+ set quickfixtextfunc&
+
+ " set the per-quickfix list text function to a lambda function
+ call g:Xsetlist([], ' ',
+ \ {'quickfixtextfunc' :
+ \ {d -> map(g:Xgetlist({'id' : d.id, 'items' : 1}).items[d.start_idx-1:d.end_idx-1],
+ \ "'Line ' .. v:val.lnum .. ', Col ' .. v:val.col")}})
+ Xaddexpr ['F1:10:2:green', 'F1:20:4:blue']
+ Xwindow
+ call assert_equal('Line 10, Col 2', getline(1))
+ call assert_equal('Line 20, Col 4', getline(2))
+ Xclose
+ call assert_match("function('<lambda>\\d\\+')", string(g:Xgetlist({'quickfixtextfunc' : 1}).quickfixtextfunc))
+ call g:Xsetlist([], 'f')
endfunc
func Test_qftextfunc()
@@ -5087,4 +5138,62 @@ func Test_qftextfunc()
call Xtest_qftextfunc('l')
endfunc
+" Test for updating a location list for some other window and check that
+" 'qftextfunc' uses the correct location list.
+func Test_qftextfunc_other_loclist()
+ %bw!
+ call setloclist(0, [], 'f')
+
+ " create a window and a location list for it and open the location list
+ " window
+ lexpr ['F1:10:12:one', 'F1:20:14:two']
+ let w1_id = win_getid()
+ call setloclist(0, [], ' ',
+ \ {'lines': ['F1:10:12:one', 'F1:20:14:two'],
+ \ 'quickfixtextfunc':
+ \ {d -> map(getloclist(d.winid, {'id' : d.id,
+ \ 'items' : 1}).items[d.start_idx-1:d.end_idx-1],
+ \ "'Line ' .. v:val.lnum .. ', Col ' .. v:val.col")}})
+ lwindow
+ let w2_id = win_getid()
+
+ " create another window and a location list for it and open the location
+ " list window
+ topleft new
+ let w3_id = win_getid()
+ call setloclist(0, [], ' ',
+ \ {'lines': ['F2:30:32:eleven', 'F2:40:34:twelve'],
+ \ 'quickfixtextfunc':
+ \ {d -> map(getloclist(d.winid, {'id' : d.id,
+ \ 'items' : 1}).items[d.start_idx-1:d.end_idx-1],
+ \ "'Ligne ' .. v:val.lnum .. ', Colonne ' .. v:val.col")}})
+ lwindow
+ let w4_id = win_getid()
+
+ topleft new
+ lexpr ['F3:50:52:green', 'F3:60:54:blue']
+ let w5_id = win_getid()
+
+ " change the location list for some other window
+ call setloclist(0, [], 'r', {'lines': ['F3:55:56:aaa', 'F3:57:58:bbb']})
+ call setloclist(w1_id, [], 'r', {'lines': ['F1:62:63:bbb', 'F1:64:65:ccc']})
+ call setloclist(w3_id, [], 'r', {'lines': ['F2:76:77:ddd', 'F2:78:79:eee']})
+ call assert_equal(['Line 62, Col 63', 'Line 64, Col 65'],
+ \ getbufline(winbufnr(w2_id), 1, '$'))
+ call assert_equal(['Ligne 76, Colonne 77', 'Ligne 78, Colonne 79'],
+ \ getbufline(winbufnr(w4_id), 1, '$'))
+ call setloclist(w2_id, [], 'r', {'lines': ['F1:32:33:fff', 'F1:34:35:ggg']})
+ call setloclist(w4_id, [], 'r', {'lines': ['F2:46:47:hhh', 'F2:48:49:jjj']})
+ call assert_equal(['Line 32, Col 33', 'Line 34, Col 35'],
+ \ getbufline(winbufnr(w2_id), 1, '$'))
+ call assert_equal(['Ligne 46, Colonne 47', 'Ligne 48, Colonne 49'],
+ \ getbufline(winbufnr(w4_id), 1, '$'))
+
+ call win_gotoid(w5_id)
+ lwindow
+ call assert_equal(['F3|55 col 56| aaa', 'F3|57 col 58| bbb'],
+ \ getline(1, '$'))
+ %bw!
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_visual.vim b/src/nvim/testdir/test_visual.vim
index a40b0236e0..b7c5717bd2 100644
--- a/src/nvim/testdir/test_visual.vim
+++ b/src/nvim/testdir/test_visual.vim
@@ -1,5 +1,8 @@
" Tests for various Visual modes.
+source shared.vim
+source check.vim
+
func Test_block_shift_multibyte()
" Uses double-wide character.
split
@@ -1060,5 +1063,24 @@ func Test_visual_put_in_block_using_zy_and_zp()
bwipe!
endfunc
+func Test_visual_put_blockedit_zy_and_zp()
+ new
+
+ call setline(1, ['aa', 'bbbbb', 'ccc', '', 'XX', 'GGHHJ', 'RTZU'])
+ exe "normal! gg0\<c-v>2j$zy"
+ norm! 5gg0zP
+ call assert_equal(['aa', 'bbbbb', 'ccc', '', 'aaXX', 'bbbbbGGHHJ', 'cccRTZU'], getline(1, 7))
+ "
+ " now with blockmode editing
+ sil %d
+ :set ve=block
+ call setline(1, ['aa', 'bbbbb', 'ccc', '', 'XX', 'GGHHJ', 'RTZU'])
+ exe "normal! gg0\<c-v>2j$zy"
+ norm! 5gg0zP
+ call assert_equal(['aa', 'bbbbb', 'ccc', '', 'aaXX', 'bbbbbGGHHJ', 'cccRTZU'], getline(1, 7))
+ set ve&vim
+ bw!
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c
index fd83681aed..6e885279a9 100644
--- a/src/nvim/tui/tui.c
+++ b/src/nvim/tui/tui.c
@@ -52,21 +52,18 @@
#define OUTBUF_SIZE 0xffff
#define TOO_MANY_EVENTS 1000000
-#define STARTS_WITH(str, prefix) (strlen(str) >= (sizeof(prefix) - 1) \
- && 0 == memcmp((str), (prefix), sizeof(prefix) - 1))
-#define TMUX_WRAP(is_tmux, seq) ((is_tmux) \
- ? DCS_STR "tmux;\x1b" seq STERM_STR : seq)
-#define SCREEN_TMUX_WRAP(is_screen, is_tmux, seq) \
- ((is_screen) \
- ? DCS_STR seq STERM_STR : (is_tmux) \
- ? DCS_STR "tmux;\x1b" seq STERM_STR : seq)
+#define STARTS_WITH(str, prefix) \
+ (strlen(str) >= (sizeof(prefix) - 1) \
+ && 0 == memcmp((str), (prefix), sizeof(prefix) - 1))
+#define TMUX_WRAP(is_tmux, seq) \
+ ((is_tmux) ? "\x1bPtmux;\x1b" seq "\x1b\\" : seq)
#define LINUXSET0C "\x1b[?0c"
#define LINUXSET1C "\x1b[?1c"
#ifdef NVIM_UNIBI_HAS_VAR_FROM
#define UNIBI_SET_NUM_VAR(var, num) \
do { \
- (var) = unibi_var_from_num((num)); \
+ (var) = unibi_var_from_num((num)); \
} while (0)
#else
#define UNIBI_SET_NUM_VAR(var, num) (var).i = (num);
@@ -301,12 +298,6 @@ static void terminfo_start(UI *ui)
data->invis, sizeof data->invis);
// Set 't_Co' from the result of unibilium & fix_terminfo.
t_colors = unibi_get_num(data->ut, unibi_max_colors);
- // Ask the terminal to send us the background color.
- // If get_bg is sent at the same time after enter_ca_mode, tmux will not send
- // get_bg to the host terminal. To avoid this, send get_bg before
- // enter_ca_mode.
- data->input.waiting_for_bg_response = 5;
- unibi_out_ext(ui, data->unibi_ext.get_bg);
// Enter alternate screen, save title, and clear.
// NOTE: Do this *before* changing terminal settings. #6433
unibi_out(ui, unibi_enter_ca_mode);
@@ -314,6 +305,9 @@ static void terminfo_start(UI *ui)
unibi_out_ext(ui, data->unibi_ext.save_title);
unibi_out(ui, unibi_keypad_xmit);
unibi_out(ui, unibi_clear_screen);
+ // Ask the terminal to send us the background color.
+ data->input.waiting_for_bg_response = 5;
+ unibi_out_ext(ui, data->unibi_ext.get_bg);
// Enable bracketed paste
unibi_out_ext(ui, data->unibi_ext.enable_bracketed_paste);
@@ -335,7 +329,6 @@ static void terminfo_start(UI *ui)
uv_pipe_init(&data->write_loop, &data->output_handle.pipe, 0);
uv_pipe_open(&data->output_handle.pipe, data->out_fd);
}
-
flush_buf(ui);
}
@@ -1780,10 +1773,8 @@ static void patch_terminfo_bugs(TUIData *data, const char *term,
#define XTERM_SETAB_16 \
"\x1b[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e39%;m"
- data->unibi_ext.get_bg =
- (int)unibi_add_ext_str(ut, "ext.get_bg",
- SCREEN_TMUX_WRAP((screen && !tmux), tmux,
- "\x1b]11;?\x07"));
+ data->unibi_ext.get_bg = (int)unibi_add_ext_str(ut, "ext.get_bg",
+ "\x1b]11;?\x07");
// Terminals with 256-colour SGR support despite what terminfo says.
if (unibi_get_num(ut, unibi_max_colors) < 256) {
diff --git a/src/nvim/window.c b/src/nvim/window.c
index aea60fe24c..d051e8e467 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -4597,6 +4597,18 @@ static win_T *win_alloc(win_T *after, int hidden)
}
+// Free one wininfo_T.
+void
+free_wininfo(wininfo_T *wip, buf_T *bp)
+{
+ if (wip->wi_optset) {
+ clear_winopt(&wip->wi_opt);
+ deleteFoldRecurse(bp, &wip->wi_folds);
+ }
+ xfree(wip);
+}
+
+
/*
* Remove window 'wp' from the window list and free the structure.
*/
@@ -4647,9 +4659,30 @@ win_free (
/* Remove the window from the b_wininfo lists, it may happen that the
* freed memory is re-used for another window. */
FOR_ALL_BUFFERS(buf) {
- for (wip = buf->b_wininfo; wip != NULL; wip = wip->wi_next)
- if (wip->wi_win == wp)
+ for (wip = buf->b_wininfo; wip != NULL; wip = wip->wi_next) {
+ if (wip->wi_win == wp) {
+ wininfo_T *wip2;
+
+ // If there already is an entry with "wi_win" set to NULL it
+ // must be removed, it would never be used.
+ for (wip2 = buf->b_wininfo; wip2 != NULL; wip2 = wip2->wi_next) {
+ if (wip2->wi_win == NULL) {
+ if (wip2->wi_next != NULL) {
+ wip2->wi_next->wi_prev = wip2->wi_prev;
+ }
+ if (wip2->wi_prev == NULL) {
+ buf->b_wininfo = wip2->wi_next;
+ } else {
+ wip2->wi_prev->wi_next = wip2->wi_next;
+ }
+ free_wininfo(wip2, buf);
+ break;
+ }
+ }
+
wip->wi_win = NULL;
+ }
+ }
}
clear_matches(wp);
diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua
index 0c0f610401..91d2745130 100644
--- a/test/functional/api/vim_spec.lua
+++ b/test/functional/api/vim_spec.lua
@@ -2003,6 +2003,7 @@ describe('API', function()
it('should have information about window options', function()
eq({
+ allows_duplicates = true,
commalist = false;
default = "";
flaglist = false;
@@ -2020,6 +2021,7 @@ describe('API', function()
it('should have information about buffer options', function()
eq({
+ allows_duplicates = true,
commalist = false,
default = "",
flaglist = false,
@@ -2041,6 +2043,7 @@ describe('API', function()
eq(false, meths.get_option'showcmd')
eq({
+ allows_duplicates = true,
commalist = false,
default = true,
flaglist = false,
diff --git a/test/functional/autocmd/autocmd_spec.lua b/test/functional/autocmd/autocmd_spec.lua
index e62d3bb66b..93d71a9e45 100644
--- a/test/functional/autocmd/autocmd_spec.lua
+++ b/test/functional/autocmd/autocmd_spec.lua
@@ -101,6 +101,17 @@ describe('autocmd', function()
}, eval('g:evs'))
end)
+ it('WinClosed from root directory', function()
+ command('cd /')
+ command('let g:evs = []')
+ command('autocmd WinClosed * :call add(g:evs, ["WinClosed", expand("<afile>")])')
+ command('new')
+ command('close')
+ eq({
+ {'WinClosed', '1001'},
+ }, eval('g:evs'))
+ end)
+
it('v:vim_did_enter is 1 after VimEnter', function()
eq(1, eval('v:vim_did_enter'))
end)
diff --git a/test/functional/core/startup_spec.lua b/test/functional/core/startup_spec.lua
index 658dfbda60..a70b94c0e9 100644
--- a/test/functional/core/startup_spec.lua
+++ b/test/functional/core/startup_spec.lua
@@ -443,10 +443,7 @@ describe('user config init', function()
before_each(function()
rmdir(xhome)
- -- TODO, make mkdir_p helper
- mkdir(xhome)
- mkdir(xconfig)
- mkdir(xconfig .. pathsep .. 'nvim')
+ mkdir_p(xconfig .. pathsep .. 'nvim')
write_file(init_lua_path, [[
vim.g.lua_rc = 1
diff --git a/test/functional/eval/system_spec.lua b/test/functional/eval/system_spec.lua
index 8b18eff451..c374baf695 100644
--- a/test/functional/eval/system_spec.lua
+++ b/test/functional/eval/system_spec.lua
@@ -174,6 +174,21 @@ describe('system()', function()
end)
end
+ it('works with powershell w/ UTF-8 text (#13713)', function()
+ if not helpers.has_powershell() then
+ pending("not tested; powershell was not found", function() end)
+ return
+ end
+ -- Should work with recommended config used in helper
+ helpers.set_shell_powershell()
+ eq('ああ\n', eval([[system('Write-Output "ああ"')]]))
+ -- Sanity test w/ default encoding
+ -- * on Windows, expected to default to Western European enc
+ -- * on Linux, expected to default to UTF8
+ command([[let &shellcmdflag = '-NoLogo -NoProfile -ExecutionPolicy RemoteSigned -Command ']])
+ eq(iswin() and '??\n' or 'ああ\n', eval([[system('Write-Output "ああ"')]]))
+ end)
+
it('`echo` and waits for its return', function()
feed(':call system("echo")<cr>')
screen:expect([[
@@ -554,4 +569,20 @@ describe('systemlist()', function()
assert(out[1]:sub(0, 5) == 'pid: ', out)
os_kill(out[1]:match("%d+"))
end)
+
+ it('works with powershell w/ UTF-8 text (#13713)', function()
+ if not helpers.has_powershell() then
+ pending("not tested; powershell was not found", function() end)
+ return
+ end
+ -- Should work with recommended config used in helper
+ helpers.set_shell_powershell()
+ eq({iswin() and 'あ\r' or 'あ'}, eval([[systemlist('Write-Output あ')]]))
+ -- Sanity test w/ default encoding
+ -- * on Windows, expected to default to Western European enc
+ -- * on Linux, expected to default to UTF8
+ command([[let &shellcmdflag = '-NoLogo -NoProfile -ExecutionPolicy RemoteSigned -Command ']])
+ eq({iswin() and '?\r' or 'あ'}, eval([[systemlist('Write-Output あ')]]))
+ end)
+
end)
diff --git a/test/functional/ex_cmds/make_spec.lua b/test/functional/ex_cmds/make_spec.lua
new file mode 100644
index 0000000000..3b4d22ab38
--- /dev/null
+++ b/test/functional/ex_cmds/make_spec.lua
@@ -0,0 +1,44 @@
+local helpers = require('test.functional.helpers')(after_each)
+local clear = helpers.clear
+local eval = helpers.eval
+local has_powershell = helpers.has_powershell
+local matches = helpers.matches
+local nvim = helpers.nvim
+local nvim_dir = helpers.nvim_dir
+
+describe(':make', function()
+ clear()
+ before_each(function ()
+ clear()
+ end)
+
+ describe('with powershell', function()
+ if not has_powershell() then
+ pending("not tested; powershell was not found", function() end)
+ return
+ end
+ before_each(function ()
+ helpers.set_shell_powershell()
+ end)
+
+ it('captures stderr & non zero exit code #14349', function ()
+ nvim('set_option', 'makeprg', nvim_dir..'/shell-test foo')
+ local out = eval('execute("make")')
+ -- Make program exit code correctly captured
+ matches('\nshell returned 3', out)
+ -- Error message is captured in the file and printed in the footer
+ matches('\n.*%: Unknown first argument%: foo', out)
+ end)
+
+ it('captures stderr & zero exit code #14349', function ()
+ nvim('set_option', 'makeprg', nvim_dir..'/shell-test')
+ local out = eval('execute("make")')
+ -- Ensure there are no "shell returned X" messages between
+ -- command and last line (indicating zero exit)
+ matches('LastExitCode%s+[(]', out)
+ matches('\n.*%: ready [$]', out)
+ end)
+
+ end)
+
+end)
diff --git a/test/functional/ex_cmds/source_spec.lua b/test/functional/ex_cmds/source_spec.lua
index a03e1ae9ce..37c97f519a 100644
--- a/test/functional/ex_cmds/source_spec.lua
+++ b/test/functional/ex_cmds/source_spec.lua
@@ -9,6 +9,8 @@ local feed_command = helpers.feed_command
local write_file = helpers.write_file
local exec = helpers.exec
local eval = helpers.eval
+local exec_capture = helpers.exec_capture
+local neq = helpers.neq
describe(':source', function()
before_each(function()
@@ -90,4 +92,27 @@ describe(':source', function()
eq(12, eval('g:c'))
os.remove(test_file)
end)
+
+ it("doesn't throw E484 for lua parsing/runtime errors", function()
+ local test_file = 'test.lua'
+
+ -- Does throw E484 for unreadable files
+ local ok, result = pcall(exec_capture, ":source "..test_file ..'noexisting')
+ eq(false, ok)
+ neq(nil, result:find("E484"))
+
+ -- Doesn't throw for parsing error
+ write_file (test_file, "vim.g.c = ")
+ ok, result = pcall(exec_capture, ":source "..test_file)
+ eq(false, ok)
+ eq(nil, result:find("E484"))
+ os.remove(test_file)
+
+ -- Doesn't throw for runtime error
+ write_file (test_file, "error('Cause error anyway :D')")
+ ok, result = pcall(exec_capture, ":source "..test_file)
+ eq(false, ok)
+ eq(nil, result:find("E484"))
+ os.remove(test_file)
+ end)
end)
diff --git a/test/functional/fixtures/fake-lsp-server.lua b/test/functional/fixtures/fake-lsp-server.lua
index bcd5e22492..b7fddc8f29 100644
--- a/test/functional/fixtures/fake-lsp-server.lua
+++ b/test/functional/fixtures/fake-lsp-server.lua
@@ -464,6 +464,23 @@ function tests.invalid_header()
io.stdout:write("Content-length: \r\n")
end
+function tests.decode_nil()
+ skeleton {
+ on_init = function(_)
+ return { capabilities = {} }
+ end;
+ body = function()
+ notify('start')
+ notify("workspace/executeCommand", {
+ arguments = { "EXTRACT_METHOD", {metadata = {field = vim.NIL}}, 3, 0, 6123, vim.NIL },
+ command = "refactor.perform",
+ title = "EXTRACT_METHOD"
+ })
+ notify('finish')
+ end;
+ }
+end
+
-- Tests will be indexed by TEST_NAME
local kill_timer = vim.loop.new_timer()
diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua
index 08ca14c3df..03ef441ef3 100644
--- a/test/functional/helpers.lua
+++ b/test/functional/helpers.lua
@@ -513,13 +513,15 @@ end
function module.set_shell_powershell()
local shell = iswin() and 'powershell' or 'pwsh'
assert(module.has_powershell())
- local cmd = 'Remove-Item -Force '..table.concat(iswin()
+ local set_encoding = '[Console]::InputEncoding=[Console]::OutputEncoding=[System.Text.Encoding]::UTF8;'
+ local cmd = set_encoding..'Remove-Item -Force '..table.concat(iswin()
and {'alias:cat', 'alias:echo', 'alias:sleep'}
or {'alias:echo'}, ',')..';'
module.source([[
let &shell = ']]..shell..[['
- set shellquote= shellpipe=\| shellxquote=
- let &shellredir = '| Out-File -Encoding UTF8'
+ set shellquote= shellxquote=
+ let &shellpipe = '2>&1 | Out-File -Encoding UTF8 %s; exit $LastExitCode'
+ let &shellredir = '2>&1 | Out-File -Encoding UTF8 %s; exit $LastExitCode'
let &shellcmdflag = '-NoLogo -NoProfile -ExecutionPolicy RemoteSigned -Command ]]..cmd..[['
]])
end
@@ -878,9 +880,11 @@ function module.os_kill(pid)
or 'kill -9 '..pid..' > /dev/null'))
end
--- Create directories with non exsisting intermidiate directories
+-- Create folder with non existing parents
function module.mkdir_p(path)
- return module.meths.call_function('mkdir', {path, 'p'})
+ return os.execute((iswin()
+ and 'mkdir '..path
+ or 'mkdir -p '..path))
end
module = global_helpers.tbl_extend('error', module, global_helpers)
diff --git a/test/functional/legacy/memory_usage_spec.lua b/test/functional/legacy/memory_usage_spec.lua
index 97ac96804e..0f2d77093a 100644
--- a/test/functional/legacy/memory_usage_spec.lua
+++ b/test/functional/legacy/memory_usage_spec.lua
@@ -166,4 +166,36 @@ describe('memory usage', function()
check_result({before=before, after=after, last=last},
pcall(ok, last.last < upper))
end)
+
+ it('releases memory when closing windows when folds exist', function()
+ local pid = eval('getpid()')
+ source([[
+ new
+ " Insert lines
+ call nvim_buf_set_lines(0, 0, 0, v:false, repeat([''], 999))
+ " Create folds
+ normal! gg
+ for _ in range(500)
+ normal! zfjj
+ endfor
+ ]])
+ poke_eventloop()
+ local before = monitor_memory_usage(pid)
+ source([[
+ " Split and close window multiple times
+ for _ in range(1000)
+ split
+ close
+ endfor
+ ]])
+ poke_eventloop()
+ local after = monitor_memory_usage(pid)
+ source('bwipe!')
+ poke_eventloop()
+ -- Allow for an increase of 5% in memory usage, which accommodates minor fluctuation,
+ -- but is small enough that if memory were not released (prior to PR #14884), the test
+ -- would fail.
+ local upper = before.last * 1.05
+ check_result({before=before, after=after}, pcall(ok, after.last <= upper))
+ end)
end)
diff --git a/test/functional/lua/buffer_updates_spec.lua b/test/functional/lua/buffer_updates_spec.lua
index 1b2c21783e..a5b613f0b2 100644
--- a/test/functional/lua/buffer_updates_spec.lua
+++ b/test/functional/lua/buffer_updates_spec.lua
@@ -1001,6 +1001,39 @@ describe('lua: nvim_buf_attach on_bytes', function()
}
end)
+ it("flushes delbytes on substitute", function()
+ local check_events = setup_eventcheck(verify, {"AAA", "BBB", "CCC"})
+
+ feed("gg0")
+ command("s/AAA/GGG/")
+
+ check_events {
+ { "test1", "bytes", 1, 3, 0, 0, 0, 0, 3, 3, 0, 3, 3 };
+ }
+
+ -- check that byte updates for :delete (which uses curbuf->deleted_bytes2)
+ -- are correct
+ command("delete")
+ check_events {
+ { "test1", "bytes", 1, 4, 0, 0, 0, 1, 0, 4, 0, 0, 0 };
+ }
+ end)
+
+ it("flushes delbytes on join", function()
+ local check_events = setup_eventcheck(verify, {"AAA", "BBB", "CCC"})
+
+ feed("gg0J")
+
+ check_events {
+ { "test1", "bytes", 1, 3, 0, 3, 3, 1, 0, 1, 0, 1, 1 };
+ }
+
+ command("delete")
+ check_events {
+ { "test1", "bytes", 1, 5, 0, 0, 0, 1, 0, 8, 0, 0, 0 };
+ }
+ end)
+
teardown(function()
os.remove "Xtest-reload"
os.remove "Xtest-undofile"
diff --git a/test/functional/lua/vim_spec.lua b/test/functional/lua/vim_spec.lua
index 836f514433..eff838aea3 100644
--- a/test/functional/lua/vim_spec.lua
+++ b/test/functional/lua/vim_spec.lua
@@ -1228,6 +1228,21 @@ describe('lua stdlib', function()
eq("only-local", result[8])
end)
+ it('should allow you to retrieve window opts even if they have not been set', function()
+ local result = exec_lua [[
+ local result = {}
+ table.insert(result, vim.opt.number:get())
+ table.insert(result, vim.opt_local.number:get())
+
+ vim.opt_local.number = true
+ table.insert(result, vim.opt.number:get())
+ table.insert(result, vim.opt_local.number:get())
+
+ return result
+ ]]
+ eq({false, false, true, true}, result)
+ end)
+
it('should allow all sorts of string manipulation', function()
eq({'hello', 'hello world', 'start hello world'}, exec_lua [[
local results = {}
@@ -1299,6 +1314,22 @@ describe('lua stdlib', function()
eq("*.c", wildignore[1])
end)
+ it('should work for options that are both commalist and flaglist', function()
+ local result = exec_lua [[
+ vim.opt.whichwrap = "b,s"
+ return vim.opt.whichwrap:get()
+ ]]
+
+ eq({b = true, s = true}, result)
+
+ result = exec_lua [[
+ vim.opt.whichwrap = { b = true, s = false, h = true }
+ return vim.opt.whichwrap:get()
+ ]]
+
+ eq({b = true, h = true}, result)
+ end)
+
it('should work for key-value pair options', function()
local listchars = exec_lua [[
vim.opt.listchars = "tab:>~,space:_"
@@ -1569,7 +1600,222 @@ describe('lua stdlib', function()
eq(wildignore, 'super_first,first,foo')
end)
- end)
+ it('should not remove duplicates from wildmode: #14708', function()
+ local wildmode = exec_lua [[
+ vim.opt.wildmode = {"full", "list", "full"}
+ return vim.o.wildmode
+ ]]
+
+ eq(wildmode, 'full,list,full')
+ end)
+
+ describe('option types', function()
+ it('should allow to set option with numeric value', function()
+ eq(4, exec_lua [[
+ vim.opt.tabstop = 4
+ return vim.bo.tabstop
+ ]])
+
+ matches("Invalid option type 'string' for 'tabstop'", pcall_err(exec_lua, [[
+ vim.opt.tabstop = '4'
+ ]]))
+ matches("Invalid option type 'boolean' for 'tabstop'", pcall_err(exec_lua, [[
+ vim.opt.tabstop = true
+ ]]))
+ matches("Invalid option type 'table' for 'tabstop'", pcall_err(exec_lua, [[
+ vim.opt.tabstop = {4, 2}
+ ]]))
+ matches("Invalid option type 'function' for 'tabstop'", pcall_err(exec_lua, [[
+ vim.opt.tabstop = function()
+ return 4
+ end
+ ]]))
+ end)
+
+ it('should allow to set option with boolean value', function()
+ eq(true, exec_lua [[
+ vim.opt.undofile = true
+ return vim.bo.undofile
+ ]])
+
+ matches("Invalid option type 'number' for 'undofile'", pcall_err(exec_lua, [[
+ vim.opt.undofile = 0
+ ]]))
+ matches("Invalid option type 'table' for 'undofile'", pcall_err(exec_lua, [[
+ vim.opt.undofile = {true}
+ ]]))
+ matches("Invalid option type 'string' for 'undofile'", pcall_err(exec_lua, [[
+ vim.opt.undofile = 'true'
+ ]]))
+ matches("Invalid option type 'function' for 'undofile'", pcall_err(exec_lua, [[
+ vim.opt.undofile = function()
+ return true
+ end
+ ]]))
+ end)
+
+ it('should allow to set option with array or string value', function()
+ eq('indent,eol,start', exec_lua [[
+ vim.opt.backspace = {'indent','eol','start'}
+ return vim.go.backspace
+ ]])
+ eq('indent,eol,start', exec_lua [[
+ vim.opt.backspace = 'indent,eol,start'
+ return vim.go.backspace
+ ]])
+
+ matches("Invalid option type 'boolean' for 'backspace'", pcall_err(exec_lua, [[
+ vim.opt.backspace = true
+ ]]))
+ matches("Invalid option type 'number' for 'backspace'", pcall_err(exec_lua, [[
+ vim.opt.backspace = 2
+ ]]))
+ matches("Invalid option type 'function' for 'backspace'", pcall_err(exec_lua, [[
+ vim.opt.backspace = function()
+ return 'indent,eol,start'
+ end
+ ]]))
+ end)
+
+ it('should allow set option with map or string value', function()
+ eq("eol:~,space:.", exec_lua [[
+ vim.opt.listchars = {
+ eol = "~",
+ space = ".",
+ }
+ return vim.o.listchars
+ ]])
+ eq("eol:~,space:.,tab:>~", exec_lua [[
+ vim.opt.listchars = "eol:~,space:.,tab:>~"
+ return vim.o.listchars
+ ]])
+
+ matches("Invalid option type 'boolean' for 'listchars'", pcall_err(exec_lua, [[
+ vim.opt.listchars = true
+ ]]))
+ matches("Invalid option type 'number' for 'listchars'", pcall_err(exec_lua, [[
+ vim.opt.listchars = 2
+ ]]))
+ matches("Invalid option type 'function' for 'listchars'", pcall_err(exec_lua, [[
+ vim.opt.listchars = function()
+ return "eol:~,space:.,tab:>~"
+ end
+ ]]))
+ end)
+
+ it('should allow set option with set or string value', function()
+ local ww = exec_lua [[
+ vim.opt.whichwrap = {
+ b = true,
+ s = 1,
+ }
+ return vim.go.whichwrap
+ ]]
+
+ eq(ww, "b,s")
+ eq("b,s,<,>,[,]", exec_lua [[
+ vim.opt.whichwrap = "b,s,<,>,[,]"
+ return vim.go.whichwrap
+ ]])
+
+ matches("Invalid option type 'boolean' for 'whichwrap'", pcall_err(exec_lua, [[
+ vim.opt.whichwrap = true
+ ]]))
+ matches("Invalid option type 'number' for 'whichwrap'", pcall_err(exec_lua, [[
+ vim.opt.whichwrap = 2
+ ]]))
+ matches("Invalid option type 'function' for 'whichwrap'", pcall_err(exec_lua, [[
+ vim.opt.whichwrap = function()
+ return "b,s,<,>,[,]"
+ end
+ ]]))
+ end)
+ end)
+
+ -- isfname=a,b,c,,,d,e,f
+ it('can handle isfname ,,,', function()
+ local result = exec_lua [[
+ vim.opt.isfname = "a,b,,,c"
+ return { vim.opt.isfname:get(), vim.api.nvim_get_option('isfname') }
+ ]]
+
+ eq({{",", "a", "b", "c"}, "a,b,,,c"}, result)
+ end)
+
+ -- isfname=a,b,c,^,,def
+ it('can handle isfname ,^,,', function()
+ local result = exec_lua [[
+ vim.opt.isfname = "a,b,^,,c"
+ return { vim.opt.isfname:get(), vim.api.nvim_get_option('isfname') }
+ ]]
+
+ eq({{"^,", "a", "b", "c"}, "a,b,^,,c"}, result)
+ end)
+
+
+
+ describe('https://github.com/neovim/neovim/issues/14828', function()
+ it('gives empty list when item is empty:array', function()
+ eq({}, exec_lua [[
+ vim.cmd("set wildignore=")
+ return vim.opt.wildignore:get()
+ ]])
+
+ eq({}, exec_lua [[
+ vim.opt.wildignore = {}
+ return vim.opt.wildignore:get()
+ ]])
+ end)
+
+ it('gives empty list when item is empty:set', function()
+ eq({}, exec_lua [[
+ vim.cmd("set formatoptions=")
+ return vim.opt.formatoptions:get()
+ ]])
+
+ eq({}, exec_lua [[
+ vim.opt.formatoptions = {}
+ return vim.opt.formatoptions:get()
+ ]])
+ end)
+
+ it('does not append to empty item', function()
+ eq({"*.foo", "*.bar"}, exec_lua [[
+ vim.opt.wildignore = {}
+ vim.opt.wildignore:append { "*.foo", "*.bar" }
+
+ return vim.opt.wildignore:get()
+ ]])
+ end)
+
+ it('does not prepend to empty item', function()
+ eq({"*.foo", "*.bar"}, exec_lua [[
+ vim.opt.wildignore = {}
+ vim.opt.wildignore:prepend { "*.foo", "*.bar" }
+
+ return vim.opt.wildignore:get()
+ ]])
+ end)
+
+ it('append to empty set', function()
+ eq({ t = true }, exec_lua [[
+ vim.opt.formatoptions = {}
+ vim.opt.formatoptions:append("t")
+
+ return vim.opt.formatoptions:get()
+ ]])
+ end)
+
+ it('prepend to empty set', function()
+ eq({ t = true }, exec_lua [[
+ vim.opt.formatoptions = {}
+ vim.opt.formatoptions:prepend("t")
+
+ return vim.opt.formatoptions:get()
+ ]])
+ end)
+ end)
+ end) -- vim.opt
it('vim.cmd', function()
exec_lua [[
diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua
index 663271deab..35cc2d3075 100644
--- a/test/functional/plugin/lsp_spec.lua
+++ b/test/functional/plugin/lsp_spec.lua
@@ -105,8 +105,8 @@ local function test_rpc_server(config)
return NIL
end
if method == 'handler' then
- if config.on_callback then
- config.on_callback(unpack(args))
+ if config.on_handler then
+ config.on_handler(unpack(args))
end
end
return NIL
@@ -215,7 +215,7 @@ describe('LSP', function()
end)
it('should run correctly', function()
- local expected_callbacks = {
+ local expected_handlers = {
{NIL, "test", {}, 1};
}
test_rpc_server {
@@ -232,15 +232,15 @@ describe('LSP', function()
eq(0, signal, "exit signal", fake_lsp_logfile)
end;
-- Note that NIL must be used here.
- -- on_callback(err, method, result, client_id)
- on_callback = function(...)
- eq(table.remove(expected_callbacks), {...})
+ -- on_handler(err, method, result, client_id)
+ on_handler = function(...)
+ eq(table.remove(expected_handlers), {...})
end;
}
end)
it('should fail', function()
- local expected_callbacks = {
+ local expected_handlers = {
{NIL, "test", {}, 1};
}
test_rpc_server {
@@ -255,8 +255,8 @@ describe('LSP', function()
assert_log(pesc([[assert_eq failed: left == "\"shutdown\"", right == "\"test\""]]),
fake_lsp_logfile)
end;
- on_callback = function(...)
- eq(table.remove(expected_callbacks), {...}, "expected callback")
+ on_handler = function(...)
+ eq(table.remove(expected_handlers), {...}, "expected handler")
end;
}
end)
@@ -266,7 +266,7 @@ describe('LSP', function()
pending('hangs the build on openbsd #14028, re-enable with freeze timeout #14204')
return
end
- local expected_callbacks = {
+ local expected_handlers = {
{NIL, "shutdown", {}, 1, NIL};
{NIL, "test", {}, 1};
}
@@ -282,14 +282,14 @@ describe('LSP', function()
eq(0, code, "exit code", fake_lsp_logfile)
eq(0, signal, "exit signal", fake_lsp_logfile)
end;
- on_callback = function(...)
- eq(table.remove(expected_callbacks), {...}, "expected callback")
+ on_handler = function(...)
+ eq(table.remove(expected_handlers), {...}, "expected handler")
end;
}
end)
it('client should return settings via workspace/configuration handler', function()
- local expected_callbacks = {
+ local expected_handlers = {
{NIL, "shutdown", {}, 1};
{NIL, "workspace/configuration", { items = {
{ section = "testSetting1" };
@@ -307,8 +307,8 @@ describe('LSP', function()
eq(0, code, "exit code", fake_lsp_logfile)
eq(0, signal, "exit signal", fake_lsp_logfile)
end;
- on_callback = function(err, method, params, client_id)
- eq(table.remove(expected_callbacks), {err, method, params, client_id}, "expected callback")
+ on_handler = function(err, method, params, client_id)
+ eq(table.remove(expected_handlers), {err, method, params, client_id}, "expected handler")
if method == 'start' then
exec_lua([=[
local client = vim.lsp.get_client_by_id(TEST_RPC_CLIENT_ID)
@@ -344,7 +344,7 @@ describe('LSP', function()
end)
it('should verify capabilities sent', function()
- local expected_callbacks = {
+ local expected_handlers = {
{NIL, "shutdown", {}, 1};
}
test_rpc_server {
@@ -361,14 +361,14 @@ describe('LSP', function()
eq(0, code, "exit code", fake_lsp_logfile)
eq(0, signal, "exit signal", fake_lsp_logfile)
end;
- on_callback = function(...)
- eq(table.remove(expected_callbacks), {...}, "expected callback")
+ on_handler = function(...)
+ eq(table.remove(expected_handlers), {...}, "expected handler")
end;
}
end)
it('client.supports_methods() should validate capabilities', function()
- local expected_callbacks = {
+ local expected_handlers = {
{NIL, "shutdown", {}, 1};
}
test_rpc_server {
@@ -395,14 +395,14 @@ describe('LSP', function()
eq(0, code, "exit code", fake_lsp_logfile)
eq(0, signal, "exit signal", fake_lsp_logfile)
end;
- on_callback = function(...)
- eq(table.remove(expected_callbacks), {...}, "expected callback")
+ on_handler = function(...)
+ eq(table.remove(expected_handlers), {...}, "expected handler")
end;
}
end)
it('should call unsupported_method when trying to call an unsupported method', function()
- local expected_callbacks = {
+ local expected_handlers = {
{NIL, "shutdown", {}, 1};
}
test_rpc_server {
@@ -412,7 +412,7 @@ describe('LSP', function()
BUFFER = vim.api.nvim_get_current_buf()
lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID)
vim.lsp.handlers['textDocument/typeDefinition'] = function(err, method)
- vim.lsp._last_lsp_callback = { err = err; method = method }
+ vim.lsp._last_lsp_handler = { err = err; method = method }
end
vim.lsp._unsupported_method = function(method)
vim.lsp._last_unsupported_method = method
@@ -425,7 +425,7 @@ describe('LSP', function()
client.stop()
local method = exec_lua("return vim.lsp._last_unsupported_method")
eq("textDocument/typeDefinition", method)
- local lsp_cb_call = exec_lua("return vim.lsp._last_lsp_callback")
+ local lsp_cb_call = exec_lua("return vim.lsp._last_lsp_handler")
eq("fake-error", lsp_cb_call.err)
eq("textDocument/typeDefinition", lsp_cb_call.method)
exec_lua [[
@@ -436,14 +436,14 @@ describe('LSP', function()
eq(0, code, "exit code", fake_lsp_logfile)
eq(0, signal, "exit signal", fake_lsp_logfile)
end;
- on_callback = function(...)
- eq(table.remove(expected_callbacks), {...}, "expected callback")
+ on_handler = function(...)
+ eq(table.remove(expected_handlers), {...}, "expected handler")
end;
}
end)
it('shouldn\'t call unsupported_method when no client and trying to call an unsupported method', function()
- local expected_callbacks = {
+ local expected_handlers = {
{NIL, "shutdown", {}, 1};
}
test_rpc_server {
@@ -451,7 +451,7 @@ describe('LSP', function()
on_setup = function()
exec_lua([=[
vim.lsp.handlers['textDocument/typeDefinition'] = function(err, method)
- vim.lsp._last_lsp_callback = { err = err; method = method }
+ vim.lsp._last_lsp_handler = { err = err; method = method }
end
vim.lsp._unsupported_method = function(method)
vim.lsp._last_unsupported_method = method
@@ -463,20 +463,20 @@ describe('LSP', function()
on_init = function(client)
client.stop()
eq(NIL, exec_lua("return vim.lsp._last_unsupported_method"))
- eq(NIL, exec_lua("return vim.lsp._last_lsp_callback"))
+ eq(NIL, exec_lua("return vim.lsp._last_lsp_handler"))
end;
on_exit = function(code, signal)
eq(0, code, "exit code", fake_lsp_logfile)
eq(0, signal, "exit signal", fake_lsp_logfile)
end;
- on_callback = function(...)
- eq(table.remove(expected_callbacks), {...}, "expected callback")
+ on_handler = function(...)
+ eq(table.remove(expected_handlers), {...}, "expected handler")
end;
}
end)
it('should not send didOpen if the buffer closes before init', function()
- local expected_callbacks = {
+ local expected_handlers = {
{NIL, "shutdown", {}, 1};
{NIL, "finish", {}, 1};
}
@@ -509,8 +509,8 @@ describe('LSP', function()
eq(0, code, "exit code", fake_lsp_logfile)
eq(0, signal, "exit signal", fake_lsp_logfile)
end;
- on_callback = function(err, method, params, client_id)
- eq(table.remove(expected_callbacks), {err, method, params, client_id}, "expected callback")
+ on_handler = function(err, method, params, client_id)
+ eq(table.remove(expected_handlers), {err, method, params, client_id}, "expected handler")
if method == 'finish' then
client.stop()
end
@@ -519,7 +519,7 @@ describe('LSP', function()
end)
it('should check the body sent attaching before init', function()
- local expected_callbacks = {
+ local expected_handlers = {
{NIL, "shutdown", {}, 1};
{NIL, "finish", {}, 1};
{NIL, "start", {}, 1};
@@ -552,11 +552,11 @@ describe('LSP', function()
eq(0, code, "exit code", fake_lsp_logfile)
eq(0, signal, "exit signal", fake_lsp_logfile)
end;
- on_callback = function(err, method, params, client_id)
+ on_handler = function(err, method, params, client_id)
if method == 'start' then
client.notify('finish')
end
- eq(table.remove(expected_callbacks), {err, method, params, client_id}, "expected callback")
+ eq(table.remove(expected_handlers), {err, method, params, client_id}, "expected handler")
if method == 'finish' then
client.stop()
end
@@ -565,7 +565,7 @@ describe('LSP', function()
end)
it('should check the body sent attaching after init', function()
- local expected_callbacks = {
+ local expected_handlers = {
{NIL, "shutdown", {}, 1};
{NIL, "finish", {}, 1};
{NIL, "start", {}, 1};
@@ -595,11 +595,11 @@ describe('LSP', function()
eq(0, code, "exit code", fake_lsp_logfile)
eq(0, signal, "exit signal", fake_lsp_logfile)
end;
- on_callback = function(err, method, params, client_id)
+ on_handler = function(err, method, params, client_id)
if method == 'start' then
client.notify('finish')
end
- eq(table.remove(expected_callbacks), {err, method, params, client_id}, "expected callback")
+ eq(table.remove(expected_handlers), {err, method, params, client_id}, "expected handler")
if method == 'finish' then
client.stop()
end
@@ -608,7 +608,7 @@ describe('LSP', function()
end)
it('should check the body and didChange full', function()
- local expected_callbacks = {
+ local expected_handlers = {
{NIL, "shutdown", {}, 1};
{NIL, "finish", {}, 1};
{NIL, "start", {}, 1};
@@ -638,7 +638,7 @@ describe('LSP', function()
eq(0, code, "exit code", fake_lsp_logfile)
eq(0, signal, "exit signal", fake_lsp_logfile)
end;
- on_callback = function(err, method, params, client_id)
+ on_handler = function(err, method, params, client_id)
if method == 'start' then
exec_lua [[
vim.api.nvim_buf_set_lines(BUFFER, 1, 2, false, {
@@ -647,7 +647,7 @@ describe('LSP', function()
]]
client.notify('finish')
end
- eq(table.remove(expected_callbacks), {err, method, params, client_id}, "expected callback")
+ eq(table.remove(expected_handlers), {err, method, params, client_id}, "expected handler")
if method == 'finish' then
client.stop()
end
@@ -656,7 +656,7 @@ describe('LSP', function()
end)
it('should check the body and didChange full with noeol', function()
- local expected_callbacks = {
+ local expected_handlers = {
{NIL, "shutdown", {}, 1};
{NIL, "finish", {}, 1};
{NIL, "start", {}, 1};
@@ -687,7 +687,7 @@ describe('LSP', function()
eq(0, code, "exit code", fake_lsp_logfile)
eq(0, signal, "exit signal", fake_lsp_logfile)
end;
- on_callback = function(err, method, params, client_id)
+ on_handler = function(err, method, params, client_id)
if method == 'start' then
exec_lua [[
vim.api.nvim_buf_set_lines(BUFFER, 1, 2, false, {
@@ -696,7 +696,7 @@ describe('LSP', function()
]]
client.notify('finish')
end
- eq(table.remove(expected_callbacks), {err, method, params, client_id}, "expected callback")
+ eq(table.remove(expected_handlers), {err, method, params, client_id}, "expected handler")
if method == 'finish' then
client.stop()
end
@@ -705,7 +705,7 @@ describe('LSP', function()
end)
it('should check the body and didChange incremental', function()
- local expected_callbacks = {
+ local expected_handlers = {
{NIL, "shutdown", {}, 1};
{NIL, "finish", {}, 1};
{NIL, "start", {}, 1};
@@ -736,7 +736,7 @@ describe('LSP', function()
eq(0, code, "exit code", fake_lsp_logfile)
eq(0, signal, "exit signal", fake_lsp_logfile)
end;
- on_callback = function(err, method, params, client_id)
+ on_handler = function(err, method, params, client_id)
if method == 'start' then
exec_lua [[
vim.api.nvim_buf_set_lines(BUFFER, 1, 2, false, {
@@ -745,7 +745,7 @@ describe('LSP', function()
]]
client.notify('finish')
end
- eq(table.remove(expected_callbacks), {err, method, params, client_id}, "expected callback")
+ eq(table.remove(expected_handlers), {err, method, params, client_id}, "expected handler")
if method == 'finish' then
client.stop()
end
@@ -755,7 +755,7 @@ describe('LSP', function()
-- TODO(askhan) we don't support full for now, so we can disable these tests.
pending('should check the body and didChange incremental normal mode editing', function()
- local expected_callbacks = {
+ local expected_handlers = {
{NIL, "shutdown", {}, 1};
{NIL, "finish", {}, 1};
{NIL, "start", {}, 1};
@@ -785,12 +785,12 @@ describe('LSP', function()
eq(0, code, "exit code", fake_lsp_logfile)
eq(0, signal, "exit signal", fake_lsp_logfile)
end;
- on_callback = function(err, method, params, client_id)
+ on_handler = function(err, method, params, client_id)
if method == 'start' then
helpers.command("normal! 1Go")
client.notify('finish')
end
- eq(table.remove(expected_callbacks), {err, method, params, client_id}, "expected callback")
+ eq(table.remove(expected_handlers), {err, method, params, client_id}, "expected handler")
if method == 'finish' then
client.stop()
end
@@ -799,7 +799,7 @@ describe('LSP', function()
end)
it('should check the body and didChange full with 2 changes', function()
- local expected_callbacks = {
+ local expected_handlers = {
{NIL, "shutdown", {}, 1};
{NIL, "finish", {}, 1};
{NIL, "start", {}, 1};
@@ -829,7 +829,7 @@ describe('LSP', function()
eq(0, code, "exit code", fake_lsp_logfile)
eq(0, signal, "exit signal", fake_lsp_logfile)
end;
- on_callback = function(err, method, params, client_id)
+ on_handler = function(err, method, params, client_id)
if method == 'start' then
exec_lua [[
vim.api.nvim_buf_set_lines(BUFFER, 1, 2, false, {
@@ -841,7 +841,7 @@ describe('LSP', function()
]]
client.notify('finish')
end
- eq(table.remove(expected_callbacks), {err, method, params, client_id}, "expected callback")
+ eq(table.remove(expected_handlers), {err, method, params, client_id}, "expected handler")
if method == 'finish' then
client.stop()
end
@@ -850,7 +850,7 @@ describe('LSP', function()
end)
it('should check the body and didChange full lifecycle', function()
- local expected_callbacks = {
+ local expected_handlers = {
{NIL, "shutdown", {}, 1};
{NIL, "finish", {}, 1};
{NIL, "start", {}, 1};
@@ -880,7 +880,7 @@ describe('LSP', function()
eq(0, code, "exit code", fake_lsp_logfile)
eq(0, signal, "exit signal", fake_lsp_logfile)
end;
- on_callback = function(err, method, params, client_id)
+ on_handler = function(err, method, params, client_id)
if method == 'start' then
exec_lua [[
vim.api.nvim_buf_set_lines(BUFFER, 1, 2, false, {
@@ -893,7 +893,7 @@ describe('LSP', function()
]]
client.notify('finish')
end
- eq(table.remove(expected_callbacks), {err, method, params, client_id}, "expected callback")
+ eq(table.remove(expected_handlers), {err, method, params, client_id}, "expected handler")
if method == 'finish' then
client.stop()
end
@@ -904,7 +904,7 @@ describe('LSP', function()
describe("parsing tests", function()
it('should handle invalid content-length correctly', function()
- local expected_callbacks = {
+ local expected_handlers = {
{NIL, "shutdown", {}, 1};
{NIL, "finish", {}, 1};
{NIL, "start", {}, 1};
@@ -923,7 +923,49 @@ describe('LSP', function()
eq(0, signal, "exit signal", fake_lsp_logfile)
end;
on_handler = function(err, method, params, client_id)
- eq(table.remove(expected_callbacks), {err, method, params, client_id}, "expected handler")
+ eq(table.remove(expected_handlers), {err, method, params, client_id}, "expected handler")
+ end;
+ }
+ end)
+
+ it('should not trim vim.NIL from the end of a list', function()
+ local expected_handlers = {
+ {NIL, "shutdown", {}, 1};
+ {NIL, "finish", {}, 1};
+ {NIL, "workspace/executeCommand", {
+ arguments = { "EXTRACT_METHOD", {metadata = {}}, 3, 0, 6123, NIL },
+ command = "refactor.perform",
+ title = "EXTRACT_METHOD"
+ }, 1};
+ {NIL, "start", {}, 1};
+ }
+ local client
+ test_rpc_server {
+ test_name = "decode_nil";
+ on_setup = function()
+ exec_lua [[
+ BUFFER = vim.api.nvim_create_buf(false, true)
+ vim.api.nvim_buf_set_lines(BUFFER, 0, -1, false, {
+ "testing";
+ "123";
+ })
+ ]]
+ end;
+ on_init = function(_client)
+ client = _client
+ exec_lua [[
+ assert(lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID))
+ ]]
+ end;
+ on_exit = function(code, signal)
+ eq(0, code, "exit code", fake_lsp_logfile)
+ eq(0, signal, "exit signal", fake_lsp_logfile)
+ end;
+ on_handler = function(err, method, params, client_id)
+ eq(table.remove(expected_handlers), {err, method, params, client_id}, "expected handler")
+ if method == 'finish' then
+ client.stop()
+ end
end;
}
end)
diff --git a/test/functional/provider/clipboard_spec.lua b/test/functional/provider/clipboard_spec.lua
index 2c681eb9d8..e5e21f11a6 100644
--- a/test/functional/provider/clipboard_spec.lua
+++ b/test/functional/provider/clipboard_spec.lua
@@ -506,6 +506,20 @@ describe('clipboard (with fake clipboard.vim)', function()
feed('p')
eq('textstar', meths.get_current_line())
end)
+
+ it('Block paste works currectly', function()
+ insert([[
+ aabbcc
+ ddeeff
+ ]])
+ feed('gg^<C-v>') -- Goto start of top line enter visual block mode
+ feed('3ljy^k') -- yank 4x2 block & goto initial location
+ feed('P') -- Paste it infront
+ expect([[
+ aabbaabbcc
+ ddeeddeeff
+ ]])
+ end)
end)
describe('clipboard=unnamedplus', function()
diff --git a/test/functional/ui/tabline_spec.lua b/test/functional/ui/tabline_spec.lua
index 23aae81745..ab8d63cda1 100644
--- a/test/functional/ui/tabline_spec.lua
+++ b/test/functional/ui/tabline_spec.lua
@@ -4,14 +4,17 @@ local clear, command, eq = helpers.clear, helpers.command, helpers.eq
describe('ui/ext_tabline', function()
local screen
- local event_tabs, event_curtab
+ local event_tabs, event_curtab, event_curbuf, event_buffers
before_each(function()
clear()
screen = Screen.new(25, 5)
screen:attach({rgb=true, ext_tabline=true})
- function screen:_handle_tabline_update(curtab, tabs)
- event_curtab, event_tabs = curtab, tabs
+ function screen:_handle_tabline_update(curtab, tabs, curbuf, buffers)
+ event_curtab = curtab
+ event_tabs = tabs
+ event_curbuf = curbuf
+ event_buffers = buffers
end
end)
@@ -45,4 +48,38 @@ describe('ui/ext_tabline', function()
eq(expected_tabs, event_tabs)
end}
end)
+
+ it('buffer UI events', function()
+ local expected_buffers_initial= {
+ {buffer = { id = 1 }, name = '[No Name]'},
+ }
+
+ screen:expect{grid=[[
+ ^ |
+ ~ |
+ ~ |
+ ~ |
+ |
+ ]], condition=function()
+ eq({ id = 1}, event_curbuf)
+ eq(expected_buffers_initial, event_buffers)
+ end}
+
+ command("badd another-buffer")
+ command("bnext")
+
+ local expected_buffers = {
+ {buffer = { id = 2 }, name = 'another-buffer'},
+ }
+ screen:expect{grid=[[
+ ^ |
+ ~ |
+ ~ |
+ ~ |
+ |
+ ]], condition=function()
+ eq({ id = 2 }, event_curbuf)
+ eq(expected_buffers, event_buffers)
+ end}
+ end)
end)
diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua
index 7c03005529..d81e272877 100644
--- a/test/unit/eval/typval_spec.lua
+++ b/test/unit/eval/typval_spec.lua
@@ -2623,7 +2623,7 @@ describe('typval.c', function()
describe('check_lock()', function()
local function tv_check_lock(lock, name, name_len, emsg)
return check_emsg(function()
- return lib.tv_check_lock(lock, name, name_len)
+ return lib.var_check_lock(lock, name, name_len)
end, emsg)
end
itp('works', function()
diff --git a/test/unit/marktree_spec.lua b/test/unit/marktree_spec.lua
index 56acc0f93e..cd9c7bef13 100644
--- a/test/unit/marktree_spec.lua
+++ b/test/unit/marktree_spec.lua
@@ -186,5 +186,20 @@ describe('marktree', function()
lib.marktree_check(tree)
shadoworder(tree, shadow, iter2)
end
+
+ -- Check iterator validity for 2 specific edge cases:
+ -- https://github.com/neovim/neovim/pull/14719
+ lib.marktree_clear(tree)
+ for i = 1,20 do
+ lib.marktree_put(tree, i, i, false)
+ end
+
+ lib.marktree_itr_get(tree, 10, 10, iter)
+ lib.marktree_del_itr(tree, iter, false)
+ eq(11, iter[0].node.key[iter[0].i].pos.col)
+
+ lib.marktree_itr_get(tree, 11, 11, iter)
+ lib.marktree_del_itr(tree, iter, false)
+ eq(12, iter[0].node.key[iter[0].i].pos.col)
end)
end)
diff --git a/third-party/CMakeLists.txt b/third-party/CMakeLists.txt
index f9da7c497a..e8999bf1ed 100644
--- a/third-party/CMakeLists.txt
+++ b/third-party/CMakeLists.txt
@@ -209,9 +209,8 @@ set(LIBICONV_SHA256 ccf536620a45458d26ba83887a983b96827001e92a13847b45e4925cc891
set(TREESITTER_C_URL https://github.com/tree-sitter/tree-sitter-c/archive/5aa0bbb.tar.gz)
set(TREESITTER_C_SHA256 a5dcb37460d83002dfae7f9a208170ddbc9a047f231b9d6b75da7d36d707db2f)
-# This is a bit after 0.19.5 because it fixes issues with queries
-set(TREESITTER_URL https://github.com/tree-sitter/tree-sitter/archive/6ed42747a4e0faee9b65edbbacc86ed0caeae05c.zip)
-set(TREESITTER_SHA256 07b8d090ae856d4ea8a494c08900271545d44af2558278a27693f9a47d9e75e3)
+set(TREESITTER_URL https://github.com/tree-sitter/tree-sitter/archive/v0.20.0.tar.gz)
+set(TREESITTER_SHA256 4a8070b9de17c3b8096181fe8530320ab3e8cca685d8bee6a3e8d164b5fb47da)
if(USE_BUNDLED_UNIBILIUM)
include(BuildUnibilium)