diff options
322 files changed, 14743 insertions, 5615 deletions
diff --git a/.github/actions/cache/action.yml b/.github/actions/cache/action.yml index 9ad14b8c27..e7ffdd66b6 100644 --- a/.github/actions/cache/action.yml +++ b/.github/actions/cache/action.yml @@ -10,6 +10,10 @@ runs: run: echo "CACHE_KEY=$CACHE_KEY-${{ join(matrix.*, '-') }}" >> $GITHUB_ENV shell: bash + - id: image + run: echo "version=$ImageVersion" >> $GITHUB_OUTPUT + shell: bash + # Avoid using '**/CMakeLists.txt' (or any pattern starting with '**/') even # if it makes the expression below simpler. hashFiles() has a timer that # will fail the job if it times out, which can happen if there are too many @@ -17,6 +21,6 @@ runs: - uses: actions/cache@v3 with: path: .deps - key: ${{ env.CACHE_KEY }}-${{ hashFiles('cmake**', + key: ${{ env.CACHE_KEY }}-${{ steps.image.outputs.version }}-${{ hashFiles('cmake**', '.github/workflows/test.yml', 'CMakeLists.txt', 'runtime/CMakeLists.txt', 'src/nvim/**/CMakeLists.txt') }} diff --git a/.github/workflows/notes.md b/.github/workflows/notes.md index 0d62215fe4..8f05c39d2a 100644 --- a/.github/workflows/notes.md +++ b/.github/workflows/notes.md @@ -10,13 +10,13 @@ ${NVIM_VERSION} 1. Download **nvim-win64.zip** 2. Extract the zip -3. Run `nvim-qt.exe` +3. Run `nvim.exe` on your CLI of choice #### MSI 1. Download **nvim-win64.msi** 2. Run the MSI -3. Search and run `nvim-qt.exe` or run `nvim.exe` on your CLI of choice +3. Run `nvim.exe` on your CLI of choice ### macOS diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 14546cb371..bdec96babb 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -48,7 +48,7 @@ jobs: cmake --build .deps - if: success() || failure() && steps.abort_job.outputs.status == 'success' - run: cmake -B build -G Ninja + run: cmake -B build -G Ninja -D CI_LINT=ON - if: "!cancelled()" name: Determine if run should be aborted diff --git a/CMakeLists.txt b/CMakeLists.txt index 9b80f81bc8..d0534731b0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -229,11 +229,14 @@ endif() # # Lint # -find_program(SHELLCHECK_PRG shellcheck) -find_program(STYLUA_PRG stylua) +option(CI_LINT "Abort if lint programs not found" OFF) +if(CI_LINT) + set(LINT_REQUIRED "REQUIRED") +endif() +find_program(SHELLCHECK_PRG shellcheck ${LINT_REQUIRED}) +find_program(STYLUA_PRG stylua ${LINT_REQUIRED}) add_glob_target( - REQUIRED TARGET lintlua-luacheck COMMAND ${DEPS_BIN_DIR}/luacheck FLAGS -q @@ -289,21 +292,10 @@ install_helper( FILES ${CMAKE_SOURCE_DIR}/src/man/nvim.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1) -if(EXISTS "${DEPS_PREFIX}/share/nvim-qt") - option(USE_BUNDLED_NVIMQT "Bundle neovim-qt" ON) -else() - option(USE_BUNDLED_NVIMQT "Bundle neovim-qt" OFF) -endif() - add_subdirectory(src/nvim) add_subdirectory(cmake.config) add_subdirectory(runtime) add_subdirectory(test) -if(WIN32 AND USE_BUNDLED_NVIMQT) - install_helper( - FILES ${DEPS_PREFIX}/share/nvim-qt/runtime/plugin/nvim_gui_shim.vim - DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/nvim-qt/runtime/plugin) -endif() add_custom_target(uninstall COMMAND ${CMAKE_COMMAND} -P ${PROJECT_SOURCE_DIR}/cmake/UninstallHelper.cmake) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4c8387fbd2..91badd4c58 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -33,7 +33,7 @@ Reporting problems Developer guidelines -------------------- -- Read [:help dev](https://neovim.io/doc/user/develop.html#dev) if you are working on Nvim core. +- Read [:help dev](https://neovim.io/doc/user/develop.html#dev) and [:help dev-doc][dev-doc-guide] if you are working on Nvim core. - Read [:help dev-ui](https://neovim.io/doc/user/develop.html#dev-ui) if you are developing a UI. - Read [:help dev-api-client](https://neovim.io/doc/user/develop.html#dev-api-client) if you are developing an API client. - Install `ninja` for faster builds of Nvim. @@ -264,40 +264,52 @@ For managing includes in C files, use [include-what-you-use]. See [#549][549] for more details. +### Lua runtime files + +Most of the Lua core [`runtime/`](./runtime) modules are precompiled to +bytecode, so changes to those files won't get used unless you rebuild Nvim or +by passing `--luamod-dev` and `$VIMRUNTIME`. For example, try adding a function +to `runtime/lua/vim/_editor.lua` then: + + VIMRUNTIME=./runtime ./build/bin/nvim --luamod-dev + Documenting ----------- -Many parts of the `:help` documentation are autogenerated from C or Lua docstrings using the `./scripts/gen_vimdoc.py` script. -You can filter the regeneration based on the target (api, lua, or lsp), or the file you changed, that need a doc refresh using `./scripts/gen_vimdoc.py -t <target>`. +Read [:help dev-doc][dev-doc-guide] to understand the expected documentation style and conventions. -## Lua docstrings +### Generating :help -Lua documentation uses a subset of [EmmyLua] annotations. A rough outline of a function documentation is +Many `:help` docs are autogenerated from (C or Lua) docstrings by the `./scripts/gen_vimdoc.py` script. +For convenience you can filter the regeneration by target (api, lua, lsp) using the `-t` option, for example: -```lua ---- {Brief} ---- ---- {Long explanation} ---- ----@param arg1 type {description} ----@param arg2 type {description} -{...} ---- ----@return type {description} -``` + ./scripts/gen_vimdoc.py -t lua -If possible, always add type information (`table`, `string`, `number`, ...). Multiple valid types are separated by a bar (`string|table`). Indicate optional parameters via `type|nil`. +### Lua docstrings -If a function in your Lua module should not be documented (e.g. internal function or local function), you should set the doc comment to: - -``` ----@private -``` +Lua documentation uses a subset of [EmmyLua] annotations. See [:help dev-doc-lua][dev-doc-lua]. -Mark functions that are deprecated as -``` ----@deprecated -``` +- The template for function documentation is: + ```lua + --- {Brief} + --- + --- {Long explanation} + --- + ---@param arg1 type {description} + ---@param arg2 type {description} + --- ... + --- + ---@return type {description} + ``` +- If possible, add type information (`table`, `string`, `number`, ...). Multiple valid types are separated by a bar (`string|table`). Indicate optional parameters via `type|nil`. +- If a function in your Lua module should _not_ be documented (e.g. internal or local function), set the doc comment to: + ``` + ---@private + ``` +- Mark deprecated functions with: + ``` + ---@deprecated + ``` Reviewing --------- @@ -326,6 +338,8 @@ as context, use the `-W` argument as well. [Merge a Vim patch]: https://github.com/neovim/neovim/wiki/Merging-patches-from-upstream-Vim [complexity:low]: https://github.com/neovim/neovim/issues?q=is%3Aopen+is%3Aissue+label%3Acomplexity%3Alow [conventional_commits]: https://www.conventionalcommits.org +[dev-doc-guide]: https://neovim.io/doc/user/develop.html#dev-doc +[dev-doc-lua]: https://neovim.io/doc/user/develop.html#dev-lua-doc [EmmyLua]: https://github.com/sumneko/lua-language-server/wiki/Annotations [gcc-warnings]: https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html [gh]: https://cli.github.com/ diff --git a/MAINTAIN.md b/MAINTAIN.md index 2ba764851b..191ef70709 100644 --- a/MAINTAIN.md +++ b/MAINTAIN.md @@ -60,6 +60,32 @@ has a major bug: Neovim automation includes a [backport bot](https://github.com/zeebe-io/backport-action). Trigger the action by labeling a PR with `backport release-X.Y`. See `.github/workflows/backport.yml`. +Deprecating and removing features +--------------------------------- + +Neovim inherits many features and design decisions from Vim, not all of which +align with the goals of this project. It is sometimes desired or necessary to +remove existing features, or refactor parts of the code that would change +user's workflow. In these cases, a deprecation policy is needed to properly +inform users of the change. + +In general, when a feature is slated to be removed it should: + +1. Be marked deprecated in the _next_ release + - This includes a note in the release notes (include a "Deprecation Warning" + section just below "Breaking Changes") + - Lua features can use `vim.deprecate()` + - Features implemented in Vimscript or in C will need bespoke implementations + to communicate to users that the feature is deprecated +2. Be removed in a release following the release in which it was marked + deprecated + - Usually this will be the next release, but it may be a later release if a + longer deprecation cycle is desired + +Feature removals which may benefit from community input or further discussion +should also have a tracking issue (which should be linked to in the release +notes). + Third-party dependencies ------------------------ @@ -93,6 +119,8 @@ These dependencies are "vendored" (inlined), we must update the sources manually * `runtime/lua/vim/inspect.lua`: [inspect.lua](https://github.com/kikito/inspect.lua) * `src/nvim/tui/terminfo_defs.h`: terminfo definitions * Run `scripts/update_terminfo.sh` to update these definitions. +* `runtime/lua/vim/lsp/types/protocol.lua`: LSP specification + * Run `scripts/lsp_types.lua` to update. * `src/bit.c`: only for PUC lua: port of `require'bit'` from luajit https://bitop.luajit.org/ * [treesitter parsers](https://github.com/neovim/neovim/blob/fcc24e43e0b5f9d801a01ff2b8f78ce8c16dd551/cmake.deps/CMakeLists.txt#L197-L210) @@ -81,7 +81,7 @@ Project layout ├─ runtime/ plugins and docs ├─ src/nvim/ application source code (see src/nvim/README.md) │ ├─ api/ API subsystem - │ ├─ eval/ VimL subsystem + │ ├─ eval/ Vimscript subsystem │ ├─ event/ event-loop subsystem │ ├─ generators/ code generation (pre-compilation) │ ├─ lib/ generic data structures diff --git a/cmake.deps/CMakeLists.txt b/cmake.deps/CMakeLists.txt index 86d2228e68..f76957d37d 100644 --- a/cmake.deps/CMakeLists.txt +++ b/cmake.deps/CMakeLists.txt @@ -54,10 +54,6 @@ else() option(USE_BUNDLED_LIBICONV "Use the bundled version of libiconv." OFF) endif() -if(WIN32) - option(USE_BUNDLED_NVIMQT "Bundle neovim-qt" ON) -endif() - option(USE_EXISTING_SRC_DIR "Skip download of deps sources in case of existing source directory." OFF) find_package(Git) @@ -158,12 +154,6 @@ if(WIN32) GetExecutable(TARGET tee) GetExecutable(TARGET xxd) - if(USE_BUNDLED_NVIMQT) - GetBinaryDep(TARGET wingui - INSTALL_COMMAND ${CMAKE_COMMAND} -E copy_directory bin ${DEPS_BIN_DIR} - COMMAND ${CMAKE_COMMAND} -E copy_directory share ${DEPS_INSTALL_DIR}/share) - endif() - GetBinaryDep(TARGET win32yank_X86_64 INSTALL_COMMAND ${CMAKE_COMMAND} -E copy win32yank.exe ${DEPS_BIN_DIR}) endif() diff --git a/cmake.deps/cmake/BuildTreesitterParsers.cmake b/cmake.deps/cmake/BuildTreesitterParsers.cmake index 56c8a5a7c6..e54544740e 100644 --- a/cmake.deps/cmake/BuildTreesitterParsers.cmake +++ b/cmake.deps/cmake/BuildTreesitterParsers.cmake @@ -34,6 +34,7 @@ function(BuildTSParser) CMAKE_CACHE_ARGS ${DEPS_CMAKE_CACHE_ARGS}) endfunction() -foreach(lang c lua vim vimdoc query) +foreach(lang c lua vim vimdoc query python bash) BuildTSParser(LANG ${lang}) endforeach() +BuildTSParser(LANG markdown CMAKE_FILE MarkdownParserCMakeLists.txt) diff --git a/cmake.deps/cmake/MarkdownParserCMakeLists.txt b/cmake.deps/cmake/MarkdownParserCMakeLists.txt new file mode 100644 index 0000000000..a356aaa391 --- /dev/null +++ b/cmake.deps/cmake/MarkdownParserCMakeLists.txt @@ -0,0 +1,28 @@ +cmake_minimum_required(VERSION 3.10) +project(${PARSERLANG} C) + +set(CMAKE_C_STANDARD 99) + +add_library(markdown MODULE +tree-sitter-markdown/src/parser.c +tree-sitter-markdown/src/scanner.c) +target_include_directories(markdown + PRIVATE + tree-sitter-markdown/src) + +add_library(markdown_inline MODULE +tree-sitter-markdown-inline/src/parser.c +tree-sitter-markdown-inline/src/scanner.c) +target_include_directories(markdown_inline + PRIVATE + tree-sitter-markdown-inline/src) + +set_target_properties( + markdown markdown_inline + PROPERTIES + PREFIX "" +) + +install(TARGETS markdown markdown_inline LIBRARY DESTINATION lib/nvim/parser) + +# vim: set ft=cmake: diff --git a/cmake.deps/deps.txt b/cmake.deps/deps.txt index 80526dc84a..57032b340e 100644 --- a/cmake.deps/deps.txt +++ b/cmake.deps/deps.txt @@ -1,5 +1,5 @@ -LIBUV_URL https://github.com/libuv/libuv/archive/v1.45.0.tar.gz -LIBUV_SHA256 458e34d5ef7f3c0394a2bfd8c39d757cb1553baa5959b9b4b45df63aa027a228 +LIBUV_URL https://github.com/libuv/libuv/archive/v1.46.0.tar.gz +LIBUV_SHA256 7aa66be3413ae10605e1f5c9ae934504ffe317ef68ea16fdaa83e23905c681bd MSGPACK_URL https://github.com/msgpack/msgpack-c/releases/download/c-6.0.0/msgpack-c-6.0.0.tar.gz MSGPACK_SHA256 3654f5e2c652dc52e0a993e270bb57d5702b262703f03771c152bba51602aeba @@ -19,8 +19,8 @@ LIBTERMKEY_SHA256 6945bd3c4aaa83da83d80a045c5563da4edd7d0374c62c0d35aec09eb30146 LIBVTERM_URL https://github.com/neovim/deps/raw/6c0a5213d062f2210fd5fecb9524ac27ca018ef4/opt/libvterm-0.3.2.tar.gz LIBVTERM_SHA256 91eb5088069f4e6edab69e14c4212f6da0192e65695956dc048016a0dab8bcf6 -LUV_URL https://github.com/luvit/luv/archive/c1497c0ffd3e1400a137ac99a614159a685f716b.tar.gz -LUV_SHA256 fc8c8c777454b78e09c06bd177860da9b79804affc967b015ecccb75b3af6893 +LUV_URL https://github.com/luvit/luv/archive/1.45.0-0.tar.gz +LUV_SHA256 97e89940f9eeaa8dfb34f1c19f80dd373299c42719d15228ec790f415d4e4965 LPEG_URL https://github.com/neovim/deps/raw/aa004f1b2b6470a92363cba8e1cc1874141dacc4/opt/lpeg-1.0.2.tar.gz LPEG_SHA256 48d66576051b6c78388faad09b70493093264588fcd0f258ddaab1cdd4a15ffe @@ -35,9 +35,6 @@ TEE_SHA256 950eea4e17fa3a7e89fa2c55374037b5797b3f1a54fea1304634884ab42ec14d XXD_URL https://github.com/neovim/deps/raw/21c5e8bdda33521a6ed497b315e03265a2785cbc/opt/xxd.exe XXD_SHA256 7a581e3882d28161cc52850f9a11d634b3eaf2c029276f093c1ed4c90e45a10c -WINGUI_URL https://github.com/equalsraf/neovim-qt/releases/download/v0.2.17/neovim-qt.zip -WINGUI_SHA256 502e386eef677c2c2e0c11d8cbb27f3e12b4d96818369417e8da4129c4580c25 - WIN32YANK_X86_64_URL https://github.com/equalsraf/win32yank/releases/download/v0.1.1/win32yank-x64.zip WIN32YANK_X86_64_SHA256 247c9a05b94387a884b49d3db13f806b1677dfc38020f955f719be6902260cd6 @@ -49,13 +46,19 @@ LIBICONV_SHA256 ccf536620a45458d26ba83887a983b96827001e92a13847b45e4925cc8913178 TREESITTER_C_URL https://github.com/tree-sitter/tree-sitter-c/archive/v0.20.2.tar.gz TREESITTER_C_SHA256 af66fde03feb0df4faf03750102a0d265b007e5d957057b6b293c13116a70af2 -TREESITTER_LUA_URL https://github.com/MunifTanjim/tree-sitter-lua/archive/v0.0.17.tar.gz -TREESITTER_LUA_SHA256 8963fd0a185d786c164dfca3824941c7eaec497ce49a3a0bc24bf753f5e0e59c +TREESITTER_LUA_URL https://github.com/MunifTanjim/tree-sitter-lua/archive/v0.0.18.tar.gz +TREESITTER_LUA_SHA256 659beef871a7fa1d9a02c23f5ebf55019aa3adce6d7f5441947781e128845256 TREESITTER_VIM_URL https://github.com/neovim/tree-sitter-vim/archive/v0.3.0.tar.gz TREESITTER_VIM_SHA256 403acec3efb7cdb18ff3d68640fc823502a4ffcdfbb71cec3f98aa786c21cbe2 -TREESITTER_VIMDOC_URL https://github.com/neovim/tree-sitter-vimdoc/archive/v2.0.0.tar.gz -TREESITTER_VIMDOC_SHA256 1ff8f4afd3a9599dd4c3ce87c155660b078c1229704d1a254433e33794b8f274 +TREESITTER_VIMDOC_URL https://github.com/neovim/tree-sitter-vimdoc/archive/v2.0.1.tar.gz +TREESITTER_VIMDOC_SHA256 61e165df29778dc0c9277c2a7bc67447cc4e1bed36ca916a2f476dd25ce3260e TREESITTER_QUERY_URL https://github.com/nvim-treesitter/tree-sitter-query/archive/v0.1.0.tar.gz TREESITTER_QUERY_SHA256 e2b806f80e8bf1c4f4e5a96248393fe6622fc1fc6189d6896d269658f67f914c +TREESITTER_PYTHON_URL https://github.com/tree-sitter/tree-sitter-python/archive/36f9e33d52b7572536ac1a8af8d7e78363ad52c3.tar.gz +TREESITTER_PYTHON_SHA256 ac92b4759c363fe284a1bfd9df2d204b5efa166042a1484eefdf8e03b3f37d57 +TREESITTER_BASH_URL https://github.com/tree-sitter/tree-sitter-bash/archive/493646764e7ad61ce63ce3b8c59ebeb37f71b841.tar.gz +TREESITTER_BASH_SHA256 99ebe9f2886efecc1a5e9e1360d804a1b49ad89976a66bb5c3871539cca5fb7e +TREESITTER_MARKDOWN_URL https://github.com/MDeiml/tree-sitter-markdown/archive/936cc84289f6de83c263ae8e659fb342867ceb16.tar.gz +TREESITTER_MARKDOWN_SHA256 4f2315930dc2c1bd42971a0b728cf4dafc57830c61f8abe3e2548cf230968713 TREESITTER_URL https://github.com/tree-sitter/tree-sitter/archive/91e4d940169a0c0b024560632ef53c4f119117ca.tar.gz TREESITTER_SHA256 e15e335d127d38aaa73e727f3169df6015f43de1010d806e69b9e9222ad50fe1 diff --git a/cmake.packaging/CMakeLists.txt b/cmake.packaging/CMakeLists.txt index cf7196fd6e..fa815da7f9 100644 --- a/cmake.packaging/CMakeLists.txt +++ b/cmake.packaging/CMakeLists.txt @@ -37,8 +37,7 @@ if(WIN32) # Create start menu and desktop shortcuts set(CPACK_WIX_PROGRAM_MENU_FOLDER "${CPACK_PACKAGE_NAME}") - set(CPACK_PACKAGE_EXECUTABLES "nvim" "Neovim" "nvim-qt" "Neovim Qt") - set(CPACK_CREATE_DESKTOP_LINKS "nvim-qt") + set(CPACK_PACKAGE_EXECUTABLES "nvim" "Neovim") # We use a wix patch to add further options to the installer. # See: https://cmake.org/cmake/help/v3.7/module/CPackWIX.html#variable:CPACK_WIX_PATCH_FILE diff --git a/cmake/GenerateVersion.cmake b/cmake/GenerateVersion.cmake index ab046e93ba..0758dad8ad 100644 --- a/cmake/GenerateVersion.cmake +++ b/cmake/GenerateVersion.cmake @@ -14,6 +14,9 @@ if(RES) return() endif() +# Extract build info: "v0.9.0-145-g0f9113907" => "g0f9113907" +string(REGEX REPLACE ".*\\-" "" NVIM_VERSION_BUILD "${GIT_TAG}") + # `git describe` annotates the most recent tagged release; for pre-release # builds we append that to the dev version. if(NVIM_VERSION_PRERELEASE) @@ -24,7 +27,7 @@ if(NVIM_VERSION_PRERELEASE) set(NVIM_VERSION "${NVIM_VERSION}-${NVIM_VERSION_GIT}") endif() -set(NVIM_VERSION_STRING "#define NVIM_VERSION_MEDIUM \"${NVIM_VERSION}\"\n") +set(NVIM_VERSION_STRING "#define NVIM_VERSION_MEDIUM \"${NVIM_VERSION}\"\n#define NVIM_VERSION_BUILD \"${NVIM_VERSION_BUILD}\"\n") string(SHA1 CURRENT_VERSION_HASH "${NVIM_VERSION_STRING}") if(EXISTS ${OUTPUT}) diff --git a/cmake/RunTests.cmake b/cmake/RunTests.cmake index e1a0c8d6c3..5e83b72235 100644 --- a/cmake/RunTests.cmake +++ b/cmake/RunTests.cmake @@ -66,6 +66,8 @@ set(ENV{SYSTEM_NAME} ${CMAKE_HOST_SYSTEM_NAME}) # used by test/helpers.lua. set(ENV{DEPS_INSTALL_DIR} ${DEPS_INSTALL_DIR}) # used by test/busted_runner.lua execute_process( + # Note: because of "-ll" (low-level interpreter mode), some modules like + # _editor.lua are not loaded. COMMAND ${NVIM_PRG} -ll ${WORKING_DIR}/test/busted_runner.lua -v -o test.busted.outputHandlers.${BUSTED_OUTPUT_TYPE} --lazy --helper=${TEST_DIR}/${TEST_TYPE}/preload.lua --lpath=${BUILD_DIR}/?.lua diff --git a/cmake/Util.cmake b/cmake/Util.cmake index b70f33a302..0d6fa2a4ce 100644 --- a/cmake/Util.cmake +++ b/cmake/Util.cmake @@ -4,7 +4,6 @@ # depends on the value of TOUCH_STRATEGY. # # Options: -# REQUIRED - Abort if COMMAND doesn't exist. # # Single value arguments: # TARGET - Name of the target @@ -53,7 +52,7 @@ # files. function(add_glob_target) cmake_parse_arguments(ARG - "REQUIRED" + "" "TARGET;COMMAND;GLOB_PAT;TOUCH_STRATEGY" "FLAGS;FILES;GLOB_DIRS;EXCLUDE" ${ARGN} @@ -61,14 +60,8 @@ function(add_glob_target) if(NOT ARG_COMMAND) add_custom_target(${ARG_TARGET}) - if(ARG_REQUIRED) - add_custom_command(TARGET ${ARG_TARGET} - COMMAND ${CMAKE_COMMAND} -E echo "${ARG_TARGET}: ${ARG_COMMAND} not found" - COMMAND false) - else() - add_custom_command(TARGET ${ARG_TARGET} - COMMAND ${CMAKE_COMMAND} -E echo "${ARG_TARGET} SKIP: ${ARG_COMMAND} not found") - endif() + add_custom_command(TARGET ${ARG_TARGET} + COMMAND ${CMAKE_COMMAND} -E echo "${ARG_TARGET} SKIP: ${ARG_COMMAND} not found") return() endif() diff --git a/contrib/flake.lock b/contrib/flake.lock index 1144165ed1..554dc22bdb 100644 --- a/contrib/flake.lock +++ b/contrib/flake.lock @@ -1,12 +1,15 @@ { "nodes": { "flake-utils": { + "inputs": { + "systems": "systems" + }, "locked": { - "lastModified": 1667395993, - "narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=", + "lastModified": 1685518550, + "narHash": "sha256-o2d0KcvaXzTrPRIo0kOLV0/QXHhDQ5DTi+OxcjO8xqY=", "owner": "numtide", "repo": "flake-utils", - "rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f", + "rev": "a1720a10a6cfe8234c0e93907ffe81be440f4cef", "type": "github" }, "original": { @@ -17,11 +20,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1681465517, - "narHash": "sha256-EasJh15/jcJNAHtq2SGbiADRXteURAnQbj1NqBoKkzU=", + "lastModified": 1686226982, + "narHash": "sha256-nLuiPoeiVfqqzeq9rmXxpybh77VS37dsY/k8N2LoxVg=", "owner": "nixos", "repo": "nixpkgs", - "rev": "abe7316dd51a313ce528972b104f4f04f56eefc4", + "rev": "a64b73e07d4aa65cfcbda29ecf78eaf9e72e44bd", "type": "github" }, "original": { @@ -36,6 +39,21 @@ "flake-utils": "flake-utils", "nixpkgs": "nixpkgs" } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } } }, "root": "root", diff --git a/contrib/flake.nix b/contrib/flake.nix index 59d977b748..57e56d0fdb 100644 --- a/contrib/flake.nix +++ b/contrib/flake.nix @@ -7,13 +7,59 @@ }; outputs = { self, nixpkgs, flake-utils }: + let + inherit (builtins) + elemAt + foldl' + mapAttrs + match + readFile + ; + inherit (nixpkgs.lib) + const + flip + pipe + remove + splitString + toLower + ; + in { overlay = final: prev: { - neovim = final.neovim-unwrapped.overrideAttrs (oa: rec { + neovim = (final.neovim-unwrapped.override { + treesitter-parsers = pipe ../cmake.deps/deps.txt [ + readFile + (splitString "\n") + (map (match "TREESITTER_([A-Z_]+)_(URL|SHA256)[[:space:]]+([^[:space:]]+)[[:space:]]*")) + (remove null) + (flip foldl' { } + (acc: matches: + let + lang = toLower (elemAt matches 0); + type = toLower (elemAt matches 1); + value = elemAt matches 2; + in + acc // { + ${lang} = acc.${lang} or { } // { + ${type} = value; + }; + })) + (mapAttrs (const final.fetchurl)) + (self: self // { + markdown = final.stdenv.mkDerivation { + inherit (self.markdown) name; + src = self.markdown; + installPhase = '' + mv tree-sitter-markdown $out + ''; + }; + }) + ]; + }).overrideAttrs (oa: rec { version = self.shortRev or "dirty"; src = ../.; - preConfigure = '' + preConfigure = oa.preConfigure or "" + '' sed -i cmake.config/versiondef.h.in -e 's/@NVIM_VERSION_PRERELEASE@/-dev-${version}/' ''; nativeBuildInputs = oa.nativeBuildInputs ++ [ diff --git a/contrib/luarc.json b/contrib/luarc.json index ebad0581b9..31126e4215 100644 --- a/contrib/luarc.json +++ b/contrib/luarc.json @@ -13,6 +13,9 @@ "teardown", "finally", "lfs" + ], + "disable": [ + "luadoc-miss-see-name" ] }, "workspace": { diff --git a/runtime/autoload/msgpack.vim b/runtime/autoload/msgpack.vim index 7f98a5b230..18dcd1e6a6 100644 --- a/runtime/autoload/msgpack.vim +++ b/runtime/autoload/msgpack.vim @@ -101,8 +101,8 @@ function s:msgpack_init_python() abort " @return Formatted timestamp. " " @warning Without +python or +python3 this function does not work correctly. - " The VimL code contains “reference” implementation which does not - " really work because of precision loss. + " The Vimscript code contains “reference” implementation which does + " not really work because of precision loss. function s:msgpack_dict_strftime(format, timestamp) return msgpack#strftime(a:format, +msgpack#int_dict_to_str(a:timestamp)) endfunction @@ -541,8 +541,8 @@ let s:MSGPACK_SPECIAL_OBJECTS = { \} "" -" Convert msgpack object dumped by msgpack#string() to a VimL object suitable -" for msgpackdump(). +" Convert msgpack object dumped by msgpack#string() to a Vimscript object +" suitable for msgpackdump(). " " @param[in] s String to evaluate. " @param[in] special_objs Additional special objects, in the same format as diff --git a/runtime/autoload/netrw.vim b/runtime/autoload/netrw.vim index c9f66f7927..be170d8aec 100644 --- a/runtime/autoload/netrw.vim +++ b/runtime/autoload/netrw.vim @@ -211,10 +211,8 @@ let g:netrw_localmovecmdopt = "" " --------------------------------------------------------------------- " Default values for netrw's global protocol variables {{{2 -if (v:version > 802 || (v:version == 802 && has("patch486"))) && has("balloon_eval") && !exists("s:initbeval") && !exists("g:netrw_nobeval") && has("syntax") && exists("g:syntax_on") && has("mouse") - call s:NetrwInit("g:netrw_use_errorwindow",2) -else - call s:NetrwInit("g:netrw_use_errorwindow",1) +if !exists("g:netrw_use_errorwindow") + let g:netrw_use_errorwindow = 0 endif if !exists("g:netrw_dav_cmd") diff --git a/runtime/autoload/shada.vim b/runtime/autoload/shada.vim index 87acc515ee..ae718ad2e7 100644 --- a/runtime/autoload/shada.vim +++ b/runtime/autoload/shada.vim @@ -487,7 +487,7 @@ let s:SHADA_ENTRY_OBJECT_SEQUENCE = ['type', 'timestamp', 'length', 'data'] "" " Convert list returned by msgpackparse() to a list of ShaDa objects " -" @param[in] mpack List of VimL objects returned by msgpackparse(). +" @param[in] mpack List of Vimscript objects returned by msgpackparse(). " " @return List of dictionaries with keys type, timestamp, length and data. Each " dictionary describes one ShaDa entry. diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt index 83422c9501..87ea000047 100644 --- a/runtime/doc/api.txt +++ b/runtime/doc/api.txt @@ -7,7 +7,7 @@ Nvim API *API* *api* Nvim exposes a powerful API that can be used by plugins and external processes -via |RPC|, |Lua| and VimL (|eval-api|). +via |RPC|, |Lua| and Vimscript (|eval-api|). Applications can also embed libnvim to work with the C API directly. @@ -17,8 +17,15 @@ Applications can also embed libnvim to work with the C API directly. API Usage *api-rpc* *RPC* *rpc* *msgpack-rpc* -RPC is the typical way to control Nvim programmatically. Nvim implements the -MessagePack-RPC protocol: +RPC is the main way to control Nvim programmatically. Nvim implements the +MessagePack-RPC protocol with these extra (out-of-spec) constraints: + +1. Responses must be given in reverse order of requests (like "unwinding + a stack"). +2. Nvim processes all messages (requests and notifications) in the order they + are received. + +MessagePack-RPC specification: https://github.com/msgpack-rpc/msgpack-rpc/blob/master/spec.md https://github.com/msgpack/msgpack/blob/0b8f5ac/spec.md @@ -202,7 +209,7 @@ any of these approaches: nvim --api-info | python -c 'import msgpack, sys, yaml; yaml.dump(msgpack.unpackb(sys.stdin.buffer.read()), sys.stdout)' < 3. Use the |api_info()| Vimscript function. >vim - :lua print(vim.inspect(vim.fn.api_info())) + :lua vim.print(vim.fn.api_info()) < Example using |filter()| to exclude non-deprecated API functions: >vim :new|put =map(filter(api_info().functions, '!has_key(v:val,''deprecated_since'')'), 'v:val.name') @@ -273,7 +280,7 @@ nvim_buf_lines_event[{buf}, {changedtick}, {firstline}, {lastline}, {linedata}, changed but not the buffer contents. {linedata} contains the changed screen lines. This happens when 'inccommand' shows a buffer preview. - Properties:~ + Properties: ~ {buf} API buffer handle (buffer number) {changedtick} value of |b:changedtick| for the buffer. If you send an @@ -306,7 +313,7 @@ nvim_buf_changedtick_event[{buf}, {changedtick}] *nvim_buf_changedtick_event* When |b:changedtick| was incremented but no text was changed. Relevant for undo/redo. - Properties:~ + Properties: ~ {buf} API buffer handle (buffer number) {changedtick} new value of |b:changedtick| for the buffer @@ -319,7 +326,7 @@ nvim_buf_detach_event[{buf}] *nvim_buf_detach_event* |:checktime| or 'autoread'. - Generally: whenever the buffer contents are unloaded from memory. - Properties:~ + Properties: ~ {buf} API buffer handle (buffer number) @@ -580,7 +587,7 @@ nvim__get_runtime({pat}, {all}, {*opts}) *nvim__get_runtime()* Parameters: ~ • {pat} pattern of files to search for • {all} whether to return all matches or only the first - • {opts} is_lua: only search lua subdirs + • {opts} is_lua: only search Lua subdirs Return: ~ list of absolute paths to the found files @@ -1133,7 +1140,7 @@ nvim_list_chans() *nvim_list_chans()* specified at |nvim_get_chan_info()|. nvim_list_runtime_paths() *nvim_list_runtime_paths()* - Gets the paths contained in 'runtimepath'. + Gets the paths contained in |runtime-search-path|. Return: ~ List of paths @@ -1196,7 +1203,7 @@ nvim_open_term({buffer}, {opts}) *nvim_open_term()* Parameters: ~ • {buffer} the buffer to use (expected to be empty) • {opts} Optional parameters. - • on_input: lua callback for input sent, i e keypresses in + • on_input: Lua callback for input sent, i e keypresses in terminal mode. Note: keypresses are sent raw as they would be to the pty master end. For instance, a carriage return is sent as a "\r", not as a "\n". |textlock| applies. It @@ -1486,14 +1493,16 @@ nvim_set_keymap({mode}, {lhs}, {rhs}, {*opts}) *nvim_set_keymap()* Parameters: ~ • {mode} Mode short-name (map command prefix: "n", "i", "v", "x", …) or - "!" for |:map!|, or empty string for |:map|. + "!" for |:map!|, or empty string for |:map|. "ia", "ca" or + "!a" for abbreviation in Insert mode, Cmdline mode, or both, + respectively • {lhs} Left-hand-side |{lhs}| of the mapping. • {rhs} Right-hand-side |{rhs}| of the mapping. • {opts} Optional parameters map: Accepts all |:map-arguments| as keys except |<buffer>|, values are booleans (default false). Also: • "noremap" non-recursive mapping |:noremap| • "desc" human-readable description. - • "callback" Lua function called when the mapping is executed. + • "callback" Lua function called in place of {rhs}. • "replace_keycodes" (boolean) When "expr" is true, replace keycodes in the resulting string (see |nvim_replace_termcodes()|). Returning nil from the Lua @@ -1547,22 +1556,22 @@ Vimscript Functions *api-vimscript* *nvim_call_dict_function()* nvim_call_dict_function({dict}, {fn}, {args}) - Calls a VimL |Dictionary-function| with the given arguments. + Calls a Vimscript |Dictionary-function| with the given arguments. - On execution error: fails with VimL error, updates v:errmsg. + On execution error: fails with Vimscript error, updates v:errmsg. Parameters: ~ - • {dict} Dictionary, or String evaluating to a VimL |self| dict - • {fn} Name of the function defined on the VimL dict + • {dict} Dictionary, or String evaluating to a Vimscript |self| dict + • {fn} Name of the function defined on the Vimscript dict • {args} Function arguments packed in an Array Return: ~ Result of the function call nvim_call_function({fn}, {args}) *nvim_call_function()* - Calls a VimL function with the given arguments. + Calls a Vimscript function with the given arguments. - On execution error: fails with VimL error, updates v:errmsg. + On execution error: fails with Vimscript error, updates v:errmsg. Parameters: ~ • {fn} Function to call @@ -1574,7 +1583,7 @@ nvim_call_function({fn}, {args}) *nvim_call_function()* nvim_command({command}) *nvim_command()* Executes an Ex command. - On execution error: fails with VimL error, updates v:errmsg. + On execution error: fails with Vimscript error, updates v:errmsg. Prefer using |nvim_cmd()| or |nvim_exec2()| over this. To evaluate multiple lines of Vim script or an Ex command directly, use @@ -1586,13 +1595,13 @@ nvim_command({command}) *nvim_command()* • {command} Ex command string nvim_eval({expr}) *nvim_eval()* - Evaluates a VimL |expression|. Dictionaries and Lists are recursively + Evaluates a Vimscript |expression|. Dictionaries and Lists are recursively expanded. - On execution error: fails with VimL error, updates v:errmsg. + On execution error: fails with Vimscript error, updates v:errmsg. Parameters: ~ - • {expr} VimL expression string + • {expr} Vimscript expression string Return: ~ Evaluation result or expanded object @@ -1604,7 +1613,7 @@ nvim_exec2({src}, {*opts}) *nvim_exec2()* Unlike |nvim_command()| this function supports heredocs, script-scope (s:), etc. - On execution error: fails with VimL error, updates v:errmsg. + On execution error: fails with Vimscript error, updates v:errmsg. Parameters: ~ • {src} Vimscript code @@ -1623,7 +1632,7 @@ nvim_exec2({src}, {*opts}) *nvim_exec2()* *nvim_parse_expression()* nvim_parse_expression({expr}, {flags}, {highlight}) - Parse a VimL expression. + Parse a Vimscript expression. Attributes: ~ |api-fast| @@ -1751,7 +1760,7 @@ nvim_cmd({*cmd}, {*opts}) *nvim_cmd()* example, instead of `vim.cmd.bdelete{ count = 2 }`, you may do `vim.cmd.bdelete(2)`. - On execution error: fails with VimL error, updates v:errmsg. + On execution error: fails with Vimscript error, updates v:errmsg. Parameters: ~ • {cmd} Command to execute. Must be a Dictionary that can contain the @@ -2006,7 +2015,7 @@ Buffer Functions *api-buffer* For more information on buffers, see |buffers|. -Unloaded Buffers:~ +Unloaded Buffers: ~ Buffers may be unloaded by the |:bunload| command or the buffer's |'bufhidden'| option. When a buffer is unloaded its file contents are @@ -2021,7 +2030,7 @@ whether a buffer is loaded. nvim_buf_attach({buffer}, {send_buffer}, {opts}) *nvim_buf_attach()* Activates buffer-update events on a channel, or as Lua callbacks. - Example (Lua): capture buffer updates in a global `events` variable (use "print(vim.inspect(events))" to see its contents): >lua + Example (Lua): capture buffer updates in a global `events` variable (use "vim.print(events)" to see its contents): >lua events = {} vim.api.nvim_buf_attach(0, false, { on_lines=function(...) table.insert(events, {...}) end}) @@ -2046,7 +2055,7 @@ nvim_buf_attach({buffer}, {send_buffer}, {opts}) *nvim_buf_attach()* • deleted_codepoints (if `utf_sizes` is true) • deleted_codeunits (if `utf_sizes` is true) - • on_bytes: lua callback invoked on change. This + • on_bytes: Lua callback invoked on change. This callback receives more granular information about the change compared to on_lines. Return `true` to detach. Args: • the string "bytes" @@ -2109,12 +2118,12 @@ nvim_buf_call({buffer}, {fun}) *nvim_buf_call()* Parameters: ~ • {buffer} Buffer handle, or 0 for current buffer - • {fun} Function to call inside the buffer (currently lua callable + • {fun} Function to call inside the buffer (currently Lua callable only) Return: ~ - Return value of function. NB: will deepcopy lua values currently, use - upvalues to send lua references in and out. + Return value of function. NB: will deepcopy Lua values currently, use + upvalues to send Lua references in and out. nvim_buf_del_keymap({buffer}, {mode}, {lhs}) *nvim_buf_del_keymap()* Unmaps a buffer-local |mapping| for the given mode. @@ -2545,7 +2554,7 @@ nvim_buf_get_extmarks({buffer}, {ns_id}, {start}, {end}, {opts}) local ms = api.nvim_buf_get_extmarks(0, ns, {2,0}, {2,0}, {}) -- Get all marks in this buffer + namespace. local all = api.nvim_buf_get_extmarks(0, ns, 0, -1, {}) - print(vim.inspect(ms)) + vim.print(ms) < Parameters: ~ @@ -2621,9 +2630,10 @@ nvim_buf_set_extmark({buffer}, {ns_id}, {line}, {col}, {*opts}) highlights of the text. Currently only affects virt_text highlights, but might affect `hl_group` in later versions. • "replace": only show the virt_text color. This is the - default - • "combine": combine with background text color - • "blend": blend with background text color. + default. + • "combine": combine with background text color. + • "blend": blend with background text color. Not supported + for "inline" virt_text. • virt_lines : virtual lines to add next to this mark This should be an array over lines, where each line in turn is @@ -2711,7 +2721,7 @@ nvim_get_namespaces() *nvim_get_namespaces()* nvim_set_decoration_provider({ns_id}, {*opts}) Set or change decoration provider for a |namespace| - This is a very general purpose interface for having lua callbacks being + This is a very general purpose interface for having Lua callbacks being triggered during the redraw code. The expected usage is to set |extmarks| for the currently redrawn buffer. @@ -2767,12 +2777,12 @@ nvim_win_call({window}, {fun}) *nvim_win_call()* Parameters: ~ • {window} Window handle, or 0 for current window - • {fun} Function to call inside the window (currently lua callable + • {fun} Function to call inside the window (currently Lua callable only) Return: ~ - Return value of function. NB: will deepcopy lua values currently, use - upvalues to send lua references in and out. + Return value of function. NB: will deepcopy Lua values currently, use + upvalues to send Lua references in and out. See also: ~ • |win_execute()| @@ -2817,6 +2827,9 @@ nvim_win_get_cursor({window}) *nvim_win_get_cursor()* Return: ~ (row, col) tuple + See also: ~ + • |getcurpos()| + nvim_win_get_height({window}) *nvim_win_get_height()* Gets the window height diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt index 94a529930e..ffa7d6282a 100644 --- a/runtime/doc/autocmd.txt +++ b/runtime/doc/autocmd.txt @@ -16,7 +16,7 @@ For a basic explanation, see section |40.3| in the user manual. You can specify commands to be executed automatically when reading or writing a file, when entering or leaving a buffer or window, and when exiting Vim. For example, you can create an autocommand to set the 'cindent' option for -files matching *.c. You can also use autocommands to implement advanced +files matching `*.c`. You can also use autocommands to implement advanced features, such as editing compressed files (see |gzip-example|). The usual place to put autocommands is in your vimrc file. @@ -197,7 +197,7 @@ For READING FILES there are four kinds of events possible: Vim uses only one of these four kinds when reading a file. The "Pre" and "Post" events are both triggered, before and after reading the file. -Note that the autocommands for the *ReadPre events and all the Filter events +Note that the autocommands for the "*ReadPre" events and all the Filter events are not allowed to change the current buffer (you will get an error message if this happens). This is to prevent the file to be read into the wrong buffer. @@ -212,28 +212,27 @@ events. Nvim recognizes the following events. Names are case-insensitive. *BufAdd* -BufAdd Just after creating a new buffer which is - added to the buffer list, or adding a buffer - to the buffer list, a buffer in the buffer - list was renamed. - Not triggered for the initial buffers created - during startup. +BufAdd After adding a new buffer or existing unlisted + buffer to the buffer list (except during + startup, see |VimEnter|), or renaming a listed + buffer. Before |BufEnter|. - NOTE: Current buffer "%" may be different from - the buffer being created "<afile>". + NOTE: Current buffer "%" is not the target + buffer "<afile>", "<abuf>". |<buffer=abuf>| *BufDelete* BufDelete Before deleting a buffer from the buffer list. The BufUnload may be called first (if the buffer was loaded). Also used just before a buffer in the buffer list is renamed. - NOTE: Current buffer "%" may be different from - the buffer being deleted "<afile>" and "<abuf>". + NOTE: Current buffer "%" is not the target + buffer "<afile>", "<abuf>". |<buffer=abuf>| Do not change to another buffer. *BufEnter* -BufEnter After entering a buffer. Useful for setting - options for a file type. Also executed when - starting to edit a buffer. +BufEnter After entering (visiting, switching-to) a new + or existing buffer. Useful for setting + filetype options. Compare |BufNew| which + does not trigger for existing buffers. After |BufAdd|. After |BufReadPost|. *BufFilePost* @@ -248,8 +247,8 @@ BufHidden Before a buffer becomes hidden: when there are the buffer is not unloaded or deleted. Not used for ":qa" or ":q" when exiting Vim. - NOTE: current buffer "%" may be different from - the buffer being unloaded "<afile>". + NOTE: Current buffer "%" is not the target + buffer "<afile>", "<abuf>". |<buffer=abuf>| *BufLeave* BufLeave Before leaving to another buffer. Also when leaving or closing the current window and the @@ -260,12 +259,14 @@ BufLeave Before leaving to another buffer. Also when BufModifiedSet After the `'modified'` value of a buffer has been changed. *BufNew* -BufNew Just after creating a new buffer. Also used - just after a buffer has been renamed. When - the buffer is added to the buffer list BufAdd - will be triggered too. - NOTE: Current buffer "%" may be different from - the buffer being created "<afile>". +BufNew After creating a new buffer (except during + startup, see |VimEnter|) or renaming an + existing buffer. Unlike |BufEnter|, visiting + (switching to) an existing buffer will not + trigger this again. + NOTE: Current buffer "%" is not the target + buffer "<afile>", "<abuf>". |<buffer=abuf>| + See also |BufAdd|, |BufNewFile|. *BufNewFile* BufNewFile When starting to edit a file that doesn't exist. Can be used to read in a skeleton @@ -298,8 +299,8 @@ BufUnload Before unloading a buffer, when the text in Before BufDelete. Triggers for all loaded buffers when Vim is going to exit. - NOTE: Current buffer "%" may be different from - the buffer being unloaded "<afile>". + NOTE: Current buffer "%" is not the target + buffer "<afile>", "<abuf>". |<buffer=abuf>| Do not switch buffers or windows! Not triggered when exiting and v:dying is 2 or more. @@ -319,8 +320,8 @@ BufWinLeave Before a buffer is removed from a window. Not when it's still visible in another window. Also triggered when exiting. Before BufUnload, BufHidden. - NOTE: Current buffer "%" may be different from - the buffer being unloaded "<afile>". + NOTE: Current buffer "%" is not the target + buffer "<afile>", "<abuf>". |<buffer=abuf>| Not triggered when exiting and v:dying is 2 or more. *BufWipeout* @@ -330,8 +331,8 @@ BufWipeout Before completely deleting a buffer. The buffer list). Also used just before a buffer is renamed (also when it's not in the buffer list). - NOTE: Current buffer "%" may be different from - the buffer being deleted "<afile>". + NOTE: Current buffer "%" is not the target + buffer "<afile>", "<abuf>". |<buffer=abuf>| Do not change to another buffer. *BufWrite* *BufWritePre* BufWrite or BufWritePre Before writing the whole buffer to a file. @@ -597,8 +598,8 @@ FileChangedShell When Vim notices that the modification time of prompt is not given. |v:fcs_reason| indicates what happened. Set |v:fcs_choice| to control what happens next. - NOTE: Current buffer "%" may be different from - the buffer that was changed "<afile>". + NOTE: Current buffer "%" is not the target + buffer "<afile>" and "<abuf>". |<buffer=abuf>| *E246* *E811* Cannot switch, jump to or delete buffers. Non-recursive (event cannot trigger itself). @@ -774,6 +775,9 @@ OptionSet After setting an option (except during the option. Similarly |v:option_oldglobal| is only set when |:set| or |:setglobal| was used. + This does not set |<abuf>|, you could use + |bufnr()|. + Note that when setting a |global-local| string option with |:set|, then |v:option_old| is the old global value. However, for all other kinds @@ -998,7 +1002,7 @@ TextChangedT After a change was made to the text in the *TextYankPost* TextYankPost Just after a |yank| or |deleting| command, but not if the black hole register |quote_| is used nor - for |setreg()|. Pattern must be *. + for |setreg()|. Pattern must be "*". Sets these |v:event| keys: inclusive operator @@ -1414,8 +1418,8 @@ When there is a matching "*Cmd" autocommand, it is assumed it will do the writing. No further writing is done and the other events are not triggered. |Cmd-event| -Note that the *WritePost commands should undo any changes to the buffer that -were caused by the *WritePre commands; otherwise, writing the file will have +Note that the "*WritePost" commands should undo any changes to the buffer that +were caused by the "*WritePre" commands; otherwise, writing the file will have the side effect of changing the buffer. Before executing the autocommands, the buffer from which the lines are to be @@ -1423,15 +1427,15 @@ written temporarily becomes the current buffer. Unless the autocommands change the current buffer or delete the previously current buffer, the previously current buffer is made the current buffer again. -The *WritePre and *AppendPre autocommands must not delete the buffer from +The "*WritePre" and "*AppendPre" autocommands must not delete the buffer from which the lines are to be written. The '[ and '] marks have a special position: -- Before the *ReadPre event the '[ mark is set to the line just above where +- Before the "*ReadPre" event the '[ mark is set to the line just above where the new lines will be inserted. -- Before the *ReadPost event the '[ mark is set to the first line that was +- Before the "*ReadPost" event the '[ mark is set to the first line that was just read, the '] mark to the last line. -- Before executing the *WriteCmd, *WritePre and *AppendPre autocommands the '[ +- Before executing the "*WriteCmd", "*WritePre" and "*AppendPre" autocommands the '[ mark is set to the first line that will be written, the '] mark to the last line. Careful: '[ and '] change when using commands that change the buffer. @@ -1539,7 +1543,7 @@ To read a skeleton (template) file when opening a new file: > :autocmd BufNewFile *.h 0r ~/vim/skeleton.h :autocmd BufNewFile *.java 0r ~/vim/skeleton.java -To insert the current date and time in a *.html file when writing it: > +To insert the current date and time in a "*.html" file when writing it: > :autocmd BufWritePre,FileWritePre *.html ks|call LastMod()|'s :fun LastMod() diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt index 27d52b7ac6..150022d62a 100644 --- a/runtime/doc/builtin.txt +++ b/runtime/doc/builtin.txt @@ -656,7 +656,7 @@ api_info() *api_info()* Returns Dictionary of |api-metadata|. View it in a nice human-readable format: > - :lua print(vim.inspect(vim.fn.api_info())) + :lua vim.print(vim.fn.api_info()) append({lnum}, {text}) *append()* When {text} is a |List|: Append each item of the |List| as a @@ -1167,11 +1167,13 @@ charidx({string}, {idx} [, {countcc} [, {utf16}]]) When {utf16} is present and TRUE, {idx} is used as the UTF-16 index in the String {expr} instead of as the byte index. - Returns -1 if the arguments are invalid or if {idx} is greater - than the index of the last byte in {string}. An error is - given if the first argument is not a string, the second - argument is not a number or when the third argument is present - and is not zero or one. + Returns -1 if the arguments are invalid or if there are less + than {idx} bytes. If there are exactly {idx} bytes the length + of the string in characters is returned. + + An error is given and -1 is returned if the first argument is + not a string, the second argument is not a number or when the + third argument is present and is not zero or one. See |byteidx()| and |byteidxcomp()| for getting the byte index from the character index and |utf16idx()| for getting the @@ -1231,7 +1233,7 @@ clearmatches([{win}]) *clearmatches()* Can also be used as a |method|: > GetWin()->clearmatches() < -col({expr} [, {winid}) *col()* +col({expr} [, {winid}]) *col()* The result is a Number, which is the byte index of the column position given with {expr}. The accepted positions are: . the cursor position @@ -1294,7 +1296,7 @@ complete({startcol}, {matches}) *complete()* *E785* Example: > inoremap <F5> <C-R>=ListMonths()<CR> - func! ListMonths() + func ListMonths() call complete(col('.'), ['January', 'February', 'March', \ 'April', 'May', 'June', 'July', 'August', 'September', \ 'October', 'November', 'December']) @@ -1538,7 +1540,7 @@ cursor({list}) This is like the return value of |getpos()| or |getcurpos()|, but without the first item. - To position the cursor using the character count, use + To position the cursor using {col} as the character count, use |setcursorcharpos()|. Does not change the jumplist. @@ -1937,7 +1939,7 @@ exists({expr}) The result is a Number, which is |TRUE| if {expr} is $ENVNAME environment variable (could also be done by comparing with an empty string) - *funcname built-in function (see |functions|) + `*funcname` built-in function (see |functions|) or user defined function (see |user-function|). Also works for a variable that is a Funcref. @@ -3017,7 +3019,7 @@ getcmdtype() *getcmdtype()* / forward search command ? backward search command @ |input()| command - - |:insert| or |:append| command + `-` |:insert| or |:append| command = |i_CTRL-R_=| Only works when editing the command line, thus requires use of |c_CTRL-\_e| or |c_CTRL-R_=| or an expression mapping. @@ -3362,20 +3364,26 @@ getmatches([{win}]) *getmatches()* If {win} is specified, use the window with this number or window ID instead of the current window. If {win} is invalid, an empty list is returned. - Example: > + Example: >vim :echo getmatches() -< [{"group": "MyGroup1", "pattern": "TODO", +> + [{"group": "MyGroup1", "pattern": "TODO", "priority": 10, "id": 1}, {"group": "MyGroup2", - "pattern": "FIXME", "priority": 10, "id": 2}] > + "pattern": "FIXME", "priority": 10, "id": 2}] +>vim :let m = getmatches() :call clearmatches() :echo getmatches() -< [] > +> + [] +>vim :call setmatches(m) :echo getmatches() -< [{"group": "MyGroup1", "pattern": "TODO", +> + [{"group": "MyGroup1", "pattern": "TODO", "priority": 10, "id": 1}, {"group": "MyGroup2", - "pattern": "FIXME", "priority": 10, "id": 2}] > + "pattern": "FIXME", "priority": 10, "id": 2}] +>vim :unlet m < getmousepos() *getmousepos()* @@ -3635,7 +3643,7 @@ getscriptinfo([{opts}]) *getscriptinfo()* name Vim script file name. sid Script ID |<SID>|. variables A dictionary with the script-local variables. - Present only when the a particular script is + Present only when a particular script is specified using the "sid" item in {opts}. Note that this is a copy, the value of script-local variables cannot be changed using @@ -4540,6 +4548,8 @@ jobresize({job}, {width}, {height}) *jobresize()* Fails if the job was not started with `"pty":v:true`. jobstart({cmd} [, {opts}]) *jobstart()* + Note: Prefer |vim.system()| in Lua. + Spawns {cmd} as a job. If {cmd} is a List it runs directly (no 'shell'). If {cmd} is a String it runs in the 'shell', like this: > @@ -4595,11 +4605,9 @@ jobstart({cmd} [, {opts}]) *jobstart()* stdout data. |on_stderr|: (function) Callback invoked when the job emits stderr data. - overlapped: (boolean) Set FILE_FLAG_OVERLAPPED for the - standard input/output passed to the child process. - Normally you do not need to set this. - (Only available on MS-Windows, On other - platforms, this option is silently ignored.) + overlapped: (boolean) Sets FILE_FLAG_OVERLAPPED for the + stdio passed to the child process. Only on + MS-Windows; ignored on other platforms. pty: (boolean) Connect the job to a new pseudo terminal, and its streams to the master file descriptor. `on_stdout` receives all output, @@ -5375,7 +5383,7 @@ matchfuzzy({list}, {str} [, {dict}]) *matchfuzzy()* < results in `['two one', 'one two']` . > :echo ['one two', 'two one']->matchfuzzy('two one', \ {'matchseq': 1}) -< results in ['two one']. +< results in `['two one']`. matchfuzzypos({list}, {str} [, {dict}]) *matchfuzzypos()* Same as |matchfuzzy()|, but returns the list of matched @@ -5397,7 +5405,7 @@ matchfuzzypos({list}, {str} [, {dict}]) *matchfuzzypos()* < results in [["lacy", "clay"], [[0, 1], [1, 2]], [153, 133]] > :echo [{'text': 'hello', 'id' : 10}] \ ->matchfuzzypos('ll', {'key' : 'text'}) -< results in [[{"id": 10, "text": "hello"}], [[2, 3]], [127]] +< results in `[[{"id": 10, "text": "hello"}], [[2, 3]], [127]]` matchlist({expr}, {pat} [, {start} [, {count}]]) *matchlist()* Same as |match()|, but return a |List|. The first item in the @@ -5624,7 +5632,7 @@ mkdir({name} [, {flags} [, {prot}]]) < and "subdir" already exists then "subdir/tmp" will be scheduled for deletion, like with: > defer delete('subdir/tmp', 'rf') - +< If {prot} is given it is used to set the protection bits of the new directory. The default is 0o755 (rwxr-xr-x: r/w for the user, readable for others). Use 0o700 to make it @@ -5701,7 +5709,7 @@ mode([expr]) Return a string that indicates the current mode. DoFull()->mode() msgpackdump({list} [, {type}]) *msgpackdump()* - Convert a list of VimL objects to msgpack. Returned value is a + Convert a list of Vimscript objects to msgpack. Returned value is a |readfile()|-style list. When {type} contains "B", a |Blob| is returned instead. Example: > call writefile(msgpackdump([{}]), 'fname.mpack', 'b') @@ -5721,7 +5729,7 @@ msgpackdump({list} [, {type}]) *msgpackdump()* msgpackparse({data}) *msgpackparse()* Convert a |readfile()|-style list or a |Blob| to a list of - VimL objects. + Vimscript objects. Example: > let fname = expand('~/.config/nvim/shada/main.shada') let mpack = readfile(fname, 'b') @@ -8266,7 +8274,7 @@ string({expr}) Return {expr} converted to a String. If {expr} is a Number, Funcref `function('name')` Blob 0z00112233.44556677.8899 List [item, item] - Dictionary {key: value, key: value} + Dictionary `{key: value, key: value}` Note that in String values the ' character is doubled. Also see |strtrans()|. Note 2: Output format is mostly compatible with YAML, except @@ -8663,6 +8671,8 @@ synstack({lnum}, {col}) *synstack()* valid positions. system({cmd} [, {input}]) *system()* *E677* + Note: Prefer |vim.system()| in Lua. + Gets the output of {cmd} as a |string| (|systemlist()| returns a |List|) and sets |v:shell_error| to the error code. {cmd} is treated as in |jobstart()|: @@ -8838,10 +8848,13 @@ termopen({cmd} [, {opts}]) *termopen()* to the current (unmodified) buffer. Parameters and behavior are the same as |jobstart()| except "pty", "width", "height", and "TERM" are ignored: "height" and "width" are taken from - the current window. - Returns the same values as |jobstart()|. + the current window. Note that termopen() implies a "pty" arg + to jobstart(), and thus has the implications documented at + |jobstart()|. + + Returns the same values as jobstart(). - Terminal environment is initialized as in ||jobstart-env|, + Terminal environment is initialized as in |jobstart-env|, except $TERM is set to "xterm-256color". Full behavior is described in |terminal|. @@ -9138,14 +9151,18 @@ uniq({list} [, {func} [, {dict}]]) *uniq()* *E882* < *utf16idx()* utf16idx({string}, {idx} [, {countcc} [, {charidx}]]) - Same as |charidx()| but returns the UTF-16 index of the byte - at {idx} in {string} (after converting it to UTF-16). + Same as |charidx()| but returns the UTF-16 code unit index of + the byte at {idx} in {string} (after converting it to UTF-16). When {charidx} is present and TRUE, {idx} is used as the character index in the String {string} instead of as the byte index. - An {idx} in the middle of a UTF-8 sequence is rounded upwards - to the end of that sequence. + An {idx} in the middle of a UTF-8 sequence is rounded + downwards to the beginning of that sequence. + + Returns -1 if the arguments are invalid or if there are less + than {idx} bytes in {string}. If there are exactly {idx} bytes + the length of the string in UTF-16 code units is returned. See |byteidx()| and |byteidxcomp()| for getting the byte index from the UTF-16 index and |charidx()| for getting the @@ -9490,11 +9507,11 @@ winlayout([{tabnr}]) *winlayout()* For a leaf window, it returns: ["leaf", {winid}] For horizontally split windows, which form a column, it - returns: + returns: > ["col", [{nested list of windows}]] - For vertically split windows, which form a row, it returns: +< For vertically split windows, which form a row, it returns: > ["row", [{nested list of windows}]] - +< Example: > " Only one window in the tab page :echo winlayout() diff --git a/runtime/doc/change.txt b/runtime/doc/change.txt index 96af05bc63..894557818b 100644 --- a/runtime/doc/change.txt +++ b/runtime/doc/change.txt @@ -1490,7 +1490,7 @@ white space). Three types of comments can be used: lines. An example is this list with dashes. - Three-piece comments that have a start string, an end string, and optional lines in between. The strings for the start, middle and end are different. - An example is the C style comment: + An example is the C style comment: > /* * this is a C comment */ @@ -1565,20 +1565,20 @@ When you hit Return in a C-comment, Vim will insert the middle comment leader for the new line: " * ". To close this comment you just have to type "/" before typing anything else on the new line. This will replace the middle-comment leader with the end-comment leader and apply any specified -alignment, leaving just " */". There is no need to hit Backspace first. +alignment, leaving just `" */"`. There is no need to hit Backspace first. When there is a match with a middle part, but there also is a matching end part which is longer, the end part is used. This makes a C style comment work without requiring the middle part to end with a space. Here is an example of alignment flags at work to make a comment stand out -(kind of looks like a 1 too). Consider comment string: > +(kind of looks like a 1 too). Consider comment string: >vim :set comments=sr:/***,m:**,ex-2:******/ -< +> /*** ~ **<--right aligned from "r" flag ~ ** ~ -offset 2 spaces for the "-2" flag--->** ~ + offset 2 spaces for the "-2" flag-->** ~ ******/ ~ In this case, the first comment was typed, then return was pressed 4 times, then "/" was pressed to end the comment. @@ -1741,10 +1741,10 @@ happens with formatting and auto-wrapping. Opening a line after a line starting with "/*" or "*" and containing "*/", will cause no comment leader to be inserted, and the indent of the new line is taken from the line containing the start of the comment. -E.g.: - /* ~ - * Your typical comment. ~ - */ ~ +E.g.: > + /* + * Your typical comment. + */ The indent on this line is the same as the start of the above comment. diff --git a/runtime/doc/channel.txt b/runtime/doc/channel.txt index 1c52b2d692..afc4ef3787 100644 --- a/runtime/doc/channel.txt +++ b/runtime/doc/channel.txt @@ -151,7 +151,7 @@ from the host TTY, or if Nvim is |--headless| it uses default values: >vim When channels are opened with the `rpc` option set to true, the channel can be used for remote method calls in both directions, see |msgpack-rpc|. Note that rpc channels are implicitly trusted and the process at the other end can -invoke any |api| function! +invoke any |API| function! ============================================================================== 4. Standard IO channel *channel-stdio* diff --git a/runtime/doc/cmdline.txt b/runtime/doc/cmdline.txt index c43d1caa0e..0213888537 100644 --- a/runtime/doc/cmdline.txt +++ b/runtime/doc/cmdline.txt @@ -100,7 +100,7 @@ CTRL-E or <End> *c_CTRL-E* *c_<End>* *c_End* *c_<MiddleMouse>* <MiddleMouse> Paste the contents of the clipboard (for X11 the primary - selection). This is similar to using CTRL-R *, but no CR + selection). This is similar to using `CTRL-R *`, but no CR characters are inserted between lines. CTRL-H *c_<BS>* *c_CTRL-H* *c_BS* @@ -190,8 +190,8 @@ CTRL-R CTRL-L *c_CTRL-R_CTRL-L* *c_<C-R>_<C-L>* *c_CTRL-R_CTRL-R* *c_<C-R>_<C-R>* *c_CTRL-R_CTRL-O* *c_<C-R>_<C-O>* -CTRL-R CTRL-R {register CTRL-F CTRL-P CTRL-W CTRL-A CTRL-L} -CTRL-R CTRL-O {register CTRL-F CTRL-P CTRL-W CTRL-A CTRL-L} +CTRL-R CTRL-R `{register CTRL-F CTRL-P CTRL-W CTRL-A CTRL-L}` +CTRL-R CTRL-O `{register CTRL-F CTRL-P CTRL-W CTRL-A CTRL-L}` Insert register or object under the cursor. Works like |c_CTRL-R| but inserts the text literally. For example, if register a contains "xy^Hz" (where ^H is a backspace), @@ -902,9 +902,10 @@ Note: these are typed literally, they are not special keys! write. *E495* *:<abuf>* *<abuf>* <abuf> When executing autocommands, is replaced with the currently - effective buffer number (for ":r file" and ":so file" it is - the current buffer, the file being read/sourced is not in a - buffer). *E496* + effective buffer number. It is not set for all events, + also see |bufnr()|. For ":r file" and ":so file" it is the + current buffer, the file being read/sourced is not in a + buffer. *E496* *:<amatch>* *<amatch>* <amatch> When executing autocommands, is replaced with the match for which this autocommand was executed. *E497* @@ -1057,7 +1058,7 @@ But expansion is only done if there are any wildcards before expanding the '%', '#', etc.. This avoids expanding wildcards inside a file name. If you want to expand the result of <cfile>, add a wildcard character to it. Examples: (alternate file name is "?readme?") - command expands to ~ + command expands to > :e # :e ?readme? :e `ls #` :e {files matching "?readme?"} :e #.* :e {files matching "?readme?.*"} @@ -1229,6 +1230,6 @@ The character used for the pattern indicates the type of command-line: ? backward search string = expression for "= |expr-register| @ string for |input()| - - text for |:insert| or |:append| + `-` text for |:insert| or |:append| vim:tw=78:ts=8:noet:ft=help:norl: diff --git a/runtime/doc/deprecated.txt b/runtime/doc/deprecated.txt index 6494c53059..6317789d22 100644 --- a/runtime/doc/deprecated.txt +++ b/runtime/doc/deprecated.txt @@ -117,19 +117,29 @@ internally and are no longer exposed as part of the API. Instead, use - *vim.lsp.diagnostic.set_virtual_text()* LSP FUNCTIONS -- *vim.lsp.buf.range_code_action()* Use |vim.lsp.buf.code_action()| with - the `range` parameter. -- *vim.lsp.util.diagnostics_to_items()* Use |vim.diagnostic.toqflist()| instead. -- *vim.lsp.util.set_qflist()* Use |setqflist()| instead. -- *vim.lsp.util.set_loclist()* Use |setloclist()| instead. -- *vim.lsp.buf_get_clients()* Use |vim.lsp.get_active_clients()| with - {buffer = bufnr} instead. -- *vim.lsp.buf.formatting()* Use |vim.lsp.buf.format()| with - {async = true} instead. -- *vim.lsp.buf.formatting_sync()* Use |vim.lsp.buf.format()| with - {async = false} instead. -- *vim.lsp.buf.range_formatting()* Use |vim.lsp.formatexpr()| - or |vim.lsp.buf.format()| instead. +- *vim.lsp.buf.server_ready()* + Use |LspAttach| instead, depending on your use-case. "Server ready" is not + part of the LSP spec, so the Nvim LSP client cannot meaningfully implement + it. "Ready" is ambiguous because: + - Language servers may finish analyzing the workspace, but edits can always + re-trigger analysis/builds. + - Language servers can serve some requests even while processing changes. +- *vim.lsp.buf.range_code_action()* Use |vim.lsp.buf.code_action()| with + the `range` parameter. +- *vim.lsp.util.diagnostics_to_items()* Use |vim.diagnostic.toqflist()| instead. +- *vim.lsp.util.set_qflist()* Use |setqflist()| instead. +- *vim.lsp.util.set_loclist()* Use |setloclist()| instead. +- *vim.lsp.buf_get_clients()* Use |vim.lsp.get_active_clients()| with + {buffer=bufnr} instead. +- *vim.lsp.buf.formatting()* Use |vim.lsp.buf.format()| with + {async=true} instead. +- *vim.lsp.buf.formatting_sync()* Use |vim.lsp.buf.format()| with + {async=false} instead. +- *vim.lsp.buf.range_formatting()* Use |vim.lsp.formatexpr()| + or |vim.lsp.buf.format()| instead. +- *vim.lsp.util.get_progress_messages()* Use |vim.lsp.status()| or access + `progress` of |vim.lsp.client| +- *vim.lsp.for_each_buffer_client()* Use |vim.lsp.get_active_clients()| TREESITTER FUNCTIONS - *vim.treesitter.language.require_language()* Use |vim.treesitter.language.add()| @@ -161,27 +171,21 @@ OPTIONS - 'viewoptions' Flags "unix", "slash" are ignored and always enabled. - *'viminfo'* Deprecated alias to 'shada' option. - *'viminfofile'* Deprecated alias to 'shadafile' option. -- *'paste'* *'nopaste'* This option is obsolete; |paste| is handled automatically. +- *'paste'* *'nopaste'* Just Paste It.™ The 'paste' option is obsolete: + |paste| is handled automatically when you paste text + using your terminal's or GUI's paste feature + (CTRL-SHIFT-v, CMD-v (macOS), middle-click, …). Enables "paste mode": - - mappings in Insert mode and Command-line mode are - disabled - - abbreviations are disabled - - 'autoindent' is reset - - 'expandtab' is reset - - 'formatoptions' is used like it is empty - - 'revins' is reset - - 'ruler' is reset - - 'showmatch' is reset - - 'smartindent' is reset - - 'smarttab' is reset - - 'softtabstop' is set to 0 - - 'textwidth' is set to 0 - - 'wrapmargin' is set to 0 - These options keep their value, but their effect is - disabled: - - 'cindent' - - 'indentexpr' - - 'lisp' + - Disables mappings in Insert, Cmdline mode. + - Disables abbreviations. + - Resets 'autoindent' 'expandtab' 'revins' 'ruler' + 'showmatch' 'smartindent' 'smarttab' 'softtabstop' + 'textwidth' 'wrapmargin'. + - Treats 'formatoptions' as empty. + - Disables the effect of these options: + - 'cindent' + - 'indentexpr' + - 'lisp' UI EXTENSIONS - *ui-wildmenu* Use |ui-cmdline| with |ui-popupmenu| instead. Enabled diff --git a/runtime/doc/develop.txt b/runtime/doc/develop.txt index b090fd4f09..db878ac304 100644 --- a/runtime/doc/develop.txt +++ b/runtime/doc/develop.txt @@ -99,8 +99,8 @@ Examples: scripting is performed by an external host process implemented in ~2k lines of Python. -The provider framework invokes VimL from C. It is composed of two functions -in eval.c: +The provider framework invokes Vimscript from C. It is composed of two +functions in eval.c: - eval_call_provider(name, method, arguments, discard): calls provider#{name}#Call with the method and arguments. If discard is true, any @@ -130,10 +130,14 @@ DOCUMENTATION *dev-doc* (docstrings, user manual, website materials, newsletters, …). Don't mince words. Personality and flavor, used sparingly, are welcome--but in general, optimize for the reader's time and energy: be "precise yet concise". - - See https://developers.google.com/style/tone - Prefer the active voice: "Foo does X", not "X is done by Foo". -- Start function docstrings with present tense ("Gets"), not imperative - ("Get"). This tends to reduce ambiguity and improve clarity. > + - "The words you choose are an essential part of the user experience." + https://developer.apple.com/design/human-interface-guidelines/foundations/writing/ + - "...without being overly colloquial or frivolous." + https://developers.google.com/style/tone +- Write docstrings (as opposed to inline comments) with present tense ("Gets"), + not imperative ("Get"). This tends to reduce ambiguity and improve clarity + by describing "What" instead of "How". > GOOD: /// Gets a highlight definition. BAD: @@ -272,6 +276,12 @@ See also |dev-naming|. - mimic the pairs() or ipairs() interface if the function is intended to be used in a "for" loop. +Interface conventions ~ + +- When accepting a buffer id, etc., 0 means "current buffer", nil means "all + buffers". Likewise for window id, tabpage id, etc. + - Examples: |vim.lsp.codelens.clear()| |vim.diagnostic.enable()| + API DESIGN GUIDELINES *dev-api* @@ -346,6 +356,7 @@ Use consistent names for {noun} (nouns) in API functions: buffer is called Do NOT use these deprecated nouns: - buffer + - callback Use on_foo instead - command - window @@ -418,7 +429,7 @@ Examples of API-client package names: - GOOD: nvim-racket - GOOD: pynvim - BAD: python-client -- BAD: neovim +- BAD: neovim_ API client implementation guidelines ~ @@ -467,6 +478,8 @@ External UIs are expected to implement these common features: - Consider the "option_set" |ui-global| event as a hint for other GUI behaviors. Various UI-related options ('guifont', 'ambiwidth', …) are published in this event. See also "mouse_on", "mouse_off". +- UIs generally should NOT set |$NVIM_APPNAME| (unless explicitly requested by + the user). vim:tw=78:ts=8:sw=4:et:ft=help:norl: diff --git a/runtime/doc/diagnostic.txt b/runtime/doc/diagnostic.txt index bfcc1c9092..9dd42061ef 100644 --- a/runtime/doc/diagnostic.txt +++ b/runtime/doc/diagnostic.txt @@ -478,7 +478,7 @@ get({bufnr}, {opts}) *vim.diagnostic.get()* • severity: See |diagnostic-severity|. Return: ~ - Diagnostic [] table A list of diagnostic items |diagnostic-structure|. + Diagnostic [] table A list of diagnostic items |diagnostic-structure|. Keys `bufnr` , `end_lnum` , `end_col` , and `severity` are guaranteed to be present. get_namespace({namespace}) *vim.diagnostic.get_namespace()* Get namespace metadata. diff --git a/runtime/doc/digraph.txt b/runtime/doc/digraph.txt index d2cc000789..b024894c96 100644 --- a/runtime/doc/digraph.txt +++ b/runtime/doc/digraph.txt @@ -169,1311 +169,1313 @@ The rouble sign was added in 2014 as 0x20bd. Vim supports the digraphs =R and =P for this. Note that R= and P= are other characters. *digraph-table* *digraph-table-mbyte* -char digraph hex dec official name ~ -^@ NU 0x00 0 NULL (NUL) -^A SH 0x01 1 START OF HEADING (SOH) -^B SX 0x02 2 START OF TEXT (STX) -^C EX 0x03 3 END OF TEXT (ETX) -^D ET 0x04 4 END OF TRANSMISSION (EOT) -^E EQ 0x05 5 ENQUIRY (ENQ) -^F AK 0x06 6 ACKNOWLEDGE (ACK) -^G BL 0x07 7 BELL (BEL) -^H BS 0x08 8 BACKSPACE (BS) -^I HT 0x09 9 CHARACTER TABULATION (HT) -^@ LF 0x0a 10 LINE FEED (LF) -^K VT 0x0b 11 LINE TABULATION (VT) -^L FF 0x0c 12 FORM FEED (FF) -^M CR 0x0d 13 CARRIAGE RETURN (CR) -^N SO 0x0e 14 SHIFT OUT (SO) -^O SI 0x0f 15 SHIFT IN (SI) -^P DL 0x10 16 DATALINK ESCAPE (DLE) -^Q D1 0x11 17 DEVICE CONTROL ONE (DC1) -^R D2 0x12 18 DEVICE CONTROL TWO (DC2) -^S D3 0x13 19 DEVICE CONTROL THREE (DC3) -^T D4 0x14 20 DEVICE CONTROL FOUR (DC4) -^U NK 0x15 21 NEGATIVE ACKNOWLEDGE (NAK) -^V SY 0x16 22 SYNCHRONOUS IDLE (SYN) -^W EB 0x17 23 END OF TRANSMISSION BLOCK (ETB) -^X CN 0x18 24 CANCEL (CAN) -^Y EM 0x19 25 END OF MEDIUM (EM) -^Z SB 0x1a 26 SUBSTITUTE (SUB) -^[ EC 0x1b 27 ESCAPE (ESC) -^\ FS 0x1c 28 FILE SEPARATOR (IS4) -^] GS 0x1d 29 GROUP SEPARATOR (IS3) -^^ RS 0x1e 30 RECORD SEPARATOR (IS2) -^_ US 0x1f 31 UNIT SEPARATOR (IS1) - SP 0x20 32 SPACE -# Nb 0x23 35 NUMBER SIGN -$ DO 0x24 36 DOLLAR SIGN -@ At 0x40 64 COMMERCIAL AT -[ <( 0x5b 91 LEFT SQUARE BRACKET -\ // 0x5c 92 REVERSE SOLIDUS -] )> 0x5d 93 RIGHT SQUARE BRACKET -^ '> 0x5e 94 CIRCUMFLEX ACCENT -` '! 0x60 96 GRAVE ACCENT -{ (! 0x7b 123 LEFT CURLY BRACKET -| !! 0x7c 124 VERTICAL LINE -} !) 0x7d 125 RIGHT CURLY BRACKET -~ '? 0x7e 126 TILDE -^? DT 0x7f 127 DELETE (DEL) -~@ PA 0x80 128 PADDING CHARACTER (PAD) -~A HO 0x81 129 HIGH OCTET PRESET (HOP) -~B BH 0x82 130 BREAK PERMITTED HERE (BPH) -~C NH 0x83 131 NO BREAK HERE (NBH) -~D IN 0x84 132 INDEX (IND) -~E NL 0x85 133 NEXT LINE (NEL) -~F SA 0x86 134 START OF SELECTED AREA (SSA) -~G ES 0x87 135 END OF SELECTED AREA (ESA) -~H HS 0x88 136 CHARACTER TABULATION SET (HTS) -~I HJ 0x89 137 CHARACTER TABULATION WITH JUSTIFICATION (HTJ) -~J VS 0x8a 138 LINE TABULATION SET (VTS) -~K PD 0x8b 139 PARTIAL LINE FORWARD (PLD) -~L PU 0x8c 140 PARTIAL LINE BACKWARD (PLU) -~M RI 0x8d 141 REVERSE LINE FEED (RI) -~N S2 0x8e 142 SINGLE-SHIFT TWO (SS2) -~O S3 0x8f 143 SINGLE-SHIFT THREE (SS3) -~P DC 0x90 144 DEVICE CONTROL STRING (DCS) -~Q P1 0x91 145 PRIVATE USE ONE (PU1) -~R P2 0x92 146 PRIVATE USE TWO (PU2) -~S TS 0x93 147 SET TRANSMIT STATE (STS) -~T CC 0x94 148 CANCEL CHARACTER (CCH) -~U MW 0x95 149 MESSAGE WAITING (MW) -~V SG 0x96 150 START OF GUARDED AREA (SPA) -~W EG 0x97 151 END OF GUARDED AREA (EPA) -~X SS 0x98 152 START OF STRING (SOS) -~Y GC 0x99 153 SINGLE GRAPHIC CHARACTER INTRODUCER (SGCI) -~Z SC 0x9a 154 SINGLE CHARACTER INTRODUCER (SCI) -~[ CI 0x9b 155 CONTROL SEQUENCE INTRODUCER (CSI) -~\ ST 0x9c 156 STRING TERMINATOR (ST) -~] OC 0x9d 157 OPERATING SYSTEM COMMAND (OSC) -~^ PM 0x9e 158 PRIVACY MESSAGE (PM) -~_ AC 0x9f 159 APPLICATION PROGRAM COMMAND (APC) -| NS 0xa0 160 NO-BREAK SPACE -¡ !I 0xa1 161 INVERTED EXCLAMATION MARK -¢ Ct 0xa2 162 CENT SIGN -£ Pd 0xa3 163 POUND SIGN -¤ Cu 0xa4 164 CURRENCY SIGN -¥ Ye 0xa5 165 YEN SIGN -¦ BB 0xa6 166 BROKEN BAR -§ SE 0xa7 167 SECTION SIGN -¨ ': 0xa8 168 DIAERESIS -© Co 0xa9 169 COPYRIGHT SIGN -ª -a 0xaa 170 FEMININE ORDINAL INDICATOR -« << 0xab 171 LEFT-POINTING DOUBLE ANGLE QUOTATION MARK -¬ NO 0xac 172 NOT SIGN - -- 0xad 173 SOFT HYPHEN -® Rg 0xae 174 REGISTERED SIGN -¯ 'm 0xaf 175 MACRON -° DG 0xb0 176 DEGREE SIGN -± +- 0xb1 177 PLUS-MINUS SIGN -² 2S 0xb2 178 SUPERSCRIPT TWO -³ 3S 0xb3 179 SUPERSCRIPT THREE -´ '' 0xb4 180 ACUTE ACCENT -µ My 0xb5 181 MICRO SIGN -¶ PI 0xb6 182 PILCROW SIGN -· .M 0xb7 183 MIDDLE DOT -¸ ', 0xb8 184 CEDILLA -¹ 1S 0xb9 185 SUPERSCRIPT ONE -º -o 0xba 186 MASCULINE ORDINAL INDICATOR -» >> 0xbb 187 RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK -¼ 14 0xbc 188 VULGAR FRACTION ONE QUARTER -½ 12 0xbd 189 VULGAR FRACTION ONE HALF -¾ 34 0xbe 190 VULGAR FRACTION THREE QUARTERS -¿ ?I 0xbf 191 INVERTED QUESTION MARK -À A! 0xc0 192 LATIN CAPITAL LETTER A WITH GRAVE -Á A' 0xc1 193 LATIN CAPITAL LETTER A WITH ACUTE - A> 0xc2 194 LATIN CAPITAL LETTER A WITH CIRCUMFLEX -à A? 0xc3 195 LATIN CAPITAL LETTER A WITH TILDE -Ä A: 0xc4 196 LATIN CAPITAL LETTER A WITH DIAERESIS -Å AA 0xc5 197 LATIN CAPITAL LETTER A WITH RING ABOVE -Æ AE 0xc6 198 LATIN CAPITAL LETTER AE -Ç C, 0xc7 199 LATIN CAPITAL LETTER C WITH CEDILLA -È E! 0xc8 200 LATIN CAPITAL LETTER E WITH GRAVE -É E' 0xc9 201 LATIN CAPITAL LETTER E WITH ACUTE -Ê E> 0xca 202 LATIN CAPITAL LETTER E WITH CIRCUMFLEX -Ë E: 0xcb 203 LATIN CAPITAL LETTER E WITH DIAERESIS -Ì I! 0xcc 204 LATIN CAPITAL LETTER I WITH GRAVE -Í I' 0xcd 205 LATIN CAPITAL LETTER I WITH ACUTE -Î I> 0xce 206 LATIN CAPITAL LETTER I WITH CIRCUMFLEX -Ï I: 0xcf 207 LATIN CAPITAL LETTER I WITH DIAERESIS -Ð D- 0xd0 208 LATIN CAPITAL LETTER ETH (Icelandic) -Ñ N? 0xd1 209 LATIN CAPITAL LETTER N WITH TILDE -Ò O! 0xd2 210 LATIN CAPITAL LETTER O WITH GRAVE -Ó O' 0xd3 211 LATIN CAPITAL LETTER O WITH ACUTE -Ô O> 0xd4 212 LATIN CAPITAL LETTER O WITH CIRCUMFLEX -Õ O? 0xd5 213 LATIN CAPITAL LETTER O WITH TILDE -Ö O: 0xd6 214 LATIN CAPITAL LETTER O WITH DIAERESIS -× *X 0xd7 215 MULTIPLICATION SIGN -Ø O/ 0xd8 216 LATIN CAPITAL LETTER O WITH STROKE -Ù U! 0xd9 217 LATIN CAPITAL LETTER U WITH GRAVE -Ú U' 0xda 218 LATIN CAPITAL LETTER U WITH ACUTE -Û U> 0xdb 219 LATIN CAPITAL LETTER U WITH CIRCUMFLEX -Ü U: 0xdc 220 LATIN CAPITAL LETTER U WITH DIAERESIS -Ý Y' 0xdd 221 LATIN CAPITAL LETTER Y WITH ACUTE -Þ TH 0xde 222 LATIN CAPITAL LETTER THORN (Icelandic) -ß ss 0xdf 223 LATIN SMALL LETTER SHARP S (German) -à a! 0xe0 224 LATIN SMALL LETTER A WITH GRAVE -á a' 0xe1 225 LATIN SMALL LETTER A WITH ACUTE -â a> 0xe2 226 LATIN SMALL LETTER A WITH CIRCUMFLEX -ã a? 0xe3 227 LATIN SMALL LETTER A WITH TILDE -ä a: 0xe4 228 LATIN SMALL LETTER A WITH DIAERESIS -å aa 0xe5 229 LATIN SMALL LETTER A WITH RING ABOVE -æ ae 0xe6 230 LATIN SMALL LETTER AE -ç c, 0xe7 231 LATIN SMALL LETTER C WITH CEDILLA -è e! 0xe8 232 LATIN SMALL LETTER E WITH GRAVE -é e' 0xe9 233 LATIN SMALL LETTER E WITH ACUTE -ê e> 0xea 234 LATIN SMALL LETTER E WITH CIRCUMFLEX -ë e: 0xeb 235 LATIN SMALL LETTER E WITH DIAERESIS -ì i! 0xec 236 LATIN SMALL LETTER I WITH GRAVE -í i' 0xed 237 LATIN SMALL LETTER I WITH ACUTE -î i> 0xee 238 LATIN SMALL LETTER I WITH CIRCUMFLEX -ï i: 0xef 239 LATIN SMALL LETTER I WITH DIAERESIS -ð d- 0xf0 240 LATIN SMALL LETTER ETH (Icelandic) -ñ n? 0xf1 241 LATIN SMALL LETTER N WITH TILDE -ò o! 0xf2 242 LATIN SMALL LETTER O WITH GRAVE -ó o' 0xf3 243 LATIN SMALL LETTER O WITH ACUTE -ô o> 0xf4 244 LATIN SMALL LETTER O WITH CIRCUMFLEX -õ o? 0xf5 245 LATIN SMALL LETTER O WITH TILDE -ö o: 0xf6 246 LATIN SMALL LETTER O WITH DIAERESIS -÷ -: 0xf7 247 DIVISION SIGN -ø o/ 0xf8 248 LATIN SMALL LETTER O WITH STROKE -ù u! 0xf9 249 LATIN SMALL LETTER U WITH GRAVE -ú u' 0xfa 250 LATIN SMALL LETTER U WITH ACUTE -û u> 0xfb 251 LATIN SMALL LETTER U WITH CIRCUMFLEX -ü u: 0xfc 252 LATIN SMALL LETTER U WITH DIAERESIS -ý y' 0xfd 253 LATIN SMALL LETTER Y WITH ACUTE -þ th 0xfe 254 LATIN SMALL LETTER THORN (Icelandic) -ÿ y: 0xff 255 LATIN SMALL LETTER Y WITH DIAERESIS -Ā A- 0100 0256 LATIN CAPITAL LETTER A WITH MACRON -ā a- 0101 0257 LATIN SMALL LETTER A WITH MACRON -Ă A( 0102 0258 LATIN CAPITAL LETTER A WITH BREVE -ă a( 0103 0259 LATIN SMALL LETTER A WITH BREVE -Ą A; 0104 0260 LATIN CAPITAL LETTER A WITH OGONEK -ą a; 0105 0261 LATIN SMALL LETTER A WITH OGONEK -Ć C' 0106 0262 LATIN CAPITAL LETTER C WITH ACUTE -ć c' 0107 0263 LATIN SMALL LETTER C WITH ACUTE -Ĉ C> 0108 0264 LATIN CAPITAL LETTER C WITH CIRCUMFLEX -ĉ c> 0109 0265 LATIN SMALL LETTER C WITH CIRCUMFLEX -Ċ C. 010A 0266 LATIN CAPITAL LETTER C WITH DOT ABOVE -ċ c. 010B 0267 LATIN SMALL LETTER C WITH DOT ABOVE -Č C< 010C 0268 LATIN CAPITAL LETTER C WITH CARON -č c< 010D 0269 LATIN SMALL LETTER C WITH CARON -Ď D< 010E 0270 LATIN CAPITAL LETTER D WITH CARON -ď d< 010F 0271 LATIN SMALL LETTER D WITH CARON -Đ D/ 0110 0272 LATIN CAPITAL LETTER D WITH STROKE -đ d/ 0111 0273 LATIN SMALL LETTER D WITH STROKE -Ē E- 0112 0274 LATIN CAPITAL LETTER E WITH MACRON -ē e- 0113 0275 LATIN SMALL LETTER E WITH MACRON -Ĕ E( 0114 0276 LATIN CAPITAL LETTER E WITH BREVE -ĕ e( 0115 0277 LATIN SMALL LETTER E WITH BREVE -Ė E. 0116 0278 LATIN CAPITAL LETTER E WITH DOT ABOVE -ė e. 0117 0279 LATIN SMALL LETTER E WITH DOT ABOVE -Ę E; 0118 0280 LATIN CAPITAL LETTER E WITH OGONEK -ę e; 0119 0281 LATIN SMALL LETTER E WITH OGONEK -Ě E< 011A 0282 LATIN CAPITAL LETTER E WITH CARON -ě e< 011B 0283 LATIN SMALL LETTER E WITH CARON -Ĝ G> 011C 0284 LATIN CAPITAL LETTER G WITH CIRCUMFLEX -ĝ g> 011D 0285 LATIN SMALL LETTER G WITH CIRCUMFLEX -Ğ G( 011E 0286 LATIN CAPITAL LETTER G WITH BREVE -ğ g( 011F 0287 LATIN SMALL LETTER G WITH BREVE -Ġ G. 0120 0288 LATIN CAPITAL LETTER G WITH DOT ABOVE -ġ g. 0121 0289 LATIN SMALL LETTER G WITH DOT ABOVE -Ģ G, 0122 0290 LATIN CAPITAL LETTER G WITH CEDILLA -ģ g, 0123 0291 LATIN SMALL LETTER G WITH CEDILLA -Ĥ H> 0124 0292 LATIN CAPITAL LETTER H WITH CIRCUMFLEX -ĥ h> 0125 0293 LATIN SMALL LETTER H WITH CIRCUMFLEX -Ħ H/ 0126 0294 LATIN CAPITAL LETTER H WITH STROKE -ħ h/ 0127 0295 LATIN SMALL LETTER H WITH STROKE -Ĩ I? 0128 0296 LATIN CAPITAL LETTER I WITH TILDE -ĩ i? 0129 0297 LATIN SMALL LETTER I WITH TILDE -Ī I- 012A 0298 LATIN CAPITAL LETTER I WITH MACRON -ī i- 012B 0299 LATIN SMALL LETTER I WITH MACRON -Ĭ I( 012C 0300 LATIN CAPITAL LETTER I WITH BREVE -ĭ i( 012D 0301 LATIN SMALL LETTER I WITH BREVE -Į I; 012E 0302 LATIN CAPITAL LETTER I WITH OGONEK -į i; 012F 0303 LATIN SMALL LETTER I WITH OGONEK -İ I. 0130 0304 LATIN CAPITAL LETTER I WITH DOT ABOVE -ı i. 0131 0305 LATIN SMALL LETTER DOTLESS I -IJ IJ 0132 0306 LATIN CAPITAL LIGATURE IJ -ij ij 0133 0307 LATIN SMALL LIGATURE IJ -Ĵ J> 0134 0308 LATIN CAPITAL LETTER J WITH CIRCUMFLEX -ĵ j> 0135 0309 LATIN SMALL LETTER J WITH CIRCUMFLEX -Ķ K, 0136 0310 LATIN CAPITAL LETTER K WITH CEDILLA -ķ k, 0137 0311 LATIN SMALL LETTER K WITH CEDILLA -ĸ kk 0138 0312 LATIN SMALL LETTER KRA -Ĺ L' 0139 0313 LATIN CAPITAL LETTER L WITH ACUTE -ĺ l' 013A 0314 LATIN SMALL LETTER L WITH ACUTE -Ļ L, 013B 0315 LATIN CAPITAL LETTER L WITH CEDILLA -ļ l, 013C 0316 LATIN SMALL LETTER L WITH CEDILLA -Ľ L< 013D 0317 LATIN CAPITAL LETTER L WITH CARON -ľ l< 013E 0318 LATIN SMALL LETTER L WITH CARON -Ŀ L. 013F 0319 LATIN CAPITAL LETTER L WITH MIDDLE DOT -ŀ l. 0140 0320 LATIN SMALL LETTER L WITH MIDDLE DOT -Ł L/ 0141 0321 LATIN CAPITAL LETTER L WITH STROKE -ł l/ 0142 0322 LATIN SMALL LETTER L WITH STROKE -Ń N' 0143 0323 LATIN CAPITAL LETTER N WITH ACUTE ` -ń n' 0144 0324 LATIN SMALL LETTER N WITH ACUTE ` -Ņ N, 0145 0325 LATIN CAPITAL LETTER N WITH CEDILLA ` -ņ n, 0146 0326 LATIN SMALL LETTER N WITH CEDILLA ` -Ň N< 0147 0327 LATIN CAPITAL LETTER N WITH CARON ` -ň n< 0148 0328 LATIN SMALL LETTER N WITH CARON ` -ʼn 'n 0149 0329 LATIN SMALL LETTER N PRECEDED BY APOSTROPHE ` -Ŋ NG 014A 0330 LATIN CAPITAL LETTER ENG -ŋ ng 014B 0331 LATIN SMALL LETTER ENG -Ō O- 014C 0332 LATIN CAPITAL LETTER O WITH MACRON -ō o- 014D 0333 LATIN SMALL LETTER O WITH MACRON -Ŏ O( 014E 0334 LATIN CAPITAL LETTER O WITH BREVE -ŏ o( 014F 0335 LATIN SMALL LETTER O WITH BREVE -Ő O" 0150 0336 LATIN CAPITAL LETTER O WITH DOUBLE ACUTE -ő o" 0151 0337 LATIN SMALL LETTER O WITH DOUBLE ACUTE -Œ OE 0152 0338 LATIN CAPITAL LIGATURE OE -œ oe 0153 0339 LATIN SMALL LIGATURE OE -Ŕ R' 0154 0340 LATIN CAPITAL LETTER R WITH ACUTE -ŕ r' 0155 0341 LATIN SMALL LETTER R WITH ACUTE -Ŗ R, 0156 0342 LATIN CAPITAL LETTER R WITH CEDILLA -ŗ r, 0157 0343 LATIN SMALL LETTER R WITH CEDILLA -Ř R< 0158 0344 LATIN CAPITAL LETTER R WITH CARON -ř r< 0159 0345 LATIN SMALL LETTER R WITH CARON -Ś S' 015A 0346 LATIN CAPITAL LETTER S WITH ACUTE -ś s' 015B 0347 LATIN SMALL LETTER S WITH ACUTE -Ŝ S> 015C 0348 LATIN CAPITAL LETTER S WITH CIRCUMFLEX -ŝ s> 015D 0349 LATIN SMALL LETTER S WITH CIRCUMFLEX -Ş S, 015E 0350 LATIN CAPITAL LETTER S WITH CEDILLA -ş s, 015F 0351 LATIN SMALL LETTER S WITH CEDILLA -Š S< 0160 0352 LATIN CAPITAL LETTER S WITH CARON -š s< 0161 0353 LATIN SMALL LETTER S WITH CARON -Ţ T, 0162 0354 LATIN CAPITAL LETTER T WITH CEDILLA -ţ t, 0163 0355 LATIN SMALL LETTER T WITH CEDILLA -Ť T< 0164 0356 LATIN CAPITAL LETTER T WITH CARON -ť t< 0165 0357 LATIN SMALL LETTER T WITH CARON -Ŧ T/ 0166 0358 LATIN CAPITAL LETTER T WITH STROKE -ŧ t/ 0167 0359 LATIN SMALL LETTER T WITH STROKE -Ũ U? 0168 0360 LATIN CAPITAL LETTER U WITH TILDE -ũ u? 0169 0361 LATIN SMALL LETTER U WITH TILDE -Ū U- 016A 0362 LATIN CAPITAL LETTER U WITH MACRON -ū u- 016B 0363 LATIN SMALL LETTER U WITH MACRON -Ŭ U( 016C 0364 LATIN CAPITAL LETTER U WITH BREVE -ŭ u( 016D 0365 LATIN SMALL LETTER U WITH BREVE -Ů U0 016E 0366 LATIN CAPITAL LETTER U WITH RING ABOVE -ů u0 016F 0367 LATIN SMALL LETTER U WITH RING ABOVE -Ű U" 0170 0368 LATIN CAPITAL LETTER U WITH DOUBLE ACUTE -ű u" 0171 0369 LATIN SMALL LETTER U WITH DOUBLE ACUTE -Ų U; 0172 0370 LATIN CAPITAL LETTER U WITH OGONEK -ų u; 0173 0371 LATIN SMALL LETTER U WITH OGONEK -Ŵ W> 0174 0372 LATIN CAPITAL LETTER W WITH CIRCUMFLEX -ŵ w> 0175 0373 LATIN SMALL LETTER W WITH CIRCUMFLEX -Ŷ Y> 0176 0374 LATIN CAPITAL LETTER Y WITH CIRCUMFLEX -ŷ y> 0177 0375 LATIN SMALL LETTER Y WITH CIRCUMFLEX -Ÿ Y: 0178 0376 LATIN CAPITAL LETTER Y WITH DIAERESIS -Ź Z' 0179 0377 LATIN CAPITAL LETTER Z WITH ACUTE -ź z' 017A 0378 LATIN SMALL LETTER Z WITH ACUTE -Ż Z. 017B 0379 LATIN CAPITAL LETTER Z WITH DOT ABOVE -ż z. 017C 0380 LATIN SMALL LETTER Z WITH DOT ABOVE -Ž Z< 017D 0381 LATIN CAPITAL LETTER Z WITH CARON -ž z< 017E 0382 LATIN SMALL LETTER Z WITH CARON -Ơ O9 01A0 0416 LATIN CAPITAL LETTER O WITH HORN -ơ o9 01A1 0417 LATIN SMALL LETTER O WITH HORN -Ƣ OI 01A2 0418 LATIN CAPITAL LETTER OI -ƣ oi 01A3 0419 LATIN SMALL LETTER OI -Ʀ yr 01A6 0422 LATIN LETTER YR -Ư U9 01AF 0431 LATIN CAPITAL LETTER U WITH HORN -ư u9 01B0 0432 LATIN SMALL LETTER U WITH HORN -Ƶ Z/ 01B5 0437 LATIN CAPITAL LETTER Z WITH STROKE -ƶ z/ 01B6 0438 LATIN SMALL LETTER Z WITH STROKE -Ʒ ED 01B7 0439 LATIN CAPITAL LETTER EZH -Ǎ A< 01CD 0461 LATIN CAPITAL LETTER A WITH CARON -ǎ a< 01CE 0462 LATIN SMALL LETTER A WITH CARON -Ǐ I< 01CF 0463 LATIN CAPITAL LETTER I WITH CARON -ǐ i< 01D0 0464 LATIN SMALL LETTER I WITH CARON -Ǒ O< 01D1 0465 LATIN CAPITAL LETTER O WITH CARON -ǒ o< 01D2 0466 LATIN SMALL LETTER O WITH CARON -Ǔ U< 01D3 0467 LATIN CAPITAL LETTER U WITH CARON -ǔ u< 01D4 0468 LATIN SMALL LETTER U WITH CARON -Ǟ A1 01DE 0478 LATIN CAPITAL LETTER A WITH DIAERESIS AND MACRON -ǟ a1 01DF 0479 LATIN SMALL LETTER A WITH DIAERESIS AND MACRON -Ǡ A7 01E0 0480 LATIN CAPITAL LETTER A WITH DOT ABOVE AND MACRON -ǡ a7 01E1 0481 LATIN SMALL LETTER A WITH DOT ABOVE AND MACRON -Ǣ A3 01E2 0482 LATIN CAPITAL LETTER AE WITH MACRON -ǣ a3 01E3 0483 LATIN SMALL LETTER AE WITH MACRON -Ǥ G/ 01E4 0484 LATIN CAPITAL LETTER G WITH STROKE -ǥ g/ 01E5 0485 LATIN SMALL LETTER G WITH STROKE -Ǧ G< 01E6 0486 LATIN CAPITAL LETTER G WITH CARON -ǧ g< 01E7 0487 LATIN SMALL LETTER G WITH CARON -Ǩ K< 01E8 0488 LATIN CAPITAL LETTER K WITH CARON -ǩ k< 01E9 0489 LATIN SMALL LETTER K WITH CARON -Ǫ O; 01EA 0490 LATIN CAPITAL LETTER O WITH OGONEK -ǫ o; 01EB 0491 LATIN SMALL LETTER O WITH OGONEK -Ǭ O1 01EC 0492 LATIN CAPITAL LETTER O WITH OGONEK AND MACRON -ǭ o1 01ED 0493 LATIN SMALL LETTER O WITH OGONEK AND MACRON -Ǯ EZ 01EE 0494 LATIN CAPITAL LETTER EZH WITH CARON -ǯ ez 01EF 0495 LATIN SMALL LETTER EZH WITH CARON -ǰ j< 01F0 0496 LATIN SMALL LETTER J WITH CARON -Ǵ G' 01F4 0500 LATIN CAPITAL LETTER G WITH ACUTE -ǵ g' 01F5 0501 LATIN SMALL LETTER G WITH ACUTE -ʿ ;S 02BF 0703 MODIFIER LETTER LEFT HALF RING -ˇ '< 02C7 0711 CARON -˘ '( 02D8 0728 BREVE -˙ '. 02D9 0729 DOT ABOVE -˚ '0 02DA 0730 RING ABOVE -˛ '; 02DB 0731 OGONEK -˝ '" 02DD 0733 DOUBLE ACUTE ACCENT -Ά A% 0386 0902 GREEK CAPITAL LETTER ALPHA WITH TONOS -Έ E% 0388 0904 GREEK CAPITAL LETTER EPSILON WITH TONOS -Ή Y% 0389 0905 GREEK CAPITAL LETTER ETA WITH TONOS -Ί I% 038A 0906 GREEK CAPITAL LETTER IOTA WITH TONOS -Ό O% 038C 0908 GREEK CAPITAL LETTER OMICRON WITH TONOS -Ύ U% 038E 0910 GREEK CAPITAL LETTER UPSILON WITH TONOS -Ώ W% 038F 0911 GREEK CAPITAL LETTER OMEGA WITH TONOS -ΐ i3 0390 0912 GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS -Α A* 0391 0913 GREEK CAPITAL LETTER ALPHA -Β B* 0392 0914 GREEK CAPITAL LETTER BETA -Γ G* 0393 0915 GREEK CAPITAL LETTER GAMMA -Δ D* 0394 0916 GREEK CAPITAL LETTER DELTA -Ε E* 0395 0917 GREEK CAPITAL LETTER EPSILON -Ζ Z* 0396 0918 GREEK CAPITAL LETTER ZETA -Η Y* 0397 0919 GREEK CAPITAL LETTER ETA -Θ H* 0398 0920 GREEK CAPITAL LETTER THETA -Ι I* 0399 0921 GREEK CAPITAL LETTER IOTA -Κ K* 039A 0922 GREEK CAPITAL LETTER KAPPA -Λ L* 039B 0923 GREEK CAPITAL LETTER LAMDA (aka LAMBDA) -Μ M* 039C 0924 GREEK CAPITAL LETTER MU -Ν N* 039D 0925 GREEK CAPITAL LETTER NU -Ξ C* 039E 0926 GREEK CAPITAL LETTER XI -Ο O* 039F 0927 GREEK CAPITAL LETTER OMICRON -Π P* 03A0 0928 GREEK CAPITAL LETTER PI -Ρ R* 03A1 0929 GREEK CAPITAL LETTER RHO -Σ S* 03A3 0931 GREEK CAPITAL LETTER SIGMA -Τ T* 03A4 0932 GREEK CAPITAL LETTER TAU -Υ U* 03A5 0933 GREEK CAPITAL LETTER UPSILON -Φ F* 03A6 0934 GREEK CAPITAL LETTER PHI -Χ X* 03A7 0935 GREEK CAPITAL LETTER CHI -Ψ Q* 03A8 0936 GREEK CAPITAL LETTER PSI -Ω W* 03A9 0937 GREEK CAPITAL LETTER OMEGA -Ϊ J* 03AA 0938 GREEK CAPITAL LETTER IOTA WITH DIALYTIKA -Ϋ V* 03AB 0939 GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA -ά a% 03AC 0940 GREEK SMALL LETTER ALPHA WITH TONOS -έ e% 03AD 0941 GREEK SMALL LETTER EPSILON WITH TONOS -ή y% 03AE 0942 GREEK SMALL LETTER ETA WITH TONOS -ί i% 03AF 0943 GREEK SMALL LETTER IOTA WITH TONOS -ΰ u3 03B0 0944 GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS -α a* 03B1 0945 GREEK SMALL LETTER ALPHA -β b* 03B2 0946 GREEK SMALL LETTER BETA -γ g* 03B3 0947 GREEK SMALL LETTER GAMMA -δ d* 03B4 0948 GREEK SMALL LETTER DELTA -ε e* 03B5 0949 GREEK SMALL LETTER EPSILON -ζ z* 03B6 0950 GREEK SMALL LETTER ZETA -η y* 03B7 0951 GREEK SMALL LETTER ETA -θ h* 03B8 0952 GREEK SMALL LETTER THETA -ι i* 03B9 0953 GREEK SMALL LETTER IOTA -κ k* 03BA 0954 GREEK SMALL LETTER KAPPA -λ l* 03BB 0955 GREEK SMALL LETTER LAMDA (aka LAMBDA) -μ m* 03BC 0956 GREEK SMALL LETTER MU -ν n* 03BD 0957 GREEK SMALL LETTER NU -ξ c* 03BE 0958 GREEK SMALL LETTER XI -ο o* 03BF 0959 GREEK SMALL LETTER OMICRON -π p* 03C0 0960 GREEK SMALL LETTER PI -ρ r* 03C1 0961 GREEK SMALL LETTER RHO -ς *s 03C2 0962 GREEK SMALL LETTER FINAL SIGMA -σ s* 03C3 0963 GREEK SMALL LETTER SIGMA -τ t* 03C4 0964 GREEK SMALL LETTER TAU -υ u* 03C5 0965 GREEK SMALL LETTER UPSILON -φ f* 03C6 0966 GREEK SMALL LETTER PHI -χ x* 03C7 0967 GREEK SMALL LETTER CHI -ψ q* 03C8 0968 GREEK SMALL LETTER PSI -ω w* 03C9 0969 GREEK SMALL LETTER OMEGA -ϊ j* 03CA 0970 GREEK SMALL LETTER IOTA WITH DIALYTIKA -ϋ v* 03CB 0971 GREEK SMALL LETTER UPSILON WITH DIALYTIKA -ό o% 03CC 0972 GREEK SMALL LETTER OMICRON WITH TONOS -ύ u% 03CD 0973 GREEK SMALL LETTER UPSILON WITH TONOS -ώ w% 03CE 0974 GREEK SMALL LETTER OMEGA WITH TONOS -Ϙ 'G 03D8 0984 GREEK LETTER ARCHAIC KOPPA -ϙ ,G 03D9 0985 GREEK SMALL LETTER ARCHAIC KOPPA -Ϛ T3 03DA 0986 GREEK LETTER STIGMA -ϛ t3 03DB 0987 GREEK SMALL LETTER STIGMA -Ϝ M3 03DC 0988 GREEK LETTER DIGAMMA -ϝ m3 03DD 0989 GREEK SMALL LETTER DIGAMMA -Ϟ K3 03DE 0990 GREEK LETTER KOPPA -ϟ k3 03DF 0991 GREEK SMALL LETTER KOPPA -Ϡ P3 03E0 0992 GREEK LETTER SAMPI -ϡ p3 03E1 0993 GREEK SMALL LETTER SAMPI -ϴ '% 03F4 1012 GREEK CAPITAL THETA SYMBOL -ϵ j3 03F5 1013 GREEK LUNATE EPSILON SYMBOL -Ё IO 0401 1025 CYRILLIC CAPITAL LETTER IO -Ђ D% 0402 1026 CYRILLIC CAPITAL LETTER DJE -Ѓ G% 0403 1027 CYRILLIC CAPITAL LETTER GJE -Є IE 0404 1028 CYRILLIC CAPITAL LETTER UKRAINIAN IE -Ѕ DS 0405 1029 CYRILLIC CAPITAL LETTER DZE -І II 0406 1030 CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I -Ї YI 0407 1031 CYRILLIC CAPITAL LETTER YI -Ј J% 0408 1032 CYRILLIC CAPITAL LETTER JE -Љ LJ 0409 1033 CYRILLIC CAPITAL LETTER LJE -Њ NJ 040A 1034 CYRILLIC CAPITAL LETTER NJE -Ћ Ts 040B 1035 CYRILLIC CAPITAL LETTER TSHE -Ќ KJ 040C 1036 CYRILLIC CAPITAL LETTER KJE -Ў V% 040E 1038 CYRILLIC CAPITAL LETTER SHORT U -Џ DZ 040F 1039 CYRILLIC CAPITAL LETTER DZHE -А A= 0410 1040 CYRILLIC CAPITAL LETTER A -Б B= 0411 1041 CYRILLIC CAPITAL LETTER BE -В V= 0412 1042 CYRILLIC CAPITAL LETTER VE -Г G= 0413 1043 CYRILLIC CAPITAL LETTER GHE -Д D= 0414 1044 CYRILLIC CAPITAL LETTER DE -Е E= 0415 1045 CYRILLIC CAPITAL LETTER IE -Ж Z% 0416 1046 CYRILLIC CAPITAL LETTER ZHE -З Z= 0417 1047 CYRILLIC CAPITAL LETTER ZE -И I= 0418 1048 CYRILLIC CAPITAL LETTER I -Й J= 0419 1049 CYRILLIC CAPITAL LETTER SHORT I -К K= 041A 1050 CYRILLIC CAPITAL LETTER KA -Л L= 041B 1051 CYRILLIC CAPITAL LETTER EL -М M= 041C 1052 CYRILLIC CAPITAL LETTER EM -Н N= 041D 1053 CYRILLIC CAPITAL LETTER EN -О O= 041E 1054 CYRILLIC CAPITAL LETTER O -П P= 041F 1055 CYRILLIC CAPITAL LETTER PE -Р R= 0420 1056 CYRILLIC CAPITAL LETTER ER -С S= 0421 1057 CYRILLIC CAPITAL LETTER ES -Т T= 0422 1058 CYRILLIC CAPITAL LETTER TE -У U= 0423 1059 CYRILLIC CAPITAL LETTER U -Ф F= 0424 1060 CYRILLIC CAPITAL LETTER EF -Х H= 0425 1061 CYRILLIC CAPITAL LETTER HA -Ц C= 0426 1062 CYRILLIC CAPITAL LETTER TSE -Ч C% 0427 1063 CYRILLIC CAPITAL LETTER CHE -Ш S% 0428 1064 CYRILLIC CAPITAL LETTER SHA -Щ Sc 0429 1065 CYRILLIC CAPITAL LETTER SHCHA -Ъ =" 042A 1066 CYRILLIC CAPITAL LETTER HARD SIGN -Ы Y= 042B 1067 CYRILLIC CAPITAL LETTER YERU -Ь %" 042C 1068 CYRILLIC CAPITAL LETTER SOFT SIGN -Э JE 042D 1069 CYRILLIC CAPITAL LETTER E -Ю JU 042E 1070 CYRILLIC CAPITAL LETTER YU -Я JA 042F 1071 CYRILLIC CAPITAL LETTER YA -а a= 0430 1072 CYRILLIC SMALL LETTER A -б b= 0431 1073 CYRILLIC SMALL LETTER BE -в v= 0432 1074 CYRILLIC SMALL LETTER VE -г g= 0433 1075 CYRILLIC SMALL LETTER GHE -д d= 0434 1076 CYRILLIC SMALL LETTER DE -е e= 0435 1077 CYRILLIC SMALL LETTER IE -ж z% 0436 1078 CYRILLIC SMALL LETTER ZHE -з z= 0437 1079 CYRILLIC SMALL LETTER ZE -и i= 0438 1080 CYRILLIC SMALL LETTER I -й j= 0439 1081 CYRILLIC SMALL LETTER SHORT I -к k= 043A 1082 CYRILLIC SMALL LETTER KA -л l= 043B 1083 CYRILLIC SMALL LETTER EL -м m= 043C 1084 CYRILLIC SMALL LETTER EM -н n= 043D 1085 CYRILLIC SMALL LETTER EN -о o= 043E 1086 CYRILLIC SMALL LETTER O -п p= 043F 1087 CYRILLIC SMALL LETTER PE -р r= 0440 1088 CYRILLIC SMALL LETTER ER -с s= 0441 1089 CYRILLIC SMALL LETTER ES -т t= 0442 1090 CYRILLIC SMALL LETTER TE -у u= 0443 1091 CYRILLIC SMALL LETTER U -ф f= 0444 1092 CYRILLIC SMALL LETTER EF -х h= 0445 1093 CYRILLIC SMALL LETTER HA -ц c= 0446 1094 CYRILLIC SMALL LETTER TSE -ч c% 0447 1095 CYRILLIC SMALL LETTER CHE -ш s% 0448 1096 CYRILLIC SMALL LETTER SHA -щ sc 0449 1097 CYRILLIC SMALL LETTER SHCHA -ъ =' 044A 1098 CYRILLIC SMALL LETTER HARD SIGN -ы y= 044B 1099 CYRILLIC SMALL LETTER YERU -ь %' 044C 1100 CYRILLIC SMALL LETTER SOFT SIGN -э je 044D 1101 CYRILLIC SMALL LETTER E -ю ju 044E 1102 CYRILLIC SMALL LETTER YU -я ja 044F 1103 CYRILLIC SMALL LETTER YA -ё io 0451 1105 CYRILLIC SMALL LETTER IO -ђ d% 0452 1106 CYRILLIC SMALL LETTER DJE -ѓ g% 0453 1107 CYRILLIC SMALL LETTER GJE -є ie 0454 1108 CYRILLIC SMALL LETTER UKRAINIAN IE -ѕ ds 0455 1109 CYRILLIC SMALL LETTER DZE -і ii 0456 1110 CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I -ї yi 0457 1111 CYRILLIC SMALL LETTER YI -ј j% 0458 1112 CYRILLIC SMALL LETTER JE -љ lj 0459 1113 CYRILLIC SMALL LETTER LJE -њ nj 045A 1114 CYRILLIC SMALL LETTER NJE -ћ ts 045B 1115 CYRILLIC SMALL LETTER TSHE -ќ kj 045C 1116 CYRILLIC SMALL LETTER KJE -ў v% 045E 1118 CYRILLIC SMALL LETTER SHORT U -џ dz 045F 1119 CYRILLIC SMALL LETTER DZHE -Ѣ Y3 0462 1122 CYRILLIC CAPITAL LETTER YAT -ѣ y3 0463 1123 CYRILLIC SMALL LETTER YAT -Ѫ O3 046A 1130 CYRILLIC CAPITAL LETTER BIG YUS -ѫ o3 046B 1131 CYRILLIC SMALL LETTER BIG YUS -Ѳ F3 0472 1138 CYRILLIC CAPITAL LETTER FITA -ѳ f3 0473 1139 CYRILLIC SMALL LETTER FITA -Ѵ V3 0474 1140 CYRILLIC CAPITAL LETTER IZHITSA -ѵ v3 0475 1141 CYRILLIC SMALL LETTER IZHITSA -Ҁ C3 0480 1152 CYRILLIC CAPITAL LETTER KOPPA -ҁ c3 0481 1153 CYRILLIC SMALL LETTER KOPPA -Ґ G3 0490 1168 CYRILLIC CAPITAL LETTER GHE WITH UPTURN -ґ g3 0491 1169 CYRILLIC SMALL LETTER GHE WITH UPTURN -א A+ 05D0 1488 HEBREW LETTER ALEF -ב B+ 05D1 1489 HEBREW LETTER BET -ג G+ 05D2 1490 HEBREW LETTER GIMEL -ד D+ 05D3 1491 HEBREW LETTER DALET -ה H+ 05D4 1492 HEBREW LETTER HE -ו W+ 05D5 1493 HEBREW LETTER VAV -ז Z+ 05D6 1494 HEBREW LETTER ZAYIN -ח X+ 05D7 1495 HEBREW LETTER HET -ט Tj 05D8 1496 HEBREW LETTER TET -י J+ 05D9 1497 HEBREW LETTER YOD -ך K% 05DA 1498 HEBREW LETTER FINAL KAF -כ K+ 05DB 1499 HEBREW LETTER KAF -ל L+ 05DC 1500 HEBREW LETTER LAMED -ם M% 05DD 1501 HEBREW LETTER FINAL MEM -מ M+ 05DE 1502 HEBREW LETTER MEM -ן N% 05DF 1503 HEBREW LETTER FINAL NUN ` -נ N+ 05E0 1504 HEBREW LETTER NUN ` -ס S+ 05E1 1505 HEBREW LETTER SAMEKH -ע E+ 05E2 1506 HEBREW LETTER AYIN -ף P% 05E3 1507 HEBREW LETTER FINAL PE -פ P+ 05E4 1508 HEBREW LETTER PE -ץ Zj 05E5 1509 HEBREW LETTER FINAL TSADI -צ ZJ 05E6 1510 HEBREW LETTER TSADI -ק Q+ 05E7 1511 HEBREW LETTER QOF -ר R+ 05E8 1512 HEBREW LETTER RESH -ש Sh 05E9 1513 HEBREW LETTER SHIN -ת T+ 05EA 1514 HEBREW LETTER TAV -، ,+ 060C 1548 ARABIC COMMA -؛ ;+ 061B 1563 ARABIC SEMICOLON -؟ ?+ 061F 1567 ARABIC QUESTION MARK -ء H' 0621 1569 ARABIC LETTER HAMZA -آ aM 0622 1570 ARABIC LETTER ALEF WITH MADDA ABOVE -أ aH 0623 1571 ARABIC LETTER ALEF WITH HAMZA ABOVE -ؤ wH 0624 1572 ARABIC LETTER WAW WITH HAMZA ABOVE -إ ah 0625 1573 ARABIC LETTER ALEF WITH HAMZA BELOW -ئ yH 0626 1574 ARABIC LETTER YEH WITH HAMZA ABOVE -ا a+ 0627 1575 ARABIC LETTER ALEF -ب b+ 0628 1576 ARABIC LETTER BEH -ة tm 0629 1577 ARABIC LETTER TEH MARBUTA -ت t+ 062A 1578 ARABIC LETTER TEH -ث tk 062B 1579 ARABIC LETTER THEH -ج g+ 062C 1580 ARABIC LETTER JEEM -ح hk 062D 1581 ARABIC LETTER HAH -خ x+ 062E 1582 ARABIC LETTER KHAH -د d+ 062F 1583 ARABIC LETTER DAL -ذ dk 0630 1584 ARABIC LETTER THAL -ر r+ 0631 1585 ARABIC LETTER REH -ز z+ 0632 1586 ARABIC LETTER ZAIN -س s+ 0633 1587 ARABIC LETTER SEEN -ش sn 0634 1588 ARABIC LETTER SHEEN -ص c+ 0635 1589 ARABIC LETTER SAD -ض dd 0636 1590 ARABIC LETTER DAD -ط tj 0637 1591 ARABIC LETTER TAH -ظ zH 0638 1592 ARABIC LETTER ZAH -ع e+ 0639 1593 ARABIC LETTER AIN -غ i+ 063A 1594 ARABIC LETTER GHAIN -ـ ++ 0640 1600 ARABIC TATWEEL -ف f+ 0641 1601 ARABIC LETTER FEH -ق q+ 0642 1602 ARABIC LETTER QAF -ك k+ 0643 1603 ARABIC LETTER KAF -ل l+ 0644 1604 ARABIC LETTER LAM -م m+ 0645 1605 ARABIC LETTER MEEM -ن n+ 0646 1606 ARABIC LETTER NOON -ه h+ 0647 1607 ARABIC LETTER HEH -و w+ 0648 1608 ARABIC LETTER WAW -ى j+ 0649 1609 ARABIC LETTER ALEF MAKSURA -ي y+ 064A 1610 ARABIC LETTER YEH -ً :+ 064B 1611 ARABIC FATHATAN -ٌ "+ 064C 1612 ARABIC DAMMATAN -ٍ =+ 064D 1613 ARABIC KASRATAN -َ /+ 064E 1614 ARABIC FATHA -ُ '+ 064F 1615 ARABIC DAMMA -ِ 1+ 0650 1616 ARABIC KASRA -ّ 3+ 0651 1617 ARABIC SHADDA -ْ 0+ 0652 1618 ARABIC SUKUN -ٰ aS 0670 1648 ARABIC LETTER SUPERSCRIPT ALEF -پ p+ 067E 1662 ARABIC LETTER PEH -ڤ v+ 06A4 1700 ARABIC LETTER VEH -گ gf 06AF 1711 ARABIC LETTER GAF -۰ 0a 06F0 1776 EXTENDED ARABIC-INDIC DIGIT ZERO -۱ 1a 06F1 1777 EXTENDED ARABIC-INDIC DIGIT ONE -۲ 2a 06F2 1778 EXTENDED ARABIC-INDIC DIGIT TWO -۳ 3a 06F3 1779 EXTENDED ARABIC-INDIC DIGIT THREE -۴ 4a 06F4 1780 EXTENDED ARABIC-INDIC DIGIT FOUR -۵ 5a 06F5 1781 EXTENDED ARABIC-INDIC DIGIT FIVE -۶ 6a 06F6 1782 EXTENDED ARABIC-INDIC DIGIT SIX -۷ 7a 06F7 1783 EXTENDED ARABIC-INDIC DIGIT SEVEN -۸ 8a 06F8 1784 EXTENDED ARABIC-INDIC DIGIT EIGHT -۹ 9a 06F9 1785 EXTENDED ARABIC-INDIC DIGIT NINE -Ḃ B. 1E02 7682 LATIN CAPITAL LETTER B WITH DOT ABOVE -ḃ b. 1E03 7683 LATIN SMALL LETTER B WITH DOT ABOVE -Ḇ B_ 1E06 7686 LATIN CAPITAL LETTER B WITH LINE BELOW -ḇ b_ 1E07 7687 LATIN SMALL LETTER B WITH LINE BELOW -Ḋ D. 1E0A 7690 LATIN CAPITAL LETTER D WITH DOT ABOVE -ḋ d. 1E0B 7691 LATIN SMALL LETTER D WITH DOT ABOVE -Ḏ D_ 1E0E 7694 LATIN CAPITAL LETTER D WITH LINE BELOW -ḏ d_ 1E0F 7695 LATIN SMALL LETTER D WITH LINE BELOW -Ḑ D, 1E10 7696 LATIN CAPITAL LETTER D WITH CEDILLA -ḑ d, 1E11 7697 LATIN SMALL LETTER D WITH CEDILLA -Ḟ F. 1E1E 7710 LATIN CAPITAL LETTER F WITH DOT ABOVE -ḟ f. 1E1F 7711 LATIN SMALL LETTER F WITH DOT ABOVE -Ḡ G- 1E20 7712 LATIN CAPITAL LETTER G WITH MACRON -ḡ g- 1E21 7713 LATIN SMALL LETTER G WITH MACRON -Ḣ H. 1E22 7714 LATIN CAPITAL LETTER H WITH DOT ABOVE -ḣ h. 1E23 7715 LATIN SMALL LETTER H WITH DOT ABOVE -Ḧ H: 1E26 7718 LATIN CAPITAL LETTER H WITH DIAERESIS -ḧ h: 1E27 7719 LATIN SMALL LETTER H WITH DIAERESIS -Ḩ H, 1E28 7720 LATIN CAPITAL LETTER H WITH CEDILLA -ḩ h, 1E29 7721 LATIN SMALL LETTER H WITH CEDILLA -Ḱ K' 1E30 7728 LATIN CAPITAL LETTER K WITH ACUTE -ḱ k' 1E31 7729 LATIN SMALL LETTER K WITH ACUTE -Ḵ K_ 1E34 7732 LATIN CAPITAL LETTER K WITH LINE BELOW -ḵ k_ 1E35 7733 LATIN SMALL LETTER K WITH LINE BELOW -Ḻ L_ 1E3A 7738 LATIN CAPITAL LETTER L WITH LINE BELOW -ḻ l_ 1E3B 7739 LATIN SMALL LETTER L WITH LINE BELOW -Ḿ M' 1E3E 7742 LATIN CAPITAL LETTER M WITH ACUTE -ḿ m' 1E3F 7743 LATIN SMALL LETTER M WITH ACUTE -Ṁ M. 1E40 7744 LATIN CAPITAL LETTER M WITH DOT ABOVE -ṁ m. 1E41 7745 LATIN SMALL LETTER M WITH DOT ABOVE -Ṅ N. 1E44 7748 LATIN CAPITAL LETTER N WITH DOT ABOVE ` -ṅ n. 1E45 7749 LATIN SMALL LETTER N WITH DOT ABOVE ` -Ṉ N_ 1E48 7752 LATIN CAPITAL LETTER N WITH LINE BELOW ` -ṉ n_ 1E49 7753 LATIN SMALL LETTER N WITH LINE BELOW ` -Ṕ P' 1E54 7764 LATIN CAPITAL LETTER P WITH ACUTE -ṕ p' 1E55 7765 LATIN SMALL LETTER P WITH ACUTE -Ṗ P. 1E56 7766 LATIN CAPITAL LETTER P WITH DOT ABOVE -ṗ p. 1E57 7767 LATIN SMALL LETTER P WITH DOT ABOVE -Ṙ R. 1E58 7768 LATIN CAPITAL LETTER R WITH DOT ABOVE -ṙ r. 1E59 7769 LATIN SMALL LETTER R WITH DOT ABOVE -Ṟ R_ 1E5E 7774 LATIN CAPITAL LETTER R WITH LINE BELOW -ṟ r_ 1E5F 7775 LATIN SMALL LETTER R WITH LINE BELOW -Ṡ S. 1E60 7776 LATIN CAPITAL LETTER S WITH DOT ABOVE -ṡ s. 1E61 7777 LATIN SMALL LETTER S WITH DOT ABOVE -Ṫ T. 1E6A 7786 LATIN CAPITAL LETTER T WITH DOT ABOVE -ṫ t. 1E6B 7787 LATIN SMALL LETTER T WITH DOT ABOVE -Ṯ T_ 1E6E 7790 LATIN CAPITAL LETTER T WITH LINE BELOW -ṯ t_ 1E6F 7791 LATIN SMALL LETTER T WITH LINE BELOW -Ṽ V? 1E7C 7804 LATIN CAPITAL LETTER V WITH TILDE -ṽ v? 1E7D 7805 LATIN SMALL LETTER V WITH TILDE -Ẁ W! 1E80 7808 LATIN CAPITAL LETTER W WITH GRAVE -ẁ w! 1E81 7809 LATIN SMALL LETTER W WITH GRAVE -Ẃ W' 1E82 7810 LATIN CAPITAL LETTER W WITH ACUTE -ẃ w' 1E83 7811 LATIN SMALL LETTER W WITH ACUTE -Ẅ W: 1E84 7812 LATIN CAPITAL LETTER W WITH DIAERESIS -ẅ w: 1E85 7813 LATIN SMALL LETTER W WITH DIAERESIS -Ẇ W. 1E86 7814 LATIN CAPITAL LETTER W WITH DOT ABOVE -ẇ w. 1E87 7815 LATIN SMALL LETTER W WITH DOT ABOVE -Ẋ X. 1E8A 7818 LATIN CAPITAL LETTER X WITH DOT ABOVE -ẋ x. 1E8B 7819 LATIN SMALL LETTER X WITH DOT ABOVE -Ẍ X: 1E8C 7820 LATIN CAPITAL LETTER X WITH DIAERESIS -ẍ x: 1E8D 7821 LATIN SMALL LETTER X WITH DIAERESIS -Ẏ Y. 1E8E 7822 LATIN CAPITAL LETTER Y WITH DOT ABOVE -ẏ y. 1E8F 7823 LATIN SMALL LETTER Y WITH DOT ABOVE -Ẑ Z> 1E90 7824 LATIN CAPITAL LETTER Z WITH CIRCUMFLEX -ẑ z> 1E91 7825 LATIN SMALL LETTER Z WITH CIRCUMFLEX -Ẕ Z_ 1E94 7828 LATIN CAPITAL LETTER Z WITH LINE BELOW -ẕ z_ 1E95 7829 LATIN SMALL LETTER Z WITH LINE BELOW -ẖ h_ 1E96 7830 LATIN SMALL LETTER H WITH LINE BELOW -ẗ t: 1E97 7831 LATIN SMALL LETTER T WITH DIAERESIS -ẘ w0 1E98 7832 LATIN SMALL LETTER W WITH RING ABOVE -ẙ y0 1E99 7833 LATIN SMALL LETTER Y WITH RING ABOVE -Ả A2 1EA2 7842 LATIN CAPITAL LETTER A WITH HOOK ABOVE -ả a2 1EA3 7843 LATIN SMALL LETTER A WITH HOOK ABOVE -Ẻ E2 1EBA 7866 LATIN CAPITAL LETTER E WITH HOOK ABOVE -ẻ e2 1EBB 7867 LATIN SMALL LETTER E WITH HOOK ABOVE -Ẽ E? 1EBC 7868 LATIN CAPITAL LETTER E WITH TILDE -ẽ e? 1EBD 7869 LATIN SMALL LETTER E WITH TILDE -Ỉ I2 1EC8 7880 LATIN CAPITAL LETTER I WITH HOOK ABOVE -ỉ i2 1EC9 7881 LATIN SMALL LETTER I WITH HOOK ABOVE -Ỏ O2 1ECE 7886 LATIN CAPITAL LETTER O WITH HOOK ABOVE -ỏ o2 1ECF 7887 LATIN SMALL LETTER O WITH HOOK ABOVE -Ủ U2 1EE6 7910 LATIN CAPITAL LETTER U WITH HOOK ABOVE -ủ u2 1EE7 7911 LATIN SMALL LETTER U WITH HOOK ABOVE -Ỳ Y! 1EF2 7922 LATIN CAPITAL LETTER Y WITH GRAVE -ỳ y! 1EF3 7923 LATIN SMALL LETTER Y WITH GRAVE -Ỷ Y2 1EF6 7926 LATIN CAPITAL LETTER Y WITH HOOK ABOVE -ỷ y2 1EF7 7927 LATIN SMALL LETTER Y WITH HOOK ABOVE -Ỹ Y? 1EF8 7928 LATIN CAPITAL LETTER Y WITH TILDE -ỹ y? 1EF9 7929 LATIN SMALL LETTER Y WITH TILDE -ἀ ;' 1F00 7936 GREEK SMALL LETTER ALPHA WITH PSILI -ἁ ,' 1F01 7937 GREEK SMALL LETTER ALPHA WITH DASIA -ἂ ;! 1F02 7938 GREEK SMALL LETTER ALPHA WITH PSILI AND VARIA -ἃ ,! 1F03 7939 GREEK SMALL LETTER ALPHA WITH DASIA AND VARIA -ἄ ?; 1F04 7940 GREEK SMALL LETTER ALPHA WITH PSILI AND OXIA -ἅ ?, 1F05 7941 GREEK SMALL LETTER ALPHA WITH DASIA AND OXIA -ἆ !: 1F06 7942 GREEK SMALL LETTER ALPHA WITH PSILI AND PERISPOMENI -ἇ ?: 1F07 7943 GREEK SMALL LETTER ALPHA WITH DASIA AND PERISPOMENI - 1N 2002 8194 EN SPACE - 1M 2003 8195 EM SPACE - 3M 2004 8196 THREE-PER-EM SPACE - 4M 2005 8197 FOUR-PER-EM SPACE - 6M 2006 8198 SIX-PER-EM SPACE - 1T 2009 8201 THIN SPACE - 1H 200A 8202 HAIR SPACE -‐ -1 2010 8208 HYPHEN -– -N 2013 8211 EN DASH ` -— -M 2014 8212 EM DASH -― -3 2015 8213 HORIZONTAL BAR -‖ !2 2016 8214 DOUBLE VERTICAL LINE -‗ =2 2017 8215 DOUBLE LOW LINE -‘ '6 2018 8216 LEFT SINGLE QUOTATION MARK -’ '9 2019 8217 RIGHT SINGLE QUOTATION MARK -‚ .9 201A 8218 SINGLE LOW-9 QUOTATION MARK -‛ 9' 201B 8219 SINGLE HIGH-REVERSED-9 QUOTATION MARK -“ "6 201C 8220 LEFT DOUBLE QUOTATION MARK -” "9 201D 8221 RIGHT DOUBLE QUOTATION MARK -„ :9 201E 8222 DOUBLE LOW-9 QUOTATION MARK -‟ 9" 201F 8223 DOUBLE HIGH-REVERSED-9 QUOTATION MARK -† /- 2020 8224 DAGGER -‡ /= 2021 8225 DOUBLE DAGGER -• oo 2022 8226 BULLET -‥ .. 2025 8229 TWO DOT LEADER -… ,. 2026 8230 HORIZONTAL ELLIPSIS -‰ %0 2030 8240 PER MILLE SIGN -′ 1' 2032 8242 PRIME -″ 2' 2033 8243 DOUBLE PRIME -‴ 3' 2034 8244 TRIPLE PRIME -‵ 1" 2035 8245 REVERSED PRIME -‶ 2" 2036 8246 REVERSED DOUBLE PRIME -‷ 3" 2037 8247 REVERSED TRIPLE PRIME -‸ Ca 2038 8248 CARET -‹ <1 2039 8249 SINGLE LEFT-POINTING ANGLE QUOTATION MARK -› >1 203A 8250 SINGLE RIGHT-POINTING ANGLE QUOTATION MARK -※ :X 203B 8251 REFERENCE MARK -‾ '- 203E 8254 OVERLINE -⁄ /f 2044 8260 FRACTION SLASH -⁰ 0S 2070 8304 SUPERSCRIPT ZERO -⁴ 4S 2074 8308 SUPERSCRIPT FOUR -⁵ 5S 2075 8309 SUPERSCRIPT FIVE -⁶ 6S 2076 8310 SUPERSCRIPT SIX -⁷ 7S 2077 8311 SUPERSCRIPT SEVEN -⁸ 8S 2078 8312 SUPERSCRIPT EIGHT -⁹ 9S 2079 8313 SUPERSCRIPT NINE -⁺ +S 207A 8314 SUPERSCRIPT PLUS SIGN -⁻ -S 207B 8315 SUPERSCRIPT MINUS -⁼ =S 207C 8316 SUPERSCRIPT EQUALS SIGN -⁽ (S 207D 8317 SUPERSCRIPT LEFT PARENTHESIS -⁾ )S 207E 8318 SUPERSCRIPT RIGHT PARENTHESIS -ⁿ nS 207F 8319 SUPERSCRIPT LATIN SMALL LETTER N ` -₀ 0s 2080 8320 SUBSCRIPT ZERO -₁ 1s 2081 8321 SUBSCRIPT ONE -₂ 2s 2082 8322 SUBSCRIPT TWO -₃ 3s 2083 8323 SUBSCRIPT THREE -₄ 4s 2084 8324 SUBSCRIPT FOUR -₅ 5s 2085 8325 SUBSCRIPT FIVE -₆ 6s 2086 8326 SUBSCRIPT SIX -₇ 7s 2087 8327 SUBSCRIPT SEVEN -₈ 8s 2088 8328 SUBSCRIPT EIGHT -₉ 9s 2089 8329 SUBSCRIPT NINE -₊ +s 208A 8330 SUBSCRIPT PLUS SIGN -₋ -s 208B 8331 SUBSCRIPT MINUS -₌ =s 208C 8332 SUBSCRIPT EQUALS SIGN -₍ (s 208D 8333 SUBSCRIPT LEFT PARENTHESIS -₎ )s 208E 8334 SUBSCRIPT RIGHT PARENTHESIS -₤ Li 20A4 8356 LIRA SIGN -₧ Pt 20A7 8359 PESETA SIGN -₩ W= 20A9 8361 WON SIGN -€ Eu 20AC 8364 EURO SIGN -₽ =R 20BD 8381 ROUBLE SIGN -₽ =P 20BD 8381 ROUBLE SIGN -℃ oC 2103 8451 DEGREE CELSIUS -℅ co 2105 8453 CARE OF -℉ oF 2109 8457 DEGREE FAHRENHEIT -№ N0 2116 8470 NUMERO SIGN -℗ PO 2117 8471 SOUND RECORDING COPYRIGHT -℞ Rx 211E 8478 PRESCRIPTION TAKE -℠ SM 2120 8480 SERVICE MARK -™ TM 2122 8482 TRADE MARK SIGN -Ω Om 2126 8486 OHM SIGN -Å AO 212B 8491 ANGSTROM SIGN -⅓ 13 2153 8531 VULGAR FRACTION ONE THIRD -⅔ 23 2154 8532 VULGAR FRACTION TWO THIRDS -⅕ 15 2155 8533 VULGAR FRACTION ONE FIFTH -⅖ 25 2156 8534 VULGAR FRACTION TWO FIFTHS -⅗ 35 2157 8535 VULGAR FRACTION THREE FIFTHS -⅘ 45 2158 8536 VULGAR FRACTION FOUR FIFTHS -⅙ 16 2159 8537 VULGAR FRACTION ONE SIXTH -⅚ 56 215A 8538 VULGAR FRACTION FIVE SIXTHS -⅛ 18 215B 8539 VULGAR FRACTION ONE EIGHTH -⅜ 38 215C 8540 VULGAR FRACTION THREE EIGHTHS -⅝ 58 215D 8541 VULGAR FRACTION FIVE EIGHTHS -⅞ 78 215E 8542 VULGAR FRACTION SEVEN EIGHTHS -Ⅰ 1R 2160 8544 ROMAN NUMERAL ONE -Ⅱ 2R 2161 8545 ROMAN NUMERAL TWO -Ⅲ 3R 2162 8546 ROMAN NUMERAL THREE -Ⅳ 4R 2163 8547 ROMAN NUMERAL FOUR -Ⅴ 5R 2164 8548 ROMAN NUMERAL FIVE -Ⅵ 6R 2165 8549 ROMAN NUMERAL SIX -Ⅶ 7R 2166 8550 ROMAN NUMERAL SEVEN -Ⅷ 8R 2167 8551 ROMAN NUMERAL EIGHT -Ⅸ 9R 2168 8552 ROMAN NUMERAL NINE -Ⅹ aR 2169 8553 ROMAN NUMERAL TEN -Ⅺ bR 216A 8554 ROMAN NUMERAL ELEVEN -Ⅻ cR 216B 8555 ROMAN NUMERAL TWELVE -ⅰ 1r 2170 8560 SMALL ROMAN NUMERAL ONE -ⅱ 2r 2171 8561 SMALL ROMAN NUMERAL TWO -ⅲ 3r 2172 8562 SMALL ROMAN NUMERAL THREE -ⅳ 4r 2173 8563 SMALL ROMAN NUMERAL FOUR -ⅴ 5r 2174 8564 SMALL ROMAN NUMERAL FIVE -ⅵ 6r 2175 8565 SMALL ROMAN NUMERAL SIX -ⅶ 7r 2176 8566 SMALL ROMAN NUMERAL SEVEN -ⅷ 8r 2177 8567 SMALL ROMAN NUMERAL EIGHT -ⅸ 9r 2178 8568 SMALL ROMAN NUMERAL NINE -ⅹ ar 2179 8569 SMALL ROMAN NUMERAL TEN -ⅺ br 217A 8570 SMALL ROMAN NUMERAL ELEVEN -ⅻ cr 217B 8571 SMALL ROMAN NUMERAL TWELVE -← <- 2190 8592 LEFTWARDS ARROW -↑ -! 2191 8593 UPWARDS ARROW -→ -> 2192 8594 RIGHTWARDS ARROW -↓ -v 2193 8595 DOWNWARDS ARROW -↔ <> 2194 8596 LEFT RIGHT ARROW -↕ UD 2195 8597 UP DOWN ARROW -⇐ <= 21D0 8656 LEFTWARDS DOUBLE ARROW -⇒ => 21D2 8658 RIGHTWARDS DOUBLE ARROW -⇔ == 21D4 8660 LEFT RIGHT DOUBLE ARROW -∀ FA 2200 8704 FOR ALL -∂ dP 2202 8706 PARTIAL DIFFERENTIAL -∃ TE 2203 8707 THERE EXISTS -∅ /0 2205 8709 EMPTY SET -∆ DE 2206 8710 INCREMENT -∇ NB 2207 8711 NABLA -∈ (- 2208 8712 ELEMENT OF -∋ -) 220B 8715 CONTAINS AS MEMBER -∏ *P 220F 8719 N-ARY PRODUCT ` -∑ +Z 2211 8721 N-ARY SUMMATION ` -− -2 2212 8722 MINUS SIGN -∓ -+ 2213 8723 MINUS-OR-PLUS SIGN -∗ *- 2217 8727 ASTERISK OPERATOR -∘ Ob 2218 8728 RING OPERATOR -∙ Sb 2219 8729 BULLET OPERATOR -√ RT 221A 8730 SQUARE ROOT -∝ 0( 221D 8733 PROPORTIONAL TO -∞ 00 221E 8734 INFINITY -∟ -L 221F 8735 RIGHT ANGLE -∠ -V 2220 8736 ANGLE -∥ PP 2225 8741 PARALLEL TO -∧ AN 2227 8743 LOGICAL AND -∨ OR 2228 8744 LOGICAL OR -∩ (U 2229 8745 INTERSECTION -∪ )U 222A 8746 UNION -∫ In 222B 8747 INTEGRAL -∬ DI 222C 8748 DOUBLE INTEGRAL -∮ Io 222E 8750 CONTOUR INTEGRAL -∴ .: 2234 8756 THEREFORE -∵ :. 2235 8757 BECAUSE -∶ :R 2236 8758 RATIO -∷ :: 2237 8759 PROPORTION -∼ ?1 223C 8764 TILDE OPERATOR -∾ CG 223E 8766 INVERTED LAZY S -≃ ?- 2243 8771 ASYMPTOTICALLY EQUAL TO -≅ ?= 2245 8773 APPROXIMATELY EQUAL TO -≈ ?2 2248 8776 ALMOST EQUAL TO -≌ =? 224C 8780 ALL EQUAL TO -≓ HI 2253 8787 IMAGE OF OR APPROXIMATELY EQUAL TO -≠ != 2260 8800 NOT EQUAL TO -≡ =3 2261 8801 IDENTICAL TO -≤ =< 2264 8804 LESS-THAN OR EQUAL TO -≥ >= 2265 8805 GREATER-THAN OR EQUAL TO -≪ <* 226A 8810 MUCH LESS-THAN -≫ *> 226B 8811 MUCH GREATER-THAN -≮ !< 226E 8814 NOT LESS-THAN -≯ !> 226F 8815 NOT GREATER-THAN -⊂ (C 2282 8834 SUBSET OF -⊃ )C 2283 8835 SUPERSET OF -⊆ (_ 2286 8838 SUBSET OF OR EQUAL TO -⊇ )_ 2287 8839 SUPERSET OF OR EQUAL TO -⊙ 0. 2299 8857 CIRCLED DOT OPERATOR -⊚ 02 229A 8858 CIRCLED RING OPERATOR -⊥ -T 22A5 8869 UP TACK -⋅ .P 22C5 8901 DOT OPERATOR -⋮ :3 22EE 8942 VERTICAL ELLIPSIS -⋯ .3 22EF 8943 MIDLINE HORIZONTAL ELLIPSIS -⌂ Eh 2302 8962 HOUSE -⌈ <7 2308 8968 LEFT CEILING -⌉ >7 2309 8969 RIGHT CEILING -⌊ 7< 230A 8970 LEFT FLOOR -⌋ 7> 230B 8971 RIGHT FLOOR -⌐ NI 2310 8976 REVERSED NOT SIGN -⌒ (A 2312 8978 ARC -⌕ TR 2315 8981 TELEPHONE RECORDER -⌠ Iu 2320 8992 TOP HALF INTEGRAL -⌡ Il 2321 8993 BOTTOM HALF INTEGRAL -〈 </ 2329 9001 LEFT-POINTING ANGLE BRACKET -〉 /> 232A 9002 RIGHT-POINTING ANGLE BRACKET -␣ Vs 2423 9251 OPEN BOX -⑀ 1h 2440 9280 OCR HOOK -⑁ 3h 2441 9281 OCR CHAIR -⑂ 2h 2442 9282 OCR FORK -⑃ 4h 2443 9283 OCR INVERTED FORK -⑆ 1j 2446 9286 OCR BRANCH BANK IDENTIFICATION -⑇ 2j 2447 9287 OCR AMOUNT OF CHECK -⑈ 3j 2448 9288 OCR DASH -⑉ 4j 2449 9289 OCR CUSTOMER ACCOUNT NUMBER -⒈ 1. 2488 9352 DIGIT ONE FULL STOP -⒉ 2. 2489 9353 DIGIT TWO FULL STOP -⒊ 3. 248A 9354 DIGIT THREE FULL STOP -⒋ 4. 248B 9355 DIGIT FOUR FULL STOP -⒌ 5. 248C 9356 DIGIT FIVE FULL STOP -⒍ 6. 248D 9357 DIGIT SIX FULL STOP -⒎ 7. 248E 9358 DIGIT SEVEN FULL STOP -⒏ 8. 248F 9359 DIGIT EIGHT FULL STOP -⒐ 9. 2490 9360 DIGIT NINE FULL STOP -─ hh 2500 9472 BOX DRAWINGS LIGHT HORIZONTAL -━ HH 2501 9473 BOX DRAWINGS HEAVY HORIZONTAL -│ vv 2502 9474 BOX DRAWINGS LIGHT VERTICAL -┃ VV 2503 9475 BOX DRAWINGS HEAVY VERTICAL -┄ 3- 2504 9476 BOX DRAWINGS LIGHT TRIPLE DASH HORIZONTAL -┅ 3_ 2505 9477 BOX DRAWINGS HEAVY TRIPLE DASH HORIZONTAL -┆ 3! 2506 9478 BOX DRAWINGS LIGHT TRIPLE DASH VERTICAL -┇ 3/ 2507 9479 BOX DRAWINGS HEAVY TRIPLE DASH VERTICAL -┈ 4- 2508 9480 BOX DRAWINGS LIGHT QUADRUPLE DASH HORIZONTAL -┉ 4_ 2509 9481 BOX DRAWINGS HEAVY QUADRUPLE DASH HORIZONTAL -┊ 4! 250A 9482 BOX DRAWINGS LIGHT QUADRUPLE DASH VERTICAL -┋ 4/ 250B 9483 BOX DRAWINGS HEAVY QUADRUPLE DASH VERTICAL -┌ dr 250C 9484 BOX DRAWINGS LIGHT DOWN AND RIGHT -┍ dR 250D 9485 BOX DRAWINGS DOWN LIGHT AND RIGHT HEAVY -┎ Dr 250E 9486 BOX DRAWINGS DOWN HEAVY AND RIGHT LIGHT -┏ DR 250F 9487 BOX DRAWINGS HEAVY DOWN AND RIGHT -┐ dl 2510 9488 BOX DRAWINGS LIGHT DOWN AND LEFT -┑ dL 2511 9489 BOX DRAWINGS DOWN LIGHT AND LEFT HEAVY -┒ Dl 2512 9490 BOX DRAWINGS DOWN HEAVY AND LEFT LIGHT -┓ LD 2513 9491 BOX DRAWINGS HEAVY DOWN AND LEFT -└ ur 2514 9492 BOX DRAWINGS LIGHT UP AND RIGHT -┕ uR 2515 9493 BOX DRAWINGS UP LIGHT AND RIGHT HEAVY -┖ Ur 2516 9494 BOX DRAWINGS UP HEAVY AND RIGHT LIGHT -┗ UR 2517 9495 BOX DRAWINGS HEAVY UP AND RIGHT -┘ ul 2518 9496 BOX DRAWINGS LIGHT UP AND LEFT -┙ uL 2519 9497 BOX DRAWINGS UP LIGHT AND LEFT HEAVY -┚ Ul 251A 9498 BOX DRAWINGS UP HEAVY AND LEFT LIGHT -┛ UL 251B 9499 BOX DRAWINGS HEAVY UP AND LEFT -├ vr 251C 9500 BOX DRAWINGS LIGHT VERTICAL AND RIGHT -┝ vR 251D 9501 BOX DRAWINGS VERTICAL LIGHT AND RIGHT HEAVY -┠ Vr 2520 9504 BOX DRAWINGS VERTICAL HEAVY AND RIGHT LIGHT -┣ VR 2523 9507 BOX DRAWINGS HEAVY VERTICAL AND RIGHT -┤ vl 2524 9508 BOX DRAWINGS LIGHT VERTICAL AND LEFT -┥ vL 2525 9509 BOX DRAWINGS VERTICAL LIGHT AND LEFT HEAVY -┨ Vl 2528 9512 BOX DRAWINGS VERTICAL HEAVY AND LEFT LIGHT -┫ VL 252B 9515 BOX DRAWINGS HEAVY VERTICAL AND LEFT -┬ dh 252C 9516 BOX DRAWINGS LIGHT DOWN AND HORIZONTAL -┯ dH 252F 9519 BOX DRAWINGS DOWN LIGHT AND HORIZONTAL HEAVY -┰ Dh 2530 9520 BOX DRAWINGS DOWN HEAVY AND HORIZONTAL LIGHT -┳ DH 2533 9523 BOX DRAWINGS HEAVY DOWN AND HORIZONTAL -┴ uh 2534 9524 BOX DRAWINGS LIGHT UP AND HORIZONTAL -┷ uH 2537 9527 BOX DRAWINGS UP LIGHT AND HORIZONTAL HEAVY -┸ Uh 2538 9528 BOX DRAWINGS UP HEAVY AND HORIZONTAL LIGHT -┻ UH 253B 9531 BOX DRAWINGS HEAVY UP AND HORIZONTAL -┼ vh 253C 9532 BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL -┿ vH 253F 9535 BOX DRAWINGS VERTICAL LIGHT AND HORIZONTAL HEAVY -╂ Vh 2542 9538 BOX DRAWINGS VERTICAL HEAVY AND HORIZONTAL LIGHT -╋ VH 254B 9547 BOX DRAWINGS HEAVY VERTICAL AND HORIZONTAL -╱ FD 2571 9585 BOX DRAWINGS LIGHT DIAGONAL UPPER RIGHT TO LOWER LEFT -╲ BD 2572 9586 BOX DRAWINGS LIGHT DIAGONAL UPPER LEFT TO LOWER RIGHT -▀ TB 2580 9600 UPPER HALF BLOCK -▄ LB 2584 9604 LOWER HALF BLOCK -█ FB 2588 9608 FULL BLOCK -▌ lB 258C 9612 LEFT HALF BLOCK -▐ RB 2590 9616 RIGHT HALF BLOCK -░ .S 2591 9617 LIGHT SHADE -▒ :S 2592 9618 MEDIUM SHADE -▓ ?S 2593 9619 DARK SHADE -■ fS 25A0 9632 BLACK SQUARE -□ OS 25A1 9633 WHITE SQUARE -▢ RO 25A2 9634 WHITE SQUARE WITH ROUNDED CORNERS -▣ Rr 25A3 9635 WHITE SQUARE CONTAINING BLACK SMALL SQUARE -▤ RF 25A4 9636 SQUARE WITH HORIZONTAL FILL -▥ RY 25A5 9637 SQUARE WITH VERTICAL FILL -▦ RH 25A6 9638 SQUARE WITH ORTHOGONAL CROSSHATCH FILL -▧ RZ 25A7 9639 SQUARE WITH UPPER LEFT TO LOWER RIGHT FILL -▨ RK 25A8 9640 SQUARE WITH UPPER RIGHT TO LOWER LEFT FILL -▩ RX 25A9 9641 SQUARE WITH DIAGONAL CROSSHATCH FILL -▪ sB 25AA 9642 BLACK SMALL SQUARE -▬ SR 25AC 9644 BLACK RECTANGLE -▭ Or 25AD 9645 WHITE RECTANGLE -▲ UT 25B2 9650 BLACK UP-POINTING TRIANGLE -△ uT 25B3 9651 WHITE UP-POINTING TRIANGLE -▶ PR 25B6 9654 BLACK RIGHT-POINTING TRIANGLE -▷ Tr 25B7 9655 WHITE RIGHT-POINTING TRIANGLE -▼ Dt 25BC 9660 BLACK DOWN-POINTING TRIANGLE -▽ dT 25BD 9661 WHITE DOWN-POINTING TRIANGLE -◀ PL 25C0 9664 BLACK LEFT-POINTING TRIANGLE -◁ Tl 25C1 9665 WHITE LEFT-POINTING TRIANGLE -◆ Db 25C6 9670 BLACK DIAMOND -◇ Dw 25C7 9671 WHITE DIAMOND -◊ LZ 25CA 9674 LOZENGE -○ 0m 25CB 9675 WHITE CIRCLE -◎ 0o 25CE 9678 BULLSEYE -● 0M 25CF 9679 BLACK CIRCLE -◐ 0L 25D0 9680 CIRCLE WITH LEFT HALF BLACK -◑ 0R 25D1 9681 CIRCLE WITH RIGHT HALF BLACK -◘ Sn 25D8 9688 INVERSE BULLET -◙ Ic 25D9 9689 INVERSE WHITE CIRCLE -◢ Fd 25E2 9698 BLACK LOWER RIGHT TRIANGLE -◣ Bd 25E3 9699 BLACK LOWER LEFT TRIANGLE -★ *2 2605 9733 BLACK STAR -☆ *1 2606 9734 WHITE STAR -☜ <H 261C 9756 WHITE LEFT POINTING INDEX -☞ >H 261E 9758 WHITE RIGHT POINTING INDEX -☺ 0u 263A 9786 WHITE SMILING FACE -☻ 0U 263B 9787 BLACK SMILING FACE -☼ SU 263C 9788 WHITE SUN WITH RAYS -♀ Fm 2640 9792 FEMALE SIGN -♂ Ml 2642 9794 MALE SIGN -♠ cS 2660 9824 BLACK SPADE SUIT -♡ cH 2661 9825 WHITE HEART SUIT -♢ cD 2662 9826 WHITE DIAMOND SUIT -♣ cC 2663 9827 BLACK CLUB SUIT -♩ Md 2669 9833 QUARTER NOTE ` -♪ M8 266A 9834 EIGHTH NOTE ` -♫ M2 266B 9835 BEAMED EIGHTH NOTES -♭ Mb 266D 9837 MUSIC FLAT SIGN -♮ Mx 266E 9838 MUSIC NATURAL SIGN -♯ MX 266F 9839 MUSIC SHARP SIGN -✓ OK 2713 10003 CHECK MARK -✗ XX 2717 10007 BALLOT X -✠ -X 2720 10016 MALTESE CROSS - IS 3000 12288 IDEOGRAPHIC SPACE -、 ,_ 3001 12289 IDEOGRAPHIC COMMA -。 ._ 3002 12290 IDEOGRAPHIC FULL STOP -〃 +" 3003 12291 DITTO MARK -〄 +_ 3004 12292 JAPANESE INDUSTRIAL STANDARD SYMBOL -々 *_ 3005 12293 IDEOGRAPHIC ITERATION MARK -〆 ;_ 3006 12294 IDEOGRAPHIC CLOSING MARK -〇 0_ 3007 12295 IDEOGRAPHIC NUMBER ZERO -《 <+ 300A 12298 LEFT DOUBLE ANGLE BRACKET -》 >+ 300B 12299 RIGHT DOUBLE ANGLE BRACKET -「 <' 300C 12300 LEFT CORNER BRACKET -」 >' 300D 12301 RIGHT CORNER BRACKET -『 <" 300E 12302 LEFT WHITE CORNER BRACKET -』 >" 300F 12303 RIGHT WHITE CORNER BRACKET -【 (" 3010 12304 LEFT BLACK LENTICULAR BRACKET -】 )" 3011 12305 RIGHT BLACK LENTICULAR BRACKET -〒 =T 3012 12306 POSTAL MARK -〓 =_ 3013 12307 GETA MARK -〔 (' 3014 12308 LEFT TORTOISE SHELL BRACKET -〕 )' 3015 12309 RIGHT TORTOISE SHELL BRACKET -〖 (I 3016 12310 LEFT WHITE LENTICULAR BRACKET -〗 )I 3017 12311 RIGHT WHITE LENTICULAR BRACKET -〜 -? 301C 12316 WAVE DASH -ぁ A5 3041 12353 HIRAGANA LETTER SMALL A -あ a5 3042 12354 HIRAGANA LETTER A -ぃ I5 3043 12355 HIRAGANA LETTER SMALL I -い i5 3044 12356 HIRAGANA LETTER I -ぅ U5 3045 12357 HIRAGANA LETTER SMALL U -う u5 3046 12358 HIRAGANA LETTER U -ぇ E5 3047 12359 HIRAGANA LETTER SMALL E -え e5 3048 12360 HIRAGANA LETTER E -ぉ O5 3049 12361 HIRAGANA LETTER SMALL O -お o5 304A 12362 HIRAGANA LETTER O -か ka 304B 12363 HIRAGANA LETTER KA -が ga 304C 12364 HIRAGANA LETTER GA -き ki 304D 12365 HIRAGANA LETTER KI -ぎ gi 304E 12366 HIRAGANA LETTER GI -く ku 304F 12367 HIRAGANA LETTER KU -ぐ gu 3050 12368 HIRAGANA LETTER GU -け ke 3051 12369 HIRAGANA LETTER KE -げ ge 3052 12370 HIRAGANA LETTER GE -こ ko 3053 12371 HIRAGANA LETTER KO -ご go 3054 12372 HIRAGANA LETTER GO -さ sa 3055 12373 HIRAGANA LETTER SA -ざ za 3056 12374 HIRAGANA LETTER ZA -し si 3057 12375 HIRAGANA LETTER SI -じ zi 3058 12376 HIRAGANA LETTER ZI -す su 3059 12377 HIRAGANA LETTER SU -ず zu 305A 12378 HIRAGANA LETTER ZU -せ se 305B 12379 HIRAGANA LETTER SE -ぜ ze 305C 12380 HIRAGANA LETTER ZE -そ so 305D 12381 HIRAGANA LETTER SO -ぞ zo 305E 12382 HIRAGANA LETTER ZO -た ta 305F 12383 HIRAGANA LETTER TA -だ da 3060 12384 HIRAGANA LETTER DA -ち ti 3061 12385 HIRAGANA LETTER TI -ぢ di 3062 12386 HIRAGANA LETTER DI -っ tU 3063 12387 HIRAGANA LETTER SMALL TU -つ tu 3064 12388 HIRAGANA LETTER TU -づ du 3065 12389 HIRAGANA LETTER DU -て te 3066 12390 HIRAGANA LETTER TE -で de 3067 12391 HIRAGANA LETTER DE -と to 3068 12392 HIRAGANA LETTER TO -ど do 3069 12393 HIRAGANA LETTER DO -な na 306A 12394 HIRAGANA LETTER NA -に ni 306B 12395 HIRAGANA LETTER NI -ぬ nu 306C 12396 HIRAGANA LETTER NU -ね ne 306D 12397 HIRAGANA LETTER NE -の no 306E 12398 HIRAGANA LETTER NO -は ha 306F 12399 HIRAGANA LETTER HA -ば ba 3070 12400 HIRAGANA LETTER BA -ぱ pa 3071 12401 HIRAGANA LETTER PA -ひ hi 3072 12402 HIRAGANA LETTER HI -び bi 3073 12403 HIRAGANA LETTER BI -ぴ pi 3074 12404 HIRAGANA LETTER PI -ふ hu 3075 12405 HIRAGANA LETTER HU -ぶ bu 3076 12406 HIRAGANA LETTER BU -ぷ pu 3077 12407 HIRAGANA LETTER PU -へ he 3078 12408 HIRAGANA LETTER HE -べ be 3079 12409 HIRAGANA LETTER BE -ぺ pe 307A 12410 HIRAGANA LETTER PE -ほ ho 307B 12411 HIRAGANA LETTER HO -ぼ bo 307C 12412 HIRAGANA LETTER BO -ぽ po 307D 12413 HIRAGANA LETTER PO -ま ma 307E 12414 HIRAGANA LETTER MA -み mi 307F 12415 HIRAGANA LETTER MI -む mu 3080 12416 HIRAGANA LETTER MU -め me 3081 12417 HIRAGANA LETTER ME -も mo 3082 12418 HIRAGANA LETTER MO -ゃ yA 3083 12419 HIRAGANA LETTER SMALL YA -や ya 3084 12420 HIRAGANA LETTER YA -ゅ yU 3085 12421 HIRAGANA LETTER SMALL YU -ゆ yu 3086 12422 HIRAGANA LETTER YU -ょ yO 3087 12423 HIRAGANA LETTER SMALL YO -よ yo 3088 12424 HIRAGANA LETTER YO -ら ra 3089 12425 HIRAGANA LETTER RA -り ri 308A 12426 HIRAGANA LETTER RI -る ru 308B 12427 HIRAGANA LETTER RU -れ re 308C 12428 HIRAGANA LETTER RE -ろ ro 308D 12429 HIRAGANA LETTER RO -ゎ wA 308E 12430 HIRAGANA LETTER SMALL WA -わ wa 308F 12431 HIRAGANA LETTER WA -ゐ wi 3090 12432 HIRAGANA LETTER WI -ゑ we 3091 12433 HIRAGANA LETTER WE -を wo 3092 12434 HIRAGANA LETTER WO -ん n5 3093 12435 HIRAGANA LETTER N ` -ゔ vu 3094 12436 HIRAGANA LETTER VU -゛ "5 309B 12443 KATAKANA-HIRAGANA VOICED SOUND MARK -゜ 05 309C 12444 KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK -ゝ *5 309D 12445 HIRAGANA ITERATION MARK -ゞ +5 309E 12446 HIRAGANA VOICED ITERATION MARK -ァ a6 30A1 12449 KATAKANA LETTER SMALL A -ア A6 30A2 12450 KATAKANA LETTER A -ィ i6 30A3 12451 KATAKANA LETTER SMALL I -イ I6 30A4 12452 KATAKANA LETTER I -ゥ u6 30A5 12453 KATAKANA LETTER SMALL U -ウ U6 30A6 12454 KATAKANA LETTER U -ェ e6 30A7 12455 KATAKANA LETTER SMALL E -エ E6 30A8 12456 KATAKANA LETTER E -ォ o6 30A9 12457 KATAKANA LETTER SMALL O -オ O6 30AA 12458 KATAKANA LETTER O -カ Ka 30AB 12459 KATAKANA LETTER KA -ガ Ga 30AC 12460 KATAKANA LETTER GA -キ Ki 30AD 12461 KATAKANA LETTER KI -ギ Gi 30AE 12462 KATAKANA LETTER GI -ク Ku 30AF 12463 KATAKANA LETTER KU -グ Gu 30B0 12464 KATAKANA LETTER GU -ケ Ke 30B1 12465 KATAKANA LETTER KE -ゲ Ge 30B2 12466 KATAKANA LETTER GE -コ Ko 30B3 12467 KATAKANA LETTER KO -ゴ Go 30B4 12468 KATAKANA LETTER GO -サ Sa 30B5 12469 KATAKANA LETTER SA -ザ Za 30B6 12470 KATAKANA LETTER ZA -シ Si 30B7 12471 KATAKANA LETTER SI -ジ Zi 30B8 12472 KATAKANA LETTER ZI -ス Su 30B9 12473 KATAKANA LETTER SU -ズ Zu 30BA 12474 KATAKANA LETTER ZU -セ Se 30BB 12475 KATAKANA LETTER SE -ゼ Ze 30BC 12476 KATAKANA LETTER ZE -ソ So 30BD 12477 KATAKANA LETTER SO -ゾ Zo 30BE 12478 KATAKANA LETTER ZO -タ Ta 30BF 12479 KATAKANA LETTER TA -ダ Da 30C0 12480 KATAKANA LETTER DA -チ Ti 30C1 12481 KATAKANA LETTER TI -ヂ Di 30C2 12482 KATAKANA LETTER DI -ッ TU 30C3 12483 KATAKANA LETTER SMALL TU -ツ Tu 30C4 12484 KATAKANA LETTER TU -ヅ Du 30C5 12485 KATAKANA LETTER DU -テ Te 30C6 12486 KATAKANA LETTER TE -デ De 30C7 12487 KATAKANA LETTER DE -ト To 30C8 12488 KATAKANA LETTER TO -ド Do 30C9 12489 KATAKANA LETTER DO -ナ Na 30CA 12490 KATAKANA LETTER NA -ニ Ni 30CB 12491 KATAKANA LETTER NI -ヌ Nu 30CC 12492 KATAKANA LETTER NU -ネ Ne 30CD 12493 KATAKANA LETTER NE -ノ No 30CE 12494 KATAKANA LETTER NO -ハ Ha 30CF 12495 KATAKANA LETTER HA -バ Ba 30D0 12496 KATAKANA LETTER BA -パ Pa 30D1 12497 KATAKANA LETTER PA -ヒ Hi 30D2 12498 KATAKANA LETTER HI -ビ Bi 30D3 12499 KATAKANA LETTER BI -ピ Pi 30D4 12500 KATAKANA LETTER PI -フ Hu 30D5 12501 KATAKANA LETTER HU -ブ Bu 30D6 12502 KATAKANA LETTER BU -プ Pu 30D7 12503 KATAKANA LETTER PU -ヘ He 30D8 12504 KATAKANA LETTER HE -ベ Be 30D9 12505 KATAKANA LETTER BE -ペ Pe 30DA 12506 KATAKANA LETTER PE -ホ Ho 30DB 12507 KATAKANA LETTER HO -ボ Bo 30DC 12508 KATAKANA LETTER BO -ポ Po 30DD 12509 KATAKANA LETTER PO -マ Ma 30DE 12510 KATAKANA LETTER MA -ミ Mi 30DF 12511 KATAKANA LETTER MI -ム Mu 30E0 12512 KATAKANA LETTER MU -メ Me 30E1 12513 KATAKANA LETTER ME -モ Mo 30E2 12514 KATAKANA LETTER MO -ャ YA 30E3 12515 KATAKANA LETTER SMALL YA -ヤ Ya 30E4 12516 KATAKANA LETTER YA -ュ YU 30E5 12517 KATAKANA LETTER SMALL YU -ユ Yu 30E6 12518 KATAKANA LETTER YU -ョ YO 30E7 12519 KATAKANA LETTER SMALL YO -ヨ Yo 30E8 12520 KATAKANA LETTER YO -ラ Ra 30E9 12521 KATAKANA LETTER RA -リ Ri 30EA 12522 KATAKANA LETTER RI -ル Ru 30EB 12523 KATAKANA LETTER RU -レ Re 30EC 12524 KATAKANA LETTER RE -ロ Ro 30ED 12525 KATAKANA LETTER RO -ヮ WA 30EE 12526 KATAKANA LETTER SMALL WA -ワ Wa 30EF 12527 KATAKANA LETTER WA -ヰ Wi 30F0 12528 KATAKANA LETTER WI -ヱ We 30F1 12529 KATAKANA LETTER WE -ヲ Wo 30F2 12530 KATAKANA LETTER WO -ン N6 30F3 12531 KATAKANA LETTER N ` -ヴ Vu 30F4 12532 KATAKANA LETTER VU -ヵ KA 30F5 12533 KATAKANA LETTER SMALL KA -ヶ KE 30F6 12534 KATAKANA LETTER SMALL KE -ヷ Va 30F7 12535 KATAKANA LETTER VA -ヸ Vi 30F8 12536 KATAKANA LETTER VI -ヹ Ve 30F9 12537 KATAKANA LETTER VE -ヺ Vo 30FA 12538 KATAKANA LETTER VO -・ .6 30FB 12539 KATAKANA MIDDLE DOT -ー -6 30FC 12540 KATAKANA-HIRAGANA PROLONGED SOUND MARK -ヽ *6 30FD 12541 KATAKANA ITERATION MARK -ヾ +6 30FE 12542 KATAKANA VOICED ITERATION MARK -ㄅ b4 3105 12549 BOPOMOFO LETTER B -ㄆ p4 3106 12550 BOPOMOFO LETTER P -ㄇ m4 3107 12551 BOPOMOFO LETTER M -ㄈ f4 3108 12552 BOPOMOFO LETTER F -ㄉ d4 3109 12553 BOPOMOFO LETTER D -ㄊ t4 310A 12554 BOPOMOFO LETTER T -ㄋ n4 310B 12555 BOPOMOFO LETTER N ` -ㄌ l4 310C 12556 BOPOMOFO LETTER L -ㄍ g4 310D 12557 BOPOMOFO LETTER G -ㄎ k4 310E 12558 BOPOMOFO LETTER K -ㄏ h4 310F 12559 BOPOMOFO LETTER H -ㄐ j4 3110 12560 BOPOMOFO LETTER J -ㄑ q4 3111 12561 BOPOMOFO LETTER Q -ㄒ x4 3112 12562 BOPOMOFO LETTER X -ㄓ zh 3113 12563 BOPOMOFO LETTER ZH -ㄔ ch 3114 12564 BOPOMOFO LETTER CH -ㄕ sh 3115 12565 BOPOMOFO LETTER SH -ㄖ r4 3116 12566 BOPOMOFO LETTER R -ㄗ z4 3117 12567 BOPOMOFO LETTER Z -ㄘ c4 3118 12568 BOPOMOFO LETTER C -ㄙ s4 3119 12569 BOPOMOFO LETTER S -ㄚ a4 311A 12570 BOPOMOFO LETTER A -ㄛ o4 311B 12571 BOPOMOFO LETTER O -ㄜ e4 311C 12572 BOPOMOFO LETTER E -ㄞ ai 311E 12574 BOPOMOFO LETTER AI -ㄟ ei 311F 12575 BOPOMOFO LETTER EI -ㄠ au 3120 12576 BOPOMOFO LETTER AU -ㄡ ou 3121 12577 BOPOMOFO LETTER OU -ㄢ an 3122 12578 BOPOMOFO LETTER AN -ㄣ en 3123 12579 BOPOMOFO LETTER EN -ㄤ aN 3124 12580 BOPOMOFO LETTER ANG -ㄥ eN 3125 12581 BOPOMOFO LETTER ENG -ㄦ er 3126 12582 BOPOMOFO LETTER ER -ㄧ i4 3127 12583 BOPOMOFO LETTER I -ㄨ u4 3128 12584 BOPOMOFO LETTER U -ㄩ iu 3129 12585 BOPOMOFO LETTER IU -ㄪ v4 312A 12586 BOPOMOFO LETTER V -ㄫ nG 312B 12587 BOPOMOFO LETTER NG -ㄬ gn 312C 12588 BOPOMOFO LETTER GN -㈠ 1c 3220 12832 PARENTHESIZED IDEOGRAPH ONE -㈡ 2c 3221 12833 PARENTHESIZED IDEOGRAPH TWO -㈢ 3c 3222 12834 PARENTHESIZED IDEOGRAPH THREE -㈣ 4c 3223 12835 PARENTHESIZED IDEOGRAPH FOUR -㈤ 5c 3224 12836 PARENTHESIZED IDEOGRAPH FIVE -㈥ 6c 3225 12837 PARENTHESIZED IDEOGRAPH SIX -㈦ 7c 3226 12838 PARENTHESIZED IDEOGRAPH SEVEN -㈧ 8c 3227 12839 PARENTHESIZED IDEOGRAPH EIGHT -㈨ 9c 3228 12840 PARENTHESIZED IDEOGRAPH NINE -ff ff FB00 64256 LATIN SMALL LIGATURE FF -fi fi FB01 64257 LATIN SMALL LIGATURE FI -fl fl FB02 64258 LATIN SMALL LIGATURE FL -ſt ft FB05 64261 LATIN SMALL LIGATURE LONG S T -st st FB06 64262 LATIN SMALL LIGATURE ST +> + char digraph hex dec official name + ^@ NU 0x00 0 NULL (NUL) + ^A SH 0x01 1 START OF HEADING (SOH) + ^B SX 0x02 2 START OF TEXT (STX) + ^C EX 0x03 3 END OF TEXT (ETX) + ^D ET 0x04 4 END OF TRANSMISSION (EOT) + ^E EQ 0x05 5 ENQUIRY (ENQ) + ^F AK 0x06 6 ACKNOWLEDGE (ACK) + ^G BL 0x07 7 BELL (BEL) + ^H BS 0x08 8 BACKSPACE (BS) + ^I HT 0x09 9 CHARACTER TABULATION (HT) + ^@ LF 0x0a 10 LINE FEED (LF) + ^K VT 0x0b 11 LINE TABULATION (VT) + ^L FF 0x0c 12 FORM FEED (FF) + ^M CR 0x0d 13 CARRIAGE RETURN (CR) + ^N SO 0x0e 14 SHIFT OUT (SO) + ^O SI 0x0f 15 SHIFT IN (SI) + ^P DL 0x10 16 DATALINK ESCAPE (DLE) + ^Q D1 0x11 17 DEVICE CONTROL ONE (DC1) + ^R D2 0x12 18 DEVICE CONTROL TWO (DC2) + ^S D3 0x13 19 DEVICE CONTROL THREE (DC3) + ^T D4 0x14 20 DEVICE CONTROL FOUR (DC4) + ^U NK 0x15 21 NEGATIVE ACKNOWLEDGE (NAK) + ^V SY 0x16 22 SYNCHRONOUS IDLE (SYN) + ^W EB 0x17 23 END OF TRANSMISSION BLOCK (ETB) + ^X CN 0x18 24 CANCEL (CAN) + ^Y EM 0x19 25 END OF MEDIUM (EM) + ^Z SB 0x1a 26 SUBSTITUTE (SUB) + ^[ EC 0x1b 27 ESCAPE (ESC) + ^\ FS 0x1c 28 FILE SEPARATOR (IS4) + ^] GS 0x1d 29 GROUP SEPARATOR (IS3) + ^^ RS 0x1e 30 RECORD SEPARATOR (IS2) + ^_ US 0x1f 31 UNIT SEPARATOR (IS1) + SP 0x20 32 SPACE + # Nb 0x23 35 NUMBER SIGN + $ DO 0x24 36 DOLLAR SIGN + @ At 0x40 64 COMMERCIAL AT + [ <( 0x5b 91 LEFT SQUARE BRACKET + \ // 0x5c 92 REVERSE SOLIDUS + ] )> 0x5d 93 RIGHT SQUARE BRACKET + ^ '> 0x5e 94 CIRCUMFLEX ACCENT + ` '! 0x60 96 GRAVE ACCENT + { (! 0x7b 123 LEFT CURLY BRACKET + | !! 0x7c 124 VERTICAL LINE + } !) 0x7d 125 RIGHT CURLY BRACKET + ~ '? 0x7e 126 TILDE + ^? DT 0x7f 127 DELETE (DEL) + ~@ PA 0x80 128 PADDING CHARACTER (PAD) + ~A HO 0x81 129 HIGH OCTET PRESET (HOP) + ~B BH 0x82 130 BREAK PERMITTED HERE (BPH) + ~C NH 0x83 131 NO BREAK HERE (NBH) + ~D IN 0x84 132 INDEX (IND) + ~E NL 0x85 133 NEXT LINE (NEL) + ~F SA 0x86 134 START OF SELECTED AREA (SSA) + ~G ES 0x87 135 END OF SELECTED AREA (ESA) + ~H HS 0x88 136 CHARACTER TABULATION SET (HTS) + ~I HJ 0x89 137 CHARACTER TABULATION WITH JUSTIFICATION (HTJ) + ~J VS 0x8a 138 LINE TABULATION SET (VTS) + ~K PD 0x8b 139 PARTIAL LINE FORWARD (PLD) + ~L PU 0x8c 140 PARTIAL LINE BACKWARD (PLU) + ~M RI 0x8d 141 REVERSE LINE FEED (RI) + ~N S2 0x8e 142 SINGLE-SHIFT TWO (SS2) + ~O S3 0x8f 143 SINGLE-SHIFT THREE (SS3) + ~P DC 0x90 144 DEVICE CONTROL STRING (DCS) + ~Q P1 0x91 145 PRIVATE USE ONE (PU1) + ~R P2 0x92 146 PRIVATE USE TWO (PU2) + ~S TS 0x93 147 SET TRANSMIT STATE (STS) + ~T CC 0x94 148 CANCEL CHARACTER (CCH) + ~U MW 0x95 149 MESSAGE WAITING (MW) + ~V SG 0x96 150 START OF GUARDED AREA (SPA) + ~W EG 0x97 151 END OF GUARDED AREA (EPA) + ~X SS 0x98 152 START OF STRING (SOS) + ~Y GC 0x99 153 SINGLE GRAPHIC CHARACTER INTRODUCER (SGCI) + ~Z SC 0x9a 154 SINGLE CHARACTER INTRODUCER (SCI) + ~[ CI 0x9b 155 CONTROL SEQUENCE INTRODUCER (CSI) + ~\ ST 0x9c 156 STRING TERMINATOR (ST) + ~] OC 0x9d 157 OPERATING SYSTEM COMMAND (OSC) + ~^ PM 0x9e 158 PRIVACY MESSAGE (PM) + ~_ AC 0x9f 159 APPLICATION PROGRAM COMMAND (APC) + | NS 0xa0 160 NO-BREAK SPACE + ¡ !I 0xa1 161 INVERTED EXCLAMATION MARK + ¢ Ct 0xa2 162 CENT SIGN + £ Pd 0xa3 163 POUND SIGN + ¤ Cu 0xa4 164 CURRENCY SIGN + ¥ Ye 0xa5 165 YEN SIGN + ¦ BB 0xa6 166 BROKEN BAR + § SE 0xa7 167 SECTION SIGN + ¨ ': 0xa8 168 DIAERESIS + © Co 0xa9 169 COPYRIGHT SIGN + ª -a 0xaa 170 FEMININE ORDINAL INDICATOR + « << 0xab 171 LEFT-POINTING DOUBLE ANGLE QUOTATION MARK + ¬ NO 0xac 172 NOT SIGN + -- 0xad 173 SOFT HYPHEN + ® Rg 0xae 174 REGISTERED SIGN + ¯ 'm 0xaf 175 MACRON + ° DG 0xb0 176 DEGREE SIGN + ± +- 0xb1 177 PLUS-MINUS SIGN + ² 2S 0xb2 178 SUPERSCRIPT TWO + ³ 3S 0xb3 179 SUPERSCRIPT THREE + ´ '' 0xb4 180 ACUTE ACCENT + µ My 0xb5 181 MICRO SIGN + ¶ PI 0xb6 182 PILCROW SIGN + · .M 0xb7 183 MIDDLE DOT + ¸ ', 0xb8 184 CEDILLA + ¹ 1S 0xb9 185 SUPERSCRIPT ONE + º -o 0xba 186 MASCULINE ORDINAL INDICATOR + » >> 0xbb 187 RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK + ¼ 14 0xbc 188 VULGAR FRACTION ONE QUARTER + ½ 12 0xbd 189 VULGAR FRACTION ONE HALF + ¾ 34 0xbe 190 VULGAR FRACTION THREE QUARTERS + ¿ ?I 0xbf 191 INVERTED QUESTION MARK + À A! 0xc0 192 LATIN CAPITAL LETTER A WITH GRAVE + Á A' 0xc1 193 LATIN CAPITAL LETTER A WITH ACUTE +  A> 0xc2 194 LATIN CAPITAL LETTER A WITH CIRCUMFLEX + à A? 0xc3 195 LATIN CAPITAL LETTER A WITH TILDE + Ä A: 0xc4 196 LATIN CAPITAL LETTER A WITH DIAERESIS + Å AA 0xc5 197 LATIN CAPITAL LETTER A WITH RING ABOVE + Æ AE 0xc6 198 LATIN CAPITAL LETTER AE + Ç C, 0xc7 199 LATIN CAPITAL LETTER C WITH CEDILLA + È E! 0xc8 200 LATIN CAPITAL LETTER E WITH GRAVE + É E' 0xc9 201 LATIN CAPITAL LETTER E WITH ACUTE + Ê E> 0xca 202 LATIN CAPITAL LETTER E WITH CIRCUMFLEX + Ë E: 0xcb 203 LATIN CAPITAL LETTER E WITH DIAERESIS + Ì I! 0xcc 204 LATIN CAPITAL LETTER I WITH GRAVE + Í I' 0xcd 205 LATIN CAPITAL LETTER I WITH ACUTE + Î I> 0xce 206 LATIN CAPITAL LETTER I WITH CIRCUMFLEX + Ï I: 0xcf 207 LATIN CAPITAL LETTER I WITH DIAERESIS + Ð D- 0xd0 208 LATIN CAPITAL LETTER ETH (Icelandic) + Ñ N? 0xd1 209 LATIN CAPITAL LETTER N WITH TILDE + Ò O! 0xd2 210 LATIN CAPITAL LETTER O WITH GRAVE + Ó O' 0xd3 211 LATIN CAPITAL LETTER O WITH ACUTE + Ô O> 0xd4 212 LATIN CAPITAL LETTER O WITH CIRCUMFLEX + Õ O? 0xd5 213 LATIN CAPITAL LETTER O WITH TILDE + Ö O: 0xd6 214 LATIN CAPITAL LETTER O WITH DIAERESIS + × *X 0xd7 215 MULTIPLICATION SIGN + Ø O/ 0xd8 216 LATIN CAPITAL LETTER O WITH STROKE + Ù U! 0xd9 217 LATIN CAPITAL LETTER U WITH GRAVE + Ú U' 0xda 218 LATIN CAPITAL LETTER U WITH ACUTE + Û U> 0xdb 219 LATIN CAPITAL LETTER U WITH CIRCUMFLEX + Ü U: 0xdc 220 LATIN CAPITAL LETTER U WITH DIAERESIS + Ý Y' 0xdd 221 LATIN CAPITAL LETTER Y WITH ACUTE + Þ TH 0xde 222 LATIN CAPITAL LETTER THORN (Icelandic) + ß ss 0xdf 223 LATIN SMALL LETTER SHARP S (German) + à a! 0xe0 224 LATIN SMALL LETTER A WITH GRAVE + á a' 0xe1 225 LATIN SMALL LETTER A WITH ACUTE + â a> 0xe2 226 LATIN SMALL LETTER A WITH CIRCUMFLEX + ã a? 0xe3 227 LATIN SMALL LETTER A WITH TILDE + ä a: 0xe4 228 LATIN SMALL LETTER A WITH DIAERESIS + å aa 0xe5 229 LATIN SMALL LETTER A WITH RING ABOVE + æ ae 0xe6 230 LATIN SMALL LETTER AE + ç c, 0xe7 231 LATIN SMALL LETTER C WITH CEDILLA + è e! 0xe8 232 LATIN SMALL LETTER E WITH GRAVE + é e' 0xe9 233 LATIN SMALL LETTER E WITH ACUTE + ê e> 0xea 234 LATIN SMALL LETTER E WITH CIRCUMFLEX + ë e: 0xeb 235 LATIN SMALL LETTER E WITH DIAERESIS + ì i! 0xec 236 LATIN SMALL LETTER I WITH GRAVE + í i' 0xed 237 LATIN SMALL LETTER I WITH ACUTE + î i> 0xee 238 LATIN SMALL LETTER I WITH CIRCUMFLEX + ï i: 0xef 239 LATIN SMALL LETTER I WITH DIAERESIS + ð d- 0xf0 240 LATIN SMALL LETTER ETH (Icelandic) + ñ n? 0xf1 241 LATIN SMALL LETTER N WITH TILDE + ò o! 0xf2 242 LATIN SMALL LETTER O WITH GRAVE + ó o' 0xf3 243 LATIN SMALL LETTER O WITH ACUTE + ô o> 0xf4 244 LATIN SMALL LETTER O WITH CIRCUMFLEX + õ o? 0xf5 245 LATIN SMALL LETTER O WITH TILDE + ö o: 0xf6 246 LATIN SMALL LETTER O WITH DIAERESIS + ÷ -: 0xf7 247 DIVISION SIGN + ø o/ 0xf8 248 LATIN SMALL LETTER O WITH STROKE + ù u! 0xf9 249 LATIN SMALL LETTER U WITH GRAVE + ú u' 0xfa 250 LATIN SMALL LETTER U WITH ACUTE + û u> 0xfb 251 LATIN SMALL LETTER U WITH CIRCUMFLEX + ü u: 0xfc 252 LATIN SMALL LETTER U WITH DIAERESIS + ý y' 0xfd 253 LATIN SMALL LETTER Y WITH ACUTE + þ th 0xfe 254 LATIN SMALL LETTER THORN (Icelandic) + ÿ y: 0xff 255 LATIN SMALL LETTER Y WITH DIAERESIS + Ā A- 0100 0256 LATIN CAPITAL LETTER A WITH MACRON + ā a- 0101 0257 LATIN SMALL LETTER A WITH MACRON + Ă A( 0102 0258 LATIN CAPITAL LETTER A WITH BREVE + ă a( 0103 0259 LATIN SMALL LETTER A WITH BREVE + Ą A; 0104 0260 LATIN CAPITAL LETTER A WITH OGONEK + ą a; 0105 0261 LATIN SMALL LETTER A WITH OGONEK + Ć C' 0106 0262 LATIN CAPITAL LETTER C WITH ACUTE + ć c' 0107 0263 LATIN SMALL LETTER C WITH ACUTE + Ĉ C> 0108 0264 LATIN CAPITAL LETTER C WITH CIRCUMFLEX + ĉ c> 0109 0265 LATIN SMALL LETTER C WITH CIRCUMFLEX + Ċ C. 010A 0266 LATIN CAPITAL LETTER C WITH DOT ABOVE + ċ c. 010B 0267 LATIN SMALL LETTER C WITH DOT ABOVE + Č C< 010C 0268 LATIN CAPITAL LETTER C WITH CARON + č c< 010D 0269 LATIN SMALL LETTER C WITH CARON + Ď D< 010E 0270 LATIN CAPITAL LETTER D WITH CARON + ď d< 010F 0271 LATIN SMALL LETTER D WITH CARON + Đ D/ 0110 0272 LATIN CAPITAL LETTER D WITH STROKE + đ d/ 0111 0273 LATIN SMALL LETTER D WITH STROKE + Ē E- 0112 0274 LATIN CAPITAL LETTER E WITH MACRON + ē e- 0113 0275 LATIN SMALL LETTER E WITH MACRON + Ĕ E( 0114 0276 LATIN CAPITAL LETTER E WITH BREVE + ĕ e( 0115 0277 LATIN SMALL LETTER E WITH BREVE + Ė E. 0116 0278 LATIN CAPITAL LETTER E WITH DOT ABOVE + ė e. 0117 0279 LATIN SMALL LETTER E WITH DOT ABOVE + Ę E; 0118 0280 LATIN CAPITAL LETTER E WITH OGONEK + ę e; 0119 0281 LATIN SMALL LETTER E WITH OGONEK + Ě E< 011A 0282 LATIN CAPITAL LETTER E WITH CARON + ě e< 011B 0283 LATIN SMALL LETTER E WITH CARON + Ĝ G> 011C 0284 LATIN CAPITAL LETTER G WITH CIRCUMFLEX + ĝ g> 011D 0285 LATIN SMALL LETTER G WITH CIRCUMFLEX + Ğ G( 011E 0286 LATIN CAPITAL LETTER G WITH BREVE + ğ g( 011F 0287 LATIN SMALL LETTER G WITH BREVE + Ġ G. 0120 0288 LATIN CAPITAL LETTER G WITH DOT ABOVE + ġ g. 0121 0289 LATIN SMALL LETTER G WITH DOT ABOVE + Ģ G, 0122 0290 LATIN CAPITAL LETTER G WITH CEDILLA + ģ g, 0123 0291 LATIN SMALL LETTER G WITH CEDILLA + Ĥ H> 0124 0292 LATIN CAPITAL LETTER H WITH CIRCUMFLEX + ĥ h> 0125 0293 LATIN SMALL LETTER H WITH CIRCUMFLEX + Ħ H/ 0126 0294 LATIN CAPITAL LETTER H WITH STROKE + ħ h/ 0127 0295 LATIN SMALL LETTER H WITH STROKE + Ĩ I? 0128 0296 LATIN CAPITAL LETTER I WITH TILDE + ĩ i? 0129 0297 LATIN SMALL LETTER I WITH TILDE + Ī I- 012A 0298 LATIN CAPITAL LETTER I WITH MACRON + ī i- 012B 0299 LATIN SMALL LETTER I WITH MACRON + Ĭ I( 012C 0300 LATIN CAPITAL LETTER I WITH BREVE + ĭ i( 012D 0301 LATIN SMALL LETTER I WITH BREVE + Į I; 012E 0302 LATIN CAPITAL LETTER I WITH OGONEK + į i; 012F 0303 LATIN SMALL LETTER I WITH OGONEK + İ I. 0130 0304 LATIN CAPITAL LETTER I WITH DOT ABOVE + ı i. 0131 0305 LATIN SMALL LETTER DOTLESS I + IJ IJ 0132 0306 LATIN CAPITAL LIGATURE IJ + ij ij 0133 0307 LATIN SMALL LIGATURE IJ + Ĵ J> 0134 0308 LATIN CAPITAL LETTER J WITH CIRCUMFLEX + ĵ j> 0135 0309 LATIN SMALL LETTER J WITH CIRCUMFLEX + Ķ K, 0136 0310 LATIN CAPITAL LETTER K WITH CEDILLA + ķ k, 0137 0311 LATIN SMALL LETTER K WITH CEDILLA + ĸ kk 0138 0312 LATIN SMALL LETTER KRA + Ĺ L' 0139 0313 LATIN CAPITAL LETTER L WITH ACUTE + ĺ l' 013A 0314 LATIN SMALL LETTER L WITH ACUTE + Ļ L, 013B 0315 LATIN CAPITAL LETTER L WITH CEDILLA + ļ l, 013C 0316 LATIN SMALL LETTER L WITH CEDILLA + Ľ L< 013D 0317 LATIN CAPITAL LETTER L WITH CARON + ľ l< 013E 0318 LATIN SMALL LETTER L WITH CARON + Ŀ L. 013F 0319 LATIN CAPITAL LETTER L WITH MIDDLE DOT + ŀ l. 0140 0320 LATIN SMALL LETTER L WITH MIDDLE DOT + Ł L/ 0141 0321 LATIN CAPITAL LETTER L WITH STROKE + ł l/ 0142 0322 LATIN SMALL LETTER L WITH STROKE + Ń N' 0143 0323 LATIN CAPITAL LETTER N WITH ACUTE ` + ń n' 0144 0324 LATIN SMALL LETTER N WITH ACUTE ` + Ņ N, 0145 0325 LATIN CAPITAL LETTER N WITH CEDILLA ` + ņ n, 0146 0326 LATIN SMALL LETTER N WITH CEDILLA ` + Ň N< 0147 0327 LATIN CAPITAL LETTER N WITH CARON ` + ň n< 0148 0328 LATIN SMALL LETTER N WITH CARON ` + ʼn 'n 0149 0329 LATIN SMALL LETTER N PRECEDED BY APOSTROPHE ` + Ŋ NG 014A 0330 LATIN CAPITAL LETTER ENG + ŋ ng 014B 0331 LATIN SMALL LETTER ENG + Ō O- 014C 0332 LATIN CAPITAL LETTER O WITH MACRON + ō o- 014D 0333 LATIN SMALL LETTER O WITH MACRON + Ŏ O( 014E 0334 LATIN CAPITAL LETTER O WITH BREVE + ŏ o( 014F 0335 LATIN SMALL LETTER O WITH BREVE + Ő O" 0150 0336 LATIN CAPITAL LETTER O WITH DOUBLE ACUTE + ő o" 0151 0337 LATIN SMALL LETTER O WITH DOUBLE ACUTE + Œ OE 0152 0338 LATIN CAPITAL LIGATURE OE + œ oe 0153 0339 LATIN SMALL LIGATURE OE + Ŕ R' 0154 0340 LATIN CAPITAL LETTER R WITH ACUTE + ŕ r' 0155 0341 LATIN SMALL LETTER R WITH ACUTE + Ŗ R, 0156 0342 LATIN CAPITAL LETTER R WITH CEDILLA + ŗ r, 0157 0343 LATIN SMALL LETTER R WITH CEDILLA + Ř R< 0158 0344 LATIN CAPITAL LETTER R WITH CARON + ř r< 0159 0345 LATIN SMALL LETTER R WITH CARON + Ś S' 015A 0346 LATIN CAPITAL LETTER S WITH ACUTE + ś s' 015B 0347 LATIN SMALL LETTER S WITH ACUTE + Ŝ S> 015C 0348 LATIN CAPITAL LETTER S WITH CIRCUMFLEX + ŝ s> 015D 0349 LATIN SMALL LETTER S WITH CIRCUMFLEX + Ş S, 015E 0350 LATIN CAPITAL LETTER S WITH CEDILLA + ş s, 015F 0351 LATIN SMALL LETTER S WITH CEDILLA + Š S< 0160 0352 LATIN CAPITAL LETTER S WITH CARON + š s< 0161 0353 LATIN SMALL LETTER S WITH CARON + Ţ T, 0162 0354 LATIN CAPITAL LETTER T WITH CEDILLA + ţ t, 0163 0355 LATIN SMALL LETTER T WITH CEDILLA + Ť T< 0164 0356 LATIN CAPITAL LETTER T WITH CARON + ť t< 0165 0357 LATIN SMALL LETTER T WITH CARON + Ŧ T/ 0166 0358 LATIN CAPITAL LETTER T WITH STROKE + ŧ t/ 0167 0359 LATIN SMALL LETTER T WITH STROKE + Ũ U? 0168 0360 LATIN CAPITAL LETTER U WITH TILDE + ũ u? 0169 0361 LATIN SMALL LETTER U WITH TILDE + Ū U- 016A 0362 LATIN CAPITAL LETTER U WITH MACRON + ū u- 016B 0363 LATIN SMALL LETTER U WITH MACRON + Ŭ U( 016C 0364 LATIN CAPITAL LETTER U WITH BREVE + ŭ u( 016D 0365 LATIN SMALL LETTER U WITH BREVE + Ů U0 016E 0366 LATIN CAPITAL LETTER U WITH RING ABOVE + ů u0 016F 0367 LATIN SMALL LETTER U WITH RING ABOVE + Ű U" 0170 0368 LATIN CAPITAL LETTER U WITH DOUBLE ACUTE + ű u" 0171 0369 LATIN SMALL LETTER U WITH DOUBLE ACUTE + Ų U; 0172 0370 LATIN CAPITAL LETTER U WITH OGONEK + ų u; 0173 0371 LATIN SMALL LETTER U WITH OGONEK + Ŵ W> 0174 0372 LATIN CAPITAL LETTER W WITH CIRCUMFLEX + ŵ w> 0175 0373 LATIN SMALL LETTER W WITH CIRCUMFLEX + Ŷ Y> 0176 0374 LATIN CAPITAL LETTER Y WITH CIRCUMFLEX + ŷ y> 0177 0375 LATIN SMALL LETTER Y WITH CIRCUMFLEX + Ÿ Y: 0178 0376 LATIN CAPITAL LETTER Y WITH DIAERESIS + Ź Z' 0179 0377 LATIN CAPITAL LETTER Z WITH ACUTE + ź z' 017A 0378 LATIN SMALL LETTER Z WITH ACUTE + Ż Z. 017B 0379 LATIN CAPITAL LETTER Z WITH DOT ABOVE + ż z. 017C 0380 LATIN SMALL LETTER Z WITH DOT ABOVE + Ž Z< 017D 0381 LATIN CAPITAL LETTER Z WITH CARON + ž z< 017E 0382 LATIN SMALL LETTER Z WITH CARON + Ơ O9 01A0 0416 LATIN CAPITAL LETTER O WITH HORN + ơ o9 01A1 0417 LATIN SMALL LETTER O WITH HORN + Ƣ OI 01A2 0418 LATIN CAPITAL LETTER OI + ƣ oi 01A3 0419 LATIN SMALL LETTER OI + Ʀ yr 01A6 0422 LATIN LETTER YR + Ư U9 01AF 0431 LATIN CAPITAL LETTER U WITH HORN + ư u9 01B0 0432 LATIN SMALL LETTER U WITH HORN + Ƶ Z/ 01B5 0437 LATIN CAPITAL LETTER Z WITH STROKE + ƶ z/ 01B6 0438 LATIN SMALL LETTER Z WITH STROKE + Ʒ ED 01B7 0439 LATIN CAPITAL LETTER EZH + Ǎ A< 01CD 0461 LATIN CAPITAL LETTER A WITH CARON + ǎ a< 01CE 0462 LATIN SMALL LETTER A WITH CARON + Ǐ I< 01CF 0463 LATIN CAPITAL LETTER I WITH CARON + ǐ i< 01D0 0464 LATIN SMALL LETTER I WITH CARON + Ǒ O< 01D1 0465 LATIN CAPITAL LETTER O WITH CARON + ǒ o< 01D2 0466 LATIN SMALL LETTER O WITH CARON + Ǔ U< 01D3 0467 LATIN CAPITAL LETTER U WITH CARON + ǔ u< 01D4 0468 LATIN SMALL LETTER U WITH CARON + Ǟ A1 01DE 0478 LATIN CAPITAL LETTER A WITH DIAERESIS AND MACRON + ǟ a1 01DF 0479 LATIN SMALL LETTER A WITH DIAERESIS AND MACRON + Ǡ A7 01E0 0480 LATIN CAPITAL LETTER A WITH DOT ABOVE AND MACRON + ǡ a7 01E1 0481 LATIN SMALL LETTER A WITH DOT ABOVE AND MACRON + Ǣ A3 01E2 0482 LATIN CAPITAL LETTER AE WITH MACRON + ǣ a3 01E3 0483 LATIN SMALL LETTER AE WITH MACRON + Ǥ G/ 01E4 0484 LATIN CAPITAL LETTER G WITH STROKE + ǥ g/ 01E5 0485 LATIN SMALL LETTER G WITH STROKE + Ǧ G< 01E6 0486 LATIN CAPITAL LETTER G WITH CARON + ǧ g< 01E7 0487 LATIN SMALL LETTER G WITH CARON + Ǩ K< 01E8 0488 LATIN CAPITAL LETTER K WITH CARON + ǩ k< 01E9 0489 LATIN SMALL LETTER K WITH CARON + Ǫ O; 01EA 0490 LATIN CAPITAL LETTER O WITH OGONEK + ǫ o; 01EB 0491 LATIN SMALL LETTER O WITH OGONEK + Ǭ O1 01EC 0492 LATIN CAPITAL LETTER O WITH OGONEK AND MACRON + ǭ o1 01ED 0493 LATIN SMALL LETTER O WITH OGONEK AND MACRON + Ǯ EZ 01EE 0494 LATIN CAPITAL LETTER EZH WITH CARON + ǯ ez 01EF 0495 LATIN SMALL LETTER EZH WITH CARON + ǰ j< 01F0 0496 LATIN SMALL LETTER J WITH CARON + Ǵ G' 01F4 0500 LATIN CAPITAL LETTER G WITH ACUTE + ǵ g' 01F5 0501 LATIN SMALL LETTER G WITH ACUTE + ʿ ;S 02BF 0703 MODIFIER LETTER LEFT HALF RING + ˇ '< 02C7 0711 CARON + ˘ '( 02D8 0728 BREVE + ˙ '. 02D9 0729 DOT ABOVE + ˚ '0 02DA 0730 RING ABOVE + ˛ '; 02DB 0731 OGONEK + ˝ '" 02DD 0733 DOUBLE ACUTE ACCENT + Ά A% 0386 0902 GREEK CAPITAL LETTER ALPHA WITH TONOS + Έ E% 0388 0904 GREEK CAPITAL LETTER EPSILON WITH TONOS + Ή Y% 0389 0905 GREEK CAPITAL LETTER ETA WITH TONOS + Ί I% 038A 0906 GREEK CAPITAL LETTER IOTA WITH TONOS + Ό O% 038C 0908 GREEK CAPITAL LETTER OMICRON WITH TONOS + Ύ U% 038E 0910 GREEK CAPITAL LETTER UPSILON WITH TONOS + Ώ W% 038F 0911 GREEK CAPITAL LETTER OMEGA WITH TONOS + ΐ i3 0390 0912 GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS + Α A* 0391 0913 GREEK CAPITAL LETTER ALPHA + Β B* 0392 0914 GREEK CAPITAL LETTER BETA + Γ G* 0393 0915 GREEK CAPITAL LETTER GAMMA + Δ D* 0394 0916 GREEK CAPITAL LETTER DELTA + Ε E* 0395 0917 GREEK CAPITAL LETTER EPSILON + Ζ Z* 0396 0918 GREEK CAPITAL LETTER ZETA + Η Y* 0397 0919 GREEK CAPITAL LETTER ETA + Θ H* 0398 0920 GREEK CAPITAL LETTER THETA + Ι I* 0399 0921 GREEK CAPITAL LETTER IOTA + Κ K* 039A 0922 GREEK CAPITAL LETTER KAPPA + Λ L* 039B 0923 GREEK CAPITAL LETTER LAMDA (aka LAMBDA) + Μ M* 039C 0924 GREEK CAPITAL LETTER MU + Ν N* 039D 0925 GREEK CAPITAL LETTER NU + Ξ C* 039E 0926 GREEK CAPITAL LETTER XI + Ο O* 039F 0927 GREEK CAPITAL LETTER OMICRON + Π P* 03A0 0928 GREEK CAPITAL LETTER PI + Ρ R* 03A1 0929 GREEK CAPITAL LETTER RHO + Σ S* 03A3 0931 GREEK CAPITAL LETTER SIGMA + Τ T* 03A4 0932 GREEK CAPITAL LETTER TAU + Υ U* 03A5 0933 GREEK CAPITAL LETTER UPSILON + Φ F* 03A6 0934 GREEK CAPITAL LETTER PHI + Χ X* 03A7 0935 GREEK CAPITAL LETTER CHI + Ψ Q* 03A8 0936 GREEK CAPITAL LETTER PSI + Ω W* 03A9 0937 GREEK CAPITAL LETTER OMEGA + Ϊ J* 03AA 0938 GREEK CAPITAL LETTER IOTA WITH DIALYTIKA + Ϋ V* 03AB 0939 GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA + ά a% 03AC 0940 GREEK SMALL LETTER ALPHA WITH TONOS + έ e% 03AD 0941 GREEK SMALL LETTER EPSILON WITH TONOS + ή y% 03AE 0942 GREEK SMALL LETTER ETA WITH TONOS + ί i% 03AF 0943 GREEK SMALL LETTER IOTA WITH TONOS + ΰ u3 03B0 0944 GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS + α a* 03B1 0945 GREEK SMALL LETTER ALPHA + β b* 03B2 0946 GREEK SMALL LETTER BETA + γ g* 03B3 0947 GREEK SMALL LETTER GAMMA + δ d* 03B4 0948 GREEK SMALL LETTER DELTA + ε e* 03B5 0949 GREEK SMALL LETTER EPSILON + ζ z* 03B6 0950 GREEK SMALL LETTER ZETA + η y* 03B7 0951 GREEK SMALL LETTER ETA + θ h* 03B8 0952 GREEK SMALL LETTER THETA + ι i* 03B9 0953 GREEK SMALL LETTER IOTA + κ k* 03BA 0954 GREEK SMALL LETTER KAPPA + λ l* 03BB 0955 GREEK SMALL LETTER LAMDA (aka LAMBDA) + μ m* 03BC 0956 GREEK SMALL LETTER MU + ν n* 03BD 0957 GREEK SMALL LETTER NU + ξ c* 03BE 0958 GREEK SMALL LETTER XI + ο o* 03BF 0959 GREEK SMALL LETTER OMICRON + π p* 03C0 0960 GREEK SMALL LETTER PI + ρ r* 03C1 0961 GREEK SMALL LETTER RHO + ς *s 03C2 0962 GREEK SMALL LETTER FINAL SIGMA + σ s* 03C3 0963 GREEK SMALL LETTER SIGMA + τ t* 03C4 0964 GREEK SMALL LETTER TAU + υ u* 03C5 0965 GREEK SMALL LETTER UPSILON + φ f* 03C6 0966 GREEK SMALL LETTER PHI + χ x* 03C7 0967 GREEK SMALL LETTER CHI + ψ q* 03C8 0968 GREEK SMALL LETTER PSI + ω w* 03C9 0969 GREEK SMALL LETTER OMEGA + ϊ j* 03CA 0970 GREEK SMALL LETTER IOTA WITH DIALYTIKA + ϋ v* 03CB 0971 GREEK SMALL LETTER UPSILON WITH DIALYTIKA + ό o% 03CC 0972 GREEK SMALL LETTER OMICRON WITH TONOS + ύ u% 03CD 0973 GREEK SMALL LETTER UPSILON WITH TONOS + ώ w% 03CE 0974 GREEK SMALL LETTER OMEGA WITH TONOS + Ϙ 'G 03D8 0984 GREEK LETTER ARCHAIC KOPPA + ϙ ,G 03D9 0985 GREEK SMALL LETTER ARCHAIC KOPPA + Ϛ T3 03DA 0986 GREEK LETTER STIGMA + ϛ t3 03DB 0987 GREEK SMALL LETTER STIGMA + Ϝ M3 03DC 0988 GREEK LETTER DIGAMMA + ϝ m3 03DD 0989 GREEK SMALL LETTER DIGAMMA + Ϟ K3 03DE 0990 GREEK LETTER KOPPA + ϟ k3 03DF 0991 GREEK SMALL LETTER KOPPA + Ϡ P3 03E0 0992 GREEK LETTER SAMPI + ϡ p3 03E1 0993 GREEK SMALL LETTER SAMPI + ϴ '% 03F4 1012 GREEK CAPITAL THETA SYMBOL + ϵ j3 03F5 1013 GREEK LUNATE EPSILON SYMBOL + Ё IO 0401 1025 CYRILLIC CAPITAL LETTER IO + Ђ D% 0402 1026 CYRILLIC CAPITAL LETTER DJE + Ѓ G% 0403 1027 CYRILLIC CAPITAL LETTER GJE + Є IE 0404 1028 CYRILLIC CAPITAL LETTER UKRAINIAN IE + Ѕ DS 0405 1029 CYRILLIC CAPITAL LETTER DZE + І II 0406 1030 CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I + Ї YI 0407 1031 CYRILLIC CAPITAL LETTER YI + Ј J% 0408 1032 CYRILLIC CAPITAL LETTER JE + Љ LJ 0409 1033 CYRILLIC CAPITAL LETTER LJE + Њ NJ 040A 1034 CYRILLIC CAPITAL LETTER NJE + Ћ Ts 040B 1035 CYRILLIC CAPITAL LETTER TSHE + Ќ KJ 040C 1036 CYRILLIC CAPITAL LETTER KJE + Ў V% 040E 1038 CYRILLIC CAPITAL LETTER SHORT U + Џ DZ 040F 1039 CYRILLIC CAPITAL LETTER DZHE + А A= 0410 1040 CYRILLIC CAPITAL LETTER A + Б B= 0411 1041 CYRILLIC CAPITAL LETTER BE + В V= 0412 1042 CYRILLIC CAPITAL LETTER VE + Г G= 0413 1043 CYRILLIC CAPITAL LETTER GHE + Д D= 0414 1044 CYRILLIC CAPITAL LETTER DE + Е E= 0415 1045 CYRILLIC CAPITAL LETTER IE + Ж Z% 0416 1046 CYRILLIC CAPITAL LETTER ZHE + З Z= 0417 1047 CYRILLIC CAPITAL LETTER ZE + И I= 0418 1048 CYRILLIC CAPITAL LETTER I + Й J= 0419 1049 CYRILLIC CAPITAL LETTER SHORT I + К K= 041A 1050 CYRILLIC CAPITAL LETTER KA + Л L= 041B 1051 CYRILLIC CAPITAL LETTER EL + М M= 041C 1052 CYRILLIC CAPITAL LETTER EM + Н N= 041D 1053 CYRILLIC CAPITAL LETTER EN + О O= 041E 1054 CYRILLIC CAPITAL LETTER O + П P= 041F 1055 CYRILLIC CAPITAL LETTER PE + Р R= 0420 1056 CYRILLIC CAPITAL LETTER ER + С S= 0421 1057 CYRILLIC CAPITAL LETTER ES + Т T= 0422 1058 CYRILLIC CAPITAL LETTER TE + У U= 0423 1059 CYRILLIC CAPITAL LETTER U + Ф F= 0424 1060 CYRILLIC CAPITAL LETTER EF + Х H= 0425 1061 CYRILLIC CAPITAL LETTER HA + Ц C= 0426 1062 CYRILLIC CAPITAL LETTER TSE + Ч C% 0427 1063 CYRILLIC CAPITAL LETTER CHE + Ш S% 0428 1064 CYRILLIC CAPITAL LETTER SHA + Щ Sc 0429 1065 CYRILLIC CAPITAL LETTER SHCHA + Ъ =" 042A 1066 CYRILLIC CAPITAL LETTER HARD SIGN + Ы Y= 042B 1067 CYRILLIC CAPITAL LETTER YERU + Ь %" 042C 1068 CYRILLIC CAPITAL LETTER SOFT SIGN + Э JE 042D 1069 CYRILLIC CAPITAL LETTER E + Ю JU 042E 1070 CYRILLIC CAPITAL LETTER YU + Я JA 042F 1071 CYRILLIC CAPITAL LETTER YA + а a= 0430 1072 CYRILLIC SMALL LETTER A + б b= 0431 1073 CYRILLIC SMALL LETTER BE + в v= 0432 1074 CYRILLIC SMALL LETTER VE + г g= 0433 1075 CYRILLIC SMALL LETTER GHE + д d= 0434 1076 CYRILLIC SMALL LETTER DE + е e= 0435 1077 CYRILLIC SMALL LETTER IE + ж z% 0436 1078 CYRILLIC SMALL LETTER ZHE + з z= 0437 1079 CYRILLIC SMALL LETTER ZE + и i= 0438 1080 CYRILLIC SMALL LETTER I + й j= 0439 1081 CYRILLIC SMALL LETTER SHORT I + к k= 043A 1082 CYRILLIC SMALL LETTER KA + л l= 043B 1083 CYRILLIC SMALL LETTER EL + м m= 043C 1084 CYRILLIC SMALL LETTER EM + н n= 043D 1085 CYRILLIC SMALL LETTER EN + о o= 043E 1086 CYRILLIC SMALL LETTER O + п p= 043F 1087 CYRILLIC SMALL LETTER PE + р r= 0440 1088 CYRILLIC SMALL LETTER ER + с s= 0441 1089 CYRILLIC SMALL LETTER ES + т t= 0442 1090 CYRILLIC SMALL LETTER TE + у u= 0443 1091 CYRILLIC SMALL LETTER U + ф f= 0444 1092 CYRILLIC SMALL LETTER EF + х h= 0445 1093 CYRILLIC SMALL LETTER HA + ц c= 0446 1094 CYRILLIC SMALL LETTER TSE + ч c% 0447 1095 CYRILLIC SMALL LETTER CHE + ш s% 0448 1096 CYRILLIC SMALL LETTER SHA + щ sc 0449 1097 CYRILLIC SMALL LETTER SHCHA + ъ =' 044A 1098 CYRILLIC SMALL LETTER HARD SIGN + ы y= 044B 1099 CYRILLIC SMALL LETTER YERU + ь %' 044C 1100 CYRILLIC SMALL LETTER SOFT SIGN + э je 044D 1101 CYRILLIC SMALL LETTER E + ю ju 044E 1102 CYRILLIC SMALL LETTER YU + я ja 044F 1103 CYRILLIC SMALL LETTER YA + ё io 0451 1105 CYRILLIC SMALL LETTER IO + ђ d% 0452 1106 CYRILLIC SMALL LETTER DJE + ѓ g% 0453 1107 CYRILLIC SMALL LETTER GJE + є ie 0454 1108 CYRILLIC SMALL LETTER UKRAINIAN IE + ѕ ds 0455 1109 CYRILLIC SMALL LETTER DZE + і ii 0456 1110 CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I + ї yi 0457 1111 CYRILLIC SMALL LETTER YI + ј j% 0458 1112 CYRILLIC SMALL LETTER JE + љ lj 0459 1113 CYRILLIC SMALL LETTER LJE + њ nj 045A 1114 CYRILLIC SMALL LETTER NJE + ћ ts 045B 1115 CYRILLIC SMALL LETTER TSHE + ќ kj 045C 1116 CYRILLIC SMALL LETTER KJE + ў v% 045E 1118 CYRILLIC SMALL LETTER SHORT U + џ dz 045F 1119 CYRILLIC SMALL LETTER DZHE + Ѣ Y3 0462 1122 CYRILLIC CAPITAL LETTER YAT + ѣ y3 0463 1123 CYRILLIC SMALL LETTER YAT + Ѫ O3 046A 1130 CYRILLIC CAPITAL LETTER BIG YUS + ѫ o3 046B 1131 CYRILLIC SMALL LETTER BIG YUS + Ѳ F3 0472 1138 CYRILLIC CAPITAL LETTER FITA + ѳ f3 0473 1139 CYRILLIC SMALL LETTER FITA + Ѵ V3 0474 1140 CYRILLIC CAPITAL LETTER IZHITSA + ѵ v3 0475 1141 CYRILLIC SMALL LETTER IZHITSA + Ҁ C3 0480 1152 CYRILLIC CAPITAL LETTER KOPPA + ҁ c3 0481 1153 CYRILLIC SMALL LETTER KOPPA + Ґ G3 0490 1168 CYRILLIC CAPITAL LETTER GHE WITH UPTURN + ґ g3 0491 1169 CYRILLIC SMALL LETTER GHE WITH UPTURN + א A+ 05D0 1488 HEBREW LETTER ALEF + ב B+ 05D1 1489 HEBREW LETTER BET + ג G+ 05D2 1490 HEBREW LETTER GIMEL + ד D+ 05D3 1491 HEBREW LETTER DALET + ה H+ 05D4 1492 HEBREW LETTER HE + ו W+ 05D5 1493 HEBREW LETTER VAV + ז Z+ 05D6 1494 HEBREW LETTER ZAYIN + ח X+ 05D7 1495 HEBREW LETTER HET + ט Tj 05D8 1496 HEBREW LETTER TET + י J+ 05D9 1497 HEBREW LETTER YOD + ך K% 05DA 1498 HEBREW LETTER FINAL KAF + כ K+ 05DB 1499 HEBREW LETTER KAF + ל L+ 05DC 1500 HEBREW LETTER LAMED + ם M% 05DD 1501 HEBREW LETTER FINAL MEM + מ M+ 05DE 1502 HEBREW LETTER MEM + ן N% 05DF 1503 HEBREW LETTER FINAL NUN ` + נ N+ 05E0 1504 HEBREW LETTER NUN ` + ס S+ 05E1 1505 HEBREW LETTER SAMEKH + ע E+ 05E2 1506 HEBREW LETTER AYIN + ף P% 05E3 1507 HEBREW LETTER FINAL PE + פ P+ 05E4 1508 HEBREW LETTER PE + ץ Zj 05E5 1509 HEBREW LETTER FINAL TSADI + צ ZJ 05E6 1510 HEBREW LETTER TSADI + ק Q+ 05E7 1511 HEBREW LETTER QOF + ר R+ 05E8 1512 HEBREW LETTER RESH + ש Sh 05E9 1513 HEBREW LETTER SHIN + ת T+ 05EA 1514 HEBREW LETTER TAV + ، ,+ 060C 1548 ARABIC COMMA + ؛ ;+ 061B 1563 ARABIC SEMICOLON + ؟ ?+ 061F 1567 ARABIC QUESTION MARK + ء H' 0621 1569 ARABIC LETTER HAMZA + آ aM 0622 1570 ARABIC LETTER ALEF WITH MADDA ABOVE + أ aH 0623 1571 ARABIC LETTER ALEF WITH HAMZA ABOVE + ؤ wH 0624 1572 ARABIC LETTER WAW WITH HAMZA ABOVE + إ ah 0625 1573 ARABIC LETTER ALEF WITH HAMZA BELOW + ئ yH 0626 1574 ARABIC LETTER YEH WITH HAMZA ABOVE + ا a+ 0627 1575 ARABIC LETTER ALEF + ب b+ 0628 1576 ARABIC LETTER BEH + ة tm 0629 1577 ARABIC LETTER TEH MARBUTA + ت t+ 062A 1578 ARABIC LETTER TEH + ث tk 062B 1579 ARABIC LETTER THEH + ج g+ 062C 1580 ARABIC LETTER JEEM + ح hk 062D 1581 ARABIC LETTER HAH + خ x+ 062E 1582 ARABIC LETTER KHAH + د d+ 062F 1583 ARABIC LETTER DAL + ذ dk 0630 1584 ARABIC LETTER THAL + ر r+ 0631 1585 ARABIC LETTER REH + ز z+ 0632 1586 ARABIC LETTER ZAIN + س s+ 0633 1587 ARABIC LETTER SEEN + ش sn 0634 1588 ARABIC LETTER SHEEN + ص c+ 0635 1589 ARABIC LETTER SAD + ض dd 0636 1590 ARABIC LETTER DAD + ط tj 0637 1591 ARABIC LETTER TAH + ظ zH 0638 1592 ARABIC LETTER ZAH + ع e+ 0639 1593 ARABIC LETTER AIN + غ i+ 063A 1594 ARABIC LETTER GHAIN + ـ ++ 0640 1600 ARABIC TATWEEL + ف f+ 0641 1601 ARABIC LETTER FEH + ق q+ 0642 1602 ARABIC LETTER QAF + ك k+ 0643 1603 ARABIC LETTER KAF + ل l+ 0644 1604 ARABIC LETTER LAM + م m+ 0645 1605 ARABIC LETTER MEEM + ن n+ 0646 1606 ARABIC LETTER NOON + ه h+ 0647 1607 ARABIC LETTER HEH + و w+ 0648 1608 ARABIC LETTER WAW + ى j+ 0649 1609 ARABIC LETTER ALEF MAKSURA + ي y+ 064A 1610 ARABIC LETTER YEH + ً :+ 064B 1611 ARABIC FATHATAN + ٌ "+ 064C 1612 ARABIC DAMMATAN + ٍ =+ 064D 1613 ARABIC KASRATAN + َ /+ 064E 1614 ARABIC FATHA + ُ '+ 064F 1615 ARABIC DAMMA + ِ 1+ 0650 1616 ARABIC KASRA + ّ 3+ 0651 1617 ARABIC SHADDA + ْ 0+ 0652 1618 ARABIC SUKUN + ٰ aS 0670 1648 ARABIC LETTER SUPERSCRIPT ALEF + پ p+ 067E 1662 ARABIC LETTER PEH + ڤ v+ 06A4 1700 ARABIC LETTER VEH + گ gf 06AF 1711 ARABIC LETTER GAF + ۰ 0a 06F0 1776 EXTENDED ARABIC-INDIC DIGIT ZERO + ۱ 1a 06F1 1777 EXTENDED ARABIC-INDIC DIGIT ONE + ۲ 2a 06F2 1778 EXTENDED ARABIC-INDIC DIGIT TWO + ۳ 3a 06F3 1779 EXTENDED ARABIC-INDIC DIGIT THREE + ۴ 4a 06F4 1780 EXTENDED ARABIC-INDIC DIGIT FOUR + ۵ 5a 06F5 1781 EXTENDED ARABIC-INDIC DIGIT FIVE + ۶ 6a 06F6 1782 EXTENDED ARABIC-INDIC DIGIT SIX + ۷ 7a 06F7 1783 EXTENDED ARABIC-INDIC DIGIT SEVEN + ۸ 8a 06F8 1784 EXTENDED ARABIC-INDIC DIGIT EIGHT + ۹ 9a 06F9 1785 EXTENDED ARABIC-INDIC DIGIT NINE + Ḃ B. 1E02 7682 LATIN CAPITAL LETTER B WITH DOT ABOVE + ḃ b. 1E03 7683 LATIN SMALL LETTER B WITH DOT ABOVE + Ḇ B_ 1E06 7686 LATIN CAPITAL LETTER B WITH LINE BELOW + ḇ b_ 1E07 7687 LATIN SMALL LETTER B WITH LINE BELOW + Ḋ D. 1E0A 7690 LATIN CAPITAL LETTER D WITH DOT ABOVE + ḋ d. 1E0B 7691 LATIN SMALL LETTER D WITH DOT ABOVE + Ḏ D_ 1E0E 7694 LATIN CAPITAL LETTER D WITH LINE BELOW + ḏ d_ 1E0F 7695 LATIN SMALL LETTER D WITH LINE BELOW + Ḑ D, 1E10 7696 LATIN CAPITAL LETTER D WITH CEDILLA + ḑ d, 1E11 7697 LATIN SMALL LETTER D WITH CEDILLA + Ḟ F. 1E1E 7710 LATIN CAPITAL LETTER F WITH DOT ABOVE + ḟ f. 1E1F 7711 LATIN SMALL LETTER F WITH DOT ABOVE + Ḡ G- 1E20 7712 LATIN CAPITAL LETTER G WITH MACRON + ḡ g- 1E21 7713 LATIN SMALL LETTER G WITH MACRON + Ḣ H. 1E22 7714 LATIN CAPITAL LETTER H WITH DOT ABOVE + ḣ h. 1E23 7715 LATIN SMALL LETTER H WITH DOT ABOVE + Ḧ H: 1E26 7718 LATIN CAPITAL LETTER H WITH DIAERESIS + ḧ h: 1E27 7719 LATIN SMALL LETTER H WITH DIAERESIS + Ḩ H, 1E28 7720 LATIN CAPITAL LETTER H WITH CEDILLA + ḩ h, 1E29 7721 LATIN SMALL LETTER H WITH CEDILLA + Ḱ K' 1E30 7728 LATIN CAPITAL LETTER K WITH ACUTE + ḱ k' 1E31 7729 LATIN SMALL LETTER K WITH ACUTE + Ḵ K_ 1E34 7732 LATIN CAPITAL LETTER K WITH LINE BELOW + ḵ k_ 1E35 7733 LATIN SMALL LETTER K WITH LINE BELOW + Ḻ L_ 1E3A 7738 LATIN CAPITAL LETTER L WITH LINE BELOW + ḻ l_ 1E3B 7739 LATIN SMALL LETTER L WITH LINE BELOW + Ḿ M' 1E3E 7742 LATIN CAPITAL LETTER M WITH ACUTE + ḿ m' 1E3F 7743 LATIN SMALL LETTER M WITH ACUTE + Ṁ M. 1E40 7744 LATIN CAPITAL LETTER M WITH DOT ABOVE + ṁ m. 1E41 7745 LATIN SMALL LETTER M WITH DOT ABOVE + Ṅ N. 1E44 7748 LATIN CAPITAL LETTER N WITH DOT ABOVE ` + ṅ n. 1E45 7749 LATIN SMALL LETTER N WITH DOT ABOVE ` + Ṉ N_ 1E48 7752 LATIN CAPITAL LETTER N WITH LINE BELOW ` + ṉ n_ 1E49 7753 LATIN SMALL LETTER N WITH LINE BELOW ` + Ṕ P' 1E54 7764 LATIN CAPITAL LETTER P WITH ACUTE + ṕ p' 1E55 7765 LATIN SMALL LETTER P WITH ACUTE + Ṗ P. 1E56 7766 LATIN CAPITAL LETTER P WITH DOT ABOVE + ṗ p. 1E57 7767 LATIN SMALL LETTER P WITH DOT ABOVE + Ṙ R. 1E58 7768 LATIN CAPITAL LETTER R WITH DOT ABOVE + ṙ r. 1E59 7769 LATIN SMALL LETTER R WITH DOT ABOVE + Ṟ R_ 1E5E 7774 LATIN CAPITAL LETTER R WITH LINE BELOW + ṟ r_ 1E5F 7775 LATIN SMALL LETTER R WITH LINE BELOW + Ṡ S. 1E60 7776 LATIN CAPITAL LETTER S WITH DOT ABOVE + ṡ s. 1E61 7777 LATIN SMALL LETTER S WITH DOT ABOVE + Ṫ T. 1E6A 7786 LATIN CAPITAL LETTER T WITH DOT ABOVE + ṫ t. 1E6B 7787 LATIN SMALL LETTER T WITH DOT ABOVE + Ṯ T_ 1E6E 7790 LATIN CAPITAL LETTER T WITH LINE BELOW + ṯ t_ 1E6F 7791 LATIN SMALL LETTER T WITH LINE BELOW + Ṽ V? 1E7C 7804 LATIN CAPITAL LETTER V WITH TILDE + ṽ v? 1E7D 7805 LATIN SMALL LETTER V WITH TILDE + Ẁ W! 1E80 7808 LATIN CAPITAL LETTER W WITH GRAVE + ẁ w! 1E81 7809 LATIN SMALL LETTER W WITH GRAVE + Ẃ W' 1E82 7810 LATIN CAPITAL LETTER W WITH ACUTE + ẃ w' 1E83 7811 LATIN SMALL LETTER W WITH ACUTE + Ẅ W: 1E84 7812 LATIN CAPITAL LETTER W WITH DIAERESIS + ẅ w: 1E85 7813 LATIN SMALL LETTER W WITH DIAERESIS + Ẇ W. 1E86 7814 LATIN CAPITAL LETTER W WITH DOT ABOVE + ẇ w. 1E87 7815 LATIN SMALL LETTER W WITH DOT ABOVE + Ẋ X. 1E8A 7818 LATIN CAPITAL LETTER X WITH DOT ABOVE + ẋ x. 1E8B 7819 LATIN SMALL LETTER X WITH DOT ABOVE + Ẍ X: 1E8C 7820 LATIN CAPITAL LETTER X WITH DIAERESIS + ẍ x: 1E8D 7821 LATIN SMALL LETTER X WITH DIAERESIS + Ẏ Y. 1E8E 7822 LATIN CAPITAL LETTER Y WITH DOT ABOVE + ẏ y. 1E8F 7823 LATIN SMALL LETTER Y WITH DOT ABOVE + Ẑ Z> 1E90 7824 LATIN CAPITAL LETTER Z WITH CIRCUMFLEX + ẑ z> 1E91 7825 LATIN SMALL LETTER Z WITH CIRCUMFLEX + Ẕ Z_ 1E94 7828 LATIN CAPITAL LETTER Z WITH LINE BELOW + ẕ z_ 1E95 7829 LATIN SMALL LETTER Z WITH LINE BELOW + ẖ h_ 1E96 7830 LATIN SMALL LETTER H WITH LINE BELOW + ẗ t: 1E97 7831 LATIN SMALL LETTER T WITH DIAERESIS + ẘ w0 1E98 7832 LATIN SMALL LETTER W WITH RING ABOVE + ẙ y0 1E99 7833 LATIN SMALL LETTER Y WITH RING ABOVE + Ả A2 1EA2 7842 LATIN CAPITAL LETTER A WITH HOOK ABOVE + ả a2 1EA3 7843 LATIN SMALL LETTER A WITH HOOK ABOVE + Ẻ E2 1EBA 7866 LATIN CAPITAL LETTER E WITH HOOK ABOVE + ẻ e2 1EBB 7867 LATIN SMALL LETTER E WITH HOOK ABOVE + Ẽ E? 1EBC 7868 LATIN CAPITAL LETTER E WITH TILDE + ẽ e? 1EBD 7869 LATIN SMALL LETTER E WITH TILDE + Ỉ I2 1EC8 7880 LATIN CAPITAL LETTER I WITH HOOK ABOVE + ỉ i2 1EC9 7881 LATIN SMALL LETTER I WITH HOOK ABOVE + Ỏ O2 1ECE 7886 LATIN CAPITAL LETTER O WITH HOOK ABOVE + ỏ o2 1ECF 7887 LATIN SMALL LETTER O WITH HOOK ABOVE + Ủ U2 1EE6 7910 LATIN CAPITAL LETTER U WITH HOOK ABOVE + ủ u2 1EE7 7911 LATIN SMALL LETTER U WITH HOOK ABOVE + Ỳ Y! 1EF2 7922 LATIN CAPITAL LETTER Y WITH GRAVE + ỳ y! 1EF3 7923 LATIN SMALL LETTER Y WITH GRAVE + Ỷ Y2 1EF6 7926 LATIN CAPITAL LETTER Y WITH HOOK ABOVE + ỷ y2 1EF7 7927 LATIN SMALL LETTER Y WITH HOOK ABOVE + Ỹ Y? 1EF8 7928 LATIN CAPITAL LETTER Y WITH TILDE + ỹ y? 1EF9 7929 LATIN SMALL LETTER Y WITH TILDE + ἀ ;' 1F00 7936 GREEK SMALL LETTER ALPHA WITH PSILI + ἁ ,' 1F01 7937 GREEK SMALL LETTER ALPHA WITH DASIA + ἂ ;! 1F02 7938 GREEK SMALL LETTER ALPHA WITH PSILI AND VARIA + ἃ ,! 1F03 7939 GREEK SMALL LETTER ALPHA WITH DASIA AND VARIA + ἄ ?; 1F04 7940 GREEK SMALL LETTER ALPHA WITH PSILI AND OXIA + ἅ ?, 1F05 7941 GREEK SMALL LETTER ALPHA WITH DASIA AND OXIA + ἆ !: 1F06 7942 GREEK SMALL LETTER ALPHA WITH PSILI AND PERISPOMENI + ἇ ?: 1F07 7943 GREEK SMALL LETTER ALPHA WITH DASIA AND PERISPOMENI + 1N 2002 8194 EN SPACE + 1M 2003 8195 EM SPACE + 3M 2004 8196 THREE-PER-EM SPACE + 4M 2005 8197 FOUR-PER-EM SPACE + 6M 2006 8198 SIX-PER-EM SPACE + 1T 2009 8201 THIN SPACE + 1H 200A 8202 HAIR SPACE + ‐ -1 2010 8208 HYPHEN + – -N 2013 8211 EN DASH ` + — -M 2014 8212 EM DASH + ― -3 2015 8213 HORIZONTAL BAR + ‖ !2 2016 8214 DOUBLE VERTICAL LINE + ‗ =2 2017 8215 DOUBLE LOW LINE + ‘ '6 2018 8216 LEFT SINGLE QUOTATION MARK + ’ '9 2019 8217 RIGHT SINGLE QUOTATION MARK + ‚ .9 201A 8218 SINGLE LOW-9 QUOTATION MARK + ‛ 9' 201B 8219 SINGLE HIGH-REVERSED-9 QUOTATION MARK + “ "6 201C 8220 LEFT DOUBLE QUOTATION MARK + ” "9 201D 8221 RIGHT DOUBLE QUOTATION MARK + „ :9 201E 8222 DOUBLE LOW-9 QUOTATION MARK + ‟ 9" 201F 8223 DOUBLE HIGH-REVERSED-9 QUOTATION MARK + † /- 2020 8224 DAGGER + ‡ /= 2021 8225 DOUBLE DAGGER + • oo 2022 8226 BULLET + ‥ .. 2025 8229 TWO DOT LEADER + … ,. 2026 8230 HORIZONTAL ELLIPSIS + ‰ %0 2030 8240 PER MILLE SIGN + ′ 1' 2032 8242 PRIME + ″ 2' 2033 8243 DOUBLE PRIME + ‴ 3' 2034 8244 TRIPLE PRIME + ‵ 1" 2035 8245 REVERSED PRIME + ‶ 2" 2036 8246 REVERSED DOUBLE PRIME + ‷ 3" 2037 8247 REVERSED TRIPLE PRIME + ‸ Ca 2038 8248 CARET + ‹ <1 2039 8249 SINGLE LEFT-POINTING ANGLE QUOTATION MARK + › >1 203A 8250 SINGLE RIGHT-POINTING ANGLE QUOTATION MARK + ※ :X 203B 8251 REFERENCE MARK + ‾ '- 203E 8254 OVERLINE + ⁄ /f 2044 8260 FRACTION SLASH + ⁰ 0S 2070 8304 SUPERSCRIPT ZERO + ⁴ 4S 2074 8308 SUPERSCRIPT FOUR + ⁵ 5S 2075 8309 SUPERSCRIPT FIVE + ⁶ 6S 2076 8310 SUPERSCRIPT SIX + ⁷ 7S 2077 8311 SUPERSCRIPT SEVEN + ⁸ 8S 2078 8312 SUPERSCRIPT EIGHT + ⁹ 9S 2079 8313 SUPERSCRIPT NINE + ⁺ +S 207A 8314 SUPERSCRIPT PLUS SIGN + ⁻ -S 207B 8315 SUPERSCRIPT MINUS + ⁼ =S 207C 8316 SUPERSCRIPT EQUALS SIGN + ⁽ (S 207D 8317 SUPERSCRIPT LEFT PARENTHESIS + ⁾ )S 207E 8318 SUPERSCRIPT RIGHT PARENTHESIS + ⁿ nS 207F 8319 SUPERSCRIPT LATIN SMALL LETTER N ` + ₀ 0s 2080 8320 SUBSCRIPT ZERO + ₁ 1s 2081 8321 SUBSCRIPT ONE + ₂ 2s 2082 8322 SUBSCRIPT TWO + ₃ 3s 2083 8323 SUBSCRIPT THREE + ₄ 4s 2084 8324 SUBSCRIPT FOUR + ₅ 5s 2085 8325 SUBSCRIPT FIVE + ₆ 6s 2086 8326 SUBSCRIPT SIX + ₇ 7s 2087 8327 SUBSCRIPT SEVEN + ₈ 8s 2088 8328 SUBSCRIPT EIGHT + ₉ 9s 2089 8329 SUBSCRIPT NINE + ₊ +s 208A 8330 SUBSCRIPT PLUS SIGN + ₋ -s 208B 8331 SUBSCRIPT MINUS + ₌ =s 208C 8332 SUBSCRIPT EQUALS SIGN + ₍ (s 208D 8333 SUBSCRIPT LEFT PARENTHESIS + ₎ )s 208E 8334 SUBSCRIPT RIGHT PARENTHESIS + ₤ Li 20A4 8356 LIRA SIGN + ₧ Pt 20A7 8359 PESETA SIGN + ₩ W= 20A9 8361 WON SIGN + € Eu 20AC 8364 EURO SIGN + ₽ =R 20BD 8381 ROUBLE SIGN + ₽ =P 20BD 8381 ROUBLE SIGN + ℃ oC 2103 8451 DEGREE CELSIUS + ℅ co 2105 8453 CARE OF + ℉ oF 2109 8457 DEGREE FAHRENHEIT + № N0 2116 8470 NUMERO SIGN + ℗ PO 2117 8471 SOUND RECORDING COPYRIGHT + ℞ Rx 211E 8478 PRESCRIPTION TAKE + ℠ SM 2120 8480 SERVICE MARK + ™ TM 2122 8482 TRADE MARK SIGN + Ω Om 2126 8486 OHM SIGN + Å AO 212B 8491 ANGSTROM SIGN + ⅓ 13 2153 8531 VULGAR FRACTION ONE THIRD + ⅔ 23 2154 8532 VULGAR FRACTION TWO THIRDS + ⅕ 15 2155 8533 VULGAR FRACTION ONE FIFTH + ⅖ 25 2156 8534 VULGAR FRACTION TWO FIFTHS + ⅗ 35 2157 8535 VULGAR FRACTION THREE FIFTHS + ⅘ 45 2158 8536 VULGAR FRACTION FOUR FIFTHS + ⅙ 16 2159 8537 VULGAR FRACTION ONE SIXTH + ⅚ 56 215A 8538 VULGAR FRACTION FIVE SIXTHS + ⅛ 18 215B 8539 VULGAR FRACTION ONE EIGHTH + ⅜ 38 215C 8540 VULGAR FRACTION THREE EIGHTHS + ⅝ 58 215D 8541 VULGAR FRACTION FIVE EIGHTHS + ⅞ 78 215E 8542 VULGAR FRACTION SEVEN EIGHTHS + Ⅰ 1R 2160 8544 ROMAN NUMERAL ONE + Ⅱ 2R 2161 8545 ROMAN NUMERAL TWO + Ⅲ 3R 2162 8546 ROMAN NUMERAL THREE + Ⅳ 4R 2163 8547 ROMAN NUMERAL FOUR + Ⅴ 5R 2164 8548 ROMAN NUMERAL FIVE + Ⅵ 6R 2165 8549 ROMAN NUMERAL SIX + Ⅶ 7R 2166 8550 ROMAN NUMERAL SEVEN + Ⅷ 8R 2167 8551 ROMAN NUMERAL EIGHT + Ⅸ 9R 2168 8552 ROMAN NUMERAL NINE + Ⅹ aR 2169 8553 ROMAN NUMERAL TEN + Ⅺ bR 216A 8554 ROMAN NUMERAL ELEVEN + Ⅻ cR 216B 8555 ROMAN NUMERAL TWELVE + ⅰ 1r 2170 8560 SMALL ROMAN NUMERAL ONE + ⅱ 2r 2171 8561 SMALL ROMAN NUMERAL TWO + ⅲ 3r 2172 8562 SMALL ROMAN NUMERAL THREE + ⅳ 4r 2173 8563 SMALL ROMAN NUMERAL FOUR + ⅴ 5r 2174 8564 SMALL ROMAN NUMERAL FIVE + ⅵ 6r 2175 8565 SMALL ROMAN NUMERAL SIX + ⅶ 7r 2176 8566 SMALL ROMAN NUMERAL SEVEN + ⅷ 8r 2177 8567 SMALL ROMAN NUMERAL EIGHT + ⅸ 9r 2178 8568 SMALL ROMAN NUMERAL NINE + ⅹ ar 2179 8569 SMALL ROMAN NUMERAL TEN + ⅺ br 217A 8570 SMALL ROMAN NUMERAL ELEVEN + ⅻ cr 217B 8571 SMALL ROMAN NUMERAL TWELVE + ← <- 2190 8592 LEFTWARDS ARROW + ↑ -! 2191 8593 UPWARDS ARROW + → -> 2192 8594 RIGHTWARDS ARROW + ↓ -v 2193 8595 DOWNWARDS ARROW + ↔ <> 2194 8596 LEFT RIGHT ARROW + ↕ UD 2195 8597 UP DOWN ARROW + ⇐ <= 21D0 8656 LEFTWARDS DOUBLE ARROW + ⇒ => 21D2 8658 RIGHTWARDS DOUBLE ARROW + ⇔ == 21D4 8660 LEFT RIGHT DOUBLE ARROW + ∀ FA 2200 8704 FOR ALL + ∂ dP 2202 8706 PARTIAL DIFFERENTIAL + ∃ TE 2203 8707 THERE EXISTS + ∅ /0 2205 8709 EMPTY SET + ∆ DE 2206 8710 INCREMENT + ∇ NB 2207 8711 NABLA + ∈ (- 2208 8712 ELEMENT OF + ∋ -) 220B 8715 CONTAINS AS MEMBER + ∏ *P 220F 8719 N-ARY PRODUCT ` + ∑ +Z 2211 8721 N-ARY SUMMATION ` + − -2 2212 8722 MINUS SIGN + ∓ -+ 2213 8723 MINUS-OR-PLUS SIGN + ∗ *- 2217 8727 ASTERISK OPERATOR + ∘ Ob 2218 8728 RING OPERATOR + ∙ Sb 2219 8729 BULLET OPERATOR + √ RT 221A 8730 SQUARE ROOT + ∝ 0( 221D 8733 PROPORTIONAL TO + ∞ 00 221E 8734 INFINITY + ∟ -L 221F 8735 RIGHT ANGLE + ∠ -V 2220 8736 ANGLE + ∥ PP 2225 8741 PARALLEL TO + ∧ AN 2227 8743 LOGICAL AND + ∨ OR 2228 8744 LOGICAL OR + ∩ (U 2229 8745 INTERSECTION + ∪ )U 222A 8746 UNION + ∫ In 222B 8747 INTEGRAL + ∬ DI 222C 8748 DOUBLE INTEGRAL + ∮ Io 222E 8750 CONTOUR INTEGRAL + ∴ .: 2234 8756 THEREFORE + ∵ :. 2235 8757 BECAUSE + ∶ :R 2236 8758 RATIO + ∷ :: 2237 8759 PROPORTION + ∼ ?1 223C 8764 TILDE OPERATOR + ∾ CG 223E 8766 INVERTED LAZY S + ≃ ?- 2243 8771 ASYMPTOTICALLY EQUAL TO + ≅ ?= 2245 8773 APPROXIMATELY EQUAL TO + ≈ ?2 2248 8776 ALMOST EQUAL TO + ≌ =? 224C 8780 ALL EQUAL TO + ≓ HI 2253 8787 IMAGE OF OR APPROXIMATELY EQUAL TO + ≠ != 2260 8800 NOT EQUAL TO + ≡ =3 2261 8801 IDENTICAL TO + ≤ =< 2264 8804 LESS-THAN OR EQUAL TO + ≥ >= 2265 8805 GREATER-THAN OR EQUAL TO + ≪ <* 226A 8810 MUCH LESS-THAN + ≫ *> 226B 8811 MUCH GREATER-THAN + ≮ !< 226E 8814 NOT LESS-THAN + ≯ !> 226F 8815 NOT GREATER-THAN + ⊂ (C 2282 8834 SUBSET OF + ⊃ )C 2283 8835 SUPERSET OF + ⊆ (_ 2286 8838 SUBSET OF OR EQUAL TO + ⊇ )_ 2287 8839 SUPERSET OF OR EQUAL TO + ⊙ 0. 2299 8857 CIRCLED DOT OPERATOR + ⊚ 02 229A 8858 CIRCLED RING OPERATOR + ⊥ -T 22A5 8869 UP TACK + ⋅ .P 22C5 8901 DOT OPERATOR + ⋮ :3 22EE 8942 VERTICAL ELLIPSIS + ⋯ .3 22EF 8943 MIDLINE HORIZONTAL ELLIPSIS + ⌂ Eh 2302 8962 HOUSE + ⌈ <7 2308 8968 LEFT CEILING + ⌉ >7 2309 8969 RIGHT CEILING + ⌊ 7< 230A 8970 LEFT FLOOR + ⌋ 7> 230B 8971 RIGHT FLOOR + ⌐ NI 2310 8976 REVERSED NOT SIGN + ⌒ (A 2312 8978 ARC + ⌕ TR 2315 8981 TELEPHONE RECORDER + ⌠ Iu 2320 8992 TOP HALF INTEGRAL + ⌡ Il 2321 8993 BOTTOM HALF INTEGRAL + 〈 </ 2329 9001 LEFT-POINTING ANGLE BRACKET + 〉 /> 232A 9002 RIGHT-POINTING ANGLE BRACKET + ␣ Vs 2423 9251 OPEN BOX + ⑀ 1h 2440 9280 OCR HOOK + ⑁ 3h 2441 9281 OCR CHAIR + ⑂ 2h 2442 9282 OCR FORK + ⑃ 4h 2443 9283 OCR INVERTED FORK + ⑆ 1j 2446 9286 OCR BRANCH BANK IDENTIFICATION + ⑇ 2j 2447 9287 OCR AMOUNT OF CHECK + ⑈ 3j 2448 9288 OCR DASH + ⑉ 4j 2449 9289 OCR CUSTOMER ACCOUNT NUMBER + ⒈ 1. 2488 9352 DIGIT ONE FULL STOP + ⒉ 2. 2489 9353 DIGIT TWO FULL STOP + ⒊ 3. 248A 9354 DIGIT THREE FULL STOP + ⒋ 4. 248B 9355 DIGIT FOUR FULL STOP + ⒌ 5. 248C 9356 DIGIT FIVE FULL STOP + ⒍ 6. 248D 9357 DIGIT SIX FULL STOP + ⒎ 7. 248E 9358 DIGIT SEVEN FULL STOP + ⒏ 8. 248F 9359 DIGIT EIGHT FULL STOP + ⒐ 9. 2490 9360 DIGIT NINE FULL STOP + ─ hh 2500 9472 BOX DRAWINGS LIGHT HORIZONTAL + ━ HH 2501 9473 BOX DRAWINGS HEAVY HORIZONTAL + │ vv 2502 9474 BOX DRAWINGS LIGHT VERTICAL + ┃ VV 2503 9475 BOX DRAWINGS HEAVY VERTICAL + ┄ 3- 2504 9476 BOX DRAWINGS LIGHT TRIPLE DASH HORIZONTAL + ┅ 3_ 2505 9477 BOX DRAWINGS HEAVY TRIPLE DASH HORIZONTAL + ┆ 3! 2506 9478 BOX DRAWINGS LIGHT TRIPLE DASH VERTICAL + ┇ 3/ 2507 9479 BOX DRAWINGS HEAVY TRIPLE DASH VERTICAL + ┈ 4- 2508 9480 BOX DRAWINGS LIGHT QUADRUPLE DASH HORIZONTAL + ┉ 4_ 2509 9481 BOX DRAWINGS HEAVY QUADRUPLE DASH HORIZONTAL + ┊ 4! 250A 9482 BOX DRAWINGS LIGHT QUADRUPLE DASH VERTICAL + ┋ 4/ 250B 9483 BOX DRAWINGS HEAVY QUADRUPLE DASH VERTICAL + ┌ dr 250C 9484 BOX DRAWINGS LIGHT DOWN AND RIGHT + ┍ dR 250D 9485 BOX DRAWINGS DOWN LIGHT AND RIGHT HEAVY + ┎ Dr 250E 9486 BOX DRAWINGS DOWN HEAVY AND RIGHT LIGHT + ┏ DR 250F 9487 BOX DRAWINGS HEAVY DOWN AND RIGHT + ┐ dl 2510 9488 BOX DRAWINGS LIGHT DOWN AND LEFT + ┑ dL 2511 9489 BOX DRAWINGS DOWN LIGHT AND LEFT HEAVY + ┒ Dl 2512 9490 BOX DRAWINGS DOWN HEAVY AND LEFT LIGHT + ┓ LD 2513 9491 BOX DRAWINGS HEAVY DOWN AND LEFT + └ ur 2514 9492 BOX DRAWINGS LIGHT UP AND RIGHT + ┕ uR 2515 9493 BOX DRAWINGS UP LIGHT AND RIGHT HEAVY + ┖ Ur 2516 9494 BOX DRAWINGS UP HEAVY AND RIGHT LIGHT + ┗ UR 2517 9495 BOX DRAWINGS HEAVY UP AND RIGHT + ┘ ul 2518 9496 BOX DRAWINGS LIGHT UP AND LEFT + ┙ uL 2519 9497 BOX DRAWINGS UP LIGHT AND LEFT HEAVY + ┚ Ul 251A 9498 BOX DRAWINGS UP HEAVY AND LEFT LIGHT + ┛ UL 251B 9499 BOX DRAWINGS HEAVY UP AND LEFT + ├ vr 251C 9500 BOX DRAWINGS LIGHT VERTICAL AND RIGHT + ┝ vR 251D 9501 BOX DRAWINGS VERTICAL LIGHT AND RIGHT HEAVY + ┠ Vr 2520 9504 BOX DRAWINGS VERTICAL HEAVY AND RIGHT LIGHT + ┣ VR 2523 9507 BOX DRAWINGS HEAVY VERTICAL AND RIGHT + ┤ vl 2524 9508 BOX DRAWINGS LIGHT VERTICAL AND LEFT + ┥ vL 2525 9509 BOX DRAWINGS VERTICAL LIGHT AND LEFT HEAVY + ┨ Vl 2528 9512 BOX DRAWINGS VERTICAL HEAVY AND LEFT LIGHT + ┫ VL 252B 9515 BOX DRAWINGS HEAVY VERTICAL AND LEFT + ┬ dh 252C 9516 BOX DRAWINGS LIGHT DOWN AND HORIZONTAL + ┯ dH 252F 9519 BOX DRAWINGS DOWN LIGHT AND HORIZONTAL HEAVY + ┰ Dh 2530 9520 BOX DRAWINGS DOWN HEAVY AND HORIZONTAL LIGHT + ┳ DH 2533 9523 BOX DRAWINGS HEAVY DOWN AND HORIZONTAL + ┴ uh 2534 9524 BOX DRAWINGS LIGHT UP AND HORIZONTAL + ┷ uH 2537 9527 BOX DRAWINGS UP LIGHT AND HORIZONTAL HEAVY + ┸ Uh 2538 9528 BOX DRAWINGS UP HEAVY AND HORIZONTAL LIGHT + ┻ UH 253B 9531 BOX DRAWINGS HEAVY UP AND HORIZONTAL + ┼ vh 253C 9532 BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL + ┿ vH 253F 9535 BOX DRAWINGS VERTICAL LIGHT AND HORIZONTAL HEAVY + ╂ Vh 2542 9538 BOX DRAWINGS VERTICAL HEAVY AND HORIZONTAL LIGHT + ╋ VH 254B 9547 BOX DRAWINGS HEAVY VERTICAL AND HORIZONTAL + ╱ FD 2571 9585 BOX DRAWINGS LIGHT DIAGONAL UPPER RIGHT TO LOWER LEFT + ╲ BD 2572 9586 BOX DRAWINGS LIGHT DIAGONAL UPPER LEFT TO LOWER RIGHT + ▀ TB 2580 9600 UPPER HALF BLOCK + ▄ LB 2584 9604 LOWER HALF BLOCK + █ FB 2588 9608 FULL BLOCK + ▌ lB 258C 9612 LEFT HALF BLOCK + ▐ RB 2590 9616 RIGHT HALF BLOCK + ░ .S 2591 9617 LIGHT SHADE + ▒ :S 2592 9618 MEDIUM SHADE + ▓ ?S 2593 9619 DARK SHADE + ■ fS 25A0 9632 BLACK SQUARE + □ OS 25A1 9633 WHITE SQUARE + ▢ RO 25A2 9634 WHITE SQUARE WITH ROUNDED CORNERS + ▣ Rr 25A3 9635 WHITE SQUARE CONTAINING BLACK SMALL SQUARE + ▤ RF 25A4 9636 SQUARE WITH HORIZONTAL FILL + ▥ RY 25A5 9637 SQUARE WITH VERTICAL FILL + ▦ RH 25A6 9638 SQUARE WITH ORTHOGONAL CROSSHATCH FILL + ▧ RZ 25A7 9639 SQUARE WITH UPPER LEFT TO LOWER RIGHT FILL + ▨ RK 25A8 9640 SQUARE WITH UPPER RIGHT TO LOWER LEFT FILL + ▩ RX 25A9 9641 SQUARE WITH DIAGONAL CROSSHATCH FILL + ▪ sB 25AA 9642 BLACK SMALL SQUARE + ▬ SR 25AC 9644 BLACK RECTANGLE + ▭ Or 25AD 9645 WHITE RECTANGLE + ▲ UT 25B2 9650 BLACK UP-POINTING TRIANGLE + △ uT 25B3 9651 WHITE UP-POINTING TRIANGLE + ▶ PR 25B6 9654 BLACK RIGHT-POINTING TRIANGLE + ▷ Tr 25B7 9655 WHITE RIGHT-POINTING TRIANGLE + ▼ Dt 25BC 9660 BLACK DOWN-POINTING TRIANGLE + ▽ dT 25BD 9661 WHITE DOWN-POINTING TRIANGLE + ◀ PL 25C0 9664 BLACK LEFT-POINTING TRIANGLE + ◁ Tl 25C1 9665 WHITE LEFT-POINTING TRIANGLE + ◆ Db 25C6 9670 BLACK DIAMOND + ◇ Dw 25C7 9671 WHITE DIAMOND + ◊ LZ 25CA 9674 LOZENGE + ○ 0m 25CB 9675 WHITE CIRCLE + ◎ 0o 25CE 9678 BULLSEYE + ● 0M 25CF 9679 BLACK CIRCLE + ◐ 0L 25D0 9680 CIRCLE WITH LEFT HALF BLACK + ◑ 0R 25D1 9681 CIRCLE WITH RIGHT HALF BLACK + ◘ Sn 25D8 9688 INVERSE BULLET + ◙ Ic 25D9 9689 INVERSE WHITE CIRCLE + ◢ Fd 25E2 9698 BLACK LOWER RIGHT TRIANGLE + ◣ Bd 25E3 9699 BLACK LOWER LEFT TRIANGLE + ★ *2 2605 9733 BLACK STAR + ☆ *1 2606 9734 WHITE STAR + ☜ <H 261C 9756 WHITE LEFT POINTING INDEX + ☞ >H 261E 9758 WHITE RIGHT POINTING INDEX + ☺ 0u 263A 9786 WHITE SMILING FACE + ☻ 0U 263B 9787 BLACK SMILING FACE + ☼ SU 263C 9788 WHITE SUN WITH RAYS + ♀ Fm 2640 9792 FEMALE SIGN + ♂ Ml 2642 9794 MALE SIGN + ♠ cS 2660 9824 BLACK SPADE SUIT + ♡ cH 2661 9825 WHITE HEART SUIT + ♢ cD 2662 9826 WHITE DIAMOND SUIT + ♣ cC 2663 9827 BLACK CLUB SUIT + ♩ Md 2669 9833 QUARTER NOTE ` + ♪ M8 266A 9834 EIGHTH NOTE ` + ♫ M2 266B 9835 BEAMED EIGHTH NOTES + ♭ Mb 266D 9837 MUSIC FLAT SIGN + ♮ Mx 266E 9838 MUSIC NATURAL SIGN + ♯ MX 266F 9839 MUSIC SHARP SIGN + ✓ OK 2713 10003 CHECK MARK + ✗ XX 2717 10007 BALLOT X + ✠ -X 2720 10016 MALTESE CROSS + IS 3000 12288 IDEOGRAPHIC SPACE + 、 ,_ 3001 12289 IDEOGRAPHIC COMMA + 。 ._ 3002 12290 IDEOGRAPHIC FULL STOP + 〃 +" 3003 12291 DITTO MARK + 〄 +_ 3004 12292 JAPANESE INDUSTRIAL STANDARD SYMBOL + 々 *_ 3005 12293 IDEOGRAPHIC ITERATION MARK + 〆 ;_ 3006 12294 IDEOGRAPHIC CLOSING MARK + 〇 0_ 3007 12295 IDEOGRAPHIC NUMBER ZERO + 《 <+ 300A 12298 LEFT DOUBLE ANGLE BRACKET + 》 >+ 300B 12299 RIGHT DOUBLE ANGLE BRACKET + 「 <' 300C 12300 LEFT CORNER BRACKET + 」 >' 300D 12301 RIGHT CORNER BRACKET + 『 <" 300E 12302 LEFT WHITE CORNER BRACKET + 』 >" 300F 12303 RIGHT WHITE CORNER BRACKET + 【 (" 3010 12304 LEFT BLACK LENTICULAR BRACKET + 】 )" 3011 12305 RIGHT BLACK LENTICULAR BRACKET + 〒 =T 3012 12306 POSTAL MARK + 〓 =_ 3013 12307 GETA MARK + 〔 (' 3014 12308 LEFT TORTOISE SHELL BRACKET + 〕 )' 3015 12309 RIGHT TORTOISE SHELL BRACKET + 〖 (I 3016 12310 LEFT WHITE LENTICULAR BRACKET + 〗 )I 3017 12311 RIGHT WHITE LENTICULAR BRACKET + 〜 -? 301C 12316 WAVE DASH + ぁ A5 3041 12353 HIRAGANA LETTER SMALL A + あ a5 3042 12354 HIRAGANA LETTER A + ぃ I5 3043 12355 HIRAGANA LETTER SMALL I + い i5 3044 12356 HIRAGANA LETTER I + ぅ U5 3045 12357 HIRAGANA LETTER SMALL U + う u5 3046 12358 HIRAGANA LETTER U + ぇ E5 3047 12359 HIRAGANA LETTER SMALL E + え e5 3048 12360 HIRAGANA LETTER E + ぉ O5 3049 12361 HIRAGANA LETTER SMALL O + お o5 304A 12362 HIRAGANA LETTER O + か ka 304B 12363 HIRAGANA LETTER KA + が ga 304C 12364 HIRAGANA LETTER GA + き ki 304D 12365 HIRAGANA LETTER KI + ぎ gi 304E 12366 HIRAGANA LETTER GI + く ku 304F 12367 HIRAGANA LETTER KU + ぐ gu 3050 12368 HIRAGANA LETTER GU + け ke 3051 12369 HIRAGANA LETTER KE + げ ge 3052 12370 HIRAGANA LETTER GE + こ ko 3053 12371 HIRAGANA LETTER KO + ご go 3054 12372 HIRAGANA LETTER GO + さ sa 3055 12373 HIRAGANA LETTER SA + ざ za 3056 12374 HIRAGANA LETTER ZA + し si 3057 12375 HIRAGANA LETTER SI + じ zi 3058 12376 HIRAGANA LETTER ZI + す su 3059 12377 HIRAGANA LETTER SU + ず zu 305A 12378 HIRAGANA LETTER ZU + せ se 305B 12379 HIRAGANA LETTER SE + ぜ ze 305C 12380 HIRAGANA LETTER ZE + そ so 305D 12381 HIRAGANA LETTER SO + ぞ zo 305E 12382 HIRAGANA LETTER ZO + た ta 305F 12383 HIRAGANA LETTER TA + だ da 3060 12384 HIRAGANA LETTER DA + ち ti 3061 12385 HIRAGANA LETTER TI + ぢ di 3062 12386 HIRAGANA LETTER DI + っ tU 3063 12387 HIRAGANA LETTER SMALL TU + つ tu 3064 12388 HIRAGANA LETTER TU + づ du 3065 12389 HIRAGANA LETTER DU + て te 3066 12390 HIRAGANA LETTER TE + で de 3067 12391 HIRAGANA LETTER DE + と to 3068 12392 HIRAGANA LETTER TO + ど do 3069 12393 HIRAGANA LETTER DO + な na 306A 12394 HIRAGANA LETTER NA + に ni 306B 12395 HIRAGANA LETTER NI + ぬ nu 306C 12396 HIRAGANA LETTER NU + ね ne 306D 12397 HIRAGANA LETTER NE + の no 306E 12398 HIRAGANA LETTER NO + は ha 306F 12399 HIRAGANA LETTER HA + ば ba 3070 12400 HIRAGANA LETTER BA + ぱ pa 3071 12401 HIRAGANA LETTER PA + ひ hi 3072 12402 HIRAGANA LETTER HI + び bi 3073 12403 HIRAGANA LETTER BI + ぴ pi 3074 12404 HIRAGANA LETTER PI + ふ hu 3075 12405 HIRAGANA LETTER HU + ぶ bu 3076 12406 HIRAGANA LETTER BU + ぷ pu 3077 12407 HIRAGANA LETTER PU + へ he 3078 12408 HIRAGANA LETTER HE + べ be 3079 12409 HIRAGANA LETTER BE + ぺ pe 307A 12410 HIRAGANA LETTER PE + ほ ho 307B 12411 HIRAGANA LETTER HO + ぼ bo 307C 12412 HIRAGANA LETTER BO + ぽ po 307D 12413 HIRAGANA LETTER PO + ま ma 307E 12414 HIRAGANA LETTER MA + み mi 307F 12415 HIRAGANA LETTER MI + む mu 3080 12416 HIRAGANA LETTER MU + め me 3081 12417 HIRAGANA LETTER ME + も mo 3082 12418 HIRAGANA LETTER MO + ゃ yA 3083 12419 HIRAGANA LETTER SMALL YA + や ya 3084 12420 HIRAGANA LETTER YA + ゅ yU 3085 12421 HIRAGANA LETTER SMALL YU + ゆ yu 3086 12422 HIRAGANA LETTER YU + ょ yO 3087 12423 HIRAGANA LETTER SMALL YO + よ yo 3088 12424 HIRAGANA LETTER YO + ら ra 3089 12425 HIRAGANA LETTER RA + り ri 308A 12426 HIRAGANA LETTER RI + る ru 308B 12427 HIRAGANA LETTER RU + れ re 308C 12428 HIRAGANA LETTER RE + ろ ro 308D 12429 HIRAGANA LETTER RO + ゎ wA 308E 12430 HIRAGANA LETTER SMALL WA + わ wa 308F 12431 HIRAGANA LETTER WA + ゐ wi 3090 12432 HIRAGANA LETTER WI + ゑ we 3091 12433 HIRAGANA LETTER WE + を wo 3092 12434 HIRAGANA LETTER WO + ん n5 3093 12435 HIRAGANA LETTER N ` + ゔ vu 3094 12436 HIRAGANA LETTER VU + ゛ "5 309B 12443 KATAKANA-HIRAGANA VOICED SOUND MARK + ゜ 05 309C 12444 KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK + ゝ *5 309D 12445 HIRAGANA ITERATION MARK + ゞ +5 309E 12446 HIRAGANA VOICED ITERATION MARK + ァ a6 30A1 12449 KATAKANA LETTER SMALL A + ア A6 30A2 12450 KATAKANA LETTER A + ィ i6 30A3 12451 KATAKANA LETTER SMALL I + イ I6 30A4 12452 KATAKANA LETTER I + ゥ u6 30A5 12453 KATAKANA LETTER SMALL U + ウ U6 30A6 12454 KATAKANA LETTER U + ェ e6 30A7 12455 KATAKANA LETTER SMALL E + エ E6 30A8 12456 KATAKANA LETTER E + ォ o6 30A9 12457 KATAKANA LETTER SMALL O + オ O6 30AA 12458 KATAKANA LETTER O + カ Ka 30AB 12459 KATAKANA LETTER KA + ガ Ga 30AC 12460 KATAKANA LETTER GA + キ Ki 30AD 12461 KATAKANA LETTER KI + ギ Gi 30AE 12462 KATAKANA LETTER GI + ク Ku 30AF 12463 KATAKANA LETTER KU + グ Gu 30B0 12464 KATAKANA LETTER GU + ケ Ke 30B1 12465 KATAKANA LETTER KE + ゲ Ge 30B2 12466 KATAKANA LETTER GE + コ Ko 30B3 12467 KATAKANA LETTER KO + ゴ Go 30B4 12468 KATAKANA LETTER GO + サ Sa 30B5 12469 KATAKANA LETTER SA + ザ Za 30B6 12470 KATAKANA LETTER ZA + シ Si 30B7 12471 KATAKANA LETTER SI + ジ Zi 30B8 12472 KATAKANA LETTER ZI + ス Su 30B9 12473 KATAKANA LETTER SU + ズ Zu 30BA 12474 KATAKANA LETTER ZU + セ Se 30BB 12475 KATAKANA LETTER SE + ゼ Ze 30BC 12476 KATAKANA LETTER ZE + ソ So 30BD 12477 KATAKANA LETTER SO + ゾ Zo 30BE 12478 KATAKANA LETTER ZO + タ Ta 30BF 12479 KATAKANA LETTER TA + ダ Da 30C0 12480 KATAKANA LETTER DA + チ Ti 30C1 12481 KATAKANA LETTER TI + ヂ Di 30C2 12482 KATAKANA LETTER DI + ッ TU 30C3 12483 KATAKANA LETTER SMALL TU + ツ Tu 30C4 12484 KATAKANA LETTER TU + ヅ Du 30C5 12485 KATAKANA LETTER DU + テ Te 30C6 12486 KATAKANA LETTER TE + デ De 30C7 12487 KATAKANA LETTER DE + ト To 30C8 12488 KATAKANA LETTER TO + ド Do 30C9 12489 KATAKANA LETTER DO + ナ Na 30CA 12490 KATAKANA LETTER NA + ニ Ni 30CB 12491 KATAKANA LETTER NI + ヌ Nu 30CC 12492 KATAKANA LETTER NU + ネ Ne 30CD 12493 KATAKANA LETTER NE + ノ No 30CE 12494 KATAKANA LETTER NO + ハ Ha 30CF 12495 KATAKANA LETTER HA + バ Ba 30D0 12496 KATAKANA LETTER BA + パ Pa 30D1 12497 KATAKANA LETTER PA + ヒ Hi 30D2 12498 KATAKANA LETTER HI + ビ Bi 30D3 12499 KATAKANA LETTER BI + ピ Pi 30D4 12500 KATAKANA LETTER PI + フ Hu 30D5 12501 KATAKANA LETTER HU + ブ Bu 30D6 12502 KATAKANA LETTER BU + プ Pu 30D7 12503 KATAKANA LETTER PU + ヘ He 30D8 12504 KATAKANA LETTER HE + ベ Be 30D9 12505 KATAKANA LETTER BE + ペ Pe 30DA 12506 KATAKANA LETTER PE + ホ Ho 30DB 12507 KATAKANA LETTER HO + ボ Bo 30DC 12508 KATAKANA LETTER BO + ポ Po 30DD 12509 KATAKANA LETTER PO + マ Ma 30DE 12510 KATAKANA LETTER MA + ミ Mi 30DF 12511 KATAKANA LETTER MI + ム Mu 30E0 12512 KATAKANA LETTER MU + メ Me 30E1 12513 KATAKANA LETTER ME + モ Mo 30E2 12514 KATAKANA LETTER MO + ャ YA 30E3 12515 KATAKANA LETTER SMALL YA + ヤ Ya 30E4 12516 KATAKANA LETTER YA + ュ YU 30E5 12517 KATAKANA LETTER SMALL YU + ユ Yu 30E6 12518 KATAKANA LETTER YU + ョ YO 30E7 12519 KATAKANA LETTER SMALL YO + ヨ Yo 30E8 12520 KATAKANA LETTER YO + ラ Ra 30E9 12521 KATAKANA LETTER RA + リ Ri 30EA 12522 KATAKANA LETTER RI + ル Ru 30EB 12523 KATAKANA LETTER RU + レ Re 30EC 12524 KATAKANA LETTER RE + ロ Ro 30ED 12525 KATAKANA LETTER RO + ヮ WA 30EE 12526 KATAKANA LETTER SMALL WA + ワ Wa 30EF 12527 KATAKANA LETTER WA + ヰ Wi 30F0 12528 KATAKANA LETTER WI + ヱ We 30F1 12529 KATAKANA LETTER WE + ヲ Wo 30F2 12530 KATAKANA LETTER WO + ン N6 30F3 12531 KATAKANA LETTER N ` + ヴ Vu 30F4 12532 KATAKANA LETTER VU + ヵ KA 30F5 12533 KATAKANA LETTER SMALL KA + ヶ KE 30F6 12534 KATAKANA LETTER SMALL KE + ヷ Va 30F7 12535 KATAKANA LETTER VA + ヸ Vi 30F8 12536 KATAKANA LETTER VI + ヹ Ve 30F9 12537 KATAKANA LETTER VE + ヺ Vo 30FA 12538 KATAKANA LETTER VO + ・ .6 30FB 12539 KATAKANA MIDDLE DOT + ー -6 30FC 12540 KATAKANA-HIRAGANA PROLONGED SOUND MARK + ヽ *6 30FD 12541 KATAKANA ITERATION MARK + ヾ +6 30FE 12542 KATAKANA VOICED ITERATION MARK + ㄅ b4 3105 12549 BOPOMOFO LETTER B + ㄆ p4 3106 12550 BOPOMOFO LETTER P + ㄇ m4 3107 12551 BOPOMOFO LETTER M + ㄈ f4 3108 12552 BOPOMOFO LETTER F + ㄉ d4 3109 12553 BOPOMOFO LETTER D + ㄊ t4 310A 12554 BOPOMOFO LETTER T + ㄋ n4 310B 12555 BOPOMOFO LETTER N ` + ㄌ l4 310C 12556 BOPOMOFO LETTER L + ㄍ g4 310D 12557 BOPOMOFO LETTER G + ㄎ k4 310E 12558 BOPOMOFO LETTER K + ㄏ h4 310F 12559 BOPOMOFO LETTER H + ㄐ j4 3110 12560 BOPOMOFO LETTER J + ㄑ q4 3111 12561 BOPOMOFO LETTER Q + ㄒ x4 3112 12562 BOPOMOFO LETTER X + ㄓ zh 3113 12563 BOPOMOFO LETTER ZH + ㄔ ch 3114 12564 BOPOMOFO LETTER CH + ㄕ sh 3115 12565 BOPOMOFO LETTER SH + ㄖ r4 3116 12566 BOPOMOFO LETTER R + ㄗ z4 3117 12567 BOPOMOFO LETTER Z + ㄘ c4 3118 12568 BOPOMOFO LETTER C + ㄙ s4 3119 12569 BOPOMOFO LETTER S + ㄚ a4 311A 12570 BOPOMOFO LETTER A + ㄛ o4 311B 12571 BOPOMOFO LETTER O + ㄜ e4 311C 12572 BOPOMOFO LETTER E + ㄞ ai 311E 12574 BOPOMOFO LETTER AI + ㄟ ei 311F 12575 BOPOMOFO LETTER EI + ㄠ au 3120 12576 BOPOMOFO LETTER AU + ㄡ ou 3121 12577 BOPOMOFO LETTER OU + ㄢ an 3122 12578 BOPOMOFO LETTER AN + ㄣ en 3123 12579 BOPOMOFO LETTER EN + ㄤ aN 3124 12580 BOPOMOFO LETTER ANG + ㄥ eN 3125 12581 BOPOMOFO LETTER ENG + ㄦ er 3126 12582 BOPOMOFO LETTER ER + ㄧ i4 3127 12583 BOPOMOFO LETTER I + ㄨ u4 3128 12584 BOPOMOFO LETTER U + ㄩ iu 3129 12585 BOPOMOFO LETTER IU + ㄪ v4 312A 12586 BOPOMOFO LETTER V + ㄫ nG 312B 12587 BOPOMOFO LETTER NG + ㄬ gn 312C 12588 BOPOMOFO LETTER GN + ㈠ 1c 3220 12832 PARENTHESIZED IDEOGRAPH ONE + ㈡ 2c 3221 12833 PARENTHESIZED IDEOGRAPH TWO + ㈢ 3c 3222 12834 PARENTHESIZED IDEOGRAPH THREE + ㈣ 4c 3223 12835 PARENTHESIZED IDEOGRAPH FOUR + ㈤ 5c 3224 12836 PARENTHESIZED IDEOGRAPH FIVE + ㈥ 6c 3225 12837 PARENTHESIZED IDEOGRAPH SIX + ㈦ 7c 3226 12838 PARENTHESIZED IDEOGRAPH SEVEN + ㈧ 8c 3227 12839 PARENTHESIZED IDEOGRAPH EIGHT + ㈨ 9c 3228 12840 PARENTHESIZED IDEOGRAPH NINE + ff ff FB00 64256 LATIN SMALL LIGATURE FF + fi fi FB01 64257 LATIN SMALL LIGATURE FI + fl fl FB02 64258 LATIN SMALL LIGATURE FL + ſt ft FB05 64261 LATIN SMALL LIGATURE LONG S T + st st FB06 64262 LATIN SMALL LIGATURE ST +< vim:tw=78:ts=8:noet:ft=help:norl: diff --git a/runtime/doc/editing.txt b/runtime/doc/editing.txt index c774d1ad38..2225e6e753 100644 --- a/runtime/doc/editing.txt +++ b/runtime/doc/editing.txt @@ -804,8 +804,8 @@ first line (the last line in Ex mode). *{arglist}* The wildcards in the argument list are expanded and the file names are sorted. -Thus you can use the command "vim *.c" to edit all the C files. From within -Vim the command ":n *.c" does the same. +Thus you can use the command `vim *.c` to edit all the C files. From within +Vim the command `:n *.c` does the same. White space is used to separate file names. Put a backslash before a space or tab to include it in a file name. E.g., to edit the single file "foo bar": > @@ -889,7 +889,7 @@ Example: > :args *.c :argdo set ff=unix | update This sets the 'fileformat' option to "unix" and writes the file if it is now -changed. This is done for all *.c files. +changed. This is done for all `*.c` files. Example: > :args *.[ch] @@ -1245,8 +1245,8 @@ If you want to always use ":confirm", set the 'confirm' option. |:diffsplit|, |:diffpatch|, |:pedit|, |:redir|, |:source|, |:update|, |:visual|, |:vsplit|, and |:qall| if 'confirm' is set. - {only in Win32 GUI, in console `browse edit` works - if the FileExplorer autocommand group exists} + Note: only in Win32 GUI; in console `:browse edit` + works if the FileExplorer autocommand group exists. When ":browse" is not possible you get an error message. If {command} doesn't support browsing, the {command} is executed without a dialog. @@ -1677,9 +1677,9 @@ mark a file as trusted or untrusted using the |:trust| command or the :trust [++deny] [++remove] [file] Manage trusted files. Without ++ options, :trust marks - [file] (or current buffer if no [file]) as trusted, - keyed on a hash of its contents. The trust list is - stored on disk, Nvim will re-use it after restarting. + the current buffer as trusted, keyed on a hash of its + contents. The trust list is stored on disk, Nvim will + re-use it after restarting. [++deny] marks [file] (or current buffer if no [file]) as untrusted: it will never be executed, 'exrc' will diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index aa53244dc8..3cd5ea13f6 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -39,7 +39,7 @@ List An ordered sequence of items, see |List| for details. Dictionary An associative, unordered array: Each entry has a key and a value. |Dictionary| - Examples: + Examples: > {"blue": "#0000ff", "red": "#ff0000"} #{blue: "#0000ff", red: "#ff0000"} @@ -841,19 +841,19 @@ Expression syntax summary, from least to most significant: |expr9| number number constant "string" string constant, backslash is special - 'string' string constant, ' is doubled + `'string'` string constant, ' is doubled [expr1, ...] |List| - {expr1: expr1, ...} |Dictionary| + `{expr1: expr1, ...}` |Dictionary| #{key: expr1, ...} |Dictionary| &option option value (expr1) nested expression variable internal variable va{ria}ble internal variable with curly braces $VAR environment variable - @r contents of register 'r' + @r contents of register "r" function(expr1, ...) function call func{ti}on(expr1, ...) function call with curly braces - {args -> expr1} lambda expression + `{args -> expr1}` lambda expression "..." indicates that the operations in this level can be concatenated. @@ -1547,7 +1547,7 @@ See below |functions|. ------------------------------------------------------------------------------ lambda expression *expr-lambda* *lambda* -{args -> expr1} lambda expression *E451* +`{args -> expr1}` lambda expression *E451* A lambda expression creates a new unnamed function which returns the result of evaluating |expr1|. Lambda expressions differ from |user-function|s in @@ -1846,7 +1846,7 @@ v:ctype The current locale setting for characters of the runtime v:dying Normally zero. When a deadly signal is caught it's set to one. When multiple signals are caught the number increases. Can be used in an autocommand to check if Vim didn't - terminate normally. {only works on Unix} + terminate normally. Example: > :au VimLeave * if v:dying | echo "\nAAAAaaaarrrggghhhh!!!\n" | endif < Note: if another deadly signal is caught when v:dying is one, @@ -2493,7 +2493,7 @@ This does NOT work: > *:let/=* *:let%=* *:let.=* *:let..=* *E734* :let {var} += {expr1} Like ":let {var} = {var} + {expr1}". :let {var} -= {expr1} Like ":let {var} = {var} - {expr1}". -:let {var} *= {expr1} Like ":let {var} = {var} * {expr1}". +`:let {var} *= {expr1}` Like ":let {var} = {var} * {expr1}". :let {var} /= {expr1} Like ":let {var} = {var} / {expr1}". :let {var} %= {expr1} Like ":let {var} = {var} % {expr1}". :let {var} .= {expr1} Like ":let {var} = {var} . {expr1}". @@ -2799,7 +2799,7 @@ text... let mylist = [1, 2, 3] lockvar 0 mylist let mylist[0] = 77 " OK - call add(mylist, 4] " OK + call add(mylist, 4) " OK let mylist = [7, 8, 9] " Error! < *E743* For unlimited depth use [!] and omit [depth]. diff --git a/runtime/doc/filetype.txt b/runtime/doc/filetype.txt index 48d6aed0f2..bd74f45091 100644 --- a/runtime/doc/filetype.txt +++ b/runtime/doc/filetype.txt @@ -54,9 +54,9 @@ you can either set the 'filetype' option manually, or add a modeline to your file. Example, for an IDL file use the command: > :set filetype=idl -or add this |modeline| to the file: - /* vim: set filetype=idl : */ ~ - +or add this |modeline| to the file: > + /* vim: set filetype=idl : */ +< *:filetype-plugin-on* You can enable loading the plugin files for specific file types with: > :filetype plugin on @@ -136,38 +136,38 @@ what kind of file it is. This doesn't always work. A number of global variables can be used to overrule the filetype used for certain extensions: file name variable ~ - *.asa g:filetype_asa |ft-aspvbs-syntax| |ft-aspperl-syntax| - *.asm g:asmsyntax |ft-asm-syntax| - *.asp g:filetype_asp |ft-aspvbs-syntax| |ft-aspperl-syntax| - *.bas g:filetype_bas |ft-basic-syntax| - *.cfg g:filetype_cfg - *.cls g:filetype_cls - *.csh g:filetype_csh |ft-csh-syntax| - *.dat g:filetype_dat - *.frm g:filetype_frm |ft-form-syntax| - *.fs g:filetype_fs |ft-forth-syntax| - *.i g:filetype_i |ft-progress-syntax| - *.inc g:filetype_inc - *.lsl g:filetype_lsl - *.m g:filetype_m |ft-mathematica-syntax| - *.mod g:filetype_mod - *.p g:filetype_p |ft-pascal-syntax| - *.pl g:filetype_pl - *.pp g:filetype_pp |ft-pascal-syntax| - *.prg g:filetype_prg - *.r g:filetype_r - *.sig g:filetype_sig - *.sql g:filetype_sql |ft-sql-syntax| - *.src g:filetype_src - *.sys g:filetype_sys - *.sh g:bash_is_sh |ft-sh-syntax| - *.tex g:tex_flavor |ft-tex-plugin| - *.typ g:filetype_typ - *.w g:filetype_w |ft-cweb-syntax| + `*.asa` g:filetype_asa |ft-aspvbs-syntax| |ft-aspperl-syntax| + `*.asm` g:asmsyntax |ft-asm-syntax| + `*.asp` g:filetype_asp |ft-aspvbs-syntax| |ft-aspperl-syntax| + `*.bas` g:filetype_bas |ft-basic-syntax| + `*.cfg` g:filetype_cfg + `*.cls` g:filetype_cls + `*.csh` g:filetype_csh |ft-csh-syntax| + `*.dat` g:filetype_dat + `*.frm` g:filetype_frm |ft-form-syntax| + `*.fs` g:filetype_fs |ft-forth-syntax| + `*.i` g:filetype_i |ft-progress-syntax| + `*.inc` g:filetype_inc + `*.lsl` g:filetype_lsl + `*.m` g:filetype_m |ft-mathematica-syntax| + `*.mod` g:filetype_mod + `*.p` g:filetype_p |ft-pascal-syntax| + `*.pl` g:filetype_pl + `*.pp` g:filetype_pp |ft-pascal-syntax| + `*.prg` g:filetype_prg + `*.r` g:filetype_r + `*.sig` g:filetype_sig + `*.sql` g:filetype_sql |ft-sql-syntax| + `*.src` g:filetype_src + `*.sys` g:filetype_sys + `*.sh` g:bash_is_sh |ft-sh-syntax| + `*.tex` g:tex_flavor |ft-tex-plugin| + `*.typ` g:filetype_typ + `*.w` g:filetype_w |ft-cweb-syntax| For a few filetypes the global variable is used only when the filetype could not be detected: - *.r g:filetype_r |ft-rexx-syntax| + `*.r` g:filetype_r |ft-rexx-syntax| *filetype-ignore* To avoid that certain files are being inspected, the g:ft_ignore_pat variable @@ -619,7 +619,7 @@ To use Nvim as a manpager: > export MANPAGER='nvim +Man!' Note that when running `man` from the shell and with that `MANPAGER` in your -environment, `man` will pre-format the manpage using `groff`. Thus, Neovim +environment, `man` will pre-format the manpage using `groff`. Thus, Nvim will inevitably display the manual page as it was passed to it from stdin. One of the caveats of this is that the width will _always_ be hard-wrapped and not soft wrapped as with `g:man_hardwrap=0`. You can set in your environment: > @@ -668,9 +668,8 @@ QUERY *ft-query-plugin* Linting of tree-sitter queries for installed parsers using -|lua-treesitter-query_linter| is enabled by default on -`BufEnter` and `BufWrite`. To change the events that -trigger linting, use >lua +|vim.treesitter.query.lint()| is enabled by default on `BufEnter` and +`BufWrite`. To change the events that trigger linting, use >lua vim.g.query_lint_on = { 'InsertLeave', 'TextChanged' } < @@ -881,7 +880,7 @@ file: |ft_sql.txt|. TEX *ft-tex-plugin* *g:tex_flavor* -If the first line of a *.tex file has the form > +If the first line of a `*.tex` file has the form > %&<format> then this determined the file type: plaintex (for plain TeX), context (for ConTeXt), or tex (for LaTeX). Otherwise, the file is searched for keywords to diff --git a/runtime/doc/fold.txt b/runtime/doc/fold.txt index 78cc14651e..24d7605e4a 100644 --- a/runtime/doc/fold.txt +++ b/runtime/doc/fold.txt @@ -253,7 +253,7 @@ This does not work properly when: know what to do. - Folds nearby use a level number in their marker which gets in the way. - The line is inside a comment, 'commentstring' isn't empty and nested - comments don't work. For example with C: adding /* {{{ */ inside a comment + comments don't work. For example with C: adding `/* {{{ */` inside a comment will truncate the existing comment. Either put the marker before or after the comment, or add the marker manually. Generally it's not a good idea to let Vim create markers when you already have diff --git a/runtime/doc/ft_ada.txt b/runtime/doc/ft_ada.txt index 3321228009..3b7d6a73dd 100644 --- a/runtime/doc/ft_ada.txt +++ b/runtime/doc/ft_ada.txt @@ -112,8 +112,8 @@ NOTE: "gnat xref -v" is very tricky to use as it has almost no diagnostic your .ali files. 2) "gnat xref -v ../Include/adacl.ads" won't work - use the "gnat xref -v -aI../Include adacl.ads" instead. -3) "gnat xref -v -aI../Include *.ad?" won't work - use "cd ../Include" and - then "gnat xref -v *.ad?" +3) `gnat xref -v -aI../Include *.ad?` won't work - use "cd ../Include" and + then `gnat xref -v *.ad?` 4) Project manager support is completely broken - don't even try "gnat xref -Padacl.gpr". 5) Vim is faster when the tags file is sorted - use "sort --unique diff --git a/runtime/doc/ft_raku.txt b/runtime/doc/ft_raku.txt index 3fafa6e224..c9bbe4bd04 100644 --- a/runtime/doc/ft_raku.txt +++ b/runtime/doc/ft_raku.txt @@ -35,10 +35,10 @@ Some of them are available with standard Vim digraphs: (- ∈ ?= ≅ != ≠ ~ -) ∋ ?- ≃ ~ -The Greek alphabet is available with "*" followed by a similar Latin symbol: - *p π ~ - *t τ ~ - *X × ~ +The Greek alphabet is available with "*" followed by a similar Latin symbol: > + *p π + *t τ + *X × Numbers, subscripts and superscripts are available with 's' and 'S': 0s ₀ 0S ⁰ ~ diff --git a/runtime/doc/ft_sql.txt b/runtime/doc/ft_sql.txt index 21a244e67a..68e26d2a5b 100644 --- a/runtime/doc/ft_sql.txt +++ b/runtime/doc/ft_sql.txt @@ -728,7 +728,7 @@ You can create as many additional key maps as you like. Generally, the maps will be specifying different syntax highlight groups. If you do not wish the default maps created or the key choices do not work on -your platform (often a case on *nix) you define the following variable in +your platform (often a case on unix) you define the following variable in your |init.vim|: > let g:omni_sql_no_default_maps = 1 @@ -738,7 +738,7 @@ which allows you to make customizations without changing the files that are included with the Vim distribution. If you wish to customize the maps create an after/ftplugin/sql.vim (see |after-directory|) and place the same maps from the ftplugin/sql.vim in it using your own key strokes. <C-C> was -chosen since it will work on both Windows and *nix platforms. On the windows +chosen since it will work on both Windows and unix platforms. On the windows platform you can also use <C-Space> or ALT keys. diff --git a/runtime/doc/gui.txt b/runtime/doc/gui.txt index 5d44a7d239..bf62dba539 100644 --- a/runtime/doc/gui.txt +++ b/runtime/doc/gui.txt @@ -466,12 +466,11 @@ If no argument is given after :menu at all, then ALL menu items are shown for the appropriate mode (e.g., Command-line mode for :cmenu). Special characters in the list, just before the rhs: -* The menu was defined with "nore" to disallow remapping. -& The menu was defined with "<script>" to allow remapping script-local - mappings only. -s The menu was defined with "<silent>" to avoid showing what it is - mapped to when triggered. -- The menu was disabled. +• * Menu was defined with "nore" to disallow remapping. +• & Menu was defined with "<script>" to allow remapping script-local mappings. +• s Menu was defined with "<silent>" to avoid showing what it is mapped to + when triggered. +• - Menu was disabled. Note that hitting <Tab> while entering a menu name after a menu command may be used to complete the name of the menu item. diff --git a/runtime/doc/help.txt b/runtime/doc/help.txt index aefaa0b7df..651dce08bf 100644 --- a/runtime/doc/help.txt +++ b/runtime/doc/help.txt @@ -100,7 +100,7 @@ ADVANCED EDITING ------------------------------------------------------------------------------ API (EXTENSIBILITY/SCRIPTING/PLUGINS) -|api| Nvim API via RPC, Lua and VimL +|api| Nvim API via RPC, Lua and Vimscript |ui| Nvim UI protocol |lua-guide| Nvim Lua guide |lua| Lua API diff --git a/runtime/doc/helphelp.txt b/runtime/doc/helphelp.txt index ca26332017..efcc6afae9 100644 --- a/runtime/doc/helphelp.txt +++ b/runtime/doc/helphelp.txt @@ -317,9 +317,9 @@ standard Vim help files, except for the first line. If you are writing a new help file it's best to copy one of the existing files and use it as a template. -The first line in a help file should have the following format: +The first line in a help file should have the following format: > -*plugin_name.txt* {short description of the plugin} + *plugin_name.txt* {short description of the plugin} The first field is a help tag where ":help plugin_name" will jump to. The remainder of the line, after a Tab, describes the plugin purpose in a short @@ -357,10 +357,11 @@ parameter, surround it in backticks, eg. `~/.path/to/init.vim`. HIGHLIGHTING -To define a column heading, use a tilde character at the end of the line. -This will highlight the column heading in a different color. E.g. +To define a column heading, use a tilde character at the end of the line, +preceded by a space. This will highlight the column heading in a different +color. E.g. -Column heading~ +Column heading ~ To separate sections in a help file, place a series of '=' characters in a line starting from the first column. The section separator line is highlighted @@ -383,8 +384,8 @@ The following are highlighted differently in a Vim help file: The word "Note", "Notes" and similar automagically receive distinctive highlighting. So do these: - *Todo something to do - *Error something wrong + Todo something to do + Error something wrong You can find the details in $VIMRUNTIME/syntax/help.vim diff --git a/runtime/doc/if_perl.txt b/runtime/doc/if_perl.txt index c3b49dc5bb..1fa96e2657 100644 --- a/runtime/doc/if_perl.txt +++ b/runtime/doc/if_perl.txt @@ -108,7 +108,7 @@ to the next. ============================================================================== 2. The VIM module *perl-vim* -Perl code gets all of its access to Neovim via the "VIM" module. +Perl code gets all of its access to Nvim via the "VIM" module. Overview > print "Hello" # displays a message diff --git a/runtime/doc/if_pyth.txt b/runtime/doc/if_pyth.txt index a96c9fcc0a..c7f431481f 100644 --- a/runtime/doc/if_pyth.txt +++ b/runtime/doc/if_pyth.txt @@ -187,8 +187,8 @@ vim.foreach_rtp(callable) *python-foreach_rtp* are no longer paths. If stopped in case callable returned non-None, vim.foreach_rtp function returns the value returned by callable. -vim.chdir(*args, **kwargs) *python-chdir* -vim.fchdir(*args, **kwargs) *python-fchdir* +`vim.chdir(*args, **kwargs)` *python-chdir* +`vim.fchdir(*args, **kwargs)` *python-fchdir* Run os.chdir or os.fchdir, then all appropriate vim stuff. Note: you should not use these functions directly, use os.chdir and os.fchdir instead. Behavior of vim.fchdir is undefined in case @@ -575,8 +575,8 @@ Python 3 *python3* As Python 3 is the only supported version in Nvim, "python" is synonymous with "python3" in the current version. However, code that aims to support older -versions of Neovim, as well as Vim, should prefer to use "python3" -variants explicitly if Python 3 is required. +versions of Nvim, as well as Vim, should prefer to use "python3" variants +explicitly if Python 3 is required. *:py3* *:python3* :[range]py3 {stmt} diff --git a/runtime/doc/indent.txt b/runtime/doc/indent.txt index ccff09d7fc..853facdaa0 100644 --- a/runtime/doc/indent.txt +++ b/runtime/doc/indent.txt @@ -553,9 +553,9 @@ The examples below assume a 'shiftwidth' of 4. 20 lines). *cino-star* - *N Vim searches for unclosed comments at most N lines away. This + `*N` Vim searches for unclosed comments at most N lines away. This limits the time needed to search for the start of a comment. - If your /* */ comments stop indenting after N lines this is the + If your `/* */` comments stop indenting after N lines this is the value you will want to change. (default 70 lines). @@ -574,7 +574,7 @@ The examples below assume a 'shiftwidth' of 4. them like every other preprocessor directive. -The defaults, spelled out in full, are: +The defaults, spelled out in full, are: > cinoptions=>s,e0,n0,f0,{0,}0,^0,L-1,:s,=s,l0,b0,gs,hs,N0,E0,ps,ts,is,+s, c3,C0,/0,(2s,us,U0,w0,W0,k0,m0,j0,J0,)20,*70,#0,P0 @@ -1060,19 +1060,19 @@ be configured by setting the following keys in the |Dictionary| b:sh_indent_defaults to a specific amount or to a |Funcref| that references a function that will return the amount desired: -b:sh_indent_options['default'] Default amount of indent. +b:sh_indent_options["default"] Default amount of indent. -b:sh_indent_options['continuation-line'] +b:sh_indent_options["continuation-line"] Amount of indent to add to a continued line. -b:sh_indent_options['case-labels'] +b:sh_indent_options["case-labels"] Amount of indent to add for case labels. (not actually implemented) -b:sh_indent_options['case-statements'] +b:sh_indent_options["case-statements"] Amount of indent to add for case statements. -b:sh_indent_options['case-breaks'] +b:sh_indent_options["case-breaks"] Amount of indent to add (or more likely remove) for case breaks. diff --git a/runtime/doc/index.txt b/runtime/doc/index.txt index fbd3fccec0..b0ead51d0c 100644 --- a/runtime/doc/index.txt +++ b/runtime/doc/index.txt @@ -475,7 +475,7 @@ tag command action in op-pending and Visual mode ~ |v_a)| a) same as ab |v_a<| a< "a <>" from '<' to the matching '>' |v_a>| a> same as a< -|v_aB| aB "a Block" from "[{" to "]}" (with brackets) +|v_aB| aB "a Block" from `[{` to `]}` (with brackets) |v_aW| aW "a WORD" (with white space) |v_a[| a[ "a []" from '[' to the matching ']' |v_a]| a] same as a[ @@ -493,7 +493,7 @@ tag command action in op-pending and Visual mode ~ |v_i)| i) same as ib |v_i<| i< "inner <>" from '<' to the matching '>' |v_i>| i> same as i< -|v_iB| iB "inner Block" from "[{" and "]}" +|v_iB| iB "inner Block" from `[{` and `]}` |v_iW| iW "inner WORD" |v_i[| i[ "inner []" from '[' to the matching ']' |v_i]| i] same as i[ @@ -1110,7 +1110,7 @@ to terminal mode. You found it, Arthur! *holy-grail* ============================================================================== -6. EX commands *ex-commands* *ex-cmd-index* *:index* +6. EX commands *Ex-commands* *ex-cmd-index* *:index* This is a brief but complete listing of all the ":" commands, without mentioning any arguments. The optional part of the command name is inside []. diff --git a/runtime/doc/insert.txt b/runtime/doc/insert.txt index de1b850a3f..a1e0675b36 100644 --- a/runtime/doc/insert.txt +++ b/runtime/doc/insert.txt @@ -1081,6 +1081,26 @@ Stop completion *compl-stop* CTRL-X CTRL-Z Stop completion without changing the text. +AUTO-COMPLETION *compl-autocomplete* + +To get basic "autocompletion" without installing a plugin, try this script: >lua + + local triggers = {"."} + vim.api.nvim_create_autocmd("InsertCharPre", { + buffer = vim.api.nvim_get_current_buf(), + callback = function() + if vim.fn.pumvisible() == 1 then + return + end + local char = vim.v.char + if vim.tbl_contains(triggers, char) then + local key = vim.keycode("<C-x><C-n>") + vim.api.nvim_feedkeys(key, "m", false) + end + end + }) +< + FUNCTIONS FOR FINDING COMPLETIONS *complete-functions* This applies to 'completefunc', 'thesaurusfunc' and 'omnifunc'. diff --git a/runtime/doc/intro.txt b/runtime/doc/intro.txt index 15fe3838b7..ab380184fc 100644 --- a/runtime/doc/intro.txt +++ b/runtime/doc/intro.txt @@ -83,7 +83,7 @@ For the most recent information about sponsoring look on the Vim web site: https://www.vim.org/sponsor/ -Neovim development is funded separately from Vim: +Nvim development is funded separately from Vim: https://neovim.io/#sponsor @@ -517,16 +517,15 @@ CTRL-O in Insert mode you get a beep but you are still in Insert mode, type <Esc> again. *i_esc* - TO mode ~ - Normal Visual Select Insert Replace Cmd-line Ex ~ -FROM mode ~ -Normal v V ^V `*4` *1 R gR : / ? ! Q -Visual `*2` ^G c C -- : -- -Select `*5` ^O ^G `*6` -- -- -- -Insert <Esc> -- -- <Insert> -- -- -Replace <Esc> -- -- <Insert> -- -- -Command-line `*3` -- -- :start -- -- -Ex :vi -- -- -- -- -- + FROM mode TO mode ~ + Normal Visual Select Insert Replace Cmd-line Ex > + Normal v V ^V *4 *1 R gR : / ? ! Q + Visual *2 ^G c C -- : -- + Select *5 ^O ^G *6 -- -- -- + Insert <Esc> -- -- <Insert> -- -- + Replace <Esc> -- -- <Insert> -- -- + Command-line `*3` -- -- :start -- -- + Ex :vi -- -- -- -- -- -- not possible diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt index 581cfd5348..c6f395dd15 100644 --- a/runtime/doc/lsp.txt +++ b/runtime/doc/lsp.txt @@ -24,60 +24,59 @@ QUICKSTART *lsp-quickstart* Nvim provides an LSP client, but the servers are provided by third parties. Follow these steps to get LSP features: - 1. Install language servers using your package manager or by - following the upstream installation instruction. +1. Install language servers using your package manager or by following the + upstream installation instruction. You can find language servers here: + https://microsoft.github.io/language-server-protocol/implementors/servers/ - A list of language servers is available at: +2. Configure the LSP client per language server. See |vim.lsp.start()| or use + this minimal example as a guide: >lua - https://microsoft.github.io/language-server-protocol/implementors/servers/ - - 2. Configure the LSP client per language server. - A minimal example: ->lua vim.lsp.start({ name = 'my-server-name', cmd = {'name-of-language-server-executable'}, root_dir = vim.fs.dirname(vim.fs.find({'setup.py', 'pyproject.toml'}, { upward = true })[1]), }) < - See |vim.lsp.start()| for details. +3. Check that the server attached to the buffer: > + :lua =vim.lsp.get_active_clients() - 3. Configure keymaps and autocmds to utilize LSP features. - See |lsp-config|. +4. Configure keymaps and autocmds to use LSP features. See |lsp-config|. *lsp-config* - -Starting a LSP client will automatically report diagnostics via -|vim.diagnostic|. Read |vim.diagnostic.config()| to learn how to customize the -display. - -It also sets some buffer options if the language server supports the -functionality and if the options are otherwise empty or have the default -values set by Nvim runtime files (e.g. a ftplugin). In the latter case, -the default values are not restored when the LSP client is detached from -the buffer. - -- 'omnifunc' is set to |vim.lsp.omnifunc()|. This allows to trigger completion - using |i_CTRL-X_CTRL-O| + *lsp-defaults* +When the LSP client starts it enables diagnostics |vim.diagnostic| (see +|vim.diagnostic.config()| to customize). It also sets various default options, +listed below, if (1) the language server supports the functionality and (2) +the options are empty or were set by the builtin runtime (ftplugin) files. The +options are not restored when the LSP client is stopped or detached. + +- 'omnifunc' is set to |vim.lsp.omnifunc()|, use |i_CTRL-X_CTRL-O| to trigger + completion. - 'tagfunc' is set to |vim.lsp.tagfunc()|. This enables features like go-to-definition, |:tjump|, and keymaps like |CTRL-]|, |CTRL-W_]|, |CTRL-W_}| to utilize the language server. -- 'formatexpr' is set to |vim.lsp.formatexpr()| if both 'formatprg' and - 'formatexpr' are empty. This allows to format lines via |gq| if the language - server supports it. +- 'formatexpr' is set to |vim.lsp.formatexpr()|, so you can format lines via + |gq| if the language server supports it. + - To opt out of this use |gw| instead of gq, or set 'formatexpr' on LspAttach. -To use other LSP features like hover, rename, etc. you can setup some -additional keymaps. It's recommended to setup them in a |LspAttach| autocmd to -ensure they're only active if there is a LSP client running. An example: ->lua + *lsp-defaults-disable* +To override the above defaults, set or unset the options on |LspAttach|: >lua + vim.api.nvim_create_autocmd('LspAttach', { + callback = function(ev) + vim.bo[ev.buf].formatexpr = nil + vim.bo[ev.buf].omnifunc = nil + end, + }) + +To use other LSP features like hover, rename, etc. you can set other keymaps +on |LspAttach|. Example: >lua vim.api.nvim_create_autocmd('LspAttach', { callback = function(args) vim.keymap.set('n', 'K', vim.lsp.buf.hover, { buffer = args.buf }) end, }) -< -The most used functions are: +The most common functions are: - |vim.lsp.buf.hover()| - |vim.lsp.buf.format()| @@ -101,11 +100,9 @@ calls behind capability checks: < To learn what capabilities are available you can run the following command in -a buffer with a started LSP client: +a buffer with a started LSP client: >vim ->vim :lua =vim.lsp.get_active_clients()[1].server_capabilities -< Full list of features provided by default can be found in |lsp-buf|. @@ -113,47 +110,28 @@ Full list of features provided by default can be found in |lsp-buf|. FAQ *lsp-faq* - Q: How to force-reload LSP? - A: Stop all clients, then reload the buffer. >vim - +- A: Stop all clients, then reload the buffer. >vim :lua vim.lsp.stop_client(vim.lsp.get_active_clients()) :edit - Q: Why isn't completion working? - A: In the buffer where you want to use LSP, check that 'omnifunc' is set to - "v:lua.vim.lsp.omnifunc": >vim - - :verbose set omnifunc? - -< Some other plugin may be overriding the option. To avoid that, you could - set the option in an |after-directory| ftplugin, e.g. - "after/ftplugin/python.vim". +- A: In the buffer where you want to use LSP, check that 'omnifunc' is set to + "v:lua.vim.lsp.omnifunc": `:verbose set omnifunc?` + - Some other plugin may be overriding the option. To avoid that you could + set the option in an |after-directory| ftplugin, e.g. + "after/ftplugin/python.vim". - Q: How do I run a request synchronously (e.g. for formatting on file save)? - A: Check if the function has an `async` parameter and set the value to - false. - - E.g. code formatting: >vim +- A: Check if the function has an `async` parameter and set the value to + false. E.g. code formatting: >vim " Auto-format *.rs (rust) files prior to saving them " (async = false is the default for format) autocmd BufWritePre *.rs lua vim.lsp.buf.format({ async = false }) < -- Q: How can I disable LSP formatting when using the |gq| command? - A: To use the default internal formatting method and bypass the LSP client's - 'formatexpr', use |gw| instead. - Alternatively you can completely disable LSP formatting with gq by - unsetting 'formatexpr': - ->lua - vim.api.nvim_create_autocmd('LspAttach', { - callback = function(args) - vim.bo[args.buf].formatexpr = nil - end, - }) -< *lsp-vs-treesitter* - Q: How do LSP and Treesitter compare? - A: LSP requires a client and language server. The language server uses +- A: LSP requires a client and language server. The language server uses semantic analysis to understand code at a project level. This provides language servers with the ability to rename across files, find definitions in external libraries and more. @@ -176,128 +154,84 @@ The `vim.lsp.buf_…` functions perform operations for all LSP clients attached to the given buffer. |lsp-buf| LSP request/response handlers are implemented as Lua functions (see -|lsp-handler|). The |vim.lsp.handlers| table defines default handlers used -when creating a new client. Keys are LSP method names: >vim - - :lua print(vim.inspect(vim.tbl_keys(vim.lsp.handlers))) -< +|lsp-handler|). *lsp-method* -Methods are the names of requests and notifications as defined by the LSP -specification. These LSP requests/notifications are defined by default: - - callHierarchy/incomingCalls - callHierarchy/outgoingCalls - textDocument/codeAction - textDocument/completion - textDocument/declaration* - textDocument/definition - textDocument/documentHighlight - textDocument/documentSymbol - textDocument/formatting - textDocument/hover - textDocument/implementation* - textDocument/publishDiagnostics - textDocument/rangeFormatting - textDocument/references - textDocument/rename - textDocument/signatureHelp - textDocument/typeDefinition* - window/logMessage - window/showMessage - window/showDocument - window/showMessageRequest - workspace/applyEdit - workspace/symbol - -* NOTE: These are sometimes not implemented by servers. - - *lsp-handler* +Requests and notifications defined by the LSP specification are referred to as +"LSP methods". The Nvim LSP client provides default handlers in the global +|vim.lsp.handlers| table, you can list them with this command: >vim -lsp-handlers are functions with special signatures that are designed to handle -responses and notifications from LSP servers. - -For |lsp-request|, each |lsp-handler| has this signature: > - - function(err, result, ctx, config) + :lua vim.print(vim.tbl_keys(vim.lsp.handlers)) < - Parameters: ~ - {err} (table|nil) - When the language server is unable to complete a - request, a table with information about the error is - sent. Otherwise, it is `nil`. See |lsp-response|. - {result} (Result | Params | nil) - When the language server is able to successfully - complete a request, this contains the `result` key of - the response. See |lsp-response|. - {ctx} (table) - Context describes additional calling state associated - with the handler. It consists of the following key, - value pairs: - - {method} (string) - The |lsp-method| name. - {client_id} (number) - The ID of the |vim.lsp.client|. - {bufnr} (Buffer) - Buffer handle, or 0 for current. - {params} (table|nil) - The parameters used in the original - request which resulted in this handler - call. - {config} (table) - Configuration for the handler. - - Each handler can define its own configuration table - that allows users to customize the behavior of a - particular handler. - - To configure a particular |lsp-handler|, see: - |lsp-handler-configuration| +They are also listed below. Note that handlers depend on server support: they +won't run if your server doesn't support them. + +- callHierarchy/incomingCalls +- callHierarchy/outgoingCalls +- textDocument/codeAction +- textDocument/completion +- textDocument/declaration* +- textDocument/definition +- textDocument/documentHighlight +- textDocument/documentSymbol +- textDocument/formatting +- textDocument/hover +- textDocument/implementation* +- textDocument/inlayHint +- textDocument/publishDiagnostics +- textDocument/rangeFormatting +- textDocument/references +- textDocument/rename +- textDocument/semanticTokens/full +- textDocument/semanticTokens/full/delta +- textDocument/signatureHelp +- textDocument/typeDefinition* +- window/logMessage +- window/showMessage +- window/showDocument +- window/showMessageRequest +- workspace/applyEdit +- workspace/configuration +- workspace/executeCommand +- workspace/inlayHint/refresh +- workspace/symbol +- workspace/workspaceFolders + *lsp-handler* +LSP handlers are functions that handle |lsp-response|s to requests made by Nvim +to the server. (Notifications, as opposed to requests, are fire-and-forget: +there is no response, so they can't be handled. |lsp-notification|) - Returns: ~ - The |lsp-handler| can respond by returning two values: `result, err` - Where `err` must be shaped like an RPC error: - `{ code, message, data? }` - - You can use |vim.lsp.rpc.rpc_response_error()| to create this object. - -For |lsp-notification|, each |lsp-handler| has this signature: > +Each response handler has this signature: > - function(err, result, ctx, config) + function(err, result, ctx, config) < Parameters: ~ - {err} (nil) - This is always `nil`. - See |lsp-notification| - {result} (Result) - This contains the `params` key of the notification. - See |lsp-notification| - {ctx} (table) - Context describes additional calling state associated - with the handler. It consists of the following key, - value pairs: - - {method} (string) - The |lsp-method| name. - {client_id} (number) - The ID of the |vim.lsp.client|. - {config} (table) - Configuration for the handler. - - Each handler can define its own configuration table - that allows users to customize the behavior of a - particular handler. - - For an example, see: - |vim.lsp.diagnostic.on_publish_diagnostics()| - - To configure a particular |lsp-handler|, see: - |lsp-handler-configuration| + - {err} (table|nil) Error info dict, or `nil` if the request + completed. + - {result} (Result | Params | nil) `result` key of the |lsp-response| or + `nil` if the request failed. + - {ctx} (table) Table of calling state associated with the + handler, with these keys: + - {method} (string) |lsp-method| name. + - {client_id} (number) |vim.lsp.client| identifier. + - {bufnr} (Buffer) Buffer handle. + - {params} (table|nil) Request parameters table. + - {version} (number) Document version at time of + request. Handlers can compare this to the + current document version to check if the + response is "stale". See also |b:changedtick|. + - {config} (table) Handler-defined configuration table, which allows + users to customize handler behavior. + For an example, see: + |vim.lsp.diagnostic.on_publish_diagnostics()| + To configure a particular |lsp-handler|, see: + |lsp-handler-configuration| Returns: ~ - The |lsp-handler|'s return value will be ignored. + Two values `result, err` where `err` is shaped like an RPC error: > + { code, message, data? } +< You can use |vim.lsp.rpc.rpc_response_error()| to create this object. *lsp-handler-configuration* @@ -370,42 +304,37 @@ To configure the behavior of a builtin |lsp-handler|, the convenient method Handlers can be set by: - Setting a field in vim.lsp.handlers. *vim.lsp.handlers* - vim.lsp.handlers is a global table that contains the default mapping of - |lsp-method| names to |lsp-handlers|. - - To override the handler for the `"textDocument/definition"` method: >lua + vim.lsp.handlers is a global table that contains the default mapping of + |lsp-method| names to |lsp-handlers|. To override the handler for the + `"textDocument/definition"` method: >lua vim.lsp.handlers["textDocument/definition"] = my_custom_default_definition < -- The {handlers} parameter for |vim.lsp.start_client()|. - This will set the |lsp-handler| as the default handler for this server. - - For example: >lua +- The {handlers} parameter of |vim.lsp.start()|. This sets the default + |lsp-handler| for the server being started. Example: >lua - vim.lsp.start_client { + vim.lsp.start { ..., -- Other configuration omitted. handlers = { ["textDocument/definition"] = my_custom_server_definition }, } -- The {handler} parameter for |vim.lsp.buf_request()|. - This will set the |lsp-handler| ONLY for the current request. +- The {handler} parameter of |vim.lsp.buf_request_all()|. This sets + the |lsp-handler| ONLY for the given request(s). Example: >lua - For example: >lua - - vim.lsp.buf_request( + vim.lsp.buf_request_all( 0, "textDocument/definition", - definition_params, - my_request_custom_definition + my_request_params, + my_handler ) < In summary, the |lsp-handler| will be chosen based on the current |lsp-method| in the following order: -1. Handler passed to |vim.lsp.buf_request()|, if any. -2. Handler defined in |vim.lsp.start_client()|, if any. +1. Handler passed to |vim.lsp.buf_request_all()|, if any. +2. Handler defined in |vim.lsp.start()|, if any. 3. Handler defined in |vim.lsp.handlers|, if any. *vim.lsp.log_levels* @@ -426,12 +355,12 @@ name: >lua < *lsp-response* -For the format of the response message, see: - https://microsoft.github.io/language-server-protocol/specifications/specification-current/#responseMessage +LSP response shape: +https://microsoft.github.io/language-server-protocol/specifications/specification-current/#responseMessage *lsp-notification* -For the format of the notification message, see: - https://microsoft.github.io/language-server-protocol/specifications/specification-current/#notificationMessage +LSP notification shape: +https://microsoft.github.io/language-server-protocol/specifications/specification-current/#notificationMessage *lsp-on-list-handler* @@ -474,6 +403,8 @@ LspReferenceText used for highlighting "text" references LspReferenceRead used for highlighting "read" references *hl-LspReferenceWrite* LspReferenceWrite used for highlighting "write" references + *hl-LspInlayHint* +LspInlayHint used for highlighting inlay hints *lsp-highlight-codelens* @@ -659,14 +590,20 @@ callbacks. Example: >lua }) < -Also the following |User| |autocommand| is provided: +LspProgress *LspProgress* + Upon receipt of a progress notification from the server. Notifications can + be polled from a `progress` ring buffer of a |vim.lsp.client| or use + |vim.lsp.status()| to get an aggregate message + + If the server sends a "work done progress", the `pattern` is set to `kind` + (one of `begin`, `report` or `end`). -LspProgressUpdate *LspProgressUpdate* - Upon receipt of a progress notification from the server. See - |vim.lsp.util.get_progress_messages()|. + When used from Lua, the event contains a `data` table with `client_id` and + `result` properties. `result` will contain the request params sent by the + server. Example: >vim - autocmd User LspProgressUpdate redrawstatus + autocmd LspProgress * redrawstatus < ============================================================================== @@ -682,6 +619,10 @@ buf_attach_client({bufnr}, {client_id}) *vim.lsp.buf_attach_client()* • {bufnr} (integer) Buffer handle, or 0 for current • {client_id} (integer) Client id + Return: ~ + (boolean) success `true` if client was attached successfully; `false` + otherwise + buf_detach_client({bufnr}, {client_id}) *vim.lsp.buf_detach_client()* Detaches client from the specified buffer. Note: While the server is notified that the text document (buffer) was closed, it is still able to @@ -710,31 +651,27 @@ buf_notify({bufnr}, {method}, {params}) *vim.lsp.buf_notify()* (boolean) success true if any client returns true; false otherwise *vim.lsp.buf_request_all()* -buf_request_all({bufnr}, {method}, {params}, {callback}) - Sends an async request for all active clients attached to the buffer. - Executes the callback on the combined result. Parameters are the same as - |vim.lsp.buf_request()| but the return result and callback are different. +buf_request_all({bufnr}, {method}, {params}, {handler}) + Sends an async request for all active clients attached to the buffer and + executes the `handler` callback with the combined result. Parameters: ~ - • {bufnr} (integer) Buffer handle, or 0 for current. - • {method} (string) LSP method name - • {params} (table|nil) Parameters to send to the server - • {callback} (function) The callback to call when all requests are - finished. Unlike `buf_request`, this will collect all the - responses from each server instead of handling them. A map - of client_id:request_result will be provided to the - callback. + • {bufnr} (integer) Buffer handle, or 0 for current. + • {method} (string) LSP method name + • {params} (table|nil) Parameters to send to the server + • {handler} (function) Handler called after all requests are completed. + Server results are passed as a `client_id:result` map. Return: ~ - fun() cancel A function that will cancel all requests + fun() cancel Function that cancels all requests. *vim.lsp.buf_request_sync()* buf_request_sync({bufnr}, {method}, {params}, {timeout_ms}) Sends a request to all server and waits for the response of all of them. Calls |vim.lsp.buf_request_all()| but blocks Nvim while awaiting the - result. Parameters are the same as |vim.lsp.buf_request()| but the return - result is different. Wait maximum of {timeout_ms} (default 1000) ms. + result. Parameters are the same as |vim.lsp.buf_request_all()| but the + result is different. Waits a maximum of {timeout_ms} (default 1000) ms. Parameters: ~ • {bufnr} (integer) Buffer handle, or 0 for current. @@ -806,6 +743,8 @@ client() *vim.lsp.client* |vim.lsp.start_client()|. • {server_capabilities} (table): Response from the server sent on `initialize` describing the server's capabilities. + • {progress} A ring buffer (|vim.ringbuf()|) containing progress + messages sent by the server. client_is_stopped({client_id}) *vim.lsp.client_is_stopped()* Checks whether a client is stopped. @@ -816,21 +755,6 @@ client_is_stopped({client_id}) *vim.lsp.client_is_stopped()* Return: ~ (boolean) stopped true if client is stopped, false otherwise. - *vim.lsp.for_each_buffer_client()* -for_each_buffer_client({bufnr}, {fn}) - Invokes a function for each LSP client attached to a buffer. - - Parameters: ~ - • {bufnr} (integer) Buffer number - • {fn} (function) Function to run on each client attached to buffer - {bufnr}. The function takes the client, client ID, and buffer - number as arguments. Example: >lua - - vim.lsp.for_each_buffer_client(0, function(client, client_id, bufnr) - print(vim.inspect(client)) - end) -< - formatexpr({opts}) *vim.lsp.formatexpr()* Provides an interface between the built-in client and a `formatexpr` function. @@ -858,7 +782,7 @@ get_active_clients({filter}) *vim.lsp.get_active_clients()* • name (string): Only return clients with the given name Return: ~ - (table) List of |vim.lsp.client| objects + lsp.Client []: List of |vim.lsp.client| objects *vim.lsp.get_buffers_by_client_id()* get_buffers_by_client_id({client_id}) @@ -886,6 +810,13 @@ get_log_path() *vim.lsp.get_log_path()* Return: ~ (string) path to log file +inlay_hint({bufnr}, {enable}) *vim.lsp.inlay_hint()* + Enable/disable/toggle inlay hints for a buffer + + Parameters: ~ + • {bufnr} (integer) Buffer handle, or 0 for current + • {enable} (boolean|nil) true/false to enable/disable, nil to toggle + omnifunc({findstart}, {base}) *vim.lsp.omnifunc()* Implements 'omnifunc' compatible LSP completion. @@ -961,7 +892,7 @@ start({config}, {opts}) *vim.lsp.start()* Parameters: ~ • {config} (table) Same configuration as documented in |vim.lsp.start_client()| - • {opts} nil|table Optional keyword arguments: + • {opts} (nil|lsp.StartOpts) Optional keyword arguments: • reuse_client (fun(client: client, config: table): boolean) Predicate used to decide if a client should be re-used. Used on all running clients. The default implementation @@ -970,7 +901,7 @@ start({config}, {opts}) *vim.lsp.start()* re-using a client (0 for current). Return: ~ - (number|nil) client_id + (integer|nil) client_id start_client({config}) *vim.lsp.start_client()* Starts and initializes a client with the given configuration. @@ -978,7 +909,7 @@ start_client({config}) *vim.lsp.start_client()* Field `cmd` in {config} is required. Parameters: ~ - • {config} (table) Configuration for the server: + • {config} ( lsp.ClientConfig ) Configuration for the server: • cmd: (string[]|fun(dispatchers: table):table) command a list of strings treated like |jobstart()|. The command must launch the language server process. `cmd` can also be @@ -1054,8 +985,8 @@ start_client({config}) *vim.lsp.start_client()* if `capabilities.offsetEncoding` was sent to it. You can only modify the `client.offset_encoding` here before any notifications are sent. Most language servers expect to be - sent client specified settings after initialization. - Neovim does not make this assumption. A + sent client specified settings after initialization. Nvim + does not make this assumption. A `workspace/didChangeConfiguration` notification should be sent to the server during on_init. • on_exit Callback (code, signal, client_id) invoked on @@ -1092,6 +1023,13 @@ start_client({config}) *vim.lsp.start_client()* not be fully initialized. Use `on_init` to do any actions once the client has been initialized. +status() *vim.lsp.status()* + Consumes the latest progress messages from all clients and formats them as + a string. Empty if there are no clients or if no new messages + + Return: ~ + (string) + stop_client({client_id}, {force}) *vim.lsp.stop_client()* Stops a client(s). @@ -1108,7 +1046,7 @@ stop_client({client_id}, {force}) *vim.lsp.stop_client()* thereof • {force} (boolean|nil) shutdown forcefully -tagfunc({...}) *vim.lsp.tagfunc()* +tagfunc({pattern}, {flags}) *vim.lsp.tagfunc()* Provides an interface between the built-in client and 'tagfunc'. When used with normal mode commands (e.g. |CTRL-]|) this will invoke the @@ -1140,9 +1078,12 @@ add_workspace_folder({workspace_folder}) Add the folder at path to the workspace folders. If {path} is not provided, the user will be prompted for a path using |input()|. -clear_references() *vim.lsp.buf.clear_references()* +clear_references({bufnr}) *vim.lsp.buf.clear_references()* Removes document highlights from current buffer. + Parameters: ~ + • {bufnr} integer|nil + code_action({options}) *vim.lsp.buf.code_action()* Selects a code action available at the current cursor position. @@ -1166,7 +1107,7 @@ code_action({options}) *vim.lsp.buf.code_action()* • range: (table|nil) Range for which code actions should be requested. If in visual mode this defaults to the active selection. Table must contain `start` and `end` keys with - {row, col} tuples using mark-like indexing. See + {row,col} tuples using mark-like indexing. See |api-indexing| See also: ~ @@ -1249,8 +1190,7 @@ format({options}) *vim.lsp.buf.format()* fields: • formatting_options (table|nil): Can be used to specify FormattingOptions. Some unspecified options will be - automatically derived from the current Neovim options. - See https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#formattingOptions + automatically derived from the current Nvim options. See https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#formattingOptions • timeout_ms (integer|nil, default 1000): Time in milliseconds to block for formatting requests. No effect if async=true @@ -1274,7 +1214,7 @@ format({options}) *vim.lsp.buf.format()* • name (string|nil): Restrict formatting to the client with name (client.name) matching this field. • range (table|nil) Range to format. Table must contain - `start` and `end` keys with {row, col} tuples using (1,0) + `start` and `end` keys with {row,col} tuples using (1,0) indexing. Defaults to current selection in visual mode Defaults to `nil` in other modes, formatting the full buffer @@ -1650,12 +1590,12 @@ character_offset({buf}, {row}, {col}, {offset_encoding}) • {buf} (integer) buffer number (0 for current) • {row} 0-indexed line • {col} 0-indexed byte offset in line - • {offset_encoding} (string) utf-8|utf-16|utf-32|nil defaults to + • {offset_encoding} (string) utf-8|utf-16|utf-32 defaults to `offset_encoding` of first client of `buf` Return: ~ - (integer, integer) `offset_encoding` index of the character in line - {row} column {col} in buffer {buf} + (integer) `offset_encoding` index of the character in line {row} + column {col} in buffer {buf} *vim.lsp.util.convert_input_to_markdown_lines()* convert_input_to_markdown_lines({input}, {contents}) @@ -1670,7 +1610,7 @@ convert_input_to_markdown_lines({input}, {contents}) lines. Defaults to {}. Return: ~ - {contents}, extended with lines of converted markdown. + (table) {contents} extended with lines of converted markdown. See also: ~ • https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_hover @@ -1680,14 +1620,15 @@ convert_signature_help_to_markdown_lines({signature_help}, {ft}, {triggers}) Converts `textDocument/SignatureHelp` response to markdown lines. Parameters: ~ - • {signature_help} Response of `textDocument/SignatureHelp` - • {ft} optional filetype that will be use as the `lang` for - the label markdown code block - • {triggers} optional list of trigger characters from the lsp + • {signature_help} (table) Response of `textDocument/SignatureHelp` + • {ft} (string|nil) filetype that will be use as the `lang` + for the label markdown code block + • {triggers} (table|nil) list of trigger characters from the lsp server. used to better determine parameter offsets Return: ~ - (list) of lines of converted markdown. + (table|nil) table list of lines of converted markdown. + (table|nil) table of active hl See also: ~ • https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_signatureHelp @@ -1723,7 +1664,7 @@ jump_to_location({location}, {offset_encoding}, {reuse_win}) Parameters: ~ • {location} (table) (`Location`|`LocationLink`) - • {offset_encoding} "utf-8" | "utf-16" | "utf-32" + • {offset_encoding} (string|nil) utf-8|utf-16|utf-32 • {reuse_win} (boolean|nil) Jump to existing window if buffer is already open. @@ -1741,7 +1682,8 @@ locations_to_items({locations}, {offset_encoding}) Parameters: ~ • {locations} (table) list of `Location`s or `LocationLink`s • {offset_encoding} (string) offset_encoding for locations - utf-8|utf-16|utf-32 + utf-8|utf-16|utf-32 default to first client of + buffer Return: ~ (table) list of items @@ -1750,11 +1692,11 @@ lookup_section({settings}, {section}) *vim.lsp.util.lookup_section()* Helper function to return nested values in language server settings Parameters: ~ - • {settings} a table of language server settings - • {section} a string indicating the field of the settings table + • {settings} (table) language server settings + • {section} string indicating the field of the settings table Return: ~ - (table or string) The value of settings accessed via section + table|string The value of settings accessed via section *vim.lsp.util.make_floating_popup_options()* make_floating_popup_options({width}, {height}, {opts}) @@ -1764,7 +1706,7 @@ make_floating_popup_options({width}, {height}, {opts}) Parameters: ~ • {width} (integer) window width (in character cells) • {height} (integer) window height (in character cells) - • {opts} (table, optional) + • {opts} (table) optional • offset_x (integer) offset to add to `col` • offset_y (integer) offset to add to `row` • border (string or table) override `border` @@ -1795,9 +1737,9 @@ make_given_range_params({start_pos}, {end_pos}, {bufnr}, {offset_encoding}) similar to |vim.lsp.util.make_range_params()|. Parameters: ~ - • {start_pos} integer[]|nil {row, col} mark-indexed position. + • {start_pos} integer[]|nil {row,col} mark-indexed position. Defaults to the start of the last visual selection. - • {end_pos} integer[]|nil {row, col} mark-indexed position. + • {end_pos} integer[]|nil {row,col} mark-indexed position. Defaults to the end of the last visual selection. • {bufnr} (integer|nil) buffer handle or 0 for current, defaults to current @@ -1805,8 +1747,8 @@ make_given_range_params({start_pos}, {end_pos}, {bufnr}, {offset_encoding}) `offset_encoding` of first client of `bufnr` Return: ~ - { textDocument = { uri = `current_file_uri` }, range = { start = - `start_position`, end = `end_position` } } + (table) { textDocument = { uri = `current_file_uri` }, range = { start + = `start_position`, end = `end_position` } } *vim.lsp.util.make_position_params()* make_position_params({window}, {offset_encoding}) @@ -1821,7 +1763,7 @@ make_position_params({window}, {offset_encoding}) `window` Return: ~ - `TextDocumentPositionParams` object + (table) `TextDocumentPositionParams` object See also: ~ • https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocumentPositionParams @@ -1841,8 +1783,8 @@ make_range_params({window}, {offset_encoding}) `window` Return: ~ - { textDocument = { uri = `current_file_uri` }, range = { start = - `current_position`, end = `current_position` } } + (table) { textDocument = { uri = `current_file_uri` }, range = { start + = `current_position`, end = `current_position` } } *vim.lsp.util.make_text_document_params()* make_text_document_params({bufnr}) @@ -1852,7 +1794,7 @@ make_text_document_params({bufnr}) • {bufnr} (integer|nil) Buffer handle, defaults to current Return: ~ - `TextDocumentIdentifier` + (table) `TextDocumentIdentifier` See also: ~ • https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocumentIdentifier @@ -1862,8 +1804,8 @@ make_workspace_params({added}, {removed}) Create the workspace params Parameters: ~ - • {added} - • {removed} + • {added} (table) + • {removed} (table) *vim.lsp.util.open_floating_preview()* open_floating_preview({contents}, {syntax}, {opts}) @@ -1895,8 +1837,8 @@ open_floating_preview({contents}, {syntax}, {opts}) window with the same {focus_id} Return: ~ - bufnr,winnr buffer and window number of the newly created floating - preview window + (integer) bufnr of newly created float window + (integer) winid of newly created float window preview window parse_snippet({input}) *vim.lsp.util.parse_snippet()* Parses snippets in a completion entry. @@ -1916,10 +1858,11 @@ preview_location({location}, {opts}) *vim.lsp.util.preview_location()* definition) Parameters: ~ - • {location} a single `Location` or `LocationLink` + • {location} (table) a single `Location` or `LocationLink` Return: ~ - (bufnr,winnr) buffer and window number of floating window or nil + (integer|nil) buffer id of float window + (integer|nil) window id of float window rename({old_fname}, {new_fname}, {opts}) *vim.lsp.util.rename()* Rename old_fname to new_fname @@ -1934,9 +1877,9 @@ set_lines({lines}, {A}, {B}, {new_lines}) *vim.lsp.util.set_lines()* Parameters: ~ • {lines} (table) Original list of strings - • {A} (table) Start position; a 2-tuple of {line, col} numbers - • {B} (table) End position; a 2-tuple of {line, col} numbers - • {new_lines} A list of strings to replace the original + • {A} (table) Start position; a 2-tuple of {line,col} numbers + • {B} (table) End position; a 2-tuple of {line,col} numbers + • {new_lines} (table) list of strings to replace the original Return: ~ (table) The modified {lines} object @@ -1947,7 +1890,7 @@ show_document({location}, {offset_encoding}, {opts}) Parameters: ~ • {location} (table) (`Location`|`LocationLink`) - • {offset_encoding} "utf-8" | "utf-16" | "utf-32" + • {offset_encoding} (string|nil) utf-8|utf-16|utf-32 • {opts} (table|nil) options • reuse_win (boolean) Jump to existing window if buffer is already open. @@ -1971,7 +1914,7 @@ stylize_markdown({bufnr}, {contents}, {opts}) Parameters: ~ • {contents} (table) of lines to show in window - • {opts} dictionary with optional fields + • {opts} (table) with optional fields • height of floating window • width of floating window • wrap_at character to wrap at for computing height @@ -1982,13 +1925,13 @@ stylize_markdown({bufnr}, {contents}, {opts}) • separator insert separator after code block Return: ~ - width,height size of float + (table) stripped content symbols_to_items({symbols}, {bufnr}) *vim.lsp.util.symbols_to_items()* Converts symbols to quickfix list items. Parameters: ~ - • {symbols} DocumentSymbol[] or SymbolInformation[] + • {symbols} (table) DocumentSymbol[] or SymbolInformation[] *vim.lsp.util.text_document_completion_list_to_complete_items()* text_document_completion_list_to_complete_items({result}, {prefix}) @@ -1996,16 +1939,16 @@ text_document_completion_list_to_complete_items({result}, {prefix}) vim-compatible |complete-items|. Parameters: ~ - • {result} The result of a `textDocument/completion` call, e.g. from - |vim.lsp.buf.completion()|, which may be one of + • {result} (table) The result of a `textDocument/completion` call, e.g. + from |vim.lsp.buf.completion()|, which may be one of `CompletionItem[]`, `CompletionList` or `null` • {prefix} (string) the prefix to filter the completion items Return: ~ - { matches = complete-items table, incomplete = bool } + (table) { matches = complete-items table, incomplete = bool } See also: ~ - • |complete-items| + • complete-items trim_empty_lines({lines}) *vim.lsp.util.trim_empty_lines()* Removes empty lines from the beginning and end. @@ -2043,7 +1986,7 @@ get_level() *vim.lsp.log.get_level()* Gets the current log level. Return: ~ - (string) current log level + (integer) current log level set_format_func({handle}) *vim.lsp.log.set_format_func()* Sets formatting function used to format logs @@ -2168,6 +2111,9 @@ make_client_capabilities() Gets a new ClientCapabilities object describing the LSP client capabilities. + Return: ~ + lsp.ClientCapabilities + *vim.lsp.protocol.resolve_capabilities()* resolve_capabilities({server_capabilities}) Creates a normalized object describing LSP server capabilities. @@ -2177,6 +2123,6 @@ resolve_capabilities({server_capabilities}) server Return: ~ - (table) Normalized table of capabilities + (table|nil) Normalized table of capabilities vim:tw=78:ts=8:sw=4:sts=4:et:ft=help:norl: diff --git a/runtime/doc/lua-guide.txt b/runtime/doc/lua-guide.txt index 22f51ce8c7..b138de85c9 100644 --- a/runtime/doc/lua-guide.txt +++ b/runtime/doc/lua-guide.txt @@ -30,7 +30,7 @@ The purpose of this guide is to introduce the different ways of interacting with Nvim through Lua (the "API"). This API consists of three different layers: -1. The "Vim API" inherited from Vim: |ex-commands| and |builtin-functions| as +1. The "Vim API" inherited from Vim: |Ex-commands| and |builtin-functions| as well as |user-function|s in Vimscript. These are accessed through |vim.cmd()| and |vim.fn| respectively, which are discussed under |lua-guide-vimscript| below. @@ -80,7 +80,7 @@ exactly like for a Vimscript file: :source ~/programs/baz/myluafile.lua < Finally, you can include Lua code in a Vimscript file by putting it inside a -|lua-heredoc| block: +|:lua-heredoc| block: >vim lua << EOF local tbl = {1, 2, 3} @@ -199,8 +199,8 @@ this allows you to pass multiple commands to a single call of |vim.cmd()|: highlight link Warning Error ]]) < -This is the converse of |lua-heredoc| and allows you to include Vimscript code in -your `init.lua`. +This is the converse of |:lua-heredoc| and allows you to include Vimscript +code in your `init.lua`. If you want to build your Vim command programmatically, the following form can be useful (all these are equivalent to the corresponding line above): @@ -218,7 +218,7 @@ Vimscript are automatically converted: print(vim.fn.printf('Hello from %s', 'Lua')) local reversed_list = vim.fn.reverse({ 'a', 'b', 'c' }) - print(vim.inspect(reversed_list)) -- { "c", "b", "a" } + vim.print(reversed_list) -- { "c", "b", "a" } local function print_stdout(chan_id, data, name) print(data[1]) @@ -261,7 +261,7 @@ Data types are converted automatically. For example: key2 = 300 } - print(vim.inspect(vim.g.some_global_variable)) + vim.print(vim.g.some_global_variable) --> { key1 = "value", key2 = 300 } < You can target specific buffers (via number), windows (via |window-ID|), or diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt index 54527c5a50..fb6cbca6e3 100644 --- a/runtime/doc/lua.txt +++ b/runtime/doc/lua.txt @@ -14,7 +14,7 @@ INTRODUCTION *lua-intro* The Lua 5.1 script engine is builtin and always available. Try this command to get an idea of what lurks beneath: >vim - :lua print(vim.inspect(package.loaded)) + :lua vim.print(package.loaded) Nvim includes a "standard library" |lua-stdlib| for Lua. It complements the "editor stdlib" (|builtin-functions| and |Ex-commands|) and the |API|, all of @@ -34,7 +34,7 @@ Nvim ever ships with Lua 5.4+, a Lua 5.1 compatibility shim will be provided so that old plugins continue to work transparently. *lua-luajit* -Neovim is built with luajit on platforms which support it, which provides +Nvim is built with luajit on platforms which support it, which provides extra functionality. Lua code in |init.lua| and plugins can assume its presence on installations on common platforms. For maximum compatibility with less common platforms, availability can be checked using the `jit` global variable: >lua @@ -46,7 +46,7 @@ common platforms, availability can be checked using the `jit` global variable: > < *lua-bit* In particular, the luajit "bit" extension module is _always_ available. -A fallback implementation is included when nvim is built with PUC lua 5.1, +A fallback implementation is included when nvim is built with PUC Lua 5.1, and will be transparently used when `require("bit")` is invoked. ============================================================================== @@ -252,7 +252,7 @@ arguments separated by " " (space) instead of "\t" (tab). {endmarker} Executes Lua script {script} from within Vimscript. You can omit [endmarker] after the "<<" and use a dot "." after {script} (similar to - |:append|, |:insert|). Refer to |let-heredoc| for more information. + |:append|, |:insert|). Refer to |:let-heredoc| for more information. Example: >vim function! CurrentLineInfo() @@ -423,7 +423,7 @@ is unnecessary. You can peek at the module properties: >vim - :lua print(vim.inspect(vim)) + :lua vim.print(vim) Result is something like this: > @@ -549,7 +549,7 @@ Multithreading *lua-loop-threading* Plugins can perform work in separate (os-level) threads using the threading APIs in luv, for instance `vim.uv.new_thread`. Note that every thread -gets its own separate lua interpreter state, with no access to lua globals +gets its own separate Lua interpreter state, with no access to Lua globals in the main thread. Neither can the state of the editor (buffers, windows, etc) be directly accessed from threads. @@ -557,10 +557,10 @@ A subset of the `vim.*` API is available in threads. This includes: - `vim.uv` with a separate event loop per thread. - `vim.mpack` and `vim.json` (useful for serializing messages between threads) -- `require` in threads can use lua packages from the global |package.path| +- `require` in threads can use Lua packages from the global |package.path| - `print()` and `vim.inspect` - `vim.diff` -- most utility functions in `vim.*` for working with pure lua values +- most utility functions in `vim.*` for working with pure Lua values like `vim.split`, `vim.tbl_*`, `vim.list_*`, and so on. - `vim.is_thread()` returns true from a non-main thread. @@ -624,7 +624,7 @@ vim.highlight.priorities *vim.highlight.priorities* ------------------------------------------------------------------------------ VIM.REGEX *lua-regex* -Vim regexes can be used directly from lua. Currently they only allow +Vim regexes can be used directly from Lua. Currently they only allow matching within a single line. vim.regex({re}) *vim.regex()* @@ -747,9 +747,22 @@ vim.json.encode({obj}) *vim.json.encode* vim.json.decode({str}[, {opts}]) *vim.json.decode* Decodes (or "unpacks") the JSON-encoded {str} to a Lua object. - {opts} is a table with the key `luanil = { object: bool, array: bool }` - that controls whether `null` in JSON objects or arrays should be converted - to Lua `nil` instead of `vim.NIL`. + - Decodes JSON "null" as |vim.NIL| (controllable by {opts}, see below). + - Decodes empty object as |vim.empty_dict()|. + - Decodes empty array as `{}` (empty Lua table). + + Example: >lua + :lua vim.print(vim.json.decode('{"bar":[],"foo":{},"zub":null}')) + --> { bar = {}, foo = vim.empty_dict(), zub = vim.NIL } +< + Parameters: ~ + • {str} Stringified JSON data. + • {opts} Options map keys: + • luanil: { object: bool, array: bool } + • `luanil.object=true` converts `null` in JSON objects to + Lua `nil` instead of `vim.NIL`. + • `luanil.array=true` converts `null` in JSON arrays to Lua + `nil` instead of `vim.NIL`. ------------------------------------------------------------------------------ VIM.SPELL *lua-spell* @@ -867,21 +880,6 @@ vim.schedule({callback}) *vim.schedule()* Schedules {callback} to be invoked soon by the main event-loop. Useful to avoid |textlock| or other temporary restrictions. - -vim.defer_fn({fn}, {timeout}) *vim.defer_fn* - Defers calling {fn} until {timeout} ms passes. Use to do a one-shot timer - that calls {fn}. - - Note: The {fn} is |vim.schedule_wrap()|ped automatically, so API functions are - safe to call. - - Parameters: ~ - • {fn} Callback to call once {timeout} expires - • {timeout} Time in ms to wait before calling {fn} - - Returns: ~ - |vim.uv|.new_timer() object - vim.wait({time} [, {callback}, {interval}, {fast_only}]) *vim.wait()* Wait for {time} in milliseconds until {callback} returns `true`. @@ -935,8 +933,8 @@ vim.wait({time} [, {callback}, {interval}, {fast_only}]) *vim.wait()* vim.ui_attach({ns}, {options}, {callback}) *vim.ui_attach()* Attach to ui events, similar to |nvim_ui_attach()| but receive events - as lua callback. Can be used to implement screen elements like - popupmenu or message handling in lua. + as Lua callback. Can be used to implement screen elements like + popupmenu or message handling in Lua. {options} should be a dictionary-like table, where `ext_...` options should be set to true to receive events for the respective external element. @@ -1017,10 +1015,18 @@ Log levels are one of the values defined in `vim.log.levels`: ------------------------------------------------------------------------------ LUA-VIMSCRIPT BRIDGE *lua-vimscript* -Nvim Lua provides an interface to Vimscript variables and functions, and -editor commands and options. +Nvim Lua provides an interface or "bridge" to Vimscript variables and +functions, and editor commands and options. + +Objects passed over this bridge are COPIED (marshalled): there are no +"references". |lua-guide-variables| For example, using `vim.fn.remove()` on +a Lua list copies the list object to Vimscript and does NOT modify the Lua +list: >lua + + local list = { 1, 2, 3 } + vim.fn.remove(list, 0) + vim.print(list) --> "{ 1, 2, 3 }" -See also https://github.com/nanotee/nvim-lua-guide. vim.call({func}, {...}) *vim.call()* Invokes |vim-function| or |user-function| {func} with arguments {...}. @@ -1256,7 +1262,7 @@ In any of the above examples, to replicate the behavior |:setlocal|, use *vim.opt:get()* Option:get() - Returns a lua-representation of the option. Boolean, number and string + Returns a Lua-representation of the option. Boolean, number and string values will be returned in exactly the same fashion. For values that are comma-separated lists, an array will be returned with @@ -1376,10 +1382,10 @@ connection_failure_errmsg({consequence}) TODO: Documentation defer_fn({fn}, {timeout}) *vim.defer_fn()* - Defers calling `fn` until `timeout` ms passes. + Defers calling {fn} until {timeout} ms passes. - Use to do a one-shot timer that calls `fn` Note: The {fn} is |vim.schedule_wrap()|ped automatically, so API functions - are safe to call. + Use to do a one-shot timer that calls {fn} Note: The {fn} is + |vim.schedule_wrap()|ped automatically, so API functions are safe to call. Parameters: ~ • {fn} (function) Callback to call once `timeout` expires @@ -1408,6 +1414,7 @@ inspect({object}, {options}) *vim.inspect()* Gets a human-readable representation of the given object. See also: ~ + • |vim.print()| • https://github.com/kikito/inspect.lua • https://github.com/mpeterv/vinspect @@ -1429,10 +1436,10 @@ keycode({str}) *vim.keycode()* • |nvim_replace_termcodes()| lua_omnifunc({find_start}, {_}) *vim.lua_omnifunc()* - Omnifunc for completing lua values from the runtime lua interpreter, + Omnifunc for completing Lua values from the runtime Lua interpreter, similar to the builtin completion for the `:lua` command. - Activate using `set omnifunc=v:lua.vim.lua_omnifunc` in a lua buffer. + Activate using `set omnifunc=v:lua.vim.lua_omnifunc` in a Lua buffer. notify({msg}, {level}, {opts}) *vim.notify()* Display a notification to the user. @@ -1532,6 +1539,7 @@ print({...}) *vim.print()* See also: ~ • |vim.inspect()| + • |:=| region({bufnr}, {pos1}, {pos2}, {regtype}, {inclusive}) *vim.region()* Get a table of lines with start, end columns for a region marked by two @@ -1566,6 +1574,77 @@ schedule_wrap({cb}) *vim.schedule_wrap()* • |vim.schedule()| • |vim.in_fast_event()| +system({cmd}, {opts}, {on_exit}) *vim.system()* + Run a system command + + Examples: >lua + + local on_exit = function(obj) + print(obj.code) + print(obj.signal) + print(obj.stdout) + print(obj.stderr) + end + + -- Run asynchronously + vim.system({'echo', 'hello'}, { text = true }, on_exit) + + -- Run synchronously + local obj = vim.system({'echo', 'hello'}, { text = true }):wait() + -- { code = 0, signal = 0, stdout = 'hello', stderr = '' } +< + + See |uv.spawn()| for more details. + + Parameters: ~ + • {cmd} (string[]) Command to execute + • {opts} (SystemOpts|nil) Options: + • cwd: (string) Set the current working directory for the + sub-process. + • env: table<string,string> Set environment variables for + the new process. Inherits the current environment with + `NVIM` set to |v:servername|. + • clear_env: (boolean) `env` defines the job environment + exactly, instead of merging current environment. + • stdin: (string|string[]|boolean) If `true`, then a pipe + to stdin is opened and can be written to via the + `write()` method to SystemObj. If string or string[] then + will be written to stdin and closed. Defaults to `false`. + • stdout: (boolean|function) Handle output from stdout. + When passed as a function must have the signature + `fun(err: string, data: string)`. Defaults to `true` + • stderr: (boolean|function) Handle output from stdout. + When passed as a function must have the signature + `fun(err: string, data: string)`. Defaults to `true`. + • text: (boolean) Handle stdout and stderr as text. + Replaces `\r\n` with `\n`. + • timeout: (integer) + • detach: (boolean) If true, spawn the child process in a + detached state - this will make it a process group + leader, and will effectively enable the child to keep + running after the parent exits. Note that the child + process will still keep the parent's event loop alive + unless the parent process calls |uv.unref()| on the + child's process handle. + • {on_exit} (function|nil) Called when subprocess exits. When provided, + the command runs asynchronously. Receives SystemCompleted + object, see return of SystemObj:wait(). + + Return: ~ + SystemObj Object with the fields: + • pid (integer) Process ID + • wait (fun(timeout: integer|nil): SystemCompleted) + • SystemCompleted is an object with the fields: + • code: (integer) + • signal: (integer) + • stdout: (string), nil if stdout argument is passed + • stderr: (string), nil if stderr argument is passed + + • kill (fun(signal: integer)) + • write (fun(data: string|nil)) Requires `stdin=true`. Pass `nil` to + close the stream. + • is_closing (fun(): boolean) + ============================================================================== Lua module: inspector *lua-inspector* @@ -1776,6 +1855,60 @@ pesc({s}) *vim.pesc()* See also: ~ • https://github.com/rxi/lume +ringbuf({size}) *vim.ringbuf()* + Create a ring buffer limited to a maximal number of items. Once the buffer + is full, adding a new entry overrides the oldest entry. +> + + local ringbuf = vim.ringbuf(4) + ringbuf:push("a") + ringbuf:push("b") + ringbuf:push("c") + ringbuf:push("d") + ringbuf:push("e") -- overrides "a" + print(ringbuf:pop()) -- returns "b" + print(ringbuf:pop()) -- returns "c" + + -- Can be used as iterator. Pops remaining items: + for val in ringbuf do + print(val) + end +< + + Returns a Ringbuf instance with the following methods: + + • |Ringbuf:push()| + • |Ringbuf:pop()| + • |Ringbuf:peek()| + • |Ringbuf:clear()| + + Parameters: ~ + • {size} (integer) + + Return: ~ + (table) + +Ringbuf:clear({self}) *Ringbuf:clear()* + Clear all items. + +Ringbuf:peek({self}) *Ringbuf:peek()* + Returns the first unread item without removing it + + Return: ~ + any?|ni + +Ringbuf:pop({self}) *Ringbuf:pop()* + Removes and returns the first unread item + + Return: ~ + any?|ni + +Ringbuf:push({self}, {item}) *Ringbuf:push()* + Adds an item, overriding the oldest item if the buffer is full. + + Parameters: ~ + • {item} any + spairs({t}) *vim.spairs()* Enumerate a table sorted by its keys. @@ -1946,8 +2079,8 @@ tbl_get({o}, {...}) *vim.tbl_get()* Parameters: ~ • {o} (table) Table to index - • {...} (string) Optional strings (0 or more, variadic) via which to - index the table + • {...} any Optional keys (0 or more, variadic) via which to index the + table Return: ~ any Nested value indexed by key (if it exists), else nil @@ -2099,17 +2232,17 @@ Lua module: loader *lua-loader* disable() *vim.loader.disable()* Disables the experimental Lua module loader: • removes the loaders - • adds the default Neovim loader + • adds the default Nvim loader enable() *vim.loader.enable()* Enables the experimental Lua module loader: • overrides loadfile - • adds the lua loader using the byte-compilation cache + • adds the Lua loader using the byte-compilation cache • adds the libs loader - • removes the default Neovim loader + • removes the default Nvim loader find({modname}, {opts}) *vim.loader.find()* - Finds lua modules for the given module name. + Finds Lua modules for the given module name. Parameters: ~ • {modname} (string) Module name, or `"*"` to find the top-level @@ -2426,8 +2559,8 @@ del({modes}, {lhs}, {opts}) *vim.keymap.del()* Parameters: ~ • {opts} (table|nil) A table of optional arguments: - • buffer: (number or boolean) Remove a mapping from the given - buffer. When "true" or 0, use the current buffer. + • "buffer": (number|boolean) Remove a mapping from the given + buffer. When `0` or `true`, use the current buffer. See also: ~ • |vim.keymap.set()| @@ -2438,7 +2571,7 @@ set({mode}, {lhs}, {rhs}, {opts}) *vim.keymap.set()* -- Map to a Lua function: vim.keymap.set('n', 'lhs', function() print("real lua function") end) -- Map to multiple modes: - vim.keymap.set({'n', 'v'}, '<leader>lr', vim.lsp.buf.references, { buffer=true }) + vim.keymap.set({'n', 'v'}, '<leader>lr', vim.lsp.buf.references, { buffer = true }) -- Buffer-local mapping: vim.keymap.set('n', '<leader>w', "<cmd>w<cr>", { silent = true, buffer = 5 }) -- Expr mapping: @@ -2461,9 +2594,9 @@ set({mode}, {lhs}, {rhs}, {opts}) *vim.keymap.set()* • "noremap": inverse of "remap" (see below). • Also accepts: - • "buffer" number|boolean Creates buffer-local mapping, `0` - or `true` for current buffer. - • remap: (boolean) Make the mapping recursive. Inverses + • "buffer": (number|boolean) Creates buffer-local mapping, + `0` or `true` for current buffer. + • "remap": (boolean) Make the mapping recursive. Inverse of "noremap". Defaults to `false`. See also: ~ @@ -2572,8 +2705,8 @@ find({names}, {opts}) *vim.fs.find()* directories joinpath({...}) *vim.fs.joinpath()* - Concatenate directories and/or file into a single path with normalization - (e.g., `"foo/"` and `"bar"` get joined to `"foo/bar"`) + Concatenate directories and/or file paths into a single path with + normalization (e.g., `"foo/"` and `"bar"` get joined to `"foo/bar"`) Parameters: ~ • {...} (string) @@ -2832,11 +2965,15 @@ range({spec}) *vim.version.range()* } < - `:has()` checks if a version is in the range (inclusive `from` , exclusive `to` ). Example: >lua + `:has()` checks if a version is in the range (inclusive `from`, exclusive + `to`). + + Example: >lua local r = vim.version.range('1.0.0 - 2.0.0') - print(r:has('1.9.9')) -- true - print(r:has('2.0.0')) -- false + print(r:has('1.9.9')) -- true + print(r:has('2.0.0')) -- false + print(r:has(vim.version())) -- check against current Nvim version < Or use cmp(), eq(), lt(), and gt() to compare `.to` and `.from` directly: >lua @@ -2856,8 +2993,9 @@ range({spec}) *vim.version.range()* Lua module: iter *lua-iter* -The *vim.iter* module provides a generic "iterator" interface over tables -and iterator functions. +The *vim.iter* module provides a generic interface for working with +iterables: tables, lists, iterator functions, pair()/ipair()-like +iterators, and `vim.iter()` objects. *vim.iter()* wraps its table or function argument into an *Iter* object with methods (such as |Iter:filter()| and |Iter:map()|) that transform the @@ -2870,6 +3008,8 @@ pipeline depend on the type passed to this function: • Non-list tables pass both the key and value of each element • Function iterators pass all of the values returned by their respective function +• Tables with a metatable implementing __call are treated as function + iterators Examples: >lua @@ -2903,6 +3043,12 @@ Examples: >lua end) -- true + local rb = vim.ringbuf(3) + rb:push("a") + rb:push("b") + vim.iter(rb):totable() + -- { "a", "b" } + < In addition to the |vim.iter()| function, the |vim.iter| module provides @@ -3028,7 +3174,7 @@ Iter:find({self}, {f}) *Iter:find()* any Iter:fold({self}, {init}, {f}) *Iter:fold()* - Fold an iterator or table into a single value. + Fold ("reduce") an iterator or table into a single value. Examples: >lua diff --git a/runtime/doc/luaref.txt b/runtime/doc/luaref.txt index a01196e538..dbf2f0de79 100644 --- a/runtime/doc/luaref.txt +++ b/runtime/doc/luaref.txt @@ -3830,7 +3830,7 @@ coroutine.yield({...}) *coroutine.yield()* to `yield` are passed as extra results to `resume`. ============================================================================== -5.3 - Modules *luaref-libModule* +5.3 Modules *luaref-libModule* The package library provides basic facilities for loading and building modules in Lua. It exports two of its functions directly in the global environment: @@ -3960,7 +3960,7 @@ package.seeall({module}) *package.seeall()* global environment. To be used as an option to function {module}. ============================================================================== -5.4 - String Manipulation *luaref-libString* +5.4 String Manipulation *luaref-libString* This library provides generic functions for string manipulation, such as finding and extracting substrings, and pattern matching. When indexing a @@ -4199,9 +4199,8 @@ instance, `%S` represents all non-space characters. The definitions of letter, space, and other character groups depend on the current locale. In particular, the class `[a-z]` may not be equivalent to `%l`. - *luaref-patternitem* -Pattern Item:~ -------------- +PATTERN ITEM *luaref-patternitem* + A pattern item may be - a single character class, which matches any single character in the @@ -4226,17 +4225,15 @@ A pattern item may be `y` where the count reaches 0. For instance, the item `%b()` matches expressions with balanced parentheses. - *luaref-pattern* -Pattern:~ --------- +PATTERN *luaref-pattern* + A pattern is a sequence of pattern items. A `^` at the beginning of a pattern anchors the match at the beginning of the subject string. A `$` at the end of a pattern anchors the match at the end of the subject string. At other positions, `^` and `$` have no special meaning and represent themselves. - *luaref-capture* -Captures:~ ---------- +CAPTURES *luaref-capture* + A pattern may contain sub-patterns enclosed in parentheses; they describe captures. When a match succeeds, the substrings of the subject string that match captures are stored (captured) for future use. Captures are numbered @@ -4364,7 +4361,7 @@ math.frexp({x}) *math.frexp()* absolute value of `m` is in the range `[0.5, 1)` (or zero when {x} is zero). -math.huge *math.huge()* +math.huge *math.huge* The value `HUGE_VAL`, a value larger than or equal to any other numerical value. @@ -4387,7 +4384,7 @@ math.modf({x}) *math.modf()* Returns two numbers, the integral part of {x} and the fractional part of {x}. -math.pi *math.pi()* +math.pi *math.pi* The value of `pi`. math.pow({x}, {y}) *math.pow()* diff --git a/runtime/doc/luvref.txt b/runtime/doc/luvref.txt index 131e7889e4..8c842de97a 100644 --- a/runtime/doc/luvref.txt +++ b/runtime/doc/luvref.txt @@ -3510,14 +3510,22 @@ uv.thread_setaffinity({thread}, {affinity} [, {get_old_affinity}]) - `[1, 2, 3, ..., n]` : `boolean` - `get_old_affinity`: `boolean` - Sets the specified thread's affinity setting. `affinity` must - be an array-like table where each of the keys correspond to a - CPU number and the values are booleans that represent whether - the `thread` should be eligible to run on that CPU. The length - of the `affinity` table must be greater than or equal to - `uv.cpumask_size()`. If `get_old_affinity` is `true`, the - previous affinity settings for the `thread` will be returned. - Otherwise, `true` is returned after a successful call. + Sets the specified thread's affinity setting. + + `affinity` must be a table where each of the keys are a CPU + number and the values are booleans that represent whether the + `thread` should be eligible to run on that CPU. If the length + of the `affinity` table is not greater than or equal to + |uv.cpumask_size()|, any CPU numbers missing from the table + will have their affinity set to `false`. If setting the + affinity of more than |uv.cpumask_size()| CPUs is desired, + `affinity` must be an array-like table with no gaps, since + `#affinity` will be used as the `cpumask_size` if it is + greater than |uv.cpumask_size()|. + + If `get_old_affinity` is `true`, the previous affinity + settings for the `thread` will be returned. Otherwise, `true` + is returned after a successful call. Note: Thread affinity setting is not atomic on Windows. Unsupported on macOS. @@ -4078,19 +4086,17 @@ uv.metrics_idle_time() *uv.metrics_idle_time()* uv.metrics_info() *uv.metrics_info()* Get the metrics table from current set of event loop metrics. + It is recommended to retrieve these metrics in a `prepare` + callback (see |uv.new_prepare()|, |uv.prepare_start()|) in order + to make sure there are no inconsistencies with the metrics + counters. Returns: `table` - The table contains event loop metrics. It is recommended to - retrieve these metrics in a uv_prepare_cb in order to make - sure there are no inconsistencies with the metrics counters. - - `loop_count` : `integer` - `events` : `integer` - `events_waiting` : `integer` - Note: New in libuv version 1.45.0. - ============================================================================== CREDITS *luv-credits* diff --git a/runtime/doc/map.txt b/runtime/doc/map.txt index ad7901b962..56e58baad8 100644 --- a/runtime/doc/map.txt +++ b/runtime/doc/map.txt @@ -349,7 +349,7 @@ Note: - The command is not echo'ed, no need for <silent>. - The {rhs} is not subject to abbreviations nor to other mappings, even if the mapping is recursive. -- In Visual mode you can use `line('v')` and `col('v')` to get one end of the +- In Visual mode you can use `line('v')` and `col('v')` to get one end of the Visual area, the cursor is at the other end. *E1255* *E1136* @@ -1051,8 +1051,7 @@ typed after an abbreviation: > There are no default abbreviations. Abbreviations are never recursive. You can use ":ab f f-o-o" without any -problem. But abbreviations can be mapped. {some versions of Vi support -recursive abbreviations, for no apparent reason} +problem. But abbreviations can be mapped. *:abbreviate-local* *:abbreviate-<buffer>* Just like mappings, abbreviations can be local to a buffer. This is mostly @@ -1391,7 +1390,7 @@ completion can be enabled: -complete=highlight highlight groups -complete=history :history suboptions -complete=locale locale names (as output of locale -a) - -complete=lua Lua expression + -complete=lua Lua expression |:lua| -complete=mapclear buffer argument -complete=mapping mapping name -complete=menu menus @@ -1546,6 +1545,7 @@ supports incremental command preview: -- If invoked as a preview callback, performs 'inccommand' preview by -- highlighting trailing whitespace in the current buffer. local function trim_space_preview(opts, preview_ns, preview_buf) + vim.cmd('hi clear Whitespace') local line1 = opts.line1 local line2 = opts.line2 local buf = vim.api.nvim_get_current_buf() diff --git a/runtime/doc/mbyte.txt b/runtime/doc/mbyte.txt index 99dfa54218..aedef87a09 100644 --- a/runtime/doc/mbyte.txt +++ b/runtime/doc/mbyte.txt @@ -358,150 +358,6 @@ conversion needs to be done. These conversions are supported: Try getting another iconv() implementation. ============================================================================== -Input on X11 *mbyte-XIM* - -X INPUT METHOD (XIM) BACKGROUND *XIM* *xim* *x-input-method* - -XIM is an international input module for X. There are two kinds of structures, -Xlib unit type and |IM-server| (Input-Method server) type. |IM-server| type -is suitable for complex input, such as CJK. - -- IM-server - *IM-server* - In |IM-server| type input structures, the input event is handled by either - of the two ways: FrontEnd system and BackEnd system. In the FrontEnd - system, input events are snatched by the |IM-server| first, then |IM-server| - give the application the result of input. On the other hand, the BackEnd - system works reverse order. MS-Windows adopt BackEnd system. In X, most of - |IM-server|s adopt FrontEnd system. The demerit of BackEnd system is the - large overhead in communication, but it provides safe synchronization with - no restrictions on applications. - -- Conversion Server - *conversion-server* - Some system needs additional server: conversion server. Most of Japanese - |IM-server|s need it, Kana-Kanji conversion server. For Chinese inputting, - it depends on the method of inputting, in some methods, PinYin or ZhuYin to - HanZi conversion server is needed. For Korean inputting, if you want to - input Hanja, Hangul-Hanja conversion server is needed. - - For example, the Japanese inputting process is divided into 2 steps. First - we pre-input Hira-gana, second Kana-Kanji conversion. There are so many - Kanji characters (6349 Kanji characters are defined in JIS X 0208) and the - number of Hira-gana characters are 76. So, first, we pre-input text as - pronounced in Hira-gana, second, we convert Hira-gana to Kanji or Kata-Kana, - if needed. There are some Kana-Kanji conversion server: jserver - (distributed with Wnn, see below) and canna. Canna can be found at: - http://canna.sourceforge.jp/ - -There is a good input system: Wnn4.2. Wnn 4.2 contains, - xwnmo (|IM-server|) - jserver (Japanese Kana-Kanji conversion server) - cserver (Chinese PinYin or ZhuYin to simplified HanZi conversion server) - tserver (Chinese PinYin or ZhuYin to traditional HanZi conversion server) - kserver (Hangul-Hanja conversion server) -Wnn 4.2 for several systems can be found at various places on the internet. -Use the RPM or port for your system. - - -- Input Style - *xim-input-style* - When inputting CJK, there are four areas: - 1. The area to display of the input while it is being composed - 2. The area to display the currently active input mode. - 3. The area to display the next candidate for the selection. - 4. The area to display other tools. - - The third area is needed when converting. For example, in Japanese - inputting, multiple Kanji characters could have the same pronunciation, so - a sequence of Hira-gana characters could map to a distinct sequence of Kanji - characters. - - The first and second areas are defined in international input of X with the - names of "Preedit Area", "Status Area" respectively. The third and fourth - areas are not defined and are left to be managed by the |IM-server|. In the - international input, four input styles have been defined using combinations - of Preedit Area and Status Area: |OnTheSpot|, |OffTheSpot|, |OverTheSpot| - and |Root|. - - Currently, GUI Vim supports three styles, |OverTheSpot|, |OffTheSpot| and - |Root|. - -*. on-the-spot *OnTheSpot* - Preedit Area and Status Area are performed by the client application in - the area of application. The client application is directed by the - |IM-server| to display all pre-edit data at the location of text - insertion. The client registers callbacks invoked by the input method - during pre-editing. -*. over-the-spot *OverTheSpot* - Status Area is created in a fixed position within the area of application, - in case of Vim, the position is the additional status line. Preedit Area - is made at present input position of application. The input method - displays pre-edit data in a window which it brings up directly over the - text insertion position. -*. off-the-spot *OffTheSpot* - Preedit Area and Status Area are performed in the area of application, in - case of Vim, the area is additional status line. The client application - provides display windows for the pre-edit data to the input method which - displays into them directly. -*. root-window *Root* - Preedit Area and Status Area are outside of the application. The input - method displays all pre-edit data in a separate area of the screen in a - window specific to the input method. - - -USING XIM *multibyte-input* *E284* *E285* *E286* *E287* - *E288* *E289* - -Note that Display and Input are independent. It is possible to see your -language even though you have no input method for it. But when your Display -method doesn't match your Input method, the text will be displayed wrong. - -To input your language you should run the |IM-server| which supports your -language and |conversion-server| if needed. - -The next 3 lines should be put in your ~/.Xdefaults file. They are common for -all X applications which uses |XIM|. If you already use |XIM|, you can skip -this. > - - *international: True - *.inputMethod: your_input_server_name - *.preeditType: your_input_style -< -input_server_name is your |IM-server| name (check your |IM-server| - manual). -your_input_style is one of |OverTheSpot|, |OffTheSpot|, |Root|. See - also |xim-input-style|. - -*international may not be necessary if you use X11R6. -*.inputMethod and *.preeditType are optional if you use X11R6. - -For example, when you are using kinput2 as |IM-server|, > - - *international: True - *.inputMethod: kinput2 - *.preeditType: OverTheSpot -< -When using |OverTheSpot|, GUI Vim always connects to the IM Server even in -Normal mode, so you can input your language with commands like "f" and "r". -But when using one of the other two methods, GUI Vim connects to the IM Server -only if it is not in Normal mode. - -If your IM Server does not support |OverTheSpot|, and if you want to use your -language with some Normal mode command like "f" or "r", then you should use a -localized xterm or an xterm which supports |XIM| - -If needed, you can set the XMODIFIERS environment variable: - - sh: export XMODIFIERS="@im=input_server_name" - csh: setenv XMODIFIERS "@im=input_server_name" - -For example, when you are using kinput2 as |IM-server| and sh, > - - export XMODIFIERS="@im=kinput2" -< - -============================================================================== Input with a keymap *mbyte-keymap* When the keyboard doesn't produce the characters you want to enter in your diff --git a/runtime/doc/message.txt b/runtime/doc/message.txt index dffdb5950f..c3154fc372 100644 --- a/runtime/doc/message.txt +++ b/runtime/doc/message.txt @@ -327,7 +327,7 @@ You can switch the 'write' option on with ":set write". *E25* > Nvim does not have a built-in GUI -Neovim does not have a built in GUI, so `:gvim` and `:gui` don't work. +Nvim does not have a built in GUI, so `:gvim` and `:gui` don't work. *E49* > Invalid scroll size @@ -820,13 +820,13 @@ Type effect ~ the clipboard ("* and "+ registers) {menu-entry} what the menu is defined to in Cmdline-mode. - <LeftMouse> next page (*) + <LeftMouse> next page* Any other key causes the meaning of the keys to be displayed. -(*) Clicking the left mouse button only works: - - For the GUI: in the last line of the screen. - - When 'r' is included in 'mouse' (but then selecting text won't work). +* Clicking the left mouse button only works: + - For the GUI: in the last line of the screen. + - When 'r' is included in 'mouse' (but then selecting text won't work). Note: The typed key is directly obtained from the terminal, it is not mapped diff --git a/runtime/doc/motion.txt b/runtime/doc/motion.txt index 59ae209aa9..d7a0205ebd 100644 --- a/runtime/doc/motion.txt +++ b/runtime/doc/motion.txt @@ -620,15 +620,15 @@ it "inner tag block", select [count] tag blocks, from the a} *v_a}* *a}* *a{* a{ *v_aB* *v_a{* *aB* -aB "a Block", select [count] Blocks, from "[count] [{" to - the matching '}', including the '{' and '}' (see +aB "a Block", select [count] Blocks, from `[count] [{` to + the matching "}", including the "{" and "}" (see |[{|). When used in Visual mode it is made charwise. i} *v_i}* *i}* *i{* i{ *v_iB* *v_i{* *iB* -iB "inner Block", select [count] Blocks, from "[count] [{" - to the matching '}', excluding the '{' and '}' (see +iB "inner Block", select [count] Blocks, from `[count] [{` + to the matching "}", excluding the "{" and "}" (see |[{|). When used in Visual mode it is made charwise. @@ -1230,7 +1230,7 @@ remembered. ([{}]) parenthesis or (curly/square) brackets (this can be changed with the 'matchpairs' option) - /* */ start or end of C-style comment + `/* */` start or end of C-style comment #if, #ifdef, #else, #elif, #endif C preprocessor conditionals (when the cursor is on the # or no ([{ @@ -1279,9 +1279,9 @@ remembered. |exclusive| motion. The above four commands can be used to go to the start or end of the current -code block. It is like doing "%" on the '(', ')', '{' or '}' at the other +code block. It is like doing "%" on the "(", ")", "{" or "}" at the other end of the code block, but you can do this from anywhere in the code block. -Very useful for C programs. Example: When standing on "case x:", "[{" will +Very useful for C programs. Example: When standing on "case x:", `[{` will bring you back to the switch statement. *]m* diff --git a/runtime/doc/news-0.9.txt b/runtime/doc/news-0.9.txt index c7624119e2..5546703dab 100644 --- a/runtime/doc/news-0.9.txt +++ b/runtime/doc/news-0.9.txt @@ -109,7 +109,7 @@ The following new APIs or features were added. (or the Vimscript equivalent) to their |config| file. • A new environment variable named NVIM_APPNAME enables configuring the - directories where Neovim should find its configuration and state files. See + directories where Nvim should find its configuration and state files. See `:help $NVIM_APPNAME` . • Added support for running Lua scripts from shell using |-l|. > @@ -117,9 +117,9 @@ The following new APIs or features were added. < Also works with stdin: > echo "print(42)" | nvim -l - -• Added an omnifunc implementation for lua, |vim.lua_omnifunc()| +• Added an omnifunc implementation for Lua, |vim.lua_omnifunc()| -• Added a new experimental |lua-loader| that byte-compiles and caches lua files. +• Added a new experimental |lua-loader| that byte-compiles and caches Lua files. To enable the new loader, add the following at the top of your |init.lua|: >lua vim.loader.enable() @@ -223,7 +223,7 @@ The following changes to existing APIs or features add new behavior. • The `win_viewport` UI event now contains information about virtual lines, meaning that smooth scrolling can now be implemented more consistently. -• The `:= {expr}` syntax can be used to evaluate a lua expression, as +• The `:= {expr}` syntax can be used to evaluate a Lua expression, as a shorter form of `:lua ={expr}`. `:=` and `:[range]=` without argument are unchanged. However `:=#` and similar variants using |ex-flags| are no longer supported. diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt index 0182605b1b..61ae92296f 100644 --- a/runtime/doc/news.txt +++ b/runtime/doc/news.txt @@ -23,7 +23,12 @@ The following changes may require adaptations in user config or plugins. • "#" followed by a digit no longer stands for a function key at the start of the lhs of a mapping. -• `:behave` was removed. if you used `:behave mswin`, the following is equivalent: >vim +• `:behave` was removed. + - If you used `:behave xterm`, the following is equivalent: >vim + + set mousemodel=extend +< + - If you used `:behave mswin`, the following is equivalent: >vim set selection=exclusive set selectmode=mouse,key @@ -33,13 +38,29 @@ The following changes may require adaptations in user config or plugins. • When switching windows, |CursorMoved| autocommands trigger when Nvim is back in the main loop rather than immediately. This is more compatible with Vim. -• |LspRequest| autocmd was promoted from a |User| autocmd to a first class - citizen. +• |-l| ensures output ends with a newline if the script prints messages and + doesn't cause Nvim to exit. + +• |LspRequest| and LspProgressUpdate (renamed to |LspProgress|) autocmds were + promoted from a |User| autocmd to first class citizen. + +• Renamed `vim.treesitter.playground` to `vim.treesitter.dev`. + +• Removed functions from the |vim.json| module: + • Unnecessary, undocumented functions which caused global side-effects. + • `vim.json.null` is redundant with `vim.NIL`. + • `vim.json.array_mt` (and related) is redundant with `vim.empty_dict()`. ============================================================================== -ADDED FEATURES *news-added* +NEW FEATURES *news-features* + +The following new APIs and features were added. -The following new APIs or features were added. +• Performance: + • 'diffopt' "linematch" scoring algorithm now favours larger and less groups + https://github.com/neovim/neovim/pull/23611 + +• Added |vim.lsp.status()| to consume the last progress messages as a string. • Neovim's LSP client now always saves and restores named buffer marks when applying text edits. @@ -57,6 +78,8 @@ The following new APIs or features were added. • |vim.iter()| provides a generic iterator interface for tables and Lua iterators |luaref-in|. +• Added |vim.ringbuf()| to create ring buffers. + • Added |vim.keycode()| for translating keycodes in a string. • Added |vim.treesitter.query.omnifunc()| for treesitter query files (set by @@ -74,6 +97,16 @@ The following new APIs or features were added. is resized horizontally). Note: Lines that are not visible and kept in |'scrollback'| are not reflown. +• |vim.system()| for running system commands. + +• |nvim_set_keymap()| and |nvim_del_keymap()| now support abbreviations. + +• Implemented LSP inlay hints: |vim.lsp.inlay_hint()| + https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_inlayHint + +• Bundled treesitter parser and queries (highlight, folds) for Markdown, +Python, and Bash. + ============================================================================== CHANGED FEATURES *news-changed* @@ -93,7 +126,8 @@ The following changes to existing APIs or features add new behavior. • Automatic linting of treesitter query files (see |ft-query-plugin|). Can be disabled via: >lua vim.g.query_lint_on = {} -< • Enabled treesitter highlighting for treesitter query files. +< + • Enabled treesitter highlighting for treesitter query files. • The `workspace/didChangeWatchedFiles` LSP client capability is now enabled by default. @@ -104,6 +138,11 @@ The following changes to existing APIs or features add new behavior. • `:source` without arguments treats a buffer with its 'filetype' set to "lua" as Lua code regardless of its extension. +• |:checkhealth| buffer now implements |folding|. The initial folding status is + defined by the 'foldenable' option. + +• |:Man| now respects 'wrapmargin' + ============================================================================== REMOVED FEATURES *news-removed* @@ -133,6 +172,10 @@ release. - |nvim_win_get_option()| Use |nvim_get_option_value()| instead. - |nvim_win_set_option()| Use |nvim_set_option_value()| instead. +• vim.lsp functions: + - |vim.lsp.util.get_progress_messages()| Use |vim.lsp.status()| instead. + - |vim.lsp.for_each_buffer_client()| Use |vim.lsp.get_active_clients()| instead. + • `vim.loop` has been renamed to `vim.uv`. vim:tw=78:ts=8:sw=2:et:ft=help:norl: diff --git a/runtime/doc/nvim_terminal_emulator.txt b/runtime/doc/nvim_terminal_emulator.txt index b98c96ec87..bbcc686207 100644 --- a/runtime/doc/nvim_terminal_emulator.txt +++ b/runtime/doc/nvim_terminal_emulator.txt @@ -392,8 +392,8 @@ If there is no g:termdebug_config you can use: >vim let g:termdebug_map_K = 0 < *termdebug_disasm_window* -If you want the Asm window shown by default, set the flag to 1. -the "disasm_window_height" entry can be used to set the window height: >vim +If you want the Asm window shown by default, set the "disasm_window" flag to +1. The "disasm_window_height" entry can be used to set the window height: >vim let g:termdebug_config['disasm_window'] = 1 let g:termdebug_config['disasm_window_height'] = 15 If there is no g:termdebug_config you can use: >vim @@ -493,6 +493,20 @@ If there is no g:termdebug_config you can use: >vim let g:termdebug_popup = 0 +Change default signs ~ + *termdebug_signs* +Termdebug uses the last two characters of the breakpoint ID in the +signcolumn to represent breakpoints. For example, breakpoint ID 133 +will be displayed as `33`. + +If you want to customize the breakpoint signs: >vim + let g:termdebug_config['sign'] = '>>' +If there is no g:terminal_config yet you can use: >vim + let g:termdebug_config = {'sign': '>>'} + +After this, breakpoints will be displayed as `>>` in the signcolumn. + + Vim window width ~ *termdebug_wide* To change the width of the Vim window when debugging starts and use a vertical diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 4f5f2e8b80..da634966f9 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -676,9 +676,6 @@ A jump table for the options with a short description can be found at |Q_op|. line. When 'smartindent' or 'cindent' is on the indent is changed in a different way. - {small difference from Vi: After the indent is deleted when typing - <Esc> or <CR>, the cursor position when moving up or down is after the - deleted indent; Vi puts the cursor somewhere in the deleted indent}. *'autoread'* *'ar'* *'noautoread'* *'noar'* 'autoread' 'ar' boolean (default on) @@ -904,7 +901,7 @@ A jump table for the options with a short description can be found at |Q_op|. accidentally overwriting existing files with a backup file. You might prefer using ".bak", but make sure that you don't have files with ".bak" that you want to keep. - Only normal file name characters can be used; "/\*?[|<>" are illegal. + Only normal file name characters can be used; `/\*?[|<>` are illegal. If you like to keep a lot of backups, you could use a BufWritePre autocommand to change 'backupext' just before writing the file to @@ -1361,7 +1358,7 @@ A jump table for the options with a short description can be found at |Q_op|. The screen column can be an absolute number, or a number preceded with '+' or '-', which is added to or subtracted from 'textwidth'. > - :set cc=+1 " highlight column after 'textwidth' + :set cc=+1 " highlight column after 'textwidth' :set cc=+1,+2,+3 " highlight three columns after 'textwidth' :hi ColorColumn ctermbg=lightgrey guibg=lightgrey < @@ -1396,8 +1393,8 @@ A jump table for the options with a short description can be found at |Q_op|. 'commentstring' 'cms' string (default "") local to buffer A template for a comment. The "%s" in the value is replaced with the - comment text. Currently only used to add markers for folding, see - |fold-marker|. + comment text. For example, C uses "/*%s*/". Currently only used to + add markers for folding, see |fold-marker|. *'complete'* *'cpt'* *E535* 'complete' 'cpt' string (default: ".,w,b,u,t") @@ -1449,7 +1446,7 @@ A jump table for the options with a short description can be found at |Q_op|. *'completeslash'* *'csl'* 'completeslash' 'csl' string (default: "") local to buffer - {only for MS-Windows} + only for MS-Windows When this option is set it overrules 'shellslash' for completion: - When this option is set to "slash", a forward slash is used for path completion in insert mode. This is useful when editing HTML tag, or @@ -1861,17 +1858,16 @@ A jump table for the options with a short description can be found at |Q_op|. 'indentexpr'. *'define'* *'def'* -'define' 'def' string (default "^\s*#\s*define") +'define' 'def' string (default "") global or local to buffer |global-local| Pattern to be used to find a macro definition. It is a search pattern, just like for the "/" command. This option is used for the commands like "[i" and "[d" |include-search|. The 'isident' option is - used to recognize the defined name after the match: + used to recognize the defined name after the match: > {match with 'define'}{non-ID chars}{defined name}{non-ID char} - See |option-backslash| about inserting backslashes to include a space +< See |option-backslash| about inserting backslashes to include a space or backslash. - The default value is for C programs. For C++ this value would be - useful, to include const type declarations: > + For C++ this value would be useful, to include const type declarations: > ^\(#\s*define\|[a-z]*\s*const\s*[a-z]*\) < You can also use "\ze" just before the name and continue the pattern to check what is following. E.g. for Javascript, if a function is @@ -2089,17 +2085,13 @@ A jump table for the options with a short description can be found at |Q_op|. :set dir=c:\\tmp,\ dir\\,with\\,commas,\\\ dir\ with\ spaces < - For backwards compatibility with Vim version 3.0 a '>' at the start of the option is removed. - Using "." first in the list is recommended. This means that editing - the same file twice will result in a warning. Using "/tmp" on Unix is - discouraged: When the system crashes you lose the swap file. - "/var/tmp" is often not cleared when rebooting, thus is a better - choice than "/tmp". But others on the computer may be able to see the - files, and it can contain a lot of files, your swap files get lost in - the crowd. That is why a "tmp" directory in your home directory is - tried first. - The use of |:set+=| and |:set-=| is preferred when adding or removing - directories from the list. This avoids problems when a future version - uses another default. + + Editing the same file twice will result in a warning. Using "/tmp" on + is discouraged: if the system crashes you lose the swap file. And + others on the computer may be able to see the files. + Use |:set+=| and |:set-=| when adding or removing directories from the + list, this avoids problems if the Nvim default is changed. + This option cannot be set from a |modeline| or in the |sandbox|, for security reasons. @@ -2453,7 +2445,7 @@ A jump table for the options with a short description can be found at |Q_op|. one dot may appear. This option is not copied to another buffer, independent of the 's' or 'S' flag in 'cpoptions'. - Only normal file name characters can be used, "/\*?[|<>" are illegal. + Only normal file name characters can be used, `/\*?[|<>` are illegal. *'fillchars'* *'fcs'* 'fillchars' 'fcs' string (default "") @@ -2658,7 +2650,7 @@ A jump table for the options with a short description can be found at |Q_op|. item commands ~ all any - block "(", "{", "[[", "[{", etc. + block (, {, [[, [{, etc. hor horizontal movements: "l", "w", "fx", etc. insert any command in Insert mode jump far jumps: "G", "gg", etc. @@ -3309,12 +3301,11 @@ A jump table for the options with a short description can be found at |Q_op|. |Command-line-mode| is done. *'include'* *'inc'* -'include' 'inc' string (default "^\s*#\s*include") +'include' 'inc' string (default "") global or local to buffer |global-local| Pattern to be used to find an include command. It is a search - pattern, just like for the "/" command (See |pattern|). The default - value is for C programs. This option is used for the commands "[i", - "]I", "[d", etc. + pattern, just like for the "/" command (See |pattern|). This option + is used for the commands "[i", "]I", "[d", etc. Normally the 'isfname' option is used to recognize the file name that comes after the matched pattern. But if "\zs" appears in the pattern then the text matched from "\zs" to the end, or until "\ze" if it @@ -3590,7 +3581,7 @@ A jump table for the options with a short description can be found at |Q_op|. Setting this option to a valid keymap name has the side effect of setting 'iminsert' to one, so that the keymap becomes effective. 'imsearch' is also set to one, unless it was -1 - Only normal file name characters can be used, "/\*?[|<>" are illegal. + Only normal file name characters can be used, `/\*?[|<>` are illegal. *'keymodel'* *'km'* 'keymodel' 'km' string (default "") @@ -3675,7 +3666,7 @@ A jump table for the options with a short description can be found at |Q_op|. matter what $LANG is set to: > :set langmenu=nl_NL.ISO_8859-1 < When 'langmenu' is empty, |v:lang| is used. - Only normal file name characters can be used, "/\*?[|<>" are illegal. + Only normal file name characters can be used, `/\*?[|<>` are illegal. If your $LANG is set to a non-English language but you do want to use the English menus: > :set langmenu=none @@ -3747,7 +3738,7 @@ A jump table for the options with a short description can be found at |Q_op|. *'linespace'* *'lsp'* 'linespace' 'lsp' number (default 0) global - {only in the GUI} + only in the GUI Number of pixel lines inserted between characters. Useful if the font uses the full character cell height, making lines touch each other. When non-zero there is room for underlining. @@ -4048,9 +4039,9 @@ A jump table for the options with a short description can be found at |Q_op|. per word depends very much on how similar the words are, that's why this tuning is complicated. - There are three numbers, separated by commas: + There are three numbers, separated by commas: > {start},{inc},{added} - +< For most languages the uncompressed word tree fits in memory. {start} gives the amount of memory in Kbyte that can be used before any compression is done. It should be a bit smaller than the amount of @@ -4193,7 +4184,7 @@ A jump table for the options with a short description can be found at |Q_op|. *'mousehide'* *'mh'* *'nomousehide'* *'nomh'* 'mousehide' 'mh' boolean (default on) global - {only works in the GUI} + only in the GUI When on, the mouse pointer is hidden when characters are typed. The mouse pointer is restored when the mouse is moved. @@ -4432,7 +4423,7 @@ A jump table for the options with a short description can be found at |Q_op|. *'opendevice'* *'odev'* *'noopendevice'* *'noodev'* 'opendevice' 'odev' boolean (default off) global - {only for Windows} + only for Windows Enable reading and writing from devices. This may get Vim stuck on a device that can be opened but doesn't actually do the I/O. Therefore it is off by default. @@ -4453,7 +4444,8 @@ A jump table for the options with a short description can be found at |Q_op|. *'packpath'* *'pp'* 'packpath' 'pp' string (default: see 'runtimepath') - Directories used to find packages. See |packages| and |rtp-packages|. + Directories used to find packages. + See |packages| and |packages-runtimepath|. *'paragraphs'* *'para'* @@ -4486,11 +4478,10 @@ A jump table for the options with a short description can be found at |Q_op|. Using 'patchmode' for compressed files appends the extension at the end (e.g., "file.gz.orig"), thus the resulting name isn't always recognized as a compressed file. - Only normal file name characters can be used, "/\*?[|<>" are illegal. + Only normal file name characters can be used, `/\*?[|<>` are illegal. *'path'* *'pa'* *E343* *E345* *E347* *E854* -'path' 'pa' string (default on Unix: ".,/usr/include,," - other systems: ".,,") +'path' 'pa' string (default: ".,,") global or local to buffer |global-local| This is a list of directories which will be searched when using the |gf|, [f, ]f, ^Wf, |:find|, |:sfind|, |:tabfind| and other commands, @@ -5330,7 +5321,7 @@ A jump table for the options with a short description can be found at |Q_op|. *'shellslash'* *'ssl'* *'noshellslash'* *'nossl'* 'shellslash' 'ssl' boolean (default off) global - {only for MS-Windows} + only for MS-Windows When set, a forward slash is used when expanding file names. This is useful when a Unix-like shell is used instead of cmd.exe. Backward slashes can still be typed, but they are changed to forward slashes by @@ -5389,12 +5380,12 @@ A jump table for the options with a short description can be found at |Q_op|. local to buffer Number of spaces to use for each step of (auto)indent. Used for |'cindent'|, |>>|, |<<|, etc. - When zero the 'ts' value will be used. Use the |shiftwidth()| + When zero the 'tabstop' value will be used. Use the |shiftwidth()| function to get the effective shiftwidth value. *'shortmess'* *'shm'* 'shortmess' 'shm' string (default "filnxtToOCF") - global + global *E1336* This option helps to avoid all the |hit-enter| prompts caused by file messages, for example with CTRL-G, and to avoid some other messages. It is a list of flags: @@ -5665,6 +5656,8 @@ A jump table for the options with a short description can be found at |Q_op|. line in the window wraps part of it may not be visible, as if it is above the window. "<<<" is displayed at the start of the first line, highlighted with |hl-NonText|. + You may also want to add "lastline" to the 'display' option to show as + much of the last line as possible. NOTE: only partly implemented, currently works with CTRL-E, CTRL-Y and scrolling with the mouse. @@ -6267,7 +6260,7 @@ A jump table for the options with a short description can be found at |Q_op|. Syntax autocommand event is triggered with the value as argument. This option is not copied to another buffer, independent of the 's' or 'S' flag in 'cpoptions'. - Only normal file name characters can be used, "/\*?[|<>" are illegal. + Only normal file name characters can be used, `/\*?[|<>` are illegal. *'tabline'* *'tal'* 'tabline' 'tal' string (default empty) @@ -6316,13 +6309,25 @@ A jump table for the options with a short description can be found at |Q_op|. (or 3 or whatever you prefer) and use 'noexpandtab'. Then Vim will use a mix of tabs and spaces, but typing <Tab> and <BS> will behave like a tab appears every 4 (or 3) characters. - 2. Set 'tabstop' and 'shiftwidth' to whatever you prefer and use + This is the recommended way, the file will look the same with other + tools and when listing it in a terminal. + 2. Set 'softtabstop' and 'shiftwidth' to whatever you prefer and use + 'expandtab'. This way you will always insert spaces. The + formatting will never be messed up when 'tabstop' is changed (leave + it at 8 just in case). The file will be a bit larger. + You do need to check if no Tabs exist in the file. You can get rid + of them by first setting 'expandtab' and using `%retab!`, making + sure the value of 'tabstop' is set correctly. + 3. Set 'tabstop' and 'shiftwidth' to whatever you prefer and use 'expandtab'. This way you will always insert spaces. The formatting will never be messed up when 'tabstop' is changed. - 3. Set 'tabstop' and 'shiftwidth' to whatever you prefer and use a + You do need to check if no Tabs exist in the file, just like in the + item just above. + 4. Set 'tabstop' and 'shiftwidth' to whatever you prefer and use a |modeline| to set these values when editing the file again. Only - works when using Vim to edit the file. - 4. Always set 'tabstop' and 'shiftwidth' to the same value, and + works when using Vim to edit the file, other tools assume a tabstop + is worth 8 spaces. + 5. Always set 'tabstop' and 'shiftwidth' to the same value, and 'noexpandtab'. This should then work (for initial indents only) for any tabstop setting that people use. It might be nice to have tabs after the first non-blank inserted as spaces if you do this @@ -7047,7 +7052,7 @@ A jump table for the options with a short description can be found at |Q_op|. *'winaltkeys'* *'wak'* 'winaltkeys' 'wak' string (default "menu") global - {only used in Win32} + only used in Win32 Some GUI versions allow the access to menu entries by using the ALT key in combination with a character that appears underlined in the menu. This conflicts with the use of the ALT key for mappings and diff --git a/runtime/doc/pattern.txt b/runtime/doc/pattern.txt index 625e1f95e1..7ea3b7df76 100644 --- a/runtime/doc/pattern.txt +++ b/runtime/doc/pattern.txt @@ -432,8 +432,6 @@ after: \v \m \M \V matches ~ \\ \\ \\ \\ literal backslash \{ { { { literal curly brace -{only Vim supports \m, \M, \v and \V} - If you want to you can make a pattern immune to the 'magic' option being set or not by putting "\m" or "\M" at the start of the pattern. @@ -455,7 +453,7 @@ More explanation and examples below, follow the links. *E64* *E871* \{n} \{n} n exactly \{n,} \{n,} at least n as many as possible \{,m} \{,m} 0 to m as many as possible - \{} \{} 0 or more as many as possible (same as *) + \{} \{} 0 or more as many as possible (same as "*") |/\{-| \{-n,m} \{-n,m} n to m as few as possible \{-n} \{-n} n exactly @@ -633,7 +631,7 @@ overview. \{n} Matches n of the preceding atom \{n,} Matches at least n of the preceding atom, as many as possible \{,m} Matches 0 to m of the preceding atom, as many as possible -\{} Matches 0 or more of the preceding atom, as many as possible (like *) +\{} Matches 0 or more of the preceding atom, as many as possible (like "*") */\{-* \{-n,m} matches n to m of the preceding atom, as few as possible \{-n} matches n of the preceding atom @@ -1256,7 +1254,6 @@ letters only. When "\c" appears anywhere in the pattern, the whole pattern is handled like 'ignorecase' is on. The actual value of 'ignorecase' and 'smartcase' is ignored. "\C" does the opposite: Force matching case for the whole pattern. -{only Vim supports \c and \C} Note that 'ignorecase', "\c" and "\C" are not used for the character classes. Examples: @@ -1298,7 +1295,6 @@ will probably never match. When "\Z" appears anywhere in the pattern, all composing characters are ignored. Thus only the base characters need to match, the composing characters may be different and the number of composing characters may differ. -Only relevant when 'encoding' is "utf-8". Exception: If the pattern starts with one or more composing characters, these must match. */\%C* @@ -1339,11 +1335,10 @@ difference between them is mostly just notation; here's a summary of where they differ: Capability in Vimspeak in Perlspeak ~ ----------------------------------------------------------------- force case insensitivity \c (?i) force case sensitivity \C (?-i) backref-less grouping \%(atom\) (?:atom) -conservative quantifiers \{-n,m} *?, +?, ??, {}? +conservative quantifiers \{-n,m} `*?,` +?, ??, {}? 0-width match atom\@= (?=atom) 0-width non-match atom\@! (?!atom) 0-width preceding match atom\@<= (?<=atom) diff --git a/runtime/doc/pi_gzip.txt b/runtime/doc/pi_gzip.txt index 0363a8e34a..a709b34180 100644 --- a/runtime/doc/pi_gzip.txt +++ b/runtime/doc/pi_gzip.txt @@ -19,7 +19,7 @@ You can avoid loading this plugin by setting the "loaded_gzip" variable: > The plugin installs autocommands to intercept reading and writing of files with these extensions: - extension compression ~ + extension compression > *.Z compress (Lempel-Ziv) *.gz gzip *.bz2 bzip2 diff --git a/runtime/doc/pi_msgpack.txt b/runtime/doc/pi_msgpack.txt index 24a31f1de7..bcb7b9dc25 100644 --- a/runtime/doc/pi_msgpack.txt +++ b/runtime/doc/pi_msgpack.txt @@ -6,7 +6,7 @@ Copyright: (c) 2015 by Nikolay Pavlov The Apache license applies to the files in this package, including runtime/autoload/msgpack.vim, runtime/doc/pi_msgpack.txt and test/functional/plugin/msgpack_spec.lua. Like anything else that's free, -msgpack.vim and its associated files are provided *as is* and comes with no +msgpack.vim and its associated files are provided as is and comes with no warranty of any kind, either expressed or implied. No guarantees of merchantability. No guarantees of suitability for any purpose. By using this plugin, you agree that in no event will the copyright holder be liable for any diff --git a/runtime/doc/pi_netrw.txt b/runtime/doc/pi_netrw.txt index 276041caac..85ac290361 100644 --- a/runtime/doc/pi_netrw.txt +++ b/runtime/doc/pi_netrw.txt @@ -91,7 +91,6 @@ Copyright: Copyright (C) 2017 Charles E Campbell *netrw-copyright* Marked Files: Grep..................................|netrw-mg| Marked Files: Hiding and Unhiding by Suffix.........|netrw-mh| Marked Files: Moving................................|netrw-mm| - Marked Files: Printing..............................|netrw-mp| Marked Files: Sourcing..............................|netrw-ms| Marked Files: Setting the Target Directory..........|netrw-mt| Marked Files: Tagging...............................|netrw-mT| @@ -440,12 +439,10 @@ settings are described below, in |netrw-browser-options|, and in *g:netrw_use_errorwindow* =2: messages from netrw will use a popup window Move the mouse and pause to remove the popup window. - (default value if popup windows are available) =1 : messages from netrw will use a separate one line window. This window provides reliable delivery of messages. - (default value if popup windows are not available) - =0 : messages from netrw will use echoerr ; + =0 : (default) messages from netrw will use echoerr ; messages don't always seem to show up this way, but one doesn't have to quit the window. @@ -1108,7 +1105,6 @@ QUICK REFERENCE: MAPS *netrw-browse-maps* {{{2 mg Apply vimgrep to marked files |netrw-mg| mh Toggle marked file suffices' presence on hiding list |netrw-mh| mm Move marked files to marked-file target directory |netrw-mm| - mp Print marked files |netrw-mp| mr Mark files using a shell-style |regexp| |netrw-mr| mt Current browsing directory becomes markfile target |netrw-mt| mT Apply ctags to marked files |netrw-mT| @@ -2154,7 +2150,6 @@ The following netrw maps make use of marked files: |netrw-mF| Unmark marked files |netrw-mg| Apply vimgrep to marked files |netrw-mm| Move marked files to target - |netrw-mp| Print marked files |netrw-ms| Netrw will source marked files |netrw-mt| Set target for |netrw-mm| and |netrw-mc| |netrw-mT| Generate tags using marked files diff --git a/runtime/doc/pi_tar.txt b/runtime/doc/pi_tar.txt index b104aef194..dd1edb5707 100644 --- a/runtime/doc/pi_tar.txt +++ b/runtime/doc/pi_tar.txt @@ -11,7 +11,7 @@ Copyright 2005-2017: *tar-copyright* package, including tarPlugin.vim, tar.vim, and pi_tar.txt. Like anything else that's except use "tar.vim" instead of "VIM". Like anything else that's free, tar.vim and its associated files are - provided *as is* and comes with no warranty of any kind, either + provided as is and comes with no warranty of any kind, either expressed or implied. No guarantees of merchantability. No guarantees of suitability for any purpose. By using this plugin, you agree that in no event will the copyright holder be liable for any @@ -27,7 +27,7 @@ Copyright 2005-2017: *tar-copyright* ============================================================================== 2. Usage *tar-usage* *tar-manual* - When one edits a *.tar file, this plugin will handle displaying a + When one edits a `*.tar` file, this plugin will handle displaying a contents page. Select a file to edit by moving the cursor atop the desired file, then hit the <return> key. After editing, one may also write to the file. Currently, one may not make a new file in @@ -121,8 +121,8 @@ Copyright 2005-2017: *tar-copyright* May 28, 2008 * various security improvements. Now requires patch 299 which provides the fnameescape() function - May 30, 2008 * allows one to view *.gz and *.bz2 files that - are in *.tar files. + May 30, 2008 * allows one to view `*.gz` and `*.bz2` files that + are in `*.tar` files. v12 Sep 07, 2007 * &shq now used if not the empty string for g:tar_shq v10 May 02, 2006 * now using "redraw then echo" to show messages, diff --git a/runtime/doc/pi_zip.txt b/runtime/doc/pi_zip.txt index dd14b6c295..343c73839b 100644 --- a/runtime/doc/pi_zip.txt +++ b/runtime/doc/pi_zip.txt @@ -10,7 +10,7 @@ Copyright: Copyright (C) 2005-2015 Charles E Campbell *zip-copyright* The VIM LICENSE (see |copyright|) applies to the files in this package, including zipPlugin.vim, zip.vim, and pi_zip.vim. except use "zip.vim" instead of "VIM". Like anything else that's free, zip.vim - and its associated files are provided *as is* and comes with no + and its associated files are provided as is and comes with no warranty of any kind, either expressed or implied. No guarantees of merchantability. No guarantees of suitability for any purpose. By using this plugin, you agree that in no event will the copyright @@ -27,7 +27,7 @@ Copyright: Copyright (C) 2005-2015 Charles E Campbell *zip-copyright* ============================================================================== 2. Usage *zip-usage* *zip-manual* - When one edits a *.zip file, this plugin will handle displaying a + When one edits a `*.zip` file, this plugin will handle displaying a contents page. Select a file to edit by moving the cursor atop the desired file, then hit the <return> key. After editing, one may also write to the file. Currently, one may not make a new file in @@ -102,7 +102,7 @@ Copyright: Copyright (C) 2005-2015 Charles E Campbell *zip-copyright* ============================================================================== 4. History *zip-history* {{{1 - v33 Dec 07, 2021 * *.xlam mentioned twice in zipPlugin + v33 Dec 07, 2021 * `*.xlam` mentioned twice in zipPlugin v32 Oct 22, 2021 * to avoid an issue with a vim 8.2 patch, zipfile: has been changed to zipfile:// . This often shows up as zipfile:/// with zipped files that are root-based. @@ -114,8 +114,8 @@ Copyright: Copyright (C) 2005-2015 Charles E Campbell *zip-copyright* the command actually to be attempted in zip#Read() and zip#Write() * added the extraction of a file capability - Nov 30, 2015 * added *.epub to the |g:zipPlugin_ext| list - Sep 13, 2016 * added *.apk to the |g:zipPlugin_ext| list and + Nov 30, 2015 * added `*.epub` to the |g:zipPlugin_ext| list + Sep 13, 2016 * added `*.apk` to the |g:zipPlugin_ext| list and sorted the suffices. v27 Jul 02, 2013 * sanity check: zipfile must have "PK" as its first two bytes. diff --git a/runtime/doc/provider.txt b/runtime/doc/provider.txt index a476a56877..9e70ff8945 100644 --- a/runtime/doc/provider.txt +++ b/runtime/doc/provider.txt @@ -66,7 +66,7 @@ To disable Python 3 support: >vim PYTHON VIRTUALENVS ~ *python-virtualenv* If you plan to use per-project virtualenvs often, you should assign one -virtualenv for Neovim and hard-code the interpreter path via +virtualenv for Nvim and hard-code the interpreter path via |g:python3_host_prog| so that the "pynvim" package is not required for each virtualenv. diff --git a/runtime/doc/quickfix.txt b/runtime/doc/quickfix.txt index 7c06358be2..7a1649a74b 100644 --- a/runtime/doc/quickfix.txt +++ b/runtime/doc/quickfix.txt @@ -867,11 +867,11 @@ lists. They set one of the existing error lists as the current one. *:chistory* *:chi* :[count]chi[story] Show the list of error lists. The current list is - marked with ">". The output looks like: - error list 1 of 3; 43 errors :make ~ - > error list 2 of 3; 0 errors :helpgrep tag ~ - error list 3 of 3; 15 errors :grep ex_help *.c ~ - + marked with ">". The output looks like: > + error list 1 of 3; 43 errors :make + > error list 2 of 3; 0 errors :helpgrep tag + error list 3 of 3; 15 errors :grep ex_help *.c +< When [count] is given, then the count'th quickfix list is made the current list. Example: > " Make the 4th quickfix list current @@ -1317,8 +1317,8 @@ TEX COMPILER *compiler-tex* Included in the distribution compiler for TeX ($VIMRUNTIME/compiler/tex.vim) uses make command if possible. If the compiler finds a file named "Makefile" or "makefile" in the current directory, it supposes that you want to process -your *TeX files with make, and the makefile does the right work. In this case -compiler sets 'errorformat' for *TeX output and leaves 'makeprg' untouched. If +your `*TeX` files with make, and the makefile does the right work. In this case +compiler sets 'errorformat' for `*TeX` output and leaves 'makeprg' untouched. If neither "Makefile" nor "makefile" is found, the compiler will not use make. You can force the compiler to ignore makefiles by defining b:tex_ignore_makefile or g:tex_ignore_makefile variable (they are checked for @@ -1933,7 +1933,7 @@ You can customize the given setting to suit your own purposes, for example, all the annoying "Overfull ..." warnings could be excluded from being recognized as an error. Alternatively to filtering the LaTeX compiler output, it is also possible -to directly read the *.log file that is produced by the [La]TeX compiler. +to directly read the `*.log` file that is produced by the [La]TeX compiler. This contains even more useful information about possible error causes. However, to properly parse such a complex file, an external filter should be used. See the description further above how to make such a filter known diff --git a/runtime/doc/quickref.txt b/runtime/doc/quickref.txt index c166ecd79d..fd9eab8646 100644 --- a/runtime/doc/quickref.txt +++ b/runtime/doc/quickref.txt @@ -489,8 +489,8 @@ In Insert or Command-line mode: |v_ip| N ip Select "inner paragraph" |v_ab| N ab Select "a block" (from "[(" to "])") |v_ib| N ib Select "inner block" (from "[(" to "])") -|v_aB| N aB Select "a Block" (from "[{" to "]}") -|v_iB| N iB Select "inner Block" (from "[{" to "]}") +|v_aB| N aB Select "a Block" (from `[{` to `]}`) +|v_iB| N iB Select "inner Block" (from `[{` to `]}`) |v_a>| N a> Select "a <> block" |v_i>| N i> Select "inner <> block" |v_at| N at Select "a tag block" (from <aaa> to </aaa>) diff --git a/runtime/doc/remote_plugin.txt b/runtime/doc/remote_plugin.txt index 348f6c86a5..ea35cf2b78 100644 --- a/runtime/doc/remote_plugin.txt +++ b/runtime/doc/remote_plugin.txt @@ -119,7 +119,7 @@ would also be spawned, which could take seconds! With the manifest, each host will only be loaded when required. Continuing with the example, say the Java plugin is a semantic completion engine for Java code. -If it defines the autocommand "BufEnter *.java", then the Java host is spawned +If it defines the autocommand `BufEnter *.java`, then the Java host is spawned only when Nvim loads a buffer matching "*.java". If the explicit call to |:UpdateRemotePlugins| seems inconvenient, try to see diff --git a/runtime/doc/repeat.txt b/runtime/doc/repeat.txt index 071b062957..558cc75d65 100644 --- a/runtime/doc/repeat.txt +++ b/runtime/doc/repeat.txt @@ -260,8 +260,8 @@ For writing a Vim script, see chapter 41 of the user manual |usr_41.txt|. 'runtimepath'. If the filetype detection was already enabled (this - is usually done with a "syntax enable" or "filetype - on" command in your |init.vim|, or automatically during + is usually done with a `syntax enable` or `filetype on` + command in your |vimrc|, or automatically during |initialization|), and the package was found in "pack/*/opt/{name}", this command will also look for "{name}/ftdetect/*.vim" files. diff --git a/runtime/doc/russian.txt b/runtime/doc/russian.txt index 8d3ed360c8..89be1a3c5a 100644 --- a/runtime/doc/russian.txt +++ b/runtime/doc/russian.txt @@ -34,7 +34,6 @@ enter Normal mode command, you can also set 'langmap' option: :set langmap=ФИСВУАПРШОЛДЬТЩЗЙКЫЕГМЦЧНЯ;ABCDEFGHIJKLMNOPQRSTUVWXYZ, фисвуапршолдьтщзйкыегмцчня;abcdefghijklmnopqrstuvwxyz -This is in utf-8, you cannot read this if your 'encoding' is not utf-8. You have to type this command in one line, it is wrapped for the sake of readability. diff --git a/runtime/doc/scroll.txt b/runtime/doc/scroll.txt index 170c87a1a4..92546799b4 100644 --- a/runtime/doc/scroll.txt +++ b/runtime/doc/scroll.txt @@ -40,9 +40,6 @@ CTRL-D Scroll window Downwards in the buffer. The number of difference). When the cursor is on the last line of the buffer nothing happens and a beep is produced. See also 'startofline' option. - {difference from vi: Vim scrolls 'scroll' screen - lines, instead of file lines; makes a difference when - lines wrap} <S-Down> or *<S-Down>* *<kPageDown>* <PageDown> or *<PageDown>* *CTRL-F* diff --git a/runtime/doc/spell.txt b/runtime/doc/spell.txt index 75e4767743..29e4a7b0aa 100644 --- a/runtime/doc/spell.txt +++ b/runtime/doc/spell.txt @@ -111,7 +111,7 @@ zuG Undo |zW| and |zG|, remove the word from the internal list, like with |zW|. *:spellra* *:spellrare* -:[count]spellr[are] {word} +:[count]spellra[re] {word} Add {word} as a rare word to 'spellfile', similar to |zw|. Without count the first name is used, with a count of two the second entry, etc. @@ -124,7 +124,7 @@ zuG Undo |zW| and |zG|, remove the word from the internal nnoremap z/ :exe ':spellrare! ' .. expand('<cWORD>')<CR> < |:spellundo|, |zuw|, or |zuW| can be used to undo this. -:spellr[rare]! {word} Add {word} as a rare word to the internal word +:spellra[re]! {word} Add {word} as a rare word to the internal word list, similar to |zW|. :[count]spellu[ndo] {word} *:spellu* *:spellundo* diff --git a/runtime/doc/starting.txt b/runtime/doc/starting.txt index b3a30b20e8..53a14c586f 100644 --- a/runtime/doc/starting.txt +++ b/runtime/doc/starting.txt @@ -227,6 +227,8 @@ argument. Sets 'verbose' to 1 (like "-V1"), so Lua `print()` writes to output. + If {script} prints messages and doesn't cause Nvim to exit, + Nvim ensures output ends with a newline. Arguments before "-l" are processed before executing {script}. This example quits before executing "foo.lua": > @@ -241,8 +243,8 @@ argument. *-ll* -ll {script} [args] - Execute a lua script, similarly to |-l|, but the editor is not - initialized. This gives a lua environment similar to a worker + Execute a Lua script, similarly to |-l|, but the editor is not + initialized. This gives a Lua environment similar to a worker thread. See |lua-loop-threading|. Unlike `-l` no prior arguments are allowed. @@ -426,9 +428,11 @@ accordingly, proceeding as follows: 2. Process the arguments The options and file names from the command that start Vim are - inspected. Buffers are created for all files (but not loaded yet). + inspected. The |-V| argument can be used to display or log what happens next, useful for debugging the initializations. + The |--cmd| arguments are executed. + Buffers are created for all files (but not loaded yet). 3. Start a server (unless |--listen| was given) and set |v:servername|. @@ -822,7 +826,7 @@ resulting file, when executed with a ":source" command: 9. Restores the Views for all the windows, as with |:mkview|. But 'sessionoptions' is used instead of 'viewoptions'. 10. If a file exists with the same name as the Session file, but ending in - "x.vim" (for eXtra), executes that as well. You can use *x.vim files to + "x.vim" (for eXtra), executes that as well. You can use `*x.vim` files to specify additional settings and actions associated with a given Session, such as creating menu items in the GUI version. @@ -906,7 +910,7 @@ found. You might want to clean up your 'viewdir' directory now and then. -To automatically save and restore views for *.c files: > +To automatically save and restore views for `*.c` files: > au BufWinLeave *.c mkview au BufWinEnter *.c silent! loadview @@ -916,7 +920,7 @@ Shada ("shared data") file *shada* *shada-file* If you exit Vim and later start it again, you would normally lose a lot of information. The ShaDa file can be used to remember that information, which enables you to continue where you left off. Its name is the abbreviation of -SHAred DAta because it is used for sharing data between Neovim sessions. +SHAred DAta because it is used for sharing data between Nvim sessions. This is introduced in section |21.3| of the user manual. @@ -1008,16 +1012,16 @@ MERGING *shada-merging* When writing ShaDa files with |:wshada| without bang or at regular exit information in the existing ShaDa file is merged with information from current -Neovim instance. For this purpose ShaDa files store timestamps associated +Nvim instance. For this purpose ShaDa files store timestamps associated with ShaDa entries. Specifically the following is being done: 1. History lines are merged, ordered by timestamp. Maximum amount of items in ShaDa file is defined by 'shada' option (|shada-/|, |shada-:|, |shada-@|, etc: one suboption for each character that represents history name (|:history|)). -2. Local marks and changes for files that were not opened by Neovim are copied - to new ShaDa file. Marks for files that were opened by Neovim are merged, - changes to files opened by Neovim are ignored. |shada-'| +2. Local marks and changes for files that were not opened by Nvim are copied + to new ShaDa file. Marks for files that were opened by Nvim are merged, + changes to files opened by Nvim are ignored. |shada-'| 3. Jump list is merged: jumps are ordered by timestamp, identical jumps (identical position AND timestamp) are squashed. 4. Search patterns and substitute strings are not merged: search pattern or @@ -1025,14 +1029,14 @@ with ShaDa entries. Specifically the following is being done: to ShaDa file. 5. For each register entity with greatest timestamp is the only saved. |shada-<| -6. All saved variables are saved from current Neovim instance. Additionally +6. All saved variables are saved from current Nvim instance. Additionally existing variable values are copied, meaning that the only way to remove variable from a ShaDa file is either removing it by hand or disabling writing variables completely. |shada-!| 7. For each global mark entity with greatest timestamp is the only saved. 8. Buffer list and header are the only entries which are not merged in any fashion: the only header and buffer list present are the ones from the - Neovim instance which was last writing the file. |shada-%| + Nvim instance which was last writing the file. |shada-%| COMPATIBILITY *shada-compatibility* @@ -1057,13 +1061,13 @@ ShaDa files are forward and backward compatible. This means that history types. |history| 6. Unknown keys found in register, local mark, global mark, change, jump and search pattern entries are saved internally and dumped when writing. - Entries created during Neovim session never have such additions. + Entries created during Nvim session never have such additions. 7. Additional elements found in replacement string and history entries are - saved internally and dumped. Entries created during Neovim session never + saved internally and dumped. Entries created during Nvim session never have such additions. 8. Additional elements found in variable entries are simply ignored when reading. When writing new variables they will be preserved during merging, - but that's all. Variable values dumped from current Neovim session never + but that's all. Variable values dumped from current Nvim session never have additional elements, even if variables themselves were obtained by reading ShaDa files. @@ -1100,7 +1104,7 @@ start with an existing one to get the format right. You need to understand MessagePack (or, more likely, find software that is able to use it) format to do this. This can be useful in order to create a second file, say "~/.my.shada" which could contain certain settings that you always want when -you first start Neovim. For example, you can preload registers with +you first start Nvim. For example, you can preload registers with particular data, or put certain commands in the command line history. A line in your |config| file like > :rshada! ~/.my.shada @@ -1110,12 +1114,12 @@ file name, using the ":autocmd" command (see |:autocmd|). More information on ShaDa file format is contained in |shada-format| section. *E136* *E929* *shada-error-handling* -Some errors make Neovim leave temporary file named `{basename}.tmp.X` (X is +Some errors make Nvim leave temporary file named `{basename}.tmp.X` (X is any free letter from `a` to `z`) while normally it will create this file, write to it and then rename `{basename}.tmp.X` to `{basename}`. Such errors include: -- Errors which make Neovim think that read file is not a ShaDa file at all: +- Errors which make Nvim think that read file is not a ShaDa file at all: non-ShaDa files are not overwritten for safety reasons to avoid accidentally destroying an unrelated file. This could happen e.g. when typing "nvim -i file" in place of "nvim -R file" (yes, somebody did that at least with Vim). @@ -1123,26 +1127,26 @@ include: - If writing to the temporary file failed: e.g. because of the insufficient space left. - If renaming file failed: e.g. because of insufficient permissions. -- If target ShaDa file has different from the Neovim instance's owners (user +- If target ShaDa file has different from the Nvim instance's owners (user and group) and changing them failed. Unix-specific, applies only when - Neovim was launched from root. + Nvim was launched from root. Do not forget to remove the temporary file or replace the target file with temporary one after getting one of the above errors or all attempts to create a ShaDa file may fail with |E929|. If you got one of them when using -|:wshada| (and not when exiting Neovim: i.e. when you have Neovim session +|:wshada| (and not when exiting Nvim: i.e. when you have Nvim session running) you have additional options: - First thing which you should consider if you got any error, except failure to write to the temporary file: remove existing file and replace it with the - temporary file. Do it even if you have running Neovim instance. + temporary file. Do it even if you have running Nvim instance. - Fix the permissions and/or file ownership, free some space and attempt to write again. Do not remove the existing file. - Use |:wshada| with bang. Does not help in case of permission error. If target file was actually the ShaDa file some information may be lost in this case. To make the matters slightly better use |:rshada| prior to writing, but this still will loose buffer-local marks and change list entries for any - file which is not opened in the current Neovim instance. + file which is not opened in the current Nvim instance. - Remove the target file from shell and use |:wshada|. Consequences are not different from using |:wshada| with bang, but "rm -f" works in some cases when you don't have write permissions. @@ -1209,7 +1213,7 @@ exactly four MessagePack objects: Key Data ~ generator Binary, software used to generate ShaDa file. Is equal to "nvim" when ShaDa file was - written by Neovim. + written by Nvim. version Binary, generator version. encoding Binary, effective 'encoding' value. max_kbyte Integer, effective |shada-s| limit value. @@ -1382,20 +1386,23 @@ STATE DIRECTORY (DEFAULT) ~ Unix: ~/.local/state ~/.local/state/nvim Windows: ~/AppData/Local ~/AppData/Local/nvim-data -Note: Throughout the user manual these defaults are used as placeholders, e.g. +Note: Throughout the help pages these defaults are used as placeholders, e.g. "~/.config" is understood to mean "$XDG_CONFIG_HOME or ~/.config". NVIM_APPNAME *$NVIM_APPNAME* -The XDG directories used by Nvim can be further configured by setting the -`$NVIM_APPNAME` environment variable. This variable controls the directory -Neovim will look for (and auto-create) in the various XDG parent directories. -For example, setting `$NVIM_APPNAME` to "neovim" before running Neovim will -result in Neovim looking for configuration files in `$XDG_CONFIG_HOME/neovim` -instead of `$XDG_CONFIG_HOME/nvim`. - -Note: Similarly to the $XDG environment variables, when -`$XDG_CONFIG_HOME/nvim` is mentioned, it should be understood as -`$XDG_CONFIG_HOME/$NVIM_APPNAME`. +The standard directories can be further configured by the `$NVIM_APPNAME` +environment variable. This variable controls the sub-directory that Nvim will +read from (and auto-create) in each of the base directories. For example, +setting `$NVIM_APPNAME` to "foo" before starting will cause Nvim to look for +configuration files in `$XDG_CONFIG_HOME/foo` instead of +`$XDG_CONFIG_HOME/nvim`. + +One use-case for $NVIM_APPNAME is to "isolate" Nvim applications. +Alternatively, for true isolation, on Linux you can use cgroups namespaces: > + systemd-run --user -qt -p PrivateUsers=yes -p BindPaths=/home/user/profile_xy:/home/user/.config/nvim nvim + +Note: Throughout the help pages, wherever `$XDG_CONFIG_…/nvim` is mentioned it +is understood to mean `$XDG_CONFIG_…/$NVIM_APPNAME`. LOG FILE *log* *$NVIM_LOG_FILE* *E5430* Besides 'debug' and 'verbose', Nvim keeps a general log file for internal diff --git a/runtime/doc/syntax.txt b/runtime/doc/syntax.txt index 14f613d3fc..09c935cb9b 100644 --- a/runtime/doc/syntax.txt +++ b/runtime/doc/syntax.txt @@ -197,19 +197,19 @@ be preferred names for highlight groups that are common for many languages. These are the suggested group names (if syntax highlighting works properly you can see the actual color, except for "Ignore"): - *Comment any comment + Comment any comment - *Constant any constant + Constant any constant String a string constant: "this is a string" Character a character constant: 'c', '\n' Number a number constant: 234, 0xff Boolean a boolean constant: TRUE, false Float a floating point constant: 2.3e10 - *Identifier any variable name + Identifier any variable name Function function name (also: methods for classes) - *Statement any statement + Statement any statement Conditional if, then, else, endif, switch, etc. Repeat for, do, while, etc. Label case, default, etc. @@ -217,31 +217,31 @@ you can see the actual color, except for "Ignore"): Keyword any other keyword Exception try, catch, throw - *PreProc generic Preprocessor + PreProc generic Preprocessor Include preprocessor #include Define preprocessor #define Macro same as Define PreCondit preprocessor #if, #else, #endif, etc. - *Type int, long, char, etc. + Type int, long, char, etc. StorageClass static, register, volatile, etc. Structure struct, union, enum, etc. Typedef A typedef - *Special any special symbol + Special any special symbol SpecialChar special character in a constant Tag you can use CTRL-] on this Delimiter character that needs attention SpecialComment special things inside a comment Debug debugging statements - *Underlined text that stands out, HTML links + Underlined text that stands out, HTML links - *Ignore left blank, hidden |hl-Ignore| + Ignore left blank, hidden |hl-Ignore| - *Error any erroneous construct + Error any erroneous construct - *Todo anything that needs extra attention; mostly the + Todo anything that needs extra attention; mostly the keywords TODO FIXME and XXX The names marked with * are the preferred groups; the others are minor groups. @@ -893,7 +893,7 @@ nasm_no_warn potentially risky syntax not as ToDo ASPPERL and ASPVBS *ft-aspperl-syntax* *ft-aspvbs-syntax* -*.asp and *.asa files could be either Perl or Visual Basic script. Since it's +`*.asp` and `*.asa` files could be either Perl or Visual Basic script. Since it's hard to detect this you can set two global variables to tell Vim what you are using. For Perl script use: > :let g:filetype_asa = "aspperl" @@ -979,7 +979,7 @@ Variable Highlight ~ *c_ansi_typedefs* ... but do standard ANSI types *c_ansi_constants* ... but do standard ANSI constants *c_no_utf* don't highlight \u and \U in strings -*c_syntax_for_h* for *.h files use C syntax instead of C++ and use objc +*c_syntax_for_h* for `*.h` files use C syntax instead of C++ and use objc syntax instead of objcpp *c_no_if0* don't highlight "#if 0" blocks as comments *c_no_cformat* don't highlight %-formats in strings @@ -987,7 +987,7 @@ Variable Highlight ~ *c_no_c11* don't highlight C11 standard items *c_no_bsd* don't highlight BSD specific types -When 'foldmethod' is set to "syntax" then /* */ comments and { } blocks will +When 'foldmethod' is set to "syntax" then `/* */` comments and { } blocks will become a fold. If you don't want comments to become a fold use: > :let c_no_comment_fold = 1 "#if 0" blocks are also folded, unless: > @@ -1034,7 +1034,7 @@ CH *ch.vim* *ft-ch-syntax* C/C++ interpreter. Ch has similar syntax highlighting to C and builds upon the C syntax file. See |c.vim| for all the settings that are available for C. -By setting a variable you can tell Vim to use Ch syntax for *.h files, instead +By setting a variable you can tell Vim to use Ch syntax for `*.h` files, instead of C or C++: > :let ch_syntax_for_h = 1 @@ -1296,7 +1296,7 @@ dosbatch_colons_comment variable to anything: > :let dosbatch_colons_comment = 1 -There is an option that covers whether *.btm files should be detected as type +There is an option that covers whether `*.btm` files should be detected as type "dosbatch" (MS-DOS batch files) or type "btm" (4DOS batch files). The latter is used by default. You may select the former with the following line: > @@ -1427,13 +1427,13 @@ Euphoria version 3.1.1 (https://www.rapideuphoria.com/) is still necessary for developing applications for the DOS platform, which Euphoria version 4 (https://www.openeuphoria.org/) does not support. -The following file extensions are auto-detected as Euphoria file type: +The following file extensions are auto-detected as Euphoria file type: > *.e, *.eu, *.ew, *.ex, *.exu, *.exw *.E, *.EU, *.EW, *.EX, *.EXU, *.EXW To select syntax highlighting file for Euphoria, as well as for -auto-detecting the *.e and *.E file extensions as Euphoria file type, +auto-detecting the `*.e` and `*.E` file extensions as Euphoria file type, add the following line to your startup file: > :let g:filetype_euphoria = "euphoria3" @@ -1442,7 +1442,7 @@ add the following line to your startup file: > :let g:filetype_euphoria = "euphoria4" -Elixir and Euphoria share the *.ex file extension. If the filetype is +Elixir and Euphoria share the `*.ex` file extension. If the filetype is specifically set as Euphoria with the g:filetype_euphoria variable, or the file is determined to be Euphoria based on keywords in the file, then the filetype will be set as Euphoria. Otherwise, the filetype will default to @@ -1469,11 +1469,11 @@ ELIXIR *elixir.vim* *ft-elixir-syntax* Elixir is a dynamic, functional language for building scalable and maintainable applications. -The following file extensions are auto-detected as Elixir file types: +The following file extensions are auto-detected as Elixir file types: > *.ex, *.exs, *.eex, *.leex, *.lock -Elixir and Euphoria share the *.ex file extension. If the filetype is +Elixir and Euphoria share the `*.ex` file extension. If the filetype is specifically set as Euphoria with the g:filetype_euphoria variable, or the file is determined to be Euphoria based on keywords in the file, then the filetype will be set as Euphoria. Otherwise, the filetype will default to @@ -1905,7 +1905,7 @@ IA64 *ia64.vim* *intel-itanium* *ft-ia64-syntax* Highlighting for the Intel Itanium 64 assembly language. See |asm.vim| for how to recognize this filetype. -To have *.inc files be recognized as IA64, add this to your vimrc file: > +To have `*.inc` files be recognized as IA64, add this to your vimrc file: > :let g:filetype_inc = "ia64" @@ -2118,7 +2118,7 @@ set "lite_minlines" to the value you desire. Example: > LPC *lpc.vim* *ft-lpc-syntax* LPC stands for a simple, memory-efficient language: Lars Pensjö C. The -file name of LPC is usually *.c. Recognizing these files as LPC would bother +file name of LPC is usually `*.c`. Recognizing these files as LPC would bother users writing only C programs. If you want to use LPC syntax in Vim, you should set a variable in your vimrc file: > @@ -2153,7 +2153,7 @@ For LPC4 series of LPC: > For uLPC series of LPC: uLPC has been developed to Pike, so you should use Pike syntax -instead, and the name of your source file should be *.pike +instead, and the name of your source file should be `*.pike` LUA *lua.vim* *ft-lua-syntax* @@ -2230,7 +2230,7 @@ the start of a region, for example 500 lines: > MATHEMATICA *mma.vim* *ft-mma-syntax* *ft-mathematica-syntax* -Empty *.m files will automatically be presumed to be Matlab files unless you +Empty `*.m` files will automatically be presumed to be Matlab files unless you have the following in your vimrc: > let filetype_m = "mma" @@ -2437,7 +2437,7 @@ keywords, etc): > The option pascal_symbol_operator controls whether symbol operators such as +, -*, .., etc. are displayed using the Operator color or not. To colorize symbol +`*`, .., etc. are displayed using the Operator color or not. To colorize symbol operators, add the following line to your startup file: > :let pascal_symbol_operator=1 @@ -2600,7 +2600,7 @@ x = 0 to sync from start. PLAINTEX *plaintex.vim* *ft-plaintex-syntax* TeX is a typesetting language, and plaintex is the file type for the "plain" -variant of TeX. If you never want your *.tex files recognized as plain TeX, +variant of TeX. If you never want your `*.tex` files recognized as plain TeX, see |ft-tex-plugin|. This syntax file has the option > @@ -3149,7 +3149,7 @@ The syntax/sh.vim file provides several levels of syntax-based folding: > let g:sh_fold_enabled= 1 (enable function folding) let g:sh_fold_enabled= 2 (enable heredoc folding) let g:sh_fold_enabled= 4 (enable if/do/for folding) -> + then various syntax items (ie. HereDocuments and function bodies) become syntax-foldable (see |:syn-fold|). You also may add these together to get multiple types of folding: > @@ -3446,8 +3446,8 @@ has a starred form (ie. eqnarray*). *tex-style* *b:tex_stylish* Tex: Starting a New Style? ~ -One may use "\makeatletter" in *.tex files, thereby making the use of "@" in -commands available. However, since the *.tex file doesn't have one of the +One may use "\makeatletter" in `*.tex` files, thereby making the use of "@" in +commands available. However, since the `*.tex` file doesn't have one of the following suffices: sty cls clo dtx ltx, the syntax highlighting will flag such use of @ as an error. To solve this: > @@ -3491,7 +3491,7 @@ substitution will not be made. Tex: Controlling iskeyword~ Normally, LaTeX keywords support 0-9, a-z, A-z, and 192-255 only. Latex -keywords don't support the underscore - except when in *.sty files. The +keywords don't support the underscore - except when in `*.sty` files. The syntax highlighting script handles this with the following logic: * If g:tex_stylish exists and is 1 @@ -3715,7 +3715,7 @@ Vim understands three types of syntax items: Several syntax ITEMs can be put into one syntax GROUP. For a syntax group you can give highlighting attributes. For example, you could have an item -to define a "/* .. */" comment and another one that defines a "// .." comment, +to define a `/* .. */` comment and another one that defines a "// .." comment, and put them both in the "Comment" group. You can then specify that a "Comment" will be in bold font and have a blue color. You are free to make one highlight group for one syntax item, or put all items into one group. @@ -4473,19 +4473,19 @@ Notes: matched. This doesn't work: "a\nb"ms=e. You can make the highlighting start in another line, this does work: "a\nb"hs=e. -Example (match a comment but don't highlight the /* and */): > +Example (match a comment but don't highlight the `/* and */`): >vim :syntax region Comment start="/\*"hs=e+1 end="\*/"he=s-1 -< +< > /* this is a comment */ ^^^^^^^^^^^^^^^^^^^ highlighted - -A more complicated Example: > - :syn region Exa matchgroup=Foo start="foo"hs=s+2,rs=e+2 matchgroup=Bar end="bar"me=e-1,he=e-1,re=s-1 < +A more complicated Example: >vim + :syn region Exa matchgroup=Foo start="foo"hs=s+2,rs=e+2 matchgroup=Bar end="bar"me=e-1,he=e-1,re=s-1 +< > abcfoostringbarabc mmmmmmmmmmm match sssrrreee highlight start/region/end ("Foo", "Exa" and "Bar") - +< Leading context *:syn-lc* *:syn-leading* *:syn-context* Note: This is an obsolete feature, only included for backwards compatibility @@ -4785,7 +4785,7 @@ matches, nextgroup, etc. But there are a few differences: - When a match with a sync pattern is found, the rest of the line (or group of continued lines) is searched for another match. The last match is used. This is used when a line can contain both the start end the end of a region - (e.g., in a C-comment like /* this */, the last "*/" is used). + (e.g., in a C-comment like `/* this */`, the last "*/" is used). There are two ways how a match with a sync pattern can be used: 1. Parsing for highlighting starts where redrawing starts (and where the @@ -5481,14 +5481,14 @@ memory Vim will consume. Only highlighting typedefs, unions and structs can be done too. For this you must use Universal Ctags (https://ctags.io) or Exuberant ctags. -Put these lines in your Makefile: +Put these lines in your Makefile: > -# Make a highlight file for types. Requires Universal/Exuberant ctags and awk -types: types.vim -types.vim: *.[ch] - ctags --c-kinds=gstu -o- *.[ch] |\ - awk 'BEGIN{printf("syntax keyword Type\t")}\ - {printf("%s ", $$1)}END{print ""}' > $@ + # Make a highlight file for types. Requires Universal/Exuberant ctags and awk + types: types.vim + types.vim: *.[ch] + ctags --c-kinds=gstu -o- *.[ch] |\ + awk 'BEGIN{printf("syntax keyword Type\t")}\ + {printf("%s ", $$1)}END{print ""}' > $@ And put these lines in your vimrc: > diff --git a/runtime/doc/tagsrch.txt b/runtime/doc/tagsrch.txt index 49e51d6736..cf8f307ca4 100644 --- a/runtime/doc/tagsrch.txt +++ b/runtime/doc/tagsrch.txt @@ -695,7 +695,7 @@ do not have an absolute path. The 'comments' option is used for the commands that display a single line or jump to a line. It defines patterns that may start a comment. Those lines are ignored for the search, unless [!] is used. One exception: When the line -matches the pattern "^# *define" it is not considered to be a comment. +matches the pattern `"^# *define"` it is not considered to be a comment. If you want to list matches, and then select one to jump to, you could use a mapping to do that for you. Here is an example: > @@ -838,7 +838,7 @@ Common arguments for the commands above: [!] When included, find matches in lines that are recognized as comments. When excluded, a match is ignored when the line is recognized as a comment (according to 'comments'), or the match is in a C comment - (after "//" or inside /* */). Note that a match may be missed if a + (after "//" or inside `/* */`). Note that a match may be missed if a line is recognized as a comment, but the comment ends halfway the line. And if the line is a comment, but it is not recognized (according to 'comments') a match may be found in it anyway. Example: > diff --git a/runtime/doc/tips.txt b/runtime/doc/tips.txt index 5d5e89da77..7d38b1e0f7 100644 --- a/runtime/doc/tips.txt +++ b/runtime/doc/tips.txt @@ -47,7 +47,7 @@ is an overview with tags to jump to: |gf| Go to file name under the cursor. -|%| Go to matching (), {}, [], /* */, #if, #else, #endif. +|%| Go to matching (), {}, [], `/* */`, #if, #else, #endif. |[/| Go to previous start of comment. |]/| Go to next end of comment. |[#| Go back to unclosed #if, #ifdef, or #else. @@ -59,8 +59,8 @@ is an overview with tags to jump to: |v_ab| Select "a block" from "[(" to "])", including braces |v_ib| Select "inner block" from "[(" to "])" -|v_aB| Select "a block" from "[{" to "]}", including brackets -|v_iB| Select "inner block" from "[{" to "]}" +|v_aB| Select "a block" from `[{` to `]}`, including brackets +|v_iB| Select "inner block" from `[{` to `]}` ============================================================================== Finding where identifiers are used *ident-search* @@ -196,7 +196,7 @@ charset.c digraph.c ... -and I want to rename *.c *.bla. I'd do it like this: > +and I want to rename `*.c` `*.bla`. I'd do it like this: > $ vim :r !ls *.c diff --git a/runtime/doc/treesitter.txt b/runtime/doc/treesitter.txt index 64b4730eee..7539417197 100644 --- a/runtime/doc/treesitter.txt +++ b/runtime/doc/treesitter.txt @@ -292,7 +292,39 @@ The following directives are built in: Example: >query ((identifier) @constant (#offset! @constant 0 1 0 -1)) < + `gsub!` *treesitter-directive-gsub!* + Transforms the content of the node using a Lua pattern. This will set + a new `metadata[capture_id].text`. + Parameters: ~ + {capture_id} + {pattern} + + Example: >query + (#gsub! @_node ".*%.(.*)" "%1") +< + `trim!` *treesitter-directive-trim!* + Trim blank lines from the end of the node. This will set a new + `metadata[capture_id].range`. + + Parameters: ~ + {capture_id} + + Example: >query + (#inject-language! @_lang) +< + `inject-language!` *treesitter-directive-inject-language!* + Set the injection language from the node text, interpreted first as a + language name, then (if a parser is not found) a filetype. Custom + aliases can be added via |vim.treesitter.language.register()|. This + will set a new `metadata[capture_id]['injection.language']`. + + Parameters: ~ + {capture_id} + + Example: >query + (#inject-language! @_lang) +< Further directives can be added via |vim.treesitter.query.add_directive()|. Use |vim.treesitter.query.list_directives()| to list all available directives. @@ -1216,12 +1248,4 @@ LanguageTree:trees({self}) *LanguageTree:trees()* Parameters: ~ • {self} - -============================================================================== -Lua module: vim.treesitter.playground *lua-treesitter-playground* - -inspect_tree({opts}) *vim.treesitter.playground.inspect_tree()* - Parameters: ~ - • {opts} InspectTreeOpts - vim:tw=78:ts=8:sw=4:sts=4:et:ft=help:norl: diff --git a/runtime/doc/ui.txt b/runtime/doc/ui.txt index d3bd82ba27..360189e928 100644 --- a/runtime/doc/ui.txt +++ b/runtime/doc/ui.txt @@ -199,7 +199,7 @@ the editor. The following keys are deprecated: `hl_id`: Use `attr_id` instead. - `hl_lm`: Use `attr_id_lm` instead. + `id_lm`: Use `attr_id_lm` instead. ["option_set", name, value] ~ UI-related option changed, where `name` is one of: diff --git a/runtime/doc/userfunc.txt b/runtime/doc/userfunc.txt index db0127df95..8b6462911d 100644 --- a/runtime/doc/userfunc.txt +++ b/runtime/doc/userfunc.txt @@ -44,6 +44,13 @@ functions. unless "!" is given. {name} may be a |Dictionary| |Funcref| entry: > :function dict.init +< Note that {name} is not an expression, you cannot use + a variable that is a function reference. You can use + this dirty trick to list the function referred to with + variable "Funcref": > + let g:MyFuncref = Funcref + func g:MyFuncref + unlet g:MyFuncref :fu[nction] /{pattern} List functions with a name matching {pattern}. Example that lists all functions ending with "File": > @@ -72,8 +79,7 @@ See |:verbose-cmd| for more information. name has a colon in the name, e.g. for "foo:bar()". Before that patch no error was given). - {name} can also be a |Dictionary| entry that is a - |Funcref|: > + {name} may be a |Dictionary| |Funcref| entry: > :function dict.init(arg) < "dict" must be an existing dictionary. The entry "init" is added if it didn't exist yet. Otherwise [!] diff --git a/runtime/doc/usr_09.txt b/runtime/doc/usr_09.txt index ea16010dc2..da9a404420 100644 --- a/runtime/doc/usr_09.txt +++ b/runtime/doc/usr_09.txt @@ -63,10 +63,10 @@ Vim will set the title to show the name of the current file. First comes the name of the file. Then some special characters and the directory of the file in parens. These special characters can be present: - - The file cannot be modified (e.g., a help file) - + The file contains changes - = The file is read-only - =+ The file is read-only, contains changes anyway + • - The file cannot be modified (e.g., a help file) + • + The file contains changes + • = The file is read-only + • =+ The file is read-only, contains changes anyway If nothing is shown you have an ordinary, unchanged file. @@ -223,7 +223,7 @@ Remember, "y" is yank, which is Vim's copy command. "+P It's the same as for the current selection, but uses the plus (+) register -instead of the star (*) register. +instead of the star "*" register. ============================================================================== *09.4* Select mode diff --git a/runtime/doc/usr_11.txt b/runtime/doc/usr_11.txt index 361fe51caa..1fa7fb6f77 100644 --- a/runtime/doc/usr_11.txt +++ b/runtime/doc/usr_11.txt @@ -82,9 +82,8 @@ You must be in the right directory, otherwise Vim can't find the swap file. ============================================================================== *11.2* Where is the swap file? -Vim can store the swap file in several places. Normally it is in the same -directory as the original file. To find it, change to the directory of the -file, and use: > +Vim can store the swap file in several places. To find it, change to the +directory of the file, and use: > vim -r diff --git a/runtime/doc/usr_12.txt b/runtime/doc/usr_12.txt index 51a25b1593..dd1d62bb52 100644 --- a/runtime/doc/usr_12.txt +++ b/runtime/doc/usr_12.txt @@ -59,7 +59,7 @@ playback. Let's assume you have a directory with C++ files, all ending in ".cpp". There is a function called "GetResp" that you want to rename to "GetAnswer". - vim *.cpp Start Vim, defining the argument list to + vim `*.cpp` Start Vim, defining the argument list to contain all the C++ files. You are now in the first file. qq Start recording into the q register @@ -331,7 +331,7 @@ program files, for example, enter the following command: > :grep error_string *.c This causes Vim to search for the string "error_string" in all the specified -files (*.c). The editor will now open the first file where a match is found +files (`*.c`). The editor will now open the first file where a match is found and position the cursor on the first matching line. To go to the next matching line (no matter in what file it is), use the ":cnext" command. To go to the previous match, use the ":cprev" command. Use ":clist" to see all the diff --git a/runtime/doc/usr_21.txt b/runtime/doc/usr_21.txt index 191d333f3d..b084936857 100644 --- a/runtime/doc/usr_21.txt +++ b/runtime/doc/usr_21.txt @@ -426,7 +426,7 @@ a line of text that tells Vim the values of options, to be used in this file only. A typical example is a C program where you make indents by a multiple of 4 spaces. This requires setting the 'shiftwidth' option to 4. This modeline -will do that: +will do that: > /* vim:set shiftwidth=4: */ ~ diff --git a/runtime/doc/usr_22.txt b/runtime/doc/usr_22.txt index d977b20ecc..ae569dc6a0 100644 --- a/runtime/doc/usr_22.txt +++ b/runtime/doc/usr_22.txt @@ -50,8 +50,8 @@ You can see these items: 1. The name of the browsing tool and its version number 2. The name of the browsing directory 3. The method of sorting (may be by name, time, or size) -4. How names are to be sorted (directories first, then *.h files, - *.c files, etc) +4. How names are to be sorted (directories first, then `*.h` files, + `*.c` files, etc) 5. How to get help (use the <F1> key), and an abbreviated listing of available commands 6. A listing of files, including "../", which allows one to list diff --git a/runtime/doc/usr_24.txt b/runtime/doc/usr_24.txt index efda2bc33d..db6c8e45d0 100644 --- a/runtime/doc/usr_24.txt +++ b/runtime/doc/usr_24.txt @@ -241,11 +241,11 @@ some other editors it's called intellisense, but that is a trademark. The key to Omni completion is CTRL-X CTRL-O. Obviously the O stands for Omni here, so that you can remember it easier. Let's use an example for editing C -source: +source: > - { ~ - struct foo *p; ~ - p-> ~ + { + struct foo *p; + p-> The cursor is after "p->". Now type CTRL-X CTRL-O. Vim will offer you a list of alternatives, which are the items that "struct foo" contains. That is @@ -270,13 +270,13 @@ work. If you press CTRL-A, the editor inserts the text you typed the last time you were in Insert mode. - Assume, for example, that you have a file that begins with the following: + Assume, for example, that you have a file that begins with the following: > "file.h" ~ /* Main program begins */ ~ You edit this file by inserting "#include " at the beginning of the first -line: +line: > #include "file.h" ~ /* Main program begins */ ~ @@ -286,13 +286,13 @@ now start to insert a new "#include" line. So you type: > i CTRL-A -The result is as follows: +The result is as follows: > #include "file.h" ~ #include /* Main program begins */ ~ The "#include " was inserted because CTRL-A inserts the text of the previous -insert. Now you type "main.h"<Enter> to finish the line: +insert. Now you type "main.h"<Enter> to finish the line: > #include "file.h" ~ @@ -429,7 +429,7 @@ mistake. LISTING ABBREVIATIONS -The ":abbreviate" command lists the abbreviations: +The ":abbreviate" command lists the abbreviations: > :abbreviate i #e ****************************************/ diff --git a/runtime/doc/usr_28.txt b/runtime/doc/usr_28.txt index 86aa20597e..96f635a307 100644 --- a/runtime/doc/usr_28.txt +++ b/runtime/doc/usr_28.txt @@ -277,7 +277,7 @@ Try it: > :set foldmethod=marker -Example text, as it could appear in a C program: +Example text, as it could appear in a C program: > /* foobar () {{{ */ int foobar() @@ -292,7 +292,7 @@ Notice that the folded line will display the text before the marker. This is very useful to tell what the fold contains. It's quite annoying when the markers don't pair up correctly after moving some -lines around. This can be avoided by using numbered markers. Example: +lines around. This can be avoided by using numbered markers. Example: > /* global variables {{{1 */ int varA, varB; diff --git a/runtime/doc/usr_29.txt b/runtime/doc/usr_29.txt index 751cb9a902..dd8598a3a0 100644 --- a/runtime/doc/usr_29.txt +++ b/runtime/doc/usr_29.txt @@ -307,9 +307,9 @@ tags file. Example: > :psearch popen This will show the "stdio.h" file in the preview window, with the function -prototype for popen(): +prototype for popen(): >c - FILE *popen __P((const char *, const char *)); ~ + FILE *popen __P((const char *, const char *)); You can specify the height of the preview window, when it is opened, with the 'previewheight' option. @@ -319,13 +319,13 @@ You can specify the height of the preview window, when it is opened, with the Since a program is structured, Vim can recognize items in it. Specific commands can be used to move around. - C programs often contain constructs like this: + C programs often contain constructs like this: >c - #ifdef USE_POPEN ~ - fd = popen("ls", "r") ~ - #else ~ - fd = fopen("tmp", "w") ~ - #endif ~ + #ifdef USE_POPEN + fd = popen("ls", "r") + #else + fd = fopen("tmp", "w") + #endif But then much longer, and possibly nested. Position the cursor on the "#ifdef" and press %. Vim will jump to the "#else". Pressing % again takes @@ -361,7 +361,7 @@ MOVING IN CODE BLOCKS In C code blocks are enclosed in {}. These can get pretty long. To move to the start of the outer block use the "[[" command. Use "][" to find the end. This assumes that the "{" and "}" are in the first column. - The "[{" command moves to the start of the current block. It skips over + The [{ command moves to the start of the current block. It skips over pairs of {} at the same level. "]}" jumps to the end. An overview: @@ -410,7 +410,7 @@ That also works when they are many lines apart. MOVING IN BRACES -The "[(" and "])" commands work similar to "[{" and "]}", except that they +The [( and ]) commands work similar to [{ and ]}, except that they work on () pairs instead of {} pairs. > [( @@ -424,7 +424,7 @@ work on () pairs instead of {} pairs. MOVING IN COMMENTS To move back to the start of a comment use "[/". Move forward to the end of a -comment with "]/". This only works for /* - */ comments. +comment with "]/". This only works for `/* - */` comments. > +-> +-> /* | [/ | * A comment about --+ @@ -446,10 +446,10 @@ You are editing a C program and wonder if a variable is declared as "int" or Vim will list the matching lines it can find. Not only in the current file, but also in all included files (and files included in them, etc.). The result -looks like this: +looks like this: > - structs.h ~ - 1: 29 unsigned column; /* column number */ ~ + structs.h + 1: 29 unsigned column; /* column number */ The advantage over using tags or the preview window is that included files are searched. In most cases this results in the right declaration to be found. diff --git a/runtime/doc/usr_30.txt b/runtime/doc/usr_30.txt index 7e7b3b21f4..a0d22482c3 100644 --- a/runtime/doc/usr_30.txt +++ b/runtime/doc/usr_30.txt @@ -517,12 +517,12 @@ The other way around works just as well: > One of the great things about Vim is that it understands comments. You can ask Vim to format a comment and it will do the right thing. - Suppose, for example, that you have the following comment: + Suppose, for example, that you have the following comment: >c - /* ~ - * This is a test ~ - * of the text formatting. ~ - */ ~ + /* + * This is a test + * of the text formatting. + */ You then ask Vim to format it by positioning the cursor at the start of the comment and type: > @@ -530,33 +530,33 @@ comment and type: > gq]/ "gq" is the operator to format text. "]/" is the motion that takes you to the -end of a comment. The result is: +end of a comment. The result is: >c - /* ~ - * This is a test of the text formatting. ~ - */ ~ + /* + * This is a test of the text formatting. + */ Notice that Vim properly handled the beginning of each line. An alternative is to select the text that is to be formatted in Visual mode and type "gq". To add a new line to the comment, position the cursor on the middle line and -press "o". The result looks like this: +press "o". The result looks like this: >c - /* ~ - * This is a test of the text formatting. ~ - * ~ - */ ~ + /* + * This is a test of the text formatting. + * + */ Vim has automatically inserted a star and a space for you. Now you can type the comment text. When it gets longer than 'textwidth', Vim will break the -line. Again, the star is inserted automatically: +line. Again, the star is inserted automatically: >c - /* ~ - * This is a test of the text formatting. ~ - * Typing a lot of text here will make Vim ~ - * break ~ - */ ~ + /* + * This is a test of the text formatting. + * Typing a lot of text here will make Vim + * break + */ For this to work some flags must be present in 'formatoptions': diff --git a/runtime/doc/usr_40.txt b/runtime/doc/usr_40.txt index 8befb15528..b0d53e0d8c 100644 --- a/runtime/doc/usr_40.txt +++ b/runtime/doc/usr_40.txt @@ -159,7 +159,7 @@ RECURSIVE MAPPING When a mapping triggers itself, it will run forever. This can be used to repeat an action an unlimited number of times. For example, you have a list of files that contain a version number in the -first line. You edit these files with "vim *.txt". You are now editing the +first line. You edit these files with `vim *.txt`. You are now editing the first file. Define this mapping: > :map ,, :s/5.1/5.2/<CR>:wnext<CR>,, @@ -501,7 +501,7 @@ See |autocmd-events| for a complete list of events. PATTERNS The {file-pattern} argument can actually be a comma-separated list of file -patterns. For example: "*.c,*.h" matches files ending in ".c" and ".h". +patterns. For example: `*.c,*.h` matches files ending in ".c" and ".h". The usual file wildcards can be used. Here is a summary of the most often used ones: @@ -622,7 +622,7 @@ Example: > :autocmd BufReadPost *.log normal G -This will make the cursor jump to the last line of *.log files when you start +This will make the cursor jump to the last line of `*.log` files when you start to edit it. Using the ":normal" command is a bit tricky. First of all, make sure its argument is a complete command, including all the arguments. When you use "i" diff --git a/runtime/doc/usr_41.txt b/runtime/doc/usr_41.txt index 9075d60b1b..243e8579e0 100644 --- a/runtime/doc/usr_41.txt +++ b/runtime/doc/usr_41.txt @@ -1333,7 +1333,7 @@ is a List with arguments. Function references are most useful in combination with a Dictionary, as is explained in the next section. -More information about defining your own functions here: |user-functions|. +More information about defining your own functions here: |user-function|. ============================================================================== *41.8* Lists and Dictionaries diff --git a/runtime/doc/usr_43.txt b/runtime/doc/usr_43.txt index 15c94cd15e..54eded5111 100644 --- a/runtime/doc/usr_43.txt +++ b/runtime/doc/usr_43.txt @@ -123,12 +123,12 @@ That file is found in 'runtimepath' first. Then use this in What will happen now is that Vim searches for "filetype.vim" files in each directory in 'runtimepath'. First ~/.config/nvim/filetype.vim is found. The -autocommand to catch *.txt files is defined there. Then Vim finds the +autocommand to catch `*.txt` files is defined there. Then Vim finds the filetype.vim file in $VIMRUNTIME, which is halfway 'runtimepath'. Finally ~/.config/nvim/after/filetype.vim is found and the autocommand for detecting ruby files in /usr/share/scripts is added. When you now edit /usr/share/scripts/README.txt, the autocommands are -checked in the order in which they were defined. The *.txt pattern matches, +checked in the order in which they were defined. The `*.txt` pattern matches, thus "setf text" is executed to set the filetype to "text". The pattern for ruby matches too, and the "setf ruby" is executed. But since 'filetype' was already set to "text", nothing happens here. diff --git a/runtime/doc/usr_45.txt b/runtime/doc/usr_45.txt index 95a2bc8f79..85710b15d5 100644 --- a/runtime/doc/usr_45.txt +++ b/runtime/doc/usr_45.txt @@ -190,8 +190,7 @@ font. Example: > xterm -u8 -fn -misc-fixed-medium-r-normal--18-120-100-100-c-90-iso10646-1 -Now you can run Vim inside this terminal. Set 'encoding' to "utf-8" as -before. That's all. +Now you can run Vim inside this terminal. USING UNICODE IN AN ORDINARY TERMINAL diff --git a/runtime/doc/various.txt b/runtime/doc/various.txt index 7b0e724e31..1b1dca321b 100644 --- a/runtime/doc/various.txt +++ b/runtime/doc/various.txt @@ -88,13 +88,10 @@ g8 Print the hex values of the bytes used in the *8g8* 8g8 Find an illegal UTF-8 byte sequence at or after the - cursor. This works in two situations: - 1. when 'encoding' is any 8-bit encoding - 2. when 'encoding' is "utf-8" and 'fileencoding' is - any 8-bit encoding - Thus it can be used when editing a file that was - supposed to be UTF-8 but was read as if it is an 8-bit - encoding because it contains illegal bytes. + cursor. + Can be used when editing a file that was supposed to + be UTF-8 but was read as if it is an 8-bit encoding + because it contains illegal bytes. Does not wrap around the end of the file. Note that when the cursor is on an illegal byte or the cursor is halfway through a multibyte character the @@ -174,8 +171,8 @@ g8 Print the hex values of the bytes used in the Like ":z" or ":z!", but number the lines. *:=* -:= Without arg: Print the last line number. - with args: equivalent to `:lua ={expr}`. see |:lua| +:= [args] Without [args]: prints the last line number. + With [args]: equivalent to `:lua ={expr}`. see |:lua| :{range}= Prints the last line number in {range}. For example, this prints the current line number: > diff --git a/runtime/doc/vi_diff.txt b/runtime/doc/vi_diff.txt index afabddb7f9..0a0cbc8ec6 100644 --- a/runtime/doc/vi_diff.txt +++ b/runtime/doc/vi_diff.txt @@ -11,9 +11,9 @@ Differences between Vim and Vi *vi-differences* ============================================================================== 1. Limits *limits* -Vim has only a few limits for the files that can be edited {Vi: can not handle +Vim has only a few limits for the files that can be edited. Vi cannot handle <Nul> characters and characters above 128, has limited line length, many other -limits}. +limits. Maximum line length 2147483647 characters Maximum number of lines 2147483647 lines @@ -44,7 +44,7 @@ kept in memory: Command-line history, error messages for Quickfix mode, etc. Support for different systems. Vim can be used on: - - Modern Unix systems (*BSD, Linux, etc.) + - Modern Unix systems (BSD, Linux, etc.) - Windows (XP SP 2 or greater) - OS X @@ -180,12 +180,12 @@ Command-line editing and history. |cmdline-editing| forward/backward one character. The shifted right/left cursor keys can be used to move forward/backward one word. CTRL-B/CTRL-E can be used to go to the begin/end of the command-line. - {Vi: can only alter the last character in the line} - {Vi: when hitting <Esc> the command-line is executed. This is + (Vi: can only alter the last character in the line) + (Vi: when hitting <Esc> the command-line is executed. This is unexpected for most people; therefore it was changed in Vim. But when the <Esc> is part of a mapping, the command-line is executed. If you want the Vi behaviour also when typing <Esc>, use ":cmap ^V<Esc> - ^V^M"} + ^V^M") |cmdline-history| The command-lines are remembered. The up/down cursor keys can be used to recall previous command-lines. The 'history' option can be set to diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt index 102f867fbd..686905537d 100644 --- a/runtime/doc/vim_diff.txt +++ b/runtime/doc/vim_diff.txt @@ -15,6 +15,9 @@ centralized reference of the differences. ============================================================================== Configuration *nvim-config* +User configuration and data files are found in standard |base-directories| +(see also |$NVIM_APPNAME|). Note in particular: + - Use `$XDG_CONFIG_HOME/nvim/init.vim` instead of `.vimrc` for your |config|. - Use `$XDG_CONFIG_HOME/nvim` instead of `.vim` to store configuration files. - Use `$XDG_STATE_HOME/nvim/shada/main.shada` instead of `.viminfo` for persistent @@ -29,7 +32,7 @@ Defaults *nvim-defaults* ":syntax off" to |init.vim|. - 'autoindent' is enabled -- 'autoread' is enabled +- 'autoread' is enabled (works in all UIs, including terminal) - 'background' defaults to "dark" (unless set automatically by the terminal/UI) - 'backspace' defaults to "indent,eol,start" - 'backupdir' defaults to .,~/.local/state/nvim/backup// (|xdg|), auto-created @@ -37,6 +40,7 @@ Defaults *nvim-defaults* - 'commentstring' defaults to "" - 'compatible' is always disabled - 'complete' excludes "i" +- 'define' defaults to "". The C ftplugin sets it to "^\\s*#\\s*define" - 'directory' defaults to ~/.local/state/nvim/swap// (|xdg|), auto-created - 'display' defaults to "lastline" - 'encoding' is UTF-8 (cf. 'fileencoding' for file-content encoding) @@ -46,6 +50,7 @@ Defaults *nvim-defaults* - 'hidden' is enabled - 'history' defaults to 10000 (the maximum) - 'hlsearch' is enabled +- 'include' defaults to "". The C ftplugin sets it to "^\\s*#\\s*include" - 'incsearch' is enabled - 'joinspaces' is disabled - 'langnoremap' is enabled @@ -55,6 +60,7 @@ Defaults *nvim-defaults* - 'mouse' defaults to "nvi" - 'mousemodel' defaults to "popup_setpos" - 'nrformats' defaults to "bin,hex" +- 'path' defaults to ".,,". The C ftplugin adds "/usr/include" if it exists. - 'ruler' is enabled - 'sessionoptions' includes "unix,slash", excludes "options" - 'shortmess' includes "CF", excludes "S" @@ -192,7 +198,7 @@ server. External plugins run in separate processes. |remote-plugin| This improves stability and allows those plugins to work without blocking the editor. Even "legacy" Python and Ruby plugins which use the old Vim interfaces (|if_pyth|, -|if_ruby|) run out-of-process. +|if_ruby|) run out-of-process, so they cannot crash Nvim. Platform and I/O facilities are built upon libuv. Nvim benefits from libuv features and bug fixes, and other projects benefit from improvements to libuv @@ -270,6 +276,7 @@ Normal commands: |Q| replays the last recorded macro instead of switching to Ex mode (|gQ|). Options: + 'autoread' works in the terminal (if it supports "focus" events) 'cpoptions' flags: |cpo-_| 'diffopt' "linematch" feature 'exrc' searches for ".nvim.lua", ".nvimrc", or ".exrc" files. The @@ -290,7 +297,6 @@ Options: 'shortmess' "F" flag does not affect output from autocommands 'signcolumn' supports up to 9 dynamic/fixed columns 'statuscolumn' full control of columns using 'statusline' format - 'statusline' supports unlimited alignment sections 'tabline' %@Func@foo%X can call any function on mouse-click 'ttimeout', 'ttimeoutlen' behavior was simplified 'winblend' pseudo-transparency in floating windows |api-floatwin| @@ -379,9 +385,9 @@ These Nvim features were later integrated into Vim. - 'statusline' supports unlimited alignment sections ============================================================================== -Changed features *nvim-changed* +Other changes *nvim-changed* -This section lists various low-level details about other behavior changes. +This section documents various low-level behavior changes. |mkdir()| behaviour changed: 1. Assuming /tmp/foo does not exist and /tmp can be written to @@ -396,21 +402,21 @@ This section lists various low-level details about other behavior changes. structures. 2. |string()| fails immediately on nested containers, not when recursion limit was exceeded. -2. When |:echo| encounters duplicate containers like >vim +3. When |:echo| encounters duplicate containers like >vim let l = [] echo [l, l] < it does not use "[...]" (was: "[[], [...]]", now: "[[], []]"). "..." is only used for recursive containers. -3. |:echo| printing nested containers adds "@level" after "..." designating +4. |:echo| printing nested containers adds "@level" after "..." designating the level at which recursive container was printed: |:echo-self-refer|. Same thing applies to |string()| (though it uses construct like "{E724@level}"), but this is not reliable because |string()| continues to error out. -4. Stringifyed infinite and NaN values now use |str2float()| and can be evaled +5. Stringifyed infinite and NaN values now use |str2float()| and can be evaled back. -5. (internal) Trying to print or stringify VAR_UNKNOWN in Vim results in +6. (internal) Trying to print or stringify VAR_UNKNOWN in Vim results in nothing, E908, in Nvim it is internal error. |json_decode()| behaviour changed: @@ -453,8 +459,9 @@ Lua interface (|lua.txt|): - `:lua print("a\0b")` will print `a^@b`, like with `:echomsg "a\nb"` . In Vim that prints `a` and `b` on separate lines, exactly like `:lua print("a\nb")` . -- `:lua error('TEST')` emits the error “E5105: Error while calling lua chunk: - [string "<VimL compiled string>"]:1: TEST”, whereas Vim emits only “TEST”. +- `:lua error('TEST')` emits the error: > + E5108: Error executing lua: [string "<Vimscript compiled string>"]:1: TEST +< whereas Vim emits only "TEST". - Lua has direct access to Nvim |API| via `vim.api`. - Lua package.path and package.cpath are automatically updated according to 'runtimepath': |lua-require|. @@ -558,7 +565,8 @@ Aliases: Commands: :behave :fixdel - :hardcopy + *hardcopy* `:hardcopy` was removed. Instead, use `:TOhtml` and print the + resulting HTML using a web browser or other HTML viewer. :helpfind :mode (no longer accepts an argument) :open @@ -580,6 +588,11 @@ Compile-time features: Emacs tags support X11 integration (see |x11-selection|) +Cscope: + *cscope* + Cscope support was removed in favour of plugin-based solutions such as: + https://github.com/dhananjaylatkar/cscope_maps.nvim + Eval: Vim9script *cscope_connection()* @@ -631,7 +644,10 @@ Options: 'highlight' (Names of builtin |highlight-groups| cannot be changed.) *'hkmap'* *'hk'* use `set keymap=hebrew` instead. *'hkmapp'* *'hkp'* use `set keymap=hebrewp` instead. - *'pastetoggle'* *'pt'* + + *'pastetoggle'* *'pt'* Just Paste It.™ |paste| is handled automatically when + you paste text using your terminal's or GUI's paste feature (CTRL-SHIFT-v, + CMD-v (macOS), middle-click, …). *'imactivatefunc'* *'imaf'* *'imactivatekey'* *'imak'* @@ -702,6 +718,12 @@ Options: Performance: Folds are not updated during insert-mode. +Plugins: + +- logiPat +- rrhelper +- *vimball* + Providers: - *if_lua* : Nvim |Lua| API is not compatible with Vim's "if_lua". @@ -757,17 +779,5 @@ TUI: at how the terminal is sending CSI. Nvim does not issue such a sequence and always uses 7-bit control sequences. -Cscope: - *cscope* - Cscope support has been removed in favour of LSP based solutions. - -Hardcopy: - *hardcopy* - `:hardcopy` was removed. Instead, use `:TOhtml` and print the resulting HTML - using a web browser or some other HTML viewer. - -Bundled plugins: - vimball *vimball* - ============================================================================== vim:tw=78:ts=8:sw=2:et:ft=help:norl: diff --git a/runtime/doc/windows.txt b/runtime/doc/windows.txt index 4b7901e614..b78c1eeae5 100644 --- a/runtime/doc/windows.txt +++ b/runtime/doc/windows.txt @@ -631,7 +631,7 @@ times than |WinResized|, it may slow down editing a bit. The information provided by |WinScrolled| is a dictionary for each window that has changes, using the window ID as the key, and a total count of the changes -with the key "all". Example value for |v:event|: +with the key "all". Example value for |v:event|: > { all: {width: 0, height: 2, leftcol: 0, skipcol: 0, topline: 1, topfill: 0}, 1003: {width: 0, height: -1, leftcol: 0, skipcol: 0, topline: 0, topfill: 0}, @@ -1090,7 +1090,7 @@ list of buffers. |unlisted-buffer| a an active buffer: it is loaded and visible h a hidden buffer: It is loaded, but currently not displayed in a window |hidden-buffer| - - a buffer with 'modifiable' off + `-` a buffer with 'modifiable' off = a readonly buffer R a terminal buffer with a running job F a terminal buffer with a finished job @@ -1101,7 +1101,7 @@ list of buffers. |unlisted-buffer| [flags] can be a combination of the following characters, which restrict the buffers to be listed: + modified buffers - - buffers with 'modifiable' off + `-` buffers with 'modifiable' off = readonly buffers a active buffers u unlisted buffers (overrides the "!") diff --git a/runtime/ftplugin.vim b/runtime/ftplugin.vim index d24c0a036e..f5c411fbe6 100644 --- a/runtime/ftplugin.vim +++ b/runtime/ftplugin.vim @@ -30,9 +30,10 @@ augroup filetypeplugin for name in split(s, '\.') " Load Lua ftplugins after Vim ftplugins _per directory_ " TODO(clason): use nvim__get_runtime when supports globs and modeline - exe printf('runtime! ftplugin/%s.vim ftplugin/%s.lua', name, name) - exe printf('runtime! ftplugin/%s_*.vim ftplugin/%s_*.lua', name, name) - exe printf('runtime! ftplugin/%s/*.vim ftplugin/%s/*.lua', name, name) + exe printf('runtime! ftplugin/%s.vim ftplugin/%s.lua + \ ftplugin/%s_*.vim ftplugin/%s_*.lua + \ ftplugin/%s/*.vim ftplugin/%s/*.lua', + \ name, name, name, name, name, name) endfor endif endfunc diff --git a/runtime/ftplugin/c.lua b/runtime/ftplugin/c.lua index b4e68148f5..0ddbf09470 100644 --- a/runtime/ftplugin/c.lua +++ b/runtime/ftplugin/c.lua @@ -1 +1,14 @@ +-- These are the default option values in Vim, but not in Nvim, so must be set explicitly. vim.bo.commentstring = '/*%s*/' +vim.bo.define = '^\\s*#\\s*define' +vim.bo.include = '^\\s*#\\s*include' + +if vim.fn.isdirectory('/usr/include') == 1 then + vim.cmd([[ + setlocal path^=/usr/include + setlocal path-=. + setlocal path^=. + ]]) +end + +vim.b.undo_ftplugin = vim.b.undo_ftplugin .. '|setl path<' diff --git a/runtime/ftplugin/checkhealth.vim b/runtime/ftplugin/checkhealth.vim index 62a1970b4a..4b530e6f7c 100644 --- a/runtime/ftplugin/checkhealth.vim +++ b/runtime/ftplugin/checkhealth.vim @@ -9,6 +9,9 @@ endif runtime! ftplugin/help.vim setlocal wrap breakindent linebreak +setlocal foldexpr=getline(v:lnum-1)=~'^=\\{78}$'?'>1':(getline(v:lnum)=~'^=\\{78}'?0:'=') +setlocal foldmethod=expr +setlocal foldtext=v:lua.require('vim.health').foldtext() let &l:iskeyword='!-~,^*,^|,^",192-255' if exists("b:undo_ftplugin") diff --git a/runtime/ftplugin/corn.vim b/runtime/ftplugin/corn.vim new file mode 100644 index 0000000000..2259442229 --- /dev/null +++ b/runtime/ftplugin/corn.vim @@ -0,0 +1,18 @@ +" Vim filetype plugin +" Language: Corn +" Original Author: Jake Stanger (mail@jstanger.dev) +" License: MIT +" Last Change: 2023 May 28 + +if exists('b:did_ftplugin') + finish +endif +let b:did_ftplugin = 1 + +setlocal formatoptions-=t + +" Set comment (formatting) related options. +setlocal commentstring=//\ %s comments=:// + +" Let Vim know how to disable the plug-in. +let b:undo_ftplugin = 'setlocal commentstring< comments< formatoptions<' diff --git a/runtime/ftplugin/fennel.vim b/runtime/ftplugin/fennel.vim index 2e502699c5..93cf366726 100644 --- a/runtime/ftplugin/fennel.vim +++ b/runtime/ftplugin/fennel.vim @@ -1,7 +1,7 @@ " Vim filetype plugin file " Language: Fennel " Maintainer: Gregory Anders <greg[NOSPAM]@gpanders.com> -" Last Update: 2022 Apr 20 +" Last Update: 2023 Jun 9 if exists('b:did_ftplugin') finish @@ -13,6 +13,6 @@ setlocal comments=:;;,:; setlocal formatoptions-=t setlocal suffixesadd=.fnl setlocal lisp -setlocal lispwords=accumulate,collect,do,doto,each,eval-compiler,fn,for,icollect,lambda,let,macro,macros,match,match-try,when,while,with-open +setlocal lispwords=accumulate,case,case-try,collect,do,doto,each,eval-compiler,faccumulate,fcollect,fn,for,icollect,lambda,let,macro,macros,match,match-try,when,while,with-open let b:undo_ftplugin = 'setlocal commentstring< comments< formatoptions< suffixesadd< lisp< lispwords<' diff --git a/runtime/ftplugin/urlshortcut.vim b/runtime/ftplugin/urlshortcut.vim new file mode 100644 index 0000000000..ebe08ac1d8 --- /dev/null +++ b/runtime/ftplugin/urlshortcut.vim @@ -0,0 +1,20 @@ +" Vim filetype plugin file +" Language: MS Windows URL shortcut file +" Maintainer: ObserverOfTime <chronobserver@disroot.org> +" Latest Revision: 2023-06-04 + +if exists("b:did_ftplugin") + finish +endif +let b:did_ftplugin = 1 + +let s:cpo_save = &cpoptions +set cpoptions&vim + +let b:undo_ftplugin = "setl com< cms< fo<" + +setlocal comments=:; commentstring=;\ %s +setlocal formatoptions-=t formatoptions+=croql + +let &cpoptions = s:cpo_save +unlet s:cpo_save diff --git a/runtime/lua/man.lua b/runtime/lua/man.lua index 09265b1999..f2bd79aca8 100644 --- a/runtime/lua/man.lua +++ b/runtime/lua/man.lua @@ -14,79 +14,19 @@ local function man_error(msg) end -- Run a system command and timeout after 30 seconds. ----@param cmd_ string[] +---@param cmd string[] ---@param silent boolean? ----@param env string[] +---@param env? table<string,string|number> ---@return string -local function system(cmd_, silent, env) - local stdout_data = {} ---@type string[] - local stderr_data = {} ---@type string[] - local stdout = assert(vim.uv.new_pipe(false)) - local stderr = assert(vim.uv.new_pipe(false)) +local function system(cmd, silent, env) + local r = vim.system(cmd, { env = env, timeout = 10000 }):wait() - local done = false - local exit_code ---@type integer? - - -- We use the `env` command here rather than the env option to vim.uv.spawn since spawn will - -- completely overwrite the environment when we just want to modify the existing one. - -- - -- Overwriting mainly causes problems NixOS which relies heavily on a non-standard environment. - local cmd = cmd_ - if env then - cmd = { 'env' } - vim.list_extend(cmd, env) - vim.list_extend(cmd, cmd_) - end - - local handle - handle = vim.uv.spawn(cmd[1], { - args = vim.list_slice(cmd, 2), - stdio = { nil, stdout, stderr }, - }, function(code) - exit_code = code - stdout:close() - stderr:close() - handle:close() - done = true - end) - - if handle then - stdout:read_start(function(_, data) - stdout_data[#stdout_data + 1] = data - end) - stderr:read_start(function(_, data) - stderr_data[#stderr_data + 1] = data - end) - else - stdout:close() - stderr:close() - if not silent then - local cmd_str = table.concat(cmd, ' ') - man_error(string.format('command error: %s', cmd_str)) - end - return '' - end - - vim.wait(30000, function() - return done - end) - - if not done then - if handle then - handle:close() - stdout:close() - stderr:close() - end - local cmd_str = table.concat(cmd, ' ') - man_error(string.format('command timed out: %s', cmd_str)) - end - - if exit_code ~= 0 and not silent then + if r.code ~= 0 and not silent then local cmd_str = table.concat(cmd, ' ') - man_error(string.format("command error '%s': %s", cmd_str, table.concat(stderr_data))) + man_error(string.format("command error '%s': %s", cmd_str, r.stderr)) end - return table.concat(stdout_data) + return assert(r.stdout) end ---@param line string @@ -312,7 +252,7 @@ local function get_path(sect, name, silent) end local lines = system(cmd, silent) - local results = vim.split(lines or {}, '\n', { trimempty = true }) + local results = vim.split(lines, '\n', { trimempty = true }) if #results == 0 then return @@ -496,7 +436,7 @@ local function get_page(path, silent) elseif vim.env.MANWIDTH then manwidth = vim.env.MANWIDTH else - manwidth = api.nvim_win_get_width(0) + manwidth = api.nvim_win_get_width(0) - vim.o.wrapmargin end local cmd = localfile_arg and { 'man', '-l', path } or { 'man', path } @@ -505,9 +445,9 @@ local function get_page(path, silent) -- http://comments.gmane.org/gmane.editors.vim.devel/29085 -- Set MAN_KEEP_FORMATTING so Debian man doesn't discard backspaces. return system(cmd, silent, { - 'MANPAGER=cat', - 'MANWIDTH=' .. manwidth, - 'MAN_KEEP_FORMATTING=1', + MANPAGER = 'cat', + MANWIDTH = manwidth, + MAN_KEEP_FORMATTING = 1, }) end diff --git a/runtime/lua/vim/_editor.lua b/runtime/lua/vim/_editor.lua index 7b946a55e4..ab20c36b17 100644 --- a/runtime/lua/vim/_editor.lua +++ b/runtime/lua/vim/_editor.lua @@ -3,7 +3,7 @@ -- Lua code lives in one of three places: -- 1. runtime/lua/vim/ (the runtime): For "nice to have" features, e.g. the -- `inspect` and `lpeg` modules. --- 2. runtime/lua/vim/shared.lua: pure lua functions which always +-- 2. runtime/lua/vim/shared.lua: pure Lua functions which always -- are available. Used in the test runner, as well as worker threads -- and processes launched from Nvim. -- 3. runtime/lua/vim/_editor.lua: Code which directly interacts with @@ -42,10 +42,6 @@ for k, v in pairs({ vim._submodules[k] = v end --- Remove at Nvim 1.0 ----@deprecated -vim.loop = vim.uv - -- There are things which have special rules in vim._init_packages -- for legacy reasons (uri) or for performance (_inspector). -- most new things should go into a submodule namespace ( vim.foobar.do_thing() ) @@ -69,13 +65,73 @@ vim.log = { }, } --- Internal-only until comments in #8107 are addressed. --- Returns: --- {errcode}, {output} -function vim._system(cmd) - local out = vim.fn.system(cmd) - local err = vim.v.shell_error - return err, out +-- TODO(lewis6991): document that the signature is system({cmd}, [{opts},] {on_exit}) +--- Run a system command +--- +--- Examples: +--- <pre>lua +--- +--- local on_exit = function(obj) +--- print(obj.code) +--- print(obj.signal) +--- print(obj.stdout) +--- print(obj.stderr) +--- end +--- +--- -- Run asynchronously +--- vim.system({'echo', 'hello'}, { text = true }, on_exit) +--- +--- -- Run synchronously +--- local obj = vim.system({'echo', 'hello'}, { text = true }):wait() +--- -- { code = 0, signal = 0, stdout = 'hello', stderr = '' } +--- +--- </pre> +--- +--- See |uv.spawn()| for more details. +--- +--- @param cmd (string[]) Command to execute +--- @param opts (SystemOpts|nil) Options: +--- - cwd: (string) Set the current working directory for the sub-process. +--- - env: table<string,string> Set environment variables for the new process. Inherits the +--- current environment with `NVIM` set to |v:servername|. +--- - clear_env: (boolean) `env` defines the job environment exactly, instead of merging current +--- environment. +--- - stdin: (string|string[]|boolean) If `true`, then a pipe to stdin is opened and can be written +--- to via the `write()` method to SystemObj. If string or string[] then will be written to stdin +--- and closed. Defaults to `false`. +--- - stdout: (boolean|function) +--- Handle output from stdout. When passed as a function must have the signature `fun(err: string, data: string)`. +--- Defaults to `true` +--- - stderr: (boolean|function) +--- Handle output from stdout. When passed as a function must have the signature `fun(err: string, data: string)`. +--- Defaults to `true`. +--- - text: (boolean) Handle stdout and stderr as text. Replaces `\r\n` with `\n`. +--- - timeout: (integer) +--- - detach: (boolean) If true, spawn the child process in a detached state - this will make it +--- a process group leader, and will effectively enable the child to keep running after the +--- parent exits. Note that the child process will still keep the parent's event loop alive +--- unless the parent process calls |uv.unref()| on the child's process handle. +--- +--- @param on_exit (function|nil) Called when subprocess exits. When provided, the command runs +--- asynchronously. Receives SystemCompleted object, see return of SystemObj:wait(). +--- +--- @return SystemObj Object with the fields: +--- - pid (integer) Process ID +--- - wait (fun(timeout: integer|nil): SystemCompleted) +--- - SystemCompleted is an object with the fields: +--- - code: (integer) +--- - signal: (integer) +--- - stdout: (string), nil if stdout argument is passed +--- - stderr: (string), nil if stderr argument is passed +--- - kill (fun(signal: integer)) +--- - write (fun(data: string|nil)) Requires `stdin=true`. Pass `nil` to close the stream. +--- - is_closing (fun(): boolean) +function vim.system(cmd, opts, on_exit) + if type(opts) == 'function' then + on_exit = opts + opts = nil + end + return require('vim._system').run(cmd, opts, on_exit) end -- Gets process info from the `ps` command. @@ -85,13 +141,14 @@ function vim._os_proc_info(pid) error('invalid pid') end local cmd = { 'ps', '-p', pid, '-o', 'comm=' } - local err, name = vim._system(cmd) - if 1 == err and vim.trim(name) == '' then + local r = vim.system(cmd):wait() + local name = assert(r.stdout) + if r.code == 1 and vim.trim(name) == '' then return {} -- Process not found. - elseif 0 ~= err then + elseif r.code ~= 0 then error('command failed: ' .. vim.fn.string(cmd)) end - local _, ppid = vim._system({ 'ps', '-p', pid, '-o', 'ppid=' }) + local ppid = assert(vim.system({ 'ps', '-p', pid, '-o', 'ppid=' }):wait().stdout) -- Remove trailing whitespace. name = vim.trim(name):gsub('^.*/', '') ppid = tonumber(ppid) or -1 @@ -109,14 +166,14 @@ function vim._os_proc_children(ppid) error('invalid ppid') end local cmd = { 'pgrep', '-P', ppid } - local err, rv = vim._system(cmd) - if 1 == err and vim.trim(rv) == '' then + local r = vim.system(cmd):wait() + if r.code == 1 and vim.trim(r.stdout) == '' then return {} -- Process not found. - elseif 0 ~= err then + elseif r.code ~= 0 then error('command failed: ' .. vim.fn.string(cmd)) end local children = {} - for s in rv:gmatch('%S+') do + for s in r.stdout:gmatch('%S+') do local i = tonumber(s) if i ~= nil then table.insert(children, i) @@ -127,6 +184,7 @@ end --- Gets a human-readable representation of the given object. --- +---@see |vim.print()| ---@see https://github.com/kikito/inspect.lua ---@see https://github.com/mpeterv/vinspect local function inspect(object, options) -- luacheck: no unused @@ -477,9 +535,9 @@ function vim.region(bufnr, pos1, pos2, regtype, inclusive) return region end ---- Defers calling `fn` until `timeout` ms passes. +--- Defers calling {fn} until {timeout} ms passes. --- ---- Use to do a one-shot timer that calls `fn` +--- Use to do a one-shot timer that calls {fn} --- Note: The {fn} is |vim.schedule_wrap()|ped automatically, so API functions are --- safe to call. ---@param fn function Callback to call once `timeout` expires @@ -782,10 +840,10 @@ do -- some bugs, so fake the two-step dance for now. local matches - --- Omnifunc for completing lua values from the runtime lua interpreter, + --- Omnifunc for completing Lua values from the runtime Lua interpreter, --- similar to the builtin completion for the `:lua` command. --- - --- Activate using `set omnifunc=v:lua.vim.lua_omnifunc` in a lua buffer. + --- Activate using `set omnifunc=v:lua.vim.lua_omnifunc` in a Lua buffer. function vim.lua_omnifunc(find_start, _) if find_start == 1 then local line = vim.api.nvim_get_current_line() @@ -813,6 +871,7 @@ end --- </pre> --- --- @see |vim.inspect()| +--- @see |:=| --- @return any # given arguments. function vim.print(...) if vim.in_fast_event() then @@ -875,7 +934,7 @@ function vim._cs_remote(rcid, server_addr, connect_error, args) or subcmd == 'tab-wait' or subcmd == 'tab-wait-silent' then - return { errmsg = 'E5600: Wait commands not yet implemented in nvim' } + return { errmsg = 'E5600: Wait commands not yet implemented in Nvim' } elseif subcmd == 'tab-silent' then f_tab = true f_silent = true @@ -883,14 +942,14 @@ function vim._cs_remote(rcid, server_addr, connect_error, args) if rcid == 0 then return { errmsg = connection_failure_errmsg('Send failed.') } end - vim.fn.rpcrequest(rcid, 'nvim_input', args[2]) + vim.rpcrequest(rcid, 'nvim_input', args[2]) return { should_exit = true, tabbed = false } elseif subcmd == 'expr' then if rcid == 0 then return { errmsg = connection_failure_errmsg('Send expression failed.') } end - print(vim.fn.rpcrequest(rcid, 'nvim_eval', args[2])) - return { should_exit = true, tabbed = false } + local res = tostring(vim.rpcrequest(rcid, 'nvim_eval', args[2])) + return { result = res, should_exit = true, tabbed = false } elseif subcmd ~= '' then return { errmsg = 'Unknown option argument: ' .. args[1] } end @@ -1006,4 +1065,8 @@ end require('vim._meta') +-- Remove at Nvim 1.0 +---@deprecated +vim.loop = vim.uv + return vim diff --git a/runtime/lua/vim/_meta.lua b/runtime/lua/vim/_meta.lua index e3ad4d76c9..913f1fe203 100644 --- a/runtime/lua/vim/_meta.lua +++ b/runtime/lua/vim/_meta.lua @@ -239,7 +239,7 @@ local to_vim_value = { end, } ---- Convert a lua value to a vimoption_T value +--- Convert a Lua value to a vimoption_T value local function convert_value_to_vim(name, info, value) if value == nil then return vim.NIL @@ -250,7 +250,7 @@ local function convert_value_to_vim(name, info, value) return to_vim_value[info.metatype](info, value) end --- Map of OptionType to functions that take vimoption_T values and convert to lua values. +-- Map of OptionType to functions that take vimoption_T values and convert to Lua values. -- Each function takes (info, vim_value) -> lua_value local to_lua_value = { boolean = passthrough, diff --git a/runtime/lua/vim/_system.lua b/runtime/lua/vim/_system.lua new file mode 100644 index 0000000000..eadf801a31 --- /dev/null +++ b/runtime/lua/vim/_system.lua @@ -0,0 +1,342 @@ +local uv = vim.uv + +--- @class SystemOpts +--- @field cmd string[] +--- @field stdin string|string[]|true +--- @field stdout fun(err:string, data: string)|false +--- @field stderr fun(err:string, data: string)|false +--- @field cwd? string +--- @field env? table<string,string|number> +--- @field clear_env? boolean +--- @field text boolean? +--- @field timeout? integer Timeout in ms +--- @field detach? boolean + +--- @class SystemCompleted +--- @field code integer +--- @field signal integer +--- @field stdout? string +--- @field stderr? string + +--- @class SystemState +--- @field handle uv_process_t +--- @field timer uv_timer_t +--- @field pid integer +--- @field timeout? integer +--- @field done boolean +--- @field stdin uv_stream_t? +--- @field stdout uv_stream_t? +--- @field stderr uv_stream_t? +--- @field cmd string[] +--- @field result? SystemCompleted + +---@private +---@param state SystemState +local function close_handles(state) + for _, handle in pairs({ state.handle, state.stdin, state.stdout, state.stderr }) do + if not handle:is_closing() then + handle:close() + end + end +end + +--- @param cmd string[] +--- @return SystemCompleted +local function timeout_result(cmd) + local cmd_str = table.concat(cmd, ' ') + local err = string.format("Command timed out: '%s'", cmd_str) + return { code = 0, signal = 2, stdout = '', stderr = err } +end + +--- @class SystemObj +--- @field pid integer +--- @field private _state SystemState +--- @field wait fun(self: SystemObj, timeout?: integer): SystemCompleted +--- @field kill fun(self: SystemObj, signal: integer) +--- @field write fun(self: SystemObj, data?: string|string[]) +--- @field is_closing fun(self: SystemObj): boolean? +local SystemObj = {} + +--- @param state SystemState +--- @return SystemObj +local function new_systemobj(state) + return setmetatable({ + pid = state.pid, + _state = state, + }, { __index = SystemObj }) +end + +--- @param signal integer +function SystemObj:kill(signal) + local state = self._state + state.handle:kill(signal) + close_handles(state) +end + +local MAX_TIMEOUT = 2 ^ 31 + +--- @param timeout? integer +--- @return SystemCompleted +function SystemObj:wait(timeout) + local state = self._state + + vim.wait(timeout or state.timeout or MAX_TIMEOUT, function() + return state.done + end) + + if not state.done then + self:kill(6) -- 'sigint' + state.result = timeout_result(state.cmd) + end + + return state.result +end + +--- @param data string[]|string|nil +function SystemObj:write(data) + local stdin = self._state.stdin + + if not stdin then + error('stdin has not been opened on this object') + end + + if type(data) == 'table' then + for _, v in ipairs(data) do + stdin:write(v) + stdin:write('\n') + end + elseif type(data) == 'string' then + stdin:write(data) + elseif data == nil then + -- Shutdown the write side of the duplex stream and then close the pipe. + -- Note shutdown will wait for all the pending write requests to complete + -- TODO(lewis6991): apparently shutdown doesn't behave this way. + -- (https://github.com/neovim/neovim/pull/17620#discussion_r820775616) + stdin:write('', function() + stdin:shutdown(function() + if stdin then + stdin:close() + end + end) + end) + end +end + +--- @return boolean +function SystemObj:is_closing() + local handle = self._state.handle + return handle == nil or handle:is_closing() +end + +---@private +---@param output function|'false' +---@return uv_stream_t? +---@return function? Handler +local function setup_output(output) + if output == nil then + return assert(uv.new_pipe(false)), nil + end + + if type(output) == 'function' then + return assert(uv.new_pipe(false)), output + end + + assert(output == false) + return nil, nil +end + +---@private +---@param input string|string[]|true|nil +---@return uv_stream_t? +---@return string|string[]? +local function setup_input(input) + if not input then + return + end + + local towrite --- @type string|string[]? + if type(input) == 'string' or type(input) == 'table' then + towrite = input + end + + return assert(uv.new_pipe(false)), towrite +end + +--- @return table<string,string> +local function base_env() + local env = vim.fn.environ() + env['NVIM'] = vim.v.servername + env['NVIM_LISTEN_ADDRESS'] = nil + return env +end + +--- uv.spawn will completely overwrite the environment +--- when we just want to modify the existing one, so +--- make sure to prepopulate it with the current env. +--- @param env? table<string,string|number> +--- @param clear_env? boolean +--- @return string[]? +local function setup_env(env, clear_env) + if clear_env then + return env + end + + --- @type table<string,string|number> + env = vim.tbl_extend('force', base_env(), env or {}) + + local renv = {} --- @type string[] + for k, v in pairs(env) do + renv[#renv + 1] = string.format('%s=%s', k, tostring(v)) + end + + return renv +end + +--- @param stream uv_stream_t +--- @param text? boolean +--- @param bucket string[] +--- @return fun(err: string?, data: string?) +local function default_handler(stream, text, bucket) + return function(err, data) + if err then + error(err) + end + if data ~= nil then + if text then + bucket[#bucket + 1] = data:gsub('\r\n', '\n') + else + bucket[#bucket + 1] = data + end + else + stream:read_stop() + stream:close() + end + end +end + +local M = {} + +--- @param cmd string +--- @param opts uv.aliases.spawn_options +--- @param on_exit fun(code: integer, signal: integer) +--- @param on_error fun() +--- @return uv_process_t, integer +local function spawn(cmd, opts, on_exit, on_error) + local handle, pid_or_err = uv.spawn(cmd, opts, on_exit) + if not handle then + on_error() + error(pid_or_err) + end + return handle, pid_or_err --[[@as integer]] +end + +--- Run a system command +--- +--- @param cmd string[] +--- @param opts? SystemOpts +--- @param on_exit? fun(out: SystemCompleted) +--- @return SystemObj +function M.run(cmd, opts, on_exit) + vim.validate({ + cmd = { cmd, 'table' }, + opts = { opts, 'table', true }, + on_exit = { on_exit, 'function', true }, + }) + + opts = opts or {} + + local stdout, stdout_handler = setup_output(opts.stdout) + local stderr, stderr_handler = setup_output(opts.stderr) + local stdin, towrite = setup_input(opts.stdin) + + --- @type SystemState + local state = { + done = false, + cmd = cmd, + timeout = opts.timeout, + stdin = stdin, + stdout = stdout, + stderr = stderr, + } + + -- Define data buckets as tables and concatenate the elements at the end as + -- one operation. + --- @type string[], string[] + local stdout_data, stderr_data + + state.handle, state.pid = spawn(cmd[1], { + args = vim.list_slice(cmd, 2), + stdio = { stdin, stdout, stderr }, + cwd = opts.cwd, + env = setup_env(opts.env, opts.clear_env), + detached = opts.detach, + hide = true, + }, function(code, signal) + close_handles(state) + if state.timer then + state.timer:stop() + state.timer:close() + end + + local check = assert(uv.new_check()) + + check:start(function() + for _, pipe in pairs({ state.stdin, state.stdout, state.stderr }) do + if not pipe:is_closing() then + return + end + end + check:stop() + + state.done = true + state.result = { + code = code, + signal = signal, + stdout = stdout_data and table.concat(stdout_data) or nil, + stderr = stderr_data and table.concat(stderr_data) or nil, + } + + if on_exit then + on_exit(state.result) + end + end) + end, function() + close_handles(state) + end) + + if stdout then + stdout_data = {} + stdout:read_start(stdout_handler or default_handler(stdout, opts.text, stdout_data)) + end + + if stderr then + stderr_data = {} + stderr:read_start(stderr_handler or default_handler(stderr, opts.text, stderr_data)) + end + + local obj = new_systemobj(state) + + if towrite then + obj:write(towrite) + obj:write(nil) -- close the stream + end + + if opts.timeout then + state.timer = assert(uv.new_timer()) + state.timer:start(opts.timeout, 0, function() + state.timer:stop() + state.timer:close() + if state.handle and state.handle:is_active() then + obj:kill(6) --- 'sigint' + state.result = timeout_result(state.cmd) + if on_exit then + on_exit(state.result) + end + end + end) + end + + return obj +end + +return M diff --git a/runtime/lua/vim/_watch.lua b/runtime/lua/vim/_watch.lua index 3bd8a56f6e..d489cef9fc 100644 --- a/runtime/lua/vim/_watch.lua +++ b/runtime/lua/vim/_watch.lua @@ -11,6 +11,7 @@ M.FileChangeType = vim.tbl_add_reverse_lookup({ --- Joins filepath elements by static '/' separator --- ---@param ... (string) The path elements. +---@return string local function filepath_join(...) return table.concat({ ... }, '/') end @@ -36,7 +37,7 @@ end --- - uvflags (table|nil) --- Same flags as accepted by |uv.fs_event_start()| ---@param callback (function) The function called when new events ----@returns (function) A function to stop the watch +---@return (function) A function to stop the watch function M.watch(path, opts, callback) vim.validate({ path = { path, 'string', false }, @@ -75,10 +76,25 @@ end local default_poll_interval_ms = 2000 +--- @class watch.Watches +--- @field is_dir boolean +--- @field children? table<string,watch.Watches> +--- @field cancel? fun() +--- @field started? boolean +--- @field handle? uv_fs_poll_t + +--- @class watch.PollOpts +--- @field interval? integer +--- @field include_pattern? userdata +--- @field exclude_pattern? userdata + ---@private --- Implementation for poll, hiding internally-used parameters. --- ----@param watches (table|nil) A tree structure to maintain state for recursive watches. +---@param path string +---@param opts watch.PollOpts +---@param callback fun(patch: string, filechangetype: integer) +---@param watches (watch.Watches|nil) A tree structure to maintain state for recursive watches. --- - handle (uv_fs_poll_t) --- The libuv handle --- - cancel (function) @@ -88,15 +104,36 @@ local default_poll_interval_ms = 2000 --- be invoked recursively) --- - children (table|nil) --- A mapping of directory entry name to its recursive watches --- - started (boolean|nil) --- Whether or not the watcher has first been initialized. Used --- to prevent a flood of Created events on startup. +--- - started (boolean|nil) +--- Whether or not the watcher has first been initialized. Used +--- to prevent a flood of Created events on startup. +---@return fun() Cancel function local function poll_internal(path, opts, callback, watches) path = vim.fs.normalize(path) local interval = opts and opts.interval or default_poll_interval_ms watches = watches or { is_dir = true, } + watches.cancel = function() + if watches.children then + for _, w in pairs(watches.children) do + w.cancel() + end + end + if watches.handle then + stop(watches.handle) + end + end + + local function incl_match() + return not opts.include_pattern or opts.include_pattern:match(path) ~= nil + end + local function excl_match() + return opts.exclude_pattern and opts.exclude_pattern:match(path) ~= nil + end + if not watches.is_dir and not incl_match() or excl_match() then + return watches.cancel + end if not watches.handle then local poll, new_err = vim.uv.new_fs_poll() @@ -120,18 +157,9 @@ local function poll_internal(path, opts, callback, watches) end end - watches.cancel = function() - if watches.children then - for _, w in pairs(watches.children) do - w.cancel() - end - end - stop(watches.handle) - end - if watches.is_dir then watches.children = watches.children or {} - local exists = {} + local exists = {} --- @type table<string,true> for name, ftype in vim.fs.dir(path) do exists[name] = true if not watches.children[name] then @@ -143,14 +171,16 @@ local function poll_internal(path, opts, callback, watches) end end - local newchildren = {} + local newchildren = {} ---@type table<string,watch.Watches> for name, watch in pairs(watches.children) do if exists[name] then newchildren[name] = watch else watch.cancel() watches.children[name] = nil - callback(path .. '/' .. name, M.FileChangeType.Deleted) + if watch.handle then + callback(path .. '/' .. name, M.FileChangeType.Deleted) + end end end watches.children = newchildren @@ -168,6 +198,15 @@ end ---@param opts (table|nil) Additional options --- - interval (number|nil) --- Polling interval in ms as passed to |uv.fs_poll_start()|. Defaults to 2000. +--- - include_pattern (LPeg pattern|nil) +--- An |lpeg| pattern. Only changes to files whose full paths match the pattern +--- will be reported. Only matches against non-directoriess, all directories will +--- be watched for new potentially-matching files. exclude_pattern can be used to +--- filter out directories. When nil, matches any file name. +--- - exclude_pattern (LPeg pattern|nil) +--- An |lpeg| pattern. Only changes to files and directories whose full path does +--- not match the pattern will be reported. Matches against both files and +--- directories. When nil, matches nothing. ---@param callback (function) The function called when new events ---@returns (function) A function to stop the watch. function M.poll(path, opts, callback) diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua index 0d1d01b391..093bfb631e 100644 --- a/runtime/lua/vim/diagnostic.lua +++ b/runtime/lua/vim/diagnostic.lua @@ -767,7 +767,7 @@ end --- - namespace: (number) Limit diagnostics to the given namespace. --- - lnum: (number) Limit diagnostics to the given line number. --- - severity: See |diagnostic-severity|. ----@return Diagnostic[] table A list of diagnostic items |diagnostic-structure|. +---@return Diagnostic[] table A list of diagnostic items |diagnostic-structure|. Keys `bufnr`, `end_lnum`, `end_col`, and `severity` are guaranteed to be present. function M.get(bufnr, opts) vim.validate({ bufnr = { bufnr, 'n', true }, diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index 6990de3391..bc880ed130 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -783,6 +783,10 @@ local extension = { g = 'pccts', pcmk = 'pcmk', pdf = 'pdf', + pem = 'pem', + cer = 'pem', + crt = 'pem', + csr = 'pem', plx = 'perl', prisma = 'prisma', psgi = 'perl', @@ -1033,6 +1037,8 @@ local extension = { swift = 'swift', svh = 'systemverilog', sv = 'systemverilog', + cmm = 'trace32', + t32 = 'trace32', td = 'tablegen', tak = 'tak', tal = 'tal', @@ -1082,6 +1088,7 @@ local extension = { uit = 'uil', uil = 'uil', ungram = 'ungrammar', + url = 'urlshortcut', usd = 'usd', usda = 'usd', sba = 'vb', @@ -1171,6 +1178,7 @@ local extension = { zir = 'zir', zu = 'zimbu', zut = 'zimbutempl', + zs = 'zserio', zsh = 'zsh', vala = 'vala', web = function(path, bufnr) @@ -1368,6 +1376,7 @@ local filename = { ['named.root'] = 'bindzone', WORKSPACE = 'bzl', ['WORKSPACE.bzlmod'] = 'bzl', + BUCK = 'bzl', BUILD = 'bzl', ['cabal.project'] = 'cabalproject', ['cabal.config'] = 'cabalconfig', @@ -1804,6 +1813,8 @@ local pattern = { ['bzr_log%..*'] = 'bzr', ['.*enlightenment/.*%.cfg'] = 'c', ['${HOME}/cabal%.config'] = 'cabalconfig', + ['${HOME}/%.config/cabal/config'] = 'cabalconfig', + ['${XDG_CONFIG_HOME}/cabal/config'] = 'cabalconfig', ['cabal%.project%..*'] = starsetf('cabalproject'), ['.*/%.calendar/.*'] = starsetf('calendar'), ['.*/share/calendar/.*/calendar%..*'] = starsetf('calendar'), @@ -2185,7 +2196,7 @@ local pattern = { ['.*/etc/profile'] = function(path, bufnr) return require('vim.filetype.detect').sh(path, M.getlines(bufnr)) end, - ['bash%-fc[%-%.]'] = function(path, bufnr) + ['bash%-fc[%-%.].*'] = function(path, bufnr) return require('vim.filetype.detect').sh(path, M.getlines(bufnr), 'bash') end, ['%.tcshrc.*'] = function(path, bufnr) @@ -2485,7 +2496,7 @@ local function match_pattern(name, path, tail, pat) return_early = true return nil end - return vim.env[env] + return vim.pesc(vim.env[env]) end) if return_early then return false diff --git a/runtime/lua/vim/filetype/detect.lua b/runtime/lua/vim/filetype/detect.lua index b347215895..4e18ed190a 100644 --- a/runtime/lua/vim/filetype/detect.lua +++ b/runtime/lua/vim/filetype/detect.lua @@ -1459,6 +1459,7 @@ local patterns_hashbang = { ['gforth\\>'] = { 'forth', { vim_regex = true } }, ['icon\\>'] = { 'icon', { vim_regex = true } }, guile = 'scheme', + ['nix%-shell'] = 'nix', } ---@private @@ -1491,7 +1492,7 @@ local function match_from_hashbang(contents, path, dispatch_extension) elseif matchregex(first_line, [[^#!\s*[^/\\ ]*\>\([^/\\]\|$\)]]) then name = vim.fn.substitute(first_line, [[^#!\s*\([^/\\ ]*\>\).*]], '\\1', '') else - name = vim.fn.substitute(first_line, [[^#!\s*\S*[/\\]\(\i\+\).*]], '\\1', '') + name = vim.fn.substitute(first_line, [[^#!\s*\S*[/\\]\(\f\+\).*]], '\\1', '') end -- tcl scripts may have #!/bin/sh in the first line and "exec wish" in the diff --git a/runtime/lua/vim/fs.lua b/runtime/lua/vim/fs.lua index 5e63fb6237..cdb314fa3d 100644 --- a/runtime/lua/vim/fs.lua +++ b/runtime/lua/vim/fs.lua @@ -72,7 +72,7 @@ function M.basename(file) return file:match('[/\\]$') and '' or (file:match('[^\\/]*$'):gsub('\\', '/')) end ---- Concatenate directories and/or file into a single path with normalization +--- Concatenate directories and/or file paths into a single path with normalization --- (e.g., `"foo/"` and `"bar"` get joined to `"foo/bar"`) --- ---@param ... string @@ -348,9 +348,7 @@ function M.normalize(path, opts) path = path:gsub('%$([%w_]+)', vim.uv.os_getenv) end - path = path:gsub('\\', '/'):gsub('/+', '/') - - return path:sub(-1) == '/' and path:sub(1, -2) or path + return (path:gsub('\\', '/'):gsub('/+', '/'):gsub('(.)/$', '%1')) end return M diff --git a/runtime/lua/vim/health.lua b/runtime/lua/vim/health.lua index ff338b95ea..6e47a22d03 100644 --- a/runtime/lua/vim/health.lua +++ b/runtime/lua/vim/health.lua @@ -2,6 +2,40 @@ local M = {} local s_output = {} +-- Returns the fold text of the current healthcheck section +function M.foldtext() + local foldtext = vim.fn.foldtext() + + if vim.bo.filetype ~= 'checkhealth' then + return foldtext + end + + if vim.b.failedchecks == nil then + vim.b.failedchecks = vim.empty_dict() + end + + if vim.b.failedchecks[foldtext] == nil then + local warning = '- WARNING ' + local warninglen = string.len(warning) + local err = '- ERROR ' + local errlen = string.len(err) + local failedchecks = vim.b.failedchecks + failedchecks[foldtext] = false + + local foldcontent = vim.api.nvim_buf_get_lines(0, vim.v.foldstart - 1, vim.v.foldend, false) + for _, line in ipairs(foldcontent) do + if string.sub(line, 1, warninglen) == warning or string.sub(line, 1, errlen) == err then + failedchecks[foldtext] = true + break + end + end + + vim.b.failedchecks = failedchecks + end + + return vim.b.failedchecks[foldtext] and '+WE' .. foldtext:sub(4) or foldtext +end + -- From a path return a list [{name}, {func}, {type}] representing a healthcheck local function filepath_to_healthcheck(path) path = vim.fs.normalize(path) diff --git a/runtime/lua/vim/iter.lua b/runtime/lua/vim/iter.lua index 0e98d0437e..245a33625e 100644 --- a/runtime/lua/vim/iter.lua +++ b/runtime/lua/vim/iter.lua @@ -1,7 +1,8 @@ ---@defgroup lua-iter --- ---- The \*vim.iter\* module provides a generic "iterator" interface over tables ---- and iterator functions. +--- @brief The \*vim.iter\* module provides a generic interface for working with +--- iterables: tables, lists, iterator functions, pair()/ipair()-like iterators, +--- and \`vim.iter()\` objects. --- --- \*vim.iter()\* wraps its table or function argument into an \*Iter\* object --- with methods (such as |Iter:filter()| and |Iter:map()|) that transform the @@ -14,6 +15,8 @@ --- - Non-list tables pass both the key and value of each element --- - Function iterators pass all of the values returned by their respective --- function +--- - Tables with a metatable implementing __call are treated as function +--- iterators --- --- Examples: --- <pre>lua @@ -46,6 +49,12 @@ --- return k == 'z' --- end) --- -- true +--- +--- local rb = vim.ringbuf(3) +--- rb:push("a") +--- rb:push("b") +--- vim.iter(rb):totable() +--- -- { "a", "b" } --- </pre> --- --- In addition to the |vim.iter()| function, the |vim.iter| module provides @@ -350,7 +359,7 @@ function ListIter.totable(self) return self._table end ---- Fold an iterator or table into a single value. +--- Fold ("reduce") an iterator or table into a single value. --- --- Examples: --- <pre>lua @@ -889,6 +898,17 @@ end function Iter.new(src, ...) local it = {} if type(src) == 'table' then + local mt = getmetatable(src) + if mt and type(mt.__call) == 'function' then + ---@private + function it.next() + return src() + end + + setmetatable(it, Iter) + return it + end + local t = {} -- Check if source table can be treated like a list (indices are consecutive integers diff --git a/runtime/lua/vim/keymap.lua b/runtime/lua/vim/keymap.lua index 911b584c14..9dbc65afe8 100644 --- a/runtime/lua/vim/keymap.lua +++ b/runtime/lua/vim/keymap.lua @@ -6,7 +6,7 @@ local keymap = {} --- -- Map to a Lua function: --- vim.keymap.set('n', 'lhs', function() print("real lua function") end) --- -- Map to multiple modes: ---- vim.keymap.set({'n', 'v'}, '<leader>lr', vim.lsp.buf.references, { buffer=true }) +--- vim.keymap.set({'n', 'v'}, '<leader>lr', vim.lsp.buf.references, { buffer = true }) --- -- Buffer-local mapping: --- vim.keymap.set('n', '<leader>w', "<cmd>w<cr>", { silent = true, buffer = 5 }) --- -- Expr mapping: @@ -27,9 +27,9 @@ local keymap = {} --- - "replace_keycodes" defaults to `true` if "expr" is `true`. --- - "noremap": inverse of "remap" (see below). --- - Also accepts: ---- - "buffer" number|boolean Creates buffer-local mapping, `0` or `true` +--- - "buffer": (number|boolean) Creates buffer-local mapping, `0` or `true` --- for current buffer. ---- - remap: (boolean) Make the mapping recursive. Inverses "noremap". +--- - "remap": (boolean) Make the mapping recursive. Inverse of "noremap". --- Defaults to `false`. ---@see |nvim_set_keymap()| function keymap.set(mode, lhs, rhs, opts) @@ -83,8 +83,8 @@ end --- vim.keymap.del({'n', 'i', 'v'}, '<leader>w', { buffer = 5 }) --- </pre> ---@param opts table|nil A table of optional arguments: ---- - buffer: (number or boolean) Remove a mapping from the given buffer. ---- When "true" or 0, use the current buffer. +--- - "buffer": (number|boolean) Remove a mapping from the given buffer. +--- When `0` or `true`, use the current buffer. ---@see |vim.keymap.set()| --- function keymap.del(modes, lhs, opts) diff --git a/runtime/lua/vim/loader.lua b/runtime/lua/vim/loader.lua index 9024bdb231..f340dc85b5 100644 --- a/runtime/lua/vim/loader.lua +++ b/runtime/lua/vim/loader.lua @@ -161,7 +161,7 @@ function Loader.read(name) end end ---- The `package.loaders` loader for lua files using the cache. +--- The `package.loaders` loader for Lua files using the cache. ---@param modname string module name ---@return string|function ---@private @@ -211,7 +211,7 @@ end ---@private -- luacheck: ignore 312 function Loader.loadfile(filename, mode, env) - -- ignore mode, since we byte-compile the lua source files + -- ignore mode, since we byte-compile the Lua source files mode = nil return Loader.load(normalize(filename), { mode = mode, env = env }) end @@ -268,7 +268,7 @@ function Loader.load(modpath, opts) return chunk, err end ---- Finds lua modules for the given module name. +--- Finds Lua modules for the given module name. ---@param modname string Module name, or `"*"` to find the top-level modules instead ---@param opts? ModuleFindOpts (table|nil) Options for finding a module: --- - rtp: (boolean) Search for modname in the runtime path (defaults to `true`) @@ -289,7 +289,7 @@ function M.find(modname, opts) local idx = modname:find('.', 1, true) -- HACK: fix incorrect require statements. Really not a fan of keeping this, - -- but apparently the regular lua loader also allows this + -- but apparently the regular Lua loader also allows this if idx == 1 then modname = modname:gsub('^%.+', '') basename = modname:gsub('%.', '/') @@ -386,9 +386,9 @@ end --- Enables the experimental Lua module loader: --- * overrides loadfile ---- * adds the lua loader using the byte-compilation cache +--- * adds the Lua loader using the byte-compilation cache --- * adds the libs loader ---- * removes the default Neovim loader +--- * removes the default Nvim loader function M.enable() if M.enabled then return @@ -396,11 +396,11 @@ function M.enable() M.enabled = true vim.fn.mkdir(vim.fn.fnamemodify(M.path, ':p'), 'p') _G.loadfile = Loader.loadfile - -- add lua loader + -- add Lua loader table.insert(loaders, 2, Loader.loader) -- add libs loader table.insert(loaders, 3, Loader.loader_lib) - -- remove Neovim loader + -- remove Nvim loader for l, loader in ipairs(loaders) do if loader == vim._load_package then table.remove(loaders, l) @@ -411,7 +411,7 @@ end --- Disables the experimental Lua module loader: --- * removes the loaders ---- * adds the default Neovim loader +--- * adds the default Nvim loader function M.disable() if not M.enabled then return @@ -426,8 +426,8 @@ function M.disable() table.insert(loaders, 2, vim._load_package) end ---- Return the top-level `/lua/*` modules for this path ----@param path string path to check for top-level lua modules +--- Return the top-level \`/lua/*` modules for this path +---@param path string path to check for top-level Lua modules ---@private function Loader.lsmod(path) if not Loader._indexed[path] then diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 5f7a95ae14..ca4851f8d7 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -60,6 +60,8 @@ lsp._request_name_to_capability = { ['textDocument/documentHighlight'] = { 'documentHighlightProvider' }, ['textDocument/semanticTokens/full'] = { 'semanticTokensProvider' }, ['textDocument/semanticTokens/full/delta'] = { 'semanticTokensProvider' }, + ['textDocument/inlayHint'] = { 'inlayHintProvider' }, + ['inlayHint/resolve'] = { 'inlayHintProvider', 'resolveProvider' }, } -- TODO improve handling of scratch buffers with LSP attached. @@ -147,9 +149,9 @@ local function next_client_id() return client_index end -- Tracks all clients created via lsp.start_client -local active_clients = {} -local all_buffer_active_clients = {} -local uninitialized_clients = {} +local active_clients = {} --- @type table<integer,lsp.Client> +local all_buffer_active_clients = {} --- @type table<integer,table<integer,true>> +local uninitialized_clients = {} --- @type table<integer,lsp.Client> ---@private ---@param bufnr? integer @@ -166,7 +168,7 @@ local function for_each_buffer_client(bufnr, fn, restrict_client_ids) end if restrict_client_ids and #restrict_client_ids > 0 then - local filtered_client_ids = {} + local filtered_client_ids = {} --- @type table<integer,true> for client_id in pairs(client_ids) do if vim.list_contains(restrict_client_ids, client_id) then filtered_client_ids[client_id] = true @@ -257,9 +259,10 @@ end ---@private --- Validates a client configuration as given to |vim.lsp.start_client()|. --- ----@param config (table) ----@return table config Cleaned config, containing the command, its ----arguments, and a valid encoding. +---@param config (lsp.ClientConfig) +---@return (string|fun(dispatchers:table):table) Command +---@return string[] Arguments +---@return string Encoding. local function validate_client_config(config) validate({ config = { config, 't' }, @@ -290,22 +293,19 @@ local function validate_client_config(config) 'flags.debounce_text_changes must be a number with the debounce time in milliseconds' ) - local cmd, cmd_args - if type(config.cmd) == 'function' then - cmd = config.cmd + local cmd, cmd_args --- @type (string|fun(dispatchers:table):table), string[] + local config_cmd = config.cmd + if type(config_cmd) == 'function' then + cmd = config_cmd else - cmd, cmd_args = lsp._cmd_parts(config.cmd) + cmd, cmd_args = lsp._cmd_parts(config_cmd) end local offset_encoding = valid_encodings.UTF16 if config.offset_encoding then offset_encoding = validate_encoding(config.offset_encoding) end - return { - cmd = cmd, - cmd_args = cmd_args, - offset_encoding = offset_encoding, - } + return cmd, cmd_args, offset_encoding end ---@private @@ -328,10 +328,11 @@ end --- only the first returned value will be memoized and returned. The function will only be run once, --- even if it has side effects. --- ----@param fn (function) Function to run ----@return function fn Memoized function +---@generic T: function +---@param fn (T) Function to run +---@return T local function once(fn) - local value + local value --- @type any local ran = false return function(...) if not ran then @@ -371,7 +372,7 @@ do --- @field lines string[] snapshot of buffer lines from last didChange --- @field lines_tmp string[] --- @field pending_changes table[] List of debounced changes in incremental sync mode - --- @field timer nil|uv.uv_timer_t uv_timer + --- @field timer nil|uv_timer_t uv_timer --- @field last_flush nil|number uv.hrtime of the last flush/didChange-notification --- @field needs_flush boolean true if buffer updates haven't been sent to clients/servers yet --- @field refs integer how many clients are using this group @@ -610,7 +611,7 @@ do ---@private function changetracking.send_changes(bufnr, firstline, lastline, new_lastline) - local groups = {} + local groups = {} ---@type table<string,CTGroup> for _, client in pairs(lsp.get_active_clients({ bufnr = bufnr })) do local group = get_group(client) groups[group_key(group)] = group @@ -808,10 +809,17 @@ end --- --- - {server_capabilities} (table): Response from the server sent on --- `initialize` describing the server's capabilities. +--- +--- - {progress} A ring buffer (|vim.ringbuf()|) containing progress messages +--- sent by the server. function lsp.client() error() end +--- @class lsp.StartOpts +--- @field reuse_client fun(client: lsp.Client, config: table): boolean +--- @field bufnr integer + --- Create a new LSP client and start a language server or reuses an already --- running client if one is found matching `name` and `root_dir`. --- Attaches the current buffer to the client. @@ -849,7 +857,7 @@ end --- `ftplugin/<filetype_name>.lua` (See |ftplugin-name|) --- ---@param config table Same configuration as documented in |vim.lsp.start_client()| ----@param opts nil|table Optional keyword arguments: +---@param opts (nil|lsp.StartOpts) Optional keyword arguments: --- - reuse_client (fun(client: client, config: table): boolean) --- Predicate used to decide if a client should be re-used. --- Used on all running clients. @@ -858,14 +866,13 @@ end --- - bufnr (number) --- Buffer handle to attach to if starting or re-using a --- client (0 for current). ----@return number|nil client_id +---@return integer|nil client_id function lsp.start(config, opts) opts = opts or {} local reuse_client = opts.reuse_client or function(client, conf) return client.config.root_dir == conf.root_dir and client.name == conf.name end - config.name = config.name if not config.name and type(config.cmd) == 'table' then config.name = config.cmd[1] and vim.fs.basename(config.cmd[1]) or nil end @@ -889,6 +896,34 @@ function lsp.start(config, opts) return client_id end +--- Consumes the latest progress messages from all clients and formats them as a string. +--- Empty if there are no clients or if no new messages +--- +---@return string +function lsp.status() + local percentage = nil + local messages = {} + for _, client in ipairs(vim.lsp.get_active_clients()) do + for progress in client.progress do + local value = progress.value + if type(value) == 'table' and value.kind then + local message = value.message and (value.title .. ': ' .. value.message) or value.title + messages[#messages + 1] = message + if value.percentage then + percentage = math.max(percentage or 0, value.percentage) + end + end + -- else: Doesn't look like work done progress and can be in any format + -- Just ignore it as there is no sensible way to display it + end + end + local message = table.concat(messages, ', ') + if percentage then + return string.format('%3d%%: %s', percentage, message) + end + return message +end + ---@private -- Determines whether the given option can be set by `set_defaults`. local function is_empty_or_default(bufnr, option) @@ -930,6 +965,29 @@ function lsp._set_defaults(client, bufnr) end end +--- @class lsp.ClientConfig +--- @field cmd (string[]|fun(dispatchers: table):table) +--- @field cmd_cwd string +--- @field cmd_env (table) +--- @field detached boolean +--- @field workspace_folders (table) +--- @field capabilities lsp.ClientCapabilities +--- @field handlers table<string,function> +--- @field settings table +--- @field commands table +--- @field init_options table +--- @field name string +--- @field get_language_id fun(bufnr: integer, filetype: string): string +--- @field offset_encoding string +--- @field on_error fun(code: integer) +--- @field before_init function +--- @field on_init function +--- @field on_exit fun(code: integer, signal: integer, client_id: integer) +--- @field on_attach fun(client: lsp.Client, bufnr: integer) +--- @field trace 'off'|'messages'|'verbose'|nil +--- @field flags table +--- @field root_dir string + -- FIXME: DOC: Currently all methods on the `vim.lsp.client` object are -- documented twice: Here, and on the methods themselves (e.g. -- `client.request()`). This is a workaround for the vimdoc generator script @@ -940,7 +998,7 @@ end --- --- Field `cmd` in {config} is required. --- ----@param config (table) Configuration for the server: +---@param config (lsp.ClientConfig) Configuration for the server: --- - cmd: (string[]|fun(dispatchers: table):table) command a list of --- strings treated like |jobstart()|. The command must launch the language server --- process. `cmd` can also be a function that creates an RPC client. @@ -970,7 +1028,7 @@ end --- the LSP spec. --- --- - capabilities: Map overriding the default capabilities defined by ---- |vim.lsp.protocol.make_client_capabilities()|, passed to the language +--- \|vim.lsp.protocol.make_client_capabilities()|, passed to the language --- server on initialization. Hint: use make_client_capabilities() and modify --- its result. --- - Note: To send an empty dictionary use @@ -1018,7 +1076,7 @@ end --- `initialize_result.offsetEncoding` if `capabilities.offsetEncoding` was --- sent to it. You can only modify the `client.offset_encoding` here before --- any notifications are sent. Most language servers expect to be sent client specified settings after ---- initialization. Neovim does not make this assumption. A +--- initialization. Nvim does not make this assumption. A --- `workspace/didChangeConfiguration` notification should be sent --- to the server during on_init. --- @@ -1051,9 +1109,7 @@ end --- fully initialized. Use `on_init` to do any actions once --- the client has been initialized. function lsp.start_client(config) - local cleaned_config = validate_client_config(config) - local cmd, cmd_args, offset_encoding = - cleaned_config.cmd, cleaned_config.cmd_args, cleaned_config.offset_encoding + local cmd, cmd_args, offset_encoding = validate_client_config(config) config.flags = config.flags or {} config.settings = config.settings or {} @@ -1090,7 +1146,9 @@ function lsp.start_client(config) ---@param method (string) LSP method name ---@param params (table) The parameters for that method. function dispatch.notification(method, params) - local _ = log.trace() and log.trace('notification', method, params) + if log.trace() then + log.trace('notification', method, params) + end local handler = resolve_handler(method) if handler then -- Method name is provided here for convenience. @@ -1104,13 +1162,19 @@ function lsp.start_client(config) ---@param method (string) LSP method name ---@param params (table) The parameters for that method function dispatch.server_request(method, params) - local _ = log.trace() and log.trace('server_request', method, params) + if log.trace() then + log.trace('server_request', method, params) + end local handler = resolve_handler(method) if handler then - local _ = log.trace() and log.trace('server_request: found handler for', method) + if log.trace() then + log.trace('server_request: found handler for', method) + end return handler(nil, params, { method = method, client_id = client_id }) end - local _ = log.warn() and log.warn('server_request: no handler found for', method) + if log.warn() then + log.warn('server_request: no handler found for', method) + end return nil, lsp.rpc_response_error(protocol.ErrorCodes.MethodNotFound) end @@ -1119,11 +1183,12 @@ function lsp.start_client(config) --- ---@param code (integer) Error code ---@param err (...) Other arguments may be passed depending on the error kind - ---@see `vim.lsp.rpc.client_errors` for possible errors. Use + ---@see vim.lsp.rpc.client_errors for possible errors. Use ---`vim.lsp.rpc.client_errors[code]` to get a human-friendly name. function dispatch.on_error(code, err) - local _ = log.error() - and log.error(log_prefix, 'on_error', { code = lsp.client_errors[code], err = err }) + if log.error() then + log.error(log_prefix, 'on_error', { code = lsp.client_errors[code], err = err }) + end err_message(log_prefix, ': Error ', lsp.client_errors[code], ': ', vim.inspect(err)) if config.on_error then local status, usererr = pcall(config.on_error, code, err) @@ -1232,11 +1297,26 @@ function lsp.start_client(config) handlers = handlers, commands = config.commands or {}, + --- @type table<integer,{ type: string, bufnr: integer, method: string}> requests = {}, - -- for $/progress report + + --- Contains $/progress report messages. + --- They have the format {token: integer|string, value: any} + --- For "work done progress", value will be one of: + --- - lsp.WorkDoneProgressBegin, + --- - lsp.WorkDoneProgressReport (extended with title from Begin) + --- - lsp.WorkDoneProgressEnd (extended with title from Begin) + progress = vim.ringbuf(50), + + ---@deprecated use client.progress instead messages = { name = name, messages = {}, progress = {}, status = {} }, dynamic_capabilities = require('vim.lsp._dynamic').new(client_id), } + + ---@type table<string|integer, string> title of unfinished progress sequences by token + client.progress.pending = {} + + --- @type lsp.ClientCapabilities client.config.capabilities = config.capabilities or protocol.make_client_capabilities() -- Store the uninitialized_clients for cleanup in case we exit before initialize finishes. @@ -1249,11 +1329,10 @@ function lsp.start_client(config) messages = 'messages', verbose = 'verbose', } - local version = vim.version() - local workspace_folders - local root_uri - local root_path + local workspace_folders --- @type table[]? + local root_uri --- @type string? + local root_path --- @type string? if config.workspace_folders or config.root_dir then if config.root_dir and not config.workspace_folders then workspace_folders = { @@ -1278,12 +1357,12 @@ function lsp.start_client(config) -- the process has not been started by another process. If the parent -- process is not alive then the server should exit (see exit notification) -- its process. - processId = uv.getpid(), + processId = uv.os_getpid(), -- Information about the client -- since 3.15.0 clientInfo = { name = 'Neovim', - version = string.format('%s.%s.%s', version.major, version.minor, version.patch), + version = tostring(vim.version()), }, -- The rootPath of the workspace. Is null if no folder is open. -- @@ -1394,7 +1473,7 @@ function lsp.start_client(config) ---successful, then it will return {request_id} as the ---second result. You can use this with `client.cancel_request(request_id)` ---to cancel the-request. - ---@see |vim.lsp.buf_request()| + ---@see |vim.lsp.buf_request_all()| function client.request(method, params, handler, bufnr) if not handler then handler = assert( @@ -1404,20 +1483,25 @@ function lsp.start_client(config) end -- Ensure pending didChange notifications are sent so that the server doesn't operate on a stale state changetracking.flush(client, bufnr) + local version = util.buf_versions[bufnr] bufnr = resolve_bufnr(bufnr) - local _ = log.debug() - and log.debug(log_prefix, 'client.request', client_id, method, params, handler, bufnr) + if log.debug() then + log.debug(log_prefix, 'client.request', client_id, method, params, handler, bufnr) + end local success, request_id = rpc.request(method, params, function(err, result) - handler( - err, - result, - { method = method, client_id = client_id, bufnr = bufnr, params = params } - ) + local context = { + method = method, + client_id = client_id, + bufnr = bufnr, + params = params, + version = version, + } + handler(err, result, context) end, function(request_id) local request = client.requests[request_id] request.type = 'complete' nvim_exec_autocmds('LspRequest', { - buffer = bufnr, + buffer = api.nvim_buf_is_valid(bufnr) and bufnr or nil, modeline = false, data = { client_id = client_id, request_id = request_id, request = request }, }) @@ -1553,6 +1637,46 @@ function lsp.start_client(config) end ---@private + --- Execute a lsp command, either via client command function (if available) + --- or via workspace/executeCommand (if supported by the server) + --- + ---@param command lsp.Command + ---@param context? {bufnr: integer} + ---@param handler? lsp-handler only called if a server command + function client._exec_cmd(command, context, handler) + context = vim.deepcopy(context or {}) + context.bufnr = context.bufnr or api.nvim_get_current_buf() + context.client_id = client.id + local cmdname = command.command + local fn = client.commands[cmdname] or lsp.commands[cmdname] + if fn then + fn(command, context) + return + end + + local command_provider = client.server_capabilities.executeCommandProvider + local commands = type(command_provider) == 'table' and command_provider.commands or {} + if not vim.list_contains(commands, cmdname) then + vim.notify_once( + string.format( + 'Language server `%s` does not support command `%s`. This command may require a client extension.', + client.name, + cmdname + ), + vim.log.levels.WARN + ) + return + end + -- Not using command directly to exclude extra properties, + -- see https://github.com/python-lsp/python-lsp-server/issues/146 + local params = { + command = command.command, + arguments = command.arguments, + } + client.request('workspace/executeCommand', params, handler, context.bufnr) + end + + ---@private --- Runs the on_attach function from the client's config if it was defined. ---@param bufnr integer Buffer number function client._on_attach(bufnr) @@ -1616,7 +1740,7 @@ local function text_document_did_save_handler(bufnr) bufnr = resolve_bufnr(bufnr) local uri = vim.uri_from_bufnr(bufnr) local text = once(buf_get_full_text) - for_each_buffer_client(bufnr, function(client) + for _, client in ipairs(lsp.get_active_clients({ bufnr = bufnr })) do local name = api.nvim_buf_get_name(bufnr) local old_name = changetracking._get_and_set_name(client, bufnr, name) if old_name and name ~= old_name then @@ -1648,7 +1772,7 @@ local function text_document_did_save_handler(bufnr) text = included_text, }) end - end) + end end --- Implements the `textDocument/did…` notifications required to track a buffer @@ -1658,6 +1782,7 @@ end --- ---@param bufnr (integer) Buffer handle, or 0 for current ---@param client_id (integer) Client id +---@return boolean success `true` if client was attached successfully; `false` otherwise function lsp.buf_attach_client(bufnr, client_id) validate({ bufnr = { bufnr, 'n', true }, @@ -1683,7 +1808,7 @@ function lsp.buf_attach_client(bufnr, client_id) buffer = bufnr, desc = 'vim.lsp: textDocument/willSave', callback = function(ctx) - for_each_buffer_client(ctx.buf, function(client) + for _, client in ipairs(lsp.get_active_clients({ bufnr = ctx.buf })) do local params = { textDocument = { uri = uri, @@ -1702,7 +1827,7 @@ function lsp.buf_attach_client(bufnr, client_id) log.error(vim.inspect(err)) end end - end) + end end, }) api.nvim_create_autocmd('BufWritePost', { @@ -1718,23 +1843,23 @@ function lsp.buf_attach_client(bufnr, client_id) on_lines = text_document_did_change_handler, on_reload = function() local params = { textDocument = { uri = uri } } - for_each_buffer_client(bufnr, function(client, _) + for _, client in ipairs(lsp.get_active_clients({ bufnr = bufnr })) do changetracking.reset_buf(client, bufnr) if vim.tbl_get(client.server_capabilities, 'textDocumentSync', 'openClose') then client.notify('textDocument/didClose', params) end text_document_did_open_handler(bufnr, client) - end) + end end, on_detach = function() local params = { textDocument = { uri = uri } } - for_each_buffer_client(bufnr, function(client, _) + for _, client in ipairs(lsp.get_active_clients({ bufnr = bufnr })) do changetracking.reset_buf(client, bufnr) if vim.tbl_get(client.server_capabilities, 'textDocumentSync', 'openClose') then client.notify('textDocument/didClose', params) end client.attached_buffers[bufnr] = nil - end) + end util.buf_versions[bufnr] = nil all_buffer_active_clients[bufnr] = nil end, @@ -1746,7 +1871,7 @@ function lsp.buf_attach_client(bufnr, client_id) end if buffer_client_ids[client_id] then - return + return true end -- This is our first time attaching this client to this buffer. buffer_client_ids[client_id] = true @@ -1807,7 +1932,7 @@ function lsp.buf_detach_client(bufnr, client_id) all_buffer_active_clients[bufnr] = nil end - local namespace = vim.lsp.diagnostic.get_namespace(client_id) + local namespace = lsp.diagnostic.get_namespace(client_id) vim.diagnostic.reset(namespace, bufnr) vim.notify(string.format('Detached buffer (id: %d) from client (id: %d)', bufnr, client_id)) @@ -1879,13 +2004,13 @@ end --- - id (number): Only return clients with the given id --- - bufnr (number): Only return clients attached to this buffer --- - name (string): Only return clients with the given name ----@returns (table) List of |vim.lsp.client| objects +---@return lsp.Client[]: List of |vim.lsp.client| objects function lsp.get_active_clients(filter) validate({ filter = { filter, 't', true } }) filter = filter or {} - local clients = {} + local clients = {} --- @type lsp.Client[] local t = filter.bufnr and (all_buffer_active_clients[resolve_bufnr(filter.bufnr)] or {}) or active_clients @@ -1979,34 +2104,30 @@ function lsp.buf_request(bufnr, method, params, handler) handler = { handler, 'f', true }, }) - local supported_clients = {} + bufnr = resolve_bufnr(bufnr) local method_supported = false - for_each_buffer_client(bufnr, function(client, client_id) + local clients = lsp.get_active_clients({ bufnr = bufnr }) + local client_request_ids = {} + for _, client in ipairs(clients) do if client.supports_method(method, { bufnr = bufnr }) then method_supported = true - table.insert(supported_clients, client_id) + + local request_success, request_id = client.request(method, params, handler, bufnr) + -- This could only fail if the client shut down in the time since we looked + -- it up and we did the request, which should be rare. + if request_success then + client_request_ids[client.id] = request_id + end end - end) + end -- if has client but no clients support the given method, notify the user - if - not tbl_isempty(all_buffer_active_clients[resolve_bufnr(bufnr)] or {}) and not method_supported - then + if next(clients) and not method_supported then vim.notify(lsp._unsupported_method(method), vim.log.levels.ERROR) nvim_command('redraw') return {}, function() end end - local client_request_ids = {} - for_each_buffer_client(bufnr, function(client, client_id, resolved_bufnr) - local request_success, request_id = client.request(method, params, handler, resolved_bufnr) - -- This could only fail if the client shut down in the time since we looked - -- it up and we did the request, which should be rare. - if request_success then - client_request_ids[client_id] = request_id - end - end, supported_clients) - local function _cancel_all_requests() for client_id, request_id in pairs(client_request_ids) do local client = active_clients[client_id] @@ -2017,40 +2138,37 @@ function lsp.buf_request(bufnr, method, params, handler) return client_request_ids, _cancel_all_requests end ----Sends an async request for all active clients attached to the buffer. ----Executes the callback on the combined result. ----Parameters are the same as |vim.lsp.buf_request()| but the return result and callback are ----different. +--- Sends an async request for all active clients attached to the buffer and executes the `handler` +--- callback with the combined result. --- ---@param bufnr (integer) Buffer handle, or 0 for current. ---@param method (string) LSP method name ---@param params (table|nil) Parameters to send to the server ----@param callback fun(request_results: table<integer, {error: lsp.ResponseError, result: any}>) (function) ---- The callback to call when all requests are finished. ---- Unlike `buf_request`, this will collect all the responses from each server instead of handling them. ---- A map of client_id:request_result will be provided to the callback. ---- ----@return fun() cancel A function that will cancel all requests -function lsp.buf_request_all(bufnr, method, params, callback) - local request_results = {} +---@param handler fun(results: table<integer, {error: lsp.ResponseError, result: any}>) (function) +--- Handler called after all requests are completed. Server results are passed as +--- a `client_id:result` map. +--- +---@return fun() cancel Function that cancels all requests. +function lsp.buf_request_all(bufnr, method, params, handler) + local results = {} local result_count = 0 local expected_result_count = 0 local set_expected_result_count = once(function() - for_each_buffer_client(bufnr, function(client) + for _, client in ipairs(lsp.get_active_clients({ bufnr = bufnr })) do if client.supports_method(method, { bufnr = bufnr }) then expected_result_count = expected_result_count + 1 end - end) + end end) local function _sync_handler(err, result, ctx) - request_results[ctx.client_id] = { error = err, result = result } + results[ctx.client_id] = { error = err, result = result } result_count = result_count + 1 set_expected_result_count() if result_count >= expected_result_count then - callback(request_results) + handler(results) end end @@ -2062,8 +2180,8 @@ end --- Sends a request to all server and waits for the response of all of them. --- --- Calls |vim.lsp.buf_request_all()| but blocks Nvim while awaiting the result. ---- Parameters are the same as |vim.lsp.buf_request()| but the return result is ---- different. Wait maximum of {timeout_ms} (default 1000) ms. +--- Parameters are the same as |vim.lsp.buf_request_all()| but the result is +--- different. Waits a maximum of {timeout_ms} (default 1000) ms. --- ---@param bufnr (integer) Buffer handle, or 0 for current. ---@param method (string) LSP method name @@ -2104,11 +2222,11 @@ function lsp.buf_notify(bufnr, method, params) method = { method, 's' }, }) local resp = false - for_each_buffer_client(bufnr, function(client, _client_id, _resolved_bufnr) + for _, client in ipairs(lsp.get_active_clients({ bufnr = bufnr })) do if client.rpc.notify(method, params) then resp = true end - end) + end return resp end @@ -2143,20 +2261,20 @@ end --- - findstart=0: column where the completion starts, or -2 or -3 --- - findstart=1: list of matches (actually just calls |complete()|) function lsp.omnifunc(findstart, base) - local _ = log.debug() and log.debug('omnifunc.findstart', { findstart = findstart, base = base }) + if log.debug() then + log.debug('omnifunc.findstart', { findstart = findstart, base = base }) + end local bufnr = resolve_bufnr() local has_buffer_clients = not tbl_isempty(all_buffer_active_clients[bufnr] or {}) if not has_buffer_clients then - if findstart == 1 then - return -1 - else - return {} - end + return findstart == 1 and -1 or {} end -- Then, perform standard completion request - local _ = log.info() and log.info('base ', base) + if log.info() then + log.info('base ', base) + end local pos = api.nvim_win_get_cursor(0) local line = api.nvim_get_current_line() @@ -2248,8 +2366,8 @@ function lsp.formatexpr(opts) } local response = client.request_sync('textDocument/rangeFormatting', params, timeout_ms, bufnr) - if response.result then - vim.lsp.util.apply_text_edits(response.result, 0, client.offset_encoding) + if response and response.result then + lsp.util.apply_text_edits(response.result, 0, client.offset_encoding) return 0 end end @@ -2270,8 +2388,8 @@ end ---@param flags string See |tag-function| --- ---@return table[] tags A list of matching tags -function lsp.tagfunc(...) - return require('vim.lsp.tagfunc')(...) +function lsp.tagfunc(pattern, flags) + return require('vim.lsp.tagfunc')(pattern, flags) end ---Checks whether a client is stopped. @@ -2330,6 +2448,7 @@ function lsp.get_log_path() return log.get_filename() end +---@private --- Invokes a function for each LSP client attached to a buffer. --- ---@param bufnr integer Buffer number @@ -2338,9 +2457,10 @@ end --- buffer number as arguments. Example: --- <pre>lua --- vim.lsp.for_each_buffer_client(0, function(client, client_id, bufnr) ---- print(vim.inspect(client)) +--- vim.print(client) --- end) --- </pre> +---@deprecated use lsp.get_active_clients({ bufnr = bufnr }) with regular loop function lsp.for_each_buffer_client(bufnr, fn) return for_each_buffer_client(bufnr, fn) end @@ -2354,15 +2474,25 @@ function lsp.with(handler, override_config) end end +--- Enable/disable/toggle inlay hints for a buffer +---@param bufnr (integer) Buffer handle, or 0 for current +---@param enable (boolean|nil) true/false to enable/disable, nil to toggle +function lsp.inlay_hint(bufnr, enable) + return require('vim.lsp.inlay_hint')(bufnr, enable) +end + --- Helper function to use when implementing a handler. --- This will check that all of the keys in the user configuration --- are valid keys and make sense to include for this handler. --- --- Will error on invalid keys (i.e. keys that do not exist in the options) +--- @param name string +--- @param options table<string,any> +--- @param user_config table<string,any> function lsp._with_extend(name, options, user_config) user_config = user_config or {} - local resulting_config = {} + local resulting_config = {} --- @type table<string,any> for k, v in pairs(user_config) do if options[k] == nil then error( diff --git a/runtime/lua/vim/lsp/_watchfiles.lua b/runtime/lua/vim/lsp/_watchfiles.lua index 14e5dc6cf8..87938fe4d5 100644 --- a/runtime/lua/vim/lsp/_watchfiles.lua +++ b/runtime/lua/vim/lsp/_watchfiles.lua @@ -1,7 +1,7 @@ local bit = require('bit') -local lpeg = require('lpeg') local watch = require('vim._watch') local protocol = require('vim.lsp.protocol') +local lpeg = vim.lpeg local M = {} @@ -107,6 +107,13 @@ local to_lsp_change_type = { [watch.FileChangeType.Deleted] = protocol.FileChangeType.Deleted, } +--- Default excludes the same as VSCode's `files.watcherExclude` setting. +--- https://github.com/microsoft/vscode/blob/eef30e7165e19b33daa1e15e92fa34ff4a5df0d3/src/vs/workbench/contrib/files/browser/files.contribution.ts#L261 +---@type Lpeg pattern +M._poll_exclude_pattern = parse('**/.git/{objects,subtree-cache}/**') + + parse('**/node_modules/*/**') + + parse('**/.hg/store/**') + --- Registers the workspace/didChangeWatchedFiles capability dynamically. --- ---@param reg table LSP Registration object. @@ -122,10 +129,10 @@ function M.register(reg, ctx) then return end - local watch_regs = {} + local watch_regs = {} --- @type table<string,{pattern:userdata,kind:integer}> for _, w in ipairs(reg.registerOptions.watchers) do local relative_pattern = false - local glob_patterns = {} + local glob_patterns = {} --- @type {baseUri:string, pattern: string}[] if type(w.globPattern) == 'string' then for _, folder in ipairs(client.workspace_folders) do table.insert(glob_patterns, { baseUri = folder.uri, pattern = w.globPattern }) @@ -135,7 +142,7 @@ function M.register(reg, ctx) table.insert(glob_patterns, w.globPattern) end for _, glob_pattern in ipairs(glob_patterns) do - local base_dir = nil + local base_dir = nil ---@type string? if type(glob_pattern.baseUri) == 'string' then base_dir = glob_pattern.baseUri elseif type(glob_pattern.baseUri) == 'table' then @@ -144,6 +151,7 @@ function M.register(reg, ctx) assert(base_dir, "couldn't identify root of watch") base_dir = vim.uri_to_fname(base_dir) + ---@type integer local kind = w.kind or protocol.WatchKind.Create + protocol.WatchKind.Change + protocol.WatchKind.Delete @@ -153,8 +161,8 @@ function M.register(reg, ctx) pattern = lpeg.P(base_dir .. '/') * pattern end - table.insert(watch_regs, { - base_dir = base_dir, + watch_regs[base_dir] = watch_regs[base_dir] or {} + table.insert(watch_regs[base_dir], { pattern = pattern, kind = kind, }) @@ -163,12 +171,12 @@ function M.register(reg, ctx) local callback = function(base_dir) return function(fullpath, change_type) - for _, w in ipairs(watch_regs) do + for _, w in ipairs(watch_regs[base_dir]) do change_type = to_lsp_change_type[change_type] -- e.g. match kind with Delete bit (0b0100) to Delete change_type (3) local kind_mask = bit.lshift(1, change_type - 1) local change_type_match = bit.band(w.kind, kind_mask) == kind_mask - if base_dir == w.base_dir and M._match(w.pattern, fullpath) and change_type_match then + if M._match(w.pattern, fullpath) and change_type_match then local change = { uri = vim.uri_from_fname(fullpath), type = change_type, @@ -198,15 +206,25 @@ function M.register(reg, ctx) end end - local watching = {} - for _, w in ipairs(watch_regs) do - if not watching[w.base_dir] then - watching[w.base_dir] = true - table.insert( - cancels[client_id][reg.id], - M._watchfunc(w.base_dir, { uvflags = { recursive = true } }, callback(w.base_dir)) - ) - end + for base_dir, watches in pairs(watch_regs) do + local include_pattern = vim.iter(watches):fold(lpeg.P(false), function(acc, w) + return acc + w.pattern + end) + + table.insert( + cancels[client_id][reg.id], + M._watchfunc(base_dir, { + uvflags = { + recursive = true, + }, + -- include_pattern will ensure the pattern from *any* watcher definition for the + -- base_dir matches. This first pass prevents polling for changes to files that + -- will never be sent to the LSP server. A second pass in the callback is still necessary to + -- match a *particular* pattern+kind pair. + include_pattern = include_pattern, + exclude_pattern = M._poll_exclude_pattern, + }, callback(base_dir)) + ) end end diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index bb3ca0e6d6..c742505ec6 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -122,7 +122,7 @@ end ---@private ---@param bufnr integer ---@param mode "v"|"V" ----@return table {start={row, col}, end={row, col}} using (1, 0) indexing +---@return table {start={row,col}, end={row,col}} using (1, 0) indexing local function range_from_selection(bufnr, mode) -- TODO: Use `vim.region()` instead https://github.com/neovim/neovim/pull/13896 @@ -159,7 +159,7 @@ end --- @param options table|nil Optional table which holds the following optional fields: --- - formatting_options (table|nil): --- Can be used to specify FormattingOptions. Some unspecified options will be ---- automatically derived from the current Neovim options. +--- automatically derived from the current Nvim options. --- See https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#formattingOptions --- - timeout_ms (integer|nil, default 1000): --- Time in milliseconds to block for formatting requests. No effect if async=true @@ -189,7 +189,7 @@ end --- Restrict formatting to the client with name (client.name) matching this field. --- --- - range (table|nil) Range to format. ---- Table must contain `start` and `end` keys with {row, col} tuples using +--- Table must contain `start` and `end` keys with {row,col} tuples using --- (1,0) indexing. --- Defaults to current selection in visual mode --- Defaults to `nil` in other modes, formatting the full buffer @@ -581,9 +581,9 @@ function M.document_highlight() end --- Removes document highlights from current buffer. ---- -function M.clear_references() - util.buf_clear_references() +--- @param bufnr integer|nil +function M.clear_references(bufnr) + util.buf_clear_references(bufnr or 0) end ---@private @@ -608,9 +608,9 @@ local function on_code_action_results(results, ctx, options) end local found = false for _, o in ipairs(options.context.only) do - -- action kinds are hierarchical with . as a separator: when requesting only - -- 'quickfix' this filter allows both 'quickfix' and 'quickfix.foo', for example - if a.kind:find('^' .. o .. '$') or a.kind:find('^' .. o .. '%.') then + -- action kinds are hierarchical with . as a separator: when requesting only 'type-annotate' + -- this filter allows both 'type-annotate' and 'type-annotate.foo', for example + if a.kind == o or vim.startswith(a.kind, o .. '.') then found = true break end @@ -646,21 +646,7 @@ local function on_code_action_results(results, ctx, options) end if action.command then local command = type(action.command) == 'table' and action.command or action - local fn = client.commands[command.command] or vim.lsp.commands[command.command] - if fn then - local enriched_ctx = vim.deepcopy(ctx) - enriched_ctx.client_id = client.id - fn(command, enriched_ctx) - else - -- Not using command directly to exclude extra properties, - -- see https://github.com/python-lsp/python-lsp-server/issues/146 - local params = { - command = command.command, - arguments = command.arguments, - workDoneToken = command.workDoneToken, - } - client.request('workspace/executeCommand', params, nil, ctx.bufnr) - end + client._exec_cmd(command, ctx) end end @@ -697,7 +683,7 @@ local function on_code_action_results(results, ctx, options) return end apply_action(resolved_action, client) - end) + end, ctx.bufnr) else apply_action(action, client) end @@ -755,7 +741,7 @@ end --- - range: (table|nil) --- Range for which code actions should be requested. --- If in visual mode this defaults to the active selection. ---- Table must contain `start` and `end` keys with {row, col} tuples +--- Table must contain `start` and `end` keys with {row,col} tuples --- using mark-like indexing. See |api-indexing| --- ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_codeAction diff --git a/runtime/lua/vim/lsp/codelens.lua b/runtime/lua/vim/lsp/codelens.lua index ea8c52c334..67104cc40b 100644 --- a/runtime/lua/vim/lsp/codelens.lua +++ b/runtime/lua/vim/lsp/codelens.lua @@ -33,30 +33,10 @@ local function execute_lens(lens, bufnr, client_id) local client = vim.lsp.get_client_by_id(client_id) assert(client, 'Client is required to execute lens, client_id=' .. client_id) - local command = lens.command - local fn = client.commands[command.command] or vim.lsp.commands[command.command] - if fn then - fn(command, { bufnr = bufnr, client_id = client_id }) - return - end - -- Need to use the client that returned the lens → must not use buf_request - local command_provider = client.server_capabilities.executeCommandProvider - local commands = type(command_provider) == 'table' and command_provider.commands or {} - if not vim.list_contains(commands, command.command) then - vim.notify( - string.format( - 'Language server does not support command `%s`. This command may require a client extension.', - command.command - ), - vim.log.levels.WARN - ) - return - end - client.request('workspace/executeCommand', command, function(...) - local result = vim.lsp.handlers['workspace/executeCommand'](...) + client._exec_cmd(lens.command, { bufnr = bufnr }, function(...) + vim.lsp.handlers['workspace/executeCommand'](...) M.refresh() - return result - end, bufnr) + end) end --- Return all lenses for the given buffer @@ -136,6 +116,10 @@ end ---@param bufnr integer ---@param client_id integer function M.display(lenses, bufnr, client_id) + if not api.nvim_buf_is_loaded(bufnr) then + return + end + local ns = namespaces[client_id] if not lenses or not next(lenses) then api.nvim_buf_clear_namespace(bufnr, ns, 0, -1) @@ -181,6 +165,10 @@ end ---@param bufnr integer ---@param client_id integer function M.save(lenses, bufnr, client_id) + if not api.nvim_buf_is_loaded(bufnr) then + return + end + local lenses_by_client = lens_cache_by_buf[bufnr] if not lenses_by_client then lenses_by_client = {} @@ -221,19 +209,24 @@ local function resolve_lenses(lenses, bufnr, client_id, callback) countdown() else client.request('codeLens/resolve', lens, function(_, result) - if result and result.command then + if api.nvim_buf_is_loaded(bufnr) and result and result.command then lens.command = result.command -- Eager display to have some sort of incremental feedback -- Once all lenses got resolved there will be a full redraw for all lenses -- So that multiple lens per line are properly displayed - api.nvim_buf_set_extmark( - bufnr, - ns, - lens.range.start.line, - 0, - { virt_text = { { lens.command.title, 'LspCodeLens' } }, hl_mode = 'combine' } - ) + + local num_lines = api.nvim_buf_line_count(bufnr) + if lens.range.start.line <= num_lines then + api.nvim_buf_set_extmark( + bufnr, + ns, + lens.range.start.line, + 0, + { virt_text = { { lens.command.title, 'LspCodeLens' } }, hl_mode = 'combine' } + ) + end end + countdown() end, bufnr) end diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index 5346160871..625a2ed282 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -9,7 +9,7 @@ local M = {} ---@private --- Writes to error buffer. ----@param ... (table of strings) Will be concatenated before being written +---@param ... string Will be concatenated before being written local function err_message(...) vim.notify(table.concat(vim.tbl_flatten({ ... })), vim.log.levels.ERROR) api.nvim_command('redraw') @@ -20,63 +20,52 @@ M['workspace/executeCommand'] = function(_, _, _, _) -- Error handling is done implicitly by wrapping all handlers; see end of this file end ----@private -local function progress_handler(_, result, ctx, _) - local client_id = ctx.client_id - local client = vim.lsp.get_client_by_id(client_id) - local client_name = client and client.name or string.format('id=%d', client_id) +--see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#progress +---@param result lsp.ProgressParams +---@param ctx lsp.HandlerContext +M['$/progress'] = function(_, result, ctx) + local client = vim.lsp.get_client_by_id(ctx.client_id) if not client then - err_message('LSP[', client_name, '] client has shut down during progress update') + err_message('LSP[id=', tostring(ctx.client_id), '] client has shut down during progress update') return vim.NIL end - local val = result.value -- unspecified yet - local token = result.token -- string or number + local kind = nil + local value = result.value - if type(val) ~= 'table' then - val = { content = val } - end - if val.kind then - if val.kind == 'begin' then - client.messages.progress[token] = { - title = val.title, - cancellable = val.cancellable, - message = val.message, - percentage = val.percentage, - } - elseif val.kind == 'report' then - client.messages.progress[token].cancellable = val.cancellable - client.messages.progress[token].message = val.message - client.messages.progress[token].percentage = val.percentage - elseif val.kind == 'end' then - if client.messages.progress[token] == nil then - err_message('LSP[', client_name, '] received `end` message with no corresponding `begin`') - else - client.messages.progress[token].message = val.message - client.messages.progress[token].done = true + if type(value) == 'table' then + kind = value.kind + -- Carry over title of `begin` messages to `report` and `end` messages + -- So that consumers always have it available, even if they consume a + -- subset of the full sequence + if kind == 'begin' then + client.progress.pending[result.token] = value.title + else + value.title = client.progress.pending[result.token] + if kind == 'end' then + client.progress.pending[result.token] = nil end end - else - client.messages.progress[token] = val - client.messages.progress[token].done = true end - api.nvim_exec_autocmds('User', { pattern = 'LspProgressUpdate', modeline = false }) -end + client.progress:push(result) ---see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#progress -M['$/progress'] = progress_handler + api.nvim_exec_autocmds('LspProgress', { + pattern = kind, + modeline = false, + data = { client_id = ctx.client_id, result = result }, + }) +end --see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#window_workDoneProgress_create +---@param result lsp.WorkDoneProgressCreateParams +---@param ctx lsp.HandlerContext M['window/workDoneProgress/create'] = function(_, result, ctx) - local client_id = ctx.client_id - local client = vim.lsp.get_client_by_id(client_id) - local token = result.token -- string or number - local client_name = client and client.name or string.format('id=%d', client_id) + local client = vim.lsp.get_client_by_id(ctx.client_id) if not client then - err_message('LSP[', client_name, '] client has shut down while creating progress report') + err_message('LSP[id=', tostring(ctx.client_id), '] client has shut down during progress update') return vim.NIL end - client.messages.progress[token] = {} + client.progress:push(result) return vim.NIL end @@ -230,6 +219,10 @@ M['textDocument/codeLens'] = function(...) return require('vim.lsp.codelens').on_codelens(...) end +M['textDocument/inlayHint'] = function(...) + return require('vim.lsp.inlay_hint').on_inlayhint(...) +end + --see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_references M['textDocument/references'] = function(_, result, ctx, config) if not result or vim.tbl_isempty(result) then @@ -623,6 +616,11 @@ M['window/showDocument'] = function(_, result, ctx, _) return { success = success or false } end +---@see https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#workspace_inlayHint_refresh +M['workspace/inlayHint/refresh'] = function(err, result, ctx, config) + return require('vim.lsp.inlay_hint').on_refresh(err, result, ctx, config) +end + -- Add boilerplate error validation and logging for all of these. for k, fn in pairs(M) do M[k] = function(err, result, ctx, config) diff --git a/runtime/lua/vim/lsp/inlay_hint.lua b/runtime/lua/vim/lsp/inlay_hint.lua new file mode 100644 index 0000000000..f577a31696 --- /dev/null +++ b/runtime/lua/vim/lsp/inlay_hint.lua @@ -0,0 +1,313 @@ +local util = require('vim.lsp.util') +local log = require('vim.lsp.log') +local api = vim.api +local M = {} + +---@class lsp._inlay_hint.bufstate +---@field version integer +---@field client_hint table<integer, table<integer, lsp.InlayHint[]>> client_id -> (lnum -> hints) +---@field enabled boolean Whether inlay hints are enabled for the buffer +---@field timer uv.uv_timer_t? Debounce timer associated with the buffer +---@field applied table<integer, integer> Last version of hints applied to this line + +---@type table<integer, lsp._inlay_hint.bufstate> +local bufstates = {} + +local namespace = api.nvim_create_namespace('vim_lsp_inlayhint') +local augroup = api.nvim_create_augroup('vim_lsp_inlayhint', {}) + +--- Reset the request debounce timer of a buffer +---@private +local function reset_timer(reset_bufnr) + local timer = bufstates[reset_bufnr].timer + if timer then + bufstates[reset_bufnr].timer = nil + if not timer:is_closing() then + timer:stop() + timer:close() + end + end +end + +--- |lsp-handler| for the method `textDocument/inlayHint` +--- Store hints for a specific buffer and client +---@private +function M.on_inlayhint(err, result, ctx, _) + if err then + local _ = log.error() and log.error('inlayhint', err) + return + end + local bufnr = ctx.bufnr + if util.buf_versions[bufnr] ~= ctx.version then + return + end + local client_id = ctx.client_id + if not result then + return + end + local bufstate = bufstates[bufnr] + if not (bufstate.client_hint and bufstate.version) then + bufstate.client_hint = vim.defaulttable() + bufstate.version = ctx.version + end + local hints_by_client = bufstate.client_hint + local client = vim.lsp.get_client_by_id(client_id) + + local new_hints_by_lnum = vim.defaulttable() + local num_unprocessed = #result + if num_unprocessed == 0 then + hints_by_client[client_id] = {} + bufstate.version = ctx.version + api.nvim__buf_redraw_range(bufnr, 0, -1) + return + end + + local lines = api.nvim_buf_get_lines(bufnr, 0, -1, false) + ---@private + local function pos_to_byte(position) + local col = position.character + if col > 0 then + local line = lines[position.line + 1] or '' + local ok, convert_result + ok, convert_result = pcall(util._str_byteindex_enc, line, col, client.offset_encoding) + if ok then + return convert_result + end + return math.min(#line, col) + end + return col + end + + for _, hint in ipairs(result) do + local lnum = hint.position.line + hint.position.character = pos_to_byte(hint.position) + table.insert(new_hints_by_lnum[lnum], hint) + end + + hints_by_client[client_id] = new_hints_by_lnum + bufstate.version = ctx.version + api.nvim__buf_redraw_range(bufnr, 0, -1) +end + +---@private +local function resolve_bufnr(bufnr) + return bufnr > 0 and bufnr or api.nvim_get_current_buf() +end + +--- Refresh inlay hints for a buffer +--- +---@param opts (nil|table) Optional arguments +--- - bufnr (integer, default: 0): Buffer whose hints to refresh +--- - only_visible (boolean, default: false): Whether to only refresh hints for the visible regions of the buffer +--- +---@private +local function refresh(opts) + opts = opts or {} + local bufnr = resolve_bufnr(opts.bufnr or 0) + local bufstate = bufstates[bufnr] + if not (bufstate and bufstate.enabled) then + return + end + local only_visible = opts.only_visible or false + local buffer_windows = {} + for _, winid in ipairs(api.nvim_list_wins()) do + if api.nvim_win_get_buf(winid) == bufnr then + table.insert(buffer_windows, winid) + end + end + for _, window in ipairs(buffer_windows) do + local first = vim.fn.line('w0', window) + local last = vim.fn.line('w$', window) + local params = { + textDocument = util.make_text_document_params(bufnr), + range = { + start = { line = first - 1, character = 0 }, + ['end'] = { line = last, character = 0 }, + }, + } + vim.lsp.buf_request(bufnr, 'textDocument/inlayHint', params) + end + if not only_visible then + local params = { + textDocument = util.make_text_document_params(bufnr), + range = { + start = { line = 0, character = 0 }, + ['end'] = { line = api.nvim_buf_line_count(bufnr), character = 0 }, + }, + } + vim.lsp.buf_request(bufnr, 'textDocument/inlayHint', params) + end +end + +--- |lsp-handler| for the method `textDocument/inlayHint/refresh` +---@private +function M.on_refresh(err, _, ctx, _) + if err then + return vim.NIL + end + for _, bufnr in ipairs(vim.lsp.get_buffers_by_client_id(ctx.client_id)) do + for _, winid in ipairs(api.nvim_list_wins()) do + if api.nvim_win_get_buf(winid) == bufnr then + refresh({ bufnr = bufnr }) + break + end + end + end + + return vim.NIL +end + +--- Clear inlay hints +---@param bufnr (integer) Buffer handle, or 0 for current +---@private +local function clear(bufnr) + bufnr = resolve_bufnr(bufnr) + if not bufstates[bufnr] then + return + end + reset_timer(bufnr) + local bufstate = bufstates[bufnr] + local client_lens = (bufstate or {}).client_hint or {} + local client_ids = vim.tbl_keys(client_lens) + for _, iter_client_id in ipairs(client_ids) do + if bufstate then + bufstate.client_hint[iter_client_id] = {} + end + end + api.nvim_buf_clear_namespace(bufnr, namespace, 0, -1) + api.nvim__buf_redraw_range(bufnr, 0, -1) +end + +---@private +local function make_request(request_bufnr) + reset_timer(request_bufnr) + refresh({ bufnr = request_bufnr }) +end + +--- Enable inlay hints for a buffer +---@param bufnr (integer) Buffer handle, or 0 for current +---@private +local function enable(bufnr) + bufnr = resolve_bufnr(bufnr) + local bufstate = bufstates[bufnr] + if not (bufstate and bufstate.enabled) then + bufstates[bufnr] = { enabled = true, timer = nil, applied = {} } + refresh({ bufnr = bufnr }) + api.nvim_buf_attach(bufnr, true, { + on_lines = function(_, cb_bufnr) + if not bufstates[cb_bufnr].enabled then + return true + end + reset_timer(cb_bufnr) + bufstates[cb_bufnr].timer = vim.defer_fn(function() + make_request(cb_bufnr) + end, 200) + end, + on_reload = function(_, cb_bufnr) + clear(cb_bufnr) + if bufstates[cb_bufnr] and bufstates[cb_bufnr].enabled then + bufstates[cb_bufnr] = { enabled = true, applied = {} } + end + refresh({ bufnr = cb_bufnr }) + end, + on_detach = function(_, cb_bufnr) + clear(cb_bufnr) + bufstates[cb_bufnr] = nil + end, + }) + api.nvim_create_autocmd('LspDetach', { + buffer = bufnr, + callback = function(opts) + clear(opts.buf) + end, + once = true, + group = augroup, + }) + end +end + +--- Disable inlay hints for a buffer +---@param bufnr (integer) Buffer handle, or 0 for current +---@private +local function disable(bufnr) + bufnr = resolve_bufnr(bufnr) + if bufstates[bufnr] and bufstates[bufnr].enabled then + clear(bufnr) + bufstates[bufnr].enabled = nil + bufstates[bufnr].timer = nil + end +end + +--- Toggle inlay hints for a buffer +---@param bufnr (integer) Buffer handle, or 0 for current +---@private +local function toggle(bufnr) + bufnr = resolve_bufnr(bufnr) + local bufstate = bufstates[bufnr] + if bufstate and bufstate.enabled then + disable(bufnr) + else + enable(bufnr) + end +end + +api.nvim_set_decoration_provider(namespace, { + on_win = function(_, _, bufnr, topline, botline) + local bufstate = bufstates[bufnr] + if not bufstate then + return + end + + if bufstate.version ~= util.buf_versions[bufnr] then + return + end + local hints_by_client = bufstate.client_hint + + for lnum = topline, botline do + if bufstate.applied[lnum] ~= bufstate.version then + api.nvim_buf_clear_namespace(bufnr, namespace, lnum, lnum + 1) + for _, hints_by_lnum in pairs(hints_by_client) do + local line_hints = hints_by_lnum[lnum] or {} + for _, hint in pairs(line_hints) do + local text = '' + if type(hint.label) == 'string' then + text = hint.label + else + for _, part in ipairs(hint.label) do + text = text .. part.value + end + end + local vt = {} + if hint.paddingLeft then + vt[#vt + 1] = { ' ' } + end + vt[#vt + 1] = { text, 'LspInlayHint' } + if hint.paddingRight then + vt[#vt + 1] = { ' ' } + end + api.nvim_buf_set_extmark(bufnr, namespace, lnum, hint.position.character, { + virt_text_pos = 'inline', + ephemeral = false, + virt_text = vt, + hl_mode = 'combine', + }) + end + end + bufstate.applied[lnum] = bufstate.version + end + end + end, +}) + +return setmetatable(M, { + __call = function(_, bufnr, enable_) + vim.validate({ enable = { enable_, { 'boolean', 'nil' } }, bufnr = { bufnr, 'number' } }) + if enable_ then + enable(bufnr) + elseif enable_ == false then + disable(bufnr) + else + toggle(bufnr) + end + end, +}) diff --git a/runtime/lua/vim/lsp/log.lua b/runtime/lua/vim/lsp/log.lua index 07d0500797..c77e7c045b 100644 --- a/runtime/lua/vim/lsp/log.lua +++ b/runtime/lua/vim/lsp/log.lua @@ -154,7 +154,7 @@ function log.set_level(level) end --- Gets the current log level. ----@return string current log level +---@return integer current log level function log.get_level() return current_log_level end diff --git a/runtime/lua/vim/lsp/protocol.lua b/runtime/lua/vim/lsp/protocol.lua index 7558b5c6ba..27da891656 100644 --- a/runtime/lua/vim/lsp/protocol.lua +++ b/runtime/lua/vim/lsp/protocol.lua @@ -632,6 +632,7 @@ export interface WorkspaceClientCapabilities { --- Gets a new ClientCapabilities object describing the LSP client --- capabilities. +--- @return lsp.ClientCapabilities function protocol.make_client_capabilities() return { general = { @@ -640,6 +641,12 @@ function protocol.make_client_capabilities() }, }, textDocument = { + inlayHint = { + dynamicRegistration = true, + resolveSupport = { + properties = {}, + }, + }, semanticTokens = { dynamicRegistration = false, tokenTypes = { @@ -852,6 +859,9 @@ function protocol.make_client_capabilities() dynamicRegistration = true, relativePatternSupport = true, }, + inlayHint = { + refreshSupport = true, + }, }, experimental = nil, window = { @@ -870,7 +880,7 @@ end --- Creates a normalized object describing LSP server capabilities. ---@param server_capabilities table Table of capabilities supported by the server ----@return table Normalized table of capabilities +---@return table|nil Normalized table of capabilities function protocol.resolve_capabilities(server_capabilities) local TextDocumentSyncKind = protocol.TextDocumentSyncKind local textDocumentSync = server_capabilities.textDocumentSync @@ -888,7 +898,8 @@ function protocol.resolve_capabilities(server_capabilities) elseif type(textDocumentSync) == 'number' then -- Backwards compatibility if not TextDocumentSyncKind[textDocumentSync] then - return nil, 'Invalid server TextDocumentSyncKind for textDocumentSync' + vim.notify('Invalid server TextDocumentSyncKind for textDocumentSync', vim.log.levels.ERROR) + return nil end server_capabilities.textDocumentSync = { openClose = true, @@ -900,7 +911,11 @@ function protocol.resolve_capabilities(server_capabilities) }, } elseif type(textDocumentSync) ~= 'table' then - return nil, string.format('Invalid type for textDocumentSync: %q', type(textDocumentSync)) + vim.notify( + string.format('Invalid type for textDocumentSync: %q', type(textDocumentSync)), + vim.log.levels.ERROR + ) + return nil end return server_capabilities end diff --git a/runtime/lua/vim/lsp/rpc.lua b/runtime/lua/vim/lsp/rpc.lua index 5f48effebf..6552eaa800 100644 --- a/runtime/lua/vim/lsp/rpc.lua +++ b/runtime/lua/vim/lsp/rpc.lua @@ -15,32 +15,6 @@ local function is_dir(filename) end ---@private ---- Merges current process env with the given env and returns the result as ---- a list of "k=v" strings. ---- ---- <pre> ---- Example: ---- ---- in: { PRODUCTION="false", PATH="/usr/bin/", PORT=123, HOST="0.0.0.0", } ---- out: { "PRODUCTION=false", "PATH=/usr/bin/", "PORT=123", "HOST=0.0.0.0", } ---- </pre> ----@param env (table) table of environment variable assignments ----@returns (table) list of `"k=v"` strings -local function env_merge(env) - if env == nil then - return env - end - -- Merge. - env = vim.tbl_extend('force', vim.fn.environ(), env) - local final_env = {} - for k, v in pairs(env) do - assert(type(k) == 'string', 'env must be a dict') - table.insert(final_env, k .. '=' .. tostring(v)) - end - return final_env -end - ----@private --- Embeds the given string into a table and correctly computes `Content-Length`. --- ---@param encoded_message (string) @@ -62,7 +36,7 @@ end local function parse_headers(header) assert(type(header) == 'string', 'header must be a string') local headers = {} - for line in vim.gsplit(header, '\r\n', true) do + for line in vim.gsplit(header, '\r\n', { plain = true }) do if line == '' then break end @@ -658,89 +632,85 @@ end --- - `is_closing()` returns a boolean indicating if the RPC is closing. --- - `terminate()` terminates the RPC client. local function start(cmd, cmd_args, dispatchers, extra_spawn_params) - local _ = log.info() - and log.info('Starting RPC client', { cmd = cmd, args = cmd_args, extra = extra_spawn_params }) + if log.info() then + log.info('Starting RPC client', { cmd = cmd, args = cmd_args, extra = extra_spawn_params }) + end + validate({ cmd = { cmd, 's' }, cmd_args = { cmd_args, 't' }, dispatchers = { dispatchers, 't', true }, }) - if extra_spawn_params and extra_spawn_params.cwd then + extra_spawn_params = extra_spawn_params or {} + + if extra_spawn_params.cwd then assert(is_dir(extra_spawn_params.cwd), 'cwd must be a directory') end dispatchers = merge_dispatchers(dispatchers) - local stdin = uv.new_pipe(false) - local stdout = uv.new_pipe(false) - local stderr = uv.new_pipe(false) - local handle, pid + + local sysobj ---@type SystemObj local client = new_client(dispatchers, { write = function(msg) - stdin:write(msg) + sysobj:write(msg) end, is_closing = function() - return handle == nil or handle:is_closing() + return sysobj == nil or sysobj:is_closing() end, terminate = function() - if handle then - handle:kill(15) - end + sysobj:kill(15) end, }) - ---@private - --- Callback for |vim.uv.spawn()| Closes all streams and runs the `on_exit` dispatcher. - ---@param code (integer) Exit code - ---@param signal (integer) Signal that was used to terminate (if any) - local function onexit(code, signal) - stdin:close() - stdout:close() - stderr:close() - handle:close() - dispatchers.on_exit(code, signal) + local handle_body = function(body) + client:handle_body(body) end - local spawn_params = { - args = cmd_args, - stdio = { stdin, stdout, stderr }, - detached = not is_win, - } - if extra_spawn_params then - spawn_params.cwd = extra_spawn_params.cwd - spawn_params.env = env_merge(extra_spawn_params.env) - if extra_spawn_params.detached ~= nil then - spawn_params.detached = extra_spawn_params.detached + + local stdout_handler = create_read_loop(handle_body, nil, function(err) + client:on_error(client_errors.READ_ERROR, err) + end) + + local stderr_handler = function(_, chunk) + if chunk and log.error() then + log.error('rpc', cmd, 'stderr', chunk) end end - handle, pid = uv.spawn(cmd, spawn_params, onexit) - if handle == nil then - stdin:close() - stdout:close() - stderr:close() + + local detached = not is_win + if extra_spawn_params.detached ~= nil then + detached = extra_spawn_params.detached + end + + local cmd1 = { cmd } + vim.list_extend(cmd1, cmd_args) + + local ok, sysobj_or_err = pcall(vim.system, cmd1, { + stdin = true, + stdout = stdout_handler, + stderr = stderr_handler, + cwd = extra_spawn_params.cwd, + env = extra_spawn_params.env, + detach = detached, + }, function(obj) + dispatchers.on_exit(obj.code, obj.signal) + end) + + if not ok then + local err = sysobj_or_err --[[@as string]] local msg = string.format('Spawning language server with cmd: `%s` failed', cmd) - if string.match(pid, 'ENOENT') then + if string.match(err, 'ENOENT') then msg = msg .. '. The language server is either not installed, missing from PATH, or not executable.' else - msg = msg .. string.format(' with error message: %s', pid) + msg = msg .. string.format(' with error message: %s', err) end vim.notify(msg, vim.log.levels.WARN) return end - stderr:read_start(function(_, chunk) - if chunk then - local _ = log.error() and log.error('rpc', cmd, 'stderr', chunk) - end - end) - - local handle_body = function(body) - client:handle_body(body) - end - stdout:read_start(create_read_loop(handle_body, nil, function(err) - client:on_error(client_errors.READ_ERROR, err) - end)) + sysobj = sysobj_or_err --[[@as SystemObj]] return public_client(client) end diff --git a/runtime/lua/vim/lsp/types.lua b/runtime/lua/vim/lsp/types.lua index e77e1fb63a..cdfbcfb11b 100644 --- a/runtime/lua/vim/lsp/types.lua +++ b/runtime/lua/vim/lsp/types.lua @@ -1,6 +1,12 @@ ---@meta ----@alias lsp-handler fun(err: lsp.ResponseError|nil, result: any, context: table, config: table|nil) +---@alias lsp-handler fun(err: lsp.ResponseError|nil, result: any, context: lsp.HandlerContext, config: table|nil): any? + +---@class lsp.HandlerContext +---@field method string +---@field client_id integer +---@field bufnr integer +---@field params any ---@class lsp.ResponseError ---@field code integer @@ -63,3 +69,28 @@ --- @field method string --- @alias lsp.UnregistrationParams {unregisterations: lsp.Unregistration[]} + +---@class lsp.Location +---@field uri string +---@field range lsp.Range + +---@class lsp.MarkupContent +---@field kind string +---@field value string + +---@class lsp.InlayHintLabelPart +---@field value string +---@field tooltip? string | lsp.MarkupContent +---@field location? lsp.Location + +---@class lsp.TextEdit +---@field range lsp.Range +---@field newText string + +---@class lsp.InlayHint +---@field position lsp.Position +---@field label string | lsp.InlayHintLabelPart[] +---@field kind? integer +---@field textEdits? lsp.TextEdit[] +---@field paddingLeft? boolean +---@field paddingRight? boolean diff --git a/runtime/lua/vim/lsp/types/protocol.lua b/runtime/lua/vim/lsp/types/protocol.lua new file mode 100644 index 0000000000..4b6660eb51 --- /dev/null +++ b/runtime/lua/vim/lsp/types/protocol.lua @@ -0,0 +1,4398 @@ +--[[ +This file is autogenerated from scripts/lsp_types.lua +Regenerate: +nvim -l scripts/lsp_types.lua gen --runtime/lua/vim/lsp/types/protocol.lua +--]] + +---@alias lsp.null nil +---@alias uinteger integer +---@alias lsp.decimal number +---@alias lsp.DocumentUri string +---@alias lsp.URI string +---@alias lsp.LSPObject table<string, lsp.LSPAny> +---@alias lsp.LSPArray lsp.LSPAny[] +---@alias lsp.LSPAny lsp.LSPObject|lsp.LSPArray|string|number|boolean|nil + +---@class lsp.ImplementationParams: lsp.TextDocumentPositionParams, lsp.WorkDoneProgressParams, lsp.PartialResultParams + +---Represents a location inside a resource, such as a line +---inside a text file. +---@class lsp.Location +---@field uri lsp.DocumentUri +---@field range lsp.Range + +---@class lsp.ImplementationRegistrationOptions: lsp.TextDocumentRegistrationOptions, lsp.StaticRegistrationOptions + +---@class lsp.TypeDefinitionParams: lsp.TextDocumentPositionParams, lsp.WorkDoneProgressParams, lsp.PartialResultParams + +---@class lsp.TypeDefinitionRegistrationOptions: lsp.TextDocumentRegistrationOptions, lsp.StaticRegistrationOptions + +---A workspace folder inside a client. +---@class lsp.WorkspaceFolder +---The associated URI for this workspace folder. +---@field uri lsp.URI +---The name of the workspace folder. Used to refer to this +---workspace folder in the user interface. +---@field name string + +---The parameters of a `workspace/didChangeWorkspaceFolders` notification. +---@class lsp.DidChangeWorkspaceFoldersParams +---The actual workspace folder change event. +---@field event lsp.WorkspaceFoldersChangeEvent + +---The parameters of a configuration request. +---@class lsp.ConfigurationParams +---@field items lsp.ConfigurationItem[] + +---Parameters for a {@link DocumentColorRequest}. +---@class lsp.DocumentColorParams +---The text document. +---@field textDocument lsp.TextDocumentIdentifier + +---Represents a color range from a document. +---@class lsp.ColorInformation +---The range in the document where this color appears. +---@field range lsp.Range +---The actual color value for this color range. +---@field color lsp.Color + +---@class lsp.DocumentColorRegistrationOptions: lsp.TextDocumentRegistrationOptions, lsp.StaticRegistrationOptions + +---Parameters for a {@link ColorPresentationRequest}. +---@class lsp.ColorPresentationParams +---The text document. +---@field textDocument lsp.TextDocumentIdentifier +---The color to request presentations for. +---@field color lsp.Color +---The range where the color would be inserted. Serves as a context. +---@field range lsp.Range + +---@class lsp.ColorPresentation +---The label of this color presentation. It will be shown on the color +---picker header. By default this is also the text that is inserted when selecting +---this color presentation. +---@field label string +---An {@link TextEdit edit} which is applied to a document when selecting +---this presentation for the color. When `falsy` the {@link ColorPresentation.label label} +---is used. +---@field textEdit? lsp.TextEdit +---An optional array of additional {@link TextEdit text edits} that are applied when +---selecting this color presentation. Edits must not overlap with the main {@link ColorPresentation.textEdit edit} nor with themselves. +---@field additionalTextEdits? lsp.TextEdit[] + +---@class lsp.WorkDoneProgressOptions +---@field workDoneProgress? boolean + +---General text document registration options. +---@class lsp.TextDocumentRegistrationOptions +---A document selector to identify the scope of the registration. If set to null +---the document selector provided on the client side will be used. +---@field documentSelector lsp.DocumentSelector|lsp.null + +---Parameters for a {@link FoldingRangeRequest}. +---@class lsp.FoldingRangeParams +---The text document. +---@field textDocument lsp.TextDocumentIdentifier + +---Represents a folding range. To be valid, start and end line must be bigger than zero and smaller +---than the number of lines in the document. Clients are free to ignore invalid ranges. +---@class lsp.FoldingRange +---The zero-based start line of the range to fold. The folded area starts after the line's last character. +---To be valid, the end must be zero or larger and smaller than the number of lines in the document. +---@field startLine uinteger +---The zero-based character offset from where the folded range starts. If not defined, defaults to the length of the start line. +---@field startCharacter? uinteger +---The zero-based end line of the range to fold. The folded area ends with the line's last character. +---To be valid, the end must be zero or larger and smaller than the number of lines in the document. +---@field endLine uinteger +---The zero-based character offset before the folded range ends. If not defined, defaults to the length of the end line. +---@field endCharacter? uinteger +---Describes the kind of the folding range such as `comment' or 'region'. The kind +---is used to categorize folding ranges and used by commands like 'Fold all comments'. +---See {@link FoldingRangeKind} for an enumeration of standardized kinds. +---@field kind? lsp.FoldingRangeKind +---The text that the client should show when the specified range is +---collapsed. If not defined or not supported by the client, a default +---will be chosen by the client. +--- +---@since 3.17.0 +---@field collapsedText? string + +---@class lsp.FoldingRangeRegistrationOptions: lsp.TextDocumentRegistrationOptions, lsp.StaticRegistrationOptions + +---@class lsp.DeclarationParams: lsp.TextDocumentPositionParams, lsp.WorkDoneProgressParams, lsp.PartialResultParams + +---@class lsp.DeclarationRegistrationOptions: lsp.DeclarationOptions, lsp.StaticRegistrationOptions + +---A parameter literal used in selection range requests. +---@class lsp.SelectionRangeParams +---The text document. +---@field textDocument lsp.TextDocumentIdentifier +---The positions inside the text document. +---@field positions lsp.Position[] + +---A selection range represents a part of a selection hierarchy. A selection range +---may have a parent selection range that contains it. +---@class lsp.SelectionRange +---The {@link Range range} of this selection range. +---@field range lsp.Range +---The parent selection range containing this range. Therefore `parent.range` must contain `this.range`. +---@field parent? lsp.SelectionRange + +---@class lsp.SelectionRangeRegistrationOptions: lsp.SelectionRangeOptions, lsp.StaticRegistrationOptions + +---@class lsp.WorkDoneProgressCreateParams +---The token to be used to report progress. +---@field token lsp.ProgressToken + +---@class lsp.WorkDoneProgressCancelParams +---The token to be used to report progress. +---@field token lsp.ProgressToken + +---The parameter of a `textDocument/prepareCallHierarchy` request. +--- +---@since 3.16.0 +---@class lsp.CallHierarchyPrepareParams: lsp.TextDocumentPositionParams, lsp.WorkDoneProgressParams + +---Represents programming constructs like functions or constructors in the context +---of call hierarchy. +--- +---@since 3.16.0 +---@class lsp.CallHierarchyItem +---The name of this item. +---@field name string +---The kind of this item. +---@field kind lsp.SymbolKind +---Tags for this item. +---@field tags? lsp.SymbolTag[] +---More detail for this item, e.g. the signature of a function. +---@field detail? string +---The resource identifier of this item. +---@field uri lsp.DocumentUri +---The range enclosing this symbol not including leading/trailing whitespace but everything else, e.g. comments and code. +---@field range lsp.Range +---The range that should be selected and revealed when this symbol is being picked, e.g. the name of a function. +---Must be contained by the {@link CallHierarchyItem.range `range`}. +---@field selectionRange lsp.Range +---A data entry field that is preserved between a call hierarchy prepare and +---incoming calls or outgoing calls requests. +---@field data? lsp.LSPAny + +---Call hierarchy options used during static or dynamic registration. +--- +---@since 3.16.0 +---@class lsp.CallHierarchyRegistrationOptions: lsp.TextDocumentRegistrationOptions, lsp.StaticRegistrationOptions + +---The parameter of a `callHierarchy/incomingCalls` request. +--- +---@since 3.16.0 +---@class lsp.CallHierarchyIncomingCallsParams +---@field item lsp.CallHierarchyItem + +---Represents an incoming call, e.g. a caller of a method or constructor. +--- +---@since 3.16.0 +---@class lsp.CallHierarchyIncomingCall +---The item that makes the call. +---@field from lsp.CallHierarchyItem +---The ranges at which the calls appear. This is relative to the caller +---denoted by {@link CallHierarchyIncomingCall.from `this.from`}. +---@field fromRanges lsp.Range[] + +---The parameter of a `callHierarchy/outgoingCalls` request. +--- +---@since 3.16.0 +---@class lsp.CallHierarchyOutgoingCallsParams +---@field item lsp.CallHierarchyItem + +---Represents an outgoing call, e.g. calling a getter from a method or a method from a constructor etc. +--- +---@since 3.16.0 +---@class lsp.CallHierarchyOutgoingCall +---The item that is called. +---@field to lsp.CallHierarchyItem +---The range at which this item is called. This is the range relative to the caller, e.g the item +---passed to {@link CallHierarchyItemProvider.provideCallHierarchyOutgoingCalls `provideCallHierarchyOutgoingCalls`} +---and not {@link CallHierarchyOutgoingCall.to `this.to`}. +---@field fromRanges lsp.Range[] + +---@since 3.16.0 +---@class lsp.SemanticTokensParams +---The text document. +---@field textDocument lsp.TextDocumentIdentifier + +---@since 3.16.0 +---@class lsp.SemanticTokens +---An optional result id. If provided and clients support delta updating +---the client will include the result id in the next semantic token request. +---A server can then instead of computing all semantic tokens again simply +---send a delta. +---@field resultId? string +---The actual tokens. +---@field data uinteger[] + +---@since 3.16.0 +---@class lsp.SemanticTokensPartialResult +---@field data uinteger[] + +---@since 3.16.0 +---@class lsp.SemanticTokensRegistrationOptions: lsp.TextDocumentRegistrationOptions, lsp.StaticRegistrationOptions + +---@since 3.16.0 +---@class lsp.SemanticTokensDeltaParams +---The text document. +---@field textDocument lsp.TextDocumentIdentifier +---The result id of a previous response. The result Id can either point to a full response +---or a delta response depending on what was received last. +---@field previousResultId string + +---@since 3.16.0 +---@class lsp.SemanticTokensDelta +---@field resultId? string +---The semantic token edits to transform a previous result into a new result. +---@field edits lsp.SemanticTokensEdit[] + +---@since 3.16.0 +---@class lsp.SemanticTokensDeltaPartialResult +---@field edits lsp.SemanticTokensEdit[] + +---@since 3.16.0 +---@class lsp.SemanticTokensRangeParams +---The text document. +---@field textDocument lsp.TextDocumentIdentifier +---The range the semantic tokens are requested for. +---@field range lsp.Range + +---Params to show a resource in the UI. +--- +---@since 3.16.0 +---@class lsp.ShowDocumentParams +---The uri to show. +---@field uri lsp.URI +---Indicates to show the resource in an external program. +---To show, for example, `https://code.visualstudio.com/` +---in the default WEB browser set `external` to `true`. +---@field external? boolean +---An optional property to indicate whether the editor +---showing the document should take focus or not. +---Clients might ignore this property if an external +---program is started. +---@field takeFocus? boolean +---An optional selection range if the document is a text +---document. Clients might ignore the property if an +---external program is started or the file is not a text +---file. +---@field selection? lsp.Range + +---The result of a showDocument request. +--- +---@since 3.16.0 +---@class lsp.ShowDocumentResult +---A boolean indicating if the show was successful. +---@field success boolean + +---@class lsp.LinkedEditingRangeParams: lsp.TextDocumentPositionParams, lsp.WorkDoneProgressParams + +---The result of a linked editing range request. +--- +---@since 3.16.0 +---@class lsp.LinkedEditingRanges +---A list of ranges that can be edited together. The ranges must have +---identical length and contain identical text content. The ranges cannot overlap. +---@field ranges lsp.Range[] +---An optional word pattern (regular expression) that describes valid contents for +---the given ranges. If no pattern is provided, the client configuration's word +---pattern will be used. +---@field wordPattern? string + +---@class lsp.LinkedEditingRangeRegistrationOptions: lsp.TextDocumentRegistrationOptions, lsp.StaticRegistrationOptions + +---The parameters sent in notifications/requests for user-initiated creation of +---files. +--- +---@since 3.16.0 +---@class lsp.CreateFilesParams +---An array of all files/folders created in this operation. +---@field files lsp.FileCreate[] + +---A workspace edit represents changes to many resources managed in the workspace. The edit +---should either provide `changes` or `documentChanges`. If documentChanges are present +---they are preferred over `changes` if the client can handle versioned document edits. +--- +---Since version 3.13.0 a workspace edit can contain resource operations as well. If resource +---operations are present clients need to execute the operations in the order in which they +---are provided. So a workspace edit for example can consist of the following two changes: +---(1) a create file a.txt and (2) a text document edit which insert text into file a.txt. +--- +---An invalid sequence (e.g. (1) delete file a.txt and (2) insert text into file a.txt) will +---cause failure of the operation. How the client recovers from the failure is described by +---the client capability: `workspace.workspaceEdit.failureHandling` +---@class lsp.WorkspaceEdit +---Holds changes to existing resources. +---@field changes? table<lsp.DocumentUri, lsp.TextEdit[]> +---Depending on the client capability `workspace.workspaceEdit.resourceOperations` document changes +---are either an array of `TextDocumentEdit`s to express changes to n different text documents +---where each text document edit addresses a specific version of a text document. Or it can contain +---above `TextDocumentEdit`s mixed with create, rename and delete file / folder operations. +--- +---Whether a client supports versioned document edits is expressed via +---`workspace.workspaceEdit.documentChanges` client capability. +--- +---If a client neither supports `documentChanges` nor `workspace.workspaceEdit.resourceOperations` then +---only plain `TextEdit`s using the `changes` property are supported. +---@field documentChanges? lsp.TextDocumentEdit|lsp.CreateFile|lsp.RenameFile|lsp.DeleteFile[] +---A map of change annotations that can be referenced in `AnnotatedTextEdit`s or create, rename and +---delete file / folder operations. +--- +---Whether clients honor this property depends on the client capability `workspace.changeAnnotationSupport`. +--- +---@since 3.16.0 +---@field changeAnnotations? table<lsp.ChangeAnnotationIdentifier, lsp.ChangeAnnotation> + +---The options to register for file operations. +--- +---@since 3.16.0 +---@class lsp.FileOperationRegistrationOptions +---The actual filters. +---@field filters lsp.FileOperationFilter[] + +---The parameters sent in notifications/requests for user-initiated renames of +---files. +--- +---@since 3.16.0 +---@class lsp.RenameFilesParams +---An array of all files/folders renamed in this operation. When a folder is renamed, only +---the folder will be included, and not its children. +---@field files lsp.FileRename[] + +---The parameters sent in notifications/requests for user-initiated deletes of +---files. +--- +---@since 3.16.0 +---@class lsp.DeleteFilesParams +---An array of all files/folders deleted in this operation. +---@field files lsp.FileDelete[] + +---@class lsp.MonikerParams: lsp.TextDocumentPositionParams, lsp.WorkDoneProgressParams, lsp.PartialResultParams + +---Moniker definition to match LSIF 0.5 moniker definition. +--- +---@since 3.16.0 +---@class lsp.Moniker +---The scheme of the moniker. For example tsc or .Net +---@field scheme string +---The identifier of the moniker. The value is opaque in LSIF however +---schema owners are allowed to define the structure if they want. +---@field identifier string +---The scope in which the moniker is unique +---@field unique lsp.UniquenessLevel +---The moniker kind if known. +---@field kind? lsp.MonikerKind + +---@class lsp.MonikerRegistrationOptions: lsp.TextDocumentRegistrationOptions + +---The parameter of a `textDocument/prepareTypeHierarchy` request. +--- +---@since 3.17.0 +---@class lsp.TypeHierarchyPrepareParams: lsp.TextDocumentPositionParams, lsp.WorkDoneProgressParams + +---@since 3.17.0 +---@class lsp.TypeHierarchyItem +---The name of this item. +---@field name string +---The kind of this item. +---@field kind lsp.SymbolKind +---Tags for this item. +---@field tags? lsp.SymbolTag[] +---More detail for this item, e.g. the signature of a function. +---@field detail? string +---The resource identifier of this item. +---@field uri lsp.DocumentUri +---The range enclosing this symbol not including leading/trailing whitespace +---but everything else, e.g. comments and code. +---@field range lsp.Range +---The range that should be selected and revealed when this symbol is being +---picked, e.g. the name of a function. Must be contained by the +---{@link TypeHierarchyItem.range `range`}. +---@field selectionRange lsp.Range +---A data entry field that is preserved between a type hierarchy prepare and +---supertypes or subtypes requests. It could also be used to identify the +---type hierarchy in the server, helping improve the performance on +---resolving supertypes and subtypes. +---@field data? lsp.LSPAny + +---Type hierarchy options used during static or dynamic registration. +--- +---@since 3.17.0 +---@class lsp.TypeHierarchyRegistrationOptions: lsp.TextDocumentRegistrationOptions, lsp.StaticRegistrationOptions + +---The parameter of a `typeHierarchy/supertypes` request. +--- +---@since 3.17.0 +---@class lsp.TypeHierarchySupertypesParams +---@field item lsp.TypeHierarchyItem + +---The parameter of a `typeHierarchy/subtypes` request. +--- +---@since 3.17.0 +---@class lsp.TypeHierarchySubtypesParams +---@field item lsp.TypeHierarchyItem + +---A parameter literal used in inline value requests. +--- +---@since 3.17.0 +---@class lsp.InlineValueParams +---The text document. +---@field textDocument lsp.TextDocumentIdentifier +---The document range for which inline values should be computed. +---@field range lsp.Range +---Additional information about the context in which inline values were +---requested. +---@field context lsp.InlineValueContext + +---Inline value options used during static or dynamic registration. +--- +---@since 3.17.0 +---@class lsp.InlineValueRegistrationOptions: lsp.InlineValueOptions, lsp.StaticRegistrationOptions + +---A parameter literal used in inlay hint requests. +--- +---@since 3.17.0 +---@class lsp.InlayHintParams +---The text document. +---@field textDocument lsp.TextDocumentIdentifier +---The document range for which inlay hints should be computed. +---@field range lsp.Range + +---Inlay hint information. +--- +---@since 3.17.0 +---@class lsp.InlayHint +---The position of this hint. +---@field position lsp.Position +---The label of this hint. A human readable string or an array of +---InlayHintLabelPart label parts. +--- +---*Note* that neither the string nor the label part can be empty. +---@field label string|lsp.InlayHintLabelPart[] +---The kind of this hint. Can be omitted in which case the client +---should fall back to a reasonable default. +---@field kind? lsp.InlayHintKind +---Optional text edits that are performed when accepting this inlay hint. +--- +---*Note* that edits are expected to change the document so that the inlay +---hint (or its nearest variant) is now part of the document and the inlay +---hint itself is now obsolete. +---@field textEdits? lsp.TextEdit[] +---The tooltip text when you hover over this item. +---@field tooltip? string|lsp.MarkupContent +---Render padding before the hint. +--- +---Note: Padding should use the editor's background color, not the +---background color of the hint itself. That means padding can be used +---to visually align/separate an inlay hint. +---@field paddingLeft? boolean +---Render padding after the hint. +--- +---Note: Padding should use the editor's background color, not the +---background color of the hint itself. That means padding can be used +---to visually align/separate an inlay hint. +---@field paddingRight? boolean +---A data entry field that is preserved on an inlay hint between +---a `textDocument/inlayHint` and a `inlayHint/resolve` request. +---@field data? lsp.LSPAny + +---Inlay hint options used during static or dynamic registration. +--- +---@since 3.17.0 +---@class lsp.InlayHintRegistrationOptions: lsp.InlayHintOptions, lsp.StaticRegistrationOptions + +---Parameters of the document diagnostic request. +--- +---@since 3.17.0 +---@class lsp.DocumentDiagnosticParams +---The text document. +---@field textDocument lsp.TextDocumentIdentifier +---The additional identifier provided during registration. +---@field identifier? string +---The result id of a previous response if provided. +---@field previousResultId? string + +---A partial result for a document diagnostic report. +--- +---@since 3.17.0 +---@class lsp.DocumentDiagnosticReportPartialResult +---@field relatedDocuments table<lsp.DocumentUri, lsp.FullDocumentDiagnosticReport|lsp.UnchangedDocumentDiagnosticReport> + +---Cancellation data returned from a diagnostic request. +--- +---@since 3.17.0 +---@class lsp.DiagnosticServerCancellationData +---@field retriggerRequest boolean + +---Diagnostic registration options. +--- +---@since 3.17.0 +---@class lsp.DiagnosticRegistrationOptions: lsp.TextDocumentRegistrationOptions, lsp.StaticRegistrationOptions + +---Parameters of the workspace diagnostic request. +--- +---@since 3.17.0 +---@class lsp.WorkspaceDiagnosticParams +---The additional identifier provided during registration. +---@field identifier? string +---The currently known diagnostic reports with their +---previous result ids. +---@field previousResultIds lsp.PreviousResultId[] + +---A workspace diagnostic report. +--- +---@since 3.17.0 +---@class lsp.WorkspaceDiagnosticReport +---@field items lsp.WorkspaceDocumentDiagnosticReport[] + +---A partial result for a workspace diagnostic report. +--- +---@since 3.17.0 +---@class lsp.WorkspaceDiagnosticReportPartialResult +---@field items lsp.WorkspaceDocumentDiagnosticReport[] + +---The params sent in an open notebook document notification. +--- +---@since 3.17.0 +---@class lsp.DidOpenNotebookDocumentParams +---The notebook document that got opened. +---@field notebookDocument lsp.NotebookDocument +---The text documents that represent the content +---of a notebook cell. +---@field cellTextDocuments lsp.TextDocumentItem[] + +---The params sent in a change notebook document notification. +--- +---@since 3.17.0 +---@class lsp.DidChangeNotebookDocumentParams +---The notebook document that did change. The version number points +---to the version after all provided changes have been applied. If +---only the text document content of a cell changes the notebook version +---doesn't necessarily have to change. +---@field notebookDocument lsp.VersionedNotebookDocumentIdentifier +---The actual changes to the notebook document. +--- +---The changes describe single state changes to the notebook document. +---So if there are two changes c1 (at array index 0) and c2 (at array +---index 1) for a notebook in state S then c1 moves the notebook from +---S to S' and c2 from S' to S''. So c1 is computed on the state S and +---c2 is computed on the state S'. +--- +---To mirror the content of a notebook using change events use the following approach: +---- start with the same initial content +---- apply the 'notebookDocument/didChange' notifications in the order you receive them. +---- apply the `NotebookChangeEvent`s in a single notification in the order +--- you receive them. +---@field change lsp.NotebookDocumentChangeEvent + +---The params sent in a save notebook document notification. +--- +---@since 3.17.0 +---@class lsp.DidSaveNotebookDocumentParams +---The notebook document that got saved. +---@field notebookDocument lsp.NotebookDocumentIdentifier + +---The params sent in a close notebook document notification. +--- +---@since 3.17.0 +---@class lsp.DidCloseNotebookDocumentParams +---The notebook document that got closed. +---@field notebookDocument lsp.NotebookDocumentIdentifier +---The text documents that represent the content +---of a notebook cell that got closed. +---@field cellTextDocuments lsp.TextDocumentIdentifier[] + +---A parameter literal used in inline completion requests. +--- +---@since 3.18.0 +---@proposed +---@class lsp.InlineCompletionParams: lsp.TextDocumentPositionParams, lsp.WorkDoneProgressParams +---Additional information about the context in which inline completions were +---requested. +---@field context lsp.InlineCompletionContext + +---Represents a collection of {@link InlineCompletionItem inline completion items} to be presented in the editor. +--- +---@since 3.18.0 +---@proposed +---@class lsp.InlineCompletionList +---The inline completion items +---@field items lsp.InlineCompletionItem[] + +---An inline completion item represents a text snippet that is proposed inline to complete text that is being typed. +--- +---@since 3.18.0 +---@proposed +---@class lsp.InlineCompletionItem +---The text to replace the range with. Must be set. +---@field insertText string|lsp.StringValue +---A text that is used to decide if this inline completion should be shown. When `falsy` the {@link InlineCompletionItem.insertText} is used. +---@field filterText? string +---The range to replace. Must begin and end on the same line. +---@field range? lsp.Range +---An optional {@link Command} that is executed *after* inserting this completion. +---@field command? lsp.Command + +---Inline completion options used during static or dynamic registration. +--- +---@since 3.18.0 +---@proposed +---@class lsp.InlineCompletionRegistrationOptions: lsp.InlineCompletionOptions, lsp.StaticRegistrationOptions + +---@class lsp.RegistrationParams +---@field registrations lsp.Registration[] + +---@class lsp.UnregistrationParams +---@field unregisterations lsp.Unregistration[] + +---@class lsp.InitializeParams: lsp._InitializeParams + +---The result returned from an initialize request. +---@class lsp.InitializeResult +---The capabilities the language server provides. +---@field capabilities lsp.ServerCapabilities +---Information about the server. +--- +---@since 3.15.0 +---@field serverInfo? anonym1 + +---The data type of the ResponseError if the +---initialize request fails. +---@class lsp.InitializeError +---Indicates whether the client execute the following retry logic: +---(1) show the message provided by the ResponseError to the user +---(2) user selects retry or cancel +---(3) if user selected retry the initialize method is sent again. +---@field retry boolean + +---@class lsp.InitializedParams + +---The parameters of a change configuration notification. +---@class lsp.DidChangeConfigurationParams +---The actual changed settings +---@field settings lsp.LSPAny + +---@class lsp.DidChangeConfigurationRegistrationOptions +---@field section? string|string[] + +---The parameters of a notification message. +---@class lsp.ShowMessageParams +---The message type. See {@link MessageType} +---@field type lsp.MessageType +---The actual message. +---@field message string + +---@class lsp.ShowMessageRequestParams +---The message type. See {@link MessageType} +---@field type lsp.MessageType +---The actual message. +---@field message string +---The message action items to present. +---@field actions? lsp.MessageActionItem[] + +---@class lsp.MessageActionItem +---A short title like 'Retry', 'Open Log' etc. +---@field title string + +---The log message parameters. +---@class lsp.LogMessageParams +---The message type. See {@link MessageType} +---@field type lsp.MessageType +---The actual message. +---@field message string + +---The parameters sent in an open text document notification +---@class lsp.DidOpenTextDocumentParams +---The document that was opened. +---@field textDocument lsp.TextDocumentItem + +---The change text document notification's parameters. +---@class lsp.DidChangeTextDocumentParams +---The document that did change. The version number points +---to the version after all provided content changes have +---been applied. +---@field textDocument lsp.VersionedTextDocumentIdentifier +---The actual content changes. The content changes describe single state changes +---to the document. So if there are two content changes c1 (at array index 0) and +---c2 (at array index 1) for a document in state S then c1 moves the document from +---S to S' and c2 from S' to S''. So c1 is computed on the state S and c2 is computed +---on the state S'. +--- +---To mirror the content of a document using change events use the following approach: +---- start with the same initial content +---- apply the 'textDocument/didChange' notifications in the order you receive them. +---- apply the `TextDocumentContentChangeEvent`s in a single notification in the order +--- you receive them. +---@field contentChanges lsp.TextDocumentContentChangeEvent[] + +---Describe options to be used when registered for text document change events. +---@class lsp.TextDocumentChangeRegistrationOptions: lsp.TextDocumentRegistrationOptions +---How documents are synced to the server. +---@field syncKind lsp.TextDocumentSyncKind + +---The parameters sent in a close text document notification +---@class lsp.DidCloseTextDocumentParams +---The document that was closed. +---@field textDocument lsp.TextDocumentIdentifier + +---The parameters sent in a save text document notification +---@class lsp.DidSaveTextDocumentParams +---The document that was saved. +---@field textDocument lsp.TextDocumentIdentifier +---Optional the content when saved. Depends on the includeText value +---when the save notification was requested. +---@field text? string + +---Save registration options. +---@class lsp.TextDocumentSaveRegistrationOptions: lsp.TextDocumentRegistrationOptions + +---The parameters sent in a will save text document notification. +---@class lsp.WillSaveTextDocumentParams +---The document that will be saved. +---@field textDocument lsp.TextDocumentIdentifier +---The 'TextDocumentSaveReason'. +---@field reason lsp.TextDocumentSaveReason + +---A text edit applicable to a text document. +---@class lsp.TextEdit +---The range of the text document to be manipulated. To insert +---text into a document create a range where start === end. +---@field range lsp.Range +---The string to be inserted. For delete operations use an +---empty string. +---@field newText string + +---The watched files change notification's parameters. +---@class lsp.DidChangeWatchedFilesParams +---The actual file events. +---@field changes lsp.FileEvent[] + +---Describe options to be used when registered for text document change events. +---@class lsp.DidChangeWatchedFilesRegistrationOptions +---The watchers to register. +---@field watchers lsp.FileSystemWatcher[] + +---The publish diagnostic notification's parameters. +---@class lsp.PublishDiagnosticsParams +---The URI for which diagnostic information is reported. +---@field uri lsp.DocumentUri +---Optional the version number of the document the diagnostics are published for. +--- +---@since 3.15.0 +---@field version? integer +---An array of diagnostic information items. +---@field diagnostics lsp.Diagnostic[] + +---Completion parameters +---@class lsp.CompletionParams: lsp.TextDocumentPositionParams, lsp.WorkDoneProgressParams, lsp.PartialResultParams +---The completion context. This is only available it the client specifies +---to send this using the client capability `textDocument.completion.contextSupport === true` +---@field context? lsp.CompletionContext + +---A completion item represents a text snippet that is +---proposed to complete text that is being typed. +---@class lsp.CompletionItem +---The label of this completion item. +--- +---The label property is also by default the text that +---is inserted when selecting this completion. +--- +---If label details are provided the label itself should +---be an unqualified name of the completion item. +---@field label string +---Additional details for the label +--- +---@since 3.17.0 +---@field labelDetails? lsp.CompletionItemLabelDetails +---The kind of this completion item. Based of the kind +---an icon is chosen by the editor. +---@field kind? lsp.CompletionItemKind +---Tags for this completion item. +--- +---@since 3.15.0 +---@field tags? lsp.CompletionItemTag[] +---A human-readable string with additional information +---about this item, like type or symbol information. +---@field detail? string +---A human-readable string that represents a doc-comment. +---@field documentation? string|lsp.MarkupContent +---Indicates if this item is deprecated. +---@deprecated Use `tags` instead. +---@field deprecated? boolean +---Select this item when showing. +--- +---*Note* that only one completion item can be selected and that the +---tool / client decides which item that is. The rule is that the *first* +---item of those that match best is selected. +---@field preselect? boolean +---A string that should be used when comparing this item +---with other items. When `falsy` the {@link CompletionItem.label label} +---is used. +---@field sortText? string +---A string that should be used when filtering a set of +---completion items. When `falsy` the {@link CompletionItem.label label} +---is used. +---@field filterText? string +---A string that should be inserted into a document when selecting +---this completion. When `falsy` the {@link CompletionItem.label label} +---is used. +--- +---The `insertText` is subject to interpretation by the client side. +---Some tools might not take the string literally. For example +---VS Code when code complete is requested in this example +---`con<cursor position>` and a completion item with an `insertText` of +---`console` is provided it will only insert `sole`. Therefore it is +---recommended to use `textEdit` instead since it avoids additional client +---side interpretation. +---@field insertText? string +---The format of the insert text. The format applies to both the +---`insertText` property and the `newText` property of a provided +---`textEdit`. If omitted defaults to `InsertTextFormat.PlainText`. +--- +---Please note that the insertTextFormat doesn't apply to +---`additionalTextEdits`. +---@field insertTextFormat? lsp.InsertTextFormat +---How whitespace and indentation is handled during completion +---item insertion. If not provided the clients default value depends on +---the `textDocument.completion.insertTextMode` client capability. +--- +---@since 3.16.0 +---@field insertTextMode? lsp.InsertTextMode +---An {@link TextEdit edit} which is applied to a document when selecting +---this completion. When an edit is provided the value of +---{@link CompletionItem.insertText insertText} is ignored. +--- +---Most editors support two different operations when accepting a completion +---item. One is to insert a completion text and the other is to replace an +---existing text with a completion text. Since this can usually not be +---predetermined by a server it can report both ranges. Clients need to +---signal support for `InsertReplaceEdits` via the +---`textDocument.completion.insertReplaceSupport` client capability +---property. +--- +---*Note 1:* The text edit's range as well as both ranges from an insert +---replace edit must be a [single line] and they must contain the position +---at which completion has been requested. +---*Note 2:* If an `InsertReplaceEdit` is returned the edit's insert range +---must be a prefix of the edit's replace range, that means it must be +---contained and starting at the same position. +--- +---@since 3.16.0 additional type `InsertReplaceEdit` +---@field textEdit? lsp.TextEdit|lsp.InsertReplaceEdit +---The edit text used if the completion item is part of a CompletionList and +---CompletionList defines an item default for the text edit range. +--- +---Clients will only honor this property if they opt into completion list +---item defaults using the capability `completionList.itemDefaults`. +--- +---If not provided and a list's default range is provided the label +---property is used as a text. +--- +---@since 3.17.0 +---@field textEditText? string +---An optional array of additional {@link TextEdit text edits} that are applied when +---selecting this completion. Edits must not overlap (including the same insert position) +---with the main {@link CompletionItem.textEdit edit} nor with themselves. +--- +---Additional text edits should be used to change text unrelated to the current cursor position +---(for example adding an import statement at the top of the file if the completion item will +---insert an unqualified type). +---@field additionalTextEdits? lsp.TextEdit[] +---An optional set of characters that when pressed while this completion is active will accept it first and +---then type that character. *Note* that all commit characters should have `length=1` and that superfluous +---characters will be ignored. +---@field commitCharacters? string[] +---An optional {@link Command command} that is executed *after* inserting this completion. *Note* that +---additional modifications to the current document should be described with the +---{@link CompletionItem.additionalTextEdits additionalTextEdits}-property. +---@field command? lsp.Command +---A data entry field that is preserved on a completion item between a +---{@link CompletionRequest} and a {@link CompletionResolveRequest}. +---@field data? lsp.LSPAny + +---Represents a collection of {@link CompletionItem completion items} to be presented +---in the editor. +---@class lsp.CompletionList +---This list it not complete. Further typing results in recomputing this list. +--- +---Recomputed lists have all their items replaced (not appended) in the +---incomplete completion sessions. +---@field isIncomplete boolean +---In many cases the items of an actual completion result share the same +---value for properties like `commitCharacters` or the range of a text +---edit. A completion list can therefore define item defaults which will +---be used if a completion item itself doesn't specify the value. +--- +---If a completion list specifies a default value and a completion item +---also specifies a corresponding value the one from the item is used. +--- +---Servers are only allowed to return default values if the client +---signals support for this via the `completionList.itemDefaults` +---capability. +--- +---@since 3.17.0 +---@field itemDefaults? anonym3 +---The completion items. +---@field items lsp.CompletionItem[] + +---Registration options for a {@link CompletionRequest}. +---@class lsp.CompletionRegistrationOptions: lsp.TextDocumentRegistrationOptions + +---Parameters for a {@link HoverRequest}. +---@class lsp.HoverParams: lsp.TextDocumentPositionParams, lsp.WorkDoneProgressParams + +---The result of a hover request. +---@class lsp.Hover +---The hover's content +---@field contents lsp.MarkupContent|lsp.MarkedString|lsp.MarkedString[] +---An optional range inside the text document that is used to +---visualize the hover, e.g. by changing the background color. +---@field range? lsp.Range + +---Registration options for a {@link HoverRequest}. +---@class lsp.HoverRegistrationOptions: lsp.TextDocumentRegistrationOptions + +---Parameters for a {@link SignatureHelpRequest}. +---@class lsp.SignatureHelpParams: lsp.TextDocumentPositionParams, lsp.WorkDoneProgressParams +---The signature help context. This is only available if the client specifies +---to send this using the client capability `textDocument.signatureHelp.contextSupport === true` +--- +---@since 3.15.0 +---@field context? lsp.SignatureHelpContext + +---Signature help represents the signature of something +---callable. There can be multiple signature but only one +---active and only one active parameter. +---@class lsp.SignatureHelp +---One or more signatures. +---@field signatures lsp.SignatureInformation[] +---The active signature. If omitted or the value lies outside the +---range of `signatures` the value defaults to zero or is ignored if +---the `SignatureHelp` has no signatures. +--- +---Whenever possible implementors should make an active decision about +---the active signature and shouldn't rely on a default value. +--- +---In future version of the protocol this property might become +---mandatory to better express this. +---@field activeSignature? uinteger +---The active parameter of the active signature. If omitted or the value +---lies outside the range of `signatures[activeSignature].parameters` +---defaults to 0 if the active signature has parameters. If +---the active signature has no parameters it is ignored. +---In future version of the protocol this property might become +---mandatory to better express the active parameter if the +---active signature does have any. +---@field activeParameter? uinteger + +---Registration options for a {@link SignatureHelpRequest}. +---@class lsp.SignatureHelpRegistrationOptions: lsp.TextDocumentRegistrationOptions + +---Parameters for a {@link DefinitionRequest}. +---@class lsp.DefinitionParams: lsp.TextDocumentPositionParams, lsp.WorkDoneProgressParams, lsp.PartialResultParams + +---Registration options for a {@link DefinitionRequest}. +---@class lsp.DefinitionRegistrationOptions: lsp.TextDocumentRegistrationOptions + +---Parameters for a {@link ReferencesRequest}. +---@class lsp.ReferenceParams: lsp.TextDocumentPositionParams, lsp.WorkDoneProgressParams, lsp.PartialResultParams +---@field context lsp.ReferenceContext + +---Registration options for a {@link ReferencesRequest}. +---@class lsp.ReferenceRegistrationOptions: lsp.TextDocumentRegistrationOptions + +---Parameters for a {@link DocumentHighlightRequest}. +---@class lsp.DocumentHighlightParams: lsp.TextDocumentPositionParams, lsp.WorkDoneProgressParams, lsp.PartialResultParams + +---A document highlight is a range inside a text document which deserves +---special attention. Usually a document highlight is visualized by changing +---the background color of its range. +---@class lsp.DocumentHighlight +---The range this highlight applies to. +---@field range lsp.Range +---The highlight kind, default is {@link DocumentHighlightKind.Text text}. +---@field kind? lsp.DocumentHighlightKind + +---Registration options for a {@link DocumentHighlightRequest}. +---@class lsp.DocumentHighlightRegistrationOptions: lsp.TextDocumentRegistrationOptions + +---Parameters for a {@link DocumentSymbolRequest}. +---@class lsp.DocumentSymbolParams +---The text document. +---@field textDocument lsp.TextDocumentIdentifier + +---Represents information about programming constructs like variables, classes, +---interfaces etc. +---@class lsp.SymbolInformation: lsp.BaseSymbolInformation +---Indicates if this symbol is deprecated. +--- +---@deprecated Use tags instead +---@field deprecated? boolean +---The location of this symbol. The location's range is used by a tool +---to reveal the location in the editor. If the symbol is selected in the +---tool the range's start information is used to position the cursor. So +---the range usually spans more than the actual symbol's name and does +---normally include things like visibility modifiers. +--- +---The range doesn't have to denote a node range in the sense of an abstract +---syntax tree. It can therefore not be used to re-construct a hierarchy of +---the symbols. +---@field location lsp.Location + +---Represents programming constructs like variables, classes, interfaces etc. +---that appear in a document. Document symbols can be hierarchical and they +---have two ranges: one that encloses its definition and one that points to +---its most interesting range, e.g. the range of an identifier. +---@class lsp.DocumentSymbol +---The name of this symbol. Will be displayed in the user interface and therefore must not be +---an empty string or a string only consisting of white spaces. +---@field name string +---More detail for this symbol, e.g the signature of a function. +---@field detail? string +---The kind of this symbol. +---@field kind lsp.SymbolKind +---Tags for this document symbol. +--- +---@since 3.16.0 +---@field tags? lsp.SymbolTag[] +---Indicates if this symbol is deprecated. +--- +---@deprecated Use tags instead +---@field deprecated? boolean +---The range enclosing this symbol not including leading/trailing whitespace but everything else +---like comments. This information is typically used to determine if the clients cursor is +---inside the symbol to reveal in the symbol in the UI. +---@field range lsp.Range +---The range that should be selected and revealed when this symbol is being picked, e.g the name of a function. +---Must be contained by the `range`. +---@field selectionRange lsp.Range +---Children of this symbol, e.g. properties of a class. +---@field children? lsp.DocumentSymbol[] + +---Registration options for a {@link DocumentSymbolRequest}. +---@class lsp.DocumentSymbolRegistrationOptions: lsp.TextDocumentRegistrationOptions + +---The parameters of a {@link CodeActionRequest}. +---@class lsp.CodeActionParams +---The document in which the command was invoked. +---@field textDocument lsp.TextDocumentIdentifier +---The range for which the command was invoked. +---@field range lsp.Range +---Context carrying additional information. +---@field context lsp.CodeActionContext + +---Represents a reference to a command. Provides a title which +---will be used to represent a command in the UI and, optionally, +---an array of arguments which will be passed to the command handler +---function when invoked. +---@class lsp.Command +---Title of the command, like `save`. +---@field title string +---The identifier of the actual command handler. +---@field command string +---Arguments that the command handler should be +---invoked with. +---@field arguments? lsp.LSPAny[] + +---A code action represents a change that can be performed in code, e.g. to fix a problem or +---to refactor code. +--- +---A CodeAction must set either `edit` and/or a `command`. If both are supplied, the `edit` is applied first, then the `command` is executed. +---@class lsp.CodeAction +---A short, human-readable, title for this code action. +---@field title string +---The kind of the code action. +--- +---Used to filter code actions. +---@field kind? lsp.CodeActionKind +---The diagnostics that this code action resolves. +---@field diagnostics? lsp.Diagnostic[] +---Marks this as a preferred action. Preferred actions are used by the `auto fix` command and can be targeted +---by keybindings. +--- +---A quick fix should be marked preferred if it properly addresses the underlying error. +---A refactoring should be marked preferred if it is the most reasonable choice of actions to take. +--- +---@since 3.15.0 +---@field isPreferred? boolean +---Marks that the code action cannot currently be applied. +--- +---Clients should follow the following guidelines regarding disabled code actions: +--- +--- - Disabled code actions are not shown in automatic [lightbulbs](https://code.visualstudio.com/docs/editor/editingevolved#_code-action) +--- code action menus. +--- +--- - Disabled actions are shown as faded out in the code action menu when the user requests a more specific type +--- of code action, such as refactorings. +--- +--- - If the user has a [keybinding](https://code.visualstudio.com/docs/editor/refactoring#_keybindings-for-code-actions) +--- that auto applies a code action and only disabled code actions are returned, the client should show the user an +--- error message with `reason` in the editor. +--- +---@since 3.16.0 +---@field disabled? anonym4 +---The workspace edit this code action performs. +---@field edit? lsp.WorkspaceEdit +---A command this code action executes. If a code action +---provides an edit and a command, first the edit is +---executed and then the command. +---@field command? lsp.Command +---A data entry field that is preserved on a code action between +---a `textDocument/codeAction` and a `codeAction/resolve` request. +--- +---@since 3.16.0 +---@field data? lsp.LSPAny + +---Registration options for a {@link CodeActionRequest}. +---@class lsp.CodeActionRegistrationOptions: lsp.TextDocumentRegistrationOptions + +---The parameters of a {@link WorkspaceSymbolRequest}. +---@class lsp.WorkspaceSymbolParams +---A query string to filter symbols by. Clients may send an empty +---string here to request all symbols. +---@field query string + +---A special workspace symbol that supports locations without a range. +--- +---See also SymbolInformation. +--- +---@since 3.17.0 +---@class lsp.WorkspaceSymbol: lsp.BaseSymbolInformation +---The location of the symbol. Whether a server is allowed to +---return a location without a range depends on the client +---capability `workspace.symbol.resolveSupport`. +--- +---See SymbolInformation#location for more details. +---@field location lsp.Location|anonym5 +---A data entry field that is preserved on a workspace symbol between a +---workspace symbol request and a workspace symbol resolve request. +---@field data? lsp.LSPAny + +---Registration options for a {@link WorkspaceSymbolRequest}. +---@class lsp.WorkspaceSymbolRegistrationOptions: lsp.WorkspaceSymbolOptions + +---The parameters of a {@link CodeLensRequest}. +---@class lsp.CodeLensParams +---The document to request code lens for. +---@field textDocument lsp.TextDocumentIdentifier + +---A code lens represents a {@link Command command} that should be shown along with +---source text, like the number of references, a way to run tests, etc. +--- +---A code lens is _unresolved_ when no command is associated to it. For performance +---reasons the creation of a code lens and resolving should be done in two stages. +---@class lsp.CodeLens +---The range in which this code lens is valid. Should only span a single line. +---@field range lsp.Range +---The command this code lens represents. +---@field command? lsp.Command +---A data entry field that is preserved on a code lens item between +---a {@link CodeLensRequest} and a [CodeLensResolveRequest] +---(#CodeLensResolveRequest) +---@field data? lsp.LSPAny + +---Registration options for a {@link CodeLensRequest}. +---@class lsp.CodeLensRegistrationOptions: lsp.TextDocumentRegistrationOptions + +---The parameters of a {@link DocumentLinkRequest}. +---@class lsp.DocumentLinkParams +---The document to provide document links for. +---@field textDocument lsp.TextDocumentIdentifier + +---A document link is a range in a text document that links to an internal or external resource, like another +---text document or a web site. +---@class lsp.DocumentLink +---The range this link applies to. +---@field range lsp.Range +---The uri this link points to. If missing a resolve request is sent later. +---@field target? lsp.URI +---The tooltip text when you hover over this link. +--- +---If a tooltip is provided, is will be displayed in a string that includes instructions on how to +---trigger the link, such as `{0} (ctrl + click)`. The specific instructions vary depending on OS, +---user settings, and localization. +--- +---@since 3.15.0 +---@field tooltip? string +---A data entry field that is preserved on a document link between a +---DocumentLinkRequest and a DocumentLinkResolveRequest. +---@field data? lsp.LSPAny + +---Registration options for a {@link DocumentLinkRequest}. +---@class lsp.DocumentLinkRegistrationOptions: lsp.TextDocumentRegistrationOptions + +---The parameters of a {@link DocumentFormattingRequest}. +---@class lsp.DocumentFormattingParams +---The document to format. +---@field textDocument lsp.TextDocumentIdentifier +---The format options. +---@field options lsp.FormattingOptions + +---Registration options for a {@link DocumentFormattingRequest}. +---@class lsp.DocumentFormattingRegistrationOptions: lsp.TextDocumentRegistrationOptions + +---The parameters of a {@link DocumentRangeFormattingRequest}. +---@class lsp.DocumentRangeFormattingParams +---The document to format. +---@field textDocument lsp.TextDocumentIdentifier +---The range to format +---@field range lsp.Range +---The format options +---@field options lsp.FormattingOptions + +---Registration options for a {@link DocumentRangeFormattingRequest}. +---@class lsp.DocumentRangeFormattingRegistrationOptions: lsp.TextDocumentRegistrationOptions + +---The parameters of a {@link DocumentOnTypeFormattingRequest}. +---@class lsp.DocumentOnTypeFormattingParams +---The document to format. +---@field textDocument lsp.TextDocumentIdentifier +---The position around which the on type formatting should happen. +---This is not necessarily the exact position where the character denoted +---by the property `ch` got typed. +---@field position lsp.Position +---The character that has been typed that triggered the formatting +---on type request. That is not necessarily the last character that +---got inserted into the document since the client could auto insert +---characters as well (e.g. like automatic brace completion). +---@field ch string +---The formatting options. +---@field options lsp.FormattingOptions + +---Registration options for a {@link DocumentOnTypeFormattingRequest}. +---@class lsp.DocumentOnTypeFormattingRegistrationOptions: lsp.TextDocumentRegistrationOptions + +---The parameters of a {@link RenameRequest}. +---@class lsp.RenameParams +---The document to rename. +---@field textDocument lsp.TextDocumentIdentifier +---The position at which this request was sent. +---@field position lsp.Position +---The new name of the symbol. If the given name is not valid the +---request must return a {@link ResponseError} with an +---appropriate message set. +---@field newName string + +---Registration options for a {@link RenameRequest}. +---@class lsp.RenameRegistrationOptions: lsp.TextDocumentRegistrationOptions + +---@class lsp.PrepareRenameParams: lsp.TextDocumentPositionParams, lsp.WorkDoneProgressParams + +---The parameters of a {@link ExecuteCommandRequest}. +---@class lsp.ExecuteCommandParams +---The identifier of the actual command handler. +---@field command string +---Arguments that the command should be invoked with. +---@field arguments? lsp.LSPAny[] + +---Registration options for a {@link ExecuteCommandRequest}. +---@class lsp.ExecuteCommandRegistrationOptions: lsp.ExecuteCommandOptions + +---The parameters passed via an apply workspace edit request. +---@class lsp.ApplyWorkspaceEditParams +---An optional label of the workspace edit. This label is +---presented in the user interface for example on an undo +---stack to undo the workspace edit. +---@field label? string +---The edits to apply. +---@field edit lsp.WorkspaceEdit + +---The result returned from the apply workspace edit request. +--- +---@since 3.17 renamed from ApplyWorkspaceEditResponse +---@class lsp.ApplyWorkspaceEditResult +---Indicates whether the edit was applied or not. +---@field applied boolean +---An optional textual description for why the edit was not applied. +---This may be used by the server for diagnostic logging or to provide +---a suitable error for a request that triggered the edit. +---@field failureReason? string +---Depending on the client's failure handling strategy `failedChange` might +---contain the index of the change that failed. This property is only available +---if the client signals a `failureHandlingStrategy` in its client capabilities. +---@field failedChange? uinteger + +---@class lsp.WorkDoneProgressBegin +---@field kind "begin" +---Mandatory title of the progress operation. Used to briefly inform about +---the kind of operation being performed. +--- +---Examples: "Indexing" or "Linking dependencies". +---@field title string +---Controls if a cancel button should show to allow the user to cancel the +---long running operation. Clients that don't support cancellation are allowed +---to ignore the setting. +---@field cancellable? boolean +---Optional, more detailed associated progress message. Contains +---complementary information to the `title`. +--- +---Examples: "3/25 files", "project/src/module2", "node_modules/some_dep". +---If unset, the previous progress message (if any) is still valid. +---@field message? string +---Optional progress percentage to display (value 100 is considered 100%). +---If not provided infinite progress is assumed and clients are allowed +---to ignore the `percentage` value in subsequent in report notifications. +--- +---The value should be steadily rising. Clients are free to ignore values +---that are not following this rule. The value range is [0, 100]. +---@field percentage? uinteger + +---@class lsp.WorkDoneProgressReport +---@field kind "report" +---Controls enablement state of a cancel button. +--- +---Clients that don't support cancellation or don't support controlling the button's +---enablement state are allowed to ignore the property. +---@field cancellable? boolean +---Optional, more detailed associated progress message. Contains +---complementary information to the `title`. +--- +---Examples: "3/25 files", "project/src/module2", "node_modules/some_dep". +---If unset, the previous progress message (if any) is still valid. +---@field message? string +---Optional progress percentage to display (value 100 is considered 100%). +---If not provided infinite progress is assumed and clients are allowed +---to ignore the `percentage` value in subsequent in report notifications. +--- +---The value should be steadily rising. Clients are free to ignore values +---that are not following this rule. The value range is [0, 100] +---@field percentage? uinteger + +---@class lsp.WorkDoneProgressEnd +---@field kind "end" +---Optional, a final message indicating to for example indicate the outcome +---of the operation. +---@field message? string + +---@class lsp.SetTraceParams +---@field value lsp.TraceValues + +---@class lsp.LogTraceParams +---@field message string +---@field verbose? string + +---@class lsp.CancelParams +---The request id to cancel. +---@field id integer|string + +---@class lsp.ProgressParams +---The progress token provided by the client or server. +---@field token lsp.ProgressToken +---The progress data. +---@field value lsp.LSPAny + +---A parameter literal used in requests to pass a text document and a position inside that +---document. +---@class lsp.TextDocumentPositionParams +---The text document. +---@field textDocument lsp.TextDocumentIdentifier +---The position inside the text document. +---@field position lsp.Position + +---@class lsp.WorkDoneProgressParams +---An optional token that a server can use to report work done progress. +---@field workDoneToken? lsp.ProgressToken + +---@class lsp.PartialResultParams +---An optional token that a server can use to report partial results (e.g. streaming) to +---the client. +---@field partialResultToken? lsp.ProgressToken + +---Represents the connection of two locations. Provides additional metadata over normal {@link Location locations}, +---including an origin range. +---@class lsp.LocationLink +---Span of the origin of this link. +--- +---Used as the underlined span for mouse interaction. Defaults to the word range at +---the definition position. +---@field originSelectionRange? lsp.Range +---The target resource identifier of this link. +---@field targetUri lsp.DocumentUri +---The full target range of this link. If the target for example is a symbol then target range is the +---range enclosing this symbol not including leading/trailing whitespace but everything else +---like comments. This information is typically used to highlight the range in the editor. +---@field targetRange lsp.Range +---The range that should be selected and revealed when this link is being followed, e.g the name of a function. +---Must be contained by the `targetRange`. See also `DocumentSymbol#range` +---@field targetSelectionRange lsp.Range + +---A range in a text document expressed as (zero-based) start and end positions. +--- +---If you want to specify a range that contains a line including the line ending +---character(s) then use an end position denoting the start of the next line. +---For example: +---```ts +---{ +--- start: { line: 5, character: 23 } +--- end : { line 6, character : 0 } +---} +---``` +---@class lsp.Range +---The range's start position. +---@field start lsp.Position +---The range's end position. +---@field end lsp.Position + +---@class lsp.ImplementationOptions + +---Static registration options to be returned in the initialize +---request. +---@class lsp.StaticRegistrationOptions +---The id used to register the request. The id can be used to deregister +---the request again. See also Registration#id. +---@field id? string + +---@class lsp.TypeDefinitionOptions + +---The workspace folder change event. +---@class lsp.WorkspaceFoldersChangeEvent +---The array of added workspace folders +---@field added lsp.WorkspaceFolder[] +---The array of the removed workspace folders +---@field removed lsp.WorkspaceFolder[] + +---@class lsp.ConfigurationItem +---The scope to get the configuration section for. +---@field scopeUri? string +---The configuration section asked for. +---@field section? string + +---A literal to identify a text document in the client. +---@class lsp.TextDocumentIdentifier +---The text document's uri. +---@field uri lsp.DocumentUri + +---Represents a color in RGBA space. +---@class lsp.Color +---The red component of this color in the range [0-1]. +---@field red decimal +---The green component of this color in the range [0-1]. +---@field green decimal +---The blue component of this color in the range [0-1]. +---@field blue decimal +---The alpha component of this color in the range [0-1]. +---@field alpha decimal + +---@class lsp.DocumentColorOptions + +---@class lsp.FoldingRangeOptions + +---@class lsp.DeclarationOptions + +---Position in a text document expressed as zero-based line and character +---offset. Prior to 3.17 the offsets were always based on a UTF-16 string +---representation. So a string of the form `a𐐀b` the character offset of the +---character `a` is 0, the character offset of `𐐀` is 1 and the character +---offset of b is 3 since `𐐀` is represented using two code units in UTF-16. +---Since 3.17 clients and servers can agree on a different string encoding +---representation (e.g. UTF-8). The client announces it's supported encoding +---via the client capability [`general.positionEncodings`](#clientCapabilities). +---The value is an array of position encodings the client supports, with +---decreasing preference (e.g. the encoding at index `0` is the most preferred +---one). To stay backwards compatible the only mandatory encoding is UTF-16 +---represented via the string `utf-16`. The server can pick one of the +---encodings offered by the client and signals that encoding back to the +---client via the initialize result's property +---[`capabilities.positionEncoding`](#serverCapabilities). If the string value +---`utf-16` is missing from the client's capability `general.positionEncodings` +---servers can safely assume that the client supports UTF-16. If the server +---omits the position encoding in its initialize result the encoding defaults +---to the string value `utf-16`. Implementation considerations: since the +---conversion from one encoding into another requires the content of the +---file / line the conversion is best done where the file is read which is +---usually on the server side. +--- +---Positions are line end character agnostic. So you can not specify a position +---that denotes `\r|\n` or `\n|` where `|` represents the character offset. +--- +---@since 3.17.0 - support for negotiated position encoding. +---@class lsp.Position +---Line position in a document (zero-based). +--- +---If a line number is greater than the number of lines in a document, it defaults back to the number of lines in the document. +---If a line number is negative, it defaults to 0. +---@field line uinteger +---Character offset on a line in a document (zero-based). +--- +---The meaning of this offset is determined by the negotiated +---`PositionEncodingKind`. +--- +---If the character value is greater than the line length it defaults back to the +---line length. +---@field character uinteger + +---@class lsp.SelectionRangeOptions + +---Call hierarchy options used during static registration. +--- +---@since 3.16.0 +---@class lsp.CallHierarchyOptions + +---@since 3.16.0 +---@class lsp.SemanticTokensOptions +---The legend used by the server +---@field legend lsp.SemanticTokensLegend +---Server supports providing semantic tokens for a specific range +---of a document. +---@field range? boolean|anonym6 +---Server supports providing semantic tokens for a full document. +---@field full? boolean|anonym7 + +---@since 3.16.0 +---@class lsp.SemanticTokensEdit +---The start offset of the edit. +---@field start uinteger +---The count of elements to remove. +---@field deleteCount uinteger +---The elements to insert. +---@field data? uinteger[] + +---@class lsp.LinkedEditingRangeOptions + +---Represents information on a file/folder create. +--- +---@since 3.16.0 +---@class lsp.FileCreate +---A file:// URI for the location of the file/folder being created. +---@field uri string + +---Describes textual changes on a text document. A TextDocumentEdit describes all changes +---on a document version Si and after they are applied move the document to version Si+1. +---So the creator of a TextDocumentEdit doesn't need to sort the array of edits or do any +---kind of ordering. However the edits must be non overlapping. +---@class lsp.TextDocumentEdit +---The text document to change. +---@field textDocument lsp.OptionalVersionedTextDocumentIdentifier +---The edits to be applied. +--- +---@since 3.16.0 - support for AnnotatedTextEdit. This is guarded using a +---client capability. +---@field edits lsp.TextEdit|lsp.AnnotatedTextEdit[] + +---Create file operation. +---@class lsp.CreateFile: lsp.ResourceOperation +---A create +---@field kind "create" +---The resource to create. +---@field uri lsp.DocumentUri +---Additional options +---@field options? lsp.CreateFileOptions + +---Rename file operation +---@class lsp.RenameFile: lsp.ResourceOperation +---A rename +---@field kind "rename" +---The old (existing) location. +---@field oldUri lsp.DocumentUri +---The new location. +---@field newUri lsp.DocumentUri +---Rename options. +---@field options? lsp.RenameFileOptions + +---Delete file operation +---@class lsp.DeleteFile: lsp.ResourceOperation +---A delete +---@field kind "delete" +---The file to delete. +---@field uri lsp.DocumentUri +---Delete options. +---@field options? lsp.DeleteFileOptions + +---Additional information that describes document changes. +--- +---@since 3.16.0 +---@class lsp.ChangeAnnotation +---A human-readable string describing the actual change. The string +---is rendered prominent in the user interface. +---@field label string +---A flag which indicates that user confirmation is needed +---before applying the change. +---@field needsConfirmation? boolean +---A human-readable string which is rendered less prominent in +---the user interface. +---@field description? string + +---A filter to describe in which file operation requests or notifications +---the server is interested in receiving. +--- +---@since 3.16.0 +---@class lsp.FileOperationFilter +---A Uri scheme like `file` or `untitled`. +---@field scheme? string +---The actual file operation pattern. +---@field pattern lsp.FileOperationPattern + +---Represents information on a file/folder rename. +--- +---@since 3.16.0 +---@class lsp.FileRename +---A file:// URI for the original location of the file/folder being renamed. +---@field oldUri string +---A file:// URI for the new location of the file/folder being renamed. +---@field newUri string + +---Represents information on a file/folder delete. +--- +---@since 3.16.0 +---@class lsp.FileDelete +---A file:// URI for the location of the file/folder being deleted. +---@field uri string + +---@class lsp.MonikerOptions + +---Type hierarchy options used during static registration. +--- +---@since 3.17.0 +---@class lsp.TypeHierarchyOptions + +---@since 3.17.0 +---@class lsp.InlineValueContext +---The stack frame (as a DAP Id) where the execution has stopped. +---@field frameId integer +---The document range where execution has stopped. +---Typically the end position of the range denotes the line where the inline values are shown. +---@field stoppedLocation lsp.Range + +---Provide inline value as text. +--- +---@since 3.17.0 +---@class lsp.InlineValueText +---The document range for which the inline value applies. +---@field range lsp.Range +---The text of the inline value. +---@field text string + +---Provide inline value through a variable lookup. +---If only a range is specified, the variable name will be extracted from the underlying document. +---An optional variable name can be used to override the extracted name. +--- +---@since 3.17.0 +---@class lsp.InlineValueVariableLookup +---The document range for which the inline value applies. +---The range is used to extract the variable name from the underlying document. +---@field range lsp.Range +---If specified the name of the variable to look up. +---@field variableName? string +---How to perform the lookup. +---@field caseSensitiveLookup boolean + +---Provide an inline value through an expression evaluation. +---If only a range is specified, the expression will be extracted from the underlying document. +---An optional expression can be used to override the extracted expression. +--- +---@since 3.17.0 +---@class lsp.InlineValueEvaluatableExpression +---The document range for which the inline value applies. +---The range is used to extract the evaluatable expression from the underlying document. +---@field range lsp.Range +---If specified the expression overrides the extracted expression. +---@field expression? string + +---Inline value options used during static registration. +--- +---@since 3.17.0 +---@class lsp.InlineValueOptions + +---An inlay hint label part allows for interactive and composite labels +---of inlay hints. +--- +---@since 3.17.0 +---@class lsp.InlayHintLabelPart +---The value of this label part. +---@field value string +---The tooltip text when you hover over this label part. Depending on +---the client capability `inlayHint.resolveSupport` clients might resolve +---this property late using the resolve request. +---@field tooltip? string|lsp.MarkupContent +---An optional source code location that represents this +---label part. +--- +---The editor will use this location for the hover and for code navigation +---features: This part will become a clickable link that resolves to the +---definition of the symbol at the given location (not necessarily the +---location itself), it shows the hover that shows at the given location, +---and it shows a context menu with further code navigation commands. +--- +---Depending on the client capability `inlayHint.resolveSupport` clients +---might resolve this property late using the resolve request. +---@field location? lsp.Location +---An optional command for this label part. +--- +---Depending on the client capability `inlayHint.resolveSupport` clients +---might resolve this property late using the resolve request. +---@field command? lsp.Command + +---A `MarkupContent` literal represents a string value which content is interpreted base on its +---kind flag. Currently the protocol supports `plaintext` and `markdown` as markup kinds. +--- +---If the kind is `markdown` then the value can contain fenced code blocks like in GitHub issues. +---See https://help.github.com/articles/creating-and-highlighting-code-blocks/#syntax-highlighting +--- +---Here is an example how such a string can be constructed using JavaScript / TypeScript: +---```ts +---let markdown: MarkdownContent = { +--- kind: MarkupKind.Markdown, +--- value: [ +--- '# Header', +--- 'Some text', +--- '```typescript', +--- 'someCode();', +--- '```' +--- ].join('\n') +---}; +---``` +--- +---*Please Note* that clients might sanitize the return markdown. A client could decide to +---remove HTML from the markdown to avoid script execution. +---@class lsp.MarkupContent +---The type of the Markup +---@field kind lsp.MarkupKind +---The content itself +---@field value string + +---Inlay hint options used during static registration. +--- +---@since 3.17.0 +---@class lsp.InlayHintOptions +---The server provides support to resolve additional +---information for an inlay hint item. +---@field resolveProvider? boolean + +---A full diagnostic report with a set of related documents. +--- +---@since 3.17.0 +---@class lsp.RelatedFullDocumentDiagnosticReport: lsp.FullDocumentDiagnosticReport +---Diagnostics of related documents. This information is useful +---in programming languages where code in a file A can generate +---diagnostics in a file B which A depends on. An example of +---such a language is C/C++ where marco definitions in a file +---a.cpp and result in errors in a header file b.hpp. +--- +---@since 3.17.0 +---@field relatedDocuments? table<lsp.DocumentUri, lsp.FullDocumentDiagnosticReport|lsp.UnchangedDocumentDiagnosticReport> + +---An unchanged diagnostic report with a set of related documents. +--- +---@since 3.17.0 +---@class lsp.RelatedUnchangedDocumentDiagnosticReport: lsp.UnchangedDocumentDiagnosticReport +---Diagnostics of related documents. This information is useful +---in programming languages where code in a file A can generate +---diagnostics in a file B which A depends on. An example of +---such a language is C/C++ where marco definitions in a file +---a.cpp and result in errors in a header file b.hpp. +--- +---@since 3.17.0 +---@field relatedDocuments? table<lsp.DocumentUri, lsp.FullDocumentDiagnosticReport|lsp.UnchangedDocumentDiagnosticReport> + +---A diagnostic report with a full set of problems. +--- +---@since 3.17.0 +---@class lsp.FullDocumentDiagnosticReport +---A full document diagnostic report. +---@field kind "full" +---An optional result id. If provided it will +---be sent on the next diagnostic request for the +---same document. +---@field resultId? string +---The actual items. +---@field items lsp.Diagnostic[] + +---A diagnostic report indicating that the last returned +---report is still accurate. +--- +---@since 3.17.0 +---@class lsp.UnchangedDocumentDiagnosticReport +---A document diagnostic report indicating +---no changes to the last result. A server can +---only return `unchanged` if result ids are +---provided. +---@field kind "unchanged" +---A result id which will be sent on the next +---diagnostic request for the same document. +---@field resultId string + +---Diagnostic options. +--- +---@since 3.17.0 +---@class lsp.DiagnosticOptions +---An optional identifier under which the diagnostics are +---managed by the client. +---@field identifier? string +---Whether the language has inter file dependencies meaning that +---editing code in one file can result in a different diagnostic +---set in another file. Inter file dependencies are common for +---most programming languages and typically uncommon for linters. +---@field interFileDependencies boolean +---The server provides support for workspace diagnostics as well. +---@field workspaceDiagnostics boolean + +---A previous result id in a workspace pull request. +--- +---@since 3.17.0 +---@class lsp.PreviousResultId +---The URI for which the client knowns a +---result id. +---@field uri lsp.DocumentUri +---The value of the previous result id. +---@field value string + +---A notebook document. +--- +---@since 3.17.0 +---@class lsp.NotebookDocument +---The notebook document's uri. +---@field uri lsp.URI +---The type of the notebook. +---@field notebookType string +---The version number of this document (it will increase after each +---change, including undo/redo). +---@field version integer +---Additional metadata stored with the notebook +---document. +--- +---Note: should always be an object literal (e.g. LSPObject) +---@field metadata? lsp.LSPObject +---The cells of a notebook. +---@field cells lsp.NotebookCell[] + +---An item to transfer a text document from the client to the +---server. +---@class lsp.TextDocumentItem +---The text document's uri. +---@field uri lsp.DocumentUri +---The text document's language identifier. +---@field languageId string +---The version number of this document (it will increase after each +---change, including undo/redo). +---@field version integer +---The content of the opened text document. +---@field text string + +---A versioned notebook document identifier. +--- +---@since 3.17.0 +---@class lsp.VersionedNotebookDocumentIdentifier +---The version number of this notebook document. +---@field version integer +---The notebook document's uri. +---@field uri lsp.URI + +---A change event for a notebook document. +--- +---@since 3.17.0 +---@class lsp.NotebookDocumentChangeEvent +---The changed meta data if any. +--- +---Note: should always be an object literal (e.g. LSPObject) +---@field metadata? lsp.LSPObject +---Changes to cells +---@field cells? anonym10 + +---A literal to identify a notebook document in the client. +--- +---@since 3.17.0 +---@class lsp.NotebookDocumentIdentifier +---The notebook document's uri. +---@field uri lsp.URI + +---Provides information about the context in which an inline completion was requested. +--- +---@since 3.18.0 +---@proposed +---@class lsp.InlineCompletionContext +---Describes how the inline completion was triggered. +---@field triggerKind lsp.InlineCompletionTriggerKind +---Provides information about the currently selected item in the autocomplete widget if it is visible. +---@field selectedCompletionInfo? lsp.SelectedCompletionInfo + +---A string value used as a snippet is a template which allows to insert text +---and to control the editor cursor when insertion happens. +--- +---A snippet can define tab stops and placeholders with `$1`, `$2` +---and `${3:foo}`. `$0` defines the final tab stop, it defaults to +---the end of the snippet. Variables are defined with `$name` and +---`${name:default value}`. +--- +---@since 3.18.0 +---@proposed +---@class lsp.StringValue +---The kind of string value. +---@field kind "snippet" +---The snippet string. +---@field value string + +---Inline completion options used during static registration. +--- +---@since 3.18.0 +---@proposed +---@class lsp.InlineCompletionOptions + +---General parameters to register for a notification or to register a provider. +---@class lsp.Registration +---The id used to register the request. The id can be used to deregister +---the request again. +---@field id string +---The method / capability to register for. +---@field method string +---Options necessary for the registration. +---@field registerOptions? lsp.LSPAny + +---General parameters to unregister a request or notification. +---@class lsp.Unregistration +---The id used to unregister the request or notification. Usually an id +---provided during the register request. +---@field id string +---The method to unregister for. +---@field method string + +---The initialize parameters +---@class lsp._InitializeParams +---The process Id of the parent process that started +---the server. +--- +---Is `null` if the process has not been started by another process. +---If the parent process is not alive then the server should exit. +---@field processId integer|lsp.null +---Information about the client +--- +---@since 3.15.0 +---@field clientInfo? anonym11 +---The locale the client is currently showing the user interface +---in. This must not necessarily be the locale of the operating +---system. +--- +---Uses IETF language tags as the value's syntax +---(See https://en.wikipedia.org/wiki/IETF_language_tag) +--- +---@since 3.16.0 +---@field locale? string +---The rootPath of the workspace. Is null +---if no folder is open. +--- +---@deprecated in favour of rootUri. +---@field rootPath? string|lsp.null +---The rootUri of the workspace. Is null if no +---folder is open. If both `rootPath` and `rootUri` are set +---`rootUri` wins. +--- +---@deprecated in favour of workspaceFolders. +---@field rootUri lsp.DocumentUri|lsp.null +---The capabilities provided by the client (editor or tool) +---@field capabilities lsp.ClientCapabilities +---User provided initialization options. +---@field initializationOptions? lsp.LSPAny +---The initial trace setting. If omitted trace is disabled ('off'). +---@field trace? lsp.TraceValues + +---@class lsp.WorkspaceFoldersInitializeParams +---The workspace folders configured in the client when the server starts. +--- +---This property is only available if the client supports workspace folders. +---It can be `null` if the client supports workspace folders but none are +---configured. +--- +---@since 3.6.0 +---@field workspaceFolders? lsp.WorkspaceFolder[]|lsp.null + +---Defines the capabilities provided by a language +---server. +---@class lsp.ServerCapabilities +---The position encoding the server picked from the encodings offered +---by the client via the client capability `general.positionEncodings`. +--- +---If the client didn't provide any position encodings the only valid +---value that a server can return is 'utf-16'. +--- +---If omitted it defaults to 'utf-16'. +--- +---@since 3.17.0 +---@field positionEncoding? lsp.PositionEncodingKind +---Defines how text documents are synced. Is either a detailed structure +---defining each notification or for backwards compatibility the +---TextDocumentSyncKind number. +---@field textDocumentSync? lsp.TextDocumentSyncOptions|lsp.TextDocumentSyncKind +---Defines how notebook documents are synced. +--- +---@since 3.17.0 +---@field notebookDocumentSync? lsp.NotebookDocumentSyncOptions|lsp.NotebookDocumentSyncRegistrationOptions +---The server provides completion support. +---@field completionProvider? lsp.CompletionOptions +---The server provides hover support. +---@field hoverProvider? boolean|lsp.HoverOptions +---The server provides signature help support. +---@field signatureHelpProvider? lsp.SignatureHelpOptions +---The server provides Goto Declaration support. +---@field declarationProvider? boolean|lsp.DeclarationOptions|lsp.DeclarationRegistrationOptions +---The server provides goto definition support. +---@field definitionProvider? boolean|lsp.DefinitionOptions +---The server provides Goto Type Definition support. +---@field typeDefinitionProvider? boolean|lsp.TypeDefinitionOptions|lsp.TypeDefinitionRegistrationOptions +---The server provides Goto Implementation support. +---@field implementationProvider? boolean|lsp.ImplementationOptions|lsp.ImplementationRegistrationOptions +---The server provides find references support. +---@field referencesProvider? boolean|lsp.ReferenceOptions +---The server provides document highlight support. +---@field documentHighlightProvider? boolean|lsp.DocumentHighlightOptions +---The server provides document symbol support. +---@field documentSymbolProvider? boolean|lsp.DocumentSymbolOptions +---The server provides code actions. CodeActionOptions may only be +---specified if the client states that it supports +---`codeActionLiteralSupport` in its initial `initialize` request. +---@field codeActionProvider? boolean|lsp.CodeActionOptions +---The server provides code lens. +---@field codeLensProvider? lsp.CodeLensOptions +---The server provides document link support. +---@field documentLinkProvider? lsp.DocumentLinkOptions +---The server provides color provider support. +---@field colorProvider? boolean|lsp.DocumentColorOptions|lsp.DocumentColorRegistrationOptions +---The server provides workspace symbol support. +---@field workspaceSymbolProvider? boolean|lsp.WorkspaceSymbolOptions +---The server provides document formatting. +---@field documentFormattingProvider? boolean|lsp.DocumentFormattingOptions +---The server provides document range formatting. +---@field documentRangeFormattingProvider? boolean|lsp.DocumentRangeFormattingOptions +---The server provides document formatting on typing. +---@field documentOnTypeFormattingProvider? lsp.DocumentOnTypeFormattingOptions +---The server provides rename support. RenameOptions may only be +---specified if the client states that it supports +---`prepareSupport` in its initial `initialize` request. +---@field renameProvider? boolean|lsp.RenameOptions +---The server provides folding provider support. +---@field foldingRangeProvider? boolean|lsp.FoldingRangeOptions|lsp.FoldingRangeRegistrationOptions +---The server provides selection range support. +---@field selectionRangeProvider? boolean|lsp.SelectionRangeOptions|lsp.SelectionRangeRegistrationOptions +---The server provides execute command support. +---@field executeCommandProvider? lsp.ExecuteCommandOptions +---The server provides call hierarchy support. +--- +---@since 3.16.0 +---@field callHierarchyProvider? boolean|lsp.CallHierarchyOptions|lsp.CallHierarchyRegistrationOptions +---The server provides linked editing range support. +--- +---@since 3.16.0 +---@field linkedEditingRangeProvider? boolean|lsp.LinkedEditingRangeOptions|lsp.LinkedEditingRangeRegistrationOptions +---The server provides semantic tokens support. +--- +---@since 3.16.0 +---@field semanticTokensProvider? lsp.SemanticTokensOptions|lsp.SemanticTokensRegistrationOptions +---The server provides moniker support. +--- +---@since 3.16.0 +---@field monikerProvider? boolean|lsp.MonikerOptions|lsp.MonikerRegistrationOptions +---The server provides type hierarchy support. +--- +---@since 3.17.0 +---@field typeHierarchyProvider? boolean|lsp.TypeHierarchyOptions|lsp.TypeHierarchyRegistrationOptions +---The server provides inline values. +--- +---@since 3.17.0 +---@field inlineValueProvider? boolean|lsp.InlineValueOptions|lsp.InlineValueRegistrationOptions +---The server provides inlay hints. +--- +---@since 3.17.0 +---@field inlayHintProvider? boolean|lsp.InlayHintOptions|lsp.InlayHintRegistrationOptions +---The server has support for pull model diagnostics. +--- +---@since 3.17.0 +---@field diagnosticProvider? lsp.DiagnosticOptions|lsp.DiagnosticRegistrationOptions +---Inline completion options used during static registration. +--- +---@since 3.18.0 +---@proposed +---@field inlineCompletionProvider? boolean|lsp.InlineCompletionOptions +---Workspace specific server capabilities. +---@field workspace? anonym12 +---Experimental server capabilities. +---@field experimental? lsp.LSPAny + +---A text document identifier to denote a specific version of a text document. +---@class lsp.VersionedTextDocumentIdentifier: lsp.TextDocumentIdentifier +---The version number of this document. +---@field version integer + +---Save options. +---@class lsp.SaveOptions +---The client is supposed to include the content on save. +---@field includeText? boolean + +---An event describing a file change. +---@class lsp.FileEvent +---The file's uri. +---@field uri lsp.DocumentUri +---The change type. +---@field type lsp.FileChangeType + +---@class lsp.FileSystemWatcher +---The glob pattern to watch. See {@link GlobPattern glob pattern} for more detail. +--- +---@since 3.17.0 support for relative patterns. +---@field globPattern lsp.GlobPattern +---The kind of events of interest. If omitted it defaults +---to WatchKind.Create | WatchKind.Change | WatchKind.Delete +---which is 7. +---@field kind? lsp.WatchKind + +---Represents a diagnostic, such as a compiler error or warning. Diagnostic objects +---are only valid in the scope of a resource. +---@class lsp.Diagnostic +---The range at which the message applies +---@field range lsp.Range +---The diagnostic's severity. Can be omitted. If omitted it is up to the +---client to interpret diagnostics as error, warning, info or hint. +---@field severity? lsp.DiagnosticSeverity +---The diagnostic's code, which usually appear in the user interface. +---@field code? integer|string +---An optional property to describe the error code. +---Requires the code field (above) to be present/not null. +--- +---@since 3.16.0 +---@field codeDescription? lsp.CodeDescription +---A human-readable string describing the source of this +---diagnostic, e.g. 'typescript' or 'super lint'. It usually +---appears in the user interface. +---@field source? string +---The diagnostic's message. It usually appears in the user interface +---@field message string +---Additional metadata about the diagnostic. +--- +---@since 3.15.0 +---@field tags? lsp.DiagnosticTag[] +---An array of related diagnostic information, e.g. when symbol-names within +---a scope collide all definitions can be marked via this property. +---@field relatedInformation? lsp.DiagnosticRelatedInformation[] +---A data entry field that is preserved between a `textDocument/publishDiagnostics` +---notification and `textDocument/codeAction` request. +--- +---@since 3.16.0 +---@field data? lsp.LSPAny + +---Contains additional information about the context in which a completion request is triggered. +---@class lsp.CompletionContext +---How the completion was triggered. +---@field triggerKind lsp.CompletionTriggerKind +---The trigger character (a single character) that has trigger code complete. +---Is undefined if `triggerKind !== CompletionTriggerKind.TriggerCharacter` +---@field triggerCharacter? string + +---Additional details for a completion item label. +--- +---@since 3.17.0 +---@class lsp.CompletionItemLabelDetails +---An optional string which is rendered less prominently directly after {@link CompletionItem.label label}, +---without any spacing. Should be used for function signatures and type annotations. +---@field detail? string +---An optional string which is rendered less prominently after {@link CompletionItem.detail}. Should be used +---for fully qualified names and file paths. +---@field description? string + +---A special text edit to provide an insert and a replace operation. +--- +---@since 3.16.0 +---@class lsp.InsertReplaceEdit +---The string to be inserted. +---@field newText string +---The range if the insert is requested +---@field insert lsp.Range +---The range if the replace is requested. +---@field replace lsp.Range + +---Completion options. +---@class lsp.CompletionOptions +---Most tools trigger completion request automatically without explicitly requesting +---it using a keyboard shortcut (e.g. Ctrl+Space). Typically they do so when the user +---starts to type an identifier. For example if the user types `c` in a JavaScript file +---code complete will automatically pop up present `console` besides others as a +---completion item. Characters that make up identifiers don't need to be listed here. +--- +---If code complete should automatically be trigger on characters not being valid inside +---an identifier (for example `.` in JavaScript) list them in `triggerCharacters`. +---@field triggerCharacters? string[] +---The list of all possible characters that commit a completion. This field can be used +---if clients don't support individual commit characters per completion item. See +---`ClientCapabilities.textDocument.completion.completionItem.commitCharactersSupport` +--- +---If a server provides both `allCommitCharacters` and commit characters on an individual +---completion item the ones on the completion item win. +--- +---@since 3.2.0 +---@field allCommitCharacters? string[] +---The server provides support to resolve additional +---information for a completion item. +---@field resolveProvider? boolean +---The server supports the following `CompletionItem` specific +---capabilities. +--- +---@since 3.17.0 +---@field completionItem? anonym13 + +---Hover options. +---@class lsp.HoverOptions + +---Additional information about the context in which a signature help request was triggered. +--- +---@since 3.15.0 +---@class lsp.SignatureHelpContext +---Action that caused signature help to be triggered. +---@field triggerKind lsp.SignatureHelpTriggerKind +---Character that caused signature help to be triggered. +--- +---This is undefined when `triggerKind !== SignatureHelpTriggerKind.TriggerCharacter` +---@field triggerCharacter? string +---`true` if signature help was already showing when it was triggered. +--- +---Retriggers occurs when the signature help is already active and can be caused by actions such as +---typing a trigger character, a cursor move, or document content changes. +---@field isRetrigger boolean +---The currently active `SignatureHelp`. +--- +---The `activeSignatureHelp` has its `SignatureHelp.activeSignature` field updated based on +---the user navigating through available signatures. +---@field activeSignatureHelp? lsp.SignatureHelp + +---Represents the signature of something callable. A signature +---can have a label, like a function-name, a doc-comment, and +---a set of parameters. +---@class lsp.SignatureInformation +---The label of this signature. Will be shown in +---the UI. +---@field label string +---The human-readable doc-comment of this signature. Will be shown +---in the UI but can be omitted. +---@field documentation? string|lsp.MarkupContent +---The parameters of this signature. +---@field parameters? lsp.ParameterInformation[] +---The index of the active parameter. +--- +---If provided, this is used in place of `SignatureHelp.activeParameter`. +--- +---@since 3.16.0 +---@field activeParameter? uinteger + +---Server Capabilities for a {@link SignatureHelpRequest}. +---@class lsp.SignatureHelpOptions +---List of characters that trigger signature help automatically. +---@field triggerCharacters? string[] +---List of characters that re-trigger signature help. +--- +---These trigger characters are only active when signature help is already showing. All trigger characters +---are also counted as re-trigger characters. +--- +---@since 3.15.0 +---@field retriggerCharacters? string[] + +---Server Capabilities for a {@link DefinitionRequest}. +---@class lsp.DefinitionOptions + +---Value-object that contains additional information when +---requesting references. +---@class lsp.ReferenceContext +---Include the declaration of the current symbol. +---@field includeDeclaration boolean + +---Reference options. +---@class lsp.ReferenceOptions + +---Provider options for a {@link DocumentHighlightRequest}. +---@class lsp.DocumentHighlightOptions + +---A base for all symbol information. +---@class lsp.BaseSymbolInformation +---The name of this symbol. +---@field name string +---The kind of this symbol. +---@field kind lsp.SymbolKind +---Tags for this symbol. +--- +---@since 3.16.0 +---@field tags? lsp.SymbolTag[] +---The name of the symbol containing this symbol. This information is for +---user interface purposes (e.g. to render a qualifier in the user interface +---if necessary). It can't be used to re-infer a hierarchy for the document +---symbols. +---@field containerName? string + +---Provider options for a {@link DocumentSymbolRequest}. +---@class lsp.DocumentSymbolOptions +---A human-readable string that is shown when multiple outlines trees +---are shown for the same document. +--- +---@since 3.16.0 +---@field label? string + +---Contains additional diagnostic information about the context in which +---a {@link CodeActionProvider.provideCodeActions code action} is run. +---@class lsp.CodeActionContext +---An array of diagnostics known on the client side overlapping the range provided to the +---`textDocument/codeAction` request. They are provided so that the server knows which +---errors are currently presented to the user for the given range. There is no guarantee +---that these accurately reflect the error state of the resource. The primary parameter +---to compute code actions is the provided range. +---@field diagnostics lsp.Diagnostic[] +---Requested kind of actions to return. +--- +---Actions not of this kind are filtered out by the client before being shown. So servers +---can omit computing them. +---@field only? lsp.CodeActionKind[] +---The reason why code actions were requested. +--- +---@since 3.17.0 +---@field triggerKind? lsp.CodeActionTriggerKind + +---Provider options for a {@link CodeActionRequest}. +---@class lsp.CodeActionOptions +---CodeActionKinds that this server may return. +--- +---The list of kinds may be generic, such as `CodeActionKind.Refactor`, or the server +---may list out every specific kind they provide. +---@field codeActionKinds? lsp.CodeActionKind[] +---The server provides support to resolve additional +---information for a code action. +--- +---@since 3.16.0 +---@field resolveProvider? boolean + +---Server capabilities for a {@link WorkspaceSymbolRequest}. +---@class lsp.WorkspaceSymbolOptions +---The server provides support to resolve additional +---information for a workspace symbol. +--- +---@since 3.17.0 +---@field resolveProvider? boolean + +---Code Lens provider options of a {@link CodeLensRequest}. +---@class lsp.CodeLensOptions +---Code lens has a resolve provider as well. +---@field resolveProvider? boolean + +---Provider options for a {@link DocumentLinkRequest}. +---@class lsp.DocumentLinkOptions +---Document links have a resolve provider as well. +---@field resolveProvider? boolean + +---Value-object describing what options formatting should use. +---@class lsp.FormattingOptions +---Size of a tab in spaces. +---@field tabSize uinteger +---Prefer spaces over tabs. +---@field insertSpaces boolean +---Trim trailing whitespace on a line. +--- +---@since 3.15.0 +---@field trimTrailingWhitespace? boolean +---Insert a newline character at the end of the file if one does not exist. +--- +---@since 3.15.0 +---@field insertFinalNewline? boolean +---Trim all newlines after the final newline at the end of the file. +--- +---@since 3.15.0 +---@field trimFinalNewlines? boolean + +---Provider options for a {@link DocumentFormattingRequest}. +---@class lsp.DocumentFormattingOptions + +---Provider options for a {@link DocumentRangeFormattingRequest}. +---@class lsp.DocumentRangeFormattingOptions + +---Provider options for a {@link DocumentOnTypeFormattingRequest}. +---@class lsp.DocumentOnTypeFormattingOptions +---A character on which formatting should be triggered, like `{`. +---@field firstTriggerCharacter string +---More trigger characters. +---@field moreTriggerCharacter? string[] + +---Provider options for a {@link RenameRequest}. +---@class lsp.RenameOptions +---Renames should be checked and tested before being executed. +--- +---@since version 3.12.0 +---@field prepareProvider? boolean + +---The server capabilities of a {@link ExecuteCommandRequest}. +---@class lsp.ExecuteCommandOptions +---The commands to be executed on the server +---@field commands string[] + +---@since 3.16.0 +---@class lsp.SemanticTokensLegend +---The token types a server uses. +---@field tokenTypes string[] +---The token modifiers a server uses. +---@field tokenModifiers string[] + +---A text document identifier to optionally denote a specific version of a text document. +---@class lsp.OptionalVersionedTextDocumentIdentifier: lsp.TextDocumentIdentifier +---The version number of this document. If a versioned text document identifier +---is sent from the server to the client and the file is not open in the editor +---(the server has not received an open notification before) the server can send +---`null` to indicate that the version is unknown and the content on disk is the +---truth (as specified with document content ownership). +---@field version integer|lsp.null + +---A special text edit with an additional change annotation. +--- +---@since 3.16.0. +---@class lsp.AnnotatedTextEdit: lsp.TextEdit +---The actual identifier of the change annotation +---@field annotationId lsp.ChangeAnnotationIdentifier + +---A generic resource operation. +---@class lsp.ResourceOperation +---The resource operation kind. +---@field kind string +---An optional annotation identifier describing the operation. +--- +---@since 3.16.0 +---@field annotationId? lsp.ChangeAnnotationIdentifier + +---Options to create a file. +---@class lsp.CreateFileOptions +---Overwrite existing file. Overwrite wins over `ignoreIfExists` +---@field overwrite? boolean +---Ignore if exists. +---@field ignoreIfExists? boolean + +---Rename file options +---@class lsp.RenameFileOptions +---Overwrite target if existing. Overwrite wins over `ignoreIfExists` +---@field overwrite? boolean +---Ignores if target exists. +---@field ignoreIfExists? boolean + +---Delete file options +---@class lsp.DeleteFileOptions +---Delete the content recursively if a folder is denoted. +---@field recursive? boolean +---Ignore the operation if the file doesn't exist. +---@field ignoreIfNotExists? boolean + +---A pattern to describe in which file operation requests or notifications +---the server is interested in receiving. +--- +---@since 3.16.0 +---@class lsp.FileOperationPattern +---The glob pattern to match. Glob patterns can have the following syntax: +---- `*` to match one or more characters in a path segment +---- `?` to match on one character in a path segment +---- `**` to match any number of path segments, including none +---- `{}` to group sub patterns into an OR expression. (e.g. `**/*.{ts,js}` matches all TypeScript and JavaScript files) +---- `[]` to declare a range of characters to match in a path segment (e.g., `example.[0-9]` to match on `example.0`, `example.1`, …) +---- `[!...]` to negate a range of characters to match in a path segment (e.g., `example.[!0-9]` to match on `example.a`, `example.b`, but not `example.0`) +---@field glob string +---Whether to match files or folders with this pattern. +--- +---Matches both if undefined. +---@field matches? lsp.FileOperationPatternKind +---Additional options used during matching. +---@field options? lsp.FileOperationPatternOptions + +---A full document diagnostic report for a workspace diagnostic result. +--- +---@since 3.17.0 +---@class lsp.WorkspaceFullDocumentDiagnosticReport: lsp.FullDocumentDiagnosticReport +---The URI for which diagnostic information is reported. +---@field uri lsp.DocumentUri +---The version number for which the diagnostics are reported. +---If the document is not marked as open `null` can be provided. +---@field version integer|lsp.null + +---An unchanged document diagnostic report for a workspace diagnostic result. +--- +---@since 3.17.0 +---@class lsp.WorkspaceUnchangedDocumentDiagnosticReport: lsp.UnchangedDocumentDiagnosticReport +---The URI for which diagnostic information is reported. +---@field uri lsp.DocumentUri +---The version number for which the diagnostics are reported. +---If the document is not marked as open `null` can be provided. +---@field version integer|lsp.null + +---A notebook cell. +--- +---A cell's document URI must be unique across ALL notebook +---cells and can therefore be used to uniquely identify a +---notebook cell or the cell's text document. +--- +---@since 3.17.0 +---@class lsp.NotebookCell +---The cell's kind +---@field kind lsp.NotebookCellKind +---The URI of the cell's text document +---content. +---@field document lsp.DocumentUri +---Additional metadata stored with the cell. +--- +---Note: should always be an object literal (e.g. LSPObject) +---@field metadata? lsp.LSPObject +---Additional execution summary information +---if supported by the client. +---@field executionSummary? lsp.ExecutionSummary + +---A change describing how to move a `NotebookCell` +---array from state S to S'. +--- +---@since 3.17.0 +---@class lsp.NotebookCellArrayChange +---The start oftest of the cell that changed. +---@field start uinteger +---The deleted cells +---@field deleteCount uinteger +---The new cells, if any +---@field cells? lsp.NotebookCell[] + +---Describes the currently selected completion item. +--- +---@since 3.18.0 +---@proposed +---@class lsp.SelectedCompletionInfo +---The range that will be replaced if this completion item is accepted. +---@field range lsp.Range +---The text the range will be replaced with if this completion is accepted. +---@field text string + +---Defines the capabilities provided by the client. +---@class lsp.ClientCapabilities +---Workspace specific client capabilities. +---@field workspace? lsp.WorkspaceClientCapabilities +---Text document specific client capabilities. +---@field textDocument? lsp.TextDocumentClientCapabilities +---Capabilities specific to the notebook document support. +--- +---@since 3.17.0 +---@field notebookDocument? lsp.NotebookDocumentClientCapabilities +---Window specific client capabilities. +---@field window? lsp.WindowClientCapabilities +---General client capabilities. +--- +---@since 3.16.0 +---@field general? lsp.GeneralClientCapabilities +---Experimental client capabilities. +---@field experimental? lsp.LSPAny + +---@class lsp.TextDocumentSyncOptions +---Open and close notifications are sent to the server. If omitted open close notification should not +---be sent. +---@field openClose? boolean +---Change notifications are sent to the server. See TextDocumentSyncKind.None, TextDocumentSyncKind.Full +---and TextDocumentSyncKind.Incremental. If omitted it defaults to TextDocumentSyncKind.None. +---@field change? lsp.TextDocumentSyncKind +---If present will save notifications are sent to the server. If omitted the notification should not be +---sent. +---@field willSave? boolean +---If present will save wait until requests are sent to the server. If omitted the request should not be +---sent. +---@field willSaveWaitUntil? boolean +---If present save notifications are sent to the server. If omitted the notification should not be +---sent. +---@field save? boolean|lsp.SaveOptions + +---Options specific to a notebook plus its cells +---to be synced to the server. +--- +---If a selector provides a notebook document +---filter but no cell selector all cells of a +---matching notebook document will be synced. +--- +---If a selector provides no notebook document +---filter but only a cell selector all notebook +---document that contain at least one matching +---cell will be synced. +--- +---@since 3.17.0 +---@class lsp.NotebookDocumentSyncOptions +---The notebooks to be synced +---@field notebookSelector anonym15|anonym17[] +---Whether save notification should be forwarded to +---the server. Will only be honored if mode === `notebook`. +---@field save? boolean + +---Registration options specific to a notebook. +--- +---@since 3.17.0 +---@class lsp.NotebookDocumentSyncRegistrationOptions: lsp.NotebookDocumentSyncOptions, lsp.StaticRegistrationOptions + +---@class lsp.WorkspaceFoldersServerCapabilities +---The server has support for workspace folders +---@field supported? boolean +---Whether the server wants to receive workspace folder +---change notifications. +--- +---If a string is provided the string is treated as an ID +---under which the notification is registered on the client +---side. The ID can be used to unregister for these events +---using the `client/unregisterCapability` request. +---@field changeNotifications? string|boolean + +---Options for notifications/requests for user operations on files. +--- +---@since 3.16.0 +---@class lsp.FileOperationOptions +---The server is interested in receiving didCreateFiles notifications. +---@field didCreate? lsp.FileOperationRegistrationOptions +---The server is interested in receiving willCreateFiles requests. +---@field willCreate? lsp.FileOperationRegistrationOptions +---The server is interested in receiving didRenameFiles notifications. +---@field didRename? lsp.FileOperationRegistrationOptions +---The server is interested in receiving willRenameFiles requests. +---@field willRename? lsp.FileOperationRegistrationOptions +---The server is interested in receiving didDeleteFiles file notifications. +---@field didDelete? lsp.FileOperationRegistrationOptions +---The server is interested in receiving willDeleteFiles file requests. +---@field willDelete? lsp.FileOperationRegistrationOptions + +---Structure to capture a description for an error code. +--- +---@since 3.16.0 +---@class lsp.CodeDescription +---An URI to open with more information about the diagnostic error. +---@field href lsp.URI + +---Represents a related message and source code location for a diagnostic. This should be +---used to point to code locations that cause or related to a diagnostics, e.g when duplicating +---a symbol in a scope. +---@class lsp.DiagnosticRelatedInformation +---The location of this related diagnostic information. +---@field location lsp.Location +---The message of this related diagnostic information. +---@field message string + +---Represents a parameter of a callable-signature. A parameter can +---have a label and a doc-comment. +---@class lsp.ParameterInformation +---The label of this parameter information. +--- +---Either a string or an inclusive start and exclusive end offsets within its containing +---signature label. (see SignatureInformation.label). The offsets are based on a UTF-16 +---string representation as `Position` and `Range` does. +--- +---*Note*: a label of type string should be a substring of its containing signature label. +---Its intended use case is to highlight the parameter label part in the `SignatureInformation.label`. +---@field label string|{ [1]: uinteger, [2]: uinteger } +---The human-readable doc-comment of this parameter. Will be shown +---in the UI but can be omitted. +---@field documentation? string|lsp.MarkupContent + +---A notebook cell text document filter denotes a cell text +---document by different properties. +--- +---@since 3.17.0 +---@class lsp.NotebookCellTextDocumentFilter +---A filter that matches against the notebook +---containing the notebook cell. If a string +---value is provided it matches against the +---notebook type. '*' matches every notebook. +---@field notebook string|lsp.NotebookDocumentFilter +---A language id like `python`. +--- +---Will be matched against the language id of the +---notebook cell document. '*' matches every language. +---@field language? string + +---Matching options for the file operation pattern. +--- +---@since 3.16.0 +---@class lsp.FileOperationPatternOptions +---The pattern should be matched ignoring casing. +---@field ignoreCase? boolean + +---@class lsp.ExecutionSummary +---A strict monotonically increasing value +---indicating the execution order of a cell +---inside a notebook. +---@field executionOrder uinteger +---Whether the execution was successful or +---not if known by the client. +---@field success? boolean + +---Workspace specific client capabilities. +---@class lsp.WorkspaceClientCapabilities +---The client supports applying batch edits +---to the workspace by supporting the request +---'workspace/applyEdit' +---@field applyEdit? boolean +---Capabilities specific to `WorkspaceEdit`s. +---@field workspaceEdit? lsp.WorkspaceEditClientCapabilities +---Capabilities specific to the `workspace/didChangeConfiguration` notification. +---@field didChangeConfiguration? lsp.DidChangeConfigurationClientCapabilities +---Capabilities specific to the `workspace/didChangeWatchedFiles` notification. +---@field didChangeWatchedFiles? lsp.DidChangeWatchedFilesClientCapabilities +---Capabilities specific to the `workspace/symbol` request. +---@field symbol? lsp.WorkspaceSymbolClientCapabilities +---Capabilities specific to the `workspace/executeCommand` request. +---@field executeCommand? lsp.ExecuteCommandClientCapabilities +---The client has support for workspace folders. +--- +---@since 3.6.0 +---@field workspaceFolders? boolean +---The client supports `workspace/configuration` requests. +--- +---@since 3.6.0 +---@field configuration? boolean +---Capabilities specific to the semantic token requests scoped to the +---workspace. +--- +---@since 3.16.0. +---@field semanticTokens? lsp.SemanticTokensWorkspaceClientCapabilities +---Capabilities specific to the code lens requests scoped to the +---workspace. +--- +---@since 3.16.0. +---@field codeLens? lsp.CodeLensWorkspaceClientCapabilities +---The client has support for file notifications/requests for user operations on files. +--- +---Since 3.16.0 +---@field fileOperations? lsp.FileOperationClientCapabilities +---Capabilities specific to the inline values requests scoped to the +---workspace. +--- +---@since 3.17.0. +---@field inlineValue? lsp.InlineValueWorkspaceClientCapabilities +---Capabilities specific to the inlay hint requests scoped to the +---workspace. +--- +---@since 3.17.0. +---@field inlayHint? lsp.InlayHintWorkspaceClientCapabilities +---Capabilities specific to the diagnostic requests scoped to the +---workspace. +--- +---@since 3.17.0. +---@field diagnostics? lsp.DiagnosticWorkspaceClientCapabilities + +---Text document specific client capabilities. +---@class lsp.TextDocumentClientCapabilities +---Defines which synchronization capabilities the client supports. +---@field synchronization? lsp.TextDocumentSyncClientCapabilities +---Capabilities specific to the `textDocument/completion` request. +---@field completion? lsp.CompletionClientCapabilities +---Capabilities specific to the `textDocument/hover` request. +---@field hover? lsp.HoverClientCapabilities +---Capabilities specific to the `textDocument/signatureHelp` request. +---@field signatureHelp? lsp.SignatureHelpClientCapabilities +---Capabilities specific to the `textDocument/declaration` request. +--- +---@since 3.14.0 +---@field declaration? lsp.DeclarationClientCapabilities +---Capabilities specific to the `textDocument/definition` request. +---@field definition? lsp.DefinitionClientCapabilities +---Capabilities specific to the `textDocument/typeDefinition` request. +--- +---@since 3.6.0 +---@field typeDefinition? lsp.TypeDefinitionClientCapabilities +---Capabilities specific to the `textDocument/implementation` request. +--- +---@since 3.6.0 +---@field implementation? lsp.ImplementationClientCapabilities +---Capabilities specific to the `textDocument/references` request. +---@field references? lsp.ReferenceClientCapabilities +---Capabilities specific to the `textDocument/documentHighlight` request. +---@field documentHighlight? lsp.DocumentHighlightClientCapabilities +---Capabilities specific to the `textDocument/documentSymbol` request. +---@field documentSymbol? lsp.DocumentSymbolClientCapabilities +---Capabilities specific to the `textDocument/codeAction` request. +---@field codeAction? lsp.CodeActionClientCapabilities +---Capabilities specific to the `textDocument/codeLens` request. +---@field codeLens? lsp.CodeLensClientCapabilities +---Capabilities specific to the `textDocument/documentLink` request. +---@field documentLink? lsp.DocumentLinkClientCapabilities +---Capabilities specific to the `textDocument/documentColor` and the +---`textDocument/colorPresentation` request. +--- +---@since 3.6.0 +---@field colorProvider? lsp.DocumentColorClientCapabilities +---Capabilities specific to the `textDocument/formatting` request. +---@field formatting? lsp.DocumentFormattingClientCapabilities +---Capabilities specific to the `textDocument/rangeFormatting` request. +---@field rangeFormatting? lsp.DocumentRangeFormattingClientCapabilities +---Capabilities specific to the `textDocument/onTypeFormatting` request. +---@field onTypeFormatting? lsp.DocumentOnTypeFormattingClientCapabilities +---Capabilities specific to the `textDocument/rename` request. +---@field rename? lsp.RenameClientCapabilities +---Capabilities specific to the `textDocument/foldingRange` request. +--- +---@since 3.10.0 +---@field foldingRange? lsp.FoldingRangeClientCapabilities +---Capabilities specific to the `textDocument/selectionRange` request. +--- +---@since 3.15.0 +---@field selectionRange? lsp.SelectionRangeClientCapabilities +---Capabilities specific to the `textDocument/publishDiagnostics` notification. +---@field publishDiagnostics? lsp.PublishDiagnosticsClientCapabilities +---Capabilities specific to the various call hierarchy requests. +--- +---@since 3.16.0 +---@field callHierarchy? lsp.CallHierarchyClientCapabilities +---Capabilities specific to the various semantic token request. +--- +---@since 3.16.0 +---@field semanticTokens? lsp.SemanticTokensClientCapabilities +---Capabilities specific to the `textDocument/linkedEditingRange` request. +--- +---@since 3.16.0 +---@field linkedEditingRange? lsp.LinkedEditingRangeClientCapabilities +---Client capabilities specific to the `textDocument/moniker` request. +--- +---@since 3.16.0 +---@field moniker? lsp.MonikerClientCapabilities +---Capabilities specific to the various type hierarchy requests. +--- +---@since 3.17.0 +---@field typeHierarchy? lsp.TypeHierarchyClientCapabilities +---Capabilities specific to the `textDocument/inlineValue` request. +--- +---@since 3.17.0 +---@field inlineValue? lsp.InlineValueClientCapabilities +---Capabilities specific to the `textDocument/inlayHint` request. +--- +---@since 3.17.0 +---@field inlayHint? lsp.InlayHintClientCapabilities +---Capabilities specific to the diagnostic pull model. +--- +---@since 3.17.0 +---@field diagnostic? lsp.DiagnosticClientCapabilities +---Client capabilities specific to inline completions. +--- +---@since 3.18.0 +---@proposed +---@field inlineCompletion? lsp.InlineCompletionClientCapabilities + +---Capabilities specific to the notebook document support. +--- +---@since 3.17.0 +---@class lsp.NotebookDocumentClientCapabilities +---Capabilities specific to notebook document synchronization +--- +---@since 3.17.0 +---@field synchronization lsp.NotebookDocumentSyncClientCapabilities + +---@class lsp.WindowClientCapabilities +---It indicates whether the client supports server initiated +---progress using the `window/workDoneProgress/create` request. +--- +---The capability also controls Whether client supports handling +---of progress notifications. If set servers are allowed to report a +---`workDoneProgress` property in the request specific server +---capabilities. +--- +---@since 3.15.0 +---@field workDoneProgress? boolean +---Capabilities specific to the showMessage request. +--- +---@since 3.16.0 +---@field showMessage? lsp.ShowMessageRequestClientCapabilities +---Capabilities specific to the showDocument request. +--- +---@since 3.16.0 +---@field showDocument? lsp.ShowDocumentClientCapabilities + +---General client capabilities. +--- +---@since 3.16.0 +---@class lsp.GeneralClientCapabilities +---Client capability that signals how the client +---handles stale requests (e.g. a request +---for which the client will not process the response +---anymore since the information is outdated). +--- +---@since 3.17.0 +---@field staleRequestSupport? anonym18 +---Client capabilities specific to regular expressions. +--- +---@since 3.16.0 +---@field regularExpressions? lsp.RegularExpressionsClientCapabilities +---Client capabilities specific to the client's markdown parser. +--- +---@since 3.16.0 +---@field markdown? lsp.MarkdownClientCapabilities +---The position encodings supported by the client. Client and server +---have to agree on the same position encoding to ensure that offsets +---(e.g. character position in a line) are interpreted the same on both +---sides. +--- +---To keep the protocol backwards compatible the following applies: if +---the value 'utf-16' is missing from the array of position encodings +---servers can assume that the client supports UTF-16. UTF-16 is +---therefore a mandatory encoding. +--- +---If omitted it defaults to ['utf-16']. +--- +---Implementation considerations: since the conversion from one encoding +---into another requires the content of the file / line the conversion +---is best done where the file is read which is usually on the server +---side. +--- +---@since 3.17.0 +---@field positionEncodings? lsp.PositionEncodingKind[] + +---A relative pattern is a helper to construct glob patterns that are matched +---relatively to a base URI. The common value for a `baseUri` is a workspace +---folder root, but it can be another absolute URI as well. +--- +---@since 3.17.0 +---@class lsp.RelativePattern +---A workspace folder or a base URI to which this pattern will be matched +---against relatively. +---@field baseUri lsp.WorkspaceFolder|lsp.URI +---The actual glob pattern; +---@field pattern lsp.Pattern + +---@class lsp.WorkspaceEditClientCapabilities +---The client supports versioned document changes in `WorkspaceEdit`s +---@field documentChanges? boolean +---The resource operations the client supports. Clients should at least +---support 'create', 'rename' and 'delete' files and folders. +--- +---@since 3.13.0 +---@field resourceOperations? lsp.ResourceOperationKind[] +---The failure handling strategy of a client if applying the workspace edit +---fails. +--- +---@since 3.13.0 +---@field failureHandling? lsp.FailureHandlingKind +---Whether the client normalizes line endings to the client specific +---setting. +---If set to `true` the client will normalize line ending characters +---in a workspace edit to the client-specified new line +---character. +--- +---@since 3.16.0 +---@field normalizesLineEndings? boolean +---Whether the client in general supports change annotations on text edits, +---create file, rename file and delete file changes. +--- +---@since 3.16.0 +---@field changeAnnotationSupport? anonym19 + +---@class lsp.DidChangeConfigurationClientCapabilities +---Did change configuration notification supports dynamic registration. +---@field dynamicRegistration? boolean + +---@class lsp.DidChangeWatchedFilesClientCapabilities +---Did change watched files notification supports dynamic registration. Please note +---that the current protocol doesn't support static configuration for file changes +---from the server side. +---@field dynamicRegistration? boolean +---Whether the client has support for {@link RelativePattern relative pattern} +---or not. +--- +---@since 3.17.0 +---@field relativePatternSupport? boolean + +---Client capabilities for a {@link WorkspaceSymbolRequest}. +---@class lsp.WorkspaceSymbolClientCapabilities +---Symbol request supports dynamic registration. +---@field dynamicRegistration? boolean +---Specific capabilities for the `SymbolKind` in the `workspace/symbol` request. +---@field symbolKind? anonym20 +---The client supports tags on `SymbolInformation`. +---Clients supporting tags have to handle unknown tags gracefully. +--- +---@since 3.16.0 +---@field tagSupport? anonym21 +---The client support partial workspace symbols. The client will send the +---request `workspaceSymbol/resolve` to the server to resolve additional +---properties. +--- +---@since 3.17.0 +---@field resolveSupport? anonym22 + +---The client capabilities of a {@link ExecuteCommandRequest}. +---@class lsp.ExecuteCommandClientCapabilities +---Execute command supports dynamic registration. +---@field dynamicRegistration? boolean + +---@since 3.16.0 +---@class lsp.SemanticTokensWorkspaceClientCapabilities +---Whether the client implementation supports a refresh request sent from +---the server to the client. +--- +---Note that this event is global and will force the client to refresh all +---semantic tokens currently shown. It should be used with absolute care +---and is useful for situation where a server for example detects a project +---wide change that requires such a calculation. +---@field refreshSupport? boolean + +---@since 3.16.0 +---@class lsp.CodeLensWorkspaceClientCapabilities +---Whether the client implementation supports a refresh request sent from the +---server to the client. +--- +---Note that this event is global and will force the client to refresh all +---code lenses currently shown. It should be used with absolute care and is +---useful for situation where a server for example detect a project wide +---change that requires such a calculation. +---@field refreshSupport? boolean + +---Capabilities relating to events from file operations by the user in the client. +--- +---These events do not come from the file system, they come from user operations +---like renaming a file in the UI. +--- +---@since 3.16.0 +---@class lsp.FileOperationClientCapabilities +---Whether the client supports dynamic registration for file requests/notifications. +---@field dynamicRegistration? boolean +---The client has support for sending didCreateFiles notifications. +---@field didCreate? boolean +---The client has support for sending willCreateFiles requests. +---@field willCreate? boolean +---The client has support for sending didRenameFiles notifications. +---@field didRename? boolean +---The client has support for sending willRenameFiles requests. +---@field willRename? boolean +---The client has support for sending didDeleteFiles notifications. +---@field didDelete? boolean +---The client has support for sending willDeleteFiles requests. +---@field willDelete? boolean + +---Client workspace capabilities specific to inline values. +--- +---@since 3.17.0 +---@class lsp.InlineValueWorkspaceClientCapabilities +---Whether the client implementation supports a refresh request sent from the +---server to the client. +--- +---Note that this event is global and will force the client to refresh all +---inline values currently shown. It should be used with absolute care and is +---useful for situation where a server for example detects a project wide +---change that requires such a calculation. +---@field refreshSupport? boolean + +---Client workspace capabilities specific to inlay hints. +--- +---@since 3.17.0 +---@class lsp.InlayHintWorkspaceClientCapabilities +---Whether the client implementation supports a refresh request sent from +---the server to the client. +--- +---Note that this event is global and will force the client to refresh all +---inlay hints currently shown. It should be used with absolute care and +---is useful for situation where a server for example detects a project wide +---change that requires such a calculation. +---@field refreshSupport? boolean + +---Workspace client capabilities specific to diagnostic pull requests. +--- +---@since 3.17.0 +---@class lsp.DiagnosticWorkspaceClientCapabilities +---Whether the client implementation supports a refresh request sent from +---the server to the client. +--- +---Note that this event is global and will force the client to refresh all +---pulled diagnostics currently shown. It should be used with absolute care and +---is useful for situation where a server for example detects a project wide +---change that requires such a calculation. +---@field refreshSupport? boolean + +---@class lsp.TextDocumentSyncClientCapabilities +---Whether text document synchronization supports dynamic registration. +---@field dynamicRegistration? boolean +---The client supports sending will save notifications. +---@field willSave? boolean +---The client supports sending a will save request and +---waits for a response providing text edits which will +---be applied to the document before it is saved. +---@field willSaveWaitUntil? boolean +---The client supports did save notifications. +---@field didSave? boolean + +---Completion client capabilities +---@class lsp.CompletionClientCapabilities +---Whether completion supports dynamic registration. +---@field dynamicRegistration? boolean +---The client supports the following `CompletionItem` specific +---capabilities. +---@field completionItem? anonym26 +---@field completionItemKind? anonym27 +---Defines how the client handles whitespace and indentation +---when accepting a completion item that uses multi line +---text in either `insertText` or `textEdit`. +--- +---@since 3.17.0 +---@field insertTextMode? lsp.InsertTextMode +---The client supports to send additional context information for a +---`textDocument/completion` request. +---@field contextSupport? boolean +---The client supports the following `CompletionList` specific +---capabilities. +--- +---@since 3.17.0 +---@field completionList? anonym28 + +---@class lsp.HoverClientCapabilities +---Whether hover supports dynamic registration. +---@field dynamicRegistration? boolean +---Client supports the following content formats for the content +---property. The order describes the preferred format of the client. +---@field contentFormat? lsp.MarkupKind[] + +---Client Capabilities for a {@link SignatureHelpRequest}. +---@class lsp.SignatureHelpClientCapabilities +---Whether signature help supports dynamic registration. +---@field dynamicRegistration? boolean +---The client supports the following `SignatureInformation` +---specific properties. +---@field signatureInformation? anonym30 +---The client supports to send additional context information for a +---`textDocument/signatureHelp` request. A client that opts into +---contextSupport will also support the `retriggerCharacters` on +---`SignatureHelpOptions`. +--- +---@since 3.15.0 +---@field contextSupport? boolean + +---@since 3.14.0 +---@class lsp.DeclarationClientCapabilities +---Whether declaration supports dynamic registration. If this is set to `true` +---the client supports the new `DeclarationRegistrationOptions` return value +---for the corresponding server capability as well. +---@field dynamicRegistration? boolean +---The client supports additional metadata in the form of declaration links. +---@field linkSupport? boolean + +---Client Capabilities for a {@link DefinitionRequest}. +---@class lsp.DefinitionClientCapabilities +---Whether definition supports dynamic registration. +---@field dynamicRegistration? boolean +---The client supports additional metadata in the form of definition links. +--- +---@since 3.14.0 +---@field linkSupport? boolean + +---Since 3.6.0 +---@class lsp.TypeDefinitionClientCapabilities +---Whether implementation supports dynamic registration. If this is set to `true` +---the client supports the new `TypeDefinitionRegistrationOptions` return value +---for the corresponding server capability as well. +---@field dynamicRegistration? boolean +---The client supports additional metadata in the form of definition links. +--- +---Since 3.14.0 +---@field linkSupport? boolean + +---@since 3.6.0 +---@class lsp.ImplementationClientCapabilities +---Whether implementation supports dynamic registration. If this is set to `true` +---the client supports the new `ImplementationRegistrationOptions` return value +---for the corresponding server capability as well. +---@field dynamicRegistration? boolean +---The client supports additional metadata in the form of definition links. +--- +---@since 3.14.0 +---@field linkSupport? boolean + +---Client Capabilities for a {@link ReferencesRequest}. +---@class lsp.ReferenceClientCapabilities +---Whether references supports dynamic registration. +---@field dynamicRegistration? boolean + +---Client Capabilities for a {@link DocumentHighlightRequest}. +---@class lsp.DocumentHighlightClientCapabilities +---Whether document highlight supports dynamic registration. +---@field dynamicRegistration? boolean + +---Client Capabilities for a {@link DocumentSymbolRequest}. +---@class lsp.DocumentSymbolClientCapabilities +---Whether document symbol supports dynamic registration. +---@field dynamicRegistration? boolean +---Specific capabilities for the `SymbolKind` in the +---`textDocument/documentSymbol` request. +---@field symbolKind? anonym31 +---The client supports hierarchical document symbols. +---@field hierarchicalDocumentSymbolSupport? boolean +---The client supports tags on `SymbolInformation`. Tags are supported on +---`DocumentSymbol` if `hierarchicalDocumentSymbolSupport` is set to true. +---Clients supporting tags have to handle unknown tags gracefully. +--- +---@since 3.16.0 +---@field tagSupport? anonym32 +---The client supports an additional label presented in the UI when +---registering a document symbol provider. +--- +---@since 3.16.0 +---@field labelSupport? boolean + +---The Client Capabilities of a {@link CodeActionRequest}. +---@class lsp.CodeActionClientCapabilities +---Whether code action supports dynamic registration. +---@field dynamicRegistration? boolean +---The client support code action literals of type `CodeAction` as a valid +---response of the `textDocument/codeAction` request. If the property is not +---set the request can only return `Command` literals. +--- +---@since 3.8.0 +---@field codeActionLiteralSupport? anonym34 +---Whether code action supports the `isPreferred` property. +--- +---@since 3.15.0 +---@field isPreferredSupport? boolean +---Whether code action supports the `disabled` property. +--- +---@since 3.16.0 +---@field disabledSupport? boolean +---Whether code action supports the `data` property which is +---preserved between a `textDocument/codeAction` and a +---`codeAction/resolve` request. +--- +---@since 3.16.0 +---@field dataSupport? boolean +---Whether the client supports resolving additional code action +---properties via a separate `codeAction/resolve` request. +--- +---@since 3.16.0 +---@field resolveSupport? anonym35 +---Whether the client honors the change annotations in +---text edits and resource operations returned via the +---`CodeAction#edit` property by for example presenting +---the workspace edit in the user interface and asking +---for confirmation. +--- +---@since 3.16.0 +---@field honorsChangeAnnotations? boolean + +---The client capabilities of a {@link CodeLensRequest}. +---@class lsp.CodeLensClientCapabilities +---Whether code lens supports dynamic registration. +---@field dynamicRegistration? boolean + +---The client capabilities of a {@link DocumentLinkRequest}. +---@class lsp.DocumentLinkClientCapabilities +---Whether document link supports dynamic registration. +---@field dynamicRegistration? boolean +---Whether the client supports the `tooltip` property on `DocumentLink`. +--- +---@since 3.15.0 +---@field tooltipSupport? boolean + +---@class lsp.DocumentColorClientCapabilities +---Whether implementation supports dynamic registration. If this is set to `true` +---the client supports the new `DocumentColorRegistrationOptions` return value +---for the corresponding server capability as well. +---@field dynamicRegistration? boolean + +---Client capabilities of a {@link DocumentFormattingRequest}. +---@class lsp.DocumentFormattingClientCapabilities +---Whether formatting supports dynamic registration. +---@field dynamicRegistration? boolean + +---Client capabilities of a {@link DocumentRangeFormattingRequest}. +---@class lsp.DocumentRangeFormattingClientCapabilities +---Whether range formatting supports dynamic registration. +---@field dynamicRegistration? boolean + +---Client capabilities of a {@link DocumentOnTypeFormattingRequest}. +---@class lsp.DocumentOnTypeFormattingClientCapabilities +---Whether on type formatting supports dynamic registration. +---@field dynamicRegistration? boolean + +---@class lsp.RenameClientCapabilities +---Whether rename supports dynamic registration. +---@field dynamicRegistration? boolean +---Client supports testing for validity of rename operations +---before execution. +--- +---@since 3.12.0 +---@field prepareSupport? boolean +---Client supports the default behavior result. +--- +---The value indicates the default behavior used by the +---client. +--- +---@since 3.16.0 +---@field prepareSupportDefaultBehavior? lsp.PrepareSupportDefaultBehavior +---Whether the client honors the change annotations in +---text edits and resource operations returned via the +---rename request's workspace edit by for example presenting +---the workspace edit in the user interface and asking +---for confirmation. +--- +---@since 3.16.0 +---@field honorsChangeAnnotations? boolean + +---@class lsp.FoldingRangeClientCapabilities +---Whether implementation supports dynamic registration for folding range +---providers. If this is set to `true` the client supports the new +---`FoldingRangeRegistrationOptions` return value for the corresponding +---server capability as well. +---@field dynamicRegistration? boolean +---The maximum number of folding ranges that the client prefers to receive +---per document. The value serves as a hint, servers are free to follow the +---limit. +---@field rangeLimit? uinteger +---If set, the client signals that it only supports folding complete lines. +---If set, client will ignore specified `startCharacter` and `endCharacter` +---properties in a FoldingRange. +---@field lineFoldingOnly? boolean +---Specific options for the folding range kind. +--- +---@since 3.17.0 +---@field foldingRangeKind? anonym36 +---Specific options for the folding range. +--- +---@since 3.17.0 +---@field foldingRange? anonym37 + +---@class lsp.SelectionRangeClientCapabilities +---Whether implementation supports dynamic registration for selection range providers. If this is set to `true` +---the client supports the new `SelectionRangeRegistrationOptions` return value for the corresponding server +---capability as well. +---@field dynamicRegistration? boolean + +---The publish diagnostic client capabilities. +---@class lsp.PublishDiagnosticsClientCapabilities +---Whether the clients accepts diagnostics with related information. +---@field relatedInformation? boolean +---Client supports the tag property to provide meta data about a diagnostic. +---Clients supporting tags have to handle unknown tags gracefully. +--- +---@since 3.15.0 +---@field tagSupport? anonym38 +---Whether the client interprets the version property of the +---`textDocument/publishDiagnostics` notification's parameter. +--- +---@since 3.15.0 +---@field versionSupport? boolean +---Client supports a codeDescription property +--- +---@since 3.16.0 +---@field codeDescriptionSupport? boolean +---Whether code action supports the `data` property which is +---preserved between a `textDocument/publishDiagnostics` and +---`textDocument/codeAction` request. +--- +---@since 3.16.0 +---@field dataSupport? boolean + +---@since 3.16.0 +---@class lsp.CallHierarchyClientCapabilities +---Whether implementation supports dynamic registration. If this is set to `true` +---the client supports the new `(TextDocumentRegistrationOptions & StaticRegistrationOptions)` +---return value for the corresponding server capability as well. +---@field dynamicRegistration? boolean + +---@since 3.16.0 +---@class lsp.SemanticTokensClientCapabilities +---Whether implementation supports dynamic registration. If this is set to `true` +---the client supports the new `(TextDocumentRegistrationOptions & StaticRegistrationOptions)` +---return value for the corresponding server capability as well. +---@field dynamicRegistration? boolean +---Which requests the client supports and might send to the server +---depending on the server's capability. Please note that clients might not +---show semantic tokens or degrade some of the user experience if a range +---or full request is advertised by the client but not provided by the +---server. If for example the client capability `requests.full` and +---`request.range` are both set to true but the server only provides a +---range provider the client might not render a minimap correctly or might +---even decide to not show any semantic tokens at all. +---@field requests anonym41 +---The token types that the client supports. +---@field tokenTypes string[] +---The token modifiers that the client supports. +---@field tokenModifiers string[] +---The token formats the clients supports. +---@field formats lsp.TokenFormat[] +---Whether the client supports tokens that can overlap each other. +---@field overlappingTokenSupport? boolean +---Whether the client supports tokens that can span multiple lines. +---@field multilineTokenSupport? boolean +---Whether the client allows the server to actively cancel a +---semantic token request, e.g. supports returning +---LSPErrorCodes.ServerCancelled. If a server does the client +---needs to retrigger the request. +--- +---@since 3.17.0 +---@field serverCancelSupport? boolean +---Whether the client uses semantic tokens to augment existing +---syntax tokens. If set to `true` client side created syntax +---tokens and semantic tokens are both used for colorization. If +---set to `false` the client only uses the returned semantic tokens +---for colorization. +--- +---If the value is `undefined` then the client behavior is not +---specified. +--- +---@since 3.17.0 +---@field augmentsSyntaxTokens? boolean + +---Client capabilities for the linked editing range request. +--- +---@since 3.16.0 +---@class lsp.LinkedEditingRangeClientCapabilities +---Whether implementation supports dynamic registration. If this is set to `true` +---the client supports the new `(TextDocumentRegistrationOptions & StaticRegistrationOptions)` +---return value for the corresponding server capability as well. +---@field dynamicRegistration? boolean + +---Client capabilities specific to the moniker request. +--- +---@since 3.16.0 +---@class lsp.MonikerClientCapabilities +---Whether moniker supports dynamic registration. If this is set to `true` +---the client supports the new `MonikerRegistrationOptions` return value +---for the corresponding server capability as well. +---@field dynamicRegistration? boolean + +---@since 3.17.0 +---@class lsp.TypeHierarchyClientCapabilities +---Whether implementation supports dynamic registration. If this is set to `true` +---the client supports the new `(TextDocumentRegistrationOptions & StaticRegistrationOptions)` +---return value for the corresponding server capability as well. +---@field dynamicRegistration? boolean + +---Client capabilities specific to inline values. +--- +---@since 3.17.0 +---@class lsp.InlineValueClientCapabilities +---Whether implementation supports dynamic registration for inline value providers. +---@field dynamicRegistration? boolean + +---Inlay hint client capabilities. +--- +---@since 3.17.0 +---@class lsp.InlayHintClientCapabilities +---Whether inlay hints support dynamic registration. +---@field dynamicRegistration? boolean +---Indicates which properties a client can resolve lazily on an inlay +---hint. +---@field resolveSupport? anonym42 + +---Client capabilities specific to diagnostic pull requests. +--- +---@since 3.17.0 +---@class lsp.DiagnosticClientCapabilities +---Whether implementation supports dynamic registration. If this is set to `true` +---the client supports the new `(TextDocumentRegistrationOptions & StaticRegistrationOptions)` +---return value for the corresponding server capability as well. +---@field dynamicRegistration? boolean +---Whether the clients supports related documents for document diagnostic pulls. +---@field relatedDocumentSupport? boolean + +---Client capabilities specific to inline completions. +--- +---@since 3.18.0 +---@proposed +---@class lsp.InlineCompletionClientCapabilities +---Whether implementation supports dynamic registration for inline completion providers. +---@field dynamicRegistration? boolean + +---Notebook specific client capabilities. +--- +---@since 3.17.0 +---@class lsp.NotebookDocumentSyncClientCapabilities +---Whether implementation supports dynamic registration. If this is +---set to `true` the client supports the new +---`(TextDocumentRegistrationOptions & StaticRegistrationOptions)` +---return value for the corresponding server capability as well. +---@field dynamicRegistration? boolean +---The client supports sending execution summary data per cell. +---@field executionSummarySupport? boolean + +---Show message request client capabilities +---@class lsp.ShowMessageRequestClientCapabilities +---Capabilities specific to the `MessageActionItem` type. +---@field messageActionItem? anonym43 + +---Client capabilities for the showDocument request. +--- +---@since 3.16.0 +---@class lsp.ShowDocumentClientCapabilities +---The client has support for the showDocument +---request. +---@field support boolean + +---Client capabilities specific to regular expressions. +--- +---@since 3.16.0 +---@class lsp.RegularExpressionsClientCapabilities +---The engine's name. +---@field engine string +---The engine's version. +---@field version? string + +---Client capabilities specific to the used markdown parser. +--- +---@since 3.16.0 +---@class lsp.MarkdownClientCapabilities +---The name of the parser. +---@field parser string +---The version of the parser. +---@field version? string +---A list of HTML tags that the client allows / supports in +---Markdown. +--- +---@since 3.17.0 +---@field allowedTags? string[] + +---A set of predefined token types. This set is not fixed +---an clients can specify additional token types via the +---corresponding client capabilities. +--- +---@since 3.16.0 +---@alias lsp.SemanticTokenTypes +---| "namespace" # namespace +---| "type" # type +---| "class" # class +---| "enum" # enum +---| "interface" # interface +---| "struct" # struct +---| "typeParameter" # typeParameter +---| "parameter" # parameter +---| "variable" # variable +---| "property" # property +---| "enumMember" # enumMember +---| "event" # event +---| "function" # function +---| "method" # method +---| "macro" # macro +---| "keyword" # keyword +---| "modifier" # modifier +---| "comment" # comment +---| "string" # string +---| "number" # number +---| "regexp" # regexp +---| "operator" # operator +---| "decorator" # decorator + +---A set of predefined token modifiers. This set is not fixed +---an clients can specify additional token types via the +---corresponding client capabilities. +--- +---@since 3.16.0 +---@alias lsp.SemanticTokenModifiers +---| "declaration" # declaration +---| "definition" # definition +---| "readonly" # readonly +---| "static" # static +---| "deprecated" # deprecated +---| "abstract" # abstract +---| "async" # async +---| "modification" # modification +---| "documentation" # documentation +---| "defaultLibrary" # defaultLibrary + +---The document diagnostic report kinds. +--- +---@since 3.17.0 +---@alias lsp.DocumentDiagnosticReportKind +---| "full" # Full +---| "unchanged" # Unchanged + +---Predefined error codes. +---@alias lsp.ErrorCodes +---| -32700 # ParseError +---| -32600 # InvalidRequest +---| -32601 # MethodNotFound +---| -32602 # InvalidParams +---| -32603 # InternalError +---| -32002 # ServerNotInitialized +---| -32001 # UnknownErrorCode + +---@alias lsp.LSPErrorCodes +---| -32803 # RequestFailed +---| -32802 # ServerCancelled +---| -32801 # ContentModified +---| -32800 # RequestCancelled + +---A set of predefined range kinds. +---@alias lsp.FoldingRangeKind +---| "comment" # Comment +---| "imports" # Imports +---| "region" # Region + +---A symbol kind. +---@alias lsp.SymbolKind +---| 1 # File +---| 2 # Module +---| 3 # Namespace +---| 4 # Package +---| 5 # Class +---| 6 # Method +---| 7 # Property +---| 8 # Field +---| 9 # Constructor +---| 10 # Enum +---| 11 # Interface +---| 12 # Function +---| 13 # Variable +---| 14 # Constant +---| 15 # String +---| 16 # Number +---| 17 # Boolean +---| 18 # Array +---| 19 # Object +---| 20 # Key +---| 21 # Null +---| 22 # EnumMember +---| 23 # Struct +---| 24 # Event +---| 25 # Operator +---| 26 # TypeParameter + +---Symbol tags are extra annotations that tweak the rendering of a symbol. +--- +---@since 3.16 +---@alias lsp.SymbolTag +---| 1 # Deprecated + +---Moniker uniqueness level to define scope of the moniker. +--- +---@since 3.16.0 +---@alias lsp.UniquenessLevel +---| "document" # document +---| "project" # project +---| "group" # group +---| "scheme" # scheme +---| "global" # global + +---The moniker kind. +--- +---@since 3.16.0 +---@alias lsp.MonikerKind +---| "import" # import +---| "export" # export +---| "local" # local + +---Inlay hint kinds. +--- +---@since 3.17.0 +---@alias lsp.InlayHintKind +---| 1 # Type +---| 2 # Parameter + +---The message type +---@alias lsp.MessageType +---| 1 # Error +---| 2 # Warning +---| 3 # Info +---| 4 # Log + +---Defines how the host (editor) should sync +---document changes to the language server. +---@alias lsp.TextDocumentSyncKind +---| 0 # None +---| 1 # Full +---| 2 # Incremental + +---Represents reasons why a text document is saved. +---@alias lsp.TextDocumentSaveReason +---| 1 # Manual +---| 2 # AfterDelay +---| 3 # FocusOut + +---The kind of a completion entry. +---@alias lsp.CompletionItemKind +---| 1 # Text +---| 2 # Method +---| 3 # Function +---| 4 # Constructor +---| 5 # Field +---| 6 # Variable +---| 7 # Class +---| 8 # Interface +---| 9 # Module +---| 10 # Property +---| 11 # Unit +---| 12 # Value +---| 13 # Enum +---| 14 # Keyword +---| 15 # Snippet +---| 16 # Color +---| 17 # File +---| 18 # Reference +---| 19 # Folder +---| 20 # EnumMember +---| 21 # Constant +---| 22 # Struct +---| 23 # Event +---| 24 # Operator +---| 25 # TypeParameter + +---Completion item tags are extra annotations that tweak the rendering of a completion +---item. +--- +---@since 3.15.0 +---@alias lsp.CompletionItemTag +---| 1 # Deprecated + +---Defines whether the insert text in a completion item should be interpreted as +---plain text or a snippet. +---@alias lsp.InsertTextFormat +---| 1 # PlainText +---| 2 # Snippet + +---How whitespace and indentation is handled during completion +---item insertion. +--- +---@since 3.16.0 +---@alias lsp.InsertTextMode +---| 1 # asIs +---| 2 # adjustIndentation + +---A document highlight kind. +---@alias lsp.DocumentHighlightKind +---| 1 # Text +---| 2 # Read +---| 3 # Write + +---A set of predefined code action kinds +---@alias lsp.CodeActionKind +---| "" # Empty +---| "quickfix" # QuickFix +---| "refactor" # Refactor +---| "refactor.extract" # RefactorExtract +---| "refactor.inline" # RefactorInline +---| "refactor.rewrite" # RefactorRewrite +---| "source" # Source +---| "source.organizeImports" # SourceOrganizeImports +---| "source.fixAll" # SourceFixAll + +---@alias lsp.TraceValues +---| "off" # Off +---| "messages" # Messages +---| "verbose" # Verbose + +---Describes the content type that a client supports in various +---result literals like `Hover`, `ParameterInfo` or `CompletionItem`. +--- +---Please note that `MarkupKinds` must not start with a `$`. This kinds +---are reserved for internal usage. +---@alias lsp.MarkupKind +---| "plaintext" # PlainText +---| "markdown" # Markdown + +---Describes how an {@link InlineCompletionItemProvider inline completion provider} was triggered. +--- +---@since 3.18.0 +---@proposed +---@alias lsp.InlineCompletionTriggerKind +---| 0 # Invoked +---| 1 # Automatic + +---A set of predefined position encoding kinds. +--- +---@since 3.17.0 +---@alias lsp.PositionEncodingKind +---| "utf-8" # UTF8 +---| "utf-16" # UTF16 +---| "utf-32" # UTF32 + +---The file event type +---@alias lsp.FileChangeType +---| 1 # Created +---| 2 # Changed +---| 3 # Deleted + +---@alias lsp.WatchKind +---| 1 # Create +---| 2 # Change +---| 4 # Delete + +---The diagnostic's severity. +---@alias lsp.DiagnosticSeverity +---| 1 # Error +---| 2 # Warning +---| 3 # Information +---| 4 # Hint + +---The diagnostic tags. +--- +---@since 3.15.0 +---@alias lsp.DiagnosticTag +---| 1 # Unnecessary +---| 2 # Deprecated + +---How a completion was triggered +---@alias lsp.CompletionTriggerKind +---| 1 # Invoked +---| 2 # TriggerCharacter +---| 3 # TriggerForIncompleteCompletions + +---How a signature help was triggered. +--- +---@since 3.15.0 +---@alias lsp.SignatureHelpTriggerKind +---| 1 # Invoked +---| 2 # TriggerCharacter +---| 3 # ContentChange + +---The reason why code actions were requested. +--- +---@since 3.17.0 +---@alias lsp.CodeActionTriggerKind +---| 1 # Invoked +---| 2 # Automatic + +---A pattern kind describing if a glob pattern matches a file a folder or +---both. +--- +---@since 3.16.0 +---@alias lsp.FileOperationPatternKind +---| "file" # file +---| "folder" # folder + +---A notebook cell kind. +--- +---@since 3.17.0 +---@alias lsp.NotebookCellKind +---| 1 # Markup +---| 2 # Code + +---@alias lsp.ResourceOperationKind +---| "create" # Create +---| "rename" # Rename +---| "delete" # Delete + +---@alias lsp.FailureHandlingKind +---| "abort" # Abort +---| "transactional" # Transactional +---| "textOnlyTransactional" # TextOnlyTransactional +---| "undo" # Undo + +---@alias lsp.PrepareSupportDefaultBehavior +---| 1 # Identifier + +---@alias lsp.TokenFormat +---| "relative" # Relative + +---The definition of a symbol represented as one or many {@link Location locations}. +---For most programming languages there is only one location at which a symbol is +---defined. +--- +---Servers should prefer returning `DefinitionLink` over `Definition` if supported +---by the client. +---@alias lsp.Definition lsp.Location|lsp.Location[] + +---Information about where a symbol is defined. +--- +---Provides additional metadata over normal {@link Location location} definitions, including the range of +---the defining symbol +---@alias lsp.DefinitionLink lsp.LocationLink + +---LSP arrays. +---@since 3.17.0 +---@alias lsp.LSPArray lsp.LSPAny[] + +---The LSP any type. +---Please note that strictly speaking a property with the value `undefined` +---can't be converted into JSON preserving the property name. However for +---convenience it is allowed and assumed that all these properties are +---optional as well. +---@since 3.17.0 +---@alias lsp.LSPAny lsp.LSPObject|lsp.LSPArray|string|integer|uinteger|decimal|boolean|lsp.null + +---The declaration of a symbol representation as one or many {@link Location locations}. +---@alias lsp.Declaration lsp.Location|lsp.Location[] + +---Information about where a symbol is declared. +--- +---Provides additional metadata over normal {@link Location location} declarations, including the range of +---the declaring symbol. +--- +---Servers should prefer returning `DeclarationLink` over `Declaration` if supported +---by the client. +---@alias lsp.DeclarationLink lsp.LocationLink + +---Inline value information can be provided by different means: +---- directly as a text value (class InlineValueText). +---- as a name to use for a variable lookup (class InlineValueVariableLookup) +---- as an evaluatable expression (class InlineValueEvaluatableExpression) +---The InlineValue types combines all inline value types into one type. +--- +---@since 3.17.0 +---@alias lsp.InlineValue lsp.InlineValueText|lsp.InlineValueVariableLookup|lsp.InlineValueEvaluatableExpression + +---The result of a document diagnostic pull request. A report can +---either be a full report containing all diagnostics for the +---requested document or an unchanged report indicating that nothing +---has changed in terms of diagnostics in comparison to the last +---pull request. +--- +---@since 3.17.0 +---@alias lsp.DocumentDiagnosticReport lsp.RelatedFullDocumentDiagnosticReport|lsp.RelatedUnchangedDocumentDiagnosticReport + +---@alias lsp.PrepareRenameResult lsp.Range|anonym44|anonym45 + +---A document selector is the combination of one or many document filters. +--- +---@sample `let sel:DocumentSelector = [{ language: 'typescript' }, { language: 'json', pattern: '**∕tsconfig.json' }]`; +--- +---The use of a string as a document filter is deprecated @since 3.16.0. +---@alias lsp.DocumentSelector lsp.DocumentFilter[] + +---@alias lsp.ProgressToken integer|string + +---An identifier to refer to a change annotation stored with a workspace edit. +---@alias lsp.ChangeAnnotationIdentifier string + +---A workspace diagnostic document report. +--- +---@since 3.17.0 +---@alias lsp.WorkspaceDocumentDiagnosticReport lsp.WorkspaceFullDocumentDiagnosticReport|lsp.WorkspaceUnchangedDocumentDiagnosticReport + +---An event describing a change to a text document. If only a text is provided +---it is considered to be the full content of the document. +---@alias lsp.TextDocumentContentChangeEvent anonym46|anonym47 + +---MarkedString can be used to render human readable text. It is either a markdown string +---or a code-block that provides a language and a code snippet. The language identifier +---is semantically equal to the optional language identifier in fenced code blocks in GitHub +---issues. See https://help.github.com/articles/creating-and-highlighting-code-blocks/#syntax-highlighting +--- +---The pair of a language and a value is an equivalent to markdown: +---```${language} +---${value} +---``` +--- +---Note that markdown strings will be sanitized - that means html will be escaped. +---@deprecated use MarkupContent instead. +---@alias lsp.MarkedString string|anonym48 + +---A document filter describes a top level text document or +---a notebook cell document. +--- +---@since 3.17.0 - proposed support for NotebookCellTextDocumentFilter. +---@alias lsp.DocumentFilter lsp.TextDocumentFilter|lsp.NotebookCellTextDocumentFilter + +---LSP object definition. +---@since 3.17.0 +---@alias lsp.LSPObject table<string, lsp.LSPAny> + +---The glob pattern. Either a string pattern or a relative pattern. +--- +---@since 3.17.0 +---@alias lsp.GlobPattern lsp.Pattern|lsp.RelativePattern + +---A document filter denotes a document by different properties like +---the {@link TextDocument.languageId language}, the {@link Uri.scheme scheme} of +---its resource, or a glob-pattern that is applied to the {@link TextDocument.fileName path}. +--- +---Glob patterns can have the following syntax: +---- `*` to match one or more characters in a path segment +---- `?` to match on one character in a path segment +---- `**` to match any number of path segments, including none +---- `{}` to group sub patterns into an OR expression. (e.g. `**/*.{ts,js}` matches all TypeScript and JavaScript files) +---- `[]` to declare a range of characters to match in a path segment (e.g., `example.[0-9]` to match on `example.0`, `example.1`, …) +---- `[!...]` to negate a range of characters to match in a path segment (e.g., `example.[!0-9]` to match on `example.a`, `example.b`, but not `example.0`) +--- +---@sample A language filter that applies to typescript files on disk: `{ language: 'typescript', scheme: 'file' }` +---@sample A language filter that applies to all package.json paths: `{ language: 'json', pattern: '**package.json' }` +--- +---@since 3.17.0 +---@alias lsp.TextDocumentFilter anonym49|anonym50|anonym51 + +---A notebook document filter denotes a notebook document by +---different properties. The properties will be match +---against the notebook's URI (same as with documents) +--- +---@since 3.17.0 +---@alias lsp.NotebookDocumentFilter anonym52|anonym53|anonym54 + +---The glob pattern to watch relative to the base path. Glob patterns can have the following syntax: +---- `*` to match one or more characters in a path segment +---- `?` to match on one character in a path segment +---- `**` to match any number of path segments, including none +---- `{}` to group conditions (e.g. `**/*.{ts,js}` matches all TypeScript and JavaScript files) +---- `[]` to declare a range of characters to match in a path segment (e.g., `example.[0-9]` to match on `example.0`, `example.1`, …) +---- `[!...]` to negate a range of characters to match in a path segment (e.g., `example.[!0-9]` to match on `example.a`, `example.b`, but not `example.0`) +--- +---@since 3.17.0 +---@alias lsp.Pattern string + +---@class anonym1 +---The name of the server as defined by the server. +---@field name string +---The server's version as defined by the server. +---@field version? string + +---@class anonym3 +---@field insert lsp.Range +---@field replace lsp.Range + +---@class anonym2 +---A default commit character set. +--- +---@since 3.17.0 +---@field commitCharacters? string[] +---A default edit range. +--- +---@since 3.17.0 +---@field editRange? lsp.Range|anonym3 +---A default insert text format. +--- +---@since 3.17.0 +---@field insertTextFormat? lsp.InsertTextFormat +---A default insert text mode. +--- +---@since 3.17.0 +---@field insertTextMode? lsp.InsertTextMode +---A default data value. +--- +---@since 3.17.0 +---@field data? lsp.LSPAny + +---@class anonym4 +---Human readable description of why the code action is currently disabled. +--- +---This is displayed in the code actions UI. +---@field reason string + +---@class anonym5 +---@field uri lsp.DocumentUri + +---@class anonym6 + +---@class anonym7 +---The server supports deltas for full documents. +---@field delta? boolean + +---@class anonym9 +---The change to the cell array. +---@field array lsp.NotebookCellArrayChange +---Additional opened cell text documents. +---@field didOpen? lsp.TextDocumentItem[] +---Additional closed cell text documents. +---@field didClose? lsp.TextDocumentIdentifier[] + +---@class anonym10 +---@field document lsp.VersionedTextDocumentIdentifier +---@field changes lsp.TextDocumentContentChangeEvent[] + +---@class anonym8 +---Changes to the cell structure to add or +---remove cells. +---@field structure? anonym9 +---Changes to notebook cells properties like its +---kind, execution summary or metadata. +---@field data? lsp.NotebookCell[] +---Changes to the text content of notebook cells. +---@field textContent? anonym10[] + +---@class anonym11 +---The name of the client as defined by the client. +---@field name string +---The client's version as defined by the client. +---@field version? string + +---@class anonym12 +---The server supports workspace folder. +--- +---@since 3.6.0 +---@field workspaceFolders? lsp.WorkspaceFoldersServerCapabilities +---The server is interested in notifications/requests for operations on files. +--- +---@since 3.16.0 +---@field fileOperations? lsp.FileOperationOptions + +---@class anonym13 +---The server has support for completion item label +---details (see also `CompletionItemLabelDetails`) when +---receiving a completion item in a resolve call. +--- +---@since 3.17.0 +---@field labelDetailsSupport? boolean + +---@class anonym15 +---@field language string + +---@class anonym14 +---The notebook to be synced If a string +---value is provided it matches against the +---notebook type. '*' matches every notebook. +---@field notebook string|lsp.NotebookDocumentFilter +---The cells of the matching notebook to be synced. +---@field cells? anonym15[] + +---@class anonym17 +---@field language string + +---@class anonym16 +---The notebook to be synced If a string +---value is provided it matches against the +---notebook type. '*' matches every notebook. +---@field notebook? string|lsp.NotebookDocumentFilter +---The cells of the matching notebook to be synced. +---@field cells anonym17[] + +---@class anonym18 +---The client will actively cancel the request. +---@field cancel boolean +---The list of requests for which the client +---will retry the request if it receives a +---response with error code `ContentModified` +---@field retryOnContentModified string[] + +---@class anonym19 +---Whether the client groups edits with equal labels into tree nodes, +---for instance all edits labelled with "Changes in Strings" would +---be a tree node. +---@field groupsOnLabel? boolean + +---@class anonym20 +---The symbol kind values the client supports. When this +---property exists the client also guarantees that it will +---handle values outside its set gracefully and falls back +---to a default value when unknown. +--- +---If this property is not present the client only supports +---the symbol kinds from `File` to `Array` as defined in +---the initial version of the protocol. +---@field valueSet? lsp.SymbolKind[] + +---@class anonym21 +---The tags supported by the client. +---@field valueSet lsp.SymbolTag[] + +---@class anonym22 +---The properties that a client can resolve lazily. Usually +---`location.range` +---@field properties string[] + +---@class anonym24 +---The tags supported by the client. +---@field valueSet lsp.CompletionItemTag[] + +---@class anonym25 +---The properties that a client can resolve lazily. +---@field properties string[] + +---@class anonym26 +---@field valueSet lsp.InsertTextMode[] + +---@class anonym23 +---Client supports snippets as insert text. +--- +---A snippet can define tab stops and placeholders with `$1`, `$2` +---and `${3:foo}`. `$0` defines the final tab stop, it defaults to +---the end of the snippet. Placeholders with equal identifiers are linked, +---that is typing in one will update others too. +---@field snippetSupport? boolean +---Client supports commit characters on a completion item. +---@field commitCharactersSupport? boolean +---Client supports the following content formats for the documentation +---property. The order describes the preferred format of the client. +---@field documentationFormat? lsp.MarkupKind[] +---Client supports the deprecated property on a completion item. +---@field deprecatedSupport? boolean +---Client supports the preselect property on a completion item. +---@field preselectSupport? boolean +---Client supports the tag property on a completion item. Clients supporting +---tags have to handle unknown tags gracefully. Clients especially need to +---preserve unknown tags when sending a completion item back to the server in +---a resolve call. +--- +---@since 3.15.0 +---@field tagSupport? anonym24 +---Client support insert replace edit to control different behavior if a +---completion item is inserted in the text or should replace text. +--- +---@since 3.16.0 +---@field insertReplaceSupport? boolean +---Indicates which properties a client can resolve lazily on a completion +---item. Before version 3.16.0 only the predefined properties `documentation` +---and `details` could be resolved lazily. +--- +---@since 3.16.0 +---@field resolveSupport? anonym25 +---The client supports the `insertTextMode` property on +---a completion item to override the whitespace handling mode +---as defined by the client (see `insertTextMode`). +--- +---@since 3.16.0 +---@field insertTextModeSupport? anonym26 +---The client has support for completion item label +---details (see also `CompletionItemLabelDetails`). +--- +---@since 3.17.0 +---@field labelDetailsSupport? boolean + +---@class anonym27 +---The completion item kind values the client supports. When this +---property exists the client also guarantees that it will +---handle values outside its set gracefully and falls back +---to a default value when unknown. +--- +---If this property is not present the client only supports +---the completion items kinds from `Text` to `Reference` as defined in +---the initial version of the protocol. +---@field valueSet? lsp.CompletionItemKind[] + +---@class anonym28 +---The client supports the following itemDefaults on +---a completion list. +--- +---The value lists the supported property names of the +---`CompletionList.itemDefaults` object. If omitted +---no properties are supported. +--- +---@since 3.17.0 +---@field itemDefaults? string[] + +---@class anonym30 +---The client supports processing label offsets instead of a +---simple label string. +--- +---@since 3.14.0 +---@field labelOffsetSupport? boolean + +---@class anonym29 +---Client supports the following content formats for the documentation +---property. The order describes the preferred format of the client. +---@field documentationFormat? lsp.MarkupKind[] +---Client capabilities specific to parameter information. +---@field parameterInformation? anonym30 +---The client supports the `activeParameter` property on `SignatureInformation` +---literal. +--- +---@since 3.16.0 +---@field activeParameterSupport? boolean + +---@class anonym31 +---The symbol kind values the client supports. When this +---property exists the client also guarantees that it will +---handle values outside its set gracefully and falls back +---to a default value when unknown. +--- +---If this property is not present the client only supports +---the symbol kinds from `File` to `Array` as defined in +---the initial version of the protocol. +---@field valueSet? lsp.SymbolKind[] + +---@class anonym32 +---The tags supported by the client. +---@field valueSet lsp.SymbolTag[] + +---@class anonym34 +---The code action kind values the client supports. When this +---property exists the client also guarantees that it will +---handle values outside its set gracefully and falls back +---to a default value when unknown. +---@field valueSet lsp.CodeActionKind[] + +---@class anonym33 +---The code action kind is support with the following value +---set. +---@field codeActionKind anonym34 + +---@class anonym35 +---The properties that a client can resolve lazily. +---@field properties string[] + +---@class anonym36 +---The folding range kind values the client supports. When this +---property exists the client also guarantees that it will +---handle values outside its set gracefully and falls back +---to a default value when unknown. +---@field valueSet? lsp.FoldingRangeKind[] + +---@class anonym37 +---If set, the client signals that it supports setting collapsedText on +---folding ranges to display custom labels instead of the default text. +--- +---@since 3.17.0 +---@field collapsedText? boolean + +---@class anonym38 +---The tags supported by the client. +---@field valueSet lsp.DiagnosticTag[] + +---@class anonym40 + +---@class anonym41 +---The client will send the `textDocument/semanticTokens/full/delta` request if +---the server provides a corresponding handler. +---@field delta? boolean + +---@class anonym39 +---The client will send the `textDocument/semanticTokens/range` request if +---the server provides a corresponding handler. +---@field range? boolean|anonym40 +---The client will send the `textDocument/semanticTokens/full` request if +---the server provides a corresponding handler. +---@field full? boolean|anonym41 + +---@class anonym42 +---The properties that a client can resolve lazily. +---@field properties string[] + +---@class anonym43 +---Whether the client supports additional attributes which +---are preserved and send back to the server in the +---request's response. +---@field additionalPropertiesSupport? boolean + +---@class anonym44 +---@field range lsp.Range +---@field placeholder string + +---@class anonym45 +---@field defaultBehavior boolean + +---@class anonym46 +---The range of the document that changed. +---@field range lsp.Range +---The optional length of the range that got replaced. +--- +---@deprecated use range instead. +---@field rangeLength? uinteger +---The new text for the provided range. +---@field text string + +---@class anonym47 +---The new text of the whole document. +---@field text string + +---@class anonym48 +---@field language string +---@field value string + +---@class anonym49 +---A language id, like `typescript`. +---@field language string +---A Uri {@link Uri.scheme scheme}, like `file` or `untitled`. +---@field scheme? string +---A glob pattern, like `*.{ts,js}`. +---@field pattern? string + +---@class anonym50 +---A language id, like `typescript`. +---@field language? string +---A Uri {@link Uri.scheme scheme}, like `file` or `untitled`. +---@field scheme string +---A glob pattern, like `*.{ts,js}`. +---@field pattern? string + +---@class anonym51 +---A language id, like `typescript`. +---@field language? string +---A Uri {@link Uri.scheme scheme}, like `file` or `untitled`. +---@field scheme? string +---A glob pattern, like `*.{ts,js}`. +---@field pattern string + +---@class anonym52 +---The type of the enclosing notebook. +---@field notebookType string +---A Uri {@link Uri.scheme scheme}, like `file` or `untitled`. +---@field scheme? string +---A glob pattern. +---@field pattern? string + +---@class anonym53 +---The type of the enclosing notebook. +---@field notebookType? string +---A Uri {@link Uri.scheme scheme}, like `file` or `untitled`. +---@field scheme string +---A glob pattern. +---@field pattern? string + +---@class anonym54 +---The type of the enclosing notebook. +---@field notebookType? string +---A Uri {@link Uri.scheme scheme}, like `file` or `untitled`. +---@field scheme? string +---A glob pattern. +---@field pattern string diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index e36014d07d..6cb91417f2 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -25,9 +25,9 @@ local default_border = { ---@private --- Check the border given by opts or the default border for the additional --- size it adds to a float. ----@param opts (table, optional) options for the floating window +---@param opts table optional options for the floating window --- - border (string or table) the border ----@returns (table) size of border in the form of { height = height, width = width } +---@return table size of border in the form of { height = height, width = width } local function get_border_size(opts) local border = opts and opts.border or default_border local height = 0 @@ -106,7 +106,7 @@ end ---@private local function split_lines(value) value = string.gsub(value, '\r\n?', '\n') - return split(value, '\n', true) + return split(value, '\n', { plain = true }) end ---@private @@ -122,7 +122,7 @@ end --- Convenience wrapper around vim.str_utfindex ---@param line string line to be indexed ---@param index integer|nil byte index (utf-8), or `nil` for length ----@param encoding string utf-8|utf-16|utf-32|nil defaults to utf-16 +---@param encoding string|nil utf-8|utf-16|utf-32|nil defaults to utf-16 ---@return integer `encoding` index of `index` in `line` function M._str_utfindex_enc(line, index, encoding) if not encoding then @@ -150,7 +150,7 @@ end ---Alternative to vim.str_byteindex that takes an encoding. ---@param line string line to be indexed ---@param index integer UTF index ----@param encoding string utf-8|utf-16|utf-32|nil defaults to utf-16 +---@param encoding string utf-8|utf-16|utf-32| defaults to utf-16 ---@return integer byte (utf-8) index of `encoding` index `index` in `line` function M._str_byteindex_enc(line, index, encoding) if not encoding then @@ -178,10 +178,10 @@ local _str_byteindex_enc = M._str_byteindex_enc --- CAUTION: Changes in-place! --- ---@param lines (table) Original list of strings ----@param A (table) Start position; a 2-tuple of {line, col} numbers ----@param B (table) End position; a 2-tuple of {line, col} numbers ----@param new_lines A list of strings to replace the original ----@returns (table) The modified {lines} object +---@param A (table) Start position; a 2-tuple of {line,col} numbers +---@param B (table) End position; a 2-tuple of {line,col} numbers +---@param new_lines (table) list of strings to replace the original +---@return table The modified {lines} object function M.set_lines(lines, A, B, new_lines) -- 0-indexing to 1-indexing local i_0 = A[1] + 1 @@ -241,7 +241,7 @@ end --- ---@param bufnr integer bufnr to get the lines from ---@param rows integer[] zero-indexed line numbers ----@return table<integer, string> a table mapping rows to lines +---@return table<integer, string>|string a table mapping rows to lines local function get_lines(bufnr, rows) rows = type(rows) == 'table' and rows or { rows } @@ -331,8 +331,9 @@ end ---@private --- Position is a https://microsoft.github.io/language-server-protocol/specifications/specification-current/#position --- Returns a zero-indexed column, since set_lines() does the conversion to ----@param offset_encoding string utf-8|utf-16|utf-32 +---@param offset_encoding string|nil utf-8|utf-16|utf-32 --- 1-indexed +---@return integer local function get_line_byte_from_position(bufnr, position, offset_encoding) -- LSP's line and characters are 0-indexed -- Vim's line and columns are 1-indexed @@ -353,11 +354,40 @@ end --- Process and return progress reports from lsp server ---@private +---@deprecated Use vim.lsp.status() or access client.progress directly function M.get_progress_messages() + vim.deprecate('vim.lsp.util.get_progress_messages', 'vim.lsp.status', '0.11.0') local new_messages = {} local progress_remove = {} for _, client in ipairs(vim.lsp.get_active_clients()) do + local groups = {} + for progress in client.progress do + local value = progress.value + if type(value) == 'table' and value.kind then + local group = groups[progress.token] + if not group then + group = { + done = false, + progress = true, + title = 'empty title', + } + groups[progress.token] = group + end + group.title = value.title or group.title + group.cancellable = value.cancellable or group.cancellable + if value.kind == 'end' then + group.done = true + end + group.message = value.message or group.message + group.percentage = value.percentage or group.percentage + end + end + + for _, group in pairs(groups) do + table.insert(new_messages, group) + end + local messages = client.messages local data = messages for token, ctx in pairs(data.progress) do @@ -569,8 +599,8 @@ end --- Can be used to extract the completion items from a --- `textDocument/completion` request, which may return one of --- `CompletionItem[]`, `CompletionList` or null. ----@param result (table) The result of a `textDocument/completion` request ----@returns (table) List of completion items +---@param result table The result of a `textDocument/completion` request +---@return table List of completion items ---@see https://microsoft.github.io/language-server-protocol/specification#textDocument_completion function M.extract_completion_items(result) if type(result) == 'table' and result.items then @@ -629,7 +659,7 @@ end --- Parses snippets in a completion entry. --- ---@param input string unparsed snippet ----@returns string parsed snippet +---@return string parsed snippet function M.parse_snippet(input) local ok, parsed = pcall(function() return tostring(snippet.parse(input)) @@ -689,7 +719,7 @@ end --- specification. --- ---@param completion_item_kind (`vim.lsp.protocol.completionItemKind`) ----@returns (`vim.lsp.protocol.completionItemKind`) +---@return (`vim.lsp.protocol.completionItemKind`) ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_completion function M._get_completion_item_kind_name(completion_item_kind) return protocol.CompletionItemKind[completion_item_kind] or 'Unknown' @@ -698,12 +728,12 @@ end --- Turns the result of a `textDocument/completion` request into vim-compatible --- |complete-items|. --- ----@param result The result of a `textDocument/completion` call, e.g. from +---@param result table The result of a `textDocument/completion` call, e.g. from ---|vim.lsp.buf.completion()|, which may be one of `CompletionItem[]`, --- `CompletionList` or `null` ---@param prefix (string) the prefix to filter the completion items ----@returns { matches = complete-items table, incomplete = bool } ----@see |complete-items| +---@return table { matches = complete-items table, incomplete = bool } +---@see complete-items function M.text_document_completion_list_to_complete_items(result, prefix) local items = M.extract_completion_items(result) if vim.tbl_isempty(items) then @@ -908,7 +938,7 @@ end --- ---@param input (`MarkedString` | `MarkedString[]` | `MarkupContent`) ---@param contents (table|nil) List of strings to extend with converted lines. Defaults to {}. ----@returns {contents}, extended with lines of converted markdown. +---@return table {contents} extended with lines of converted markdown. ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_hover function M.convert_input_to_markdown_lines(input, contents) contents = contents or {} @@ -956,10 +986,11 @@ end --- Converts `textDocument/SignatureHelp` response to markdown lines. --- ----@param signature_help Response of `textDocument/SignatureHelp` ----@param ft optional filetype that will be use as the `lang` for the label markdown code block ----@param triggers optional list of trigger characters from the lsp server. used to better determine parameter offsets ----@returns list of lines of converted markdown. +---@param signature_help table Response of `textDocument/SignatureHelp` +---@param ft string|nil filetype that will be use as the `lang` for the label markdown code block +---@param triggers table|nil list of trigger characters from the lsp server. used to better determine parameter offsets +---@return table|nil table list of lines of converted markdown. +---@return table|nil table of active hl ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_signatureHelp function M.convert_signature_help_to_markdown_lines(signature_help, ft, triggers) if not signature_help.signatures then @@ -986,7 +1017,7 @@ function M.convert_signature_help_to_markdown_lines(signature_help, ft, triggers -- wrap inside a code block so stylize_markdown can render it properly label = ('```%s\n%s\n```'):format(ft, label) end - list_extend(contents, split(label, '\n', true)) + list_extend(contents, split(label, '\n', { plain = true })) if signature.documentation then M.convert_input_to_markdown_lines(signature.documentation, contents) end @@ -1058,16 +1089,16 @@ end --- Creates a table with sensible default options for a floating window. The --- table can be passed to |nvim_open_win()|. --- ----@param width (integer) window width (in character cells) ----@param height (integer) window height (in character cells) ----@param opts (table, optional) +---@param width integer window width (in character cells) +---@param height integer window height (in character cells) +---@param opts table optional --- - offset_x (integer) offset to add to `col` --- - offset_y (integer) offset to add to `row` --- - border (string or table) override `border` --- - focusable (string or table) override `focusable` --- - zindex (string or table) override `zindex`, defaults to 50 --- - relative ("mouse"|"cursor") defaults to "cursor" ----@returns (table) Options +---@return table Options function M.make_floating_popup_options(width, height, opts) validate({ opts = { opts, 't', true }, @@ -1131,7 +1162,7 @@ end --- Shows document and optionally jumps to the location. --- ---@param location table (`Location`|`LocationLink`) ----@param offset_encoding "utf-8" | "utf-16" | "utf-32" +---@param offset_encoding string|nil utf-8|utf-16|utf-32 ---@param opts table|nil options --- - reuse_win (boolean) Jump to existing window if buffer is already open. --- - focus (boolean) Whether to focus/jump to location if possible. Defaults to true. @@ -1188,7 +1219,7 @@ end --- Jumps to a location. --- ---@param location table (`Location`|`LocationLink`) ----@param offset_encoding "utf-8" | "utf-16" | "utf-32" +---@param offset_encoding string|nil utf-8|utf-16|utf-32 ---@param reuse_win boolean|nil Jump to existing window if buffer is already open. ---@return boolean `true` if the jump succeeded function M.jump_to_location(location, offset_encoding, reuse_win) @@ -1208,8 +1239,9 @@ end --- - for Location, range is shown (e.g., function definition) --- - for LocationLink, targetRange is shown (e.g., body of function definition) --- ----@param location a single `Location` or `LocationLink` ----@returns (bufnr,winnr) buffer and window number of floating window or nil +---@param location table a single `Location` or `LocationLink` +---@return integer|nil buffer id of float window +---@return integer|nil window id of float window function M.preview_location(location, opts) -- location may be LocationLink or Location (more useful for the former) local uri = location.targetUri or location.uri @@ -1246,10 +1278,10 @@ end --- Trims empty lines from input and pad top and bottom with empty lines --- ---@param contents table of lines to trim and pad ----@param opts dictionary with optional fields +---@param opts table with optional fields --- - pad_top number of lines to pad contents at top (default 0) --- - pad_bottom number of lines to pad contents at bottom (default 0) ----@return contents table of trimmed and padded lines +---@return table table of trimmed and padded lines function M._trim(contents, opts) validate({ contents = { contents, 't' }, @@ -1272,7 +1304,7 @@ 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 +---@return table table of lang -> syntax mappings ---@private local function get_markdown_fences() local fences = {} @@ -1295,7 +1327,7 @@ end --- If you want to open a popup with fancy markdown, use `open_floating_preview` instead --- ---@param contents table of lines to show in window ----@param opts dictionary with optional fields +---@param opts table with optional fields --- - height of floating window --- - width of floating window --- - wrap_at character to wrap at for computing height @@ -1304,7 +1336,7 @@ end --- - pad_top number of lines to pad contents at top --- - pad_bottom number of lines to pad contents at bottom --- - separator insert separator after code block ----@returns width,height size of float +---@return table stripped content function M.stylize_markdown(bufnr, contents, opts) validate({ contents = { contents, 't' }, @@ -1451,10 +1483,10 @@ function M.stylize_markdown(bufnr, contents, opts) if not langs[lang] then -- HACK: reset current_syntax, since some syntax files like markdown won't load if it is already set pcall(api.nvim_buf_del_var, bufnr, 'current_syntax') - -- TODO(ashkan): better validation before this. - if not pcall(vim.cmd, string.format('syntax include %s syntax/%s.vim', lang, ft)) then + if #api.nvim_get_runtime_file(('syntax/%s.vim'):format(ft), true) == 0 then return end + vim.cmd(string.format('syntax include %s syntax/%s.vim', lang, ft)) langs[lang] = true end vim.cmd( @@ -1513,7 +1545,7 @@ end ---@param events table list of events ---@param winnr integer window id of preview window ---@param bufnrs table list of buffers where the preview window will remain visible ----@see |autocmd-events| +---@see autocmd-events local function close_preview_autocmd(events, winnr, bufnrs) local augroup = api.nvim_create_augroup('preview_window_' .. winnr, { clear = true, @@ -1543,13 +1575,14 @@ end --- Computes size of float needed to show contents (with optional wrapping) --- ---@param contents table of lines to show in window ----@param opts dictionary with optional fields +---@param opts table with optional fields --- - height of floating window --- - width of floating window --- - wrap_at character to wrap at for computing height --- - max_width maximal width of floating window --- - max_height maximal height of floating window ----@returns width,height size of float +---@return integer width size of float +---@return integer height size of float function M._make_floating_popup_size(contents, opts) validate({ contents = { contents, 't' }, @@ -1633,7 +1666,8 @@ end --- - focus: (boolean, default true) If `true`, and if {focusable} --- is also `true`, focus an existing floating window with the same --- {focus_id} ----@returns bufnr,winnr buffer and window number of the newly created floating +---@return integer bufnr of newly created float window +---@return integer winid of newly created float window ---preview window function M.open_floating_preview(contents, syntax, opts) validate({ @@ -1737,7 +1771,7 @@ do --[[ References ]] --- ---@param bufnr integer Buffer id function M.buf_clear_references(bufnr) - validate({ bufnr = { bufnr, 'n', true } }) + validate({ bufnr = { bufnr, { 'n', 'nil' }, true } }) api.nvim_buf_clear_namespace(bufnr or 0, reference_ns, 0, -1) end @@ -1799,13 +1833,15 @@ end) --- ---@param locations table list of `Location`s or `LocationLink`s ---@param offset_encoding string offset_encoding for locations utf-8|utf-16|utf-32 ----@returns (table) list of items +--- default to first client of buffer +---@return table list of items function M.locations_to_items(locations, offset_encoding) if offset_encoding == nil then vim.notify_once( 'locations_to_items must be called with valid offset encoding', vim.log.levels.WARN ) + offset_encoding = vim.lsp.get_active_clients({ bufnr = 0 })[1].offset_encoding end local items = {} @@ -1867,7 +1903,7 @@ end --- Converts symbols to quickfix list items. --- ----@param symbols DocumentSymbol[] or SymbolInformation[] +---@param symbols table DocumentSymbol[] or SymbolInformation[] function M.symbols_to_items(symbols, bufnr) ---@private local function _symbols_to_items(_symbols, _items, _bufnr) @@ -1907,8 +1943,8 @@ function M.symbols_to_items(symbols, bufnr) end --- Removes empty lines from the beginning and end. ----@param lines (table) list of lines to trim ----@returns (table) trimmed list of lines +---@param lines table list of lines to trim +---@return table trimmed list of lines function M.trim_empty_lines(lines) local start = 1 for i = 1, #lines do @@ -1932,8 +1968,8 @@ end --- --- CAUTION: Modifies the input in-place! --- ----@param lines (table) list of lines ----@returns (string) filetype or "markdown" if it was unchanged. +---@param lines table list of lines +---@return string filetype or "markdown" if it was unchanged. function M.try_trim_markdown_code_blocks(lines) local language_id = lines[1]:match('^```(.*)') if language_id then @@ -1978,7 +2014,7 @@ end --- ---@param window integer|nil: window handle or 0 for current, defaults to current ---@param offset_encoding string|nil utf-8|utf-16|utf-32|nil defaults to `offset_encoding` of first client of buffer of `window` ----@returns `TextDocumentPositionParams` object +---@return table `TextDocumentPositionParams` object ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocumentPositionParams function M.make_position_params(window, offset_encoding) window = window or 0 @@ -1992,7 +2028,7 @@ end --- Utility function for getting the encoding of the first LSP client on the given buffer. ---@param bufnr (integer) buffer handle or 0 for current, defaults to current ----@returns (string) encoding first client if there is one, nil otherwise +---@return string encoding first client if there is one, nil otherwise function M._get_offset_encoding(bufnr) validate({ bufnr = { bufnr, 'n', true }, @@ -2031,7 +2067,7 @@ end --- ---@param window integer|nil: window handle or 0 for current, defaults to current ---@param offset_encoding "utf-8"|"utf-16"|"utf-32"|nil defaults to `offset_encoding` of first client of buffer of `window` ----@returns { textDocument = { uri = `current_file_uri` }, range = { start = +---@return table { textDocument = { uri = `current_file_uri` }, range = { start = ---`current_position`, end = `current_position` } } function M.make_range_params(window, offset_encoding) local buf = api.nvim_win_get_buf(window or 0) @@ -2046,13 +2082,13 @@ end --- Using the given range in the current buffer, creates an object that --- is similar to |vim.lsp.util.make_range_params()|. --- ----@param start_pos integer[]|nil {row, col} mark-indexed position. +---@param start_pos integer[]|nil {row,col} mark-indexed position. --- Defaults to the start of the last visual selection. ----@param end_pos integer[]|nil {row, col} mark-indexed position. +---@param end_pos integer[]|nil {row,col} mark-indexed position. --- Defaults to the end of the last visual selection. ---@param bufnr integer|nil buffer handle or 0 for current, defaults to current ---@param offset_encoding "utf-8"|"utf-16"|"utf-32"|nil defaults to `offset_encoding` of first client of `bufnr` ----@returns { textDocument = { uri = `current_file_uri` }, range = { start = +---@return table { textDocument = { uri = `current_file_uri` }, range = { start = ---`start_position`, end = `end_position` } } function M.make_given_range_params(start_pos, end_pos, bufnr, offset_encoding) validate({ @@ -2092,15 +2128,15 @@ end --- Creates a `TextDocumentIdentifier` object for the current buffer. --- ---@param bufnr integer|nil: Buffer handle, defaults to current ----@returns `TextDocumentIdentifier` +---@return table `TextDocumentIdentifier` ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocumentIdentifier function M.make_text_document_params(bufnr) return { uri = vim.uri_from_bufnr(bufnr or 0) } end --- Create the workspace params ----@param added ----@param removed +---@param added table +---@param removed table function M.make_workspace_params(added, removed) return { event = { added = added, removed = removed } } end @@ -2108,7 +2144,7 @@ end --- ---@see 'shiftwidth' ---@param bufnr (integer|nil): Buffer handle, defaults to current ----@returns (integer) indentation size +---@return (integer) indentation size function M.get_effective_tabstop(bufnr) validate({ bufnr = { bufnr, 'n', true } }) local bo = bufnr and vim.bo[bufnr] or vim.bo @@ -2119,7 +2155,7 @@ end --- Creates a `DocumentFormattingParams` object for the current buffer and cursor position. --- ---@param options table|nil with valid `FormattingOptions` entries ----@returns `DocumentFormattingParams` object +---@return `DocumentFormattingParams` object ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_formatting function M.make_formatting_params(options) validate({ options = { options, 't', true } }) @@ -2138,8 +2174,8 @@ end ---@param buf integer buffer number (0 for current) ---@param row 0-indexed line ---@param col 0-indexed byte offset in line ----@param offset_encoding string utf-8|utf-16|utf-32|nil defaults to `offset_encoding` of first client of `buf` ----@returns (integer, integer) `offset_encoding` index of the character in line {row} column {col} in buffer {buf} +---@param offset_encoding string utf-8|utf-16|utf-32 defaults to `offset_encoding` of first client of `buf` +---@return integer `offset_encoding` index of the character in line {row} column {col} in buffer {buf} function M.character_offset(buf, row, col, offset_encoding) local line = get_line(buf, row) if offset_encoding == nil then @@ -2147,6 +2183,7 @@ function M.character_offset(buf, row, col, offset_encoding) 'character_offset must be called with valid offset encoding', vim.log.levels.WARN ) + offset_encoding = vim.lsp.get_active_clients({ bufnr = buf })[1].offset_encoding end -- If the col is past the EOL, use the line length. if col > #line then @@ -2157,11 +2194,11 @@ end --- Helper function to return nested values in language server settings --- ----@param settings a table of language server settings ----@param section a string indicating the field of the settings table ----@returns (table or string) The value of settings accessed via section +---@param settings table language server settings +---@param section string indicating the field of the settings table +---@return table|string The value of settings accessed via section function M.lookup_section(settings, section) - for part in vim.gsplit(section, '.', true) do + for part in vim.gsplit(section, '.', { plain = true }) do settings = settings[part] if settings == nil then return vim.NIL diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua index 4f230c4412..dd05299295 100644 --- a/runtime/lua/vim/shared.lua +++ b/runtime/lua/vim/shared.lua @@ -456,7 +456,7 @@ end --- </pre> --- ---@param o table Table to index ----@param ... string Optional strings (0 or more, variadic) via which to index the table +---@param ... any Optional keys (0 or more, variadic) via which to index the table --- ---@return any Nested value indexed by key (if it exists), else nil function vim.tbl_get(o, ...) @@ -881,4 +881,98 @@ function vim.defaulttable(create) }) end +do + ---@class vim.Ringbuf<T> + ---@field private _items table[] + ---@field private _idx_read integer + ---@field private _idx_write integer + ---@field private _size integer + local Ringbuf = {} + + --- Clear all items + function Ringbuf.clear(self) + self._items = {} + self._idx_read = 0 + self._idx_write = 0 + end + + --- Adds an item, overriding the oldest item if the buffer is full. + ---@generic T + ---@param item T + function Ringbuf.push(self, item) + self._items[self._idx_write] = item + self._idx_write = (self._idx_write + 1) % self._size + if self._idx_write == self._idx_read then + self._idx_read = (self._idx_read + 1) % self._size + end + end + + --- Removes and returns the first unread item + ---@generic T + ---@return T? + function Ringbuf.pop(self) + local idx_read = self._idx_read + if idx_read == self._idx_write then + return nil + end + local item = self._items[idx_read] + self._items[idx_read] = nil + self._idx_read = (idx_read + 1) % self._size + return item + end + + --- Returns the first unread item without removing it + ---@generic T + ---@return T? + function Ringbuf.peek(self) + if self._idx_read == self._idx_write then + return nil + end + return self._items[self._idx_read] + end + + --- Create a ring buffer limited to a maximal number of items. + --- Once the buffer is full, adding a new entry overrides the oldest entry. + --- + --- <pre> + --- local ringbuf = vim.ringbuf(4) + --- ringbuf:push("a") + --- ringbuf:push("b") + --- ringbuf:push("c") + --- ringbuf:push("d") + --- ringbuf:push("e") -- overrides "a" + --- print(ringbuf:pop()) -- returns "b" + --- print(ringbuf:pop()) -- returns "c" + --- + --- -- Can be used as iterator. Pops remaining items: + --- for val in ringbuf do + --- print(val) + --- end + --- </pre> + --- + --- Returns a Ringbuf instance with the following methods: + --- + --- - |Ringbuf:push()| + --- - |Ringbuf:pop()| + --- - |Ringbuf:peek()| + --- - |Ringbuf:clear()| + --- + ---@param size integer + ---@return vim.Ringbuf ringbuf (table) + function vim.ringbuf(size) + local ringbuf = { + _items = {}, + _size = size + 1, + _idx_read = 0, + _idx_write = 0, + } + return setmetatable(ringbuf, { + __index = Ringbuf, + __call = function(self) + return self:pop() + end, + }) + end +end + return vim diff --git a/runtime/lua/vim/treesitter.lua b/runtime/lua/vim/treesitter.lua index 12fbe1654f..5526c9858c 100644 --- a/runtime/lua/vim/treesitter.lua +++ b/runtime/lua/vim/treesitter.lua @@ -493,7 +493,7 @@ end --- argument and should return a string. function M.inspect_tree(opts) ---@cast opts InspectTreeOpts - require('vim.treesitter.playground').inspect_tree(opts) + require('vim.treesitter.dev').inspect_tree(opts) end --- Returns the fold level for {lnum} in the current buffer. Can be set directly to 'foldexpr': diff --git a/runtime/lua/vim/treesitter/_fold.lua b/runtime/lua/vim/treesitter/_fold.lua index a8f8c7967e..d308657237 100644 --- a/runtime/lua/vim/treesitter/_fold.lua +++ b/runtime/lua/vim/treesitter/_fold.lua @@ -162,9 +162,7 @@ local function get_folds_levels(bufnr, info, srow, erow) local parser = ts.get_parser(bufnr) - if not parser:is_valid() then - return - end + parser:parse() parser:for_each_tree(function(tree, ltree) local query = ts.query.get(ltree:lang(), 'folds') @@ -283,10 +281,12 @@ local function on_bytes(bufnr, foldinfo, start_row, old_row, new_row) local end_row_old = start_row + old_row local end_row_new = start_row + new_row - if new_row < old_row then - foldinfo:remove_range(end_row_new, end_row_old) - elseif new_row > old_row then - foldinfo:add_range(start_row, end_row_new) + if new_row ~= old_row then + if new_row < old_row then + foldinfo:remove_range(end_row_new, end_row_old) + else + foldinfo:add_range(start_row, end_row_new) + end schedule_if_loaded(bufnr, function() get_folds_levels(bufnr, foldinfo, start_row, end_row_new) recompute_folds() diff --git a/runtime/lua/vim/treesitter/_query_linter.lua b/runtime/lua/vim/treesitter/_query_linter.lua index ecdee5fc95..3dd0177a81 100644 --- a/runtime/lua/vim/treesitter/_query_linter.lua +++ b/runtime/lua/vim/treesitter/_query_linter.lua @@ -53,10 +53,7 @@ end local function guess_query_lang(buf) local filename = api.nvim_buf_get_name(buf) if filename ~= '' then - local ok, query_lang = pcall(vim.fn.fnamemodify, filename, ':p:h:t') - if ok then - return query_lang - end + return vim.F.npcall(vim.fn.fnamemodify, filename, ':p:h:t') end end @@ -270,11 +267,8 @@ function M.lint(buf, opts) for i = 1, math.max(1, #opts.langs) do local lang = opts.langs[i] - --- @type boolean, (table|nil) - local ok, parser_info = pcall(vim.treesitter.language.inspect, lang) - if not ok then - parser_info = nil - end + --- @type (table|nil) + local parser_info = vim.F.npcall(vim.treesitter.language.inspect, lang) local parser = vim.treesitter.get_parser(buf) parser:parse() diff --git a/runtime/lua/vim/treesitter/playground.lua b/runtime/lua/vim/treesitter/dev.lua index 8293c1bd0a..99cd147658 100644 --- a/runtime/lua/vim/treesitter/playground.lua +++ b/runtime/lua/vim/treesitter/dev.lua @@ -1,16 +1,16 @@ local api = vim.api ----@class TSPlaygroundModule +---@class TSDevModule local M = {} ----@class TSPlayground +---@class TSTreeView ---@field ns integer API namespace ---@field opts table Options table with the following keys: --- - anon (boolean): If true, display anonymous nodes --- - lang (boolean): If true, display the language alongside each node ---@field nodes TSP.Node[] ---@field named TSP.Node[] -local TSPlayground = {} +local TSTreeView = {} ---@class TSP.Node ---@field id integer Node id @@ -82,16 +82,16 @@ local function traverse(node, depth, lang, injections, tree) return tree end ---- Create a new Playground object. +--- Create a new treesitter view. --- ---@param bufnr integer Source buffer number ---@param lang string|nil Language of source buffer --- ----@return TSPlayground|nil +---@return TSTreeView|nil ---@return string|nil Error message, if any --- ---@package -function TSPlayground:new(bufnr, lang) +function TSTreeView:new(bufnr, lang) local ok, parser = pcall(vim.treesitter.get_parser, bufnr or 0, lang) if not ok then return nil, 'No parser available for the given buffer' @@ -139,7 +139,7 @@ function TSPlayground:new(bufnr, lang) return t end -local decor_ns = api.nvim_create_namespace('ts.playground') +local decor_ns = api.nvim_create_namespace('ts.dev') ---@private ---@param lnum integer @@ -154,11 +154,11 @@ local function get_range_str(lnum, col, end_lnum, end_col) return string.format('[%d:%d - %d:%d]', lnum + 1, col + 1, end_lnum + 1, end_col) end ---- Write the contents of this Playground into {bufnr}. +--- Write the contents of this View into {bufnr}. --- ---@param bufnr integer Buffer number to write into. ---@package -function TSPlayground:draw(bufnr) +function TSTreeView:draw(bufnr) vim.bo[bufnr].modifiable = true local lines = {} ---@type string[] local lang_hl_marks = {} ---@type table[] @@ -193,25 +193,25 @@ function TSPlayground:draw(bufnr) vim.bo[bufnr].modifiable = false end ---- Get node {i} from this Playground object. +--- Get node {i} from this View. --- --- The node number is dependent on whether or not anonymous nodes are displayed. --- ---@param i integer Node number to get ---@return TSP.Node ---@package -function TSPlayground:get(i) +function TSTreeView:get(i) local t = self.opts.anon and self.nodes or self.named return t[i] end ---- Iterate over all of the nodes in this Playground object. +--- Iterate over all of the nodes in this View. --- ----@return (fun(): integer, TSP.Node) Iterator over all nodes in this Playground +---@return (fun(): integer, TSP.Node) Iterator over all nodes in this View ---@return table ---@return integer ---@package -function TSPlayground:iter() +function TSTreeView:iter() return ipairs(self.opts.anon and self.nodes or self.named) end @@ -228,6 +228,8 @@ end --- function, it accepts the buffer number of the source --- buffer as its only argument and should return a string. +--- @private +--- --- @param opts InspectTreeOpts function M.inspect_tree(opts) vim.validate({ @@ -238,11 +240,11 @@ function M.inspect_tree(opts) local buf = api.nvim_get_current_buf() local win = api.nvim_get_current_win() - local pg = assert(TSPlayground:new(buf, opts.lang)) + local pg = assert(TSTreeView:new(buf, opts.lang)) - -- Close any existing playground window - if vim.b[buf].playground then - local w = vim.b[buf].playground + -- Close any existing dev window + if vim.b[buf].dev then + local w = vim.b[buf].dev if api.nvim_win_is_valid(w) then api.nvim_win_close(w, true) end @@ -261,7 +263,7 @@ function M.inspect_tree(opts) b = api.nvim_win_get_buf(w) end - vim.b[buf].playground = w + vim.b[buf].dev = w vim.wo[w].scrolloff = 5 vim.wo[w].wrap = false @@ -330,7 +332,7 @@ function M.inspect_tree(opts) end, }) - local group = api.nvim_create_augroup('treesitter/playground', {}) + local group = api.nvim_create_augroup('treesitter/dev', {}) api.nvim_create_autocmd('CursorMoved', { group = group, @@ -399,7 +401,7 @@ function M.inspect_tree(opts) return true end - pg = assert(TSPlayground:new(buf, opts.lang)) + pg = assert(TSTreeView:new(buf, opts.lang)) pg:draw(b) end, }) diff --git a/runtime/lua/vim/treesitter/highlighter.lua b/runtime/lua/vim/treesitter/highlighter.lua index 4bb764c5c6..d4db6bc404 100644 --- a/runtime/lua/vim/treesitter/highlighter.lua +++ b/runtime/lua/vim/treesitter/highlighter.lua @@ -260,12 +260,14 @@ local function on_line_impl(self, buf, line, is_spell_nav) local spell_pri_offset = capture_name == 'nospell' and 1 or 0 if hl and end_row >= line and (not is_spell_nav or spell ~= nil) then + local priority = (tonumber(metadata.priority) or vim.highlight.priorities.treesitter) + + spell_pri_offset api.nvim_buf_set_extmark(buf, ns, start_row, start_col, { end_line = end_row, end_col = end_col, hl_group = hl, ephemeral = true, - priority = (tonumber(metadata.priority) or 100) + spell_pri_offset, -- Low but leaves room below + priority = priority, conceal = metadata.conceal, spell = spell, }) diff --git a/runtime/lua/vim/treesitter/languagetree.lua b/runtime/lua/vim/treesitter/languagetree.lua index cabfa8ccc0..bf6333aaa4 100644 --- a/runtime/lua/vim/treesitter/languagetree.lua +++ b/runtime/lua/vim/treesitter/languagetree.lua @@ -447,6 +447,9 @@ end ---@private ---@param region Range6[] local function region_tostr(region) + if #region == 0 then + return '[]' + end local srow, scol = region[1][1], region[1][2] local erow, ecol = region[#region][4], region[#region][5] return string.format('[%d:%d-%d:%d]', srow, scol, erow, ecol) diff --git a/runtime/lua/vim/treesitter/query.lua b/runtime/lua/vim/treesitter/query.lua index 73b561c777..7610ef7b7f 100644 --- a/runtime/lua/vim/treesitter/query.lua +++ b/runtime/lua/vim/treesitter/query.lua @@ -195,6 +195,12 @@ function M.set(lang, query_name, text) explicit_queries[lang][query_name] = M.parse(lang, text) end +--- `false` if query files didn't exist or were empty +---@type table<string, table<string, Query|false>> +local query_get_cache = vim.defaulttable(function() + return setmetatable({}, { __mode = 'v' }) +end) + ---@deprecated function M.get_query(...) vim.deprecate('vim.treesitter.query.get_query()', 'vim.treesitter.query.get()', '0.10') @@ -212,16 +218,28 @@ function M.get(lang, query_name) return explicit_queries[lang][query_name] end + local cached = query_get_cache[lang][query_name] + if cached then + return cached + elseif cached == false then + return nil + end + local query_files = M.get_files(lang, query_name) local query_string = read_query_files(query_files) - if #query_string > 0 then - return M.parse(lang, query_string) + if #query_string == 0 then + query_get_cache[lang][query_name] = false + return nil end + + local query = M.parse(lang, query_string) + query_get_cache[lang][query_name] = query + return query end ----@type {[string]: {[string]: Query}} -local query_cache = vim.defaulttable(function() +---@type table<string, table<string, Query>> +local query_parse_cache = vim.defaulttable(function() return setmetatable({}, { __mode = 'v' }) end) @@ -250,7 +268,7 @@ end ---@return Query Parsed query function M.parse(lang, query) language.add(lang) - local cached = query_cache[lang][query] + local cached = query_parse_cache[lang][query] if cached then return cached end @@ -259,7 +277,7 @@ function M.parse(lang, query) self.query = vim._ts_parse_query(lang, query) self.info = self.query:inspect() self.captures = self.info.captures - query_cache[lang][query] = self + query_parse_cache[lang][query] = self return self end @@ -475,7 +493,6 @@ local directive_handlers = { metadata[capture_id].range = range end end, - -- Transform the content of the node -- Example: (#gsub! @_node ".*%.(.*)" "%1") ['gsub!'] = function(match, _, bufnr, pred, metadata) @@ -497,6 +514,65 @@ local directive_handlers = { metadata[id].text = text:gsub(pattern, replacement) end, + -- Trim blank lines from end of the node + -- Example: (#trim! @fold) + -- TODO(clason): generalize to arbitrary whitespace removal + ['trim!'] = function(match, _, bufnr, pred, metadata) + local node = match[pred[2]] + if not node then + return + end + + local start_row, start_col, end_row, end_col = node:range() + + -- Don't trim if region ends in middle of a line + if end_col ~= 0 then + return + end + + while true do + -- As we only care when end_col == 0, always inspect one line above end_row. + local end_line = vim.api.nvim_buf_get_lines(bufnr, end_row - 1, end_row, true)[1] + + if end_line ~= '' then + break + end + + end_row = end_row - 1 + end + + -- If this produces an invalid range, we just skip it. + if start_row < end_row or (start_row == end_row and start_col <= end_col) then + metadata.range = { start_row, start_col, end_row, end_col } + end + end, + -- Set injection language from node text, interpreted first as language and then as filetype + -- Example: (#inject-language! @_lang) + ['inject-language!'] = function(match, _, bufnr, pred, metadata) + local id = pred[2] + local node = match[id] + if not node then + return + end + + -- TODO(clason): replace by refactored `ts.has_parser` API + local has_parser = function(lang) + return vim._ts_has_language(lang) + or #vim.api.nvim_get_runtime_file('parser/' .. lang .. '.*', false) > 0 + end + + local alias = vim.treesitter.get_node_text(node, bufnr, { metadata = metadata[id] }) + if not alias then + return + elseif has_parser(alias) then + metadata['injection.language'] = alias + else + local lang = vim.treesitter.language.get_lang(alias) + if lang and has_parser(lang) then + metadata['injection.language'] = lang + end + end + end, } --- Adds a new predicate to be used in queries diff --git a/runtime/lua/vim/version.lua b/runtime/lua/vim/version.lua index ebe8f4e053..cd28a9b54b 100644 --- a/runtime/lua/vim/version.lua +++ b/runtime/lua/vim/version.lua @@ -125,7 +125,7 @@ function Version:__tostring() if self.prerelease then ret = ret .. '-' .. self.prerelease end - if self.build then + if self.build and self.build ~= vim.NIL then ret = ret .. '+' .. self.build end return ret @@ -226,6 +226,9 @@ function Range:has(version) if type(version) == 'string' then ---@diagnostic disable-next-line: cast-local-type version = M.parse(version) + elseif getmetatable(version) ~= Version then + -- Need metatable to compare versions. + version = setmetatable(vim.deepcopy(version), Version) end if version then if version.prerelease ~= self.from.prerelease then @@ -244,11 +247,14 @@ end --- } --- </pre> --- ---- `:has()` checks if a version is in the range (inclusive `from`, exclusive `to`). Example: +--- `:has()` checks if a version is in the range (inclusive `from`, exclusive `to`). +--- +--- Example: --- <pre>lua --- local r = vim.version.range('1.0.0 - 2.0.0') ---- print(r:has('1.9.9')) -- true ---- print(r:has('2.0.0')) -- false +--- print(r:has('1.9.9')) -- true +--- print(r:has('2.0.0')) -- false +--- print(r:has(vim.version())) -- check against current Nvim version --- </pre> --- --- Or use cmp(), eq(), lt(), and gt() to compare `.to` and `.from` directly: @@ -279,7 +285,7 @@ function M.range(spec) -- Adapted from https://github.com/folke/lazy.nvim }, { __index = Range }) end ---@type string, string - local mods, version = spec:lower():match('^([%^=>~]*)(.*)$') + local mods, version = spec:lower():match('^([%^=<>~]*)(.*)$') version = version:gsub('%.[%*x]', '') local parts = vim.split(version:gsub('%-.*', ''), '.', { plain = true }) if #parts < 3 and mods == '' then @@ -292,6 +298,11 @@ function M.range(spec) -- Adapted from https://github.com/folke/lazy.nvim local to = vim.deepcopy(semver) if mods == '' or mods == '=' then to.patch = to.patch + 1 + elseif mods == '<' then + from = M._version({}) + elseif mods == '<=' then + from = M._version({}) + to.patch = to.patch + 1 elseif mods == '>' then from.patch = from.patch + 1 to = nil ---@diagnostic disable-line: cast-local-type @@ -410,8 +421,12 @@ function M.parse(version, opts) end setmetatable(M, { + --- Returns the current Nvim version. __call = function() - return vim.fn.api_info().version + local version = vim.fn.api_info().version + -- Workaround: vim.fn.api_info().version reports "prerelease" as a boolean. + version.prerelease = version.prerelease and 'dev' or nil + return setmetatable(version, Version) end, }) diff --git a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim index 43c3d7541f..e8b78b3f5f 100644 --- a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim +++ b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim @@ -2,7 +2,7 @@ " " Author: Bram Moolenaar " Copyright: Vim license applies, see ":help license" -" Last Change: 2022 Nov 10 +" Last Change: 2023 Jun 24 " " WORK IN PROGRESS - The basics works stable, more to come " Note: In general you need at least GDB 7.12 because this provides the @@ -87,6 +87,8 @@ func s:Breakpoint2SignNumber(id, subid) return s:break_id + a:id * 1000 + a:subid endfunction +" Define or adjust the default highlighting, using background "new". +" When the 'background' option is set then "old" has the old value. func s:Highlight(init, old, new) let default = a:init ? 'default ' : '' if a:new ==# 'light' && a:old !=# 'light' @@ -96,9 +98,21 @@ func s:Highlight(init, old, new) endif endfunc -call s:Highlight(1, '', &background) -hi default debugBreakpoint term=reverse ctermbg=red guibg=red -hi default debugBreakpointDisabled term=reverse ctermbg=gray guibg=gray +" Define the default highlighting, using the current 'background' value. +func s:InitHighlight() + call s:Highlight(1, '', &background) + hi default debugBreakpoint term=reverse ctermbg=red guibg=red + hi default debugBreakpointDisabled term=reverse ctermbg=gray guibg=gray +endfunc + +" Setup an autocommand to redefine the default highlight when the colorscheme +" is changed. +func s:InitAutocmd() + augroup TermDebug + autocmd! + autocmd ColorScheme * call s:InitHighlight() + augroup END +endfunc " Get the command to execute the debugger as a list, defaults to ["gdb"]. func s:GetCommand() @@ -626,7 +640,7 @@ func s:GdbOutCallback(job_id, msgs, event) for msg in a:msgs if msg =~ '^\^error,msg=' if exists('s:evalexpr') - \ && s:DecodeMessage(msg[11:]) + \ && s:DecodeMessage(msg[11:], v:false) \ =~ 'A syntax error in expression, near\|No symbol .* in current context' " Silently drop evaluation errors. call remove(a:msgs, index) @@ -634,7 +648,7 @@ func s:GdbOutCallback(job_id, msgs, event) continue endif elseif msg[0] == '~' - call add(lines, s:DecodeMessage(msg[1:])) + call add(lines, s:DecodeMessage(msg[1:], v:false)) call remove(a:msgs, index) continue endif @@ -656,21 +670,20 @@ func s:GdbOutCallback(job_id, msgs, event) call s:CommOutput(a:job_id, a:msgs, a:event) endfunc -" Decode a message from gdb. quotedText starts with a ", return the text up +" Decode a message from gdb. "quotedText" starts with a ", return the text up " to the next ", unescaping characters: -" - remove line breaks -" - change \\t to \t +" - remove line breaks (unless "literal" is v:true) +" - change \\t to \t (unless "literal" is v:true) " - change \0xhh to \xhh (disabled for now) " - change \ooo to octal " - change \\ to \ -func s:DecodeMessage(quotedText) +func s:DecodeMessage(quotedText, literal) if a:quotedText[0] != '"' echoerr 'DecodeMessage(): missing quote in ' . a:quotedText return endif - return a:quotedText - \ ->substitute('^"\|".*\|\\n', '', 'g') - \ ->substitute('\\t', "\t", 'g') + let msg = a:quotedText + \ ->substitute('^"\|".*', '', 'g') " multi-byte characters arrive in octal form " NULL-values must be kept encoded as those break the string otherwise \ ->substitute('\\000', s:NullRepl, 'g') @@ -682,6 +695,13 @@ func s:DecodeMessage(quotedText) " \ ->substitute('\\0x00', s:NullRepl, 'g') \ ->substitute('\\\\', '\', 'g') \ ->substitute(s:NullRepl, '\\000', 'g') + if !a:literal + return msg + \ ->substitute('\\t', "\t", 'g') + \ ->substitute('\\n', '', 'g') + else + return msg + endif endfunc const s:NullRepl = 'XXXNULLXXX' @@ -690,7 +710,7 @@ func s:GetFullname(msg) if a:msg !~ 'fullname' return '' endif - let name = s:DecodeMessage(substitute(a:msg, '.*fullname=', '', '')) + let name = s:DecodeMessage(substitute(a:msg, '.*fullname=', '', ''), v:true) if has('win32') && name =~ ':\\\\' " sometimes the name arrives double-escaped let name = substitute(name, '\\\\', '\\', 'g') @@ -703,11 +723,11 @@ func s:GetAsmAddr(msg) if a:msg !~ 'addr=' return '' endif - let addr = s:DecodeMessage(substitute(a:msg, '.*addr=', '', '')) + let addr = s:DecodeMessage(substitute(a:msg, '.*addr=', '', ''), v:false) return addr endfunc -function s:EndTermDebug(job_id, exit_code, event) +func s:EndTermDebug(job_id, exit_code, event) let s:running = v:false if s:starting return @@ -1523,8 +1543,18 @@ func s:CreateBreakpoint(id, subid, enabled) else let hiName = "debugBreakpoint" endif + let label = '' + if exists('g:termdebug_config') + let label = get(g:termdebug_config, 'sign', '') + endif + if label == '' + let label = substitute(nr, '\..*', '', '') + if strlen(label) > 2 + let label = strpart(label, strlen(label) - 2) + endif + endif call sign_define('debugBreakpoint' .. nr, - \ #{text: substitute(nr, '\..*', '', ''), + \ #{text: strpart(label, 0, 2), \ texthl: hiName}) endif endfunc @@ -1664,5 +1694,8 @@ func s:BufUnloaded() endfor endfunc +call s:InitHighlight() +call s:InitAutocmd() + let &cpo = s:keepcpo unlet s:keepcpo diff --git a/runtime/queries/bash/folds.scm b/runtime/queries/bash/folds.scm new file mode 100644 index 0000000000..851c67eed4 --- /dev/null +++ b/runtime/queries/bash/folds.scm @@ -0,0 +1,8 @@ +[ + (function_definition) + (if_statement) + (case_statement) + (for_statement) + (while_statement) + (c_style_for_statement) +] @fold diff --git a/runtime/queries/bash/highlights.scm b/runtime/queries/bash/highlights.scm new file mode 100644 index 0000000000..23bf03e697 --- /dev/null +++ b/runtime/queries/bash/highlights.scm @@ -0,0 +1,145 @@ +(simple_expansion) @none +(expansion + "${" @punctuation.special + "}" @punctuation.special) @none +[ + "(" + ")" + "((" + "))" + "{" + "}" + "[" + "]" + "[[" + "]]" + ] @punctuation.bracket + +[ + ";" + ";;" + (heredoc_start) + ] @punctuation.delimiter + +[ + "$" +] @punctuation.special + +[ + ">" + ">>" + "<" + "<<" + "&" + "&&" + "|" + "||" + "=" + "=~" + "==" + "!=" + ] @operator + +[ + (string) + (raw_string) + (ansi_c_string) + (heredoc_body) +] @string @spell + +(variable_assignment (word) @string) + +[ + "if" + "then" + "else" + "elif" + "fi" + "case" + "in" + "esac" + ] @conditional + +[ + "for" + "do" + "done" + "select" + "until" + "while" + ] @repeat + +[ + "declare" + "export" + "local" + "readonly" + "unset" + ] @keyword + +"function" @keyword.function + +(special_variable_name) @constant + +; trap -l +((word) @constant.builtin + (#match? @constant.builtin "^SIG(HUP|INT|QUIT|ILL|TRAP|ABRT|BUS|FPE|KILL|USR[12]|SEGV|PIPE|ALRM|TERM|STKFLT|CHLD|CONT|STOP|TSTP|TT(IN|OU)|URG|XCPU|XFSZ|VTALRM|PROF|WINCH|IO|PWR|SYS|RTMIN([+]([1-9]|1[0-5]))?|RTMAX(-([1-9]|1[0-4]))?)$")) + +((word) @boolean + (#any-of? @boolean "true" "false")) + +(comment) @comment @spell +(test_operator) @string + +(command_substitution + [ "$(" ")" ] @punctuation.bracket) + +(process_substitution + [ "<(" ")" ] @punctuation.bracket) + + +(function_definition + name: (word) @function) + +(command_name (word) @function.call) + +((command_name (word) @function.builtin) + (#any-of? @function.builtin + "alias" "bg" "bind" "break" "builtin" "caller" "cd" + "command" "compgen" "complete" "compopt" "continue" + "coproc" "dirs" "disown" "echo" "enable" "eval" + "exec" "exit" "fc" "fg" "getopts" "hash" "help" + "history" "jobs" "kill" "let" "logout" "mapfile" + "popd" "printf" "pushd" "pwd" "read" "readarray" + "return" "set" "shift" "shopt" "source" "suspend" + "test" "time" "times" "trap" "type" "typeset" + "ulimit" "umask" "unalias" "wait")) + +(command + argument: [ + (word) @parameter + (concatenation (word) @parameter) + ]) + +((word) @number + (#lua-match? @number "^[0-9]+$")) + +(file_redirect + descriptor: (file_descriptor) @operator + destination: (word) @parameter) + +(expansion + [ "${" "}" ] @punctuation.bracket) + +(variable_name) @variable + +((variable_name) @constant + (#lua-match? @constant "^[A-Z][A-Z_0-9]*$")) + +(case_item + value: (word) @parameter) + +(regex) @string.regex + +((program . (comment) @preproc) + (#lua-match? @preproc "^#!/")) diff --git a/runtime/queries/lua/highlights.scm b/runtime/queries/lua/highlights.scm index 537a171441..96ffeae793 100644 --- a/runtime/queries/lua/highlights.scm +++ b/runtime/queries/lua/highlights.scm @@ -180,13 +180,40 @@ (parameters (identifier) @parameter) -(function_call name: (identifier) @function.call) -(function_declaration name: (identifier) @function) +(function_declaration + name: [ + (identifier) @function + (dot_index_expression + field: (identifier) @function) + ]) -(function_call name: (dot_index_expression field: (identifier) @function.call)) -(function_declaration name: (dot_index_expression field: (identifier) @function)) +(function_declaration + name: (method_index_expression + method: (identifier) @method)) + +(assignment_statement + (variable_list . + name: [ + (identifier) @function + (dot_index_expression + field: (identifier) @function) + ]) + (expression_list . + value: (function_definition))) -(method_index_expression method: (identifier) @method.call) +(table_constructor + (field + name: (identifier) @function + value: (function_definition))) + +(function_call + name: [ + (identifier) @function.call + (dot_index_expression + field: (identifier) @function.call) + (method_index_expression + method: (identifier) @method.call) + ]) (function_call (identifier) @function.builtin diff --git a/runtime/queries/lua/injections.scm b/runtime/queries/lua/injections.scm index 3fcebe83f3..dbfe75ae31 100644 --- a/runtime/queries/lua/injections.scm +++ b/runtime/queries/lua/injections.scm @@ -3,7 +3,9 @@ (identifier) @_cdef_identifier (_ _ (identifier) @_cdef_identifier) ] - arguments: (arguments (string content: _ @injection.content))) + arguments: + (arguments + (string content: _ @injection.content))) (#set! injection.language "c") (#eq? @_cdef_identifier "cdef")) @@ -11,15 +13,7 @@ name: (_) @_vimcmd_identifier arguments: (arguments (string content: _ @injection.content))) (#set! injection.language "vim") - (#any-of? @_vimcmd_identifier "vim.cmd" "vim.api.nvim_command" "vim.api.nvim_exec2" "vim.api.nvim_cmd")) - -; vim.rcprequest(123, "nvim_exec_lua", "return vim.api.nvim_buf_get_lines(0, 0, -1, false)", false) -((function_call - name: (_) @_vimcmd_identifier - arguments: (arguments . (_) . (string content: _ @_method) . (string content: _ @injection.content))) - (#set! injection.language "lua") - (#any-of? @_vimcmd_identifier "vim.rpcrequest" "vim.rpcnotify") - (#eq? @_method "nvim_exec_lua")) + (#any-of? @_vimcmd_identifier "vim.cmd" "vim.api.nvim_command" "vim.api.nvim_command" "vim.api.nvim_exec2")) ((function_call name: (_) @_vimcmd_identifier @@ -27,10 +21,15 @@ (#set! injection.language "query") (#any-of? @_vimcmd_identifier "vim.treesitter.query.set" "vim.treesitter.query.parse")) +((function_call + name: (_) @_vimcmd_identifier + arguments: (arguments . (_) . (string content: _ @_method) . (string content: _ @injection.content))) + (#any-of? @_vimcmd_identifier "vim.rpcrequest" "vim.rpcnotify") + (#eq? @_method "nvim_exec_lua") + (#set! injection.language "lua")) + ;; highlight string as query if starts with `;; query` (string content: _ @injection.content - (#set! injection.language "query") - (#lua-match? @injection.content "^%s*;+%s?query")) + (#lua-match? @injection.content "^%s*;+%s?query") + (#set! injection.language "query")) -; ((comment) @injection.content -; (#set! injection.language "comment")) diff --git a/runtime/queries/markdown/folds.scm b/runtime/queries/markdown/folds.scm new file mode 100644 index 0000000000..5900f7ffbe --- /dev/null +++ b/runtime/queries/markdown/folds.scm @@ -0,0 +1,9 @@ +( + [ + (fenced_code_block) + (indented_code_block) + (list) + (section) + ] @fold + (#trim! @fold) +) diff --git a/runtime/queries/markdown/highlights.scm b/runtime/queries/markdown/highlights.scm new file mode 100644 index 0000000000..e78d233cc6 --- /dev/null +++ b/runtime/queries/markdown/highlights.scm @@ -0,0 +1,63 @@ +;From MDeiml/tree-sitter-markdown & Helix +(setext_heading (paragraph) @text.title.1 (setext_h1_underline) @text.title.1.marker) +(setext_heading (paragraph) @text.title.2 (setext_h2_underline) @text.title.2.marker) + +(atx_heading (atx_h1_marker) @text.title.1.marker (inline) @text.title.1) +(atx_heading (atx_h2_marker) @text.title.2.marker (inline) @text.title.2) +(atx_heading (atx_h3_marker) @text.title.3.marker (inline) @text.title.3) +(atx_heading (atx_h4_marker) @text.title.4.marker (inline) @text.title.4) +(atx_heading (atx_h5_marker) @text.title.5.marker (inline) @text.title.5) +(atx_heading (atx_h6_marker) @text.title.6.marker (inline) @text.title.6) + +(link_title) @text.literal +(indented_code_block) @text.literal.block +((fenced_code_block) @text.literal.block (#set! "priority" 90)) + +(info_string) @label + +(pipe_table_header (pipe_table_cell) @text.title) + +(pipe_table_header "|" @punctuation.special) +(pipe_table_row "|" @punctuation.special) +(pipe_table_delimiter_row "|" @punctuation.special) +(pipe_table_delimiter_cell) @punctuation.special + +[ + (fenced_code_block_delimiter) +] @punctuation.delimiter + +(code_fence_content) @none + +[ + (link_destination) +] @text.uri + +[ + (link_label) +] @text.reference + +[ + (list_marker_plus) + (list_marker_minus) + (list_marker_star) + (list_marker_dot) + (list_marker_parenthesis) + (thematic_break) +] @punctuation.special + + +(task_list_marker_unchecked) @text.todo.unchecked +(task_list_marker_checked) @text.todo.checked + +(block_quote) @text.quote + +[ + (block_continuation) + (block_quote_marker) +] @punctuation.special + +[ + (backslash_escape) +] @string.escape + +(inline) @spell diff --git a/runtime/queries/markdown/injections.scm b/runtime/queries/markdown/injections.scm new file mode 100644 index 0000000000..0bead6f4ac --- /dev/null +++ b/runtime/queries/markdown/injections.scm @@ -0,0 +1,26 @@ +(fenced_code_block + (info_string + (language) @_lang) + (code_fence_content) @injection.content + (#inject-language! @_lang)) + +((html_block) @injection.content + (#set! injection.language "html") + (#set! injection.combined) + (#set! injection.include-children)) + +((minus_metadata) @injection.content + (#set! injection.language "yaml") + (#offset! @injection.content 1 0 -1 0) + (#set! injection.include-children)) + +((plus_metadata) @injection.content + (#set! injection.language "toml") + (#offset! @injection.content 1 0 -1 0) + (#set! injection.include-children)) + +([ + (inline) + (pipe_table_cell) + ] @injection.content + (#set! injection.language "markdown_inline")) diff --git a/runtime/queries/markdown_inline/highlights.scm b/runtime/queries/markdown_inline/highlights.scm new file mode 100644 index 0000000000..a70e34bb87 --- /dev/null +++ b/runtime/queries/markdown_inline/highlights.scm @@ -0,0 +1,92 @@ +;; From MDeiml/tree-sitter-markdown +[ + (code_span) + (link_title) +] @text.literal @nospell + +[ + (emphasis_delimiter) + (code_span_delimiter) +] @punctuation.delimiter + +(emphasis) @text.emphasis + +(strong_emphasis) @text.strong + +(strikethrough) @text.strike + +[ + (link_destination) + (uri_autolink) +] @text.uri @nospell + +[ + (link_label) + (link_text) + (image_description) +] @text.reference + +[ + (backslash_escape) + (hard_line_break) +] @string.escape + +(image "!" @punctuation.special) +(image ["[" "]" "(" ")"] @punctuation.bracket) +(inline_link ["[" "]" "(" ")"] @punctuation.bracket) +(shortcut_link ["[" "]"] @punctuation.bracket) + +; Conceal codeblock and text style markers +([ + (code_span_delimiter) + (emphasis_delimiter) +] @conceal +(#set! conceal "")) + +; Conceal inline links +(inline_link + [ + "[" + "]" + "(" + (link_destination) + ")" + ] @conceal + (#set! conceal "")) + +; Conceal image links +(image + [ + "!" + "[" + "]" + "(" + (link_destination) + ")" + ] @conceal + (#set! conceal "")) + +; Conceal full reference links +(full_reference_link + [ + "[" + "]" + (link_label) + ] @conceal + (#set! conceal "")) + +; Conceal collapsed reference links +(collapsed_reference_link + [ + "[" + "]" + ] @conceal + (#set! conceal "")) + +; Conceal shortcut links +(shortcut_link + [ + "[" + "]" + ] @conceal + (#set! conceal "")) diff --git a/runtime/queries/markdown_inline/injections.scm b/runtime/queries/markdown_inline/injections.scm new file mode 100644 index 0000000000..f7aa19caff --- /dev/null +++ b/runtime/queries/markdown_inline/injections.scm @@ -0,0 +1,8 @@ +((html_tag) @injection.content + (#set! injection.language "html") + (#set! injection.combined) + (#set! injection.include-children)) + +((latex_block) @injection.content + (#set! injection.language "latex") + (#set! injection.include-children)) diff --git a/runtime/queries/python/folds.scm b/runtime/queries/python/folds.scm new file mode 100644 index 0000000000..78e1e2c00d --- /dev/null +++ b/runtime/queries/python/folds.scm @@ -0,0 +1,28 @@ +[ + (function_definition) + (class_definition) + + (while_statement) + (for_statement) + (if_statement) + (with_statement) + (try_statement) + (match_statement) + + (import_from_statement) + (parameters) + (argument_list) + + (parenthesized_expression) + (generator_expression) + (list_comprehension) + (set_comprehension) + (dictionary_comprehension) + + (tuple) + (list) + (set) + (dictionary) + + (string) +] @fold diff --git a/runtime/queries/python/highlights.scm b/runtime/queries/python/highlights.scm new file mode 100644 index 0000000000..c18b748674 --- /dev/null +++ b/runtime/queries/python/highlights.scm @@ -0,0 +1,345 @@ +;; From tree-sitter-python licensed under MIT License +; Copyright (c) 2016 Max Brunsfeld + +; Variables +(identifier) @variable + +; Reset highlighting in f-string interpolations +(interpolation) @none + +;; Identifier naming conventions +((identifier) @type + (#lua-match? @type "^[A-Z].*[a-z]")) +((identifier) @constant + (#lua-match? @constant "^[A-Z][A-Z_0-9]*$")) + +((identifier) @constant.builtin + (#lua-match? @constant.builtin "^__[a-zA-Z0-9_]*__$")) + +((identifier) @constant.builtin + (#any-of? @constant.builtin + ;; https://docs.python.org/3/library/constants.html + "NotImplemented" + "Ellipsis" + "quit" + "exit" + "copyright" + "credits" + "license")) + +((attribute + attribute: (identifier) @field) + (#lua-match? @field "^[%l_].*$")) + +((assignment + left: (identifier) @type.definition + (type (identifier) @_annotation)) + (#eq? @_annotation "TypeAlias")) + +((assignment + left: (identifier) @type.definition + right: (call + function: (identifier) @_func)) + (#any-of? @_func "TypeVar" "NewType")) + +; Function calls + +(call + function: (identifier) @function.call) + +(call + function: (attribute + attribute: (identifier) @method.call)) + +((call + function: (identifier) @constructor) + (#lua-match? @constructor "^%u")) + +((call + function: (attribute + attribute: (identifier) @constructor)) + (#lua-match? @constructor "^%u")) + +;; Decorators + +((decorator "@" @attribute) + (#set! "priority" 101)) + +(decorator + (identifier) @attribute) +(decorator + (attribute + attribute: (identifier) @attribute)) +(decorator + (call (identifier) @attribute)) +(decorator + (call (attribute + attribute: (identifier) @attribute))) + +((decorator + (identifier) @attribute.builtin) + (#any-of? @attribute.builtin "classmethod" "property")) + +;; Builtin functions + +((call + function: (identifier) @function.builtin) + (#any-of? @function.builtin + "abs" "all" "any" "ascii" "bin" "bool" "breakpoint" "bytearray" "bytes" "callable" "chr" "classmethod" + "compile" "complex" "delattr" "dict" "dir" "divmod" "enumerate" "eval" "exec" "filter" "float" "format" + "frozenset" "getattr" "globals" "hasattr" "hash" "help" "hex" "id" "input" "int" "isinstance" "issubclass" + "iter" "len" "list" "locals" "map" "max" "memoryview" "min" "next" "object" "oct" "open" "ord" "pow" + "print" "property" "range" "repr" "reversed" "round" "set" "setattr" "slice" "sorted" "staticmethod" "str" + "sum" "super" "tuple" "type" "vars" "zip" "__import__")) + +;; Function definitions + +(function_definition + name: (identifier) @function) + +(type (identifier) @type) +(type + (subscript + (identifier) @type)) ; type subscript: Tuple[int] + +((call + function: (identifier) @_isinstance + arguments: (argument_list + (_) + (identifier) @type)) + (#eq? @_isinstance "isinstance")) + +;; Normal parameters +(parameters + (identifier) @parameter) +;; Lambda parameters +(lambda_parameters + (identifier) @parameter) +(lambda_parameters + (tuple_pattern + (identifier) @parameter)) +; Default parameters +(keyword_argument + name: (identifier) @parameter) +; Naming parameters on call-site +(default_parameter + name: (identifier) @parameter) +(typed_parameter + (identifier) @parameter) +(typed_default_parameter + (identifier) @parameter) +; Variadic parameters *args, **kwargs +(parameters + (list_splat_pattern ; *args + (identifier) @parameter)) +(parameters + (dictionary_splat_pattern ; **kwargs + (identifier) @parameter)) + + +;; Literals + +(none) @constant.builtin +[(true) (false)] @boolean +((identifier) @variable.builtin + (#eq? @variable.builtin "self")) +((identifier) @variable.builtin + (#eq? @variable.builtin "cls")) + +(integer) @number +(float) @float + +(comment) @comment @spell + +((module . (comment) @preproc) + (#lua-match? @preproc "^#!/")) + +(string) @string +(escape_sequence) @string.escape + +; doc-strings + +(module . (expression_statement (string) @string.documentation @spell)) + +(class_definition + body: + (block + . (expression_statement (string) @string.documentation @spell))) + +(function_definition + body: + (block + . (expression_statement (string) @string.documentation @spell))) + +; Tokens + +[ + "-" + "-=" + ":=" + "!=" + "*" + "**" + "**=" + "*=" + "/" + "//" + "//=" + "/=" + "&" + "&=" + "%" + "%=" + "^" + "^=" + "+" + "+=" + "<" + "<<" + "<<=" + "<=" + "<>" + "=" + "==" + ">" + ">=" + ">>" + ">>=" + "@" + "@=" + "|" + "|=" + "~" + "->" +] @operator + +; Keywords +[ + "and" + "in" + "is" + "not" + "or" + "is not" + "not in" + + "del" +] @keyword.operator + +[ + "def" + "lambda" +] @keyword.function + +[ + "assert" + "class" + "exec" + "global" + "nonlocal" + "pass" + "print" + "with" + "as" +] @keyword + +[ + "async" + "await" +] @keyword.coroutine + +[ + "return" + "yield" +] @keyword.return +(yield "from" @keyword.return) + +(future_import_statement + "from" @include + "__future__" @constant.builtin) +(import_from_statement "from" @include) +"import" @include + +(aliased_import "as" @include) + +["if" "elif" "else" "match" "case"] @conditional + +["for" "while" "break" "continue"] @repeat + +[ + "try" + "except" + "except*" + "raise" + "finally" +] @exception + +(raise_statement "from" @exception) + +(try_statement + (else_clause + "else" @exception)) + +["(" ")" "[" "]" "{" "}"] @punctuation.bracket + +(interpolation + "{" @punctuation.special + "}" @punctuation.special) + +(type_conversion) @function.macro + +["," "." ":" ";" (ellipsis)] @punctuation.delimiter + +;; Class definitions + +(class_definition name: (identifier) @type) + +(class_definition + body: (block + (function_definition + name: (identifier) @method))) + +(class_definition + superclasses: (argument_list + (identifier) @type)) + +((class_definition + body: (block + (expression_statement + (assignment + left: (identifier) @field)))) + (#lua-match? @field "^%l.*$")) +((class_definition + body: (block + (expression_statement + (assignment + left: (_ + (identifier) @field))))) + (#lua-match? @field "^%l.*$")) + +((class_definition + (block + (function_definition + name: (identifier) @constructor))) + (#any-of? @constructor "__new__" "__init__")) + +((identifier) @type.builtin + (#any-of? @type.builtin + ;; https://docs.python.org/3/library/exceptions.html + "BaseException" "Exception" "ArithmeticError" "BufferError" "LookupError" "AssertionError" "AttributeError" + "EOFError" "FloatingPointError" "GeneratorExit" "ImportError" "ModuleNotFoundError" "IndexError" "KeyError" + "KeyboardInterrupt" "MemoryError" "NameError" "NotImplementedError" "OSError" "OverflowError" "RecursionError" + "ReferenceError" "RuntimeError" "StopIteration" "StopAsyncIteration" "SyntaxError" "IndentationError" "TabError" + "SystemError" "SystemExit" "TypeError" "UnboundLocalError" "UnicodeError" "UnicodeEncodeError" "UnicodeDecodeError" + "UnicodeTranslateError" "ValueError" "ZeroDivisionError" "EnvironmentError" "IOError" "WindowsError" + "BlockingIOError" "ChildProcessError" "ConnectionError" "BrokenPipeError" "ConnectionAbortedError" + "ConnectionRefusedError" "ConnectionResetError" "FileExistsError" "FileNotFoundError" "InterruptedError" + "IsADirectoryError" "NotADirectoryError" "PermissionError" "ProcessLookupError" "TimeoutError" "Warning" + "UserWarning" "DeprecationWarning" "PendingDeprecationWarning" "SyntaxWarning" "RuntimeWarning" + "FutureWarning" "ImportWarning" "UnicodeWarning" "BytesWarning" "ResourceWarning" + ;; https://docs.python.org/3/library/stdtypes.html + "bool" "int" "float" "complex" "list" "tuple" "range" "str" + "bytes" "bytearray" "memoryview" "set" "frozenset" "dict" "type" "object")) + +;; Error +(ERROR) @error diff --git a/runtime/syntax/meson.vim b/runtime/syntax/meson.vim index 0af0d776f8..4eaf696322 100644 --- a/runtime/syntax/meson.vim +++ b/runtime/syntax/meson.vim @@ -3,7 +3,7 @@ " License: VIM License " Maintainer: Nirbheek Chauhan <nirbheek.chauhan@gmail.com> " Liam Beguin <liambeguin@gmail.com> -" Last Change: 2021 Aug 16 +" Last Change: 2023 May 27 " Credits: Zvezdan Petkovic <zpetkovic@acm.org> " Neil Schemenauer <nas@meson.ca> " Dmitry Vasiliev @@ -68,6 +68,7 @@ syn keyword mesonBuiltin \ add_global_link_arguments \ add_languages \ add_project_arguments + \ add_project_dependencies \ add_project_link_arguments \ add_test_setup \ alias_target @@ -99,6 +100,7 @@ syn keyword mesonBuiltin \ install_headers \ install_man \ install_subdir + \ install_symlink \ install_emptydir \ is_disabler \ is_variable @@ -115,6 +117,7 @@ syn keyword mesonBuiltin \ shared_library \ shared_module \ static_library + \ structured_sources \ subdir \ subdir_done \ subproject @@ -125,6 +128,7 @@ syn keyword mesonBuiltin \ vcs_tag \ warning \ range + \ debug if exists("meson_space_error_highlight") " trailing whitespace @@ -146,7 +150,7 @@ hi def link mesonEscape Special hi def link mesonNumber Number hi def link mesonBuiltin Function hi def link mesonBoolean Boolean -if exists("meson_space_error_higlight") +if exists("meson_space_error_highlight") hi def link mesonSpaceError Error endif diff --git a/runtime/syntax/structurizr.vim b/runtime/syntax/structurizr.vim index ab9e4ee609..363ee70438 100644 --- a/runtime/syntax/structurizr.vim +++ b/runtime/syntax/structurizr.vim @@ -1,7 +1,7 @@ " Vim syntax file " Language: Structurizr DSL " Maintainer: Bastian Venthur <venthur@debian.org> -" Last Change: 2022-02-15 +" Last Change: 2022-05-22 " Remark: For a language reference, see " https://github.com/structurizr/dsl @@ -26,6 +26,7 @@ syn keyword skeyword configuration syn keyword skeyword container syn keyword skeyword containerinstance syn keyword skeyword custom +syn keyword skeyword default syn keyword skeyword deployment syn keyword skeyword deploymentenvironment syn keyword skeyword deploymentgroup @@ -40,6 +41,7 @@ syn keyword skeyword group syn keyword skeyword healthcheck syn keyword skeyword include syn keyword skeyword infrastructurenode +syn keyword skeyword instances syn keyword skeyword model syn keyword skeyword person syn keyword skeyword perspectives @@ -54,6 +56,7 @@ syn keyword skeyword tags syn keyword skeyword technology syn keyword skeyword terminology syn keyword skeyword theme +syn keyword skeyword themes syn keyword skeyword title syn keyword skeyword url syn keyword skeyword users diff --git a/runtime/syntax/swayconfig.vim b/runtime/syntax/swayconfig.vim index 996b8f596c..6b36210252 100644 --- a/runtime/syntax/swayconfig.vim +++ b/runtime/syntax/swayconfig.vim @@ -2,9 +2,9 @@ " Language: sway window manager config " Original Author: James Eapen <james.eapen@vai.org> " Maintainer: James Eapen <james.eapen@vai.org> -" Version: 0.1.6 -" Reference version (jamespeapen/swayconfig.vim): 0.11.6 -" Last Change: 2022 Aug 08 +" Version: 0.2.1 +" Reference version (jamespeapen/swayconfig.vim): 0.12.1 +" Last Change: 2023 Mar 20 " References: " http://i3wm.org/docs/userguide.html#configuring @@ -58,6 +58,10 @@ syn match swayConfigClientColor /^\s*client.\w\+\s\+.*$/ contains=i3ConfigClient syn keyword swayConfigInputKeyword input contained syn match swayConfigInput /^\s*input\s\+.*$/ contains=swayConfigInputKeyword +" Seat config +syn keyword swayConfigSeatKeyword seat contained +syn match swayConfigSeat /^\s*seat\s\+.*$/ contains=swayConfigSeatKeyword + " set display outputs syn match swayConfigOutput /^\s*output\s\+.*$/ contains=i3ConfigOutput @@ -66,6 +70,10 @@ syn keyword swayConfigFocusKeyword focus contained syn keyword swayConfigFocusType output contained syn match swayConfigFocus /^\s*focus\soutput\s.*$/ contains=swayConfigFocusKeyword,swayConfigFocusType +" mouse warping +syn keyword swayConfigMouseWarpingType container contained +syn match swayConfigMouseWarping /^\s*mouse_warping\s\+\(output\|container\|none\)\s\?$/ contains=i3ConfigMouseWarpingKeyword,i3ConfigMouseWarpingType,swayConfigMouseWarpingType + " focus follows mouse syn clear i3ConfigFocusFollowsMouseType syn clear i3ConfigFocusFollowsMouse @@ -80,7 +88,7 @@ syn match swayConfigXwaylandModifier /^\s*xwayland\s\+\(enable\|disable\|force\) " Group mode/bar syn clear i3ConfigBlock -syn region swayConfigBlock start=+.*s\?{$+ end=+^}$+ contains=i3ConfigBlockKeyword,i3ConfigString,i3ConfigBind,i3ConfigInitializeKeyword,i3ConfigComment,i3ConfigFont,i3ConfigFocusWrappingType,i3ConfigColor,i3ConfigVariable,swayConfigInputKeyword,i3ConfigOutput transparent keepend extend +syn region swayConfigBlock start=+.*s\?{$+ end=+^}$+ contains=i3ConfigBlockKeyword,i3ConfigString,i3ConfigBind,i3ConfigInitializeKeyword,i3ConfigComment,i3ConfigFont,i3ConfigFocusWrappingType,i3ConfigColor,i3ConfigVariable,swayConfigInputKeyword,swayConfigSeatKeyword,i3ConfigOutput transparent keepend extend "hi def link swayConfigError Error hi def link i3ConfigFloating Error @@ -89,6 +97,8 @@ hi def link swayConfigFloatingMouseAction Type hi def link swayConfigFocusKeyword Type hi def link swayConfigSmartBorderKeyword Type hi def link swayConfigInputKeyword Type +hi def link swayConfigSeatKeyword Type +hi def link swayConfigMouseWarpingType Type hi def link swayConfigFocusFollowsMouseType Type hi def link swayConfigBindGestureCommand Identifier hi def link swayConfigBindGestureDirection Constant diff --git a/runtime/syntax/urlshortcut.vim b/runtime/syntax/urlshortcut.vim new file mode 100644 index 0000000000..f6cc3835a2 --- /dev/null +++ b/runtime/syntax/urlshortcut.vim @@ -0,0 +1,14 @@ +" Vim syntax file +" Language: MS Windows URL shortcut file +" Maintainer: ObserverOfTime <chronobserver@disroot.org> +" LastChange: 2023-06-04 + +" Quit when a syntax file was already loaded. +if exists("b:current_syntax") + finish +endif + +" Just use the dosini syntax for now +runtime! syntax/dosini.vim + +let b:current_syntax = "urlshortcut" diff --git a/runtime/syntax/xpm.vim b/runtime/syntax/xpm.vim index 77d82403e9..b094092b73 100644 --- a/runtime/syntax/xpm.vim +++ b/runtime/syntax/xpm.vim @@ -1,7 +1,7 @@ " Vim syntax file " Language: X Pixmap " Maintainer: Ronald Schild <rs@scutum.de> -" Last Change: 2023 May 11 +" Last Change: 2023 May 24 " Version: 5.4n.2 " Jemma Nelson added termguicolors support " Dominique Pellé fixed spelling support @@ -42,7 +42,7 @@ function s:CreateSyntax() abort let values = split(s[1 : -2]) " Values string invalid, bail out - if len(values) != 4 + if len(values) != 4 && len(values) != 6 && len(values) != 7 return endif @@ -100,8 +100,8 @@ function s:CreateSyntax() abort endif " escape meta characters in patterns - let s = escape(s, '/\*^$.~[] ') - let chars = escape(chars, '/\*^$.~[] ') + let s = escape(s, '/\*^$.~[]') + let chars = escape(chars, '/\*^$.~[]') " now create syntax items " highlight the color string as normal string (no pixel string) diff --git a/scripts/gen_help_html.lua b/scripts/gen_help_html.lua index 7bc48a0662..7f84a4d54d 100644 --- a/scripts/gen_help_html.lua +++ b/scripts/gen_help_html.lua @@ -43,6 +43,7 @@ local M = {} -- All other files are "legacy" files which require fixed-width layout. local new_layout = { ['api.txt'] = true, + ['lsp.txt'] = true, ['channel.txt'] = true, ['deprecated.txt'] = true, ['develop.txt'] = true, @@ -62,7 +63,6 @@ local exclude_invalid = { ["'string'"] = "eval.txt", Query = 'treesitter.txt', ['eq?'] = 'treesitter.txt', - ['lsp-request'] = 'lsp.txt', matchit = 'vim_diff.txt', ['matchit.txt'] = 'help.txt', ["set!"] = "treesitter.txt", @@ -70,8 +70,6 @@ local exclude_invalid = { ['v:_null_dict'] = 'builtin.txt', ['v:_null_list'] = 'builtin.txt', ['v:_null_string'] = 'builtin.txt', - ['vim.lsp.buf_request()'] = 'lsp.txt', - ['vim.lsp.util.get_progress_messages()'] = 'lsp.txt', } -- False-positive "invalid URLs". @@ -90,6 +88,11 @@ local exclude_invalid_urls = { ["http://www.jclark.com/"] = "quickfix.txt", } +-- Deprecated, brain-damaged files that I don't care about. +local ignore_errors = { + ['pi_netrw.txt'] = true, +} + local function tofile(fname, text) local f = io.open(fname, 'w') if not f then @@ -145,11 +148,11 @@ local function trim(s, dir) return vim.fn.trim(s, '\r\t\n ', dir or 0) end --- Remove common punctuation from URLs. --- --- TODO: fix this in the parser instead... https://github.com/neovim/tree-sitter-vimdoc --- --- @returns (fixed_url, removed_chars) where `removed_chars` is in the order found in the input. +--- Removes common punctuation from URLs. +--- +--- TODO: fix this in the parser instead... https://github.com/neovim/tree-sitter-vimdoc +--- +--- @returns (fixed_url, removed_chars) where `removed_chars` is in the order found in the input. local function fix_url(url) local removed_chars = '' local fixed_url = url @@ -163,7 +166,7 @@ local function fix_url(url) return fixed_url, removed_chars end --- Checks if a given line is a "noise" line that doesn't look good in HTML form. +--- Checks if a given line is a "noise" line that doesn't look good in HTML form. local function is_noise(line, noise_lines) if ( -- First line is always noise. @@ -188,7 +191,7 @@ local function is_noise(line, noise_lines) return false end --- Creates a github issue URL at neovim/tree-sitter-vimdoc with prefilled content. +--- Creates a github issue URL at neovim/tree-sitter-vimdoc with prefilled content. local function get_bug_url_vimdoc(fname, to_fname, sample_text) local this_url = string.format('https://neovim.io/doc/user/%s', vim.fs.basename(to_fname)) local bug_url = ('https://github.com/neovim/tree-sitter-vimdoc/issues/new?labels=bug&title=parse+error%3A+' @@ -201,7 +204,7 @@ local function get_bug_url_vimdoc(fname, to_fname, sample_text) return bug_url end --- Creates a github issue URL at neovim/neovim with prefilled content. +--- Creates a github issue URL at neovim/neovim with prefilled content. local function get_bug_url_nvim(fname, to_fname, sample_text, token_name) local this_url = string.format('https://neovim.io/doc/user/%s', vim.fs.basename(to_fname)) local bug_url = ('https://github.com/neovim/neovim/issues/new?labels=bug&title=user+docs+HTML%3A+' @@ -216,7 +219,7 @@ local function get_bug_url_nvim(fname, to_fname, sample_text, token_name) return bug_url end --- Gets a "foo.html" name from a "foo.txt" helpfile name. +--- Gets a "foo.html" name from a "foo.txt" helpfile name. local function get_helppage(f) if not f then return nil @@ -231,9 +234,9 @@ local function get_helppage(f) return (f:gsub('%.txt$', '.html')) end --- Counts leading spaces (tab=8) to decide the indent size of multiline text. --- --- Blank lines (empty or whitespace-only) are ignored. +--- Counts leading spaces (tab=8) to decide the indent size of multiline text. +--- +--- Blank lines (empty or whitespace-only) are ignored. local function get_indent(s) local min_indent = nil for line in vim.gsplit(s, '\n') do @@ -245,7 +248,7 @@ local function get_indent(s) return min_indent or 0 end --- Removes the common indent level, after expanding tabs to 8 spaces. +--- Removes the common indent level, after expanding tabs to 8 spaces. local function trim_indent(s) local indent_size = get_indent(s) local trimmed = '' @@ -256,7 +259,7 @@ local function trim_indent(s) return trimmed:sub(1, -2) end --- Gets raw buffer text in the node's range (+/- an offset), as a newline-delimited string. +--- Gets raw buffer text in the node's range (+/- an offset), as a newline-delimited string. local function getbuflinestr(node, bufnr, offset) local line1, _, line2, _ = node:range() line1 = line1 - offset @@ -265,8 +268,8 @@ local function getbuflinestr(node, bufnr, offset) return table.concat(lines, '\n') end --- Gets the whitespace just before `node` from the raw buffer text. --- Needed for preformatted `old` lines. +--- Gets the whitespace just before `node` from the raw buffer text. +--- Needed for preformatted `old` lines. local function getws(node, bufnr) local line1, c1, line2, _ = node:range() local raw = vim.fn.getbufline(bufnr, line1 + 1, line2 + 1)[1] @@ -283,18 +286,21 @@ local function get_tagname(node, bufnr) return helppage, tag end --- Returns true if the given invalid tagname is a false positive. +--- Returns true if the given invalid tagname is a false positive. local function ignore_invalid(s) return not not ( exclude_invalid[s] -- Strings like |~/====| appear in various places and the parser thinks they are links, but they -- are just table borders. or s:find('===') - or s:find('---') + or s:find('%-%-%-') ) end -local function ignore_parse_error(s) +local function ignore_parse_error(fname, s) + if ignore_errors[vim.fs.basename(fname)] then + return true + end return ( -- Ignore parse errors for unclosed tag. -- This is common in vimdocs and is treated as plaintext by :help. @@ -315,7 +321,7 @@ local function has_ancestor(node, ancestor_name) return false end --- Gets the first matching child node matching `name`. +--- Gets the first matching child node matching `name`. local function first(node, name) for c, _ in node:iter_children() do if c:named() and c:type() == name then @@ -337,10 +343,10 @@ local function validate_link(node, bufnr, fname) return helppage, tagname, ignored end --- TODO: port the logic from scripts/check_urls.vim +--- TODO: port the logic from scripts/check_urls.vim local function validate_url(text, fname) local ignored = false - if vim.fs.basename(fname) == 'pi_netrw.txt' then + if ignore_errors[vim.fs.basename(fname)] then ignored = true elseif text:find('http%:') and not exclude_invalid_urls[text] then invalid_urls[text] = vim.fs.basename(fname) @@ -348,10 +354,12 @@ local function validate_url(text, fname) return ignored end --- Traverses the tree at `root` and checks that |tag| links point to valid helptags. +--- Traverses the tree at `root` and checks that |tag| links point to valid helptags. local function visit_validate(root, level, lang_tree, opt, stats) level = level or 0 local node_name = (root.named and root:named()) and root:type() or nil + -- Parent kind (string). + local parent = root:parent() and root:parent():type() or nil local toplevel = level < 1 local function node_text(node) return vim.treesitter.get_node_text(node or root, opt.buf) @@ -367,19 +375,21 @@ local function visit_validate(root, level, lang_tree, opt, stats) end if node_name == 'ERROR' then - if ignore_parse_error(text) then + if ignore_parse_error(opt.fname, text) then return end -- Store the raw text to give context to the error report. - local sample_text = not toplevel and getbuflinestr(root, opt.buf, 3) or '[top level!]' + local sample_text = not toplevel and getbuflinestr(root, opt.buf, 0) or '[top level!]' + -- Flatten the sample text to a single, truncated line. + sample_text = vim.trim(sample_text):gsub('[\t\n]', ' '):sub(1, 80) table.insert(stats.parse_errors, sample_text) - elseif node_name == 'word' or node_name == 'uppercase_name' then - if spell_dict[text] then - if not invalid_spelling[text] then - invalid_spelling[text] = { vim.fs.basename(opt.fname) } - else - table.insert(invalid_spelling[text], vim.fs.basename(opt.fname)) - end + elseif (node_name == 'word' or node_name == 'uppercase_name') + and (not vim.tbl_contains({'codespan', 'taglink', 'tag'}, parent)) + then + local text_nopunct = vim.fn.trim(text, '.,', 0) -- Ignore some punctuation. + if spell_dict[text_nopunct] then + invalid_spelling[text_nopunct] = invalid_spelling[text_nopunct] or {} + invalid_spelling[text_nopunct][vim.fs.basename(opt.fname)] = node_text(root:parent()) end elseif node_name == 'url' then local fixed_url, _ = fix_url(trim(text)) @@ -537,7 +547,7 @@ local function visit_node(root, level, lang_tree, headings, opt, stats) elseif node_name == 'language' then language = node_text(root) return '' - elseif node_name == 'code' then + elseif node_name == 'code' then -- Highlighted codeblock (child). if is_blank(text) then return '' end @@ -580,7 +590,7 @@ local function visit_node(root, level, lang_tree, headings, opt, stats) end return s elseif node_name == 'ERROR' then - if ignore_parse_error(trimmed) then + if ignore_parse_error(opt.fname, trimmed) then return text end @@ -610,7 +620,7 @@ local function get_helpfiles(include) return rv end --- Populates the helptags map. +--- Populates the helptags map. local function get_helptags(help_dir) local m = {} -- Load a random help file to convince taglist() to do its job. @@ -625,17 +635,19 @@ local function get_helptags(help_dir) return m end --- Use the vimdoc parser defined in the build, not whatever happens to be installed on the system. +--- Use the vimdoc parser defined in the build, not whatever happens to be installed on the system. local function ensure_runtimepath() if not vim.o.runtimepath:find('build/lib/nvim/') then vim.cmd[[set runtimepath^=./build/lib/nvim/]] end end --- Opens `fname` in a buffer and gets a treesitter parser for the buffer contents. --- --- @returns lang_tree, bufnr -local function parse_buf(fname) +--- Opens `fname` in a buffer and gets a treesitter parser for the buffer contents. +--- +--- @param fname string help file to parse +--- @param parser_path string? path to non-default vimdoc.so +--- @returns lang_tree, bufnr +local function parse_buf(fname, parser_path) local buf if type(fname) == 'string' then vim.cmd('split '..vim.fn.fnameescape(fname)) -- Filename. @@ -644,21 +656,25 @@ local function parse_buf(fname) buf = fname vim.cmd('sbuffer '..tostring(fname)) -- Buffer number. end - -- vim.treesitter.language.add('vimdoc', { path = vim.fn.expand('~/Library/Caches/tree-sitter/lib/vimdoc.so') }) + if parser_path then + vim.treesitter.language.add('vimdoc', { path = parser_path }) + end local lang_tree = vim.treesitter.get_parser(buf) return lang_tree, buf end --- Validates one :help file `fname`: --- - checks that |tag| links point to valid helptags. --- - recursively counts parse errors ("ERROR" nodes) --- --- @returns { invalid_links: number, parse_errors: number } -local function validate_one(fname) +--- Validates one :help file `fname`: +--- - checks that |tag| links point to valid helptags. +--- - recursively counts parse errors ("ERROR" nodes) +--- +--- @param fname string help file to validate +--- @param parser_path string? path to non-default vimdoc.so +--- @returns { invalid_links: number, parse_errors: string[] } +local function validate_one(fname, parser_path) local stats = { parse_errors = {}, } - local lang_tree, buf = parse_buf(fname) + local lang_tree, buf = parse_buf(fname, parser_path) for _, tree in ipairs(lang_tree:trees()) do visit_validate(tree:root(), 0, tree, { buf = buf, fname = fname, }, stats) end @@ -667,20 +683,21 @@ local function validate_one(fname) return stats end --- Generates HTML from one :help file `fname` and writes the result to `to_fname`. --- --- @param fname Source :help file --- @param to_fname Destination .html file --- @param old boolean Preformat paragraphs (for old :help files which are full of arbitrary whitespace) --- --- @returns html, stats -local function gen_one(fname, to_fname, old, commit) +--- Generates HTML from one :help file `fname` and writes the result to `to_fname`. +--- +--- @param fname string Source :help file +--- @param to_fname string Destination .html file +--- @param old boolean Preformat paragraphs (for old :help files which are full of arbitrary whitespace) +--- @param parser_path string? path to non-default vimdoc.so +--- +--- @returns html, stats +local function gen_one(fname, to_fname, old, commit, parser_path) local stats = { noise_lines = {}, parse_errors = {}, first_tags = {}, -- Track the first few tags in doc. } - local lang_tree, buf = parse_buf(fname) + local lang_tree, buf = parse_buf(fname, parser_path) local headings = {} -- Headings (for ToC). 2-dimensional: h1 contains h2/h3. local title = to_titlecase(basename_noext(fname)) @@ -927,6 +944,7 @@ local function gen_css(fname) padding-top: 10px; padding-bottom: 10px; } + .old-help-para { padding-top: 10px; padding-bottom: 10px; @@ -936,6 +954,12 @@ local function gen_css(fname) font-size: 16px; font-family: ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas,Liberation Mono,monospace; } + .old-help-para pre { + /* All text in .old-help-para is formatted as "white-space:pre" so text following <pre> is + already visually separated by the linebreak. */ + margin-bottom: 0; + } + a.help-tag, a.help-tag:focus, a.help-tag:hover { color: inherit; text-decoration: none; @@ -989,6 +1013,9 @@ local function gen_css(fname) font-size: 16px; margin-top: 10px; } + pre:last-child { + margin-bottom: 0; + } pre:hover, .help-heading:hover { overflow: visible; @@ -1060,18 +1087,20 @@ end --- @param include table|nil Process only these filenames. Example: {'api.txt', 'autocmd.txt', 'channel.txt'} --- --- @returns info dict -function M.gen(help_dir, to_dir, include, commit) +function M.gen(help_dir, to_dir, include, commit, parser_path) vim.validate{ help_dir={help_dir, function(d) return vim.fn.isdirectory(d) == 1 end, 'valid directory'}, to_dir={to_dir, 's'}, include={include, 't', true}, commit={commit, 's', true}, + parser_path={parser_path, function(f) return f == nil or vim.fn.filereadable(vim.fn.expand(f)) == 1 end, 'valid vimdoc.{so,dll} filepath'}, } local err_count = 0 ensure_runtimepath() tagmap = get_helptags(help_dir) helpfiles = get_helpfiles(include) + parser_path = parser_path and vim.fn.expand(parser_path) or nil print(('output dir: %s'):format(to_dir)) vim.fn.mkdir(to_dir, 'p') @@ -1080,7 +1109,7 @@ function M.gen(help_dir, to_dir, include, commit) for _, f in ipairs(helpfiles) do local helpfile = vim.fs.basename(f) local to_fname = ('%s/%s'):format(to_dir, get_helppage(helpfile)) - local html, stats = gen_one(f, to_fname, not new_layout[helpfile], commit or '?') + local html, stats = gen_one(f, to_fname, not new_layout[helpfile], commit or '?', parser_path) tofile(to_fname, html) print(('generated (%-4s errors): %-15s => %s'):format(#stats.parse_errors, helpfile, vim.fs.basename(to_fname))) err_count = err_count + #stats.parse_errors @@ -1103,20 +1132,27 @@ end -- This is 10x faster than gen(), for use in CI. -- -- @returns results dict -function M.validate(help_dir, include) +function M.validate(help_dir, include, parser_path) vim.validate{ help_dir={help_dir, function(d) return vim.fn.isdirectory(d) == 1 end, 'valid directory'}, include={include, 't', true}, + parser_path={parser_path, function(f) return f == nil or vim.fn.filereadable(vim.fn.expand(f)) == 1 end, 'valid vimdoc.{so,dll} filepath'}, } local err_count = 0 + local files_to_errors = {} ensure_runtimepath() tagmap = get_helptags(help_dir) helpfiles = get_helpfiles(include) + parser_path = parser_path and vim.fn.expand(parser_path) or nil for _, f in ipairs(helpfiles) do local helpfile = vim.fs.basename(f) - local rv = validate_one(f) + local rv = validate_one(f, parser_path) print(('validated (%-4s errors): %s'):format(#rv.parse_errors, helpfile)) + if #rv.parse_errors > 0 then + files_to_errors[helpfile] = rv.parse_errors + vim.print(('%s'):format(vim.iter(rv.parse_errors):fold('', function(s, v) return s..'\n '..v end))) + end err_count = err_count + #rv.parse_errors end @@ -1126,6 +1162,7 @@ function M.validate(help_dir, include) invalid_links = invalid_links, invalid_urls = invalid_urls, invalid_spelling = invalid_spelling, + parse_errors = files_to_errors, } end diff --git a/scripts/gen_vimdoc.py b/scripts/gen_vimdoc.py index e9209e219e..96db0d1aad 100755 --- a/scripts/gen_vimdoc.py +++ b/scripts/gen_vimdoc.py @@ -2,7 +2,7 @@ """Generates Nvim :help docs from C/Lua docstrings, using Doxygen. Also generates *.mpack files. To inspect the *.mpack structure: - :new | put=v:lua.vim.inspect(v:lua.vim.mpack.unpack(readfile('runtime/doc/api.mpack','B'))) + :new | put=v:lua.vim.inspect(v:lua.vim.mpack.decode(readfile('runtime/doc/api.mpack','B'))) Flow: main @@ -275,7 +275,7 @@ CONFIG = { 'query.lua', 'highlighter.lua', 'languagetree.lua', - 'playground.lua', + 'dev.lua', ], 'files': [ 'runtime/lua/vim/treesitter.lua', diff --git a/scripts/lsp_types.lua b/scripts/lsp_types.lua new file mode 100644 index 0000000000..8f3ed811d6 --- /dev/null +++ b/scripts/lsp_types.lua @@ -0,0 +1,207 @@ +--[[ +Generates lua-ls annotations for lsp +USAGE: +nvim -l scripts/lsp_types.lua gen --runtime/lua/vim/lsp/types/protocol.lua +--]] + +local M = {} + +local function tofile(fname, text) + local f = io.open(fname, 'w') + if not f then + error(('failed to write: %s'):format(f)) + else + f:write(text) + f:close() + end +end + +function M.gen(opt) + if vim.loop.fs_stat('./lsp.json') then + vim.fn.delete('./lsp.json') + end + vim.fn.system({ + 'curl', + 'https://raw.githubusercontent.com/microsoft/lsprotocol/main/generator/lsp.json', + '-o', + './lsp.json', + }) + local protocol = vim.fn.json_decode(vim.fn.readfile('./lsp.json')) + vim.fn.delete('./lsp.json') + local output_file = opt[1] + protocol = protocol or {} + local output = { + '--[[', + 'This file is autogenerated from scripts/lsp_types.lua', + 'Regenerate:', + [=[nvim -l scripts/lsp_types.lua gen --runtime/lua/vim/lsp/types/protocol.lua]=], + '--]]', + '', + '---@alias lsp.null nil', + '---@alias uinteger integer', + '---@alias lsp.decimal number', + '---@alias lsp.DocumentUri string', + '---@alias lsp.URI string', + '---@alias lsp.LSPObject table<string, lsp.LSPAny>', + '---@alias lsp.LSPArray lsp.LSPAny[]', + '---@alias lsp.LSPAny lsp.LSPObject|lsp.LSPArray|string|number|boolean|nil', + '', + } + + local anonymous_num = 0 + + local anonym_classes = {} + + local simple_types = { + 'string', + 'boolean', + 'integer', + 'uinteger', + 'decimal', + } + + local function parse_type(type) + if type.kind == 'reference' or type.kind == 'base' then + if vim.tbl_contains(simple_types, type.name) then + return type.name + end + return 'lsp.' .. type.name + elseif type.kind == 'array' then + return parse_type(type.element) .. '[]' + elseif type.kind == 'or' then + local val = '' + for _, item in ipairs(type.items) do + val = val .. parse_type(item) .. '|' + end + val = val:sub(0, -2) + return val + elseif type.kind == 'stringLiteral' then + return '"' .. type.value .. '"' + elseif type.kind == 'map' then + return 'table<' .. parse_type(type.key) .. ', ' .. parse_type(type.value) .. '>' + elseif type.kind == 'literal' then + -- can I use ---@param disabled? {reason: string} + -- use | to continue the inline class to be able to add docs + -- https://github.com/LuaLS/lua-language-server/issues/2128 + anonymous_num = anonymous_num + 1 + local anonym = { '---@class anonym' .. anonymous_num } + for _, field in ipairs(type.value.properties) do + if field.documentation then + field.documentation = field.documentation:gsub('\n', '\n---') + anonym[#anonym + 1] = '---' .. field.documentation + end + anonym[#anonym + 1] = '---@field ' + .. field.name + .. (field.optional and '?' or '') + .. ' ' + .. parse_type(field.type) + end + anonym[#anonym + 1] = '' + for _, line in ipairs(anonym) do + anonym_classes[#anonym_classes + 1] = line + end + return 'anonym' .. anonymous_num + elseif type.kind == 'tuple' then + local tuple = '{ ' + for i, value in ipairs(type.items) do + tuple = tuple .. '[' .. i .. ']: ' .. parse_type(value) .. ', ' + end + -- remove , at the end + tuple = tuple:sub(0, -3) + return tuple .. ' }' + end + vim.print(type) + return '' + end + + for _, structure in ipairs(protocol.structures) do + if structure.documentation then + structure.documentation = structure.documentation:gsub('\n', '\n---') + output[#output + 1] = '---' .. structure.documentation + end + if structure.extends then + local class_string = '---@class lsp.' + .. structure.name + .. ': ' + .. parse_type(structure.extends[1]) + for _, mixin in ipairs(structure.mixins or {}) do + class_string = class_string .. ', ' .. parse_type(mixin) + end + output[#output + 1] = class_string + else + output[#output + 1] = '---@class lsp.' .. structure.name + end + for _, field in ipairs(structure.properties or {}) do + if field.documentation then + field.documentation = field.documentation:gsub('\n', '\n---') + output[#output + 1] = '---' .. field.documentation + end + output[#output + 1] = '---@field ' + .. field.name + .. (field.optional and '?' or '') + .. ' ' + .. parse_type(field.type) + end + output[#output + 1] = '' + end + + for _, enum in ipairs(protocol.enumerations) do + if enum.documentation then + enum.documentation = enum.documentation:gsub('\n', '\n---') + output[#output + 1] = '---' .. enum.documentation + end + local enum_type = '---@alias lsp.' .. enum.name + for _, value in ipairs(enum.values) do + enum_type = enum_type + .. '\n---| ' + .. (type(value.value) == 'string' and '"' .. value.value .. '"' or value.value) + .. ' # ' + .. value.name + end + output[#output + 1] = enum_type + output[#output + 1] = '' + end + + for _, alias in ipairs(protocol.typeAliases) do + if alias.documentation then + alias.documentation = alias.documentation:gsub('\n', '\n---') + output[#output + 1] = '---' .. alias.documentation + end + if alias.type.kind == 'or' then + local alias_type = '---@alias lsp.' .. alias.name .. ' ' + for _, item in ipairs(alias.type.items) do + alias_type = alias_type .. parse_type(item) .. '|' + end + alias_type = alias_type:sub(0, -2) + output[#output + 1] = alias_type + else + output[#output + 1] = '---@alias lsp.' .. alias.name .. ' ' .. parse_type(alias.type) + end + output[#output + 1] = '' + end + + for _, line in ipairs(anonym_classes) do + output[#output + 1] = line + end + + tofile(output_file, table.concat(output, '\n')) +end + +local opt = {} + +local index = 1 +for _, a in ipairs(arg) do + if vim.startswith(a, '--') then + local name = a:sub(3) + opt[index] = name + index = index + 1 + end +end + +for _, a in ipairs(arg) do + if M[a] then + M[a](opt) + end +end + +return M diff --git a/scripts/lua2dox.lua b/scripts/lua2dox.lua index 014934aebe..bb5214f091 100644 --- a/scripts/lua2dox.lua +++ b/scripts/lua2dox.lua @@ -340,6 +340,7 @@ function TLua2DoX_filter.filter(this, AppStamp, Filename) if vim.startswith(line, '---@cast') or vim.startswith(line, '---@diagnostic') + or vim.startswith(line, '---@overload') or vim.startswith(line, '---@type') then -- Ignore LSP directives outStream:writeln('// gg:"' .. line .. '"') diff --git a/src/cjson/lua_cjson.c b/src/cjson/lua_cjson.c index c243f93c05..6af43d8eb7 100644 --- a/src/cjson/lua_cjson.c +++ b/src/cjson/lua_cjson.c @@ -1,3 +1,5 @@ +// Upstream: https://github.com/openresty/lua-cjson/blob/master/lua_cjson.c + /* Lua CJSON - JSON support for Lua * * Copyright (c) 2010-2012 Mark Pulford <mark@kyne.com.au> @@ -252,6 +254,7 @@ static json_config_t *json_fetch_config(lua_State *l) /* Ensure the correct number of arguments have been provided. * Pad with nil to allow other functions to simply check arg[i] * to find whether an argument was provided */ +/* static json_config_t *json_arg_init(lua_State *l, int args) { luaL_argcheck(l, lua_gettop(l) <= args, args + 1, @@ -262,8 +265,10 @@ static json_config_t *json_arg_init(lua_State *l, int args) return json_fetch_config(l); } +*/ /* Process integer options for configuration functions */ +/* static int json_integer_option(lua_State *l, int optindex, int *setting, int min, int max) { @@ -281,8 +286,10 @@ static int json_integer_option(lua_State *l, int optindex, int *setting, return 1; } +*/ /* Process enumerated arguments for a configuration function */ +/* static int json_enum_option(lua_State *l, int optindex, int *setting, const char **options, int bool_true) { @@ -307,11 +314,13 @@ static int json_enum_option(lua_State *l, int optindex, int *setting, return 1; } +*/ /* Configures handling of extremely sparse arrays: * convert: Convert extremely sparse arrays into objects? Otherwise error. * ratio: 0: always allow sparse; 1: never allow sparse; >1: use ratio * safe: Always use an array when the max index <= safe */ +/* static int json_cfg_encode_sparse_array(lua_State *l) { json_config_t *cfg = json_arg_init(l, 3); @@ -322,42 +331,52 @@ static int json_cfg_encode_sparse_array(lua_State *l) return 3; } +*/ /* Configures the maximum number of nested arrays/objects allowed when * encoding */ +/* static int json_cfg_encode_max_depth(lua_State *l) { json_config_t *cfg = json_arg_init(l, 1); return json_integer_option(l, 1, &cfg->encode_max_depth, 1, INT_MAX); } +*/ /* Configures the maximum number of nested arrays/objects allowed when * encoding */ +/* static int json_cfg_decode_max_depth(lua_State *l) { json_config_t *cfg = json_arg_init(l, 1); return json_integer_option(l, 1, &cfg->decode_max_depth, 1, INT_MAX); } +*/ /* Configures number precision when converting doubles to text */ +/* static int json_cfg_encode_number_precision(lua_State *l) { json_config_t *cfg = json_arg_init(l, 1); return json_integer_option(l, 1, &cfg->encode_number_precision, 1, 16); } +*/ /* Configures how to treat empty table when encode lua table */ +/* static int json_cfg_encode_empty_table_as_object(lua_State *l) { json_config_t *cfg = json_arg_init(l, 1); return json_enum_option(l, 1, &cfg->encode_empty_table_as_object, NULL, 1); } +*/ /* Configures how to decode arrays */ +/* static int json_cfg_decode_array_with_array_mt(lua_State *l) { json_config_t *cfg = json_arg_init(l, 1); @@ -366,8 +385,10 @@ static int json_cfg_decode_array_with_array_mt(lua_State *l) return 1; } +*/ /* Configures JSON encoding buffer persistence */ +/* static int json_cfg_encode_keep_buffer(lua_State *l) { json_config_t *cfg = json_arg_init(l, 1); @@ -377,7 +398,7 @@ static int json_cfg_encode_keep_buffer(lua_State *l) json_enum_option(l, 1, &cfg->encode_keep_buffer, NULL, 1); - /* Init / free the buffer if the setting has changed */ + // Init / free the buffer if the setting has changed if (old_value ^ cfg->encode_keep_buffer) { if (cfg->encode_keep_buffer) strbuf_init(&cfg->encode_buf, 0); @@ -387,6 +408,7 @@ static int json_cfg_encode_keep_buffer(lua_State *l) return 1; } +*/ #if defined(DISABLE_INVALID_NUMBERS) && !defined(USE_INTERNAL_FPCONV) void json_verify_invalid_number_setting(lua_State *l, int *setting) @@ -400,6 +422,7 @@ void json_verify_invalid_number_setting(lua_State *l, int *setting) #define json_verify_invalid_number_setting(l, s) do { } while(0) #endif +/* static int json_cfg_encode_invalid_numbers(lua_State *l) { static const char *options[] = { "off", "on", "null", NULL }; @@ -411,7 +434,9 @@ static int json_cfg_encode_invalid_numbers(lua_State *l) return 1; } +*/ +/* static int json_cfg_decode_invalid_numbers(lua_State *l) { json_config_t *cfg = json_arg_init(l, 1); @@ -422,7 +447,9 @@ static int json_cfg_decode_invalid_numbers(lua_State *l) return 1; } +*/ +/* static int json_cfg_encode_escape_forward_slash(lua_State *l) { int ret; @@ -436,6 +463,7 @@ static int json_cfg_encode_escape_forward_slash(lua_State *l) } return ret; } +*/ static int json_destroy_config(lua_State *l) { @@ -1417,9 +1445,9 @@ static int json_decode(lua_State *l) lua_getfield(l, 2, "luanil"); /* We only handle the luanil option for now */ - if (lua_isnil(l, -1)) { - lua_pop(l, 1); - break; + if (lua_isnil(l, -1)) { + lua_pop(l, 1); + break; } luaL_checktype(l, -1, LUA_TTABLE); @@ -1534,6 +1562,8 @@ int lua_cjson_new(lua_State *l) luaL_Reg reg[] = { { "encode", json_encode }, { "decode", json_decode }, + // Nvim: don't expose options which cause global side-effects. + /* { "encode_empty_table_as_object", json_cfg_encode_empty_table_as_object }, { "decode_array_with_array_mt", json_cfg_decode_array_with_array_mt }, { "encode_sparse_array", json_cfg_encode_sparse_array }, @@ -1544,6 +1574,7 @@ int lua_cjson_new(lua_State *l) { "encode_invalid_numbers", json_cfg_encode_invalid_numbers }, { "decode_invalid_numbers", json_cfg_decode_invalid_numbers }, { "encode_escape_forward_slash", json_cfg_encode_escape_forward_slash }, + */ { "new", lua_cjson_new }, { NULL, NULL } }; @@ -1589,23 +1620,31 @@ int lua_cjson_new(lua_State *l) json_create_config(l); compat_luaL_setfuncs(l, reg, 1); - /* Set cjson.null */ + // Nvim: don't expose "null", it is identical to vim.NIL. + /* nlua_pushref(l, nlua_get_nil_ref(l)); lua_setfield(l, -2, "null"); + */ - /* Set cjson.empty_array_mt */ + // Nvim: don't expose empty_array_mt. + /* lua_pushlightuserdata(l, json_lightudata_mask(&json_empty_array)); lua_rawget(l, LUA_REGISTRYINDEX); lua_setfield(l, -2, "empty_array_mt"); + */ - /* Set cjson.array_mt */ + // Nvim: don't expose array_mt. + /* lua_pushlightuserdata(l, json_lightudata_mask(&json_array)); lua_rawget(l, LUA_REGISTRYINDEX); lua_setfield(l, -2, "array_mt"); + */ - /* Set cjson.empty_array */ + // Nvim: don't expose empty_array. + /* lua_pushlightuserdata(l, json_lightudata_mask(&json_array)); lua_setfield(l, -2, "empty_array"); + */ /* Set module name / version fields */ lua_pushliteral(l, CJSON_MODNAME); diff --git a/src/man/nvim.1 b/src/man/nvim.1 index c32bdeadc6..b846ed4ba6 100644 --- a/src/man/nvim.1 +++ b/src/man/nvim.1 @@ -193,7 +193,7 @@ is do not read or write a ShaDa file. .Ic ":help shada" .It Fl -noplugin -Skip loading plugins. +Skip loading plugins (by setting the 'noloadplugins' option). Implied by .Cm -u NONE . .It Fl -clean diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 222b283a5d..0d3c05ef43 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -703,59 +703,6 @@ if(WIN32) -P ${PROJECT_SOURCE_DIR}/cmake/WindowsDllCopy.cmake) add_dependencies(nvim_runtime_deps nvim_dll_deps) - if(USE_BUNDLED_NVIMQT) - set(NVIMQT_DEPS - # Dependencies for neovim-qt - bearer/qgenericbearer.dll - iconengines/qsvgicon.dll - imageformats/qgif.dll - imageformats/qicns.dll - imageformats/qico.dll - imageformats/qjpeg.dll - imageformats/qsvg.dll - imageformats/qtga.dll - imageformats/qtiff.dll - imageformats/qwbmp.dll - imageformats/qwebp.dll - platforms/qwindows.dll - styles/qwindowsvistastyle.dll - translations/qt_ar.qm - translations/qt_bg.qm - translations/qt_ca.qm - translations/qt_cs.qm - translations/qt_da.qm - translations/qt_de.qm - translations/qt_en.qm - translations/qt_es.qm - translations/qt_fi.qm - translations/qt_fr.qm - translations/qt_gd.qm - translations/qt_he.qm - translations/qt_hu.qm - translations/qt_it.qm - translations/qt_ja.qm - translations/qt_ko.qm - translations/qt_lv.qm - translations/qt_pl.qm - translations/qt_ru.qm - translations/qt_sk.qm - translations/qt_uk.qm - D3Dcompiler_47.dll - libEGL.dll - libgcc_s_seh-1.dll - libGLESv2.dll - libstdc++-6.dll - libwinpthread-1.dll - nvim-qt.exe - opengl32sw.dll - Qt5Core.dll - Qt5Gui.dll - Qt5Network.dll - Qt5Svg.dll - Qt5Widgets.dll - ) - endif() - # A CMake script is used for copying the files to avoid the # "command line is too long" error that occurs when Ninja tries running # a command that exceeds the length limit (8191 characters) on Windows. @@ -766,9 +713,7 @@ if(WIN32) cat.exe tee.exe win32yank.exe - xxd.exe - - ${NVIMQT_DEPS}) + xxd.exe) get_filename_component(DEP_FILE_DIR ${DEP_FILE} DIRECTORY) set(EXTERNAL_BLOBS_SCRIPT "${EXTERNAL_BLOBS_SCRIPT}\n" "file(COPY \"${DEPS_PREFIX}/bin/${DEP_FILE}\" diff --git a/src/nvim/README.md b/src/nvim/README.md index 75155fb9c6..0de0fb9a3f 100644 --- a/src/nvim/README.md +++ b/src/nvim/README.md @@ -208,15 +208,11 @@ To debug the main process, you can debug the nvim binary with the `--headless` flag which does not launch the TUI and will allow you to set breakpoints in code not related to TUI rendering like so: -``` -lldb -- ./build/bin/nvim --headless --listen ~/.cache/nvim/debug-server.pipe -``` + lldb -- ./build/bin/nvim --headless --listen ~/.cache/nvim/debug-server.pipe You can then attach to the headless process to interact with the editor like so: -``` -./build/bin/nvim --remote-ui --server ~/.cache/nvim/debug-server.pipe -``` + ./build/bin/nvim --remote-ui --server ~/.cache/nvim/debug-server.pipe Conversely for debugging TUI rendering, you can start a headless process and debug the remote-ui process multiple times without losing editor state. diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index 82a62c3192..5bf7295bb3 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -48,7 +48,7 @@ /// /// \brief For more information on buffers, see |buffers| /// -/// Unloaded Buffers:~ +/// Unloaded Buffers: ~ /// /// Buffers may be unloaded by the |:bunload| command or the buffer's /// |'bufhidden'| option. When a buffer is unloaded its file contents are freed @@ -84,7 +84,7 @@ Integer nvim_buf_line_count(Buffer buffer, Error *err) /// Activates buffer-update events on a channel, or as Lua callbacks. /// /// Example (Lua): capture buffer updates in a global `events` variable -/// (use "print(vim.inspect(events))" to see its contents): +/// (use "vim.print(events)" to see its contents): /// <pre>lua /// events = {} /// vim.api.nvim_buf_attach(0, false, { @@ -112,7 +112,7 @@ Integer nvim_buf_line_count(Buffer buffer, Error *err) /// - byte count of previous contents /// - deleted_codepoints (if `utf_sizes` is true) /// - deleted_codeunits (if `utf_sizes` is true) -/// - on_bytes: lua callback invoked on change. +/// - on_bytes: Lua callback invoked on change. /// This callback receives more granular information about the /// change compared to on_lines. /// Return `true` to detach. @@ -1250,11 +1250,11 @@ ArrayOf(Integer, 2) nvim_buf_get_mark(Buffer buffer, String name, Error *err) /// buffer/window currently, like |termopen()|. /// /// @param buffer Buffer handle, or 0 for current buffer -/// @param fun Function to call inside the buffer (currently lua callable +/// @param fun Function to call inside the buffer (currently Lua callable /// only) /// @param[out] err Error details, if any -/// @return Return value of function. NB: will deepcopy lua values -/// currently, use upvalues to send lua references in and out. +/// @return Return value of function. NB: will deepcopy Lua values +/// currently, use upvalues to send Lua references in and out. Object nvim_buf_call(Buffer buffer, LuaRef fun, Error *err) FUNC_API_SINCE(7) FUNC_API_LUA_ONLY diff --git a/src/nvim/api/command.c b/src/nvim/api/command.c index 6d715bcf46..b254e326f9 100644 --- a/src/nvim/api/command.c +++ b/src/nvim/api/command.c @@ -109,7 +109,7 @@ Dictionary nvim_parse_cmd(String str, Dictionary opts, Error *err) exarg_T ea; CmdParseInfo cmdinfo; char *cmdline = string_to_cstr(str); - char *errormsg = NULL; + const char *errormsg = NULL; if (!parse_cmdline(cmdline, &ea, &cmdinfo, &errormsg)) { if (errormsg != NULL) { @@ -306,7 +306,7 @@ end: /// make their usage simpler with |vim.cmd()|. For example, instead of /// `vim.cmd.bdelete{ count = 2 }`, you may do `vim.cmd.bdelete(2)`. /// -/// On execution error: fails with VimL error, updates v:errmsg. +/// On execution error: fails with Vimscript error, updates v:errmsg. /// /// @see |nvim_exec2()| /// @see |nvim_command()| @@ -393,6 +393,12 @@ String nvim_cmd(uint64_t channel_id, Dict(cmd) *cmd, Dict(cmd_opts) *opts, Error VALIDATE(!is_cmd_ni(ea.cmdidx), "Command not implemented: %s", cmdname, { goto end; }); + const char *fullname = IS_USER_CMDIDX(ea.cmdidx) + ? get_user_command_name(ea.useridx, ea.cmdidx) + : get_command_name(NULL, ea.cmdidx); + VALIDATE(strncmp(fullname, cmdname, strlen(cmdname)) == 0, "Invalid command: \"%s\"", cmdname, { + goto end; + }); // Get the command flags so that we can know what type of arguments the command uses. // Not required for a user command since `find_ex_command` already deals with it in that case. diff --git a/src/nvim/api/deprecated.c b/src/nvim/api/deprecated.c index a7e5b32d49..2e3ebd7c39 100644 --- a/src/nvim/api/deprecated.c +++ b/src/nvim/api/deprecated.c @@ -709,14 +709,13 @@ static void set_option_to(uint64_t channel_id, void *to, int type, String name, } } - long numval = 0; - char *stringval = NULL; + OptVal optval; if (flags & SOPT_BOOL) { VALIDATE(value.type == kObjectTypeBoolean, "Option '%s' value must be Boolean", name.data, { return; }); - numval = value.data.boolean; + optval = BOOLEAN_OPTVAL(value.data.boolean); } else if (flags & SOPT_NUM) { VALIDATE(value.type == kObjectTypeInteger, "Option '%s' value must be Integer", name.data, { return; @@ -725,12 +724,12 @@ static void set_option_to(uint64_t channel_id, void *to, int type, String name, "Option '%s' value is out of range", name.data, { return; }); - numval = (int)value.data.integer; + optval = NUMBER_OPTVAL(value.data.integer); } else { VALIDATE(value.type == kObjectTypeString, "Option '%s' value must be String", name.data, { return; }); - stringval = value.data.string.data; + optval = STRING_OPTVAL(value.data.string); } // For global-win-local options -> setlocal @@ -739,6 +738,6 @@ static void set_option_to(uint64_t channel_id, void *to, int type, String name, (type == SREQ_GLOBAL) ? OPT_GLOBAL : OPT_LOCAL; WITH_SCRIPT_CONTEXT(channel_id, { - access_option_value_for(name.data, &numval, &stringval, opt_flags, type, to, false, err); + set_option_value_for(name.data, optval, opt_flags, type, to, err); }); } diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index aca290494b..0608a8961d 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -320,7 +320,7 @@ ArrayOf(Integer) nvim_buf_get_extmark_by_id(Buffer buffer, Integer ns_id, /// local ms = api.nvim_buf_get_extmarks(0, ns, {2,0}, {2,0}, {}) /// -- Get all marks in this buffer + namespace. /// local all = api.nvim_buf_get_extmarks(0, ns, 0, -1, {}) -/// print(vim.inspect(ms)) +/// vim.print(ms) /// </pre> /// /// @param buffer Buffer handle, or 0 for current buffer @@ -478,7 +478,7 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e /// shifting the underlying text. /// - "right_align": display right aligned in the window. /// - "inline": display at the specified column, and -/// shift the buffer text to the right as needed +/// shift the buffer text to the right as needed /// - virt_text_win_col : position the virtual text at a fixed /// window column (starting from the first /// text column) @@ -490,10 +490,10 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e /// highlights of the text. Currently only affects /// virt_text highlights, but might affect `hl_group` /// in later versions. -/// - "replace": only show the virt_text color. This is the -/// default -/// - "combine": combine with background text color +/// - "replace": only show the virt_text color. This is the default. +/// - "combine": combine with background text color. /// - "blend": blend with background text color. +/// Not supported for "inline" virt_text. /// /// - virt_lines : virtual lines to add next to this mark /// This should be an array over lines, where each line in @@ -701,7 +701,7 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer } else if (strequal("inline", str.data)) { decor.virt_text_pos = kVTInline; } else { - VALIDATE_S(false, "virt_text_pos", "", { + VALIDATE_S(false, "virt_text_pos", str.data, { goto error; }); } @@ -719,23 +719,28 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer OPTION_TO_BOOL(decor.virt_text_hide, virt_text_hide, false); OPTION_TO_BOOL(decor.hl_eol, hl_eol, false); - if (opts->hl_mode.type == kObjectTypeString) { + if (HAS_KEY(opts->hl_mode)) { + VALIDATE_T("hl_mode", kObjectTypeString, opts->hl_mode.type, { + goto error; + }); + String str = opts->hl_mode.data.string; if (strequal("replace", str.data)) { decor.hl_mode = kHlModeReplace; } else if (strequal("combine", str.data)) { decor.hl_mode = kHlModeCombine; } else if (strequal("blend", str.data)) { + if (decor.virt_text_pos == kVTInline) { + VALIDATE(false, "%s", "cannot use 'blend' hl_mode with inline virtual text", { + goto error; + }); + } decor.hl_mode = kHlModeBlend; } else { - VALIDATE_S(false, "virt_text_pos", "", { + VALIDATE_S(false, "hl_mode", str.data, { goto error; }); } - } else if (HAS_KEY(opts->hl_mode)) { - VALIDATE_T("hl_mode", kObjectTypeString, opts->hl_mode.type, { - goto error; - }); } bool virt_lines_leftcol = false; @@ -1048,7 +1053,7 @@ void nvim_buf_clear_namespace(Buffer buffer, Integer ns_id, Integer line_start, /// Set or change decoration provider for a |namespace| /// -/// This is a very general purpose interface for having lua callbacks +/// This is a very general purpose interface for having Lua callbacks /// being triggered during the redraw code. /// /// The expected usage is to set |extmarks| for the currently diff --git a/src/nvim/api/options.c b/src/nvim/api/options.c index e18312a6dc..b9a41adc3b 100644 --- a/src/nvim/api/options.c +++ b/src/nvim/api/options.c @@ -113,10 +113,10 @@ static buf_T *do_ft_buf(char *filetype, aco_save_T *aco, Error *err) aucmd_prepbuf(aco, ftbuf); TRY_WRAP(err, { - set_option_value("bufhidden", 0L, "hide", OPT_LOCAL); - set_option_value("buftype", 0L, "nofile", OPT_LOCAL); - set_option_value("swapfile", 0L, NULL, OPT_LOCAL); - set_option_value("modeline", 0L, NULL, OPT_LOCAL); // 'nomodeline' + set_option_value("bufhidden", STATIC_CSTR_AS_OPTVAL("hide"), OPT_LOCAL); + set_option_value("buftype", STATIC_CSTR_AS_OPTVAL("nofile"), OPT_LOCAL); + set_option_value("swapfile", BOOLEAN_OPTVAL(false), OPT_LOCAL); + set_option_value("modeline", BOOLEAN_OPTVAL(false), OPT_LOCAL); // 'nomodeline' ftbuf->b_p_ft = xstrdup(filetype); do_filetype_autocmd(ftbuf, false); @@ -125,6 +125,49 @@ static buf_T *do_ft_buf(char *filetype, aco_save_T *aco, Error *err) return ftbuf; } +/// Consume an OptVal and convert it to an API Object. +static Object optval_as_object(OptVal o) +{ + switch (o.type) { + case kOptValTypeNil: + return NIL; + case kOptValTypeBoolean: + switch (o.data.boolean) { + case kFalse: + case kTrue: + return BOOLEAN_OBJ(o.data.boolean); + case kNone: + return NIL; + default: + abort(); + } + case kOptValTypeNumber: + return INTEGER_OBJ(o.data.number); + case kOptValTypeString: + return STRING_OBJ(o.data.string); + default: + abort(); + } +} + +/// Consume an API Object and convert it to an OptVal. +static OptVal object_as_optval(Object o, bool *error) +{ + switch (o.type) { + case kObjectTypeNil: + return NIL_OPTVAL; + case kObjectTypeBoolean: + return BOOLEAN_OPTVAL(o.data.boolean); + case kObjectTypeInteger: + return NUMBER_OPTVAL(o.data.integer); + case kObjectTypeString: + return STRING_OPTVAL(o.data.string); + default: + *error = true; + return NIL_OPTVAL; + } +} + /// Gets the value of an option. The behavior of this function matches that of /// |:set|: the local value of an option is returned if it exists; otherwise, /// the global value is returned. Local values always correspond to the current @@ -147,6 +190,7 @@ Object nvim_get_option_value(String name, Dict(option) *opts, Error *err) FUNC_API_SINCE(9) { Object rv = OBJECT_INIT; + OptVal value = NIL_OPTVAL; int scope = 0; int opt_type = SREQ_GLOBAL; @@ -154,14 +198,14 @@ Object nvim_get_option_value(String name, Dict(option) *opts, Error *err) char *filetype = NULL; if (!validate_option_value_args(opts, &scope, &opt_type, &from, &filetype, err)) { - return rv; + goto err; } aco_save_T aco; buf_T *ftbuf = do_ft_buf(filetype, &aco, err); if (ERROR_SET(err)) { - return rv; + goto err; } if (ftbuf != NULL) { @@ -169,10 +213,8 @@ Object nvim_get_option_value(String name, Dict(option) *opts, Error *err) from = ftbuf; } - long numval = 0; - char *stringval = NULL; - getoption_T result = access_option_value_for(name.data, &numval, &stringval, scope, opt_type, - from, true, err); + bool hidden; + value = get_option_value_for(name.data, NULL, scope, &hidden, opt_type, from, err); if (ftbuf != NULL) { // restore curwin/curbuf and a few other things @@ -183,35 +225,16 @@ Object nvim_get_option_value(String name, Dict(option) *opts, Error *err) } if (ERROR_SET(err)) { - return rv; + goto err; } - switch (result) { - case gov_string: - rv = CSTR_AS_OBJ(stringval); - break; - case gov_number: - rv = INTEGER_OBJ(numval); - break; - case gov_bool: - switch (numval) { - case 0: - case 1: - rv = BOOLEAN_OBJ(numval); - break; - default: - // Boolean options that return something other than 0 or 1 should return nil. Currently this - // only applies to 'autoread' which uses -1 as a local value to indicate "unset" - rv = NIL; - break; - } - break; - default: - VALIDATE_S(false, "option", name.data, { - return rv; - }); - } + VALIDATE_S(!hidden && value.type != kOptValTypeNil, "option", name.data, { + goto err; + }); + return optval_as_object(value); +err: + optval_free(value); return rv; } @@ -253,30 +276,19 @@ void nvim_set_option_value(uint64_t channel_id, String name, Object value, Dict( } } - long numval = 0; - char *stringval = NULL; + bool error = false; + OptVal optval = object_as_optval(value, &error); - switch (value.type) { - case kObjectTypeInteger: - numval = (long)value.data.integer; - break; - case kObjectTypeBoolean: - numval = value.data.boolean ? 1 : 0; - break; - case kObjectTypeString: - stringval = value.data.string.data; - break; - case kObjectTypeNil: - scope |= OPT_CLEAR; - break; - default: - VALIDATE_EXP(false, name.data, "Integer/Boolean/String", api_typename(value.type), { + // Handle invalid option value type. + if (error) { + // Don't use `name` in the error message here, because `name` can be any String. + VALIDATE_EXP(false, "value", "Integer/Boolean/String", api_typename(value.type), { return; }); } WITH_SCRIPT_CONTEXT(channel_id, { - access_option_value_for(name.data, &numval, &stringval, scope, opt_type, to, false, err); + set_option_value_for(name.data, optval, scope, opt_type, to, err); }); } @@ -341,72 +353,135 @@ Dictionary nvim_get_option_info2(String name, Dict(option) *opts, Error *err) return get_vimoption(name, scope, buf, win, err); } -static getoption_T access_option_value(char *key, long *numval, char **stringval, int opt_flags, - bool get, Error *err) +/// Switch current context to get/set option value for window/buffer. +/// +/// @param[out] ctx Current context. switchwin_T for window and aco_save_T for buffer. +/// @param[in] opt_type Option type. See SREQ_* in option_defs.h. +/// @param[in] from Target buffer/window. +/// @param[out] err Error message, if any. +/// +/// @return true if context was switched, false otherwise. +static bool switch_option_context(void *const ctx, int opt_type, void *const from, Error *err) { - if (get) { - return get_option_value(key, numval, stringval, NULL, opt_flags); - } else { - const char *errmsg; - if ((errmsg = set_option_value(key, *numval, *stringval, opt_flags))) { + switch (opt_type) { + case SREQ_WIN: { + win_T *const win = (win_T *)from; + switchwin_T *const switchwin = (switchwin_T *)ctx; + + if (win == curwin) { + return false; + } + + if (switch_win_noblock(switchwin, win, win_find_tabpage(win), true) + == FAIL) { + restore_win_noblock(switchwin, true); + if (try_end(err)) { - return 0; + return false; } + api_set_error(err, kErrorTypeException, "Problem while switching windows"); + return false; + } + return true; + } + case SREQ_BUF: { + buf_T *const buf = (buf_T *)from; + aco_save_T *const aco = (aco_save_T *)ctx; - api_set_error(err, kErrorTypeException, "%s", errmsg); + if (buf == curbuf) { + return false; } - return 0; + aucmd_prepbuf(aco, buf); + return true; + } + case SREQ_GLOBAL: + return false; + default: + abort(); // This should never happen. } } -getoption_T access_option_value_for(char *key, long *numval, char **stringval, int opt_flags, - int opt_type, void *from, bool get, Error *err) +/// Restore context after getting/setting option for window/buffer. See switch_option_context() for +/// params. +static void restore_option_context(void *const ctx, const int opt_type) { - bool need_switch = false; - switchwin_T switchwin; - aco_save_T aco; - getoption_T result = 0; - - try_start(); switch (opt_type) { case SREQ_WIN: - need_switch = (win_T *)from != curwin; - if (need_switch) { - if (switch_win_noblock(&switchwin, (win_T *)from, win_find_tabpage((win_T *)from), true) - == FAIL) { - restore_win_noblock(&switchwin, true); - if (try_end(err)) { - return result; - } - api_set_error(err, kErrorTypeException, "Problem while switching windows"); - return result; - } - } - result = access_option_value(key, numval, stringval, opt_flags, get, err); - if (need_switch) { - restore_win_noblock(&switchwin, true); - } + restore_win_noblock((switchwin_T *)ctx, true); break; case SREQ_BUF: - need_switch = (buf_T *)from != curbuf; - if (need_switch) { - aucmd_prepbuf(&aco, (buf_T *)from); - } - result = access_option_value(key, numval, stringval, opt_flags, get, err); - if (need_switch) { - aucmd_restbuf(&aco); - } + aucmd_restbuf((aco_save_T *)ctx); break; case SREQ_GLOBAL: - result = access_option_value(key, numval, stringval, opt_flags, get, err); break; + default: + abort(); // This should never happen. } +} +/// Get option value for buffer / window. +/// +/// @param[in] name Option name. +/// @param[out] flagsp Set to the option flags (P_xxxx) (if not NULL). +/// @param[in] scope Option scope (can be OPT_LOCAL, OPT_GLOBAL or a combination). +/// @param[out] hidden Whether option is hidden. +/// @param[in] opt_type Option type. See SREQ_* in option_defs.h. +/// @param[in] from Target buffer/window. +/// @param[out] err Error message, if any. +/// +/// @return Option value. Must be freed by caller. +OptVal get_option_value_for(const char *const name, uint32_t *flagsp, int scope, bool *hidden, + const int opt_type, void *const from, Error *err) +{ + switchwin_T switchwin; + aco_save_T aco; + void *ctx = opt_type == SREQ_WIN ? (void *)&switchwin + : (opt_type == SREQ_BUF ? (void *)&aco : NULL); + + bool switched = switch_option_context(ctx, opt_type, from, err); if (ERROR_SET(err)) { - return result; + return NIL_OPTVAL; + } + + OptVal retv = get_option_value(name, flagsp, scope, hidden); + + if (switched) { + restore_option_context(ctx, opt_type); } - try_end(err); + return retv; +} - return result; +/// Set option value for buffer / window. +/// +/// @param[in] name Option name. +/// @param[in] value Option value. +/// @param[in] opt_flags Flags: OPT_LOCAL, OPT_GLOBAL, or 0 (both). +/// If OPT_CLEAR is set, the value of the option +/// is cleared (the exact semantics of this depend +/// on the option). +/// @param[in] opt_type Option type. See SREQ_* in option_defs.h. +/// @param[in] from Target buffer/window. +/// @param[out] err Error message, if any. +void set_option_value_for(const char *const name, OptVal value, const int opt_flags, + const int opt_type, void *const from, Error *err) +{ + switchwin_T switchwin; + aco_save_T aco; + void *ctx = opt_type == SREQ_WIN ? (void *)&switchwin + : (opt_type == SREQ_BUF ? (void *)&aco : NULL); + + bool switched = switch_option_context(ctx, opt_type, from, err); + if (ERROR_SET(err)) { + return; + } + + const char *const errmsg = set_option_value(name, value, opt_flags); + if (errmsg) { + api_set_error(err, kErrorTypeException, "%s", errmsg); + } + + if (switched) { + restore_option_context(ctx, opt_type); + } } diff --git a/src/nvim/api/private/converter.c b/src/nvim/api/private/converter.c index a62f975cfd..68939e609c 100644 --- a/src/nvim/api/private/converter.c +++ b/src/nvim/api/private/converter.c @@ -256,7 +256,7 @@ Object vim_to_object(typval_T *obj) return ret; } -/// Converts from type Object to a VimL value. +/// Converts from type Object to a Vimscript value. /// /// @param obj Object to convert from. /// @param tv Conversion result is placed here. On failure member v_type is @@ -283,7 +283,7 @@ bool object_to_vim(Object obj, typval_T *tv, Error *err) case kObjectTypeTabpage: case kObjectTypeInteger: STATIC_ASSERT(sizeof(obj.data.integer) <= sizeof(varnumber_T), - "Integer size must be <= VimL number size"); + "Integer size must be <= Vimscript number size"); tv->v_type = VAR_NUMBER; tv->vval.v_number = (varnumber_T)obj.data.integer; break; diff --git a/src/nvim/api/private/defs.h b/src/nvim/api/private/defs.h index 7c5559f096..b1b9e383b0 100644 --- a/src/nvim/api/private/defs.h +++ b/src/nvim/api/private/defs.h @@ -42,10 +42,10 @@ typedef enum { /// Mask for all internal calls #define INTERNAL_CALL_MASK (((uint64_t)1) << (sizeof(uint64_t) * 8 - 1)) -/// Internal call from VimL code +/// Internal call from Vimscript code #define VIML_INTERNAL_CALL INTERNAL_CALL_MASK -/// Internal call from lua code +/// Internal call from Lua code #define LUA_INTERNAL_CALL (VIML_INTERNAL_CALL + 1) static inline bool is_internal_call(uint64_t channel_id) diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 2544809553..f9861c82bf 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -40,10 +40,10 @@ # include "api/private/ui_events_metadata.generated.h" #endif -/// Start block that may cause VimL exceptions while evaluating another code +/// Start block that may cause Vimscript exceptions while evaluating another code /// -/// Used when caller is supposed to be operating when other VimL code is being -/// processed and that “other VimL code” must not be affected. +/// Used when caller is supposed to be operating when other Vimscript code is being +/// processed and that “other Vimscript code” must not be affected. /// /// @param[out] tstate Location where try state should be saved. void try_enter(TryState *const tstate) @@ -806,7 +806,7 @@ bool api_object_to_bool(Object obj, const char *what, bool nil_value, Error *err } else if (obj.type == kObjectTypeInteger) { return obj.data.integer; // C semantics: non-zero int is true } else if (obj.type == kObjectTypeNil) { - return nil_value; // caller decides what NIL (missing retval in lua) means + return nil_value; // caller decides what NIL (missing retval in Lua) means } else { api_set_error(err, kErrorTypeValidation, "%s is not a boolean", what); return false; diff --git a/src/nvim/api/private/helpers.h b/src/nvim/api/private/helpers.h index a9cfaeae22..cb74c655cd 100644 --- a/src/nvim/api/private/helpers.h +++ b/src/nvim/api/private/helpers.h @@ -20,7 +20,6 @@ #define BOOLEAN_OBJ(b) ((Object) { \ .type = kObjectTypeBoolean, \ .data.boolean = b }) -#define BOOL(b) BOOLEAN_OBJ(b) #define INTEGER_OBJ(i) ((Object) { \ .type = kObjectTypeInteger, \ @@ -95,7 +94,7 @@ #define cbuf_as_string(d, s) ((String) { .data = d, .size = s }) -#define STATIC_CSTR_AS_STRING(s) ((String) { .data = s, .size = sizeof(s) - 1 }) +#define STATIC_CSTR_AS_STRING(s) ((String) { .data = s, .size = sizeof("" s) - 1 }) /// Create a new String instance, putting data in allocated memory /// @@ -136,8 +135,8 @@ EXTERN PMap(int) tabpage_handles INIT(= MAP_INIT); /// Structure used for saving state for :try /// -/// Used when caller is supposed to be operating when other VimL code is being -/// processed and that “other VimL code” must not be affected. +/// Used when caller is supposed to be operating when other Vimscript code is being +/// processed and that “other Vimscript code” must not be affected. typedef struct { except_T *current_exception; msglist_T *private_msg_list; diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 4722195fe4..9996dae247 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -487,7 +487,7 @@ Integer nvim_strwidth(String text, Error *err) return (Integer)mb_string2cells(text.data); } -/// Gets the paths contained in 'runtimepath'. +/// Gets the paths contained in |runtime-search-path|. /// /// @return List of paths ArrayOf(String) nvim_list_runtime_paths(Error *err) @@ -544,7 +544,7 @@ String nvim__get_lib_dir(void) /// /// @param pat pattern of files to search for /// @param all whether to return all matches or only the first -/// @param opts is_lua: only search lua subdirs +/// @param opts is_lua: only search Lua subdirs /// @return list of absolute paths to the found files ArrayOf(String) nvim__get_runtime(Array pat, Boolean all, Dict(runtime) *opts, Error *err) FUNC_API_SINCE(8) @@ -910,10 +910,10 @@ Buffer nvim_create_buf(Boolean listed, Boolean scratch, Error *err) if (scratch) { aco_save_T aco; aucmd_prepbuf(&aco, buf); - set_option_value("bufhidden", 0L, "hide", OPT_LOCAL); - set_option_value("buftype", 0L, "nofile", OPT_LOCAL); - set_option_value("swapfile", 0L, NULL, OPT_LOCAL); - set_option_value("modeline", 0L, NULL, OPT_LOCAL); // 'nomodeline' + set_option_value("bufhidden", STATIC_CSTR_AS_OPTVAL("hide"), OPT_LOCAL); + set_option_value("buftype", STATIC_CSTR_AS_OPTVAL("nofile"), OPT_LOCAL); + set_option_value("swapfile", BOOLEAN_OPTVAL(false), OPT_LOCAL); + set_option_value("modeline", BOOLEAN_OPTVAL(false), OPT_LOCAL); // 'nomodeline' aucmd_restbuf(&aco); } return buf->b_fnum; @@ -941,7 +941,7 @@ fail: /// /// @param buffer the buffer to use (expected to be empty) /// @param opts Optional parameters. -/// - on_input: lua callback for input sent, i e keypresses in terminal +/// - on_input: Lua callback for input sent, i e keypresses in terminal /// mode. Note: keypresses are sent raw as they would be to the pty /// master end. For instance, a carriage return is sent /// as a "\r", not as a "\n". |textlock| applies. It is possible @@ -1009,7 +1009,7 @@ static void term_write(char *buf, size_t size, void *data) // NOLINT(readabilit static void term_resize(uint16_t width, uint16_t height, void *data) { - // TODO(bfredl): lua callback + // TODO(bfredl): Lua callback } static void term_close(void *data) @@ -1420,13 +1420,14 @@ ArrayOf(Dictionary) nvim_get_keymap(String mode) /// @param channel_id /// @param mode Mode short-name (map command prefix: "n", "i", "v", "x", …) /// or "!" for |:map!|, or empty string for |:map|. +/// "ia", "ca" or "!a" for abbreviation in Insert mode, Cmdline mode, or both, respectively /// @param lhs Left-hand-side |{lhs}| of the mapping. /// @param rhs Right-hand-side |{rhs}| of the mapping. /// @param opts Optional parameters map: Accepts all |:map-arguments| as keys except |<buffer>|, /// values are booleans (default false). Also: /// - "noremap" non-recursive mapping |:noremap| /// - "desc" human-readable description. -/// - "callback" Lua function called when the mapping is executed. +/// - "callback" Lua function called in place of {rhs}. /// - "replace_keycodes" (boolean) When "expr" is true, replace keycodes in the /// resulting string (see |nvim_replace_termcodes()|). Returning nil from the Lua /// "callback" is equivalent to returning an empty string. diff --git a/src/nvim/api/vimscript.c b/src/nvim/api/vimscript.c index 1a67be8860..eb573e2556 100644 --- a/src/nvim/api/vimscript.c +++ b/src/nvim/api/vimscript.c @@ -38,7 +38,7 @@ /// Unlike |nvim_command()| this function supports heredocs, script-scope (s:), /// etc. /// -/// On execution error: fails with VimL error, updates v:errmsg. +/// On execution error: fails with Vimscript error, updates v:errmsg. /// /// @see |execute()| /// @see |nvim_command()| @@ -126,7 +126,7 @@ theend: /// Executes an Ex command. /// -/// On execution error: fails with VimL error, updates v:errmsg. +/// On execution error: fails with Vimscript error, updates v:errmsg. /// /// Prefer using |nvim_cmd()| or |nvim_exec2()| over this. To evaluate multiple lines of Vim script /// or an Ex command directly, use |nvim_exec2()|. To construct an Ex command using a structured @@ -143,12 +143,12 @@ void nvim_command(String command, Error *err) try_end(err); } -/// Evaluates a VimL |expression|. +/// Evaluates a Vimscript |expression|. /// Dictionaries and Lists are recursively expanded. /// -/// On execution error: fails with VimL error, updates v:errmsg. +/// On execution error: fails with Vimscript error, updates v:errmsg. /// -/// @param expr VimL expression string +/// @param expr Vimscript expression string /// @param[out] err Error details, if any /// @return Evaluation result or expanded object Object nvim_eval(String expr, Error *err) @@ -192,7 +192,7 @@ Object nvim_eval(String expr, Error *err) return rv; } -/// Calls a VimL function. +/// Calls a Vimscript function. /// /// @param fn Function name /// @param args Function arguments @@ -258,9 +258,9 @@ free_vim_args: return rv; } -/// Calls a VimL function with the given arguments. +/// Calls a Vimscript function with the given arguments. /// -/// On execution error: fails with VimL error, updates v:errmsg. +/// On execution error: fails with Vimscript error, updates v:errmsg. /// /// @param fn Function to call /// @param args Function arguments packed in an Array @@ -272,12 +272,12 @@ Object nvim_call_function(String fn, Array args, Error *err) return _call_function(fn, args, NULL, err); } -/// Calls a VimL |Dictionary-function| with the given arguments. +/// Calls a Vimscript |Dictionary-function| with the given arguments. /// -/// On execution error: fails with VimL error, updates v:errmsg. +/// On execution error: fails with Vimscript error, updates v:errmsg. /// -/// @param dict Dictionary, or String evaluating to a VimL |self| dict -/// @param fn Name of the function defined on the VimL dict +/// @param dict Dictionary, or String evaluating to a Vimscript |self| dict +/// @param fn Name of the function defined on the Vimscript dict /// @param args Function arguments packed in an Array /// @param[out] err Error details, if any /// @return Result of the function call @@ -363,7 +363,7 @@ typedef struct { typedef kvec_withinit_t(ExprASTConvStackItem, 16) ExprASTConvStack; /// @endcond -/// Parse a VimL expression. +/// Parse a Vimscript expression. /// /// @param[in] expr Expression to parse. Always treated as a single line. /// @param[in] flags Flags: diff --git a/src/nvim/api/window.c b/src/nvim/api/window.c index c021fec220..e5a824fec9 100644 --- a/src/nvim/api/window.c +++ b/src/nvim/api/window.c @@ -57,6 +57,8 @@ void nvim_win_set_buf(Window window, Buffer buffer, Error *err) /// (different windows showing the same buffer have independent cursor /// positions). |api-indexing| /// +/// @see |getcurpos()| +/// /// @param window Window handle, or 0 for current window /// @param[out] err Error details, if any /// @return (row, col) tuple @@ -410,11 +412,11 @@ void nvim_win_close(Window window, Boolean force, Error *err) /// @see |nvim_buf_call()| /// /// @param window Window handle, or 0 for current window -/// @param fun Function to call inside the window (currently lua callable +/// @param fun Function to call inside the window (currently Lua callable /// only) /// @param[out] err Error details, if any -/// @return Return value of function. NB: will deepcopy lua values -/// currently, use upvalues to send lua references in and out. +/// @return Return value of function. NB: will deepcopy Lua values +/// currently, use upvalues to send Lua references in and out. Object nvim_win_call(Window window, LuaRef fun, Error *err) FUNC_API_SINCE(7) FUNC_API_LUA_ONLY diff --git a/src/nvim/auevents.lua b/src/nvim/auevents.lua index 048b8d6631..41d7ee9b47 100644 --- a/src/nvim/auevents.lua +++ b/src/nvim/auevents.lua @@ -74,6 +74,7 @@ return { 'LspDetach', -- after an LSP client detaches from a buffer 'LspRequest', -- after an LSP request is started, canceled, or completed 'LspTokenUpdate', -- after a visible LSP token is updated + 'LspProgress', -- after a LSP progress update 'MenuPopup', -- just before popup menu is displayed 'ModeChanged', -- after changing the mode 'OptionSet', -- after setting any option @@ -154,6 +155,7 @@ return { LspAttach=true, LspDetach=true, LspRequest=true, + LspProgress=true, LspTokenUpdate=true, RecordingEnter=true, RecordingLeave=true, diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index 53cca7baa1..36f0183fd8 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -1603,6 +1603,7 @@ bool apply_autocmds_group(event_T event, char *fname, char *fname_io, bool force // Save the autocmd_* variables and info about the current buffer. char *save_autocmd_fname = autocmd_fname; + bool save_autocmd_fname_full = autocmd_fname_full; int save_autocmd_bufnr = autocmd_bufnr; char *save_autocmd_match = autocmd_match; int save_autocmd_busy = autocmd_busy; @@ -1631,6 +1632,7 @@ bool apply_autocmds_group(event_T event, char *fname, char *fname_io, bool force // Allocate MAXPATHL for when eval_vars() resolves the fullpath. autocmd_fname = xstrnsave(autocmd_fname, MAXPATHL); } + autocmd_fname_full = false; // call FullName_save() later // Set the buffer number to be used for <abuf>. autocmd_bufnr = buf == NULL ? 0 : buf->b_fnum; @@ -1674,6 +1676,7 @@ bool apply_autocmds_group(event_T event, char *fname, char *fname_io, bool force || event == EVENT_USER || event == EVENT_WINCLOSED || event == EVENT_WINRESIZED || event == EVENT_WINSCROLLED) { fname = xstrdup(fname); + autocmd_fname_full = true; // don't expand it later } else { fname = FullName_save(fname, false); } @@ -1780,8 +1783,14 @@ bool apply_autocmds_group(event_T event, char *fname, char *fname_io, bool force check_lnums_nested(true); } + const int save_did_emsg = did_emsg; + const bool save_ex_pressedreturn = get_pressedreturn(); + // Execute the autocmd. The `getnextac` callback handles iteration. - do_cmdline(NULL, getnextac, (void *)&patcmd, DOCMD_NOWAIT | DOCMD_VERBOSE | DOCMD_REPEAT); + do_cmdline(NULL, getnextac, &patcmd, DOCMD_NOWAIT | DOCMD_VERBOSE | DOCMD_REPEAT); + + did_emsg += save_did_emsg; + set_pressedreturn(save_ex_pressedreturn); if (nesting == 1) { // restore cursor and topline, unless they were changed @@ -1806,6 +1815,7 @@ bool apply_autocmds_group(event_T event, char *fname, char *fname_io, bool force estack_pop(); xfree(autocmd_fname); autocmd_fname = save_autocmd_fname; + autocmd_fname_full = save_autocmd_fname_full; autocmd_bufnr = save_autocmd_bufnr; autocmd_match = save_autocmd_match; current_sctx = save_current_sctx; diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index bc52ab0771..a07b1c5720 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -475,7 +475,7 @@ static bool can_unload_buffer(buf_T *buf) /// Possible values: /// 0 buffer becomes hidden /// DOBUF_UNLOAD buffer is unloaded -/// DOBUF_DELETE buffer is unloaded and removed from buffer list +/// DOBUF_DEL buffer is unloaded and removed from buffer list /// DOBUF_WIPE buffer is unloaded and really deleted /// When doing all but the first one on the current buffer, the /// caller should get a new buffer very soon! @@ -4304,9 +4304,9 @@ int buf_open_scratch(handle_T bufnr, char *bufname) apply_autocmds(EVENT_BUFFILEPRE, NULL, NULL, false, curbuf); (void)setfname(curbuf, bufname, NULL, true); apply_autocmds(EVENT_BUFFILEPOST, NULL, NULL, false, curbuf); - set_option_value_give_err("bh", 0L, "hide", OPT_LOCAL); - set_option_value_give_err("bt", 0L, "nofile", OPT_LOCAL); - set_option_value_give_err("swf", 0L, NULL, OPT_LOCAL); + set_option_value_give_err("bh", STATIC_CSTR_AS_OPTVAL("hide"), OPT_LOCAL); + set_option_value_give_err("bt", STATIC_CSTR_AS_OPTVAL("nofile"), OPT_LOCAL); + set_option_value_give_err("swf", BOOLEAN_OPTVAL(false), OPT_LOCAL); RESET_BINDING(curwin); return OK; } diff --git a/src/nvim/buffer.h b/src/nvim/buffer.h index 610d9e37ec..1bca08e357 100644 --- a/src/nvim/buffer.h +++ b/src/nvim/buffer.h @@ -10,7 +10,7 @@ #include "nvim/eval/typval_defs.h" #include "nvim/ex_cmds_defs.h" #include "nvim/func_attr.h" -#include "nvim/grid_defs.h" // for StlClickRecord +#include "nvim/grid_defs.h" #include "nvim/macros.h" #include "nvim/memline.h" #include "nvim/memline_defs.h" diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index f3f98bbd17..d92d425ed5 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -18,6 +18,7 @@ typedef struct { #include "klib/kvec.h" #include "nvim/api/private/defs.h" #include "nvim/eval/typval_defs.h" +#include "nvim/extmark_defs.h" #include "nvim/garray.h" #include "nvim/grid_defs.h" #include "nvim/hashtab.h" @@ -25,9 +26,6 @@ typedef struct { #include "nvim/map.h" #include "nvim/mark_defs.h" #include "nvim/marktree.h" -// for float window title -#include "nvim/extmark_defs.h" -// for click definitions #include "nvim/option_defs.h" #include "nvim/pos.h" #include "nvim/statusline_defs.h" @@ -1292,8 +1290,9 @@ struct window_S { linenr_T w_stl_line_count; // line count when last redrawn int w_stl_topfill; // topfill when last redrawn char w_stl_empty; // true if elements show 0-1 (empty line) - int w_stl_state; // State when last redrawn int w_stl_recording; // reg_recording when last redrawn + int w_stl_state; // get_real_state() when last redrawn + int w_stl_visual_mode; // VIsual_mode when last redrawn int w_alt_fnum; // alternate file (for # and CTRL-^) diff --git a/src/nvim/charset.c b/src/nvim/charset.c index 49890a460a..c2745a66a0 100644 --- a/src/nvim/charset.c +++ b/src/nvim/charset.c @@ -645,8 +645,8 @@ size_t transchar_hex(char *const buf, const int c) size_t i = 0; buf[i++] = '<'; - if (c > 255) { - if (c > 255 * 256) { + if (c > 0xFF) { + if (c > 0xFFFF) { buf[i++] = (char)nr2hex((unsigned)c >> 20); buf[i++] = (char)nr2hex((unsigned)c >> 16); } diff --git a/src/nvim/cmdexpand.c b/src/nvim/cmdexpand.c index bf0944a81d..b2835cae0f 100644 --- a/src/nvim/cmdexpand.c +++ b/src/nvim/cmdexpand.c @@ -841,7 +841,7 @@ static char *find_longest_match(expand_T *xp, int options) char *ExpandOne(expand_T *xp, char *str, char *orig, int options, int mode) { char *ss = NULL; - static int findex; + static int findex; // TODO(vim): Move into expand_T static char *orig_save = NULL; // kept value of orig int orig_saved = false; @@ -871,7 +871,10 @@ char *ExpandOne(expand_T *xp, char *str, char *orig, int options, int mode) cmdline_pum_remove(); } } - findex = 0; + // TODO(vim): Remove condition if "findex" is part of expand_T ? + if (mode != WILD_EXPAND_FREE && mode != WILD_ALL && mode != WILD_ALL_KEEP) { + findex = 0; + } if (mode == WILD_FREE) { // only release file name return NULL; diff --git a/src/nvim/cmdhist.c b/src/nvim/cmdhist.c index fc84cecc1a..072898706b 100644 --- a/src/nvim/cmdhist.c +++ b/src/nvim/cmdhist.c @@ -204,7 +204,7 @@ static inline void clear_hist_entry(histentry_T *hisptr) /// If 'move_to_front' is true, matching entry is moved to end of history. /// /// @param move_to_front Move the entry to the front if it exists -static int in_history(int type, char *str, int move_to_front, int sep) +static int in_history(int type, const char *str, int move_to_front, int sep) { int last_i = -1; @@ -238,7 +238,7 @@ static int in_history(int type, char *str, int move_to_front, int sep) } list_T *const list = history[type][i].additional_elements; - str = history[type][i].hisstr; + char *const save_hisstr = history[type][i].hisstr; while (i != hisidx[type]) { if (++i >= hislen) { i = 0; @@ -248,7 +248,7 @@ static int in_history(int type, char *str, int move_to_front, int sep) } tv_list_unref(list); history[type][i].hisnum = ++hisnum[type]; - history[type][i].hisstr = str; + history[type][i].hisstr = save_hisstr; history[type][i].timestamp = os_time(); history[type][i].additional_elements = NULL; return true; @@ -295,7 +295,7 @@ static int last_maptick = -1; // last seen maptick /// @param histype may be one of the HIST_ values. /// @param in_map consider maptick when inside a mapping /// @param sep separator character used (search hist) -void add_to_history(int histype, char *new_entry, int in_map, int sep) +void add_to_history(int histype, const char *new_entry, int in_map, int sep) { histentry_T *hisptr; @@ -538,7 +538,7 @@ void f_histadd(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) } init_history(); - add_to_history(histype, (char *)str, false, NUL); + add_to_history(histype, str, false, NUL); rettv->vval.v_number = true; } diff --git a/src/nvim/context.c b/src/nvim/context.c index b13a331eff..f7b3491be7 100644 --- a/src/nvim/context.c +++ b/src/nvim/context.c @@ -143,9 +143,8 @@ bool ctx_restore(Context *ctx, const int flags) free_ctx = true; } - char *op_shada; - get_option_value("shada", NULL, &op_shada, NULL, OPT_GLOBAL); - set_option_value("shada", 0L, "!,'100,%", OPT_GLOBAL); + OptVal op_shada = get_option_value("shada", NULL, OPT_GLOBAL, NULL); + set_option_value("shada", STATIC_CSTR_AS_OPTVAL("!,'100,%"), OPT_GLOBAL); if (flags & kCtxRegs) { ctx_restore_regs(ctx); @@ -171,8 +170,8 @@ bool ctx_restore(Context *ctx, const int flags) ctx_free(ctx); } - set_option_value("shada", 0L, op_shada, OPT_GLOBAL); - xfree(op_shada); + set_option_value("shada", op_shada, OPT_GLOBAL); + optval_free(op_shada); return true; } diff --git a/src/nvim/diff.c b/src/nvim/diff.c index f1a3a679be..810f631a02 100644 --- a/src/nvim/diff.c +++ b/src/nvim/diff.c @@ -1389,14 +1389,14 @@ void ex_diffthis(exarg_T *eap) diff_win_options(curwin, true); } -static void set_diff_option(win_T *wp, int value) +static void set_diff_option(win_T *wp, bool value) { win_T *old_curwin = curwin; curwin = wp; curbuf = curwin->w_buffer; curbuf->b_ro_locked++; - set_option_value_give_err("diff", (long)value, NULL, OPT_LOCAL); + set_option_value_give_err("diff", BOOLEAN_OPTVAL(value), OPT_LOCAL); curbuf->b_ro_locked--; curwin = old_curwin; curbuf = curwin->w_buffer; diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c index ff14ae0e41..c04ee0c222 100644 --- a/src/nvim/drawline.c +++ b/src/nvim/drawline.c @@ -92,7 +92,7 @@ typedef struct { int fromcol; ///< start of inverting int tocol; ///< end of inverting - long vcol_sbr; ///< virtual column after showbreak + colnr_T vcol_sbr; ///< virtual column after showbreak bool need_showbreak; ///< overlong line, skipping first x chars int char_attr; ///< attributes for next character @@ -128,11 +128,13 @@ typedef struct { VirtText virt_inline; size_t virt_inline_i; + HlMode virt_inline_hl_mode; bool reset_extra_attr; int skip_cells; // nr of cells to skip for virtual text int skipped_cells; // nr of skipped virtual text cells + bool more_virt_inline_chunks; // indicates if there is more inline virtual text after n_extra } winlinevars_T; /// for line_putchar. Contains the state that needs to be remembered from @@ -733,10 +735,10 @@ static void get_statuscol_display_info(statuscol_T *stcp, winlinevars_T *wlv) wlv->draw_state = WL_STC; wlv->char_attr = stcp->cur_attr; wlv->p_extra = stcp->textp; - wlv->n_extra = - (int)((stcp->hlrecp->start ? stcp->hlrecp->start : stcp->text_end) - stcp->textp); + char *const section_end = stcp->hlrecp->start ? stcp->hlrecp->start : stcp->text_end; + wlv->n_extra = (int)(section_end - stcp->textp); // Prepare for next highlight section if not yet at the end - if (stcp->textp + wlv->n_extra < stcp->text_end) { + if (section_end < stcp->text_end) { int hl = stcp->hlrecp->userhl; stcp->textp = stcp->hlrecp->start; stcp->cur_attr = hl < 0 ? syn_id2attr(-hl) : stcp->num_attr; @@ -745,6 +747,11 @@ static void get_statuscol_display_info(statuscol_T *stcp, winlinevars_T *wlv) } // Skip over empty highlight sections } while (wlv->n_extra == 0 && stcp->textp < stcp->text_end); + if (wlv->n_extra > 0) { + static char transbuf[MAX_NUMBERWIDTH * MB_MAXBYTES + 1]; + wlv->n_extra = (int)transstr_buf(wlv->p_extra, wlv->n_extra, transbuf, sizeof transbuf, true); + wlv->p_extra = transbuf; + } } static void handle_breakindent(win_T *wp, winlinevars_T *wlv) @@ -832,6 +839,12 @@ static void handle_showbreak_and_filler(win_T *wp, winlinevars_T *wlv) wlv->need_showbreak = false; } wlv->vcol_sbr = wlv->vcol + mb_charlen(sbr); + + // Correct start of highlighted area for 'showbreak'. + if (wlv->fromcol >= wlv->vcol && wlv->fromcol < wlv->vcol_sbr) { + wlv->fromcol = wlv->vcol_sbr; + } + // Correct end of highlighted area for 'showbreak', // required when 'linebreak' is also set. if (wlv->tocol == wlv->vcol) { @@ -863,11 +876,32 @@ static void apply_cursorline_highlight(win_T *wp, winlinevars_T *wlv) } } -static void handle_inline_virtual_text(win_T *wp, winlinevars_T *wlv, ptrdiff_t v, bool *do_save) +// Checks if there is more inline virtual text that need to be drawn +// and sets has_more_virt_inline_chunks to reflect that. +static bool has_more_inline_virt(winlinevars_T *wlv, ptrdiff_t v) { - while (true) { - // we could already be inside an existing inline text with multiple chunks - if (!(wlv->virt_inline_i < kv_size(wlv->virt_inline))) { + DecorState *state = &decor_state; + for (size_t i = 0; i < kv_size(state->active); i++) { + DecorRange *item = &kv_A(state->active, i); + if (item->start_row != state->row + || !kv_size(item->decor.virt_text) + || item->decor.virt_text_pos != kVTInline) { + continue; + } + if (item->draw_col >= -1 && item->start_col >= v) { + return true; + } + } + return false; +} + +static void handle_inline_virtual_text(win_T *wp, winlinevars_T *wlv, ptrdiff_t v) +{ + while (wlv->n_extra == 0) { + if (wlv->virt_inline_i >= kv_size(wlv->virt_inline)) { + // need to find inline virtual text + wlv->virt_inline = VIRTTEXT_EMPTY; + wlv->virt_inline_i = 0; DecorState *state = &decor_state; for (size_t i = 0; i < kv_size(state->active); i++) { DecorRange *item = &kv_A(state->active, i); @@ -878,28 +912,34 @@ static void handle_inline_virtual_text(win_T *wp, winlinevars_T *wlv, ptrdiff_t } if (item->draw_col >= -1 && item->start_col == v) { wlv->virt_inline = item->decor.virt_text; - wlv->virt_inline_i = 0; + wlv->virt_inline_hl_mode = item->decor.hl_mode; item->draw_col = INT_MIN; break; } } - } - - if (wlv->n_extra == 0 || !wlv->extra_for_extmark) { - wlv->reset_extra_attr = false; - } - - if (wlv->n_extra <= 0 && wlv->virt_inline_i < kv_size(wlv->virt_inline)) { + wlv->more_virt_inline_chunks = has_more_inline_virt(wlv, v); + if (!kv_size(wlv->virt_inline)) { + // no more inline virtual text here + break; + } + } else { + // already inside existing inline virtual text with multiple chunks VirtTextChunk vtc = kv_A(wlv->virt_inline, wlv->virt_inline_i); + wlv->virt_inline_i++; wlv->p_extra = vtc.text; - wlv->n_extra = (int)strlen(wlv->p_extra); - wlv->extra_for_extmark = true; + wlv->n_extra = (int)strlen(vtc.text); + if (wlv->n_extra == 0) { + continue; + } wlv->c_extra = NUL; wlv->c_final = NUL; wlv->extra_attr = vtc.hl_id ? syn_id2attr(vtc.hl_id) : 0; wlv->n_attr = mb_charlen(vtc.text); - wlv->virt_inline_i++; - *do_save = true; + + // Checks if there is more inline virtual text chunks that need to be drawn. + wlv->more_virt_inline_chunks = has_more_inline_virt(wlv, v) + || wlv->virt_inline_i < kv_size(wlv->virt_inline); + // If the text didn't reach until the first window // column we need to skip cells. if (wlv->skip_cells > 0) { @@ -920,13 +960,13 @@ static void handle_inline_virtual_text(win_T *wp, winlinevars_T *wlv, ptrdiff_t wlv->skipped_cells += virt_text_len; wlv->n_attr = 0; wlv->n_extra = 0; - // go to the start so the next virtual text chunk can be selected. continue; } } + assert(wlv->n_extra > 0); + wlv->extra_for_extmark = true; } - break; } } @@ -1518,7 +1558,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl // If there the text doesn't reach to the desired column, need to skip // "skip_cells" cells when virtual text follows. - if (!wp->w_p_wrap && v > wlv.vcol) { + if ((!wp->w_p_wrap || (lnum == wp->w_topline && wp->w_skipcol > 0)) && v > wlv.vcol) { wlv.skip_cells = (int)(v - wlv.vcol); } @@ -1754,50 +1794,10 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl break; } - if (wlv.draw_state == WL_LINE - && has_fold - && wlv.col == win_col_offset - && wlv.n_extra == 0 - && wlv.row == startrow + wlv.filler_lines) { + const bool draw_folded = wlv.draw_state == WL_LINE && has_fold + && wlv.row == startrow + wlv.filler_lines; + if (draw_folded && wlv.n_extra == 0) { wlv.char_attr = folded_attr = win_hl_attr(wp, HLF_FL); - - linenr_T lnume = lnum + foldinfo.fi_lines - 1; - memset(buf_fold, ' ', FOLD_TEXT_LEN); - wlv.p_extra = get_foldtext(wp, lnum, lnume, foldinfo, buf_fold); - wlv.n_extra = (int)strlen(wlv.p_extra); - - if (wlv.p_extra != buf_fold) { - xfree(wlv.p_extra_free); - wlv.p_extra_free = wlv.p_extra; - } - wlv.c_extra = NUL; - wlv.c_final = NUL; - wlv.p_extra[wlv.n_extra] = NUL; - - // Get the line again as evaluating 'foldtext' may free it. - line = ml_get_buf(wp->w_buffer, lnum, false); - ptr = line + v; - } - - if (wlv.draw_state == WL_LINE - && has_fold - && wlv.col < grid->cols - && wlv.n_extra == 0 - && wlv.row == startrow + wlv.filler_lines) { - // fill rest of line with 'fold' - wlv.c_extra = wp->w_p_fcs_chars.fold; - wlv.c_final = NUL; - - wlv.n_extra = wp->w_p_rl ? (wlv.col + 1) : (grid->cols - wlv.col); - } - - if (wlv.draw_state == WL_LINE - && has_fold - && wlv.col >= grid->cols - && wlv.n_extra != 0 - && wlv.row == startrow + wlv.filler_lines) { - // Truncate the folding. - wlv.n_extra = 0; } int extmark_attr = 0; @@ -1820,15 +1820,18 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl area_active = false; } - if (has_decor && v >= 0) { + if (wlv.n_extra == 0 || !wlv.extra_for_extmark) { + wlv.reset_extra_attr = false; + } + + if (has_decor && wlv.n_extra == 0) { bool selected = (area_active || (area_highlighting && noinvcur && wlv.vcol == wp->w_virtcol)); extmark_attr = decor_redraw_col(wp, (colnr_T)v, wlv.off, selected, &decor_state); if (!has_fold) { - bool do_save = false; - handle_inline_virtual_text(wp, &wlv, v, &do_save); - if (do_save) { + handle_inline_virtual_text(wp, &wlv, v); + if (wlv.n_extra > 0 && wlv.virt_inline_hl_mode <= kHlModeReplace) { // restore search_attr and area_attr when n_extra is down to zero // TODO(bfredl): this is ugly as fuck. look if we can do this some other way. saved_search_attr = search_attr; @@ -1843,7 +1846,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl } } - if (wlv.n_extra == 0) { + if (!has_fold && wlv.n_extra == 0) { // Check for start/end of 'hlsearch' and other matches. // After end, check for start/end of next match. // When another match, have to check for start again. @@ -1910,6 +1913,37 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl } } + if (draw_folded && wlv.n_extra == 0 && wlv.col == win_col_offset) { + linenr_T lnume = lnum + foldinfo.fi_lines - 1; + memset(buf_fold, ' ', FOLD_TEXT_LEN); + wlv.p_extra = get_foldtext(wp, lnum, lnume, foldinfo, buf_fold); + wlv.n_extra = (int)strlen(wlv.p_extra); + + if (wlv.p_extra != buf_fold) { + xfree(wlv.p_extra_free); + wlv.p_extra_free = wlv.p_extra; + } + wlv.c_extra = NUL; + wlv.c_final = NUL; + wlv.p_extra[wlv.n_extra] = NUL; + + // Get the line again as evaluating 'foldtext' may free it. + line = ml_get_buf(wp->w_buffer, lnum, false); + ptr = line + v; + } + + if (draw_folded && wlv.n_extra == 0 && wlv.col < grid->cols) { + // Fill rest of line with 'fold'. + wlv.c_extra = wp->w_p_fcs_chars.fold; + wlv.c_final = NUL; + wlv.n_extra = wp->w_p_rl ? (wlv.col + 1) : (grid->cols - wlv.col); + } + + if (draw_folded && wlv.n_extra != 0 && wlv.col >= grid->cols) { + // Truncate the folding. + wlv.n_extra = 0; + } + // Get the next character to put on the screen. // // The "p_extra" points to the extra stuff that is inserted to @@ -2871,7 +2905,8 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl && !has_fold && (*ptr != NUL || lcs_eol_one > 0 - || (wlv.n_extra > 0 && (wlv.c_extra != NUL || *wlv.p_extra != NUL)))) { + || (wlv.n_extra > 0 && (wlv.c_extra != NUL || *wlv.p_extra != NUL)) + || wlv.more_virt_inline_chunks)) { c = wp->w_p_lcs_chars.ext; wlv.char_attr = win_hl_attr(wp, HLF_AT); mb_c = c; @@ -3061,7 +3096,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl || (wp->w_p_list && wp->w_p_lcs_chars.eol != NUL && wlv.p_extra != at_end_str) || (wlv.n_extra != 0 - && (wlv.c_extra != NUL || *wlv.p_extra != NUL)))) { + && (wlv.c_extra != NUL || *wlv.p_extra != NUL)) || wlv.more_virt_inline_chunks)) { bool wrap = wp->w_p_wrap // Wrapping enabled. && wlv.filler_todo <= 0 // Not drawing diff filler lines. && lcs_eol_one != -1 // Haven't printed the lcs_eol character. diff --git a/src/nvim/drawscreen.c b/src/nvim/drawscreen.c index 28a029d758..3f024c507b 100644 --- a/src/nvim/drawscreen.c +++ b/src/nvim/drawscreen.c @@ -839,7 +839,8 @@ void show_cursor_info_later(bool force) || curwin->w_topfill != curwin->w_stl_topfill || empty_line != curwin->w_stl_empty || reg_recording != curwin->w_stl_recording - || state != curwin->w_stl_state) { + || state != curwin->w_stl_state + || (VIsual_active && VIsual_mode != curwin->w_stl_visual_mode)) { if (curwin->w_status_height || global_stl_height()) { curwin->w_redr_status = true; } else { @@ -862,8 +863,11 @@ void show_cursor_info_later(bool force) curwin->w_stl_topline = curwin->w_topline; curwin->w_stl_line_count = curwin->w_buffer->b_ml.ml_line_count; curwin->w_stl_topfill = curwin->w_topfill; - curwin->w_stl_state = state; curwin->w_stl_recording = reg_recording; + curwin->w_stl_state = state; + if (VIsual_active) { + curwin->w_stl_visual_mode = VIsual_mode; + } } /// @return true when postponing displaying the mode message: when not redrawing diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 118c1d3012..1c9fdef20d 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -58,6 +58,7 @@ #include "nvim/msgpack_rpc/channel_defs.h" #include "nvim/ops.h" #include "nvim/option.h" +#include "nvim/option_defs.h" #include "nvim/optionstr.h" #include "nvim/os/fileio.h" #include "nvim/os/fs_defs.h" @@ -88,20 +89,25 @@ static const char *e_missbrac = N_("E111: Missing ']'"); static const char *e_list_end = N_("E697: Missing end of List ']': %s"); -static const char *e_cannot_slice_dictionary +static const char e_cannot_slice_dictionary[] = N_("E719: Cannot slice a Dictionary"); static const char e_cannot_index_special_variable[] = N_("E909: Cannot index a special variable"); static const char *e_nowhitespace = N_("E274: No white space allowed before parenthesis"); static const char *e_write2 = N_("E80: Error while writing: %s"); +static const char e_cannot_index_a_funcref[] + = N_("E695: Cannot index a Funcref"); static const char e_variable_nested_too_deep_for_making_copy[] = N_("E698: Variable nested too deep for making a copy"); -static const char *e_string_list_or_blob_required = N_("E1098: String, List or Blob required"); -static const char e_expression_too_recursive_str[] = N_("E1169: Expression too recursive: %s"); +static const char e_string_list_or_blob_required[] + = N_("E1098: String, List or Blob required"); +static const char e_expression_too_recursive_str[] + = N_("E1169: Expression too recursive: %s"); static const char e_dot_can_only_be_used_on_dictionary_str[] = N_("E1203: Dot can only be used on a dictionary: %s"); -static const char e_empty_function_name[] = N_("E1192: Empty function name"); +static const char e_empty_function_name[] + = N_("E1192: Empty function name"); static char * const namespace_char = "abglstvw"; @@ -1603,18 +1609,9 @@ char *get_lval(char *const name, typval_T *const rettv, lval_T *const lp, const lp->ll_dict = NULL; lp->ll_list = lp->ll_tv->vval.v_list; - lp->ll_li = tv_list_find(lp->ll_list, (int)lp->ll_n1); - if (lp->ll_li == NULL) { - if (lp->ll_n1 < 0) { - lp->ll_n1 = 0; - lp->ll_li = tv_list_find(lp->ll_list, (int)lp->ll_n1); - } - } + lp->ll_li = tv_list_check_range_index_one(lp->ll_list, &lp->ll_n1, quiet); if (lp->ll_li == NULL) { tv_clear(&var2); - if (!quiet) { - semsg(_(e_list_index_out_of_range_nr), (int64_t)lp->ll_n1); - } return NULL; } @@ -1625,25 +1622,9 @@ char *get_lval(char *const name, typval_T *const rettv, lval_T *const lp, const if (lp->ll_range && !lp->ll_empty2) { lp->ll_n2 = (long)tv_get_number(&var2); // Is number or string. tv_clear(&var2); - if (lp->ll_n2 < 0) { - listitem_T *ni = tv_list_find(lp->ll_list, (int)lp->ll_n2); - if (ni == NULL) { - if (!quiet) { - semsg(_(e_list_index_out_of_range_nr), (int64_t)lp->ll_n2); - } - return NULL; - } - lp->ll_n2 = tv_list_idx_of_item(lp->ll_list, ni); - } - - // Check that lp->ll_n2 isn't before lp->ll_n1. - if (lp->ll_n1 < 0) { - lp->ll_n1 = tv_list_idx_of_item(lp->ll_list, lp->ll_li); - } - if (lp->ll_n2 < lp->ll_n1) { - if (!quiet) { - semsg(_(e_list_index_out_of_range_nr), (int64_t)lp->ll_n2); - } + if (tv_list_check_range_index_two(lp->ll_list, + &lp->ll_n1, lp->ll_li, + &lp->ll_n2, quiet) == FAIL) { return NULL; } } @@ -1672,7 +1653,6 @@ void set_var_lval(lval_T *lp, char *endp, typval_T *rettv, int copy, const bool const char *op) { int cc; - listitem_T *ri; dictitem_T *di; if (lp->ll_tv == NULL) { @@ -1733,60 +1713,13 @@ void set_var_lval(lval_T *lp, char *endp, typval_T *rettv, int copy, const bool lp->ll_name, TV_CSTRING)) { // Skip } else if (lp->ll_range) { - listitem_T *ll_li = lp->ll_li; - int ll_n1 = (int)lp->ll_n1; - if (is_const) { emsg(_("E996: Cannot lock a range")); return; } - // Check whether any of the list items is locked - for (ri = tv_list_first(rettv->vval.v_list); - ri != NULL && ll_li != NULL;) { - if (value_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); - if (ri == NULL || (!lp->ll_empty2 && lp->ll_n2 == ll_n1)) { - break; - } - ll_li = TV_LIST_ITEM_NEXT(lp->ll_list, ll_li); - ll_n1++; - } - - // Assign the List values to the list items. - for (ri = tv_list_first(rettv->vval.v_list); ri != NULL;) { - if (op != NULL && *op != '=') { - eexe_mod_op(TV_LIST_ITEM_TV(lp->ll_li), TV_LIST_ITEM_TV(ri), op); - } else { - tv_clear(TV_LIST_ITEM_TV(lp->ll_li)); - tv_copy(TV_LIST_ITEM_TV(ri), TV_LIST_ITEM_TV(lp->ll_li)); - } - ri = TV_LIST_ITEM_NEXT(rettv->vval.v_list, ri); - if (ri == NULL || (!lp->ll_empty2 && lp->ll_n2 == lp->ll_n1)) { - break; - } - assert(lp->ll_li != NULL); - if (TV_LIST_ITEM_NEXT(lp->ll_list, lp->ll_li) == NULL) { - // Need to add an empty item. - tv_list_append_number(lp->ll_list, 0); - // ll_li may have become invalid after append, don’t use it. - lp->ll_li = tv_list_last(lp->ll_list); // Valid again. - } else { - lp->ll_li = TV_LIST_ITEM_NEXT(lp->ll_list, lp->ll_li); - } - lp->ll_n1++; - } - if (ri != NULL) { - emsg(_("E710: List value has more items than target")); - } else if (lp->ll_empty2 - ? (lp->ll_li != NULL - && TV_LIST_ITEM_NEXT(lp->ll_list, lp->ll_li) != NULL) - : lp->ll_n1 != lp->ll_n2) { - emsg(_("E711: List value has not enough items")); - } + (void)tv_list_assign_range(lp->ll_list, rettv->vval.v_list, + lp->ll_n1, lp->ll_n2, lp->ll_empty2, op, lp->ll_name); } else { typval_T oldtv = TV_INITIAL_VALUE; dict_T *dict = lp->ll_dict; @@ -3572,7 +3505,7 @@ static int check_can_index(typval_T *rettv, bool evaluate, bool verbose) case VAR_FUNC: case VAR_PARTIAL: if (verbose) { - emsg(_("E695: Cannot index a Funcref")); + emsg(_(e_cannot_index_a_funcref)); } return FAIL; case VAR_FLOAT: @@ -3770,38 +3703,38 @@ int eval_option(const char **const arg, typval_T *const rettv, const bool evalua return OK; } - long numval; - char *stringval; int ret = OK; - + bool hidden; char c = *option_end; *option_end = NUL; - getoption_T opt_type = get_option_value(*arg, &numval, - rettv == NULL ? NULL : &stringval, NULL, scope); + OptVal value = get_option_value(*arg, NULL, scope, &hidden); - if (opt_type == gov_unknown) { - if (rettv != NULL) { + if (rettv != NULL) { + switch (value.type) { + case kOptValTypeNil: semsg(_("E113: Unknown option: %s"), *arg); - } - ret = FAIL; - } else if (rettv != NULL) { - if (opt_type == gov_hidden_string) { - rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; - } else if (opt_type == gov_hidden_bool || opt_type == gov_hidden_number) { + ret = FAIL; + break; + case kOptValTypeBoolean: rettv->v_type = VAR_NUMBER; - rettv->vval.v_number = 0; - } else if (opt_type == gov_bool || opt_type == gov_number) { + rettv->vval.v_number = value.data.boolean; + break; + case kOptValTypeNumber: rettv->v_type = VAR_NUMBER; - rettv->vval.v_number = numval; - } else { // string option + rettv->vval.v_number = value.data.number; + break; + case kOptValTypeString: rettv->v_type = VAR_STRING; - rettv->vval.v_string = stringval; + rettv->vval.v_string = value.data.string.data; + break; + } + } else { + // Value isn't being used, free it. + optval_free(value); + + if (value.type == kOptValTypeNil || (working && hidden)) { + ret = FAIL; } - } else if (working && (opt_type == gov_hidden_bool - || opt_type == gov_hidden_number - || opt_type == gov_hidden_string)) { - ret = FAIL; } *option_end = c; // put back for error messages @@ -5431,7 +5364,7 @@ theend: xfree(trans_name); } -/// Get the line number from VimL object +/// Get the line number from Vimscript object /// /// @note Unlike tv_get_lnum(), this one supports only "$" special string. /// @@ -5584,9 +5517,9 @@ void get_user_input(const typval_T *const argvars, typval_T *const rettv, const cmd_silent = cmd_silent_save; } -/// Builds a process argument vector from a VimL object (typval_T). +/// Builds a process argument vector from a Vimscript object (typval_T). /// -/// @param[in] cmd_tv VimL object +/// @param[in] cmd_tv Vimscript object /// @param[out] cmd Returns the command or executable name. /// @param[out] executable Returns `false` if argv[0] is not executable. /// @@ -6259,7 +6192,7 @@ int read_blob(FILE *const fd, typval_T *rettv, off_T offset, off_T size_arg) /// @param[out] len Length of the resulting string or -1 on error. /// @param[in] endnl If true, the output will end in a newline (if a list). /// @param[in] crlf If true, list items will be joined with CRLF (if a list). -/// @returns an allocated string if `tv` represents a VimL string, list, or +/// @returns an allocated string if `tv` represents a Vimscript string, list, or /// number; NULL otherwise. char *save_tv_as_string(typval_T *tv, ptrdiff_t *const len, bool endnl, bool crlf) FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL @@ -6405,7 +6338,7 @@ int buf_charidx_to_byteidx(buf_T *buf, linenr_T lnum, int charidx) return (int)(t - str); } -/// Translate a VimL object into a position +/// Translate a Vimscript object into a position /// /// Accepts VAR_LIST and VAR_STRING objects. Does not give an error for invalid /// type. @@ -8516,7 +8449,7 @@ char *do_string_sub(char *str, char *pat, char *sub, typval_T *expr, const char // If it's still empty it was changed and restored, need to restore in // the complicated way. if (*p_cpo == NUL) { - set_option_value_give_err("cpo", 0L, save_cpo, 0); + set_option_value_give_err("cpo", CSTR_AS_OPTVAL(save_cpo), 0); } free_string_option(save_cpo); } @@ -8608,6 +8541,7 @@ typval_T eval_call_provider(char *provider, char *method, list_T *arguments, boo .es_entry = ((estack_T *)exestack.ga_data)[exestack.ga_len - 1], .autocmd_fname = autocmd_fname, .autocmd_match = autocmd_match, + .autocmd_fname_full = autocmd_fname_full, .autocmd_bufnr = autocmd_bufnr, .funccalp = (void *)get_current_funccal() }; diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index d9c7208c02..fd4108813e 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -8,7 +8,7 @@ -- base For methods: the argument to use as the base argument (1-indexed): -- base->method() -- Defaults to BASE_NONE (function cannot be used as a method). --- func Name of the C function which implements the VimL function. Defaults to +-- func Name of the C function which implements the Vimscript function. Defaults to -- `f_{funcname}`. -- fast Function can run in |api-fast| events. Defaults to false. diff --git a/src/nvim/eval/decode.c b/src/nvim/eval/decode.c index acef37e0e8..70a629ea91 100644 --- a/src/nvim/eval/decode.c +++ b/src/nvim/eval/decode.c @@ -227,7 +227,7 @@ static inline int json_decoder_pop(ValuesStackItem obj, ValuesStack *const stack /// /// @param[out] ret_tv Address where new special dictionary is saved. /// @param[in] len Expected number of items to be populated before list -/// becomes accessible from VimL. It is still valid to +/// becomes accessible from Vimscript. It is still valid to /// underpopulate a list, value only controls how many elements /// will be allocated in advance. @see ListLenSpecials. /// @@ -645,7 +645,7 @@ parse_json_number_ret: } \ } while (0) -/// Convert JSON string into VimL object +/// Convert JSON string into Vimscript object /// /// @param[in] buf String to convert. UTF-8 encoding is assumed. /// @param[in] buf_len Length of the string. @@ -921,7 +921,7 @@ json_decode_string_ret: #undef DICT_LEN -/// Convert msgpack object to a VimL one +/// Convert msgpack object to a Vimscript one int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { diff --git a/src/nvim/eval/encode.c b/src/nvim/eval/encode.c index be0cf79e85..fc9904a2d9 100644 --- a/src/nvim/eval/encode.c +++ b/src/nvim/eval/encode.c @@ -3,7 +3,7 @@ /// @file encode.c /// -/// File containing functions for encoding and decoding VimL values. +/// File containing functions for encoding and decoding Vimscript values. /// /// Split out from eval.c. diff --git a/src/nvim/eval/encode.h b/src/nvim/eval/encode.h index 41e7614fc0..b589b8b13f 100644 --- a/src/nvim/eval/encode.h +++ b/src/nvim/eval/encode.h @@ -12,7 +12,7 @@ #include "nvim/garray.h" #include "nvim/vim.h" -/// Convert VimL value to msgpack string +/// Convert Vimscript value to msgpack string /// /// @param[out] packer Packer to save results in. /// @param[in] tv Dumped value. @@ -21,7 +21,7 @@ /// @return OK in case of success, FAIL otherwise. int encode_vim_to_msgpack(msgpack_packer *packer, typval_T *tv, const char *objname); -/// Convert VimL value to :echo output +/// Convert Vimscript value to :echo output /// /// @param[out] packer Packer to save results in. /// @param[in] tv Dumped value. diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 04fd81c713..f53c5fc0c5 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -873,8 +873,7 @@ static void f_confirm(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) } if (!error) { - rettv->vval.v_number = do_dialog(type, NULL, (char *)message, (char *)buttons, def, NULL, - false); + rettv->vval.v_number = do_dialog(type, NULL, message, buttons, def, NULL, false); } } @@ -1717,7 +1716,7 @@ static void f_expand(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) emsg_off++; } size_t len; - char *errormsg = NULL; + const char *errormsg = NULL; char *result = eval_vars((char *)s, s, &len, NULL, &errormsg, NULL, false); if (p_verbose == 0) { emsg_off--; @@ -1782,7 +1781,7 @@ static void f_menu_get(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) /// Expand all the special characters in a command string. static void f_expandcmd(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { - char *errormsg = NULL; + const char *errormsg = NULL; bool emsgoff = true; if (argvars[1].v_type == VAR_DICT @@ -6552,6 +6551,7 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) sctx_T save_current_sctx; char *save_autocmd_fname, *save_autocmd_match; + bool save_autocmd_fname_full; int save_autocmd_bufnr; funccal_entry_T funccal_entry; @@ -6561,6 +6561,7 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) save_current_sctx = current_sctx; save_autocmd_fname = autocmd_fname; save_autocmd_match = autocmd_match; + save_autocmd_fname_full = autocmd_fname_full; save_autocmd_bufnr = autocmd_bufnr; save_funccal(&funccal_entry); @@ -6569,6 +6570,7 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) ((estack_T *)exestack.ga_data)[exestack.ga_len++] = provider_caller_scope.es_entry; autocmd_fname = provider_caller_scope.autocmd_fname; autocmd_match = provider_caller_scope.autocmd_match; + autocmd_fname_full = provider_caller_scope.autocmd_fname_full; autocmd_bufnr = provider_caller_scope.autocmd_bufnr; set_current_funccal((funccall_T *)(provider_caller_scope.funccalp)); } @@ -6586,6 +6588,7 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) exestack.ga_len--; autocmd_fname = save_autocmd_fname; autocmd_match = save_autocmd_match; + autocmd_fname_full = save_autocmd_fname_full; autocmd_bufnr = save_autocmd_bufnr; restore_funccal(); } @@ -7101,7 +7104,7 @@ long do_searchpair(const char *spat, const char *mpat, const char *epat, int dir // If it's still empty it was changed and restored, need to restore in // the complicated way. if (*p_cpo == NUL) { - set_option_value_give_err("cpo", 0L, save_cpo, 0); + set_option_value_give_err("cpo", CSTR_AS_OPTVAL(save_cpo), 0); } free_string_option(save_cpo); } @@ -8300,7 +8303,7 @@ static void f_synstack(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) } } -/// f_system - the VimL system() function +/// f_system - the Vimscript system() function static void f_system(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { get_system_output_as_rettv(argvars, rettv, false); diff --git a/src/nvim/eval/funcs.h b/src/nvim/eval/funcs.h index 65a95196de..5dab12787b 100644 --- a/src/nvim/eval/funcs.h +++ b/src/nvim/eval/funcs.h @@ -9,14 +9,14 @@ #include "nvim/eval/typval_defs.h" #include "nvim/types.h" -/// Prototype of C function that implements VimL function +/// Prototype of C function that implements Vimscript function typedef void (*VimLFunc)(typval_T *args, typval_T *rvar, EvalFuncData data); /// Special flags for base_arg @see EvalFuncDef #define BASE_NONE 0 ///< Not a method (no base argument). #define BASE_LAST UINT8_MAX ///< Use the last argument as the method base. -/// Structure holding VimL function definition +/// Structure holding Vimscript function definition typedef struct { char *name; ///< Name of the function. uint8_t min_argc; ///< Minimal number of arguments. diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 42e9dc8f03..abe31aab75 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -170,7 +170,7 @@ void tv_list_watch_fix(list_T *const l, const listitem_T *const item) /// Caller should take care of the reference count. /// /// @param[in] len Expected number of items to be populated before list -/// becomes accessible from VimL. It is still valid to +/// becomes accessible from Vimscript. It is still valid to /// underpopulate a list, value only controls how many elements /// will be allocated in advance. Currently does nothing. /// @see ListLenSpecials. @@ -398,7 +398,7 @@ void tv_list_insert(list_T *const l, listitem_T *const ni, listitem_T *const ite } } -/// Insert VimL value into a list +/// Insert Vimscript value into a list /// /// @param[out] l List to insert to. /// @param[in,out] tv Value to insert. Is copied (@see tv_copy()) to an @@ -434,7 +434,7 @@ void tv_list_append(list_T *const l, listitem_T *const item) item->li_next = NULL; } -/// Append VimL value to the end of list +/// Append Vimscript value to the end of list /// /// @param[out] l List to append to. /// @param[in,out] tv Value to append. Is copied (@see tv_copy()) to an @@ -594,6 +594,119 @@ tv_list_copy_error: return NULL; } +/// Get the list item in "l" with index "n1". "n1" is adjusted if needed. +/// Return NULL if there is no such item. +listitem_T *tv_list_check_range_index_one(list_T *const l, long *const n1, const bool quiet) +{ + listitem_T *li = tv_list_find_index(l, n1); + if (li == NULL) { + if (!quiet) { + semsg(_(e_list_index_out_of_range_nr), (int64_t)n1); + } + return NULL; + } + return li; +} + +/// Check that "n2" can be used as the second index in a range of list "l". +/// If "n1" or "n2" is negative it is changed to the positive index. +/// "li1" is the item for item "n1". +/// Return OK or FAIL. +int tv_list_check_range_index_two(list_T *const l, long *const n1, const listitem_T *const li1, + long *const n2, const bool quiet) +{ + if (*n2 < 0) { + listitem_T *ni = tv_list_find(l, (int)(*n2)); + if (ni == NULL) { + if (!quiet) { + semsg(_(e_list_index_out_of_range_nr), (int64_t)(*n2)); + } + return FAIL; + } + *n2 = tv_list_idx_of_item(l, ni); + } + + // Check that n2 isn't before n1. + if (*n1 < 0) { + *n1 = tv_list_idx_of_item(l, li1); + } + if (*n2 < *n1) { + if (!quiet) { + semsg(_(e_list_index_out_of_range_nr), (int64_t)(*n2)); + } + return FAIL; + } + return OK; +} + +/// Assign values from list "src" into a range of "dest". +/// "idx1_arg" is the index of the first item in "dest" to be replaced. +/// "idx2" is the index of last item to be replaced, but when "empty_idx2" is +/// true then replace all items after "idx1". +/// "op" is the operator, normally "=" but can be "+=" and the like. +/// "varname" is used for error messages. +/// Returns OK or FAIL. +int tv_list_assign_range(list_T *const dest, list_T *const src, const long idx1_arg, + const long idx2, const bool empty_idx2, const char *const op, + const char *const varname) +{ + long idx1 = idx1_arg; + listitem_T *const first_li = tv_list_find_index(dest, &idx1); + listitem_T *src_li; + + // Check whether any of the list items is locked before making any changes. + long idx = idx1; + listitem_T *dest_li = first_li; + for (src_li = tv_list_first(src); src_li != NULL && dest_li != NULL;) { + if (value_check_lock(TV_LIST_ITEM_TV(dest_li)->v_lock, varname, TV_CSTRING)) { + return FAIL; + } + src_li = TV_LIST_ITEM_NEXT(src, src_li); + if (src_li == NULL || (!empty_idx2 && idx2 == idx)) { + break; + } + dest_li = TV_LIST_ITEM_NEXT(dest, dest_li); + idx++; + } + + // Assign the List values to the list items. + idx = idx1; + dest_li = first_li; + for (src_li = tv_list_first(src); src_li != NULL;) { + assert(dest_li != NULL); + if (op != NULL && *op != '=') { + eexe_mod_op(TV_LIST_ITEM_TV(dest_li), TV_LIST_ITEM_TV(src_li), op); + } else { + tv_clear(TV_LIST_ITEM_TV(dest_li)); + tv_copy(TV_LIST_ITEM_TV(src_li), TV_LIST_ITEM_TV(dest_li)); + } + src_li = TV_LIST_ITEM_NEXT(src, src_li); + if (src_li == NULL || (!empty_idx2 && idx2 == idx)) { + break; + } + if (TV_LIST_ITEM_NEXT(dest, dest_li) == NULL) { + // Need to add an empty item. + tv_list_append_number(dest, 0); + // "dest_li" may have become invalid after append, don’t use it. + dest_li = tv_list_last(dest); // Valid again. + } else { + dest_li = TV_LIST_ITEM_NEXT(dest, dest_li); + } + idx++; + } + if (src_li != NULL) { + emsg(_("E710: List value has more items than target")); + return FAIL; + } + if (empty_idx2 + ? (dest_li != NULL && TV_LIST_ITEM_NEXT(dest, dest_li) != NULL) + : idx != idx2) { + emsg(_("E711: List value has not enough items")); + return FAIL; + } + return OK; +} + /// Flatten up to "maxitems" in "list", starting at "first" to depth "maxdepth". /// When "first" is NULL use the first item. /// Does nothing if "maxdepth" is 0. @@ -1507,6 +1620,21 @@ const char *tv_list_find_str(list_T *const l, const int n) return tv_get_string(TV_LIST_ITEM_TV(li)); } +/// Like tv_list_find() but when a negative index is used that is not found use +/// zero and set "idx" to zero. Used for first index of a range. +static listitem_T *tv_list_find_index(list_T *const l, long *const idx) + FUNC_ATTR_WARN_UNUSED_RESULT +{ + listitem_T *li = tv_list_find(l, (int)(*idx)); + if (li == NULL) { + if (*idx < 0) { + *idx = 0; + li = tv_list_find(l, (int)(*idx)); + } + } + return li; +} + /// Locate item in a list and return its index /// /// @param[in] l List to search. @@ -2958,7 +3086,7 @@ void f_list2blob(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) /// /// @param[out] ret_tv Structure where list is saved. /// @param[in] len Expected number of items to be populated before list -/// becomes accessible from VimL. It is still valid to +/// becomes accessible from Vimscript. It is still valid to /// underpopulate a list, value only controls how many elements /// will be allocated in advance. @see ListLenSpecials. /// @@ -3004,12 +3132,13 @@ static void tv_dict_list(typval_T *const tv, typval_T *const rettv, const DictLi emsg(_(e_dictreq)); return; } + + tv_list_alloc_ret(rettv, tv_dict_len(tv->vval.v_dict)); if (tv->vval.v_dict == NULL) { + // NULL dict behaves like an empty dict return; } - tv_list_alloc_ret(rettv, tv_dict_len(tv->vval.v_dict)); - TV_DICT_ITER(tv->vval.v_dict, di, { typval_T tv_item = { .v_lock = VAR_UNLOCKED }; @@ -3409,7 +3538,7 @@ void tv_clear(typval_T *const tv) //{{{3 Free -/// Free allocated VimL object and value stored inside +/// Free allocated Vimscript object and value stored inside /// /// @param tv Object to free. void tv_free(typval_T *tv) @@ -3587,7 +3716,7 @@ void tv_item_lock(typval_T *const tv, const int deep, const bool lock, const boo recurse--; } -/// Check whether VimL value is locked itself or refers to a locked container +/// Check whether Vimscript value is locked itself or refers to a locked container /// /// @warning Fixed container is not the same as locked. /// @@ -3686,7 +3815,7 @@ bool value_check_lock(VarLockStatus lock, const char *name, size_t name_len) static int tv_equal_recurse_limit; -/// Compare two VimL values +/// Compare two Vimscript values /// /// Like "==", but strings and numbers are different, as well as floats and /// numbers. @@ -3876,14 +4005,13 @@ static const char *const str_errors[] = { [VAR_FUNC]= N_(FUNC_ERROR), [VAR_LIST]= N_("E730: Using a List as a String"), [VAR_DICT]= N_("E731: Using a Dictionary as a String"), - [VAR_FLOAT]= e_using_float_as_string, [VAR_BLOB]= N_("E976: Using a Blob as a String"), [VAR_UNKNOWN]= e_using_invalid_value_as_string, }; #undef FUNC_ERROR -/// Check that given value is a VimL String or can be "cast" to it. +/// Check that given value is a Vimscript String or can be "cast" to it. /// /// Error messages are compatible with tv_get_string_chk() previously used for /// the same purpose. @@ -3899,12 +4027,12 @@ bool tv_check_str(const typval_T *const tv) case VAR_BOOL: case VAR_SPECIAL: case VAR_STRING: + case VAR_FLOAT: return true; case VAR_PARTIAL: case VAR_FUNC: case VAR_LIST: case VAR_DICT: - case VAR_FLOAT: case VAR_BLOB: case VAR_UNKNOWN: emsg(_(str_errors[tv->v_type])); @@ -3916,7 +4044,7 @@ bool tv_check_str(const typval_T *const tv) //{{{2 Get -/// Get the number value of a VimL object +/// Get the number value of a Vimscript object /// /// @note Use tv_get_number_chk() if you need to determine whether there was an /// error. @@ -3932,7 +4060,7 @@ varnumber_T tv_get_number(const typval_T *const tv) return tv_get_number_chk(tv, &error); } -/// Get the number value of a VimL object +/// Get the number value of a Vimscript object /// /// @param[in] tv Object to get value from. /// @param[out] ret_error If type error occurred then `true` will be written @@ -3991,7 +4119,7 @@ varnumber_T tv_get_bool_chk(const typval_T *const tv, bool *const ret_error) return tv_get_number_chk(tv, ret_error); } -/// Get the line number from VimL object +/// Get the line number from Vimscript object /// /// @param[in] tv Object to get value from. Is expected to be a number or /// a special string like ".", "$", … (works with current buffer @@ -4014,7 +4142,7 @@ linenr_T tv_get_lnum(const typval_T *const tv) return lnum; } -/// Get the floating-point value of a VimL object +/// Get the floating-point value of a Vimscript object /// /// Raises an error if object is not number or floating-point. /// @@ -4257,7 +4385,7 @@ int tv_check_for_list_or_blob_arg(const typval_T *const args, const int idx) return OK; } -/// Get the string value of a "stringish" VimL object. +/// Get the string value of a "stringish" Vimscript object. /// /// @param[in] tv Object to get value of. /// @param buf Buffer used to hold numbers and special variables converted to @@ -4275,6 +4403,9 @@ const char *tv_get_string_buf_chk(const typval_T *const tv, char *const buf) case VAR_NUMBER: snprintf(buf, NUMBUFLEN, "%" PRIdVARNUMBER, tv->vval.v_number); // -V576 return buf; + case VAR_FLOAT: + vim_snprintf(buf, NUMBUFLEN, "%g", tv->vval.v_float); + return buf; case VAR_STRING: if (tv->vval.v_string != NULL) { return tv->vval.v_string; @@ -4290,7 +4421,6 @@ const char *tv_get_string_buf_chk(const typval_T *const tv, char *const buf) case VAR_FUNC: case VAR_LIST: case VAR_DICT: - case VAR_FLOAT: case VAR_BLOB: case VAR_UNKNOWN: emsg(_(str_errors[tv->v_type])); @@ -4300,7 +4430,7 @@ const char *tv_get_string_buf_chk(const typval_T *const tv, char *const buf) return NULL; } -/// Get the string value of a "stringish" VimL object. +/// Get the string value of a "stringish" Vimscript object. /// /// @warning For number and special values it uses a single, static buffer. It /// may be used only once, next call to tv_get_string may reuse it. Use @@ -4319,7 +4449,7 @@ const char *tv_get_string_chk(const typval_T *const tv) return tv_get_string_buf_chk(tv, mybuf); } -/// Get the string value of a "stringish" VimL object. +/// Get the string value of a "stringish" Vimscript object. /// /// @warning For number and special values it uses a single, static buffer. It /// may be used only once, next call to tv_get_string may reuse it. Use @@ -4341,7 +4471,7 @@ const char *tv_get_string(const typval_T *const tv) return tv_get_string_buf((typval_T *)tv, mybuf); } -/// Get the string value of a "stringish" VimL object. +/// Get the string value of a "stringish" Vimscript object. /// /// @note tv_get_string_chk() and tv_get_string_buf_chk() are similar, but /// return NULL on error. diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h index e7b2499346..0b42a473cf 100644 --- a/src/nvim/eval/typval.h +++ b/src/nvim/eval/typval.h @@ -312,7 +312,7 @@ static inline void tv_blob_set(blob_T *const blob, int idx, uint8_t c) ((uint8_t *)blob->bv_ga.ga_data)[idx] = c; } -/// Initialize VimL object +/// Initialize Vimscript object /// /// Initializes to unlocked VAR_UNKNOWN object. /// @@ -424,7 +424,7 @@ static inline bool tv_get_float_chk(const typval_T *tv, float_T *ret_f) /// /// Raises an error if object is not number or floating-point. /// -/// @param[in] tv VimL object to get value from. +/// @param[in] tv Vimscript object to get value from. /// @param[out] ret_f Location where resulting float is stored. /// /// @return true in case of success, false if tv is not a number or float. diff --git a/src/nvim/eval/typval_defs.h b/src/nvim/eval/typval_defs.h index 767603ac0e..4099877539 100644 --- a/src/nvim/eval/typval_defs.h +++ b/src/nvim/eval/typval_defs.h @@ -10,7 +10,7 @@ #include "nvim/pos.h" #include "nvim/types.h" -/// Type used for VimL VAR_NUMBER values +/// Type used for Vimscript VAR_NUMBER values typedef int64_t varnumber_T; typedef uint64_t uvarnumber_T; @@ -100,7 +100,7 @@ typedef enum { VAR_FIXED = 2, ///< Locked forever. } VarLockStatus; -/// VimL variable types, for use in typval_T.v_type +/// Vimscript variable types, for use in typval_T.v_type typedef enum { VAR_UNKNOWN = 0, ///< Unknown (unspecified) value. VAR_NUMBER, ///< Number, .v_number is used. diff --git a/src/nvim/eval/typval_encode.h b/src/nvim/eval/typval_encode.h index 171b0417d0..cf01926030 100644 --- a/src/nvim/eval/typval_encode.h +++ b/src/nvim/eval/typval_encode.h @@ -30,7 +30,7 @@ typedef enum { kMPConvPartialEnd, ///< Already converted everything. } MPConvPartialStage; -/// Structure representing current VimL to messagepack conversion state +/// Structure representing current Vimscript to messagepack conversion state typedef struct { MPConvStackValType type; ///< Type of the stack entry. typval_T *tv; ///< Currently converted typval_T. @@ -60,7 +60,7 @@ typedef struct { } data; ///< Data to convert. } MPConvStackVal; -/// Stack used to convert VimL values to messagepack. +/// Stack used to convert Vimscript values to messagepack. typedef kvec_withinit_t(MPConvStackVal, 8) MPConvStack; // Defines for MPConvStack diff --git a/src/nvim/eval/vars.c b/src/nvim/eval/vars.c index 21b25b64f4..9b6427fef7 100644 --- a/src/nvim/eval/vars.c +++ b/src/nvim/eval/vars.c @@ -766,84 +766,80 @@ static char *ex_let_option(char *arg, typval_T *const tv, const bool is_const, || (endchars != NULL && vim_strchr(endchars, (uint8_t)(*skipwhite(p))) == NULL)) { emsg(_(e_letunexp)); - } else { - varnumber_T n = 0; - getoption_T opt_type; - long numval; - char *stringval = NULL; - const char *s = NULL; - bool failed = false; - uint32_t opt_p_flags; - char *tofree = NULL; - - const char c1 = *p; - *p = NUL; + return NULL; + } - opt_type = get_option_value(arg, &numval, &stringval, &opt_p_flags, scope); - if (opt_type == gov_bool - || opt_type == gov_number - || opt_type == gov_hidden_bool - || opt_type == gov_hidden_number) { - // number, possibly hidden - n = (long)tv_get_number(tv); - } - - if ((opt_p_flags & P_FUNC) && tv_is_func(*tv)) { - // If the option can be set to a function reference or a lambda - // and the passed value is a function reference, then convert it to - // the name (string) of the function reference. - s = tofree = encode_tv2string(tv, NULL); - } else if (tv->v_type != VAR_BOOL && tv->v_type != VAR_SPECIAL) { - // Avoid setting a string option to the text "v:false" or similar. - s = tv_get_string_chk(tv); - } - - if (op != NULL && *op != '=') { - if (((opt_type == gov_bool || opt_type == gov_number) && *op == '.') - || (opt_type == gov_string && *op != '.')) { - semsg(_(e_letwrong), op); - failed = true; // don't set the value - } else { - // number or bool - if (opt_type == gov_number || opt_type == gov_bool) { - switch (*op) { - case '+': - n = numval + n; break; - case '-': - n = numval - n; break; - case '*': - n = numval * n; break; - case '/': - n = num_divide(numval, n); break; - case '%': - n = num_modulus(numval, n); break; - } - s = NULL; - } else if (opt_type == gov_string && stringval != NULL && s != NULL) { - // string - char *const oldstringval = stringval; - stringval = concat_str(stringval, s); - xfree(oldstringval); - s = stringval; - } + const char c1 = *p; + *p = NUL; + + uint32_t opt_p_flags; + bool hidden; + OptVal curval = get_option_value(arg, &opt_p_flags, scope, &hidden); + OptVal newval = NIL_OPTVAL; + if (curval.type == kOptValTypeNil && arg[0] != 't' && arg[1] != '_') { + semsg(_(e_unknown_option2), arg); + goto theend; + } + if (op != NULL && *op != '=' + && ((curval.type != kOptValTypeString && *op == '.') + || (curval.type == kOptValTypeString && *op != '.'))) { + semsg(_(e_letwrong), op); + goto theend; + } + + bool error; + newval = tv_to_optval(tv, arg, opt_p_flags, &error); + if (error) { + goto theend; + } + + // Don't assume current and new values are of the same type in order to future-proof the code for + // when an option can have multiple types. + const bool is_num = ((curval.type == kOptValTypeNumber || curval.type == kOptValTypeBoolean) + && (newval.type == kOptValTypeNumber || newval.type == kOptValTypeBoolean)); + const bool is_string = curval.type == kOptValTypeString && newval.type == kOptValTypeString; + + if (op != NULL && *op != '=') { + if (!hidden && is_num) { // number or bool + Integer cur_n = curval.type == kOptValTypeNumber ? curval.data.number : curval.data.boolean; + Integer new_n = newval.type == kOptValTypeNumber ? newval.data.number : newval.data.boolean; + + switch (*op) { + case '+': + new_n = cur_n + new_n; break; + case '-': + new_n = cur_n - new_n; break; + case '*': + new_n = cur_n * new_n; break; + case '/': + new_n = num_divide(cur_n, new_n); break; + case '%': + new_n = num_modulus(cur_n, new_n); break; } - } - if (!failed) { - if (opt_type != gov_string || s != NULL) { - const char *err = set_option_value(arg, (long)n, s, scope); - arg_end = p; - if (err != NULL) { - emsg(_(err)); - } + if (curval.type == kOptValTypeNumber) { + newval = NUMBER_OPTVAL(new_n); } else { - emsg(_(e_stringreq)); + newval = BOOLEAN_OPTVAL(new_n == 0 ? kFalse : (new_n >= 1 ? kTrue : kNone)); } + } else if (!hidden && is_string + && curval.data.string.data != NULL && newval.data.string.data != NULL) { // string + OptVal newval_old = newval; + newval = CSTR_AS_OPTVAL(concat_str(curval.data.string.data, newval.data.string.data)); + optval_free(newval_old); } - *p = c1; - xfree(stringval); - xfree(tofree); } + + const char *err = set_option_value(arg, newval, scope); + arg_end = p; + if (err != NULL) { + emsg(_(err)); + } + +theend: + *p = c1; + optval_free(curval); + optval_free(newval); return arg_end; } @@ -1056,57 +1052,57 @@ static int do_unlet_var(lval_T *lp, char *name_end, exarg_T *eap, int deep FUNC_ lp->ll_name_len))) { return FAIL; } else if (lp->ll_range) { - assert(lp->ll_list != NULL); - // Delete a range of List items. - listitem_T *const first_li = lp->ll_li; - listitem_T *last_li = first_li; - while (true) { - listitem_T *const li = TV_LIST_ITEM_NEXT(lp->ll_list, lp->ll_li); - if (value_check_lock(TV_LIST_ITEM_TV(lp->ll_li)->v_lock, - lp->ll_name, - lp->ll_name_len)) { - return false; - } - lp->ll_li = li; - lp->ll_n1++; - if (lp->ll_li == NULL || (!lp->ll_empty2 && lp->ll_n2 < lp->ll_n1)) { - break; - } - last_li = lp->ll_li; - } - tv_list_remove_items(lp->ll_list, first_li, last_li); + tv_list_unlet_range(lp->ll_list, lp->ll_li, lp->ll_n1, !lp->ll_empty2, lp->ll_n2); + } else if (lp->ll_list != NULL) { + // unlet a List item. + tv_list_item_remove(lp->ll_list, lp->ll_li); } else { - if (lp->ll_list != NULL) { - // unlet a List item. - tv_list_item_remove(lp->ll_list, lp->ll_li); - } else { - // unlet a Dictionary item. - dict_T *d = lp->ll_dict; - assert(d != NULL); - dictitem_T *di = lp->ll_di; - bool watched = tv_dict_is_watched(d); - char *key = NULL; - typval_T oldtv; + // unlet a Dictionary item. + dict_T *d = lp->ll_dict; + assert(d != NULL); + dictitem_T *di = lp->ll_di; + bool watched = tv_dict_is_watched(d); + char *key = NULL; + typval_T oldtv; - if (watched) { - tv_copy(&di->di_tv, &oldtv); - // need to save key because dictitem_remove will free it - key = xstrdup(di->di_key); - } + if (watched) { + tv_copy(&di->di_tv, &oldtv); + // need to save key because dictitem_remove will free it + key = xstrdup(di->di_key); + } - tv_dict_item_remove(d, di); + tv_dict_item_remove(d, di); - if (watched) { - tv_dict_watcher_notify(d, key, NULL, &oldtv); - tv_clear(&oldtv); - xfree(key); - } + if (watched) { + tv_dict_watcher_notify(d, key, NULL, &oldtv); + tv_clear(&oldtv); + xfree(key); } } return ret; } +/// Unlet one item or a range of items from a list. +/// Return OK or FAIL. +static void tv_list_unlet_range(list_T *const l, listitem_T *const li_first, const long n1_arg, + const bool has_n2, const long n2) +{ + assert(l != NULL); + // Delete a range of List items. + listitem_T *li_last = li_first; + long n1 = n1_arg; + while (true) { + listitem_T *const li = TV_LIST_ITEM_NEXT(l, li_last); + n1++; + if (li == NULL || (has_n2 && n2 < n1)) { + break; + } + li_last = li; + } + tv_list_remove_items(l, li_first, li_last); +} + /// unlet a variable /// /// @param[in] name Variable name to unlet. @@ -1809,28 +1805,83 @@ static void getwinvar(typval_T *argvars, typval_T *rettv, int off) get_var_from(varname, rettv, &argvars[off + 2], 'w', tp, win, NULL); } -/// Set option "varname" to the value of "varp" for the current buffer/window. -static void set_option_from_tv(const char *varname, typval_T *varp) +/// Convert typval to option value for a particular option. +/// +/// @param[in] tv typval to convert. +/// @param[in] option Option name. +/// @param[in] flags Option flags. +/// @param[out] error Whether an error occured. +/// +/// @return Typval converted to OptVal. Must be freed by caller. +/// Returns NIL_OPTVAL for invalid option name. +static OptVal tv_to_optval(typval_T *tv, const char *option, uint32_t flags, bool *error) { - long numval = 0; - const char *strval; - bool error = false; + OptVal value = NIL_OPTVAL; char nbuf[NUMBUFLEN]; - - if (varp->v_type == VAR_BOOL) { - if (is_string_option(varname)) { + bool err = false; + + if ((flags & P_FUNC) && tv_is_func(*tv)) { + // If the option can be set to a function reference or a lambda + // and the passed value is a function reference, then convert it to + // the name (string) of the function reference. + char *strval = encode_tv2string(tv, NULL); + err = strval == NULL; + value = CSTR_AS_OPTVAL(strval); + } else if (flags & (P_NUM | P_BOOL)) { + varnumber_T n = (flags & P_NUM) ? tv_get_number_chk(tv, &err) + : tv_get_bool_chk(tv, &err); + // This could be either "0" or a string that's not a number. + // So we need to check if it's actually a number. + if (!err && tv->v_type == VAR_STRING && n == 0) { + unsigned idx; + for (idx = 0; tv->vval.v_string[idx] == '0'; idx++) {} + if (tv->vval.v_string[idx] != NUL || idx == 0) { + // There's another character after zeros or the string is empty. + // In both cases, we are trying to set a num option using a string. + err = true; + semsg(_("E521: Number required: &%s = '%s'"), option, tv->vval.v_string); + } + } + value = (flags & P_NUM) ? NUMBER_OPTVAL(n) + : BOOLEAN_OPTVAL(n == 0 ? kFalse : (n >= 1 ? kTrue : kNone)); + } else if ((flags & P_STRING) || is_tty_option(option)) { + // Avoid setting string option to a boolean or a special value. + if (tv->v_type != VAR_BOOL && tv->v_type != VAR_SPECIAL) { + const char *strval = tv_get_string_buf_chk(tv, nbuf); + err = strval == NULL; + value = CSTR_TO_OPTVAL(strval); + } else if (flags & P_STRING) { + err = true; emsg(_(e_stringreq)); - return; } - numval = (long)varp->vval.v_number; - strval = "0"; // avoid using "false" } else { - numval = (long)tv_get_number_chk(varp, &error); - strval = tv_get_string_buf_chk(varp, nbuf); + abort(); // This should never happen. + } + + if (error != NULL) { + *error = err; + } + return value; +} + +/// Set option "varname" to the value of "varp" for the current buffer/window. +static void set_option_from_tv(const char *varname, typval_T *varp) +{ + int opt_idx = findoption(varname); + if (opt_idx < 0) { + semsg(_(e_unknown_option2), varname); + return; } - if (!error && strval != NULL) { - set_option_value_give_err(varname, numval, strval, OPT_LOCAL); + uint32_t opt_p_flags = get_option(opt_idx)->flags; + + bool error = false; + OptVal value = tv_to_optval(varp, varname, opt_p_flags, &error); + + if (!error) { + set_option_value_give_err(varname, value, OPT_LOCAL); } + + optval_free(value); } /// "setwinvar()" and "settabwinvar()" functions diff --git a/src/nvim/event/multiqueue.c b/src/nvim/event/multiqueue.c index 0096d0e11e..262d141b26 100644 --- a/src/nvim/event/multiqueue.c +++ b/src/nvim/event/multiqueue.c @@ -40,7 +40,7 @@ // // The main reason for this queue hierarchy is to allow focusing on a single // event emitter while blocking the main loop. For example, if the `jobwait` -// VimL function is called on job1, the main loop will temporarily stop polling +// Vimscript function is called on job1, the main loop will temporarily stop polling // the event loop queue and poll job1 queue instead. Same with channels, when // calling `rpcrequest` we want to temporarily stop processing events from // other sources and focus on a specific channel. diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 40afb3250c..591f8febdc 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -389,7 +389,7 @@ typedef struct { static int string_compare(const void *s1, const void *s2) FUNC_ATTR_NONNULL_ALL { if (sort_lc) { - return strcoll((char *)s1, (char *)s2); + return strcoll((const char *)s1, (const char *)s2); } return sort_ic ? STRICMP(s1, s2) : strcmp(s1, s2); } diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 39a54fa236..a70b3b1893 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -1342,7 +1342,7 @@ void set_cmd_count(exarg_T *eap, linenr_T count, bool validate) } } -static int parse_count(exarg_T *eap, char **errormsg, bool validate) +static int parse_count(exarg_T *eap, const char **errormsg, bool validate) { // Check for a count. When accepting a EX_BUFNAME, don't use "123foo" as a // count, it's a buffer name. @@ -1396,7 +1396,7 @@ bool is_cmd_ni(cmdidx_T cmdidx) /// @param[out] errormsg Error message, if any /// /// @return Success or failure -bool parse_cmdline(char *cmdline, exarg_T *eap, CmdParseInfo *cmdinfo, char **errormsg) +bool parse_cmdline(char *cmdline, exarg_T *eap, CmdParseInfo *cmdinfo, const char **errormsg) { char *after_modifier = NULL; bool retval = false; @@ -1564,7 +1564,7 @@ static void shift_cmd_args(exarg_T *eap) xfree(oldarglens); } -static int execute_cmd0(int *retv, exarg_T *eap, char **errormsg, bool preview) +static int execute_cmd0(int *retv, exarg_T *eap, const char **errormsg, bool preview) { // If filename expansion is enabled, expand filenames if (eap->argt & EX_XFILE) { @@ -1649,7 +1649,7 @@ static int execute_cmd0(int *retv, exarg_T *eap, char **errormsg, bool preview) /// @param preview Execute command preview callback instead of actual command int execute_cmd(exarg_T *eap, CmdParseInfo *cmdinfo, bool preview) { - char *errormsg = NULL; + const char *errormsg = NULL; int retv = 0; #undef ERROR @@ -1690,7 +1690,7 @@ int execute_cmd(exarg_T *eap, CmdParseInfo *cmdinfo, bool preview) && !(eap->cmdidx == CMD_file && *eap->arg == NUL) && !IS_USER_CMDIDX(eap->cmdidx) && curbuf_locked()) { - ERROR(_(e_cannot_edit_other_buf)); + goto end; } correct_range(eap); @@ -1881,7 +1881,7 @@ static bool skip_cmd(const exarg_T *eap) static char *do_one_cmd(char **cmdlinep, int flags, cstack_T *cstack, LineGetter fgetline, void *cookie) { - char *errormsg = NULL; // error message + const char *errormsg = NULL; // error message const int save_reg_executing = reg_executing; const bool save_pending_end_reg_executing = pending_end_reg_executing; @@ -2376,7 +2376,7 @@ char *ex_errmsg(const char *const msg, const char *const arg) /// - set 'eventignore' to "all" for ":noautocmd" /// /// @return FAIL when the command is not to be executed. -int parse_command_modifiers(exarg_T *eap, char **errormsg, cmdmod_T *cmod, bool skip_only) +int parse_command_modifiers(exarg_T *eap, const char **errormsg, cmdmod_T *cmod, bool skip_only) { CLEAR_POINTER(cmod); @@ -2557,7 +2557,10 @@ int parse_command_modifiers(exarg_T *eap, char **errormsg, cmdmod_T *cmod, bool if (checkforcmd(&p, "tab", 3)) { if (!skip_only) { int tabnr = (int)get_address(eap, &eap->cmd, ADDR_TABS, eap->skip, skip_only, - false, 1); + false, 1, errormsg); + if (eap->cmd == NULL) { + return false; + } if (tabnr == MAXLNUM) { cmod->cmod_tab = tabpage_index(curtab) + 1; @@ -2701,7 +2704,7 @@ void undo_cmdmod(cmdmod_T *cmod) /// May set the last search pattern, unless "silent" is true. /// /// @return FAIL and set "errormsg" or return OK. -int parse_cmd_address(exarg_T *eap, char **errormsg, bool silent) +int parse_cmd_address(exarg_T *eap, const char **errormsg, bool silent) FUNC_ATTR_NONNULL_ALL { int address_count = 1; @@ -2715,7 +2718,7 @@ int parse_cmd_address(exarg_T *eap, char **errormsg, bool silent) eap->line2 = get_cmd_default_range(eap); eap->cmd = skipwhite(eap->cmd); lnum = get_address(eap, &eap->cmd, eap->addr_type, eap->skip, silent, - eap->addr_count == 0, address_count++); + eap->addr_count == 0, address_count++, errormsg); if (eap->cmd == NULL) { // error detected goto theend; } @@ -2794,13 +2797,13 @@ int parse_cmd_address(exarg_T *eap, char **errormsg, bool silent) eap->cmd++; if (!eap->skip) { fmark_T *fm = mark_get_visual(curbuf, '<'); - if (!mark_check(fm)) { + if (!mark_check(fm, errormsg)) { goto theend; } assert(fm != NULL); eap->line1 = fm->mark.lnum; fm = mark_get_visual(curbuf, '>'); - if (!mark_check(fm)) { + if (!mark_check(fm, errormsg)) { goto theend; } assert(fm != NULL); @@ -3229,29 +3232,30 @@ char *skip_range(const char *cmd, int *ctx) return (char *)cmd; } -static void addr_error(cmd_addr_T addr_type) +static const char *addr_error(cmd_addr_T addr_type) { if (addr_type == ADDR_NONE) { - emsg(_(e_norange)); + return _(e_norange); } else { - emsg(_(e_invrange)); + return _(e_invrange); } } -/// Get a single EX address +/// Gets a single EX address. /// -/// Set ptr to the next character after the part that was interpreted. -/// Set ptr to NULL when an error is encountered. -/// This may set the last used search pattern. +/// Sets ptr to the next character after the part that was interpreted. +/// Sets ptr to NULL when an error is encountered (stored in `errormsg`). +/// May set the last used search pattern. /// /// @param skip only skip the address, don't use it /// @param silent no errors or side effects /// @param to_other_file flag: may jump to other file /// @param address_count 1 for first, >1 after comma +/// @param errormsg Error message, if any /// /// @return MAXLNUM when no Ex address was found. static linenr_T get_address(exarg_T *eap, char **ptr, cmd_addr_T addr_type, int skip, bool silent, - int to_other_file, int address_count) + int to_other_file, int address_count, const char **errormsg) FUNC_ATTR_NONNULL_ALL { int c; @@ -3287,7 +3291,7 @@ static linenr_T get_address(exarg_T *eap, char **ptr, cmd_addr_T addr_type, int case ADDR_NONE: case ADDR_TABS_RELATIVE: case ADDR_UNSIGNED: - addr_error(addr_type); + *errormsg = addr_error(addr_type); cmd = NULL; goto error; break; @@ -3332,7 +3336,7 @@ static linenr_T get_address(exarg_T *eap, char **ptr, cmd_addr_T addr_type, int case ADDR_NONE: case ADDR_TABS_RELATIVE: case ADDR_UNSIGNED: - addr_error(addr_type); + *errormsg = addr_error(addr_type); cmd = NULL; goto error; break; @@ -3357,7 +3361,7 @@ static linenr_T get_address(exarg_T *eap, char **ptr, cmd_addr_T addr_type, int goto error; } if (addr_type != ADDR_LINES) { - addr_error(addr_type); + *errormsg = addr_error(addr_type); cmd = NULL; goto error; } @@ -3373,7 +3377,7 @@ static linenr_T get_address(exarg_T *eap, char **ptr, cmd_addr_T addr_type, int // Jumped to another file. lnum = curwin->w_cursor.lnum; } else { - if (!mark_check(fm)) { + if (!mark_check(fm, errormsg)) { cmd = NULL; goto error; } @@ -3387,7 +3391,7 @@ static linenr_T get_address(exarg_T *eap, char **ptr, cmd_addr_T addr_type, int case '?': // '/' or '?' - search c = (uint8_t)(*cmd++); if (addr_type != ADDR_LINES) { - addr_error(addr_type); + *errormsg = addr_error(addr_type); cmd = NULL; goto error; } @@ -3436,7 +3440,7 @@ static linenr_T get_address(exarg_T *eap, char **ptr, cmd_addr_T addr_type, int case '\\': // "\?", "\/" or "\&", repeat search cmd++; if (addr_type != ADDR_LINES) { - addr_error(addr_type); + *errormsg = addr_error(addr_type); cmd = NULL; goto error; } @@ -3445,7 +3449,7 @@ static linenr_T get_address(exarg_T *eap, char **ptr, cmd_addr_T addr_type, int } else if (*cmd == '?' || *cmd == '/') { i = RE_SEARCH; } else { - emsg(_(e_backslash)); + *errormsg = _(e_backslash); cmd = NULL; goto error; } @@ -3527,13 +3531,13 @@ static linenr_T get_address(exarg_T *eap, char **ptr, cmd_addr_T addr_type, int // "number", "+number" or "-number" n = getdigits_int32(&cmd, false, MAXLNUM); if (n == MAXLNUM) { - emsg(_(e_line_number_out_of_range)); + *errormsg = _(e_line_number_out_of_range); goto error; } } if (addr_type == ADDR_TABS_RELATIVE) { - emsg(_(e_invrange)); + *errormsg = _(e_invrange); cmd = NULL; goto error; } else if (addr_type == ADDR_LOADED_BUFFERS || addr_type == ADDR_BUFFERS) { @@ -3549,7 +3553,7 @@ static linenr_T get_address(exarg_T *eap, char **ptr, cmd_addr_T addr_type, int lnum -= n; } else { if (n >= INT32_MAX - lnum) { - emsg(_(e_line_number_out_of_range)); + *errormsg = _(e_line_number_out_of_range); goto error; } lnum += n; @@ -3762,7 +3766,7 @@ char *replace_makeprg(exarg_T *eap, char *arg, char **cmdlinep) /// When an error is detected, "errormsgp" is set to a non-NULL pointer. /// /// @return FAIL for failure, OK otherwise. -int expand_filename(exarg_T *eap, char **cmdlinep, char **errormsgp) +int expand_filename(exarg_T *eap, char **cmdlinep, const char **errormsgp) { // Skip a regexp pattern for ":vimgrep[add] pat file..." char *p = skip_grep_pat(eap); @@ -5855,8 +5859,12 @@ static void ex_put(exarg_T *eap) /// Handle ":copy" and ":move". static void ex_copymove(exarg_T *eap) { - long n = get_address(eap, &eap->arg, eap->addr_type, false, false, false, 1); + const char *errormsg = NULL; + long n = get_address(eap, &eap->arg, eap->addr_type, false, false, false, 1, &errormsg); if (eap->arg == NULL) { // error detected + if (errormsg != NULL) { + emsg(errormsg); + } eap->nextcmd = NULL; return; } @@ -6770,8 +6778,8 @@ ssize_t find_cmdline_var(const char *src, size_t *usedlen) /// @return an allocated string if a valid match was found. /// Returns NULL if no match was found. "usedlen" then still contains the /// number of characters to skip. -char *eval_vars(char *src, const char *srcstart, size_t *usedlen, linenr_T *lnump, char **errormsg, - int *escaped, bool empty_is_error) +char *eval_vars(char *src, const char *srcstart, size_t *usedlen, linenr_T *lnump, + const char **errormsg, int *escaped, bool empty_is_error) { char *result; char *resultbuf = NULL; @@ -6899,12 +6907,10 @@ char *eval_vars(char *src, const char *srcstart, size_t *usedlen, linenr_T *lnum break; case SPEC_AFILE: // file name for autocommand - if (autocmd_fname != NULL - && !path_is_absolute(autocmd_fname) - // For CmdlineEnter and related events, <afile> is not a path! #9348 - && !strequal("/", autocmd_fname)) { + if (autocmd_fname != NULL && !autocmd_fname_full) { // Still need to turn the fname into a full path. It was // postponed to avoid a delay when <afile> is not used. + autocmd_fname_full = true; result = FullName_save(autocmd_fname, false); // Copy into `autocmd_fname`, don't reassign it. #8165 xstrlcpy(autocmd_fname, result, MAXPATHL); @@ -7046,7 +7052,7 @@ char *expand_sfile(char *arg) } else { // replace "<sfile>" with the sourced file name, and do ":" stuff size_t srclen; - char *errormsg; + const char *errormsg; char *repl = eval_vars(p, result, &srclen, NULL, &errormsg, NULL, true); if (errormsg != NULL) { if (*errormsg) { @@ -7216,7 +7222,7 @@ static void ex_setfiletype(exarg_T *eap) arg += 9; } - set_option_value_give_err("filetype", 0L, arg, OPT_LOCAL); + set_option_value_give_err("filetype", CSTR_AS_OPTVAL(arg), OPT_LOCAL); if (arg != eap->arg) { did_filetype = false; } diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index b2acc561be..f77822cec6 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -236,7 +236,7 @@ static bool do_incsearch_highlighting(int firstc, int *search_delim, incsearch_s bool delim_optional = false; int delim; char *end; - char *dummy; + const char *dummy; pos_T save_cursor; bool use_last_pat; bool retval = false; @@ -261,7 +261,6 @@ static bool do_incsearch_highlighting(int firstc, int *search_delim, incsearch_s return false; } - emsg_off++; exarg_T ea = { .line1 = 1, .line2 = 1, @@ -369,7 +368,6 @@ static bool do_incsearch_highlighting(int firstc, int *search_delim, incsearch_s curwin->w_cursor = save_cursor; retval = true; theend: - emsg_off--; return retval; } @@ -791,7 +789,7 @@ static uint8_t *command_line_enter(int firstc, long count, int indent, bool clea // Redraw the statusline in case it uses the current mode using the mode() // function. - if (!cmd_silent) { + if (!cmd_silent && !exmode_active) { bool found_one = false; FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { @@ -2428,13 +2426,10 @@ static bool cmdpreview_may_show(CommandLineState *s) // Copy the command line so we can modify it. int cmdpreview_type = 0; char *cmdline = xstrdup(ccline.cmdbuff); - char *errormsg = NULL; - emsg_off++; // Block errors when parsing the command line, and don't update v:errmsg + const char *errormsg = NULL; if (!parse_cmdline(cmdline, &ea, &cmdinfo, &errormsg)) { - emsg_off--; goto end; } - emsg_off--; // Check if command is previewable, if not, don't attempt to show preview if (!(ea.argt & EX_PREVIEW)) { @@ -4357,7 +4352,7 @@ static int open_cmdwin(void) return Ctrl_C; } // Command-line buffer has bufhidden=wipe, unlike a true "scratch" buffer. - set_option_value_give_err("bh", 0L, "wipe", OPT_LOCAL); + set_option_value_give_err("bh", STATIC_CSTR_AS_OPTVAL("wipe"), OPT_LOCAL); curbuf->b_p_ma = true; curwin->w_p_fen = false; curwin->w_p_rl = cmdmsg_rl; @@ -4375,7 +4370,7 @@ static int open_cmdwin(void) add_map("<Tab>", "<C-X><C-V>", MODE_INSERT, true); add_map("<Tab>", "a<C-X><C-V>", MODE_NORMAL, true); } - set_option_value_give_err("ft", 0L, "vim", OPT_LOCAL); + set_option_value_give_err("ft", STATIC_CSTR_AS_OPTVAL("vim"), OPT_LOCAL); } curbuf->b_ro_locked--; diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index d24ac1c233..e22bb609b6 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -990,7 +990,8 @@ retry: } } else { // Read bytes from the file. - size = read_eintr(fd, ptr, (size_t)size); + size_t read_size = (size_t)size; + size = read_eintr(fd, ptr, read_size); } if (size <= 0) { @@ -3618,7 +3619,7 @@ bool match_file_list(char *list, char *sfname, char *ffname) // try all patterns in 'wildignore' char *p = list; while (*p) { - char buf[100]; + char buf[MAXPATHL]; copy_option_part(&p, buf, ARRAY_SIZE(buf), ","); char allow_dirs; char *regpat = file_pat_to_reg_pat(buf, NULL, &allow_dirs, false); diff --git a/src/nvim/func_attr.h b/src/nvim/func_attr.h index 4b434f6771..57e5766f67 100644 --- a/src/nvim/func_attr.h +++ b/src/nvim/func_attr.h @@ -222,9 +222,9 @@ # define FUNC_API_FAST /// Internal C function not exposed in the RPC API. # define FUNC_API_NOEXPORT -/// API function not exposed in VimL/eval. +/// API function not exposed in Vimscript/eval. # define FUNC_API_REMOTE_ONLY -/// API function not exposed in VimL/remote. +/// API function not exposed in Vimscript/remote. # define FUNC_API_LUA_ONLY /// API function checked textlock. # define FUNC_API_CHECK_TEXTLOCK diff --git a/src/nvim/generators/gen_options.lua b/src/nvim/generators/gen_options.lua index ecb1a0c11b..03b1fbec8a 100644 --- a/src/nvim/generators/gen_options.lua +++ b/src/nvim/generators/gen_options.lua @@ -108,7 +108,7 @@ local value_dumpers = { } local get_value = function(v) - return '(char *) ' .. value_dumpers[type(v)](v) + return '(void *) ' .. value_dumpers[type(v)](v) end local get_defaults = function(d,n) @@ -131,7 +131,7 @@ local dump_option = function(i, o) w(get_cond(o.enable_if)) end if o.varname then - w(' .var=(char *)&' .. o.varname) + w(' .var=&' .. o.varname) elseif #o.scope == 1 and o.scope[1] == 'window' then w(' .var=VAR_WIN') end diff --git a/src/nvim/globals.h b/src/nvim/globals.h index bedb529d04..f2c9aea675 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -29,8 +29,15 @@ # define _PATHSEPSTR "/" #endif +// FILETYPE_FILE used for file type detection +// FTPLUGIN_FILE used for loading filetype plugin files +// INDENT_FILE used for loading indent files +// FTOFF_FILE used for file type detection +// FTPLUGOF_FILE used for loading settings files +// INDOFF_FILE used for loading indent files + #ifndef FILETYPE_FILE -# define FILETYPE_FILE "filetype.lua filetype.vim" +# define FILETYPE_FILE "filetype.lua filetype.vim" #endif #ifndef FTPLUGIN_FILE @@ -336,6 +343,7 @@ EXTERN struct caller_scope { sctx_T script_ctx; estack_T es_entry; char *autocmd_fname, *autocmd_match; + bool autocmd_fname_full; int autocmd_bufnr; void *funccalp; } provider_caller_scope; @@ -759,6 +767,7 @@ EXTERN char *last_cmdline INIT(= NULL); // last command line (for ":) EXTERN char *repeat_cmdline INIT(= NULL); // command line for "." EXTERN char *new_last_cmdline INIT(= NULL); // new value for last_cmdline EXTERN char *autocmd_fname INIT(= NULL); // fname for <afile> on cmdline +EXTERN bool autocmd_fname_full INIT(= false); // autocmd_fname is full path EXTERN int autocmd_bufnr INIT(= 0); // fnum for <abuf> on cmdline EXTERN char *autocmd_match INIT(= NULL); // name for <amatch> on cmdline EXTERN bool did_cursorhold INIT(= false); // set when CursorHold t'gerd @@ -1033,6 +1042,8 @@ INIT(= N_("E5767: Cannot use :undo! to redo or move to a different undo branch") EXTERN const char e_trustfile[] INIT(= N_("E5570: Cannot update trust file: %s")); +EXTERN const char e_unknown_option2[] INIT(= N_("E355: Unknown option: %s")); + EXTERN const char top_bot_msg[] INIT(= N_("search hit TOP, continuing at BOTTOM")); EXTERN const char bot_top_msg[] INIT(= N_("search hit BOTTOM, continuing at TOP")); diff --git a/src/nvim/grid.c b/src/nvim/grid.c index f2ceb2ac24..11cd691f22 100644 --- a/src/nvim/grid.c +++ b/src/nvim/grid.c @@ -540,7 +540,7 @@ void grid_put_linebuf(ScreenGrid *grid, int row, int coloff, int endcol, int cle size_t skip = 0; if (wp->w_p_nu && wp->w_p_rnu) { // do not overwrite the line number, change "123 text" to - // "123>>>xt". + // "123<<<xt". while (skip < max_off_from && ascii_isdigit(*linebuf_char[off])) { off++; skip++; diff --git a/src/nvim/help.c b/src/nvim/help.c index d412f3a098..8c7c19e7c3 100644 --- a/src/nvim/help.c +++ b/src/nvim/help.c @@ -458,7 +458,7 @@ int find_help_tags(const char *arg, int *num_matches, char ***matches, bool keep // Replace "^x" by "CTRL-X". Don't do this for "^_" to make // ":help i_^_CTRL-D" work. // Insert '-' before and after "CTRL-X" when applicable. - if (*s < ' ' + if ((uint8_t)(*s) < ' ' || (*s == '^' && s[1] && (ASCII_ISALPHA(s[1]) || vim_strchr("?@[\\]^", (uint8_t)s[1]) != NULL))) { if (d > IObuff && d[-1] != '_' && d[-1] != '\\') { @@ -653,7 +653,7 @@ void fix_help_buffer(void) // Set filetype to "help". if (strcmp(curbuf->b_p_ft, "help") != 0) { curbuf->b_ro_locked++; - set_option_value_give_err("ft", 0L, "help", OPT_LOCAL); + set_option_value_give_err("ft", STATIC_CSTR_AS_OPTVAL("help"), OPT_LOCAL); curbuf->b_ro_locked--; } diff --git a/src/nvim/highlight_group.c b/src/nvim/highlight_group.c index 09e7d04e02..da43f678bc 100644 --- a/src/nvim/highlight_group.c +++ b/src/nvim/highlight_group.c @@ -231,6 +231,7 @@ static const char *highlight_init_both[] = { "default link DiagnosticSignOk DiagnosticOk", "default DiagnosticDeprecated cterm=strikethrough gui=strikethrough guisp=Red", "default link DiagnosticUnnecessary Comment", + "default link LspInlayHint NonText", // Text "default link @text.literal Comment", @@ -1280,7 +1281,7 @@ void do_highlight(const char *line, const bool forceit, const bool init) if (dark != -1 && dark != (*p_bg == 'd') && !option_was_set("bg")) { - set_option_value_give_err("bg", 0L, (dark ? "dark" : "light"), 0); + set_option_value_give_err("bg", CSTR_AS_OPTVAL(dark ? "dark" : "light"), 0); reset_option_was_set("bg"); } } diff --git a/src/nvim/insexpand.c b/src/nvim/insexpand.c index d6860fff30..8285c0c2b8 100644 --- a/src/nvim/insexpand.c +++ b/src/nvim/insexpand.c @@ -2432,7 +2432,7 @@ theend: } } -/// Add a match to the list of matches from VimL object +/// Add a match to the list of matches from Vimscript object /// /// @param[in] tv Object to get matches from. /// @param[in] dir Completion direction. diff --git a/src/nvim/linematch.c b/src/nvim/linematch.c index 7bde6bb121..e22cc2d9a1 100644 --- a/src/nvim/linematch.c +++ b/src/nvim/linematch.c @@ -2,6 +2,7 @@ // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com #include <assert.h> +#include <math.h> #include <stdbool.h> #include <stddef.h> #include <string.h> @@ -10,14 +11,19 @@ #include "nvim/macros.h" #include "nvim/memory.h" +#define LN_MAX_BUFS 8 +#define LN_DECISION_MAX 255 // pow(2, LN_MAX_BUFS(8)) - 1 = 255 + // struct for running the diff linematch algorithm -typedef struct { - int *df_decision; // to keep track of this path traveled +typedef struct diffcmppath_S diffcmppath_T; +struct diffcmppath_S { int df_lev_score; // to keep track of the total score of this path - size_t df_path_idx; // current index of this path -} diffcmppath_T; - -#define LN_MAX_BUFS 8 + size_t df_path_n; // current index of this path + int df_choice_mem[LN_DECISION_MAX + 1]; + int df_choice[LN_DECISION_MAX]; + diffcmppath_T *df_decision[LN_DECISION_MAX]; // to keep track of this path traveled + size_t df_optimal_choice; +}; #ifdef INCLUDE_GENERATED_DECLARATIONS # include "linematch.c.generated.h" @@ -64,26 +70,6 @@ static int matching_chars_iwhite(const char *s1, const char *s2) return matching; } -/// update the path of a point in the diff linematch algorithm -/// @param diffcmppath -/// @param score -/// @param to -/// @param from -/// @param choice -static void update_path_flat(diffcmppath_T *diffcmppath, int score, size_t to, size_t from, - const int choice) -{ - size_t path_idx = diffcmppath[from].df_path_idx; - - for (size_t k = 0; k < path_idx; k++) { - diffcmppath[to].df_decision[k] = diffcmppath[from].df_decision[k]; - } - - diffcmppath[to].df_decision[path_idx] = choice; - diffcmppath[to].df_lev_score = score; - diffcmppath[to].df_path_idx = path_idx + 1; -} - #define MATCH_CHAR_MAX_LEN 800 /// Return matching characters between "s1" and "s2" whilst respecting sequence order. @@ -206,17 +192,14 @@ static void try_possible_paths(const int *df_iters, const size_t *paths, const i int matched_chars = count_n_matched_chars(current_lines, ndiffs, iwhite); int score = diffcmppath[unwrapped_idx_from].df_lev_score + matched_chars; if (score > diffcmppath[unwrapped_idx_to].df_lev_score) { - update_path_flat(diffcmppath, score, unwrapped_idx_to, unwrapped_idx_from, *choice); - } - } else { - // initialize the 0, 0, 0 ... choice - size_t i = 0; - while (i < ndiffs && df_iters[i] == 0) { - i++; - if (i == ndiffs) { - diffcmppath[0].df_lev_score = 0; - diffcmppath[0].df_path_idx = 0; - } + diffcmppath[unwrapped_idx_to].df_path_n = 1; + diffcmppath[unwrapped_idx_to].df_decision[0] = &diffcmppath[unwrapped_idx_from]; + diffcmppath[unwrapped_idx_to].df_choice[0] = *choice; + diffcmppath[unwrapped_idx_to].df_lev_score = score; + } else if (score == diffcmppath[unwrapped_idx_to].df_lev_score) { + size_t k = diffcmppath[unwrapped_idx_to].df_path_n++; + diffcmppath[unwrapped_idx_to].df_decision[k] = &diffcmppath[unwrapped_idx_from]; + diffcmppath[unwrapped_idx_to].df_choice[k] = *choice; } } return; @@ -245,8 +228,7 @@ static size_t unwrap_indexes(const int *values, const int *diff_len, const size_ for (size_t k = 0; k < ndiffs; k++) { num_unwrap_scalar /= (size_t)diff_len[k] + 1; - // (k == 0) space optimization - int n = k == 0 ? values[k] % 2 : values[k]; + int n = values[k]; path_idx += num_unwrap_scalar * (size_t)n; } return path_idx; @@ -354,7 +336,7 @@ size_t linematch_nbuffers(const char **diff_blk, const int *diff_len, const size size_t memsize_decisions = 0; for (size_t i = 0; i < ndiffs; i++) { assert(diff_len[i] >= 0); - memsize *= i == 0 ? 2 : (size_t)(diff_len[i] + 1); + memsize *= (size_t)(diff_len[i] + 1); memsize_decisions += (size_t)diff_len[i]; } @@ -362,7 +344,11 @@ size_t linematch_nbuffers(const char **diff_blk, const int *diff_len, const size diffcmppath_T *diffcmppath = xmalloc(sizeof(diffcmppath_T) * memsize); // allocate memory here for (size_t i = 0; i < memsize; i++) { - diffcmppath[i].df_decision = xmalloc(memsize_decisions * sizeof(int)); + diffcmppath[i].df_lev_score = 0; + diffcmppath[i].df_path_n = 0; + for (size_t j = 0; j < (size_t)pow(2, (double)ndiffs); j++) { + diffcmppath[i].df_choice_mem[j] = -1; + } } // memory for avoiding repetitive calculations of score @@ -370,18 +356,49 @@ size_t linematch_nbuffers(const char **diff_blk, const int *diff_len, const size populate_tensor(df_iters, 0, diffcmppath, diff_len, ndiffs, diff_blk, iwhite); const size_t u = unwrap_indexes(diff_len, diff_len, ndiffs); - const size_t best_path_idx = diffcmppath[u].df_path_idx; - const int *best_path_decisions = diffcmppath[u].df_decision; + diffcmppath_T *startNode = &diffcmppath[u]; - *decisions = xmalloc(sizeof(int) * best_path_idx); - for (size_t i = 0; i < best_path_idx; i++) { - (*decisions)[i] = best_path_decisions[i]; + *decisions = xmalloc(sizeof(int) * memsize_decisions); + size_t n_optimal = 0; + test_charmatch_paths(startNode, 0); + while (startNode->df_path_n > 0) { + size_t j = startNode->df_optimal_choice; + (*decisions)[n_optimal++] = startNode->df_choice[j]; + startNode = startNode->df_decision[j]; } - - for (size_t i = 0; i < memsize; i++) { - xfree(diffcmppath[i].df_decision); + // reverse array + for (size_t i = 0; i < (n_optimal / 2); i++) { + int tmp = (*decisions)[i]; + (*decisions)[i] = (*decisions)[n_optimal - 1 - i]; + (*decisions)[n_optimal - 1 - i] = tmp; } + xfree(diffcmppath); - return best_path_idx; + return n_optimal; +} + +// returns the minimum amount of path changes from start to end +static size_t test_charmatch_paths(diffcmppath_T *node, int lastdecision) +{ + // memoization + if (node->df_choice_mem[lastdecision] == -1) { + if (node->df_path_n == 0) { + // we have reached the end of the tree + node->df_choice_mem[lastdecision] = 0; + } else { + size_t minimum_turns = SIZE_MAX; // the minimum amount of turns required to reach the end + for (size_t i = 0; i < node->df_path_n; i++) { + // recurse + size_t t = test_charmatch_paths(node->df_decision[i], node->df_choice[i]) + + (lastdecision != node->df_choice[i] ? 1 : 0); + if (t < minimum_turns) { + node->df_optimal_choice = i; + minimum_turns = t; + } + } + node->df_choice_mem[lastdecision] = (int)minimum_turns; + } + } + return (size_t)node->df_choice_mem[lastdecision]; } diff --git a/src/nvim/lua/converter.c b/src/nvim/lua/converter.c index 9ff03bc67d..5b3b4dbe08 100644 --- a/src/nvim/lua/converter.c +++ b/src/nvim/lua/converter.c @@ -183,7 +183,7 @@ typedef struct { int idx; ///< Container index (used to detect self-referencing structures). } TVPopStackItem; -/// Convert lua object to VimL typval_T +/// Convert lua object to Vimscript typval_T /// /// Should pop exactly one value from lua stack. /// @@ -601,7 +601,7 @@ static bool typval_conv_special = false; #undef TYPVAL_ENCODE_CONV_RECURSE #undef TYPVAL_ENCODE_ALLOW_SPECIALS -/// Convert VimL typval_T to lua value +/// Convert Vimscript typval_T to lua value /// /// Should leave single value in lua stack. May only fail if lua failed to grow /// stack. @@ -628,8 +628,7 @@ bool nlua_push_typval(lua_State *lstate, typval_T *const tv, bool special) /// Push value which is a type index /// -/// Used for all “typed” tables: i.e. for all tables which represent VimL -/// values. +/// Used for all “typed” tables: i.e. for all tables which represent Vimscript values. static inline void nlua_push_type_idx(lua_State *lstate) FUNC_ATTR_NONNULL_ALL { @@ -657,7 +656,7 @@ static inline void nlua_push_type(lua_State *lstate, ObjectType type) lua_pushnumber(lstate, (lua_Number)type); } -/// Create lua table which has an entry that determines its VimL type +/// Create Lua table which has an entry that determines its Vimscript type /// /// @param[out] lstate Lua state. /// @param[in] narr Number of “array” entries to be populated later. diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index 35540de99e..ed84e2a601 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -1487,7 +1487,7 @@ int nlua_source_using_linegetter(LineGetter fgetline, void *cookie, char *name) /// Call a LuaCallable given some typvals /// -/// Used to call any lua callable passed from Lua into VimL +/// Used to call any Lua callable passed from Lua into Vimscript. /// /// @param[in] lstate Lua State /// @param[in] lua_cb Lua Callable @@ -1622,7 +1622,7 @@ bool nlua_is_deferred_safe(void) /// /// Used for :lua. /// -/// @param eap VimL command being run. +/// @param eap Vimscript command being run. void ex_lua(exarg_T *const eap) FUNC_ATTR_NONNULL_ALL { @@ -1654,7 +1654,7 @@ void ex_lua(exarg_T *const eap) /// /// Used for :luado. /// -/// @param eap VimL command being run. +/// @param eap Vimscript command being run. void ex_luado(exarg_T *const eap) FUNC_ATTR_NONNULL_ALL { @@ -1735,7 +1735,7 @@ void ex_luado(exarg_T *const eap) /// /// Used for :luafile. /// -/// @param eap VimL command being run. +/// @param eap Vimscript command being run. void ex_luafile(exarg_T *const eap) FUNC_ATTR_NONNULL_ALL { diff --git a/src/nvim/main.c b/src/nvim/main.c index f27ebb2f67..567d364284 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -629,8 +629,14 @@ int main(int argc, char **argv) } if (params.luaf != NULL) { + // Like "--cmd", "+", "-c" and "-S", don't truncate messages. + msg_scroll = true; bool lua_ok = nlua_exec_file(params.luaf); TIME_MSG("executing Lua -l script"); + if (msg_didout) { + msg_putchar('\n'); + msg_didout = false; + } getout(lua_ok ? 0 : 1); } @@ -953,7 +959,7 @@ static void remote_request(mparm_T *params, int remote_args, char *server_addr, TriState tabbed = kNone; for (size_t i = 0; i < rvobj.data.dictionary.size; i++) { - if (strcmp(rvobj.data.dictionary.items[i].key.data, "errmsg") == 0) { + if (strequal(rvobj.data.dictionary.items[i].key.data, "errmsg")) { if (rvobj.data.dictionary.items[i].value.type != kObjectTypeString) { os_errmsg("vim._cs_remote returned an unexpected type for 'errmsg'\n"); os_exit(2); @@ -961,13 +967,19 @@ static void remote_request(mparm_T *params, int remote_args, char *server_addr, os_errmsg(rvobj.data.dictionary.items[i].value.data.string.data); os_errmsg("\n"); os_exit(2); - } else if (strcmp(rvobj.data.dictionary.items[i].key.data, "tabbed") == 0) { + } else if (strequal(rvobj.data.dictionary.items[i].key.data, "result")) { + if (rvobj.data.dictionary.items[i].value.type != kObjectTypeString) { + os_errmsg("vim._cs_remote returned an unexpected type for 'result'\n"); + os_exit(2); + } + os_msg(rvobj.data.dictionary.items[i].value.data.string.data); + } else if (strequal(rvobj.data.dictionary.items[i].key.data, "tabbed")) { if (rvobj.data.dictionary.items[i].value.type != kObjectTypeBoolean) { os_errmsg("vim._cs_remote returned an unexpected type for 'tabbed'\n"); os_exit(2); } tabbed = rvobj.data.dictionary.items[i].value.data.boolean ? kTrue : kFalse; - } else if (strcmp(rvobj.data.dictionary.items[i].key.data, "should_exit") == 0) { + } else if (strequal(rvobj.data.dictionary.items[i].key.data, "should_exit")) { if (rvobj.data.dictionary.items[i].value.type != kObjectTypeBoolean) { os_errmsg("vim._cs_remote returned an unexpected type for 'should_exit'\n"); os_exit(2); @@ -1112,7 +1124,7 @@ static void command_line_scan(mparm_T *parmp) } else if (STRNICMP(argv[0] + argv_idx, "clean", 5) == 0) { parmp->use_vimrc = "NONE"; parmp->clean = true; - set_option_value_give_err("shadafile", 0L, "NONE", 0); + set_option_value_give_err("shadafile", STATIC_CSTR_AS_OPTVAL("NONE"), 0); } else if (STRNICMP(argv[0] + argv_idx, "luamod-dev", 9) == 0) { nlua_disable_preload = true; } else { @@ -1126,7 +1138,7 @@ static void command_line_scan(mparm_T *parmp) } break; case 'A': // "-A" start in Arabic mode. - set_option_value_give_err("arabic", 1L, NULL, 0); + set_option_value_give_err("arabic", BOOLEAN_OPTVAL(true), 0); break; case 'b': // "-b" binary mode. // Needs to be effective before expanding file names, because @@ -1156,8 +1168,8 @@ static void command_line_scan(mparm_T *parmp) usage(); os_exit(0); case 'H': // "-H" start in Hebrew mode: rl + keymap=hebrew set. - set_option_value_give_err("keymap", 0L, "hebrew", 0); - set_option_value_give_err("rl", 1L, NULL, 0); + set_option_value_give_err("keymap", STATIC_CSTR_AS_OPTVAL("hebrew"), 0); + set_option_value_give_err("rl", BOOLEAN_OPTVAL(true), 0); break; case 'M': // "-M" no changes or writing of files reset_modifiable(); @@ -1237,7 +1249,7 @@ static void command_line_scan(mparm_T *parmp) // default is 10: a little bit verbose p_verbose = get_number_arg(argv[0], &argv_idx, 10); if (argv[0][argv_idx] != NUL) { - set_option_value_give_err("verbosefile", 0L, argv[0] + argv_idx, 0); + set_option_value_give_err("verbosefile", CSTR_AS_OPTVAL(argv[0] + argv_idx), 0); argv_idx = (int)strlen(argv[0]); } break; @@ -1245,7 +1257,7 @@ static void command_line_scan(mparm_T *parmp) // "-w {scriptout}" write to script if (ascii_isdigit((argv[0])[argv_idx])) { n = get_number_arg(argv[0], &argv_idx, 10); - set_option_value_give_err("window", n, NULL, 0); + set_option_value_give_err("window", NUMBER_OPTVAL(n), 0); break; } want_argument = true; @@ -1341,7 +1353,7 @@ static void command_line_scan(mparm_T *parmp) break; case 'i': // "-i {shada}" use for shada - set_option_value_give_err("shadafile", 0L, argv[0], 0); + set_option_value_give_err("shadafile", CSTR_AS_OPTVAL(argv[0]), 0); break; case 'l': // "-l" Lua script: args after "-l". @@ -1351,11 +1363,11 @@ static void command_line_scan(mparm_T *parmp) parmp->no_swap_file = true; parmp->use_vimrc = parmp->use_vimrc ? parmp->use_vimrc : "NONE"; if (p_shadafile == NULL || *p_shadafile == NUL) { - set_option_value_give_err("shadafile", 0L, "NONE", 0); + set_option_value_give_err("shadafile", STATIC_CSTR_AS_OPTVAL("NONE"), 0); } parmp->luaf = argv[0]; argc--; - if (argc > 0) { // Lua args after "-l <file>". + if (argc >= 0) { // Lua args after "-l <file>". parmp->lua_arg0 = parmp->argc - argc; argc = 0; } @@ -1387,7 +1399,7 @@ scripterror: if (ascii_isdigit(*(argv[0]))) { argv_idx = 0; n = get_number_arg(argv[0], &argv_idx, 10); - set_option_value_give_err("window", n, NULL, 0); + set_option_value_give_err("window", NUMBER_OPTVAL(n), 0); argv_idx = -1; break; } @@ -1782,7 +1794,7 @@ static void edit_buffers(mparm_T *parmp, char *cwd) p_shm_save = xstrdup(p_shm); snprintf(buf, sizeof(buf), "F%s", p_shm); - set_option_value_give_err("shm", 0L, buf, 0); + set_option_value_give_err("shm", CSTR_AS_OPTVAL(buf), 0); } } else { if (curwin->w_next == NULL) { // just checking @@ -1826,7 +1838,7 @@ static void edit_buffers(mparm_T *parmp, char *cwd) } if (p_shm_save != NULL) { - set_option_value_give_err("shm", 0L, p_shm_save, 0); + set_option_value_give_err("shm", CSTR_AS_OPTVAL(p_shm_save), 0); xfree(p_shm_save); } @@ -2179,43 +2191,33 @@ static void usage(void) signal_stop(); // kill us with CTRL-C here, if you like os_msg(_("Usage:\n")); - os_msg(_(" nvim [options] [file ...] Edit file(s)\n")); - os_msg(_(" nvim [options] -t <tag> Edit file where tag is defined\n")); - os_msg(_(" nvim [options] -q [errorfile] Edit file with first error\n")); + os_msg(_(" nvim [options] [file ...]\n")); os_msg(_("\nOptions:\n")); - os_msg(_(" -- Only file names after this\n")); - os_msg(_(" + Start at end of file\n")); os_msg(_(" --cmd <cmd> Execute <cmd> before any config\n")); os_msg(_(" +<cmd>, -c <cmd> Execute <cmd> after config and first file\n")); os_msg(_(" -l <script> [args...] Execute Lua <script> (with optional args)\n")); + os_msg(_(" -S <session> Source <session> after loading the first file\n")); + os_msg(_(" -s <scriptin> Read Normal mode commands from <scriptin>\n")); + os_msg(_(" -u <config> Use this config file\n")); os_msg("\n"); - os_msg(_(" -b Binary mode\n")); os_msg(_(" -d Diff mode\n")); - os_msg(_(" -e, -E Ex mode\n")); os_msg(_(" -es, -Es Silent (batch) mode\n")); os_msg(_(" -h, --help Print this help message\n")); os_msg(_(" -i <shada> Use this shada file\n")); - os_msg(_(" -m Modifications (writing files) not allowed\n")); - os_msg(_(" -M Modifications in text not allowed\n")); os_msg(_(" -n No swap file, use memory only\n")); os_msg(_(" -o[N] Open N windows (default: one per file)\n")); os_msg(_(" -O[N] Open N vertical windows (default: one per file)\n")); os_msg(_(" -p[N] Open N tab pages (default: one per file)\n")); - os_msg(_(" -r, -L List swap files\n")); - os_msg(_(" -r <file> Recover edit state for this file\n")); - os_msg(_(" -R Read-only mode\n")); - os_msg(_(" -S <session> Source <session> after loading the first file\n")); - os_msg(_(" -s <scriptin> Read Normal mode commands from <scriptin>\n")); - os_msg(_(" -u <config> Use this config file\n")); + os_msg(_(" -R Read-only (view) mode\n")); os_msg(_(" -v, --version Print version information\n")); os_msg(_(" -V[N][file] Verbose [level][file]\n")); os_msg("\n"); + os_msg(_(" -- Only file names after this\n")); os_msg(_(" --api-info Write msgpack-encoded API metadata to stdout\n")); os_msg(_(" --clean \"Factory defaults\" (skip user config and plugins, shada)\n")); os_msg(_(" --embed Use stdin/stdout as a msgpack-rpc channel\n")); os_msg(_(" --headless Don't start a user interface\n")); os_msg(_(" --listen <address> Serve RPC API from this address\n")); - os_msg(_(" --noplugin Don't load plugins\n")); os_msg(_(" --remote[-subcommand] Execute commands remotely on a server\n")); os_msg(_(" --server <address> Specify RPC server to send commands to\n")); os_msg(_(" --startuptime <file> Write startup timing messages to <file>\n")); diff --git a/src/nvim/mapping.c b/src/nvim/mapping.c index e40e67cead..d7747ee291 100644 --- a/src/nvim/mapping.c +++ b/src/nvim/mapping.c @@ -2603,13 +2603,21 @@ void modify_keymap(uint64_t channel_id, Buffer buffer, bool is_unmap, String mod goto fail_and_free; } - if (mode.size > 1) { + bool is_abbrev = false; + if (mode.size > 2) { api_set_error(err, kErrorTypeValidation, "Shortname is too long: %s", mode.data); goto fail_and_free; + } else if (mode.size == 2) { + if ((mode.data[0] != '!' && mode.data[0] != 'i' && mode.data[0] != 'c') + || mode.data[1] != 'a') { + api_set_error(err, kErrorTypeValidation, "Shortname is too long: %s", mode.data); + goto fail_and_free; + } + is_abbrev = true; } int mode_val; // integer value of the mapping mode, to be passed to do_map() char *p = (mode.size) ? mode.data : "m"; - if (strncmp(p, "!", 2) == 0) { + if (*p == '!') { mode_val = get_map_mode(&p, true); // mapmode-ic } else { mode_val = get_map_mode(&p, false); @@ -2654,7 +2662,7 @@ void modify_keymap(uint64_t channel_id, Buffer buffer, bool is_unmap, String mod maptype_val = MAPTYPE_NOREMAP; } - switch (buf_do_map(maptype_val, &parsed_args, mode_val, 0, target_buf)) { + switch (buf_do_map(maptype_val, &parsed_args, mode_val, is_abbrev, target_buf)) { case 0: break; case 1: diff --git a/src/nvim/mark.c b/src/nvim/mark.c index b3f94a42fc..c67dbb0b52 100644 --- a/src/nvim/mark.c +++ b/src/nvim/mark.c @@ -541,7 +541,11 @@ MarkMoveRes mark_move_to(fmark_T *fm, MarkMove flags) { static fmark_T fm_copy = INIT_FMARK; MarkMoveRes res = kMarkMoveSuccess; - if (!mark_check(fm)) { + const char *errormsg = NULL; + if (!mark_check(fm, &errormsg)) { + if (errormsg != NULL) { + emsg(errormsg); + } res = kMarkMoveFailed; goto end; } @@ -557,7 +561,10 @@ MarkMoveRes mark_move_to(fmark_T *fm, MarkMove flags) goto end; } // Check line count now that the **destination buffer is loaded**. - if (!mark_check_line_bounds(curbuf, fm)) { + if (!mark_check_line_bounds(curbuf, fm, &errormsg)) { + if (errormsg != NULL) { + emsg(errormsg); + } res |= kMarkMoveFailed; goto end; } @@ -710,43 +717,45 @@ static void fmarks_check_one(xfmark_T *fm, char *name, buf_T *buf) /// Check the position in @a fm is valid. /// -/// Emit error message and return accordingly. -/// /// Checks for: /// - NULL raising unknown mark error. /// - Line number <= 0 raising mark not set. /// - Line number > buffer line count, raising invalid mark. +/// /// @param fm[in] File mark to check. +/// @param errormsg[out] Error message, if any. /// /// @return true if the mark passes all the above checks, else false. -bool mark_check(fmark_T *fm) +bool mark_check(fmark_T *fm, const char **errormsg) { if (fm == NULL) { - emsg(_(e_umark)); + *errormsg = _(e_umark); return false; } else if (fm->mark.lnum <= 0) { // In both cases it's an error but only raise when equals to 0 if (fm->mark.lnum == 0) { - emsg(_(e_marknotset)); + *errormsg = _(e_marknotset); } return false; } // Only check for valid line number if the buffer is loaded. - if (fm->fnum == curbuf->handle && !mark_check_line_bounds(curbuf, fm)) { + if (fm->fnum == curbuf->handle && !mark_check_line_bounds(curbuf, fm, errormsg)) { return false; } return true; } /// Check if a mark line number is greater than the buffer line count, and set e_markinval. +/// /// @note Should be done after the buffer is loaded into memory. /// @param buf Buffer where the mark is set. /// @param fm Mark to check. +/// @param errormsg[out] Error message, if any. /// @return true if below line count else false. -bool mark_check_line_bounds(buf_T *buf, fmark_T *fm) +bool mark_check_line_bounds(buf_T *buf, fmark_T *fm, const char **errormsg) { if (buf != NULL && fm->mark.lnum > buf->b_ml.ml_line_count) { - emsg(_(e_markinval)); + *errormsg = _(e_markinval); return false; } return true; diff --git a/src/nvim/memline.c b/src/nvim/memline.c index eb2afc60b2..8f5a957846 100644 --- a/src/nvim/memline.c +++ b/src/nvim/memline.c @@ -977,7 +977,7 @@ void ml_recover(bool checkext) set_fileformat(b0_ff - 1, OPT_LOCAL); } if (b0_fenc != NULL) { - set_option_value_give_err("fenc", 0L, b0_fenc, OPT_LOCAL); + set_option_value_give_err("fenc", CSTR_AS_OPTVAL(b0_fenc), OPT_LOCAL); xfree(b0_fenc); } unchanged(curbuf, true, true); diff --git a/src/nvim/message.c b/src/nvim/message.c index 1cb4a3cbf4..c60e5c31fd 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -1560,6 +1560,13 @@ int msg_outtrans_len_attr(const char *msgstr, int len, int attr) attr &= ~MSG_HIST; } + // When drawing over the command line no need to clear it later or remove + // the mode message. + if (msg_row >= cmdline_row && msg_col == 0) { + clear_cmdline = false; + mode_displayed = false; + } + // If the string starts with a composing character first draw a space on // which the composing char can be drawn. if (utf_iscomposing(utf_ptr2char(msgstr))) { @@ -3504,8 +3511,8 @@ void msg_advance(int col) /// @param textfiel IObuff for inputdialog(), NULL otherwise /// @param ex_cmd when true pressing : accepts default and starts Ex command /// @returns 0 if cancelled, otherwise the nth button (1-indexed). -int do_dialog(int type, char *title, char *message, char *buttons, int dfltbutton, char *textfield, - int ex_cmd) +int do_dialog(int type, const char *title, const char *message, const char *buttons, int dfltbutton, + const char *textfield, int ex_cmd) { int retval = 0; char *hotkeys; @@ -3612,7 +3619,7 @@ static int copy_char(const char *from, char *to, bool lowercase) /// corresponding button has a hotkey /// /// @return Pointer to memory allocated for storing hotkeys -static char *console_dialog_alloc(const char *message, char *buttons, bool has_hotkey[]) +static char *console_dialog_alloc(const char *message, const char *buttons, bool has_hotkey[]) { int lenhotkey = HOTK_LEN; // count first button has_hotkey[0] = false; @@ -3620,7 +3627,7 @@ static char *console_dialog_alloc(const char *message, char *buttons, bool has_h // Compute the size of memory to allocate. int len = 0; int idx = 0; - char *r = buttons; + const char *r = buttons; while (*r) { if (*r == DLG_BUTTON_SEP) { len += 3; // '\n' -> ', '; 'x' -> '(x)' @@ -3666,7 +3673,7 @@ static char *console_dialog_alloc(const char *message, char *buttons, bool has_h /// The hotkeys can be multi-byte characters, but without combining chars. /// /// @return an allocated string with hotkeys. -static char *msg_show_console_dialog(char *message, char *buttons, int dfltbutton) +static char *msg_show_console_dialog(const char *message, const char *buttons, int dfltbutton) FUNC_ATTR_NONNULL_RET { bool has_hotkey[HAS_HOTKEY_LEN] = { false }; @@ -3686,7 +3693,7 @@ static char *msg_show_console_dialog(char *message, char *buttons, int dfltbutto /// @param has_hotkey An element in this array is true if corresponding button /// has a hotkey /// @param[out] hotkeys_ptr Pointer to the memory location where hotkeys will be copied -static void copy_hotkeys_and_msg(const char *message, char *buttons, int default_button_idx, +static void copy_hotkeys_and_msg(const char *message, const char *buttons, int default_button_idx, const bool has_hotkey[], char *hotkeys_ptr) { *confirm_msg = '\n'; @@ -3709,7 +3716,7 @@ static void copy_hotkeys_and_msg(const char *message, char *buttons, int default } int idx = 0; - char *r = buttons; + const char *r = buttons; while (*r) { if (*r == DLG_BUTTON_SEP) { *msgp++ = ','; diff --git a/src/nvim/move.c b/src/nvim/move.c index f859294d65..cc02808e4c 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -1954,17 +1954,19 @@ void scroll_cursor_bot(int min_scroll, int set_topbot) int top_plines = plines_win_nofill(curwin, curwin->w_topline, false); int skip_lines = 0; int width1 = curwin->w_width_inner - curwin_col_off(); - int width2 = width1 + curwin_col_off2(); - // similar formula is used in curs_columns() - if (curwin->w_skipcol > width1) { - skip_lines += (curwin->w_skipcol - width1) / width2 + 1; - } else if (curwin->w_skipcol > 0) { - skip_lines = 1; - } + if (width1 > 0) { + int width2 = width1 + curwin_col_off2(); + // similar formula is used in curs_columns() + if (curwin->w_skipcol > width1) { + skip_lines += (curwin->w_skipcol - width1) / width2 + 1; + } else if (curwin->w_skipcol > 0) { + skip_lines = 1; + } - top_plines -= skip_lines; - if (top_plines > curwin->w_height_inner) { - scrolled += (top_plines - curwin->w_height_inner); + top_plines -= skip_lines; + if (top_plines > curwin->w_height_inner) { + scrolled += (top_plines - curwin->w_height_inner); + } } } } diff --git a/src/nvim/msgpack_rpc/unpacker.c b/src/nvim/msgpack_rpc/unpacker.c index 4128f91b6e..c3b1022db2 100644 --- a/src/nvim/msgpack_rpc/unpacker.c +++ b/src/nvim/msgpack_rpc/unpacker.c @@ -87,7 +87,7 @@ static void api_parse_enter(mpack_parser_t *parser, mpack_node_t *node) *result = NIL; break; case MPACK_TOKEN_BOOLEAN: - *result = BOOL(mpack_unpack_boolean(node->tok)); + *result = BOOLEAN_OBJ(mpack_unpack_boolean(node->tok)); break; case MPACK_TOKEN_SINT: *result = INTEGER_OBJ(mpack_unpack_sint(node->tok)); diff --git a/src/nvim/option.c b/src/nvim/option.c index bc24a73e82..0002329da3 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -394,7 +394,7 @@ void set_init_1(bool clean_arg) // NOTE: mlterm's author is being asked to 'set' a variable // instead of an environment variable due to inheritance. if (os_env_exists("MLTERM")) { - set_option_value_give_err("tbidi", 1L, NULL, 0); + set_option_value_give_err("tbidi", BOOLEAN_OPTVAL(true), 0); } didset_options2(); @@ -422,7 +422,7 @@ static void set_option_default(const int opt_idx, int opt_flags) // pointer to variable for current option vimoption_T *opt = &options[opt_idx]; - char *varp = get_varp_scope(opt, both ? OPT_LOCAL : opt_flags); + void *varp = get_varp_scope(opt, both ? OPT_LOCAL : opt_flags); uint32_t flags = opt->flags; if (varp != NULL) { // skip hidden option, nothing to do for it if (flags & P_STRING) { @@ -556,7 +556,7 @@ void set_number_default(char *name, long val) { int opt_idx = findoption(name); if (opt_idx >= 0) { - options[opt_idx].def_val = (char *)(intptr_t)val; + options[opt_idx].def_val = (void *)(intptr_t)val; } } @@ -747,7 +747,7 @@ void ex_set(exarg_T *eap) (void)do_set(eap->arg, flags); } -static void do_set_bool(int opt_idx, int opt_flags, int prefix, int nextchar, const char *varp, +static void do_set_bool(int opt_idx, int opt_flags, int prefix, int nextchar, const void *varp, const char **errmsg) { varnumber_T value; @@ -774,11 +774,11 @@ static void do_set_bool(int opt_idx, int opt_flags, int prefix, int nextchar, co } } - *errmsg = set_bool_option(opt_idx, (char *)varp, (int)value, opt_flags); + *errmsg = set_bool_option(opt_idx, (void *)varp, (int)value, opt_flags); } static void do_set_num(int opt_idx, int opt_flags, char **argp, int nextchar, const set_op_T op, - const char *varp, char *errbuf, size_t errbuflen, const char **errmsg) + const void *varp, char *errbuf, size_t errbuflen, const char **errmsg) { varnumber_T value; char *arg = *argp; @@ -794,10 +794,14 @@ static void do_set_num(int opt_idx, int opt_flags, char **argp, int nextchar, co if (nextchar == '&') { value = (long)(intptr_t)options[opt_idx].def_val; } else if (nextchar == '<') { - // For 'undolevels' NO_LOCAL_UNDOLEVEL means to - // use the global value. if ((long *)varp == &curbuf->b_p_ul && opt_flags == OPT_LOCAL) { + // for 'undolevels' NO_LOCAL_UNDOLEVEL means using the global value value = NO_LOCAL_UNDOLEVEL; + } else if (opt_flags == OPT_LOCAL + && ((long *)varp == &curwin->w_p_siso + || (long *)varp == &curwin->w_p_so)) { + // for 'scrolloff'/'sidescrolloff' -1 means using the global value + value = -1; } else { value = *(long *)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL); } @@ -834,7 +838,7 @@ static void do_set_num(int opt_idx, int opt_flags, char **argp, int nextchar, co if (op == OP_REMOVING) { value = *(long *)varp - value; } - *errmsg = set_num_option(opt_idx, (char *)varp, (long)value, + *errmsg = set_num_option(opt_idx, (void *)varp, (long)value, errbuf, errbuflen, opt_flags); } @@ -1080,7 +1084,7 @@ static void stropt_remove_dupflags(char *newval, uint32_t flags) /// set {opt}< /// set {opt}={val} /// set {opt}:{val} -static char *stropt_get_newval(int nextchar, int opt_idx, char **argp, char *varp, +static char *stropt_get_newval(int nextchar, int opt_idx, char **argp, void *varp, char **origval_arg, char **origval_l_arg, char **origval_g_arg, char **oldval_arg, set_op_T *op_arg, uint32_t flags) { @@ -1162,12 +1166,12 @@ static char *stropt_get_newval(int nextchar, int opt_idx, char **argp, char *var /// Part of do_set() for string options. static void do_set_option_string(int opt_idx, int opt_flags, char **argp, int nextchar, - set_op_T op_arg, uint32_t flags, char *varp_arg, char *errbuf, + set_op_T op_arg, uint32_t flags, void *varp_arg, char *errbuf, size_t errbuflen, int *value_checked, const char **errmsg) { char *arg = *argp; set_op_T op = op_arg; - char *varp = varp_arg; + void *varp = varp_arg; char *origval_l = NULL; char *origval_g = NULL; @@ -1400,7 +1404,7 @@ static int validate_opt_idx(win_T *win, int opt_idx, int opt_flags, uint32_t fla } static void do_set_option_value(int opt_idx, int opt_flags, char **argp, int prefix, int nextchar, - set_op_T op, uint32_t flags, char *varp, char *errbuf, + set_op_T op, uint32_t flags, void *varp, char *errbuf, size_t errbuflen, const char **errmsg) { int value_checked = false; @@ -1463,7 +1467,7 @@ static void do_set_option(int opt_flags, char **argp, bool *did_show, char *errb } uint32_t flags; // flags for current option - char *varp = NULL; // pointer to variable for current option + void *varp = NULL; // pointer to variable for current option if (opt_idx >= 0) { if (options[opt_idx].var == NULL) { // hidden option: skip @@ -2424,7 +2428,7 @@ static const char *did_set_arabic(optset_T *args) p_deco = true; // Force-set the necessary keymap for arabic. - errmsg = set_option_value("keymap", 0L, "arabic", OPT_LOCAL); + errmsg = set_option_value("keymap", STATIC_CSTR_AS_OPTVAL("arabic"), OPT_LOCAL); } else { // 'arabic' is reset, handle various sub-settings. if (!p_tbidi) { @@ -2933,7 +2937,7 @@ static const char *check_num_option_bounds(long *pp, long old_value, long old_Ro /// @param[in] opt_flags OPT_LOCAL, OPT_GLOBAL or OPT_MODELINE. /// /// @return NULL on success, error message on error. -static const char *set_num_option(int opt_idx, char *varp, long value, char *errbuf, +static const char *set_num_option(int opt_idx, void *varp, long value, char *errbuf, size_t errbuflen, int opt_flags) { const char *errmsg = NULL; @@ -3339,7 +3343,7 @@ void set_tty_background(const char *value) ? "autocmd VimEnter * ++once ++nested :lua if not vim.api.nvim_get_option_info2('bg', {}).was_set then vim.o.bg = 'light' end" : "autocmd VimEnter * ++once ++nested :lua if not vim.api.nvim_get_option_info2('bg', {}).was_set then vim.o.bg = 'dark' end"); } else { - set_option_value_give_err("bg", 0L, value, 0); + set_option_value_give_err("bg", CSTR_AS_OPTVAL((char *)value), 0); reset_option_was_set("bg"); } } @@ -3355,32 +3359,153 @@ int findoption(const char *const arg) return findoption_len(arg, strlen(arg)); } +void optval_free(OptVal o) +{ + switch (o.type) { + case kOptValTypeNil: + case kOptValTypeBoolean: + case kOptValTypeNumber: + break; + case kOptValTypeString: + api_free_string(o.data.string); + break; + } +} + +OptVal optval_copy(OptVal o) +{ + switch (o.type) { + case kOptValTypeNil: + case kOptValTypeBoolean: + case kOptValTypeNumber: + return o; + case kOptValTypeString: + return STRING_OPTVAL(copy_string(o.data.string, NULL)); + default: + abort(); + } +} + +// Match type of OptVal with the type of the target option. Returns true if the types match and +// false otherwise. +static bool optval_match_type(OptVal o, int opt_idx) +{ + assert(opt_idx >= 0); + uint32_t flags = options[opt_idx].flags; + + switch (o.type) { + case kOptValTypeNil: + return false; + case kOptValTypeBoolean: + return flags & P_BOOL; + case kOptValTypeNumber: + return flags & P_NUM; + case kOptValTypeString: + return flags & P_STRING; + default: + abort(); + } +} + +// Return C-string representation of OptVal. Caller must free the returned C-string. +static char *optval_to_cstr(OptVal o) +{ + switch (o.type) { + case kOptValTypeNil: + return xstrdup(""); + case kOptValTypeBoolean: + return xstrdup(o.data.boolean ? "true" : "false"); + case kOptValTypeNumber: { + char *buf = xmalloc(NUMBUFLEN); + snprintf(buf, NUMBUFLEN, "%" PRId64, o.data.number); + return buf; + } + case kOptValTypeString: { + char *buf = xmalloc(o.data.string.size + 3); + snprintf(buf, o.data.string.size + 3, "\"%s\"", o.data.string.data); + return buf; + } + default: + abort(); + } +} + +// Get an allocated string containing a list of valid types for an option. +// For options with a singular type, it returns the name of the type. For options with multiple +// possible types, it returns a slash separated list of types. For example, if an option can be a +// number, boolean or string, the function returns "Number/Boolean/String" +static char *option_get_valid_types(int opt_idx) +{ + uint32_t flags = options[opt_idx].flags; + uint32_t type_count = 0; + + StringBuilder str = KV_INITIAL_VALUE; + kv_resize(str, 32); + +#define OPTION_ADD_TYPE(typename) \ + do { \ + if (type_count == 0) { \ + kv_concat(str, typename); \ + } else { \ + kv_printf(str, "/%s", typename); \ + } \ + type_count++; \ + } while (0); + + if (flags & P_NUM) { + OPTION_ADD_TYPE("Number"); + } + if (flags & P_BOOL) { + OPTION_ADD_TYPE("Boolean"); + } + if (flags & P_STRING) { + OPTION_ADD_TYPE("String"); + } + + if (type_count == 0) { + abort(); + } + + // Ensure that the string is NUL-terminated. + kv_push(str, NUL); + return str.items; + +#undef OPTION_ADD_TYPE +} + /// Gets the value for an option. /// -/// @param stringval NULL when only checking existence -/// @param flagsp set to the option flags (P_xxxx) (if not NULL) +/// @param[in] name Option name. +/// @param[out] flagsp Set to the option flags (P_xxxx) (if not NULL). +/// @param[in] scope Option scope (can be OPT_LOCAL, OPT_GLOBAL or a combination). +/// @param[out] hidden Whether option is hidden. /// -/// @returns: -/// Number option: gov_number, *numval gets value. -/// Tottle option: gov_bool, *numval gets value. -/// String option: gov_string, *stringval gets allocated string. -/// Hidden Number option: gov_hidden_number. -/// Hidden Toggle option: gov_hidden_bool. -/// Hidden String option: gov_hidden_string. -/// Unknown option: gov_unknown. -getoption_T get_option_value(const char *name, long *numval, char **stringval, uint32_t *flagsp, - int scope) +/// @return Option value. Returns NIL_OPTVAL for invalid options. Return value must be freed by +/// caller. +OptVal get_option_value(const char *name, uint32_t *flagsp, int scope, bool *hidden) { - if (get_tty_option(name, stringval)) { - return gov_string; + // Make sure that hidden and flagsp are never returned uninitialized + if (hidden != NULL) { + *hidden = false; + } + if (flagsp != NULL) { + *flagsp = 0; + } + + char *str; + if (get_tty_option(name, &str)) { + return CSTR_AS_OPTVAL(str); } int opt_idx = findoption(name); if (opt_idx < 0) { // option not in the table - return gov_unknown; + return NIL_OPTVAL; } - char *varp = get_varp_scope(&(options[opt_idx]), scope); + void *varp = get_varp_scope(&(options[opt_idx]), scope); + if (hidden != NULL) { + *hidden = varp == NULL; + } if (flagsp != NULL) { // Return the P_xxxx option flags. @@ -3388,30 +3513,23 @@ getoption_T get_option_value(const char *name, long *numval, char **stringval, u } if (options[opt_idx].flags & P_STRING) { - if (varp == NULL) { // hidden option - return gov_hidden_string; - } - if (stringval != NULL) { - *stringval = xstrdup(*(char **)(varp)); - } - return gov_string; + return varp == NULL ? STRING_OPTVAL(STRING_INIT) : CSTR_TO_OPTVAL(*(char **)(varp)); } - if (varp == NULL) { // hidden option - return (options[opt_idx].flags & P_NUM) ? gov_hidden_number : gov_hidden_bool; - } if (options[opt_idx].flags & P_NUM) { - *numval = *(long *)varp; + return NUMBER_OPTVAL(varp == NULL ? 0 : (*(long *)varp)); } else { // Special case: 'modified' is b_changed, but we also want to consider // it set when 'ff' or 'fenc' changed. - if ((int *)varp == &curbuf->b_changed) { - *numval = curbufIsChanged(); + if (varp == NULL) { + return BOOLEAN_OPTVAL(false); + } else if ((int *)varp == &curbuf->b_changed) { + return BOOLEAN_OPTVAL(curbufIsChanged()); } else { - *numval = (long)(*(int *)varp); + int n = *(int *)varp; + return BOOLEAN_OPTVAL(n == 0 ? kFalse : (n >= 1 ? kTrue : kNone)); } } - return (options[opt_idx].flags & P_NUM) ? gov_number : gov_bool; } // Returns the option attributes and its value. Unlike the above function it @@ -3485,7 +3603,7 @@ int get_option_value_strict(char *name, int64_t *numval, char **stringval, int o return rv; } - char *varp = NULL; + void *varp = NULL; if (opt_type == SREQ_GLOBAL) { if (p->var == VAR_WIN) { @@ -3544,20 +3662,25 @@ vimoption_T *get_option(int opt_idx) /// Set the value of an option /// -/// @param[in] name Option name. -/// @param[in] number New value for the number or boolean option. -/// @param[in] string New value for string option. +/// @param[in] name Option name. +/// @param[in] value Option value. If NIL_OPTVAL, the option value is cleared. /// @param[in] opt_flags Flags: OPT_LOCAL, OPT_GLOBAL, or 0 (both). /// If OPT_CLEAR is set, the value of the option /// is cleared (the exact semantics of this depend /// on the option). /// /// @return NULL on success, an untranslated error message on error. -const char *set_option_value(const char *const name, const long number, const char *const string, - const int opt_flags) +const char *set_option_value(const char *const name, const OptVal value, int opt_flags) FUNC_ATTR_NONNULL_ARG(1) { - static char errbuf[80]; + static const char *optval_type_names[] = { + [kOptValTypeNil] = "Nil", + [kOptValTypeBoolean] = "Boolean", + [kOptValTypeNumber] = "Number", + [kOptValTypeString] = "String" + }; + + static char errbuf[IOSIZE]; if (is_tty_option(name)) { return NULL; // Fail silently; many old vimrcs set t_xx options. @@ -3565,71 +3688,97 @@ const char *set_option_value(const char *const name, const long number, const ch int opt_idx = findoption(name); if (opt_idx < 0) { - semsg(_("E355: Unknown option: %s"), name); - return NULL; + snprintf(errbuf, IOSIZE, _(e_unknown_option2), name); + return errbuf; } uint32_t flags = options[opt_idx].flags; // Disallow changing some options in the sandbox if (sandbox > 0 && (flags & P_SECURE)) { - emsg(_(e_sandbox)); - return NULL; + return _(e_sandbox); } - if (flags & P_STRING) { - const char *s = string; - if (s == NULL || opt_flags & OPT_CLEAR) { - s = ""; - } - return set_string_option(opt_idx, s, opt_flags, errbuf, sizeof(errbuf)); - } - - char *varp = get_varp_scope(&(options[opt_idx]), opt_flags); + void *varp = get_varp_scope(&(options[opt_idx]), opt_flags); if (varp == NULL) { // hidden option is not changed return NULL; } - if (number == 0 && string != NULL) { - int idx; - - // Either we are given a string or we are setting option - // to zero. - for (idx = 0; string[idx] == '0'; idx++) {} - if (string[idx] != NUL || idx == 0) { - // There's another character after zeros or the string - // is empty. In both cases, we are trying to set a - // num option using a string. - semsg(_("E521: Number required: &%s = '%s'"), - name, string); - return NULL; // do nothing as we hit an error - } - } - long numval = number; - if (opt_flags & OPT_CLEAR) { - if ((int *)varp == &curbuf->b_p_ar) { - numval = -1; - } else if ((long *)varp == &curbuf->b_p_ul) { - numval = NO_LOCAL_UNDOLEVEL; - } else if ((long *)varp == &curwin->w_p_so || (long *)varp == &curwin->w_p_siso) { - numval = -1; - } else { - char *s = NULL; - (void)get_option_value(name, &numval, &s, NULL, OPT_GLOBAL); + const char *errmsg = NULL; + // Copy the value so we can modify the copy. + OptVal v = optval_copy(value); + + if (v.type == kOptValTypeNil) { + opt_flags |= OPT_CLEAR; + + // Change the type of the OptVal to the type used by the option so that it can be cleared. + // TODO(famiu): Clean up all of this after set_(num|bool|string)_option() is unified. + if (flags & P_BOOL) { + v.type = kOptValTypeBoolean; + } else if (flags & P_NUM) { + v.type = kOptValTypeNumber; + } else if (flags & P_STRING) { + v.type = kOptValTypeString; + } + } else if (!optval_match_type(v, opt_idx)) { + char *rep = optval_to_cstr(v); + char *valid_types = option_get_valid_types(opt_idx); + snprintf(errbuf, IOSIZE, _("Invalid value for option '%s': expected %s, got %s %s"), + name, valid_types, optval_type_names[v.type], rep); + xfree(rep); + xfree(valid_types); + errmsg = errbuf; + goto end; + } + + switch (v.type) { + case kOptValTypeNil: + abort(); // This will never happen. + case kOptValTypeBoolean: { + if (opt_flags & OPT_CLEAR) { + if ((int *)varp == &curbuf->b_p_ar) { + v.data.boolean = kNone; + } else { + v = get_option_value(name, NULL, OPT_GLOBAL, NULL); + } } + errmsg = set_bool_option(opt_idx, varp, (int)v.data.boolean, opt_flags); + break; } - if (flags & P_NUM) { - return set_num_option(opt_idx, varp, numval, errbuf, sizeof(errbuf), opt_flags); + case kOptValTypeNumber: { + if (opt_flags & OPT_CLEAR) { + if ((long *)varp == &curbuf->b_p_ul) { + v.data.number = NO_LOCAL_UNDOLEVEL; + } else if ((long *)varp == &curwin->w_p_so || (long *)varp == &curwin->w_p_siso) { + v.data.number = -1; + } else { + v = get_option_value(name, NULL, OPT_GLOBAL, NULL); + } + } + errmsg = set_num_option(opt_idx, varp, (long)v.data.number, errbuf, sizeof(errbuf), opt_flags); + break; } - return set_bool_option(opt_idx, varp, (int)numval, opt_flags); + case kOptValTypeString: { + const char *s = v.data.string.data; + if (s == NULL || opt_flags & OPT_CLEAR) { + s = ""; + } + errmsg = set_string_option(opt_idx, s, opt_flags, errbuf, sizeof(errbuf)); + break; + } + } + +end: + optval_free(v); // Free the copied OptVal. + return errmsg; } /// Call set_option_value() and when an error is returned report it. /// /// @param opt_flags OPT_LOCAL or 0 (both) -void set_option_value_give_err(const char *name, long number, const char *string, int opt_flags) +void set_option_value_give_err(const char *name, OptVal value, int opt_flags) { - const char *errmsg = set_option_value(name, number, string, opt_flags); + const char *errmsg = set_option_value(name, value, opt_flags); if (errmsg != NULL) { emsg(_(errmsg)); @@ -3642,14 +3791,6 @@ bool is_option_allocated(const char *name) return idx >= 0 && (options[idx].flags & P_ALLOCED); } -/// Return true if "name" is a string option. -/// Returns false if option "name" does not exist. -bool is_string_option(const char *name) -{ - int idx = findoption(name); - return idx >= 0 && (options[idx].flags & P_STRING); -} - // Translate a string like "t_xx", "<t_xx>" or "<S-Tab>" to a key number. // When "has_lt" is true there is a '<' before "*arg_arg". // Returns 0 when the key is not recognized. @@ -3712,7 +3853,7 @@ static void showoptions(bool all, int opt_flags) continue; } - char *varp = NULL; + void *varp = NULL; if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) != 0) { if (p->indir != PV_NONE) { varp = get_varp_scope(p, opt_flags); @@ -3771,7 +3912,7 @@ static void showoptions(bool all, int opt_flags) } /// Return true if option "p" has its default value. -static int optval_default(vimoption_T *p, const char *varp) +static int optval_default(vimoption_T *p, const void *varp) { if (varp == NULL) { return true; // hidden option is always at default @@ -3823,7 +3964,7 @@ static void showoneopt(vimoption_T *p, int opt_flags) silent_mode = false; info_message = true; // use os_msg(), not os_errmsg() - char *varp = get_varp_scope(p, opt_flags); + void *varp = get_varp_scope(p, opt_flags); // for 'modified' we also need to check if 'ff' or 'fenc' changed. if ((p->flags & P_BOOL) && ((int *)varp == &curbuf->b_changed @@ -3890,7 +4031,7 @@ int makeset(FILE *fd, int opt_flags, int local_only) continue; } - char *varp = get_varp_scope(p, opt_flags); // currently used value + void *varp = get_varp_scope(p, opt_flags); // currently used value // Hidden options are never written. if (!varp) { continue; @@ -3901,12 +4042,12 @@ int makeset(FILE *fd, int opt_flags, int local_only) } if ((opt_flags & OPT_SKIPRTP) - && (p->var == (char *)&p_rtp || p->var == (char *)&p_pp)) { + && (p->var == &p_rtp || p->var == &p_pp)) { continue; } int round = 2; - char *varp_local = NULL; // fresh value + void *varp_local = NULL; // fresh value if (p->indir != PV_NONE) { if (p->var == VAR_WIN) { // skip window-local option when only doing globals @@ -3916,7 +4057,7 @@ int makeset(FILE *fd, int opt_flags, int local_only) // When fresh value of window-local option is not at the // default, need to write it too. if (!(opt_flags & OPT_GLOBAL) && !local_only) { - char *varp_fresh = get_varp_scope(p, OPT_GLOBAL); // local value + void *varp_fresh = get_varp_scope(p, OPT_GLOBAL); // local value if (!optval_default(p, varp_fresh)) { round = 1; varp_local = varp; @@ -4060,7 +4201,7 @@ static int put_setnum(FILE *fd, char *cmd, char *name, long *valuep) return FAIL; } long wc; - if (wc_use_keyname((char *)valuep, &wc)) { + if (wc_use_keyname(valuep, &wc)) { // print 'wildchar' and 'wildcharm' as a key name if (fputs(get_special_key_name((int)wc, 0), fd) < 0) { return FAIL; @@ -4195,7 +4336,7 @@ void unset_global_local_option(char *name, void *from) } } -char *get_varp_scope_from(vimoption_T *p, int scope, buf_T *buf, win_T *win) +void *get_varp_scope_from(vimoption_T *p, int scope, buf_T *buf, win_T *win) { if ((scope & OPT_GLOBAL) && p->indir != PV_NONE) { if (p->var == VAR_WIN) { @@ -4206,61 +4347,61 @@ char *get_varp_scope_from(vimoption_T *p, int scope, buf_T *buf, win_T *win) if ((scope & OPT_LOCAL) && ((int)p->indir & PV_BOTH)) { switch ((int)p->indir) { case PV_FP: - return (char *)&(buf->b_p_fp); + return &(buf->b_p_fp); case PV_EFM: - return (char *)&(buf->b_p_efm); + return &(buf->b_p_efm); case PV_GP: - return (char *)&(buf->b_p_gp); + return &(buf->b_p_gp); case PV_MP: - return (char *)&(buf->b_p_mp); + return &(buf->b_p_mp); case PV_EP: - return (char *)&(buf->b_p_ep); + return &(buf->b_p_ep); case PV_KP: - return (char *)&(buf->b_p_kp); + return &(buf->b_p_kp); case PV_PATH: - return (char *)&(buf->b_p_path); + return &(buf->b_p_path); case PV_AR: - return (char *)&(buf->b_p_ar); + return &(buf->b_p_ar); case PV_TAGS: - return (char *)&(buf->b_p_tags); + return &(buf->b_p_tags); case PV_TC: - return (char *)&(buf->b_p_tc); + return &(buf->b_p_tc); case PV_SISO: - return (char *)&(win->w_p_siso); + return &(win->w_p_siso); case PV_SO: - return (char *)&(win->w_p_so); + return &(win->w_p_so); case PV_DEF: - return (char *)&(buf->b_p_def); + return &(buf->b_p_def); case PV_INC: - return (char *)&(buf->b_p_inc); + return &(buf->b_p_inc); case PV_DICT: - return (char *)&(buf->b_p_dict); + return &(buf->b_p_dict); case PV_TSR: - return (char *)&(buf->b_p_tsr); + return &(buf->b_p_tsr); case PV_TSRFU: - return (char *)&(buf->b_p_tsrfu); + return &(buf->b_p_tsrfu); case PV_TFU: - return (char *)&(buf->b_p_tfu); + return &(buf->b_p_tfu); case PV_SBR: - return (char *)&(win->w_p_sbr); + return &(win->w_p_sbr); case PV_STL: - return (char *)&(win->w_p_stl); + return &(win->w_p_stl); case PV_WBR: - return (char *)&(win->w_p_wbr); + return &(win->w_p_wbr); case PV_UL: - return (char *)&(buf->b_p_ul); + return &(buf->b_p_ul); case PV_LW: - return (char *)&(buf->b_p_lw); + return &(buf->b_p_lw); case PV_BKC: - return (char *)&(buf->b_p_bkc); + return &(buf->b_p_bkc); case PV_MENC: - return (char *)&(buf->b_p_menc); + return &(buf->b_p_menc); case PV_FCS: - return (char *)&(win->w_p_fcs); + return &(win->w_p_fcs); case PV_LCS: - return (char *)&(win->w_p_lcs); + return &(win->w_p_lcs); case PV_VE: - return (char *)&(win->w_p_ve); + return &(win->w_p_ve); } return NULL; // "cannot happen" } @@ -4270,19 +4411,19 @@ char *get_varp_scope_from(vimoption_T *p, int scope, buf_T *buf, win_T *win) /// Get pointer to option variable, depending on local or global scope. /// /// @param scope can be OPT_LOCAL, OPT_GLOBAL or a combination. -char *get_varp_scope(vimoption_T *p, int scope) +void *get_varp_scope(vimoption_T *p, int scope) { return get_varp_scope_from(p, scope, curbuf, curwin); } /// Get pointer to option variable at 'opt_idx', depending on local or global /// scope. -char *get_option_varp_scope_from(int opt_idx, int scope, buf_T *buf, win_T *win) +void *get_option_varp_scope_from(int opt_idx, int scope, buf_T *buf, win_T *win) { return get_varp_scope_from(&(options[opt_idx]), scope, buf, win); } -static char *get_varp_from(vimoption_T *p, buf_T *buf, win_T *win) +static void *get_varp_from(vimoption_T *p, buf_T *buf, win_T *win) { // hidden option, always return NULL if (p->var == NULL) { @@ -4295,311 +4436,284 @@ static char *get_varp_from(vimoption_T *p, buf_T *buf, win_T *win) // global option with local value: use local value if it's been set case PV_EP: - return *buf->b_p_ep != NUL - ? (char *)&buf->b_p_ep : p->var; + return *buf->b_p_ep != NUL ? &buf->b_p_ep : p->var; case PV_KP: - return *buf->b_p_kp != NUL - ? (char *)&buf->b_p_kp : p->var; + return *buf->b_p_kp != NUL ? &buf->b_p_kp : p->var; case PV_PATH: - return *buf->b_p_path != NUL - ? (char *)&(buf->b_p_path) : p->var; + return *buf->b_p_path != NUL ? &(buf->b_p_path) : p->var; case PV_AR: - return buf->b_p_ar >= 0 - ? (char *)&(buf->b_p_ar) : p->var; + return buf->b_p_ar >= 0 ? &(buf->b_p_ar) : p->var; case PV_TAGS: - return *buf->b_p_tags != NUL - ? (char *)&(buf->b_p_tags) : p->var; + return *buf->b_p_tags != NUL ? &(buf->b_p_tags) : p->var; case PV_TC: - return *buf->b_p_tc != NUL - ? (char *)&(buf->b_p_tc) : p->var; + return *buf->b_p_tc != NUL ? &(buf->b_p_tc) : p->var; case PV_SISO: - return win->w_p_siso >= 0 - ? (char *)&(win->w_p_siso) : p->var; + return win->w_p_siso >= 0 ? &(win->w_p_siso) : p->var; case PV_SO: - return win->w_p_so >= 0 - ? (char *)&(win->w_p_so) : p->var; + return win->w_p_so >= 0 ? &(win->w_p_so) : p->var; case PV_BKC: - return *buf->b_p_bkc != NUL - ? (char *)&(buf->b_p_bkc) : p->var; + return *buf->b_p_bkc != NUL ? &(buf->b_p_bkc) : p->var; case PV_DEF: - return *buf->b_p_def != NUL - ? (char *)&(buf->b_p_def) : p->var; + return *buf->b_p_def != NUL ? &(buf->b_p_def) : p->var; case PV_INC: - return *buf->b_p_inc != NUL - ? (char *)&(buf->b_p_inc) : p->var; + return *buf->b_p_inc != NUL ? &(buf->b_p_inc) : p->var; case PV_DICT: - return *buf->b_p_dict != NUL - ? (char *)&(buf->b_p_dict) : p->var; + return *buf->b_p_dict != NUL ? &(buf->b_p_dict) : p->var; case PV_TSR: - return *buf->b_p_tsr != NUL - ? (char *)&(buf->b_p_tsr) : p->var; + return *buf->b_p_tsr != NUL ? &(buf->b_p_tsr) : p->var; case PV_TSRFU: - return *buf->b_p_tsrfu != NUL - ? (char *)&(buf->b_p_tsrfu) : p->var; + return *buf->b_p_tsrfu != NUL ? &(buf->b_p_tsrfu) : p->var; case PV_FP: - return *buf->b_p_fp != NUL - ? (char *)&(buf->b_p_fp) : p->var; + return *buf->b_p_fp != NUL ? &(buf->b_p_fp) : p->var; case PV_EFM: - return *buf->b_p_efm != NUL - ? (char *)&(buf->b_p_efm) : p->var; + return *buf->b_p_efm != NUL ? &(buf->b_p_efm) : p->var; case PV_GP: - return *buf->b_p_gp != NUL - ? (char *)&(buf->b_p_gp) : p->var; + return *buf->b_p_gp != NUL ? &(buf->b_p_gp) : p->var; case PV_MP: - return *buf->b_p_mp != NUL - ? (char *)&(buf->b_p_mp) : p->var; + return *buf->b_p_mp != NUL ? &(buf->b_p_mp) : p->var; case PV_SBR: - return *win->w_p_sbr != NUL - ? (char *)&(win->w_p_sbr) : p->var; + return *win->w_p_sbr != NUL ? &(win->w_p_sbr) : p->var; case PV_STL: - return *win->w_p_stl != NUL - ? (char *)&(win->w_p_stl) : p->var; + return *win->w_p_stl != NUL ? &(win->w_p_stl) : p->var; case PV_WBR: - return *win->w_p_wbr != NUL - ? (char *)&(win->w_p_wbr) : p->var; + return *win->w_p_wbr != NUL ? &(win->w_p_wbr) : p->var; case PV_UL: - return buf->b_p_ul != NO_LOCAL_UNDOLEVEL - ? (char *)&(buf->b_p_ul) : p->var; + return buf->b_p_ul != NO_LOCAL_UNDOLEVEL ? &(buf->b_p_ul) : p->var; case PV_LW: - return *buf->b_p_lw != NUL - ? (char *)&(buf->b_p_lw) : p->var; + return *buf->b_p_lw != NUL ? &(buf->b_p_lw) : p->var; case PV_MENC: - return *buf->b_p_menc != NUL - ? (char *)&(buf->b_p_menc) : p->var; + return *buf->b_p_menc != NUL ? &(buf->b_p_menc) : p->var; case PV_FCS: - return *win->w_p_fcs != NUL - ? (char *)&(win->w_p_fcs) : p->var; + return *win->w_p_fcs != NUL ? &(win->w_p_fcs) : p->var; case PV_LCS: - return *win->w_p_lcs != NUL - ? (char *)&(win->w_p_lcs) : p->var; + return *win->w_p_lcs != NUL ? &(win->w_p_lcs) : p->var; case PV_VE: - return *win->w_p_ve != NUL - ? (char *)&win->w_p_ve : p->var; + return *win->w_p_ve != NUL ? &win->w_p_ve : p->var; case PV_ARAB: - return (char *)&(win->w_p_arab); + return &(win->w_p_arab); case PV_LIST: - return (char *)&(win->w_p_list); + return &(win->w_p_list); case PV_SPELL: - return (char *)&(win->w_p_spell); + return &(win->w_p_spell); case PV_CUC: - return (char *)&(win->w_p_cuc); + return &(win->w_p_cuc); case PV_CUL: - return (char *)&(win->w_p_cul); + return &(win->w_p_cul); case PV_CULOPT: - return (char *)&(win->w_p_culopt); + return &(win->w_p_culopt); case PV_CC: - return (char *)&(win->w_p_cc); + return &(win->w_p_cc); case PV_DIFF: - return (char *)&(win->w_p_diff); + return &(win->w_p_diff); case PV_FDC: - return (char *)&(win->w_p_fdc); + return &(win->w_p_fdc); case PV_FEN: - return (char *)&(win->w_p_fen); + return &(win->w_p_fen); case PV_FDI: - return (char *)&(win->w_p_fdi); + return &(win->w_p_fdi); case PV_FDL: - return (char *)&(win->w_p_fdl); + return &(win->w_p_fdl); case PV_FDM: - return (char *)&(win->w_p_fdm); + return &(win->w_p_fdm); case PV_FML: - return (char *)&(win->w_p_fml); + return &(win->w_p_fml); case PV_FDN: - return (char *)&(win->w_p_fdn); + return &(win->w_p_fdn); case PV_FDE: - return (char *)&(win->w_p_fde); + return &(win->w_p_fde); case PV_FDT: - return (char *)&(win->w_p_fdt); + return &(win->w_p_fdt); case PV_FMR: - return (char *)&(win->w_p_fmr); + return &(win->w_p_fmr); case PV_NU: - return (char *)&(win->w_p_nu); + return &(win->w_p_nu); case PV_RNU: - return (char *)&(win->w_p_rnu); + return &(win->w_p_rnu); case PV_NUW: - return (char *)&(win->w_p_nuw); + return &(win->w_p_nuw); case PV_WFH: - return (char *)&(win->w_p_wfh); + return &(win->w_p_wfh); case PV_WFW: - return (char *)&(win->w_p_wfw); + return &(win->w_p_wfw); case PV_PVW: - return (char *)&(win->w_p_pvw); + return &(win->w_p_pvw); case PV_RL: - return (char *)&(win->w_p_rl); + return &(win->w_p_rl); case PV_RLC: - return (char *)&(win->w_p_rlc); + return &(win->w_p_rlc); case PV_SCROLL: - return (char *)&(win->w_p_scr); + return &(win->w_p_scr); case PV_SMS: - return (char *)&(win->w_p_sms); + return &(win->w_p_sms); case PV_WRAP: - return (char *)&(win->w_p_wrap); + return &(win->w_p_wrap); case PV_LBR: - return (char *)&(win->w_p_lbr); + return &(win->w_p_lbr); case PV_BRI: - return (char *)&(win->w_p_bri); + return &(win->w_p_bri); case PV_BRIOPT: - return (char *)&(win->w_p_briopt); + return &(win->w_p_briopt); case PV_SCBIND: - return (char *)&(win->w_p_scb); + return &(win->w_p_scb); case PV_CRBIND: - return (char *)&(win->w_p_crb); + return &(win->w_p_crb); case PV_COCU: - return (char *)&(win->w_p_cocu); + return &(win->w_p_cocu); case PV_COLE: - return (char *)&(win->w_p_cole); + return &(win->w_p_cole); case PV_AI: - return (char *)&(buf->b_p_ai); + return &(buf->b_p_ai); case PV_BIN: - return (char *)&(buf->b_p_bin); + return &(buf->b_p_bin); case PV_BOMB: - return (char *)&(buf->b_p_bomb); + return &(buf->b_p_bomb); case PV_BH: - return (char *)&(buf->b_p_bh); + return &(buf->b_p_bh); case PV_BT: - return (char *)&(buf->b_p_bt); + return &(buf->b_p_bt); case PV_BL: - return (char *)&(buf->b_p_bl); + return &(buf->b_p_bl); case PV_CHANNEL: - return (char *)&(buf->b_p_channel); + return &(buf->b_p_channel); case PV_CI: - return (char *)&(buf->b_p_ci); + return &(buf->b_p_ci); case PV_CIN: - return (char *)&(buf->b_p_cin); + return &(buf->b_p_cin); case PV_CINK: - return (char *)&(buf->b_p_cink); + return &(buf->b_p_cink); case PV_CINO: - return (char *)&(buf->b_p_cino); + return &(buf->b_p_cino); case PV_CINSD: - return (char *)&(buf->b_p_cinsd); + return &(buf->b_p_cinsd); case PV_CINW: - return (char *)&(buf->b_p_cinw); + return &(buf->b_p_cinw); case PV_COM: - return (char *)&(buf->b_p_com); + return &(buf->b_p_com); case PV_CMS: - return (char *)&(buf->b_p_cms); + return &(buf->b_p_cms); case PV_CPT: - return (char *)&(buf->b_p_cpt); + return &(buf->b_p_cpt); #ifdef BACKSLASH_IN_FILENAME case PV_CSL: - return (char *)&(buf->b_p_csl); + return &(buf->b_p_csl); #endif case PV_CFU: - return (char *)&(buf->b_p_cfu); + return &(buf->b_p_cfu); case PV_OFU: - return (char *)&(buf->b_p_ofu); + return &(buf->b_p_ofu); case PV_EOF: - return (char *)&(buf->b_p_eof); + return &(buf->b_p_eof); case PV_EOL: - return (char *)&(buf->b_p_eol); + return &(buf->b_p_eol); case PV_FIXEOL: - return (char *)&(buf->b_p_fixeol); + return &(buf->b_p_fixeol); case PV_ET: - return (char *)&(buf->b_p_et); + return &(buf->b_p_et); case PV_FENC: - return (char *)&(buf->b_p_fenc); + return &(buf->b_p_fenc); case PV_FF: - return (char *)&(buf->b_p_ff); + return &(buf->b_p_ff); case PV_FT: - return (char *)&(buf->b_p_ft); + return &(buf->b_p_ft); case PV_FO: - return (char *)&(buf->b_p_fo); + return &(buf->b_p_fo); case PV_FLP: - return (char *)&(buf->b_p_flp); + return &(buf->b_p_flp); case PV_IMI: - return (char *)&(buf->b_p_iminsert); + return &(buf->b_p_iminsert); case PV_IMS: - return (char *)&(buf->b_p_imsearch); + return &(buf->b_p_imsearch); case PV_INF: - return (char *)&(buf->b_p_inf); + return &(buf->b_p_inf); case PV_ISK: - return (char *)&(buf->b_p_isk); + return &(buf->b_p_isk); case PV_INEX: - return (char *)&(buf->b_p_inex); + return &(buf->b_p_inex); case PV_INDE: - return (char *)&(buf->b_p_inde); + return &(buf->b_p_inde); case PV_INDK: - return (char *)&(buf->b_p_indk); + return &(buf->b_p_indk); case PV_FEX: - return (char *)&(buf->b_p_fex); + return &(buf->b_p_fex); case PV_LISP: - return (char *)&(buf->b_p_lisp); + return &(buf->b_p_lisp); case PV_LOP: - return (char *)&(buf->b_p_lop); + return &(buf->b_p_lop); case PV_ML: - return (char *)&(buf->b_p_ml); + return &(buf->b_p_ml); case PV_MPS: - return (char *)&(buf->b_p_mps); + return &(buf->b_p_mps); case PV_MA: - return (char *)&(buf->b_p_ma); + return &(buf->b_p_ma); case PV_MOD: - return (char *)&(buf->b_changed); + return &(buf->b_changed); case PV_NF: - return (char *)&(buf->b_p_nf); + return &(buf->b_p_nf); case PV_PI: - return (char *)&(buf->b_p_pi); + return &(buf->b_p_pi); case PV_QE: - return (char *)&(buf->b_p_qe); + return &(buf->b_p_qe); case PV_RO: - return (char *)&(buf->b_p_ro); + return &(buf->b_p_ro); case PV_SCBK: - return (char *)&(buf->b_p_scbk); + return &(buf->b_p_scbk); case PV_SI: - return (char *)&(buf->b_p_si); + return &(buf->b_p_si); case PV_STS: - return (char *)&(buf->b_p_sts); + return &(buf->b_p_sts); case PV_SUA: - return (char *)&(buf->b_p_sua); + return &(buf->b_p_sua); case PV_SWF: - return (char *)&(buf->b_p_swf); + return &(buf->b_p_swf); case PV_SMC: - return (char *)&(buf->b_p_smc); + return &(buf->b_p_smc); case PV_SYN: - return (char *)&(buf->b_p_syn); + return &(buf->b_p_syn); case PV_SPC: - return (char *)&(win->w_s->b_p_spc); + return &(win->w_s->b_p_spc); case PV_SPF: - return (char *)&(win->w_s->b_p_spf); + return &(win->w_s->b_p_spf); case PV_SPL: - return (char *)&(win->w_s->b_p_spl); + return &(win->w_s->b_p_spl); case PV_SPO: - return (char *)&(win->w_s->b_p_spo); + return &(win->w_s->b_p_spo); case PV_SW: - return (char *)&(buf->b_p_sw); + return &(buf->b_p_sw); case PV_TFU: - return (char *)&(buf->b_p_tfu); + return &(buf->b_p_tfu); case PV_TS: - return (char *)&(buf->b_p_ts); + return &(buf->b_p_ts); case PV_TW: - return (char *)&(buf->b_p_tw); + return &(buf->b_p_tw); case PV_UDF: - return (char *)&(buf->b_p_udf); + return &(buf->b_p_udf); case PV_WM: - return (char *)&(buf->b_p_wm); + return &(buf->b_p_wm); case PV_VSTS: - return (char *)&(buf->b_p_vsts); + return &(buf->b_p_vsts); case PV_VTS: - return (char *)&(buf->b_p_vts); + return &(buf->b_p_vts); case PV_KMAP: - return (char *)&(buf->b_p_keymap); + return &(buf->b_p_keymap); case PV_SCL: - return (char *)&(win->w_p_scl); + return &(win->w_p_scl); case PV_WINHL: - return (char *)&(win->w_p_winhl); + return &(win->w_p_winhl); case PV_WINBL: - return (char *)&(win->w_p_winbl); + return &(win->w_p_winbl); case PV_STC: - return (char *)&(win->w_p_stc); + return &(win->w_p_stc); default: iemsg(_("E356: get_varp ERROR")); } // always return a valid pointer to avoid a crash! - return (char *)&(buf->b_p_wm); + return &(buf->b_p_wm); } /// Get pointer to option variable. -static inline char *get_varp(vimoption_T *p) +static inline void *get_varp(vimoption_T *p) { return get_varp_from(p, curbuf, curwin); } @@ -5269,7 +5383,7 @@ void set_context_in_set_cmd(expand_T *xp, char *arg, int opt_flags) } // for 'spellsuggest' start at "file:" - if (options[opt_idx].var == (char *)&p_sps + if (options[opt_idx].var == &p_sps && strncmp(p, "file:", 5) == 0) { xp->xp_pattern = p + 5; break; @@ -5445,7 +5559,7 @@ void ExpandOldSetting(int *numMatches, char ***matches) /// @param opt_flags OPT_GLOBAL and/or OPT_LOCAL static void option_value2string(vimoption_T *opp, int scope) { - char *varp = get_varp_scope(opp, scope); + void *varp = get_varp_scope(opp, scope); if (opp->flags & P_NUM) { long wc = 0; @@ -5475,7 +5589,7 @@ static void option_value2string(vimoption_T *opp, int scope) /// Return true if "varp" points to 'wildchar' or 'wildcharm' and it can be /// printed as a keyname. /// "*wcp" is set to the value of the option if it's 'wildchar' or 'wildcharm'. -static int wc_use_keyname(const char *varp, long *wcp) +static int wc_use_keyname(const void *varp, long *wcp) { if (((long *)varp == &p_wc) || ((long *)varp == &p_wcm)) { *wcp = *(long *)varp; @@ -6063,7 +6177,7 @@ dict_T *get_winbuf_options(const int bufopt) if ((bufopt && (opt->indir & PV_BUF)) || (!bufopt && (opt->indir & PV_WIN))) { - char *varp = get_varp(opt); + void *varp = get_varp(opt); if (varp != NULL) { if (opt->flags & P_STRING) { @@ -6139,11 +6253,11 @@ static Dictionary vimoption2dict(vimoption_T *opt, int req_scope, buf_T *buf, wi PUT(dict, "scope", CSTR_TO_OBJ(scope)); // welcome to the jungle - PUT(dict, "global_local", BOOL(opt->indir & PV_BOTH)); - PUT(dict, "commalist", BOOL(opt->flags & P_COMMA)); - PUT(dict, "flaglist", BOOL(opt->flags & P_FLAGLIST)); + PUT(dict, "global_local", BOOLEAN_OBJ(opt->indir & PV_BOTH)); + PUT(dict, "commalist", BOOLEAN_OBJ(opt->flags & P_COMMA)); + PUT(dict, "flaglist", BOOLEAN_OBJ(opt->flags & P_FLAGLIST)); - PUT(dict, "was_set", BOOL(opt->flags & P_WAS_SET)); + PUT(dict, "was_set", BOOLEAN_OBJ(opt->flags & P_WAS_SET)); LastSet last_set = { .channel_id = 0 }; if (req_scope == OPT_GLOBAL) { @@ -6177,13 +6291,13 @@ static Dictionary vimoption2dict(vimoption_T *opt, int req_scope, buf_T *buf, wi def = INTEGER_OBJ((Integer)(intptr_t)def_val); } else if (opt->flags & P_BOOL) { type = "boolean"; - def = BOOL((intptr_t)def_val); + def = BOOLEAN_OBJ((intptr_t)def_val); } else { type = ""; def = NIL; } PUT(dict, "type", CSTR_TO_OBJ(type)); PUT(dict, "default", def); - PUT(dict, "allows_duplicates", BOOL(!(opt->flags & P_NODUP))); + PUT(dict, "allows_duplicates", BOOLEAN_OBJ(!(opt->flags & P_NODUP))); return dict; } diff --git a/src/nvim/option.h b/src/nvim/option.h index 636257bfe8..4b8329ad1c 100644 --- a/src/nvim/option.h +++ b/src/nvim/option.h @@ -1,19 +1,9 @@ #ifndef NVIM_OPTION_H #define NVIM_OPTION_H +#include "nvim/api/private/helpers.h" #include "nvim/ex_cmds_defs.h" -/// Returned by get_option_value(). -typedef enum { - gov_unknown, - gov_bool, - gov_number, - gov_string, - gov_hidden_bool, - gov_hidden_number, - gov_hidden_string, -} getoption_T; - // flags for buf_copy_options() #define BCO_ENTER 1 // going to enter the buffer #define BCO_ALWAYS 2 // always copy the options @@ -21,6 +11,17 @@ typedef enum { #define MAX_NUMBERWIDTH 20 // used for 'numberwidth' and 'statuscolumn' +// OptVal helper macros. +#define NIL_OPTVAL ((OptVal) { .type = kOptValTypeNil }) +#define BOOLEAN_OPTVAL(b) ((OptVal) { .type = kOptValTypeBoolean, .data.boolean = b }) +#define NUMBER_OPTVAL(n) ((OptVal) { .type = kOptValTypeNumber, .data.number = n }) +#define STRING_OPTVAL(s) ((OptVal) { .type = kOptValTypeString, .data.string = s }) + +#define CSTR_AS_OPTVAL(s) STRING_OPTVAL(cstr_as_string(s)) +#define CSTR_TO_OPTVAL(s) STRING_OPTVAL(cstr_to_string(s)) +#define STATIC_CSTR_AS_OPTVAL(s) STRING_OPTVAL(STATIC_CSTR_AS_STRING(s)) +#define STATIC_CSTR_TO_OPTVAL(s) STRING_OPTVAL(STATIC_CSTR_TO_STRING(s)) + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "option.h.generated.h" #endif diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h index bbf009213d..35687a19b7 100644 --- a/src/nvim/option_defs.h +++ b/src/nvim/option_defs.h @@ -1,6 +1,7 @@ #ifndef NVIM_OPTION_DEFS_H #define NVIM_OPTION_DEFS_H +#include "nvim/api/private/defs.h" #include "nvim/eval/typval_defs.h" #include "nvim/macros.h" #include "nvim/types.h" @@ -984,7 +985,7 @@ enum { typedef struct { // Pointer to the option variable. The variable can be a long (numeric // option), an int (boolean option) or a char pointer (string option). - char *os_varp; + void *os_varp; int os_idx; int os_flags; @@ -1047,19 +1048,19 @@ typedef enum { } idopt_T; typedef struct vimoption { - char *fullname; // full option name - char *shortname; // permissible abbreviation - uint32_t flags; // see above - char *var; // global option: pointer to variable; - // window-local option: VAR_WIN; - // buffer-local option: global value - idopt_T indir; // global option: PV_NONE; - // local option: indirect option index - // callback function to invoke after an option is modified to validate and - // apply the new value. + char *fullname; // full option name + char *shortname; // permissible abbreviation + uint32_t flags; // see above + void *var; // global option: pointer to variable; + // window-local option: VAR_WIN; + // buffer-local option: global value + idopt_T indir; // global option: PV_NONE; + // local option: indirect option index + // callback function to invoke after an option is modified to validate and + // apply the new value. opt_did_set_cb_T opt_did_set_cb; - char *def_val; // default values for variable (neovim!!) - LastSet last_set; // script in which the option was last set + void *def_val; // default values for variable (neovim!!) + LastSet last_set; // script in which the option was last set } vimoption_T; // The options that are local to a window or buffer have "indir" set to one of @@ -1080,4 +1081,24 @@ typedef struct vimoption { // buffers. Indicate this by setting "var" to VAR_WIN. #define VAR_WIN ((char *)-1) +// Option value type +typedef enum { + kOptValTypeNil = 0, + kOptValTypeBoolean, + kOptValTypeNumber, + kOptValTypeString, +} OptValType; + +// Option value +typedef struct { + OptValType type; + + union { + // Vim boolean options are actually tri-states because they have a third "None" value. + TriState boolean; + Integer number; + String string; + } data; +} OptVal; + #endif // NVIM_OPTION_DEFS_H diff --git a/src/nvim/options.lua b/src/nvim/options.lua index 2d6b049a48..d3566ba318 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -570,7 +570,7 @@ return { alloced=true, redraw={'curswant'}, varname='p_def', - defaults={if_true="^\\s*#\\s*define"} + defaults={if_true=""} }, { full_name='delcombine', abbreviation='deco', @@ -1227,7 +1227,7 @@ return { type='string', scope={'global', 'buffer'}, alloced=true, varname='p_inc', - defaults={if_true="^\\s*#\\s*include"} + defaults={if_true=""} }, { full_name='includeexpr', abbreviation='inex', @@ -1815,7 +1815,7 @@ return { deny_duplicates=true, expand=true, varname='p_path', - defaults={if_true=".,/usr/include,,"} + defaults={if_true=".,,"} }, { full_name='preserveindent', abbreviation='pi', diff --git a/src/nvim/optionstr.c b/src/nvim/optionstr.c index d3f676379d..a0ffecbad0 100644 --- a/src/nvim/optionstr.c +++ b/src/nvim/optionstr.c @@ -352,7 +352,7 @@ void set_string_option_direct(const char *name, int opt_idx, const char *val, in return; } - assert((void *)opt->var != (void *)&p_shada); + assert(opt->var != &p_shada); s = xstrdup(val); { @@ -2067,7 +2067,7 @@ static const char *did_set_string_option_for(buf_T *buf, win_T *win, int opt_idx bool value_changed = false; optset_T args = { - .os_varp = (char *)varp, + .os_varp = varp, .os_idx = opt_idx, .os_flags = opt_flags, .os_oldval.string = oldval, @@ -2250,7 +2250,7 @@ void save_clear_shm_value(void) if (++set_shm_recursive == 1) { STRCPY(shm_buf, p_shm); - set_option_value_give_err("shm", 0L, "", 0); + set_option_value_give_err("shm", STATIC_CSTR_AS_OPTVAL(""), 0); } } @@ -2258,7 +2258,7 @@ void save_clear_shm_value(void) void restore_shm_value(void) { if (--set_shm_recursive == 0) { - set_option_value_give_err("shm", 0L, shm_buf, 0); + set_option_value_give_err("shm", CSTR_AS_OPTVAL(shm_buf), 0); memset(shm_buf, 0, SHM_LEN); } } diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c index d270f8767e..6c3eca8961 100644 --- a/src/nvim/os/fs.c +++ b/src/nvim/os/fs.c @@ -14,6 +14,19 @@ #include <sys/stat.h> #include <uv.h> +#ifdef MSWIN +# include <shlobj.h> +#endif + +#if defined(HAVE_ACL) +# ifdef HAVE_SYS_ACL_H +# include <sys/acl.h> +# endif +# ifdef HAVE_SYS_ACCESS_H +# include <sys/access.h> +# endif +#endif + #include "auto/config.h" #include "nvim/ascii.h" #include "nvim/gettext.h" @@ -731,15 +744,6 @@ int os_setperm(const char *const name, int perm) return (r == kLibuvSuccess ? OK : FAIL); } -#if defined(HAVE_ACL) -# ifdef HAVE_SYS_ACL_H -# include <sys/acl.h> -# endif -# ifdef HAVE_SYS_ACCESS_H -# include <sys/access.h> -# endif -#endif - // Return a pointer to the ACL of file "fname" in allocated memory. // Return NULL if the ACL is not available for whatever reason. vim_acl_T os_get_acl(const char *fname) @@ -1225,8 +1229,6 @@ char *os_realpath(const char *name, char *buf) } #ifdef MSWIN -# include <shlobj.h> - /// When "fname" is the name of a shortcut (*.lnk) resolve the file it points /// to and return that name in allocated memory. /// Otherwise NULL is returned. diff --git a/src/nvim/os/os_defs.h b/src/nvim/os/os_defs.h index c595013a21..1e0f5b77f8 100644 --- a/src/nvim/os/os_defs.h +++ b/src/nvim/os/os_defs.h @@ -7,6 +7,12 @@ #include <sys/stat.h> #include <sys/types.h> +// Note: Some systems need both string.h and strings.h (Savage). +#include <string.h> +#ifdef HAVE_STRINGS_H +# include <strings.h> +#endif + #ifdef MSWIN # include "nvim/os/win_defs.h" #else @@ -36,12 +42,6 @@ // Command-processing buffer. Use large buffers for all platforms. #define CMDBUFFSIZE 1024 -// Note: Some systems need both string.h and strings.h (Savage). -#include <string.h> -#ifdef HAVE_STRINGS_H -# include <strings.h> -#endif - /// Converts libuv error (negative int) to error description string. #define os_strerror uv_strerror diff --git a/src/nvim/os/process.c b/src/nvim/os/process.c index 98ae251e2b..a636689f97 100644 --- a/src/nvim/os/process.c +++ b/src/nvim/os/process.c @@ -13,14 +13,8 @@ #include <stdio.h> #include <uv.h> -#include "nvim/log.h" -#include "nvim/memory.h" -#include "nvim/os/process.h" - #ifdef MSWIN # include <tlhelp32.h> - -# include "nvim/api/private/helpers.h" #endif #if defined(__FreeBSD__) // XXX: OpenBSD ? @@ -38,6 +32,14 @@ # include <sys/sysctl.h> #endif +#include "nvim/log.h" +#include "nvim/memory.h" +#include "nvim/os/process.h" + +#ifdef MSWIN +# include "nvim/api/private/helpers.h" +#endif + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "os/process.c.generated.h" // IWYU pragma: export #endif diff --git a/src/nvim/os/pty_process_unix.c b/src/nvim/os/pty_process_unix.c index 2413f0339b..b5423c59d7 100644 --- a/src/nvim/os/pty_process_unix.c +++ b/src/nvim/os/pty_process_unix.c @@ -11,6 +11,7 @@ #include <string.h> #include <sys/ioctl.h> #include <sys/wait.h> +#include <uv.h> // forkpty is not in POSIX, so headers are platform-specific #if defined(__FreeBSD__) || defined(__DragonFly__) @@ -31,8 +32,6 @@ # include <crt_externs.h> #endif -#include <uv.h> - #include "auto/config.h" #include "klib/klist.h" #include "nvim/eval/typval.h" diff --git a/src/nvim/os/pty_process_win.c b/src/nvim/os/pty_process_win.c index a8330acd54..abeb020645 100644 --- a/src/nvim/os/pty_process_win.c +++ b/src/nvim/os/pty_process_win.c @@ -7,7 +7,7 @@ #include "nvim/ascii.h" #include "nvim/eval/typval.h" -#include "nvim/mbyte.h" // for utf8_to_utf16, utf16_to_utf8 +#include "nvim/mbyte.h" #include "nvim/memory.h" #include "nvim/os/os.h" #include "nvim/os/pty_conpty_win.h" diff --git a/src/nvim/os/signal.c b/src/nvim/os/signal.c index e7b745fb7e..56fd2125c2 100644 --- a/src/nvim/os/signal.c +++ b/src/nvim/os/signal.c @@ -5,7 +5,7 @@ #include <stdbool.h> #include <stdio.h> #ifndef MSWIN -# include <signal.h> // for sigset_t +# include <signal.h> #endif #include "nvim/autocmd.h" diff --git a/src/nvim/path.c b/src/nvim/path.c index 21a3a67e24..b9ae756027 100644 --- a/src/nvim/path.c +++ b/src/nvim/path.c @@ -1810,7 +1810,7 @@ bool path_with_extension(const char *path, const char *extension) } /// Return true if "name" is a full (absolute) path name or URL. -bool vim_isAbsName(char *name) +bool vim_isAbsName(const char *name) { return path_with_url(name) != 0 || path_is_absolute(name); } @@ -1871,7 +1871,7 @@ char *fix_fname(const char *fname) #ifdef UNIX return FullName_save(fname, true); #else - if (!vim_isAbsName((char *)fname) + if (!vim_isAbsName(fname) || strstr(fname, "..") != NULL || strstr(fname, "//") != NULL # ifdef BACKSLASH_IN_FILENAME @@ -2087,17 +2087,17 @@ char *path_shorten_fname(char *full_path, char *dir_name) assert(dir_name != NULL); size_t len = strlen(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 (path_fnamencmp(dir_name, full_path, len) != 0) { return NULL; } + // 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; + } + char *p = full_path + len; // If *p is not pointing to a path separator, this means that full_path's @@ -2129,7 +2129,7 @@ int expand_wildcards_eval(char **pat, int *num_file, char ***file, int flags) int ret = FAIL; char *eval_pat = NULL; char *exp_pat = *pat; - char *ignored_msg; + const char *ignored_msg; size_t usedlen; const bool is_cur_alt_file = *exp_pat == '%' || *exp_pat == '#'; bool star_follows = false; diff --git a/src/nvim/popupmenu.c b/src/nvim/popupmenu.c index 15d0372c6f..fe31c70d5d 100644 --- a/src/nvim/popupmenu.c +++ b/src/nvim/popupmenu.c @@ -780,11 +780,11 @@ static bool pum_set_selected(int n, int repeat) if (res == OK) { // Edit a new, empty buffer. Set options for a "wipeout" // buffer. - set_option_value_give_err("swf", 0L, NULL, OPT_LOCAL); - set_option_value_give_err("bl", 0L, NULL, OPT_LOCAL); - set_option_value_give_err("bt", 0L, "nofile", OPT_LOCAL); - set_option_value_give_err("bh", 0L, "wipe", OPT_LOCAL); - set_option_value_give_err("diff", 0L, NULL, OPT_LOCAL); + set_option_value_give_err("swf", BOOLEAN_OPTVAL(false), OPT_LOCAL); + set_option_value_give_err("bl", BOOLEAN_OPTVAL(false), OPT_LOCAL); + set_option_value_give_err("bt", STATIC_CSTR_AS_OPTVAL("nofile"), OPT_LOCAL); + set_option_value_give_err("bh", STATIC_CSTR_AS_OPTVAL("wipe"), OPT_LOCAL); + set_option_value_give_err("diff", BOOLEAN_OPTVAL(false), OPT_LOCAL); } } diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index d6bbcbc80d..d42e0ed24f 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -2848,6 +2848,7 @@ static void qf_jump_print_msg(qf_info_T *qi, int qf_index, qfline_T *qf_ptr, buf // Add the message, skipping leading whitespace and newlines. ga_concat(gap, IObuff); qf_fmt_text(gap, skipwhite(qf_ptr->qf_text)); + ga_append(gap, NUL); // Output the message. Overwrite to avoid scrolling when the 'O' // flag is present in 'shortmess'; But when not jumping, print the @@ -3123,9 +3124,7 @@ static void qf_list_entry(qfline_T *qfp, int qf_idx, bool cursel) msg_puts_attr(":", qfSepAttr); } garray_T *gap = qfga_get(); - if (qfp->qf_lnum == 0) { - ga_append(gap, NUL); - } else { + if (qfp->qf_lnum != 0) { qf_range_text(gap, qfp); } ga_concat(gap, qf_types(qfp->qf_type, qfp->qf_nr)); @@ -3135,6 +3134,7 @@ static void qf_list_entry(qfline_T *qfp, int qf_idx, bool cursel) if (qfp->qf_pattern != NULL) { gap = qfga_get(); qf_fmt_text(gap, qfp->qf_pattern); + ga_append(gap, NUL); msg_puts(gap->ga_data); msg_puts_attr(":", qfSepAttr); } @@ -3145,6 +3145,7 @@ static void qf_list_entry(qfline_T *qfp, int qf_idx, bool cursel) // with ^^^^. gap = qfga_get(); qf_fmt_text(gap, (fname != NULL || qfp->qf_lnum != 0) ? skipwhite(qfp->qf_text) : qfp->qf_text); + ga_append(gap, NUL); msg_prt_line(gap->ga_data, false); } @@ -3229,7 +3230,6 @@ static void qf_fmt_text(garray_T *gap, const char *restrict text) FUNC_ATTR_NONNULL_ALL { const char *p = text; - while (*p != NUL) { if (*p == '\n') { ga_append(gap, ' '); @@ -3242,8 +3242,6 @@ static void qf_fmt_text(garray_T *gap, const char *restrict text) ga_append(gap, (uint8_t)(*p++)); } } - - ga_append(gap, NUL); } /// Add the range information from the lnum, col, end_lnum, and end_col values @@ -3268,7 +3266,6 @@ static void qf_range_text(garray_T *gap, const qfline_T *qfp) len += strlen(buf + len); } } - buf[len] = NUL; ga_concat_len(gap, buf, len); } @@ -3612,12 +3609,12 @@ static int qf_goto_cwindow(const qf_info_T *qi, bool resize, int sz, bool vertsp static void qf_set_cwindow_options(void) { // switch off 'swapfile' - set_option_value_give_err("swf", 0L, NULL, OPT_LOCAL); - set_option_value_give_err("bt", 0L, "quickfix", OPT_LOCAL); - set_option_value_give_err("bh", 0L, "hide", OPT_LOCAL); + set_option_value_give_err("swf", BOOLEAN_OPTVAL(false), OPT_LOCAL); + set_option_value_give_err("bt", STATIC_CSTR_AS_OPTVAL("quickfix"), OPT_LOCAL); + set_option_value_give_err("bh", STATIC_CSTR_AS_OPTVAL("hide"), OPT_LOCAL); RESET_BINDING(curwin); curwin->w_p_diff = false; - set_option_value_give_err("fdm", 0L, "manual", OPT_LOCAL); + set_option_value_give_err("fdm", STATIC_CSTR_AS_OPTVAL("manual"), OPT_LOCAL); } // Open a new quickfix or location list window, load the quickfix buffer and @@ -3983,7 +3980,6 @@ static int qf_buf_add_line(qf_list_T *qfl, buf_T *buf, linenr_T lnum, const qfli // for this entry, then use it. if (qftf_str != NULL && *qftf_str != NUL) { ga_concat(gap, qftf_str); - ga_append(gap, NUL); } else { buf_T *errbuf; if (qfp->qf_module != NULL) { @@ -4026,6 +4022,7 @@ static int qf_buf_add_line(qf_list_T *qfl, buf_T *buf, linenr_T lnum, const qfli qf_fmt_text(gap, gap->ga_len > 3 ? skipwhite(qfp->qf_text) : qfp->qf_text); } + ga_append(gap, NUL); if (ml_append_buf(buf, lnum, gap->ga_data, gap->ga_len, false) == FAIL) { return FAIL; } @@ -4176,7 +4173,7 @@ static void qf_fill_buffer(qf_list_T *qfl, buf_T *buf, qfline_T *old_last, int q // resembles reading a file into a buffer, it's more logical when using // autocommands. curbuf->b_ro_locked++; - set_option_value_give_err("ft", 0L, "qf", OPT_LOCAL); + set_option_value_give_err("ft", STATIC_CSTR_AS_OPTVAL("qf"), OPT_LOCAL); curbuf->b_p_ma = false; keep_filetype = true; // don't detect 'filetype' @@ -7183,7 +7180,7 @@ void ex_helpgrep(exarg_T *eap) // Darn, some plugin changed the value. If it's still empty it was // changed and restored, need to restore in the complicated way. if (*p_cpo == NUL) { - set_option_value_give_err("cpo", 0L, save_cpo, 0); + set_option_value_give_err("cpo", CSTR_AS_OPTVAL(save_cpo), 0); } if (save_cpo_allocated) { free_string_option(save_cpo); @@ -7279,7 +7276,7 @@ void f_getqflist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) get_qf_loc_list(true, NULL, &argvars[0], rettv); } -/// Create quickfix/location list from VimL values +/// Create quickfix/location list from Vimscript values /// /// Used by `setqflist()` and `setloclist()` functions. Accepts invalid /// args argument in which case errors out, including VAR_UNKNOWN parameters. diff --git a/src/nvim/runtime.c b/src/nvim/runtime.c index 964e2d0933..0503c08af9 100644 --- a/src/nvim/runtime.c +++ b/src/nvim/runtime.c @@ -1006,7 +1006,7 @@ static int add_pack_dir_to_rtp(char *fname, bool is_pack) xstrlcat(new_rtp, afterdir, new_rtp_capacity); } - set_option_value_give_err("rtp", 0L, new_rtp, 0); + set_option_value_give_err("rtp", CSTR_AS_OPTVAL(new_rtp), 0); xfree(new_rtp); retval = OK; @@ -1770,7 +1770,7 @@ static FILE *fopen_noinh_readbin(char *filename) return fdopen(fd_tmp, READBIN); } -/// Concatenate VimL line if it starts with a line continuation into a growarray +/// Concatenate Vimscript line if it starts with a line continuation into a growarray /// (excluding the continuation chars and leading whitespace) /// /// @note Growsize of the growarray may be changed to speed up concatenations! diff --git a/src/nvim/shada.c b/src/nvim/shada.c index db911f4bfd..f8c448dce0 100644 --- a/src/nvim/shada.c +++ b/src/nvim/shada.c @@ -3396,7 +3396,7 @@ static ShaDaReadResult msgpack_read_uint64(ShaDaReadDef *const sd_reader, const if (msgpack_to_vim(obj, &adtv) == FAIL \ || adtv.v_type != VAR_DICT) { \ semsg(_(READERR(name, \ - "cannot be converted to a VimL dictionary")), \ + "cannot be converted to a Vimscript dictionary")), \ initial_fpos); \ ga_clear(&ad_ga); \ tv_clear(&adtv); \ @@ -3420,7 +3420,7 @@ static ShaDaReadResult msgpack_read_uint64(ShaDaReadDef *const sd_reader, const }; \ typval_T aetv; \ if (msgpack_to_vim(obj, &aetv) == FAIL) { \ - semsg(_(READERR(name, "cannot be converted to a VimL list")), \ + semsg(_(READERR(name, "cannot be converted to a Vimscript list")), \ initial_fpos); \ tv_clear(&aetv); \ goto shada_read_next_item_error; \ @@ -3819,7 +3819,7 @@ shada_read_next_item_start: } else if (msgpack_to_vim(unpacked.data.via.array.ptr[1], &(entry->data.global_var.value)) == FAIL) { semsg(_(READERR("variable", "has value that cannot " - "be converted to the VimL value")), initial_fpos); + "be converted to the Vimscript value")), initial_fpos); goto shada_read_next_item_error; } break; diff --git a/src/nvim/spell.c b/src/nvim/spell.c index 9ae2036ee0..b337504bd9 100644 --- a/src/nvim/spell.c +++ b/src/nvim/spell.c @@ -2606,7 +2606,7 @@ void ex_spellrepall(exarg_T *eap) } const size_t repl_from_len = strlen(repl_from); const size_t repl_to_len = strlen(repl_to); - int addlen = (int)(repl_to_len - repl_from_len); + const int addlen = (int)(repl_to_len - repl_from_len); const size_t frompatlen = repl_from_len + 7; char *frompat = xmalloc(frompatlen); @@ -3171,17 +3171,15 @@ void ex_spelldump(exarg_T *eap) if (no_spell_checking(curwin)) { return; } - char *spl; - long dummy; - (void)get_option_value("spl", &dummy, &spl, NULL, OPT_LOCAL); + OptVal spl = get_option_value("spl", NULL, OPT_LOCAL, NULL); // Create a new empty buffer in a new window. do_cmdline_cmd("new"); // enable spelling locally in the new window - set_option_value_give_err("spell", true, "", OPT_LOCAL); - set_option_value_give_err("spl", dummy, spl, OPT_LOCAL); - xfree(spl); + set_option_value_give_err("spell", BOOLEAN_OPTVAL(true), OPT_LOCAL); + set_option_value_give_err("spl", spl, OPT_LOCAL); + optval_free(spl); if (!buf_is_empty(curbuf)) { return; diff --git a/src/nvim/spellfile.c b/src/nvim/spellfile.c index e81cebe18a..330dcdad05 100644 --- a/src/nvim/spellfile.c +++ b/src/nvim/spellfile.c @@ -5720,7 +5720,7 @@ static void init_spellfile(void) && strstr(path_tail(fname), ".ascii.") != NULL) ? "ascii" : spell_enc())); - set_option_value_give_err("spellfile", 0L, buf, OPT_LOCAL); + set_option_value_give_err("spellfile", CSTR_AS_OPTVAL(buf), OPT_LOCAL); break; } aspath = false; diff --git a/src/nvim/spellsuggest.c b/src/nvim/spellsuggest.c index 1c34c2487f..e68a90c982 100644 --- a/src/nvim/spellsuggest.c +++ b/src/nvim/spellsuggest.c @@ -1175,7 +1175,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char *fword, bool soun // word). int depth = 0; trystate_T *sp = &stack[0]; - CLEAR_POINTER(sp); // -V1068 + CLEAR_POINTER(sp); // -V1086 sp->ts_curi = 1; if (soundfold) { diff --git a/src/nvim/statusline.c b/src/nvim/statusline.c index b89d346fbf..f2502fe1e3 100644 --- a/src/nvim/statusline.c +++ b/src/nvim/statusline.c @@ -248,8 +248,8 @@ StlClickDefinition *stl_alloc_click_defs(StlClickDefinition *cdp, long width, si } /// Fill the click definitions array if needed. -void stl_fill_click_defs(StlClickDefinition *click_defs, StlClickRecord *click_recs, char *buf, - int width, bool tabline) +void stl_fill_click_defs(StlClickDefinition *click_defs, StlClickRecord *click_recs, + const char *buf, int width, bool tabline) { if (click_defs == NULL) { return; @@ -263,6 +263,7 @@ void stl_fill_click_defs(StlClickDefinition *click_defs, StlClickRecord *click_r }; for (int i = 0; click_recs[i].start != NULL; i++) { len += vim_strnsize(buf, (int)(click_recs[i].start - buf)); + assert(len <= width); if (col < len) { while (col < len) { click_defs[col++] = cur_click_def; @@ -270,7 +271,7 @@ void stl_fill_click_defs(StlClickDefinition *click_defs, StlClickRecord *click_r } else { xfree(cur_click_def.func); } - buf = (char *)click_recs[i].start; + buf = click_recs[i].start; cur_click_def = click_recs[i].def; if (!tabline && !(cur_click_def.type == kStlClickDisabled || cur_click_def.type == kStlClickFuncRun)) { @@ -905,9 +906,9 @@ int build_statuscol_str(win_T *wp, linenr_T lnum, long relnum, statuscol_T *stcp // Only update click definitions once per window per redraw if (fillclick) { stl_clear_click_defs(wp->w_statuscol_click_defs, wp->w_statuscol_click_defs_size); - wp->w_statuscol_click_defs = stl_alloc_click_defs(wp->w_statuscol_click_defs, width, + wp->w_statuscol_click_defs = stl_alloc_click_defs(wp->w_statuscol_click_defs, stcp->width, &wp->w_statuscol_click_defs_size); - stl_fill_click_defs(wp->w_statuscol_click_defs, clickrec, stcp->text, width, false); + stl_fill_click_defs(wp->w_statuscol_click_defs, clickrec, stcp->text, stcp->width, false); } return width; @@ -2052,17 +2053,6 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n // Put a `<` to mark where we truncated at *trunc_p = '<'; - - if (width + 1 < maxwidth) { - // Advance the pointer to the end of the string - trunc_p = trunc_p + strlen(trunc_p); - } - - // Fill up for half a double-wide character. - while (++width < maxwidth) { - MB_CHAR2BYTES(fillchar, trunc_p); - *trunc_p = NUL; - } // } // { Change the start point for items based on @@ -2077,13 +2067,24 @@ int build_stl_str_hl(win_T *wp, char *out, size_t outlen, char *fmt, char *opt_n // to be moved backwards. if (stl_items[i].start >= trunc_end_p) { stl_items[i].start -= item_offset; + } else { // Anything inside the truncated area is set to start // at the `<` truncation character. - } else { stl_items[i].start = trunc_p; } } // } + + if (width + 1 < maxwidth) { + // Advance the pointer to the end of the string + trunc_p = trunc_p + strlen(trunc_p); + } + + // Fill up for half a double-wide character. + while (++width < maxwidth) { + MB_CHAR2BYTES(fillchar, trunc_p); + *trunc_p = NUL; + } } width = maxwidth; diff --git a/src/nvim/strings.c b/src/nvim/strings.c index 4e521b14f7..6fe2fd8ff3 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -500,13 +500,10 @@ static const char *const e_printf = /// Get number argument from idxp entry in tvs /// -/// Will give an error message for VimL entry with invalid type or for -/// insufficient entries. +/// Will give an error message for Vimscript entry with invalid type or for insufficient entries. /// -/// @param[in] tvs List of VimL values. List is terminated by VAR_UNKNOWN -/// value. -/// @param[in,out] idxp Index in a list. Will be incremented. Indexing starts -/// at 1. +/// @param[in] tvs List of Vimscript values. List is terminated by VAR_UNKNOWN value. +/// @param[in,out] idxp Index in a list. Will be incremented. Indexing starts at 1. /// /// @return Number value or 0 in case of error. static varnumber_T tv_nr(typval_T *tvs, int *idxp) @@ -530,10 +527,10 @@ static varnumber_T tv_nr(typval_T *tvs, int *idxp) /// Get string argument from idxp entry in tvs /// -/// Will give an error message for VimL entry with invalid type or for +/// Will give an error message for Vimscript entry with invalid type or for /// insufficient entries. /// -/// @param[in] tvs List of VimL values. List is terminated by VAR_UNKNOWN +/// @param[in] tvs List of Vimscript values. List is terminated by VAR_UNKNOWN /// value. /// @param[in,out] idxp Index in a list. Will be incremented. /// @param[out] tofree If the idxp entry in tvs is not a String or a Number, @@ -564,7 +561,7 @@ static const char *tv_str(typval_T *tvs, int *idxp, char **const tofree) /// Get pointer argument from the next entry in tvs /// -/// Will give an error message for VimL entry with invalid type or for +/// Will give an error message for Vimscript entry with invalid type or for /// insufficient entries. /// /// @param[in] tvs List of typval_T values. @@ -595,11 +592,10 @@ static const void *tv_ptr(const typval_T *const tvs, int *const idxp) /// Get float argument from idxp entry in tvs /// -/// Will give an error message for VimL entry with invalid type or for +/// Will give an error message for Vimscript entry with invalid type or for /// insufficient entries. /// -/// @param[in] tvs List of VimL values. List is terminated by VAR_UNKNOWN -/// value. +/// @param[in] tvs List of Vimscript values. List is terminated by VAR_UNKNOWN value. /// @param[in,out] idxp Index in a list. Will be incremented. /// /// @return Floating-point value or zero in case of error. @@ -727,7 +723,7 @@ int vim_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap) /// @param[in] str_m String length. /// @param[in] fmt String format. /// @param[in] ap Values that should be formatted. Ignored if tvs is not NULL. -/// @param[in] tvs Values that should be formatted, for printf() VimL +/// @param[in] tvs Values that should be formatted, for printf() Vimscript /// function. Must be NULL in other cases. /// /// @return Number of bytes excluding NUL byte that would be written to the @@ -1603,6 +1599,11 @@ void f_charidx(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) int len; for (p = str, len = 0; utf16idx ? idx >= 0 : p <= str + idx; len++) { if (*p == NUL) { + // If the index is exactly the number of bytes or utf-16 code units + // in the string then return the length of the string in characters. + if (utf16idx ? (idx == 0) : (p == (str + idx))) { + rettv->vval.v_number = len; + } return; } if (utf16idx) { @@ -2009,6 +2010,9 @@ void f_strtrans(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) } /// "utf16idx()" function +/// +/// Converts a byte or character offset in a string to the corresponding UTF-16 +/// code unit offset. void f_utf16idx(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { rettv->vval.v_number = -1; @@ -2045,10 +2049,17 @@ void f_utf16idx(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) const char *p; int len; + int utf16idx = 0; for (p = str, len = 0; charidx ? idx >= 0 : p <= str + idx; len++) { if (*p == NUL) { + // If the index is exactly the number of bytes or characters in the + // string then return the length of the string in utf-16 code units. + if (charidx ? (idx == 0) : (p == (str + idx))) { + rettv->vval.v_number = len; + } return; } + utf16idx = len; const int clen = ptr2len(p); const int c = (clen > 1) ? utf_ptr2char(p) : *p; if (c > 0xFFFF) { @@ -2060,7 +2071,7 @@ void f_utf16idx(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) } } - rettv->vval.v_number = len > 0 ? len - 1 : 0; + rettv->vval.v_number = utf16idx; } /// "tolower(string)" function diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index d237972e40..ea7f3a085f 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -3979,7 +3979,7 @@ static void syn_cmd_include(exarg_T *eap, int syncing) int sgl_id = 1; char *group_name_end; char *rest; - char *errormsg = NULL; + const char *errormsg = NULL; int prev_toplvl_grp; int prev_syn_inc_tag; bool source = false; diff --git a/src/nvim/tag.c b/src/nvim/tag.c index 18331cc95d..4fe669631f 100644 --- a/src/nvim/tag.c +++ b/src/nvim/tag.c @@ -1762,7 +1762,7 @@ static tagmatch_status_T findtags_parse_line(findtags_state_T *st, tagptrs_T *ta if (st->state == TS_BINARY) { int tagcmp; // Simplistic check for unsorted tags file. - int i = (int)tagpp->tagname[0]; + int i = (uint8_t)tagpp->tagname[0]; if (margs->sortic) { i = TOUPPER_ASC(tagpp->tagname[0]); } diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index 792071963d..676ac954f7 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -235,7 +235,7 @@ Terminal *terminal_open(buf_T *buf, TerminalOptions opts) aucmd_prepbuf(&aco, buf); refresh_screen(rv, buf); - set_option_value("buftype", 0, "terminal", OPT_LOCAL); // -V666 + set_option_value("buftype", STATIC_CSTR_AS_OPTVAL("terminal"), OPT_LOCAL); // -V666 // Default settings for terminal buffers buf->b_p_ma = false; // 'nomodifiable' @@ -243,8 +243,8 @@ Terminal *terminal_open(buf_T *buf, TerminalOptions opts) buf->b_p_scbk = // 'scrollback' (initialize local from global) (p_scbk < 0) ? 10000 : MAX(1, p_scbk); buf->b_p_tw = 0; // 'textwidth' - set_option_value("wrap", false, NULL, OPT_LOCAL); - set_option_value("list", false, NULL, OPT_LOCAL); + set_option_value("wrap", BOOLEAN_OPTVAL(false), OPT_LOCAL); + set_option_value("list", BOOLEAN_OPTVAL(false), OPT_LOCAL); if (buf->b_ffname != NULL) { buf_set_term_title(buf, buf->b_ffname, strlen(buf->b_ffname)); } diff --git a/src/nvim/types.h b/src/nvim/types.h index ca0ae16a66..3a05223023 100644 --- a/src/nvim/types.h +++ b/src/nvim/types.h @@ -18,7 +18,7 @@ typedef int handle_T; // absent callback etc. typedef int LuaRef; -/// Type used for VimL VAR_FLOAT values +/// Type used for Vimscript VAR_FLOAT values typedef double float_T; typedef struct MsgpackRpcRequestHandler MsgpackRpcRequestHandler; diff --git a/src/nvim/ui.c b/src/nvim/ui.c index 87a0271f3d..7d8328d913 100644 --- a/src/nvim/ui.c +++ b/src/nvim/ui.c @@ -229,7 +229,7 @@ void ui_refresh(void) p_lz = save_p_lz; if (ext_widgets[kUIMessages]) { - set_option_value("cmdheight", 0L, NULL, 0); + set_option_value("cmdheight", NUMBER_OPTVAL(0), 0); command_height(); } ui_mode_info_set(); diff --git a/src/nvim/ui_client.c b/src/nvim/ui_client.c index 9395ff2f1e..0fc6d4936d 100644 --- a/src/nvim/ui_client.c +++ b/src/nvim/ui_client.c @@ -43,7 +43,7 @@ uint64_t ui_client_start_server(int argc, char **argv) varnumber_T exit_status; char **args = xmalloc(((size_t)(2 + argc)) * sizeof(char *)); int args_idx = 0; - args[args_idx++] = xstrdup(get_vim_var_str(VV_PROGPATH)); + args[args_idx++] = xstrdup(argv[0]); args[args_idx++] = xstrdup("--embed"); for (int i = 1; i < argc; i++) { args[args_idx++] = xstrdup(argv[i]); diff --git a/src/nvim/undo_defs.h b/src/nvim/undo_defs.h index 7c065c540b..7947bff6e6 100644 --- a/src/nvim/undo_defs.h +++ b/src/nvim/undo_defs.h @@ -1,7 +1,7 @@ #ifndef NVIM_UNDO_DEFS_H #define NVIM_UNDO_DEFS_H -#include <time.h> // for time_t +#include <time.h> #include "nvim/extmark_defs.h" #include "nvim/mark_defs.h" diff --git a/src/nvim/version.c b/src/nvim/version.c index c0e0ceef55..95e275bceb 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -2560,6 +2560,11 @@ Dictionary version_dict(void) PUT(d, "major", INTEGER_OBJ(NVIM_VERSION_MAJOR)); PUT(d, "minor", INTEGER_OBJ(NVIM_VERSION_MINOR)); PUT(d, "patch", INTEGER_OBJ(NVIM_VERSION_PATCH)); +#ifndef NVIM_VERSION_BUILD + PUT(d, "build", NIL); +#else + PUT(d, "build", CSTR_AS_OBJ(NVIM_VERSION_BUILD)); +#endif PUT(d, "prerelease", BOOLEAN_OBJ(NVIM_VERSION_PRERELEASE[0] != '\0')); PUT(d, "api_level", INTEGER_OBJ(NVIM_API_LEVEL)); PUT(d, "api_compatible", INTEGER_OBJ(NVIM_API_LEVEL_COMPAT)); @@ -2695,33 +2700,39 @@ void list_version(void) msg(longVersion); msg(version_buildtype); list_lua_version(); + + if (p_verbose > 0) { #ifndef NDEBUG - msg(version_cflags); + msg(version_cflags); #endif - - version_msg("\n\n"); + version_msg("\n\n"); #ifdef SYS_VIMRC_FILE - version_msg(_(" system vimrc file: \"")); - version_msg(SYS_VIMRC_FILE); - version_msg("\"\n"); -#endif // ifdef SYS_VIMRC_FILE -#ifdef HAVE_PATHDEF - - if (*default_vim_dir != NUL) { - version_msg(_(" fall-back for $VIM: \"")); - version_msg(default_vim_dir); + version_msg(_(" system vimrc file: \"")); + version_msg(SYS_VIMRC_FILE); version_msg("\"\n"); - } +#endif - if (*default_vimruntime_dir != NUL) { - version_msg(_(" f-b for $VIMRUNTIME: \"")); - version_msg(default_vimruntime_dir); - version_msg("\"\n"); +#ifdef HAVE_PATHDEF + if (*default_vim_dir != NUL) { + version_msg(_(" fall-back for $VIM: \"")); + version_msg(default_vim_dir); + version_msg("\"\n"); + } + + if (*default_vimruntime_dir != NUL) { + version_msg(_(" f-b for $VIMRUNTIME: \"")); + version_msg(default_vimruntime_dir); + version_msg("\"\n"); + } +#endif } -#endif // ifdef HAVE_PATHDEF - version_msg("\nRun :checkhealth for more info"); + version_msg(p_verbose > 0 + ? "\nRun :checkhealth for more info" + : (starting + ? "\nRun \"nvim -V1 -v\" for more info" + : "\nRun \":verbose version\" for more info")); } /// Show the intro message when not editing a file. diff --git a/src/nvim/viml/parser/expressions.c b/src/nvim/viml/parser/expressions.c index 830d8c5f34..b53508bc6c 100644 --- a/src/nvim/viml/parser/expressions.c +++ b/src/nvim/viml/parser/expressions.c @@ -1,10 +1,10 @@ // This is an open source non-commercial project. Dear PVS-Studio, please check // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com -/// VimL expression parser +/// Vimscript expression parser // Planned incompatibilities (to be included into vim_diff.txt when this parser -// will be an actual part of VimL evaluation process): +// will be an actual part of Vimscript evaluation process): // // 1. Expressions are first fully parsed and only then executed. This means // that while ":echo [system('touch abc')" will create file "abc" in Vim and @@ -89,7 +89,7 @@ typedef enum { /// Parse type: what is being parsed currently typedef enum { - /// Parsing regular VimL expression + /// Parsing regular Vimscript expression kEPTExpr = 0, /// Parsing lambda arguments /// @@ -171,7 +171,7 @@ static inline float_T scale_number(const float_T num, const uint8_t base, return ret; } -/// Get next token for the VimL expression input +/// Get next token for the Vimscript expression input /// /// @param pstate Parser state. /// @param[in] flags Flags, @see LexExprFlags. @@ -1168,7 +1168,7 @@ static struct { // represented as "list(comma(a, comma(b, comma(c, d))))" then if it is // "list(comma(comma(comma(a, b), c), d))" in which case you will need to // traverse all three comma() structures. And with comma operator (including - // actual comma operator from C which is not present in VimL) nobody cares + // actual comma operator from C which is not present in Vimscript) nobody cares // about associativity, only about order of execution. [kExprNodeComma] = { kEOpLvlComma, kEOpAssRight }, @@ -1915,7 +1915,7 @@ static const uint8_t base_to_prefix_length[] = { [16] = 2, }; -/// Parse one VimL expression +/// Parse one Vimscript expression /// /// @param pstate Parser state. /// @param[in] flags Additional flags, see ExprParserFlags diff --git a/test/functional/api/autocmd_spec.lua b/test/functional/api/autocmd_spec.lua index 57390519de..4cfe8672b1 100644 --- a/test/functional/api/autocmd_spec.lua +++ b/test/functional/api/autocmd_spec.lua @@ -228,23 +228,28 @@ describe('autocmd api', function() end) it('receives an args table', function() - local res = exec_lua [[ - local group_id = vim.api.nvim_create_augroup("TestGroup", {}) - local autocmd_id = vim.api.nvim_create_autocmd("User", { + local group_id = meths.create_augroup("TestGroup", {}) + -- Having an existing autocmd calling expand("<afile>") shouldn't change args #18964 + meths.create_autocmd('User', { + group = 'TestGroup', + pattern = 'Te*', + command = 'call expand("<afile>")', + }) + + local autocmd_id = exec_lua [[ + return vim.api.nvim_create_autocmd("User", { group = "TestGroup", pattern = "Te*", callback = function(args) vim.g.autocmd_args = args end, }) - - return {group_id, autocmd_id} ]] meths.exec_autocmds("User", {pattern = "Test pattern"}) eq({ - id = res[2], - group = res[1], + id = autocmd_id, + group = group_id, event = "User", match = "Test pattern", file = "Test pattern", @@ -252,27 +257,24 @@ describe('autocmd api', function() }, meths.get_var("autocmd_args")) -- Test without a group - res = exec_lua [[ - local autocmd_id = vim.api.nvim_create_autocmd("User", { + autocmd_id = exec_lua [[ + return vim.api.nvim_create_autocmd("User", { pattern = "*", callback = function(args) vim.g.autocmd_args = args end, }) - - return {autocmd_id} ]] meths.exec_autocmds("User", {pattern = "some_pat"}) eq({ - id = res[1], + id = autocmd_id, group = nil, event = "User", match = "some_pat", file = "some_pat", buf = 1, }, meths.get_var("autocmd_args")) - end) it('can receive arbitrary data', function() diff --git a/test/functional/api/buffer_spec.lua b/test/functional/api/buffer_spec.lua index bf9e952319..d9d4539fe8 100644 --- a/test/functional/api/buffer_spec.lua +++ b/test/functional/api/buffer_spec.lua @@ -706,7 +706,7 @@ describe('api/buf', function() nvim('set_option_value', 'define', 'test', {buf = 0}) eq('test', nvim('get_option_value', 'define', {buf = 0})) -- Doesn't change the global value - eq([[^\s*#\s*define]], nvim('get_option_value', 'define', {scope='global'})) + eq("", nvim('get_option_value', 'define', {scope='global'})) end) it('returns values for unset local options', function() diff --git a/test/functional/api/extmark_spec.lua b/test/functional/api/extmark_spec.lua index 675c8332de..ec1c9245ba 100644 --- a/test/functional/api/extmark_spec.lua +++ b/test/functional/api/extmark_spec.lua @@ -105,6 +105,10 @@ describe('API/extmarks', function() it('validation', function() eq("Invalid 'end_col': expected Integer, got Array", pcall_err(set_extmark, ns, marks[2], 0, 0, { end_col = {}, end_row = 1 })) eq("Invalid 'end_row': expected Integer, got Array", pcall_err(set_extmark, ns, marks[2], 0, 0, { end_col = 1, end_row = {} })) + eq("Invalid 'virt_text_pos': expected String, got Integer", pcall_err(set_extmark, ns, marks[2], 0, 0, { virt_text_pos = 0 })) + eq("Invalid 'virt_text_pos': 'foo'", pcall_err(set_extmark, ns, marks[2], 0, 0, { virt_text_pos = 'foo' })) + eq("Invalid 'hl_mode': expected String, got Integer", pcall_err(set_extmark, ns, marks[2], 0, 0, { hl_mode = 0 })) + eq("Invalid 'hl_mode': 'foo'", pcall_err(set_extmark, ns, marks[2], 0, 0, { hl_mode = 'foo' })) eq("Invalid 'id': expected positive Integer", pcall_err(set_extmark, ns, {}, 0, 0, { end_col = 1, end_row = 1 })) eq("Invalid mark position: expected 2 Integer items", pcall_err(get_extmarks, ns, {}, {-1, -1})) eq("Invalid mark position: expected mark id Integer or 2-item Array", pcall_err(get_extmarks, ns, true, {-1, -1})) diff --git a/test/functional/api/keymap_spec.lua b/test/functional/api/keymap_spec.lua index e239717d3a..953402ada3 100644 --- a/test/functional/api/keymap_spec.lua +++ b/test/functional/api/keymap_spec.lua @@ -400,6 +400,9 @@ describe('nvim_set_keymap, nvim_del_keymap', function() -- maparg(), which does not accept "!" (though it returns "!" in its output -- if getting a mapping set with |:map!|). local function normalize_mapmode(mode, generate_expected) + if mode:sub(-1) == 'a' then + mode = mode:sub(1, -2) + end if not generate_expected and mode == '!' then -- Cannot retrieve mapmode-ic mappings with "!", but can with "i" or "c". mode = 'i' @@ -435,7 +438,7 @@ describe('nvim_set_keymap, nvim_del_keymap', function() -- Gets a maparg() dict from Nvim, if one exists. local function get_mapargs(mode, lhs) - local mapargs = funcs.maparg(lhs, normalize_mapmode(mode), false, true) + local mapargs = funcs.maparg(lhs, normalize_mapmode(mode), mode:sub(-1) == 'a', true) -- drop "lhsraw" and "lhsrawalt" which are hard to check mapargs.lhsraw = nil mapargs.lhsrawalt = nil @@ -744,7 +747,7 @@ describe('nvim_set_keymap, nvim_del_keymap', function() end) -- Perform exhaustive tests of basic functionality - local mapmodes = {'n', 'v', 'x', 's', 'o', '!', 'i', 'l', 'c', 't', ''} + local mapmodes = {'n', 'v', 'x', 's', 'o', '!', 'i', 'l', 'c', 't', '', 'ia', 'ca', '!a'} for _, mapmode in ipairs(mapmodes) do it('can set/unset normal mappings in mapmode '..mapmode, function() meths.set_keymap(mapmode, 'lhs', 'rhs', {}) @@ -773,11 +776,9 @@ describe('nvim_set_keymap, nvim_del_keymap', function() -- remove some map arguments that are harder to test, or were already tested optnames = {'nowait', 'silent', 'expr', 'noremap'} for _, mapmode in ipairs(mapmodes) do - local printable_mode = normalize_mapmode(mapmode) - -- Test with single mappings for _, maparg in ipairs(optnames) do - it('can set/unset '..printable_mode..'-mappings with maparg: '..maparg, + it('can set/unset '..mapmode..'-mappings with maparg: '..maparg, function() meths.set_keymap(mapmode, 'lhs', 'rhs', {[maparg] = true}) eq(generate_mapargs(mapmode, 'lhs', 'rhs', {[maparg] = true}), @@ -785,7 +786,7 @@ describe('nvim_set_keymap, nvim_del_keymap', function() meths.del_keymap(mapmode, 'lhs') eq({}, get_mapargs(mapmode, 'lhs')) end) - it ('can set/unset '..printable_mode..'-mode mappings with maparg '.. + it ('can set/unset '..mapmode..'-mode mappings with maparg '.. maparg..', whose value is false', function() meths.set_keymap(mapmode, 'lhs', 'rhs', {[maparg] = false}) eq(generate_mapargs(mapmode, 'lhs', 'rhs'), @@ -798,7 +799,7 @@ describe('nvim_set_keymap, nvim_del_keymap', function() -- Test with triplets of mappings, one of which is false for i = 1, (#optnames - 2) do local opt1, opt2, opt3 = optnames[i], optnames[i + 1], optnames[i + 2] - it('can set/unset '..printable_mode..'-mode mappings with mapargs '.. + it('can set/unset '..mapmode..'-mode mappings with mapargs '.. opt1..', '..opt2..', '..opt3, function() local opts = {[opt1] = true, [opt2] = false, [opt3] = true} meths.set_keymap(mapmode, 'lhs', 'rhs', opts) @@ -988,6 +989,54 @@ describe('nvim_set_keymap, nvim_del_keymap', function() eq("\nn lhs rhs\n map description", helpers.exec_capture("nmap lhs")) end) + + it('can define !-mode abbreviations with lua callbacks', function() + exec_lua [[ + GlobalCount = 0 + vim.api.nvim_set_keymap('!a', 'foo', '', {expr = true, callback = function() + GlobalCount = GlobalCount + 1 + return tostring(GlobalCount) + end}) + ]] + + feed 'iThe foo and the bar and the foo again<esc>' + eq('The 1 and the bar and the 2 again', meths.get_current_line()) + + feed ':let x = "The foo is the one"<cr>' + eq('The 3 is the one', meths.eval'x') + end) + + it('can define insert mode abbreviations with lua callbacks', function() + exec_lua [[ + GlobalCount = 0 + vim.api.nvim_set_keymap('ia', 'foo', '', {expr = true, callback = function() + GlobalCount = GlobalCount + 1 + return tostring(GlobalCount) + end}) + ]] + + feed 'iThe foo and the bar and the foo again<esc>' + eq('The 1 and the bar and the 2 again', meths.get_current_line()) + + feed ':let x = "The foo is the one"<cr>' + eq('The foo is the one', meths.eval'x') + end) + + it('can define cmdline mode abbreviations with lua callbacks', function() + exec_lua [[ + GlobalCount = 0 + vim.api.nvim_set_keymap('ca', 'foo', '', {expr = true, callback = function() + GlobalCount = GlobalCount + 1 + return tostring(GlobalCount) + end}) + ]] + + feed 'iThe foo and the bar and the foo again<esc>' + eq('The foo and the bar and the foo again', meths.get_current_line()) + + feed ':let x = "The foo is the one"<cr>' + eq('The 1 is the one', meths.eval'x') + end) end) describe('nvim_buf_set_keymap, nvim_buf_del_keymap', function() diff --git a/test/functional/api/version_spec.lua b/test/functional/api/version_spec.lua index 771192e9ab..6d466b0cc1 100644 --- a/test/functional/api/version_spec.lua +++ b/test/functional/api/version_spec.lua @@ -34,6 +34,7 @@ describe("api_info()['version']", function() local minor = version['minor'] local patch = version['patch'] local prerelease = version['prerelease'] + local build = version['build'] eq("number", type(major)) eq("number", type(minor)) eq("number", type(patch)) @@ -42,6 +43,7 @@ describe("api_info()['version']", function() eq(0, funcs.has("nvim-"..major.."."..minor.."."..(patch + 1))) eq(0, funcs.has("nvim-"..major.."."..(minor + 1).."."..patch)) eq(0, funcs.has("nvim-"..(major + 1).."."..minor.."."..patch)) + assert(build == nil or type(build) == 'string') end) end) diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index 040c26e058..c4f89318e0 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -335,8 +335,7 @@ describe('API', function() nvim('command', 'edit '..fname) nvim('command', 'normal itesting\napi') nvim('command', 'w') - local f = io.open(fname) - ok(f ~= nil) + local f = assert(io.open(fname)) if is_os('win') then eq('testing\r\napi\r\n', f:read('*a')) else @@ -346,15 +345,15 @@ describe('API', function() os.remove(fname) end) - it('VimL validation error: fails with specific error', function() + it('Vimscript validation error: fails with specific error', function() local status, rv = pcall(nvim, "command", "bogus_command") eq(false, status) -- nvim_command() failed. - eq("E492:", string.match(rv, "E%d*:")) -- VimL error was returned. + eq("E492:", string.match(rv, "E%d*:")) -- Vimscript error was returned. eq('', nvim('eval', 'v:errmsg')) -- v:errmsg was not updated. eq('', eval('v:exception')) end) - it('VimL execution error: fails with specific error', function() + it('Vimscript execution error: fails with specific error', function() local status, rv = pcall(nvim, "command", "buffer 23487") eq(false, status) -- nvim_command() failed. eq("E86: Buffer 23487 does not exist", string.match(rv, "E%d*:.*")) @@ -422,7 +421,7 @@ describe('API', function() eq(':!echo foo\r\n\nfoo'..win_lf..'\n', nvim('command_output', [[!echo foo]])) end) - it('VimL validation error: fails with specific error', function() + it('Vimscript validation error: fails with specific error', function() local status, rv = pcall(nvim, "command_output", "bogus commannnd") eq(false, status) -- nvim_command_output() failed. eq("E492: Not an editor command: bogus commannnd", @@ -432,7 +431,7 @@ describe('API', function() eq({mode='n', blocking=false}, nvim("get_mode")) end) - it('VimL execution error: fails with specific error', function() + it('Vimscript execution error: fails with specific error', function() local status, rv = pcall(nvim, "command_output", "buffer 42") eq(false, status) -- nvim_command_output() failed. eq("E86: Buffer 42 does not exist", string.match(rv, "E%d*:.*")) @@ -463,7 +462,7 @@ describe('API', function() eq(2, request("vim_eval", "1+1")) end) - it("VimL error: returns error details, does NOT update v:errmsg", function() + it("Vimscript error: returns error details, does NOT update v:errmsg", function() eq('Vim:E121: Undefined variable: bogus', pcall_err(request, 'nvim_eval', 'bogus expression')) eq('', eval('v:errmsg')) -- v:errmsg was not updated. @@ -478,7 +477,7 @@ describe('API', function() eq('foo', nvim('call_function', 'simplify', {'this/./is//redundant/../../../foo'})) end) - it("VimL validation error: returns specific error, does NOT update v:errmsg", function() + it("Vimscript validation error: returns specific error, does NOT update v:errmsg", function() eq('Vim:E117: Unknown function: bogus function', pcall_err(request, 'nvim_call_function', 'bogus function', {'arg1'})) eq('Vim:E119: Not enough arguments for function: atan', @@ -487,7 +486,7 @@ describe('API', function() eq('', eval('v:errmsg')) -- v:errmsg was not updated. end) - it("VimL error: returns error details, does NOT update v:errmsg", function() + it("Vimscript error: returns error details, does NOT update v:errmsg", function() eq('Vim:E808: Number or Float required', pcall_err(request, 'nvim_call_function', 'atan', {'foo'})) eq('Vim:Invalid channel stream "xxx"', @@ -498,7 +497,7 @@ describe('API', function() eq('', eval('v:errmsg')) -- v:errmsg was not updated. end) - it("VimL exception: returns exception details, does NOT update v:errmsg", function() + it("Vimscript exception: returns exception details, does NOT update v:errmsg", function() source([[ function! Foo() abort throw 'wtf' @@ -523,7 +522,7 @@ describe('API', function() end) describe('nvim_call_dict_function', function() - it('invokes VimL dict function', function() + it('invokes Vimscript dict function', function() source([[ function! F(name) dict return self.greeting.', '.a:name.'!' @@ -653,7 +652,7 @@ describe('API', function() end) describe('nvim_input', function() - it("VimL error: does NOT fail, updates v:errmsg", function() + it("Vimscript error: does NOT fail, updates v:errmsg", function() local status, _ = pcall(nvim, "input", ":call bogus_fn()<CR>") local v_errnum = string.match(nvim("eval", "v:errmsg"), "E%d*:") eq(true, status) -- nvim_input() did not fail. @@ -1435,8 +1434,12 @@ describe('API', function() pcall_err(nvim, 'set_option_value', 'scrolloff', 1, {scope = 'bogus'})) eq("Invalid 'scope': expected String, got Integer", pcall_err(nvim, 'get_option_value', 'scrolloff', {scope = 42})) - eq("Invalid 'scrolloff': expected Integer/Boolean/String, got Array", + eq("Invalid 'value': expected Integer/Boolean/String, got Array", pcall_err(nvim, 'set_option_value', 'scrolloff', {}, {})) + eq("Invalid value for option 'scrolloff': expected Number, got Boolean true", + pcall_err(nvim, 'set_option_value', 'scrolloff', true, {})) + eq("Invalid value for option 'scrolloff': expected Number, got String \"wrong\"", + pcall_err(nvim, 'set_option_value', 'scrolloff', 'wrong', {})) end) it('can get local values when global value is set', function() @@ -3464,6 +3467,7 @@ describe('API', function() end) end) end) + describe('nvim_parse_cmd', function() it('works', function() eq({ @@ -3946,7 +3950,7 @@ describe('API', function() } }, meths.parse_cmd('MyCommand test it', {})) end) - it('errors for invalid command', function() + it('validates command', function() eq('Error while parsing command line', pcall_err(meths.parse_cmd, '', {})) eq('Error while parsing command line', pcall_err(meths.parse_cmd, '" foo', {})) eq('Error while parsing command line: E492: Not an editor command: Fubar', @@ -4045,7 +4049,13 @@ describe('API', function() meths.cmd(meths.parse_cmd("set cursorline", {}), {}) eq(true, meths.get_option_value("cursorline", {})) end) + it('no side-effects (error messages) in pcall() #20339', function() + eq({ false, 'Error while parsing command line: E16: Invalid range' }, + exec_lua([=[return {pcall(vim.api.nvim_parse_cmd, "'<,'>n", {})}]=])) + eq('', eval('v:errmsg')) + end) end) + describe('nvim_cmd', function() it('works', function () meths.cmd({ cmd = "set", args = { "cursorline" } }, {}) @@ -4093,6 +4103,11 @@ describe('API', function() eq("Invalid 'reg': expected single character, got xx", pcall_err(meths.cmd, { cmd = "put", args = {}, reg = 'xx' }, {})) + -- #20681 + eq('Invalid command: "win_getid"', pcall_err(meths.cmd, { cmd = 'win_getid'}, {})) + eq('Invalid command: "echo "hi""', pcall_err(meths.cmd, { cmd = 'echo "hi"'}, {})) + eq('Invalid command: "win_getid"', pcall_err(exec_lua, [[return vim.cmd.win_getid{}]])) + -- Lua call allows empty {} for dict item. eq('', exec_lua([[return vim.cmd{ cmd = "set", args = {}, magic = {} }]])) eq('', exec_lua([[return vim.cmd{ cmd = "set", args = {}, mods = {} }]])) diff --git a/test/functional/core/main_spec.lua b/test/functional/core/main_spec.lua index efab40dd11..19c7a93730 100644 --- a/test/functional/core/main_spec.lua +++ b/test/functional/core/main_spec.lua @@ -3,6 +3,7 @@ local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') local eq = helpers.eq +local matches = helpers.matches local feed = helpers.feed local eval = helpers.eval local clear = helpers.clear @@ -12,21 +13,24 @@ local write_file = helpers.write_file local is_os = helpers.is_os local skip = helpers.skip -describe('Command-line option', function() +describe('command-line option', function() describe('-s', function() local fname = 'Xtest-functional-core-main-s' local fname_2 = fname .. '.2' local nonexistent_fname = fname .. '.nonexistent' local dollar_fname = '$' .. fname + before_each(function() clear() os.remove(fname) os.remove(dollar_fname) end) + after_each(function() os.remove(fname) os.remove(dollar_fname) end) + it('treats - as stdin', function() eq(nil, luv.fs_stat(fname)) funcs.system( @@ -38,6 +42,7 @@ describe('Command-line option', function() local attrs = luv.fs_stat(fname) eq(#('42\n'), attrs.size) end) + it('does not expand $VAR', function() eq(nil, luv.fs_stat(fname)) eq(true, not not dollar_fname:find('%$%w+')) @@ -50,6 +55,7 @@ describe('Command-line option', function() local attrs = luv.fs_stat(fname) eq(#('100500\n'), attrs.size) end) + it('does not crash after reading from stdin in non-headless mode', function() skip(is_os('win')) local screen = Screen.new(40, 8) @@ -100,6 +106,7 @@ describe('Command-line option', function() ]]) ]=] end) + it('errors out when trying to use nonexistent file with -s', function() eq( 'Cannot open for reading: "'..nonexistent_fname..'": no such file or directory\n', @@ -110,6 +117,7 @@ describe('Command-line option', function() '-s', nonexistent_fname})) eq(2, eval('v:shell_error')) end) + it('errors out when trying to use -s twice', function() write_file(fname, ':call setline(1, "1")\n:wqall!\n') write_file(dollar_fname, ':call setline(1, "2")\n:wqall!\n') @@ -124,4 +132,11 @@ describe('Command-line option', function() eq(nil, luv.fs_stat(fname_2)) end) end) + + it('nvim -v, :version', function() + matches('Run ":verbose version"', funcs.execute(':version')) + matches('Compilation: .*Run :checkhealth', funcs.execute(':verbose version')) + matches('Run "nvim %-V1 %-v"', funcs.system({nvim_prog_abs(), '-v'})) + matches('Compilation: .*Run :checkhealth', funcs.system({nvim_prog_abs(), '-V1', '-v'})) + end) end) diff --git a/test/functional/core/remote_spec.lua b/test/functional/core/remote_spec.lua index f74bb65553..39b1ee2f5f 100644 --- a/test/functional/core/remote_spec.lua +++ b/test/functional/core/remote_spec.lua @@ -8,6 +8,7 @@ local exec_lua = helpers.exec_lua local expect = helpers.expect local funcs = helpers.funcs local insert = helpers.insert +local nvim_prog = helpers.nvim_prog local new_argv = helpers.new_argv local neq = helpers.neq local set_session = helpers.set_session @@ -38,10 +39,11 @@ describe('Remote', function() server:close() end) + -- Run a `nvim --remote*` command and return { stdout, stderr } of the process local function run_remote(...) set_session(server) local addr = funcs.serverlist()[1] - local client_argv = new_argv({args={'--server', addr, ...}}) + local client_argv = { nvim_prog, '--clean', '--headless', '--server', addr, ... } -- Create an nvim instance just to run the remote-invoking nvim. We want -- to wait for the remote instance to exit and calling jobwait blocks @@ -50,32 +52,43 @@ describe('Remote', function() local client_starter = spawn(new_argv(), false, nil, true) set_session(client_starter) -- Call jobstart() and jobwait() in the same RPC request to reduce flakiness. - eq({ 0 }, exec_lua([[return vim.fn.jobwait({ vim.fn.jobstart(...) })]], client_argv)) + eq({ 0 }, exec_lua([[return vim.fn.jobwait({ vim.fn.jobstart(..., { + stdout_buffered = true, + stderr_buffered = true, + on_stdout = function(_, data, _) + _G.Remote_stdout = table.concat(data, '\n') + end, + on_stderr = function(_, data, _) + _G.Remote_stderr = table.concat(data, '\n') + end, + }) })]], client_argv)) + local res = exec_lua([[return { _G.Remote_stdout, _G.Remote_stderr }]]) client_starter:close() set_session(server) + return res end it('edit a single file', function() - run_remote('--remote', fname) + eq({ '', '' }, run_remote('--remote', fname)) expect(contents) eq(2, #funcs.getbufinfo()) end) it('tab edit a single file with a non-changed buffer', function() - run_remote('--remote-tab', fname) + eq({ '', '' }, run_remote('--remote-tab', fname)) expect(contents) eq(1, #funcs.gettabinfo()) end) it('tab edit a single file with a changed buffer', function() insert('hello') - run_remote('--remote-tab', fname) + eq({ '', '' }, run_remote('--remote-tab', fname)) expect(contents) eq(2, #funcs.gettabinfo()) end) it('edit multiple files', function() - run_remote('--remote', fname, other_fname) + eq({ '', '' }, run_remote('--remote', fname, other_fname)) expect(contents) command('next') expect(other_contents) @@ -83,7 +96,7 @@ describe('Remote', function() end) it('send keys', function() - run_remote('--remote-send', ':edit '..fname..'<CR><C-W>v') + eq({ '', '' }, run_remote('--remote-send', ':edit '..fname..'<CR><C-W>v')) expect(contents) eq(2, #funcs.getwininfo()) -- Only a single buffer as we're using edit and not drop like --remote does @@ -91,8 +104,13 @@ describe('Remote', function() end) it('evaluate expressions', function() - run_remote('--remote-expr', 'setline(1, "Yo")') + eq({ '0', '' }, run_remote('--remote-expr', 'setline(1, "Yo")')) + eq({ 'Yo', '' }, run_remote('--remote-expr', 'getline(1)')) expect('Yo') + eq({ ('k'):rep(1234), '' }, run_remote('--remote-expr', 'repeat("k", 1234)')) + eq({ '1.25', '' }, run_remote('--remote-expr', '1.25')) + eq({ 'no', '' }, run_remote('--remote-expr', '0z6E6F')) + eq({ '\t', '' }, run_remote('--remote-expr', '"\t"')) end) end) @@ -113,7 +131,7 @@ describe('Remote', function() eq(nil, string.find(exec_capture('messages'), 'E247:')) end) - pending('exits with error on', function() + describe('exits with error on', function() local function run_and_check_exit_code(...) local bogus_argv = new_argv(...) diff --git a/test/functional/core/startup_spec.lua b/test/functional/core/startup_spec.lua index 58bf7f8681..3253854093 100644 --- a/test/functional/core/startup_spec.lua +++ b/test/functional/core/startup_spec.lua @@ -101,10 +101,11 @@ describe('startup', function() it('os.exit() sets Nvim exitcode', function() -- tricky: LeakSanitizer triggers on os.exit() and disrupts the return value, disable it exec_lua [[ - local asan_options = os.getenv 'ASAN_OPTIONS' - if asan_options ~= nil and asan_options ~= '' then - vim.uv.os_setenv('ASAN_OPTIONS', asan_options..':detect_leaks=0') + local asan_options = os.getenv('ASAN_OPTIONS') or '' + if asan_options ~= '' then + asan_options = asan_options .. ':' end + vim.uv.os_setenv('ASAN_OPTIONS', asan_options .. ':detect_leaks=0') ]] -- nvim -l foo.lua -arg1 -- a b c assert_l_out([[ @@ -130,12 +131,12 @@ describe('startup', function() end) it('executes stdin "-"', function() - assert_l_out('arg0=- args=2 whoa', + assert_l_out('arg0=- args=2 whoa\n', nil, { 'arg1', 'arg 2' }, '-', "print(('arg0=%s args=%d %s'):format(_G.arg[0], #_G.arg, 'whoa'))") - assert_l_out('biiig input: 1000042', + assert_l_out('biiig input: 1000042\n', nil, nil, '-', @@ -143,14 +144,37 @@ describe('startup', function() eq(0, eval('v:shell_error')) end) + it('does not truncate long print() message', function() + assert_l_out(('k'):rep(1234) .. '\n', nil, nil, '-', "print(('k'):rep(1234))") + end) + + it('does not add newline when unnecessary', function() + assert_l_out('', nil, nil, '-', '') + assert_l_out('foobar\n', nil, nil, '-', [[print('foobar\n')]]) + end) + it('sets _G.arg', function() + -- nvim -l foo.lua + assert_l_out([[ + bufs: + nvim args: 3 + lua args: { + [0] = "test/functional/fixtures/startup.lua" + } + ]], + {}, + {} + ) + eq(0, eval('v:shell_error')) + -- nvim -l foo.lua [args] assert_l_out([[ bufs: nvim args: 7 lua args: { "-arg1", "--arg2", "--", "arg3", [0] = "test/functional/fixtures/startup.lua" - }]], + } + ]], {}, { '-arg1', '--arg2', '--', 'arg3' } ) @@ -162,7 +186,8 @@ describe('startup', function() nvim args: 10 lua args: { "-arg1", "arg 2", "--", "file3", "file4", [0] = "test/functional/fixtures/startup.lua" - }]], + } + ]], { 'file1', 'file2', }, { '-arg1', 'arg 2', '--', 'file3', 'file4' } ) @@ -174,7 +199,8 @@ describe('startup', function() nvim args: 5 lua args: { "-c", "set wrap?", [0] = "test/functional/fixtures/startup.lua" - }]], + } + ]], {}, { '-c', 'set wrap?' } ) @@ -190,7 +216,8 @@ describe('startup', function() nvim args: 7 lua args: { "-c", "set wrap?", [0] = "test/functional/fixtures/startup.lua" - }]], + } + ]], { '-c', 'set wrap?' }, { '-c', 'set wrap?' } ) @@ -198,7 +225,7 @@ describe('startup', function() end) it('disables swapfile/shada/config/plugins', function() - assert_l_out('updatecount=0 shadafile=NONE loadplugins=false scripts=1', + assert_l_out('updatecount=0 shadafile=NONE loadplugins=false scripts=1\n', nil, nil, '-', @@ -207,6 +234,15 @@ describe('startup', function() end) end) + it('--cmd/-c/+ do not truncate long Lua print() message with --headless', function() + local out = funcs.system({ nvim_prog, '-u', 'NONE', '-i', 'NONE', '--headless', + '--cmd', 'lua print(("A"):rep(1234))', + '-c', 'lua print(("B"):rep(1234))', + '+lua print(("C"):rep(1234))', + '+q' }) + eq(('A'):rep(1234) .. '\r\n' .. ('B'):rep(1234) .. '\r\n' .. ('C'):rep(1234), out) + end) + it('pipe at both ends: has("ttyin")==0 has("ttyout")==0', function() -- system() puts a pipe at both ends. local out = funcs.system({ nvim_prog, '-u', 'NONE', '-i', 'NONE', '--headless', diff --git a/test/functional/editor/mark_spec.lua b/test/functional/editor/mark_spec.lua index a6e4b0c5eb..36485ded7a 100644 --- a/test/functional/editor/mark_spec.lua +++ b/test/functional/editor/mark_spec.lua @@ -161,7 +161,7 @@ describe('named marks', function() feed('ifoo<Esc>mA') command('enew') feed('ibar<Esc>') - eq('Vim(print):E20: Mark not set', pcall_err(command, [['Aprint]])) + eq("Vim(print):E20: Mark not set: 'Aprint", pcall_err(command, [['Aprint]])) end) it("leave a context mark when moving with '", function() diff --git a/test/functional/ex_cmds/dict_notifications_spec.lua b/test/functional/ex_cmds/dict_notifications_spec.lua index afa6b519d5..6a0b40bd88 100644 --- a/test/functional/ex_cmds/dict_notifications_spec.lua +++ b/test/functional/ex_cmds/dict_notifications_spec.lua @@ -9,7 +9,7 @@ local command = helpers.command local eval = helpers.eval -describe('VimL dictionary notifications', function() +describe('Vimscript dictionary notifications', function() local channel before_each(function() diff --git a/test/functional/ex_cmds/help_spec.lua b/test/functional/ex_cmds/help_spec.lua index 0ec7249218..aca0cbbaa6 100644 --- a/test/functional/ex_cmds/help_spec.lua +++ b/test/functional/ex_cmds/help_spec.lua @@ -4,6 +4,10 @@ local clear = helpers.clear local command = helpers.command local eq = helpers.eq local funcs = helpers.funcs +local meths = helpers.meths +local mkdir = helpers.mkdir +local rmdir = helpers.rmdir +local write_file = helpers.write_file describe(':help', function() before_each(clear) @@ -25,4 +29,17 @@ describe(':help', function() -- Before #9773, Nvim would crash on quitting the help window. eq(1002, funcs.win_getid()) end) + + it('multibyte help tags work #23975', function() + mkdir('Xhelptags') + finally(function() + rmdir('Xhelptags') + end) + mkdir('Xhelptags/doc') + write_file('Xhelptags/doc/Xhelptags.txt', '*…*') + command('helptags Xhelptags/doc') + command('set rtp+=Xhelptags') + command('help …') + eq('*…*', meths.get_current_line()) + end) end) diff --git a/test/functional/ex_cmds/swapfile_preserve_recover_spec.lua b/test/functional/ex_cmds/swapfile_preserve_recover_spec.lua index 639bc6c94e..206838408f 100644 --- a/test/functional/ex_cmds/swapfile_preserve_recover_spec.lua +++ b/test/functional/ex_cmds/swapfile_preserve_recover_spec.lua @@ -191,7 +191,7 @@ describe('swapfile detection', function() feed('e') -- Chose "Edit" at the swap dialog. screen2:expect(expected_no_dialog) - -- With API (via eval/VimL) call and shortmess+=F + -- With API (via eval/Vimscript) call and shortmess+=F feed(':call nvim_command("edit %")<CR>') screen2:expect{any=[[Found a swap file by the name ".*]] ..[[Xtest_swapdialog_dir[/\].*]]..testfile..[[%.swp"]]} diff --git a/test/functional/fixtures/fake-lsp-server.lua b/test/functional/fixtures/fake-lsp-server.lua index dc0428afdc..6ee9dac2ca 100644 --- a/test/functional/fixtures/fake-lsp-server.lua +++ b/test/functional/fixtures/fake-lsp-server.lua @@ -788,6 +788,9 @@ function tests.code_action_server_side_command() codeActionProvider = { resolveProvider = false, }, + executeCommandProvider = { + commands = {"dummy1"} + }, }, } end, @@ -831,21 +834,21 @@ function tests.code_action_filter() isPreferred = true, command = 'preferred_command', } - local quickfix_action = { + local type_annotate_action = { title = 'Action 3', - kind = 'quickfix', - command = 'quickfix_command', + kind = 'type-annotate', + command = 'type_annotate_command', } - local quickfix_foo_action = { + local type_annotate_foo_action = { title = 'Action 4', - kind = 'quickfix.foo', - command = 'quickfix_foo_command', + kind = 'type-annotate.foo', + command = 'type_annotate_foo_command', } expect_request('textDocument/codeAction', function() - return nil, { action, preferred_action, quickfix_action, quickfix_foo_action, } + return nil, { action, preferred_action, type_annotate_action, type_annotate_foo_action, } end) expect_request('textDocument/codeAction', function() - return nil, { action, preferred_action, quickfix_action, quickfix_foo_action, } + return nil, { action, preferred_action, type_annotate_action, type_annotate_foo_action, } end) notify('shutdown') end; diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua index 6e668b22b0..67275b12a4 100644 --- a/test/functional/helpers.lua +++ b/test/functional/helpers.lua @@ -265,7 +265,7 @@ function module.nvim_prog_abs() end end --- Executes an ex-command. VimL errors manifest as client (lua) errors, but +-- Executes an ex-command. Vimscript errors manifest as client (lua) errors, but -- v:errmsg will not be updated. function module.command(cmd) module.request('nvim_command', cmd) @@ -289,26 +289,26 @@ function module.expect_exit(fn_or_timeout, ...) end end --- Evaluates a VimL expression. --- Fails on VimL error, but does not update v:errmsg. +-- Evaluates a Vimscript expression. +-- Fails on Vimscript error, but does not update v:errmsg. function module.eval(expr) return module.request('nvim_eval', expr) end --- Executes a VimL function via RPC. --- Fails on VimL error, but does not update v:errmsg. +-- Executes a Vimscript function via RPC. +-- Fails on Vimscript error, but does not update v:errmsg. function module.call(name, ...) return module.request('nvim_call_function', name, {...}) end --- Executes a VimL function via Lua. --- Fails on VimL error, but does not update v:errmsg. +-- Executes a Vimscript function via Lua. +-- Fails on Vimscript error, but does not update v:errmsg. function module.call_lua(name, ...) return module.exec_lua([[return vim.call(...)]], name, ...) end -- Sends user input to Nvim. --- Does not fail on VimL error, but v:errmsg will be updated. +-- Does not fail on Vimscript error, but v:errmsg will be updated. local function nvim_feed(input) while #input > 0 do local written = module.request('nvim_input', input) @@ -518,7 +518,7 @@ function module.insert(...) nvim_feed('<ESC>') end --- Executes an ex-command by user input. Because nvim_input() is used, VimL +-- Executes an ex-command by user input. Because nvim_input() is used, Vimscript -- errors will not manifest as client (lua) errors. Use command() for that. function module.feed_command(...) for _, v in ipairs({...}) do diff --git a/test/functional/legacy/glob2regpat_spec.lua b/test/functional/legacy/glob2regpat_spec.lua index 1771f12f85..de304f3e4b 100644 --- a/test/functional/legacy/glob2regpat_spec.lua +++ b/test/functional/legacy/glob2regpat_spec.lua @@ -1,16 +1,10 @@ --- Tests for signs - local helpers = require('test.functional.helpers')(after_each) -local clear, exc_exec = helpers.clear, helpers.exc_exec +local clear = helpers.clear local eq, eval = helpers.eq, helpers.eval describe('glob2regpat()', function() before_each(clear) - it('handles invalid input', function() - eq('Vim(call):E806: Using a Float as a String', - exc_exec('call glob2regpat(1.33)')) - end) it('returns ^$ for empty input', function() eq('^$', eval("glob2regpat('')")) end) diff --git a/test/functional/legacy/051_highlight_spec.lua b/test/functional/legacy/highlight_spec.lua index d3f2897493..0a130f1607 100644 --- a/test/functional/legacy/051_highlight_spec.lua +++ b/test/functional/legacy/highlight_spec.lua @@ -1,5 +1,3 @@ --- Tests for ":highlight". - local Screen = require('test.functional.ui.screen') local helpers = require('test.functional.helpers')(after_each) local clear, feed = helpers.clear, helpers.feed @@ -8,10 +6,11 @@ local eq = helpers.eq local poke_eventloop = helpers.poke_eventloop local exc_exec = helpers.exc_exec local feed_command = helpers.feed_command +local exec = helpers.exec -describe(':highlight', function() - setup(clear) +before_each(clear) +describe(':highlight', function() it('is working', function() local screen = Screen.new(35, 10) screen:attach() @@ -94,3 +93,30 @@ describe(':highlight', function() Group3 xxx cleared]]) end) end) + +describe('Visual selection highlight', function() + -- oldtest: Test_visual_sbr() + it("when 'showbreak' is set", function() + local screen = Screen.new(60, 6) + screen:set_default_attr_ids({ + [0] = {bold = true, foreground = Screen.colors.Blue}, -- NonText + [1] = {background = Screen.colors.LightGrey}, -- Visual + [2] = {bold = true}, -- ModeMsg + }) + screen:attach() + exec([[ + set showbreak=> + call setline(1, 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.') + exe "normal! z1\<CR>" + ]]) + feed('v$') + screen:expect([[ + {0:>}{1:n, no sea takimata sanctus est Lorem ipsum dolor sit amet.}^ | + | + | + | + | + {2:-- VISUAL --} | + ]]) + end) +end) diff --git a/test/functional/legacy/memory_usage_spec.lua b/test/functional/legacy/memory_usage_spec.lua index 59839157ea..bf37315914 100644 --- a/test/functional/legacy/memory_usage_spec.lua +++ b/test/functional/legacy/memory_usage_spec.lua @@ -13,7 +13,7 @@ local is_os = helpers.is_os local is_ci = helpers.is_ci local function isasan() - local version = eval('execute("version")') + local version = eval('execute("verbose version")') return version:match('-fsanitize=[a-z,]*address') end diff --git a/test/functional/legacy/messages_spec.lua b/test/functional/legacy/messages_spec.lua index 794676153c..a604e68822 100644 --- a/test/functional/legacy/messages_spec.lua +++ b/test/functional/legacy/messages_spec.lua @@ -49,6 +49,71 @@ describe('messages', function() ]]) end) + -- oldtest: Test_message_not_cleared_after_mode() + it('clearing mode does not remove message', function() + screen = Screen.new(60, 10) + screen:set_default_attr_ids({ + [0] = {bold = true, foreground = Screen.colors.Blue}, -- NonText + [1] = {background = Screen.colors.Red, foreground = Screen.colors.White}, -- ErrorMsg + }) + screen:attach() + exec([[ + nmap <silent> gx :call DebugSilent('normal')<CR> + vmap <silent> gx :call DebugSilent('visual')<CR> + function DebugSilent(arg) + echomsg "from DebugSilent" a:arg + endfunction + set showmode + set cmdheight=1 + call setline(1, ['one', 'NoSuchFile', 'three']) + ]]) + + feed('gx') + screen:expect([[ + ^one | + NoSuchFile | + three | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + from DebugSilent normal | + ]]) + + -- removing the mode message used to also clear the intended message + feed('vEgx') + screen:expect([[ + ^one | + NoSuchFile | + three | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + from DebugSilent visual | + ]]) + + -- removing the mode message used to also clear the error message + command('set cmdheight=2') + feed('2GvEgf') + screen:expect([[ + one | + NoSuchFil^e | + three | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + from DebugSilent visual | + {1:E447: Can't find file "NoSuchFile" in path} | + ]]) + end) + describe('more prompt', function() before_each(function() command('set more') diff --git a/test/functional/legacy/scroll_opt_spec.lua b/test/functional/legacy/scroll_opt_spec.lua index 3b755ff3e8..ae6a409762 100644 --- a/test/functional/legacy/scroll_opt_spec.lua +++ b/test/functional/legacy/scroll_opt_spec.lua @@ -939,6 +939,49 @@ describe('smoothscroll', function() ]]) end) + -- oldtest: Test_smoothscroll_zero_width_scroll_cursor_bot() + it('does not divide by zero in zero-width window', function() + screen:try_resize(40, 19) + screen:set_default_attr_ids({ + [1] = {foreground = Screen.colors.Brown}; -- LineNr + [2] = {bold = true, foreground = Screen.colors.Blue}; -- NonText + [3] = {bold = true, reverse = true}; -- StatusLine + [4] = {reverse = true}; -- StatusLineNC + }) + exec([[ + silent normal yy + silent normal 19p + set cpoptions+=n + vsplit + vertical resize 0 + set foldcolumn=1 + set number + set smoothscroll + silent normal 20G + ]]) + screen:expect([[ + {1: }│ | + {2:@}│ | + {2:@}│ | + {2:@}│ | + {2:@}│ | + {2:@}│ | + {2:@}│ | + {2:@}│ | + {2:@}│ | + {2:@}│ | + {2:@}│ | + {2:@}│ | + {2:@}│ | + {2:@}│ | + {2:@}│ | + {2:@}│ | + {2:^@}│ | + {3:< }{4:[No Name] [+] }| + | + ]]) + end) + it("works with virt_lines above and below", function() screen:try_resize(55, 7) exec([=[ diff --git a/test/functional/lua/fs_spec.lua b/test/functional/lua/fs_spec.lua index aae0ed91a7..c1091cff60 100644 --- a/test/functional/lua/fs_spec.lua +++ b/test/functional/lua/fs_spec.lua @@ -281,6 +281,12 @@ describe('vim.fs', function() it('works with backward slashes', function() eq('C:/Users/jdoe', exec_lua [[ return vim.fs.normalize('C:\\Users\\jdoe') ]]) end) + it('removes trailing /', function() + eq('/home/user', exec_lua [[ return vim.fs.normalize('/home/user/') ]]) + end) + it('works with /', function() + eq('/', exec_lua [[ return vim.fs.normalize('/') ]]) + end) it('works with ~', function() eq( exec_lua([[ local home = ... diff --git a/test/functional/lua/help_spec.lua b/test/functional/lua/help_spec.lua index d66d9f7fbe..8d843cd208 100644 --- a/test/functional/lua/help_spec.lua +++ b/test/functional/lua/help_spec.lua @@ -19,10 +19,12 @@ describe(':help docs', function() local rv = exec_lua([[return require('scripts.gen_help_html').validate('./build/runtime/doc')]]) -- Check that we actually found helpfiles. ok(rv.helpfiles > 100, '>100 :help files', rv.helpfiles) + + eq({}, rv.parse_errors, 'no parse errors') + eq(0, rv.err_count, 'no parse errors') eq({}, rv.invalid_links, 'invalid tags in :help docs') eq({}, rv.invalid_urls, 'invalid URLs in :help docs') - -- Check that parse errors did not increase. - ok(rv.err_count == 0, 'no parse errors', rv.err_count) + eq({}, rv.invalid_spelling, 'invalid spelling in :help docs (see spell_dict in scripts/gen_help_html.lua)') end) it('gen_help_html.lua generates HTML', function() diff --git a/test/functional/lua/iter_spec.lua b/test/functional/lua/iter_spec.lua index 6e1ecc2f7e..3b603c9911 100644 --- a/test/functional/lua/iter_spec.lua +++ b/test/functional/lua/iter_spec.lua @@ -4,6 +4,15 @@ local matches = helpers.matches local pcall_err = helpers.pcall_err describe('vim.iter', function() + it('new() on iterable class instance', function() + local rb = vim.ringbuf(3) + rb:push("a") + rb:push("b") + + local it = vim.iter(rb) + eq({"a", "b"}, it:totable()) + end) + it('filter()', function() local function odd(v) return v % 2 ~= 0 diff --git a/test/functional/lua/json_spec.lua b/test/functional/lua/json_spec.lua index fbb21bfd57..25fdb48eea 100644 --- a/test/functional/lua/json_spec.lua +++ b/test/functional/lua/json_spec.lua @@ -1,20 +1,57 @@ local helpers = require('test.functional.helpers')(after_each) local clear = helpers.clear -local NIL = helpers.NIL local exec_lua = helpers.exec_lua local eq = helpers.eq +local pcall_err = helpers.pcall_err -describe('vim.json.decode function', function() +describe('vim.json.decode()', function() before_each(function() clear() end) it('parses null, true, false', function() - eq(NIL, exec_lua([[return vim.json.decode('null')]])) + eq(vim.NIL, exec_lua([[return vim.json.decode('null')]])) eq(true, exec_lua([[return vim.json.decode('true')]])) eq(false, exec_lua([[return vim.json.decode('false')]])) end) + it('validation', function() + eq('Expected object key string but found invalid token at character 2', + pcall_err(exec_lua, [[return vim.json.decode('{a:"b"}')]])) + end) + + it('options', function() + local jsonstr = '{"arr":[1,2,null],"bar":[3,7],"foo":{"a":"b"},"baz":null}' + eq({ + arr = { 1, 2, vim.NIL }, + bar = { 3, 7 }, + baz = vim.NIL, + foo = { a = 'b' }, + }, + exec_lua([[return vim.json.decode(..., {})]], jsonstr)) + eq({ + arr = { 1, 2, vim.NIL }, + bar = { 3, 7 }, + -- baz = nil, + foo = { a = 'b' }, + }, + exec_lua([[return vim.json.decode(..., { luanil = { object = true } })]], jsonstr)) + eq({ + arr = { 1, 2 }, + bar = { 3, 7 }, + baz = vim.NIL, + foo = { a = 'b' }, + }, + exec_lua([[return vim.json.decode(..., { luanil = { array = true } })]], jsonstr)) + eq({ + arr = { 1, 2 }, + bar = { 3, 7 }, + -- baz = nil, + foo = { a = 'b' }, + }, + exec_lua([[return vim.json.decode(..., { luanil = { array = true, object = true } })]], jsonstr)) + end) + it('parses integer numbers', function() eq(100000, exec_lua([[return vim.json.decode('100000')]])) eq(-100000, exec_lua([[return vim.json.decode('-100000')]])) @@ -60,7 +97,7 @@ describe('vim.json.decode function', function() it('parses containers', function() eq({1}, exec_lua([[return vim.json.decode('[1]')]])) - eq({NIL, 1}, exec_lua([[return vim.json.decode('[null, 1]')]])) + eq({vim.NIL, 1}, exec_lua([[return vim.json.decode('[null, 1]')]])) eq({['1']=2}, exec_lua([[return vim.json.decode('{"1": 2}')]])) eq({['1']=2, ['3']={{['4']={['5']={{}, 1}}}}}, exec_lua([[return vim.json.decode('{"1": 2, "3": [{"4": {"5": [ [], 1]}}]}')]])) @@ -88,43 +125,43 @@ describe('vim.json.decode function', function() end) -describe('vim.json.encode function', function() +describe('vim.json.encode()', function() before_each(function() clear() end) - it('dumps strings', function() - eq('"Test"', exec_lua([[return vim.json.encode('Test')]])) - eq('""', exec_lua([[return vim.json.encode('')]])) - eq('"\\t"', exec_lua([[return vim.json.encode('\t')]])) - eq('"\\n"', exec_lua([[return vim.json.encode('\n')]])) - -- vim.fn.json_encode return \\u001B - eq('"\\u001b"', exec_lua([[return vim.json.encode('\27')]])) - eq('"þÿþ"', exec_lua([[return vim.json.encode('þÿþ')]])) - end) - - it('dumps numbers', function() - eq('0', exec_lua([[return vim.json.encode(0)]])) - eq('10', exec_lua([[return vim.json.encode(10)]])) - eq('-10', exec_lua([[return vim.json.encode(-10)]])) - end) - - it('dumps floats', function() - eq('10.5', exec_lua([[return vim.json.encode(10.5)]])) - eq('-10.5', exec_lua([[return vim.json.encode(-10.5)]])) - eq('-1e-05', exec_lua([[return vim.json.encode(-1e-5)]])) - end) - - it('dumps lists', function() - eq('[]', exec_lua([[return vim.json.encode({})]])) - eq('[[]]', exec_lua([[return vim.json.encode({{}})]])) - eq('[[],[]]', exec_lua([[return vim.json.encode({{}, {}})]])) - end) - - it('dumps dictionaries', function() - eq('{}', exec_lua([[return vim.json.encode(vim.empty_dict())]])) - eq('{"d":[]}', exec_lua([[return vim.json.encode({d={}})]])) - end) + it('dumps strings', function() + eq('"Test"', exec_lua([[return vim.json.encode('Test')]])) + eq('""', exec_lua([[return vim.json.encode('')]])) + eq('"\\t"', exec_lua([[return vim.json.encode('\t')]])) + eq('"\\n"', exec_lua([[return vim.json.encode('\n')]])) + -- vim.fn.json_encode return \\u001B + eq('"\\u001b"', exec_lua([[return vim.json.encode('\27')]])) + eq('"þÿþ"', exec_lua([[return vim.json.encode('þÿþ')]])) + end) + + it('dumps numbers', function() + eq('0', exec_lua([[return vim.json.encode(0)]])) + eq('10', exec_lua([[return vim.json.encode(10)]])) + eq('-10', exec_lua([[return vim.json.encode(-10)]])) + end) + + it('dumps floats', function() + eq('10.5', exec_lua([[return vim.json.encode(10.5)]])) + eq('-10.5', exec_lua([[return vim.json.encode(-10.5)]])) + eq('-1e-05', exec_lua([[return vim.json.encode(-1e-5)]])) + end) + + it('dumps lists', function() + eq('[]', exec_lua([[return vim.json.encode({})]])) + eq('[[]]', exec_lua([[return vim.json.encode({{}})]])) + eq('[[],[]]', exec_lua([[return vim.json.encode({{}, {}})]])) + end) + + it('dumps dictionaries', function() + eq('{}', exec_lua([[return vim.json.encode(vim.empty_dict())]])) + eq('{"d":[]}', exec_lua([[return vim.json.encode({d={}})]])) + end) it('dumps vim.NIL', function() eq('null', exec_lua([[return vim.json.encode(vim.NIL)]])) diff --git a/test/functional/lua/runtime_spec.lua b/test/functional/lua/runtime_spec.lua index cd19ea4e49..e80a65ef3d 100644 --- a/test/functional/lua/runtime_spec.lua +++ b/test/functional/lua/runtime_spec.lua @@ -138,6 +138,30 @@ describe('runtime:', function() exec [[set filetype=new-ft]] eq(1, eval('b:lua_ftplugin')) end) + + it("'rtp' order is respected", function() + local after_ftplugin_folder = table.concat({plug_dir, 'after', 'ftplugin'}, sep) + mkdir_p(table.concat({ftplugin_folder, 'new-ft'}, sep)) + mkdir_p(table.concat({after_ftplugin_folder, 'new-ft'}, sep)) + exec('set rtp+=' .. plug_dir .. '/after') + exec('let g:seq = ""') + -- A .lua file is loaded after a .vim file if they only differ in extension. + -- All files in after/ftplugin/ are loaded after all files in ftplugin/. + write_file(table.concat({ftplugin_folder, 'new-ft.vim'}, sep), [[let g:seq ..= 'A']]) + write_file(table.concat({ftplugin_folder, 'new-ft.lua'}, sep), [[vim.g.seq = vim.g.seq .. 'B']]) + write_file(table.concat({ftplugin_folder, 'new-ft_a.vim'}, sep), [[let g:seq ..= 'C']]) + write_file(table.concat({ftplugin_folder, 'new-ft_a.lua'}, sep), [[vim.g.seq = vim.g.seq .. 'D']]) + write_file(table.concat({ftplugin_folder, 'new-ft', 'a.vim'}, sep), [[let g:seq ..= 'E']]) + write_file(table.concat({ftplugin_folder, 'new-ft', 'a.lua'}, sep), [[vim.g.seq = vim.g.seq .. 'F']]) + write_file(table.concat({after_ftplugin_folder, 'new-ft.vim'}, sep), [[let g:seq ..= 'a']]) + write_file(table.concat({after_ftplugin_folder, 'new-ft.lua'}, sep), [[vim.g.seq = vim.g.seq .. 'b']]) + write_file(table.concat({after_ftplugin_folder, 'new-ft_a.vim'}, sep), [[let g:seq ..= 'c']]) + write_file(table.concat({after_ftplugin_folder, 'new-ft_a.lua'}, sep), [[vim.g.seq = vim.g.seq .. 'd']]) + write_file(table.concat({after_ftplugin_folder, 'new-ft', 'a.vim'}, sep), [[let g:seq ..= 'e']]) + write_file(table.concat({after_ftplugin_folder, 'new-ft', 'a.lua'}, sep), [[vim.g.seq = vim.g.seq .. 'f']]) + exec('setfiletype new-ft') + eq('ABCDEFabcdef', eval('g:seq')) + end) end) describe('indent', function() diff --git a/test/functional/lua/system_spec.lua b/test/functional/lua/system_spec.lua new file mode 100644 index 0000000000..836d3a83b0 --- /dev/null +++ b/test/functional/lua/system_spec.lua @@ -0,0 +1,57 @@ +local helpers = require('test.functional.helpers')(after_each) +local clear = helpers.clear +local exec_lua = helpers.exec_lua +local eq = helpers.eq + +local function system_sync(cmd, opts) + return exec_lua([[ + return vim.system(...):wait() + ]], cmd, opts) +end + +local function system_async(cmd, opts) + exec_lua([[ + local cmd, opts = ... + _G.done = false + vim.system(cmd, opts, function(obj) + _G.done = true + _G.ret = obj + end) + ]], cmd, opts) + + while true do + if exec_lua[[return _G.done]] then + break + end + end + + return exec_lua[[return _G.ret]] +end + +describe('vim.system', function() + before_each(function() + clear() + end) + + for name, system in pairs{ sync = system_sync, async = system_async, } do + describe('('..name..')', function() + it('can run simple commands', function() + eq('hello\n', system({'echo', 'hello' }, { text = true }).stdout) + end) + + it('handle input', function() + eq('hellocat', system({ 'cat' }, { stdin = 'hellocat', text = true }).stdout) + end) + + it ('supports timeout', function() + eq({ + code = 0, + signal = 2, + stdout = '', + stderr = "Command timed out: 'sleep 10'" + }, system({ 'sleep', '10' }, { timeout = 1 })) + end) + end) + end + +end) diff --git a/test/functional/lua/version_spec.lua b/test/functional/lua/version_spec.lua index 75b62e8318..d1c981c388 100644 --- a/test/functional/lua/version_spec.lua +++ b/test/functional/lua/version_spec.lua @@ -17,6 +17,18 @@ describe('version', function() eq({ major = 42, minor = 3, patch = 99 }, exec_lua("return vim.version.parse('v42.3.99')")) end) + it('version() returns Nvim version', function() + local expected = exec_lua('return vim.fn.api_info().version') + local actual = exec_lua('return vim.version()') + eq(expected.major, actual.major) + eq(expected.minor, actual.minor) + eq(expected.patch, actual.patch) + eq(expected.prerelease and 'dev' or nil, actual.prerelease) + + -- tostring() #23863 + matches([[%d+%.%d+%.%d+]], exec_lua('return tostring(vim.version())')) + end) + describe('_version()', function() local tests = { ['v1.2.3'] = { major = 1, minor = 2, patch = 3 }, @@ -40,6 +52,8 @@ describe('version', function() ['=1.2.3'] = { from = { 1, 2, 3 }, to = { 1, 2, 4 } }, ['>1.2.3'] = { from = { 1, 2, 4 } }, ['>=1.2.3'] = { from = { 1, 2, 3 } }, + ['<1.2.3'] = { from = { 0, 0, 0 }, to = { 1, 2, 3 } }, + ['<=1.2.3'] = { from = { 0, 0, 0 }, to = { 1, 2, 4 } }, ['~1.2.3'] = { from = { 1, 2, 3 }, to = { 1, 3, 0 } }, ['^1.2.3'] = { from = { 1, 2, 3 }, to = { 2, 0, 0 } }, ['^0.2.3'] = { from = { 0, 2, 3 }, to = { 0, 3, 0 } }, diff --git a/test/functional/lua/vim_spec.lua b/test/functional/lua/vim_spec.lua index 55c03e21b3..d5f550a5d1 100644 --- a/test/functional/lua/vim_spec.lua +++ b/test/functional/lua/vim_spec.lua @@ -3040,6 +3040,46 @@ describe('lua stdlib', function() eq(4, exec_lua [[ return vim.re.match("abcde", '[a-c]+') ]]) end) + + it("vim.ringbuf", function() + local results = exec_lua([[ + local ringbuf = vim.ringbuf(3) + ringbuf:push("a") -- idx: 0 + local peeka1 = ringbuf:peek() + local peeka2 = ringbuf:peek() + local popa = ringbuf:pop() + local popnil = ringbuf:pop() + ringbuf:push("a") -- idx: 1 + ringbuf:push("b") -- idx: 2 + + -- doesn't read last added item, but uses separate read index + local pop_after_add_b = ringbuf:pop() + + ringbuf:push("c") -- idx: 3 wraps around, overrides idx: 0 "a" + ringbuf:push("d") -- idx: 4 wraps around, overrides idx: 1 "a" + return { + peeka1 = peeka1, + peeka2 = peeka2, + pop1 = popa, + pop2 = popnil, + pop3 = ringbuf:pop(), + pop4 = ringbuf:pop(), + pop5 = ringbuf:pop(), + pop_after_add_b = pop_after_add_b, + } + ]]) + local expected = { + peeka1 = "a", + peeka2 = "a", + pop1 = "a", + pop2 = nil, + pop3 = "b", + pop4 = "c", + pop5 = "d", + pop_after_add_b = "a", + } + eq(expected, results) + end) end) describe('lua: builtin modules', function() diff --git a/test/functional/lua/watch_spec.lua b/test/functional/lua/watch_spec.lua index ad8678c17a..f041f4f1b6 100644 --- a/test/functional/lua/watch_spec.lua +++ b/test/functional/lua/watch_spec.lua @@ -107,6 +107,7 @@ describe('vim._watch', function() local result = exec_lua( [[ local root_dir = ... + local lpeg = vim.lpeg local events = {} @@ -118,7 +119,13 @@ describe('vim._watch', function() assert(vim.wait(poll_wait_ms, function() return #events == expected_events end), 'Timed out waiting for expected number of events. Current events seen so far: ' .. vim.inspect(events)) end - local stop = vim._watch.poll(root_dir, { interval = poll_interval_ms }, function(path, change_type) + local incl = lpeg.P(root_dir) * lpeg.P("/file")^-1 + local excl = lpeg.P(root_dir..'/file.unwatched') + local stop = vim._watch.poll(root_dir, { + interval = poll_interval_ms, + include_pattern = incl, + exclude_pattern = excl, + }, function(path, change_type) table.insert(events, { path = path, change_type = change_type }) end) @@ -127,12 +134,17 @@ describe('vim._watch', function() local watched_path = root_dir .. '/file' local watched, err = io.open(watched_path, 'w') assert(not err, err) + local unwatched_path = root_dir .. '/file.unwatched' + local unwatched, err = io.open(unwatched_path, 'w') + assert(not err, err) expected_events = expected_events + 2 wait_for_events() watched:close() os.remove(watched_path) + unwatched:close() + os.remove(unwatched_path) expected_events = expected_events + 2 wait_for_events() diff --git a/test/functional/plugin/health_spec.lua b/test/functional/plugin/health_spec.lua index 488a213a9e..50b1d03f36 100644 --- a/test/functional/plugin/health_spec.lua +++ b/test/functional/plugin/health_spec.lua @@ -136,7 +136,7 @@ describe('health.vim', function() Bar = { foreground = Screen.colors.LightGrey, background = Screen.colors.DarkGrey }, }) command("checkhealth foo success1") - command("set nowrap laststatus=0") + command("set nofoldenable nowrap laststatus=0") screen:expect{grid=[[ ^ | {Bar:──────────────────────────────────────────────────}| @@ -153,6 +153,22 @@ describe('health.vim', function() ]]} end) + it("fold healthchecks", function() + local screen = Screen.new(50, 7) + screen:attach() + command("checkhealth foo success1") + command("set nowrap laststatus=0") + screen:expect{grid=[[ + ^ | + ──────────────────────────────────────────────────| + +WE 4 lines: foo: ·······························| + ──────────────────────────────────────────────────| + +-- 8 lines: test_plug.success1: require("test_pl| + ~ | + | + ]]} + end) + it("gracefully handles invalid healthcheck", function() command("checkhealth non_existent_healthcheck") -- luacheck: ignore 613 diff --git a/test/functional/plugin/lsp/inlay_hint_spec.lua b/test/functional/plugin/lsp/inlay_hint_spec.lua new file mode 100644 index 0000000000..b19f2ba146 --- /dev/null +++ b/test/functional/plugin/lsp/inlay_hint_spec.lua @@ -0,0 +1,139 @@ +local helpers = require('test.functional.helpers')(after_each) +local lsp_helpers = require('test.functional.plugin.lsp.helpers') +local Screen = require('test.functional.ui.screen') + +local eq = helpers.eq +local dedent = helpers.dedent +local exec_lua = helpers.exec_lua +local insert = helpers.insert + +local clear_notrace = lsp_helpers.clear_notrace +local create_server_definition = lsp_helpers.create_server_definition + +before_each(function() + clear_notrace() +end) + +after_each(function() + exec_lua("vim.api.nvim_exec_autocmds('VimLeavePre', { modeline = false })") +end) + +describe('inlay hints', function() + local screen + before_each(function() + screen = Screen.new(50, 9) + screen:attach() + end) + + describe('general', function() + local text = dedent([[ + auto add(int a, int b) { return a + b; } + + int main() { + int x = 1; + int y = 2; + return add(x,y); + } + }]]) + + + local response = [==[ + [ + {"kind":1,"paddingLeft":false,"label":"-> int","position":{"character":22,"line":0},"paddingRight":false}, + {"kind":2,"paddingLeft":false,"label":"a:","position":{"character":15,"line":5},"paddingRight":true}, + {"kind":2,"paddingLeft":false,"label":"b:","position":{"character":17,"line":5},"paddingRight":true} + ] + ]==] + + + before_each(function() + exec_lua(create_server_definition) + exec_lua([[ + local response = ... + server = _create_server({ + capabilities = { + inlayHintProvider = true, + }, + handlers = { + ['textDocument/inlayHint'] = function() + return vim.json.decode(response) + end, + } + }) + ]], response) + end) + + it( + 'inlay hints are applied when vim.lsp.inlay_hint(true) is called', + function() + local res = exec_lua([[ + bufnr = vim.api.nvim_get_current_buf() + vim.api.nvim_win_set_buf(0, bufnr) + client_id = vim.lsp.start({ name = 'dummy', cmd = server.cmd }) + local client = vim.lsp.get_client_by_id(client_id) + return { + supports_method = client.supports_method("textDocument/inlayHint") + } + ]]) + eq(res, { supports_method = true }) + + + insert(text) + exec_lua([[vim.lsp.inlay_hint(bufnr, true)]]) + screen:expect({ + grid = [[ + auto add(int a, int b)-> int { return a + b; } | + | + int main() { | + int x = 1; | + int y = 2; | + return add(a: x,b: y); | + } | + ^} | + | +]] + }) + end) + + it( + 'inlay hints are cleared when vim.lsp.inlay_hint(false) is called', + function() + exec_lua([[ + bufnr = vim.api.nvim_get_current_buf() + vim.api.nvim_win_set_buf(0, bufnr) + client_id = vim.lsp.start({ name = 'dummy', cmd = server.cmd }) + ]]) + + insert(text) + exec_lua([[vim.lsp.inlay_hint(bufnr, true)]]) + screen:expect({ + grid = [[ + auto add(int a, int b)-> int { return a + b; } | + | + int main() { | + int x = 1; | + int y = 2; | + return add(a: x,b: y); | + } | + ^} | + | +]] + }) + exec_lua([[vim.lsp.inlay_hint(bufnr, false)]]) + screen:expect({ + grid = [[ + auto add(int a, int b) { return a + b; } | + | + int main() { | + int x = 1; | + int y = 2; | + return add(x,y); | + } | + ^} | + | +]], + unchanged = true + }) + end) + end) +end) diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua index 85138417ff..8eced4bfb5 100644 --- a/test/functional/plugin/lsp_spec.lua +++ b/test/functional/plugin/lsp_spec.lua @@ -1070,7 +1070,7 @@ describe('LSP', function() eq(full_kind, client.server_capabilities().textDocumentSync.change) eq(true, client.server_capabilities().textDocumentSync.openClose) exec_lua [[ - assert(not lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID), "Shouldn't attach twice") + assert(lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID), "Already attached, returns true") ]] end; on_exit = function(code, signal) @@ -3230,9 +3230,10 @@ describe('LSP', function() eq(0, signal, "exit signal") end; on_handler = function(err, result, ctx) - -- Don't compare & assert params, they're not relevant for the testcase + -- Don't compare & assert params and version, they're not relevant for the testcase -- This allows us to be lazy and avoid declaring them ctx.params = nil + ctx.version = nil eq(table.remove(test.expected_handlers), {err, result, ctx}, "expected handler") if ctx.method == 'start' then @@ -3314,6 +3315,7 @@ describe('LSP', function() end, on_handler = function(err, result, ctx) ctx.params = nil -- don't compare in assert + ctx.version = nil eq(table.remove(expected_handlers), { err, result, ctx }) if ctx.method == 'start' then exec_lua([[ @@ -3355,22 +3357,22 @@ describe('LSP', function() vim.lsp.commands['executed_preferred'] = function() end end - vim.lsp.commands['quickfix_command'] = function(cmd) - vim.lsp.commands['executed_quickfix'] = function() + vim.lsp.commands['type_annotate_command'] = function(cmd) + vim.lsp.commands['executed_type_annotate'] = function() end end local bufnr = vim.api.nvim_get_current_buf() vim.lsp.buf_attach_client(bufnr, TEST_RPC_CLIENT_ID) vim.lsp.buf.code_action({ filter = function(a) return a.isPreferred end, apply = true, }) vim.lsp.buf.code_action({ - -- expect to be returned actions 'quickfix' and 'quickfix.foo' - context = { only = {'quickfix'}, }, + -- expect to be returned actions 'type-annotate' and 'type-annotate.foo' + context = { only = { 'type-annotate' }, }, apply = true, filter = function(a) - if a.kind == 'quickfix.foo' then - vim.lsp.commands['filtered_quickfix_foo'] = function() end + if a.kind == 'type-annotate.foo' then + vim.lsp.commands['filtered_type_annotate_foo'] = function() end return false - elseif a.kind == 'quickfix' then + elseif a.kind == 'type-annotate' then return true else assert(nil, 'unreachable') @@ -3380,8 +3382,8 @@ describe('LSP', function() ]]) elseif ctx.method == 'shutdown' then eq('function', exec_lua[[return type(vim.lsp.commands['executed_preferred'])]]) - eq('function', exec_lua[[return type(vim.lsp.commands['filtered_quickfix_foo'])]]) - eq('function', exec_lua[[return type(vim.lsp.commands['executed_quickfix'])]]) + eq('function', exec_lua[[return type(vim.lsp.commands['filtered_type_annotate_foo'])]]) + eq('function', exec_lua[[return type(vim.lsp.commands['executed_type_annotate'])]]) client.stop() end end diff --git a/test/functional/provider/clipboard_spec.lua b/test/functional/provider/clipboard_spec.lua index c8f1518283..0099183302 100644 --- a/test/functional/provider/clipboard_spec.lua +++ b/test/functional/provider/clipboard_spec.lua @@ -211,7 +211,7 @@ describe('clipboard', function() eq('', eval('provider#clipboard#Error()')) end) - it('g:clipboard using VimL functions', function() + it('g:clipboard using Vimscript functions', function() -- Implements a fake clipboard provider. cache_enabled is meaningless here. source([[let g:clipboard = { \ 'name': 'custom', @@ -245,7 +245,7 @@ describe('clipboard', function() eq({{'star', ''}, 'b'}, eval("g:dummy_clipboard_star")) end) - describe('g:clipboard[paste] VimL function', function() + describe('g:clipboard[paste] Vimscript function', function() it('can return empty list for empty clipboard', function() source([[let g:dummy_clipboard = [] let g:clipboard = { diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua index 7723b6c7e7..b1836f2e4d 100644 --- a/test/functional/terminal/tui_spec.lua +++ b/test/functional/terminal/tui_spec.lua @@ -2475,7 +2475,7 @@ describe("TUI as a client", function() -- grid smaller than containing terminal window is cleared properly feed_data(":call setline(1,['a'->repeat(&columns)]->repeat(&lines))\n") - feed_data("0:set lines=2\n") + feed_data("0:set lines=3\n") screen_server:expect{grid=[[ {1:a}aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| {5:[No Name] [+] }| diff --git a/test/functional/treesitter/parser_spec.lua b/test/functional/treesitter/parser_spec.lua index e463382d07..7cfe5b69de 100644 --- a/test/functional/treesitter/parser_spec.lua +++ b/test/functional/treesitter/parser_spec.lua @@ -783,7 +783,7 @@ int x = INT_MAX; return list ]] - eq({ 'gsub!', 'offset!', 'set!' }, res_list) + eq({ 'gsub!', 'inject-language!', 'offset!', 'set!', 'trim!' }, res_list) end) end) end) @@ -922,12 +922,6 @@ int x = INT_MAX; [19] = '1' }, get_fold_levels()) helpers.command('1,2d') - helpers.poke_eventloop() - - exec_lua([[vim.treesitter.get_parser():parse()]]) - - helpers.poke_eventloop() - helpers.sleep(100) eq({ [1] = '0', @@ -947,6 +941,29 @@ int x = INT_MAX; [15] = '2', [16] = '1', [17] = '0' }, get_fold_levels()) + + helpers.command('1put!') + + eq({ + [1] = '>1', + [2] = '1', + [3] = '1', + [4] = '1', + [5] = '>2', + [6] = '2', + [7] = '2', + [8] = '1', + [9] = '1', + [10] = '>2', + [11] = '2', + [12] = '2', + [13] = '2', + [14] = '2', + [15] = '>3', + [16] = '3', + [17] = '3', + [18] = '2', + [19] = '1' }, get_fold_levels()) end) it('tracks the root range properly (#22911)', function() diff --git a/test/functional/ui/cmdline_spec.lua b/test/functional/ui/cmdline_spec.lua index a9469bdf2d..dc29b765bd 100644 --- a/test/functional/ui/cmdline_spec.lua +++ b/test/functional/ui/cmdline_spec.lua @@ -944,6 +944,45 @@ describe('statusline is redrawn on entering cmdline', function() end) end) +it('tabline is not redrawn in Ex mode #24122', function() + clear() + local screen = Screen.new(60, 5) + screen:set_default_attr_ids({ + [0] = {bold = true, foreground = Screen.colors.Blue}, -- NonText + [1] = {bold = true, reverse = true}, -- MsgSeparator + [2] = {reverse = true}, -- TabLineFill + }) + screen:attach() + + exec([[ + set showtabline=2 + set tabline=%!MyTabLine() + + function! MyTabLine() + + return "foo" + endfunction + ]]) + + feed('gQ') + screen:expect{grid=[[ + {2:foo }| + | + {1: }| + Entering Ex mode. Type "visual" to go to Normal mode. | + :^ | + ]]} + + feed('echo 1<CR>') + screen:expect{grid=[[ + {1: }| + Entering Ex mode. Type "visual" to go to Normal mode. | + :echo 1 | + 1 | + :^ | + ]]} +end) + describe("cmdline height", function() it("does not crash resized screen #14263", function() clear() diff --git a/test/functional/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua index cbf0178d28..4c04bad3a0 100644 --- a/test/functional/ui/decorations_spec.lua +++ b/test/functional/ui/decorations_spec.lua @@ -667,6 +667,8 @@ describe('extmark decorations', function() [31] = {underline = true, foreground = Screen.colors.DarkCyan}; [32] = {underline = true}; [33] = {foreground = Screen.colors.DarkBlue, background = Screen.colors.LightGray}; + [34] = {background = Screen.colors.Yellow}; + [35] = {background = Screen.colors.Yellow, bold = true, foreground = Screen.colors.Blue}; } ns = meths.create_namespace 'test' @@ -890,6 +892,25 @@ describe('extmark decorations', function() ]]} end) + it('overlay virtual text works on and after a TAB #24022', function() + screen:try_resize(40, 3) + meths.buf_set_lines(0, 0, -1, true, {'\t\tline 1'}) + meths.buf_set_extmark(0, ns, 0, 0, { virt_text = {{'AA', 'Search'}}, virt_text_pos = 'overlay', hl_mode = 'combine' }) + meths.buf_set_extmark(0, ns, 0, 1, { virt_text = {{'BB', 'Search'}}, virt_text_pos = 'overlay', hl_mode = 'combine' }) + meths.buf_set_extmark(0, ns, 0, 2, { virt_text = {{'CC', 'Search'}}, virt_text_pos = 'overlay', hl_mode = 'combine' }) + screen:expect{grid=[[ + {34:AA} ^ {34:BB} {34:CC}ne 1 | + {1:~ }| + | + ]]} + command('setlocal list listchars=tab:<->') + screen:expect{grid=[[ + {35:^AA}{1:----->}{35:BB}{1:----->}{34:CC}ne 1 | + {1:~ }| + | + ]]} + end) + it('can have virtual text of overlay position and styling', function() insert(example_text) feed 'gg' @@ -1294,6 +1315,26 @@ describe('extmark decorations', function() ]]) end) + it('conceal works just before truncated double-width char #21486', function() + screen:try_resize(40, 4) + meths.buf_set_lines(0, 0, -1, true, {'', ('a'):rep(37) .. '<>古'}) + meths.buf_set_extmark(0, ns, 1, 37, {end_col=39, conceal=''}) + command('setlocal conceallevel=2') + screen:expect{grid=[[ + ^ | + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa{1:>} | + 古 | + | + ]]} + feed('j') + screen:expect{grid=[[ + | + ^aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa<>{1:>}| + 古 | + | + ]]} + end) + it('avoids redraw issue #20651', function() exec_lua[[ vim.cmd.normal'10oXXX' @@ -1546,13 +1587,15 @@ describe('decorations: inline virtual text', function() [9] = {background = Screen.colors.Plum1}; [10] = {foreground = Screen.colors.SlateBlue}; [11] = {blend = 30, background = Screen.colors.Red1}; - [12] = {background = Screen.colors.Yellow1}; + [12] = {background = Screen.colors.Yellow}; [13] = {reverse = true}; [14] = {foreground = Screen.colors.SlateBlue, background = Screen.colors.LightMagenta}; [15] = {bold = true, reverse = true}; [16] = {foreground = Screen.colors.Red}; [17] = {background = Screen.colors.LightGrey, foreground = Screen.colors.DarkBlue}; [18] = {background = Screen.colors.LightGrey, foreground = Screen.colors.Red}; + [19] = {background = Screen.colors.Yellow, foreground = Screen.colors.SlateBlue}; + [20] = {background = Screen.colors.LightGrey, foreground = Screen.colors.SlateBlue}; } ns = meths.create_namespace 'test' @@ -1618,6 +1661,53 @@ describe('decorations: inline virtual text', function() ]]} end) + it('works with empty chunk', function() + insert(example_text) + feed 'gg' + screen:expect{grid=[[ + ^for _,item in ipairs(items) do | + local text, hl_id_cell, count = unpack(item) | + if hl_id_cell ~= nil then | + hl_id = hl_id_cell | + end | + for _ = 1, (count or 1) do | + local cell = line[colpos] | + cell.text = text | + cell.hl_id = hl_id | + | + ]]} + + meths.buf_set_extmark(0, ns, 0, 5, {virt_text={{''}, {''}}, virt_text_pos='inline'}) + meths.buf_set_extmark(0, ns, 1, 14, {virt_text={{''}, {': ', 'Special'}, {'string', 'Type'}}, virt_text_pos='inline'}) + feed('V') + screen:expect{grid=[[ + ^f{7:or _,item in ipairs(items) do} | + local text{10:: }{3:string}, hl_id_cell, count = unpack| + (item) | + if hl_id_cell ~= nil then | + hl_id = hl_id_cell | + end | + for _ = 1, (count or 1) do | + local cell = line[colpos] | + cell.text = text | + {8:-- VISUAL LINE --} | + ]]} + + feed('<Esc>jf,') + screen:expect{grid=[[ + for _,item in ipairs(items) do | + local text{10:: }{3:string}^, hl_id_cell, count = unpack| + (item) | + if hl_id_cell ~= nil then | + hl_id = hl_id_cell | + end | + for _ = 1, (count or 1) do | + local cell = line[colpos] | + cell.text = text | + | + ]]} + end) + it('cursor positions are correct with multiple inline virtual text', function() insert('12345678') meths.buf_set_extmark(0, ns, 0, 4, @@ -1912,13 +2002,15 @@ bbbbbbb]]) end) it('search highlight is correct', function() - insert('foo foo foo foo') - feed('0') - meths.buf_set_extmark(0, ns, 0, 8, - { virt_text = { { 'virtual text', 'Special' } }, virt_text_pos = 'inline' }) + insert('foo foo foo foo\nfoo foo foo foo') + feed('gg0') + meths.buf_set_extmark(0, ns, 0, 9, { virt_text = { { 'AAA', 'Special' } }, virt_text_pos = 'inline' }) + meths.buf_set_extmark(0, ns, 0, 9, { virt_text = { { 'BBB', 'Special' } }, virt_text_pos = 'inline', hl_mode = 'combine' }) + meths.buf_set_extmark(0, ns, 1, 9, { virt_text = { { 'CCC', 'Special' } }, virt_text_pos = 'inline', hl_mode = 'combine' }) + meths.buf_set_extmark(0, ns, 1, 9, { virt_text = { { 'DDD', 'Special' } }, virt_text_pos = 'inline', hl_mode = 'replace' }) screen:expect { grid = [[ - ^foo foo {10:virtual text}foo foo | - {1:~ }| + ^foo foo f{10:AAABBB}oo foo | + foo foo f{10:CCCDDD}oo foo | {1:~ }| {1:~ }| {1:~ }| @@ -1931,8 +2023,8 @@ bbbbbbb]]) feed('/foo') screen:expect { grid = [[ - {12:foo} {13:foo} {10:virtual text}{12:foo} {12:foo} | - {1:~ }| + {12:foo} {13:foo} {12:f}{10:AAA}{19:BBB}{12:oo} {12:foo} | + {12:foo} {12:foo} {12:f}{19:CCC}{10:DDD}{12:oo} {12:foo} | {1:~ }| {1:~ }| {1:~ }| @@ -1945,14 +2037,16 @@ bbbbbbb]]) end) it('visual select highlight is correct', function() - insert('foo foo foo foo') - feed('0') - meths.buf_set_extmark(0, ns, 0, 8, - { virt_text = { { 'virtual text', 'Special' } }, virt_text_pos = 'inline' }) + insert('foo foo foo foo\nfoo foo foo foo') + feed('gg0') + meths.buf_set_extmark(0, ns, 0, 8, { virt_text = { { 'AAA', 'Special' } }, virt_text_pos = 'inline' }) + meths.buf_set_extmark(0, ns, 0, 8, { virt_text = { { 'BBB', 'Special' } }, virt_text_pos = 'inline', hl_mode = 'combine' }) + meths.buf_set_extmark(0, ns, 1, 8, { virt_text = { { 'CCC', 'Special' } }, virt_text_pos = 'inline', hl_mode = 'combine' }) + meths.buf_set_extmark(0, ns, 1, 8, { virt_text = { { 'DDD', 'Special' } }, virt_text_pos = 'inline', hl_mode = 'replace' }) feed('8l') screen:expect { grid = [[ - foo foo {10:virtual text}^foo foo | - {1:~ }| + foo foo {10:AAABBB}^foo foo | + foo foo {10:CCCDDD}foo foo | {1:~ }| {1:~ }| {1:~ }| @@ -1963,11 +2057,11 @@ bbbbbbb]]) | ]]} - feed('v') - feed('2h') + feed('<C-V>') + feed('2hj') screen:expect { grid = [[ - foo fo^o{7: }{10:virtual text}{7:f}oo foo | - {1:~ }| + foo fo{7:o }{10:AAA}{20:BBB}{7:f}oo foo | + foo fo^o{7: }{20:CCC}{10:DDD}{7:f}oo foo | {1:~ }| {1:~ }| {1:~ }| @@ -1975,7 +2069,7 @@ bbbbbbb]]) {1:~ }| {1:~ }| {1:~ }| - {8:-- VISUAL --} | + {8:-- VISUAL BLOCK --} | ]]} end) @@ -2013,7 +2107,7 @@ bbbbbbb]]) ]]} end) - it('cursor position is correct when inserting around virtual texts with both left and right gravity ', function() + it('cursor position is correct when inserting around virtual texts with both left and right gravity', function() insert('foo foo foo foo') meths.buf_set_extmark(0, ns, 0, 8, { virt_text = {{ '>>', 'Special' }}, virt_text_pos = 'inline', right_gravity = false }) meths.buf_set_extmark(0, ns, 0, 8, { virt_text = {{ '<<', 'Special' }}, virt_text_pos = 'inline', right_gravity = true }) @@ -2128,6 +2222,51 @@ bbbbbbb]]) ]]} end) + it('hidden virtual text does not interfere with Visual highlight', function() + insert('abcdef') + command('set nowrap') + meths.buf_set_extmark(0, ns, 0, 0, { virt_text = { { 'XXX', 'Special' } }, virt_text_pos = 'inline' }) + feed('V2zl') + screen:expect{grid=[[ + {10:X}{7:abcde}^f | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {8:-- VISUAL LINE --} | + ]]} + feed('zl') + screen:expect{grid=[[ + {7:abcde}^f | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {8:-- VISUAL LINE --} | + ]]} + feed('zl') + screen:expect{grid=[[ + {7:bcde}^f | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {8:-- VISUAL LINE --} | + ]]} + end) + it('highlighting is correct when virtual text wraps with number', function() insert([[ test @@ -2183,6 +2322,38 @@ bbbbbbb]]) ]]} end) + it('smoothscroll works correctly when virtual text wraps', function() + insert('foobar') + meths.buf_set_extmark(0, ns, 0, 3, + { virt_text = { { string.rep('X', 55), 'Special' } }, virt_text_pos = 'inline' }) + command('setlocal smoothscroll') + screen:expect{grid=[[ + foo{10:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX}| + {10:XXXXXXXX}ba^r | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + feed('<C-E>') + screen:expect{grid=[[ + {1:<<<}{10:XXXXX}ba^r | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + end) + it('in diff mode is highlighted correct', function() insert([[ 9000 @@ -2191,9 +2362,11 @@ bbbbbbb]]) 9000 0009 ]]) + insert('aaa\tbbb') command("set diff") - meths.buf_set_extmark(0, ns, 0, 1, - { virt_text = { { 'test', 'Special' } }, virt_text_pos = 'inline', right_gravity = false }) + meths.buf_set_extmark(0, ns, 0, 1, { virt_text = { { 'test', 'Special' } }, virt_text_pos = 'inline', right_gravity = false }) + meths.buf_set_extmark(0, ns, 5, 0, { virt_text = { { '!', 'Special' } }, virt_text_pos = 'inline' }) + meths.buf_set_extmark(0, ns, 5, 3, { virt_text = { { '' } }, virt_text_pos = 'inline' }) command("vnew") insert([[ 000 @@ -2202,6 +2375,7 @@ bbbbbbb]]) 000 000 ]]) + insert('aaabbb') command("set diff") feed('gg0') screen:expect { grid = [[ @@ -2210,12 +2384,26 @@ bbbbbbb]]) {9:000 }│{9:000}{5:9}{9: }| {9:000 }│{5:9}{9:000 }| {9:000 }│{9:000}{5:9}{9: }| - │ | + {9:aaabbb }│{14:!}{9:aaa}{5: }{9:bbb }| {1:~ }│{1:~ }| {1:~ }│{1:~ }| {15:[No Name] [+] }{13:[No Name] [+] }| | ]]} + command('wincmd w | set nowrap') + feed('zl') + screen:expect { grid = [[ + {9:000 }│{14:test}{9:000 }| + {9:000 }│{9:00}{5:9}{9: }| + {9:000 }│{9:00}{5:9}{9: }| + {9:000 }│{9:000 }| + {9:000 }│{9:00}{5:9}{9: }| + {9:aaabbb }│{9:aaa}{5: }{9:bb^b }| + {1:~ }│{1:~ }| + {1:~ }│{1:~ }| + {13:[No Name] [+] }{15:[No Name] [+] }| + | + ]]} end) it('correctly draws when there are multiple overlapping virtual texts on the same line with nowrap', function() @@ -2318,6 +2506,64 @@ bbbbbbb]]) | ]]} end) + + it('does not crash at right edge of wide window #23848', function() + screen:try_resize(82, 5) + meths.buf_set_extmark(0, ns, 0, 0, {virt_text = {{('a'):rep(82)}, {'b'}}, virt_text_pos = 'inline'}) + screen:expect{grid=[[ + ^aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| + b | + {1:~ }| + {1:~ }| + | + ]]} + command('set nowrap') + screen:expect{grid=[[ + ^aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + feed('82i0<Esc>0') + screen:expect{grid=[[ + ^0000000000000000000000000000000000000000000000000000000000000000000000000000000000| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + command('set wrap') + screen:expect{grid=[[ + ^0000000000000000000000000000000000000000000000000000000000000000000000000000000000| + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa| + b | + {1:~ }| + | + ]]} + end) + + it('list "extends" is drawn with only inline virtual text offscreen', function() + command('set nowrap') + command('set list') + command('set listchars+=extends:c') + meths.buf_set_extmark(0, ns, 0, 0, + { virt_text = { { 'test', 'Special' } }, virt_text_pos = 'inline' }) + insert(string.rep('a', 50)) + feed('gg0') + screen:expect { grid = [[ + ^aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa{1:c}| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + end) end) describe('decorations: virtual lines', function() diff --git a/test/functional/ui/fold_spec.lua b/test/functional/ui/fold_spec.lua index 68a7c89b32..71b9e49d2f 100644 --- a/test/functional/ui/fold_spec.lua +++ b/test/functional/ui/fold_spec.lua @@ -2453,6 +2453,86 @@ describe("folded lines", function() ]]) end end) + + it('do not show search or match highlight #24084', function() + insert([[ + line 1 + line 2 + line 3 + line 4]]) + command('2,3fold') + feed('/line') + if multigrid then + screen:expect([[ + ## grid 1 + [2:---------------------------------------------]| + [2:---------------------------------------------]| + [2:---------------------------------------------]| + [2:---------------------------------------------]| + [2:---------------------------------------------]| + [2:---------------------------------------------]| + [2:---------------------------------------------]| + [3:---------------------------------------------]| + ## grid 2 + {2:line} 1 | + {5:+-- 2 lines: line 2·························}| + {6:line} 4 | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 3 + /line^ | + ]]) + else + screen:expect([[ + {2:line} 1 | + {5:+-- 2 lines: line 2·························}| + {6:line} 4 | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + /line^ | + ]]) + end + feed('<Esc>') + funcs.matchadd('Search', 'line') + if multigrid then + screen:expect([[ + ## grid 1 + [2:---------------------------------------------]| + [2:---------------------------------------------]| + [2:---------------------------------------------]| + [2:---------------------------------------------]| + [2:---------------------------------------------]| + [2:---------------------------------------------]| + [2:---------------------------------------------]| + [3:---------------------------------------------]| + ## grid 2 + {6:line} 1 | + {5:+-- 2 lines: line 2·························}| + {6:line} ^4 | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ## grid 3 + | + ]]) + else + screen:expect([[ + {6:line} 1 | + {5:+-- 2 lines: line 2·························}| + {6:line} ^4 | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]) + end + end) end describe("with ext_multigrid", function() diff --git a/test/functional/ui/linematch_spec.lua b/test/functional/ui/linematch_spec.lua index 697677aa67..76197bc7e0 100644 --- a/test/functional/ui/linematch_spec.lua +++ b/test/functional/ui/linematch_spec.lua @@ -779,6 +779,192 @@ something end) end) + describe('setup a diff with 2 files and set linematch:30', function() + before_each(function() + feed(':set diffopt+=linematch:30<cr>') + local f1 = [[ +// abc d +// d +// d + ]] + local f2 = [[ + +abc d +d + ]] + write_file(fname, f1, false) + write_file(fname_2, f2, false) + reread() + end) + + it('display results', function() + screen:expect([[ + {1: }{10: 1 }{4:^ }│{1: }{10: }{2:--------------------------------------------}| + {1: }{10: 2 }{9:abc d }│{1: }{10: 1 }{8:// }{9:abc d }| + {1: }{10: 3 }{9:d }│{1: }{10: 2 }{8:// }{9:d }| + {1: }{10: }{2:-------------------------------------------}│{1: }{10: 3 }{4:// d }| + {1: }{10: 4 } │{1: }{10: 4 } | + {6:~ }│{6:~ }| + {6:~ }│{6:~ }| + {6:~ }│{6:~ }| + {6:~ }│{6:~ }| + {6:~ }│{6:~ }| + {6:~ }│{6:~ }| + {6:~ }│{6:~ }| + {6:~ }│{6:~ }| + {6:~ }│{6:~ }| + {6:~ }│{6:~ }| + {6:~ }│{6:~ }| + {6:~ }│{6:~ }| + {6:~ }│{6:~ }| + {7:Xtest-functional-diff-screen-1.2 }{3:Xtest-functional-diff-screen-1 }| + :e | + ]]) + end) + end) + describe('setup a diff with 2 files and set linematch:30, with ignore white', function() + before_each(function() + feed(':set diffopt+=linematch:30<cr>:set diffopt+=iwhiteall<cr>') + local f1 = [[ +void testFunction () { + for (int i = 0; i < 10; i++) { + for (int j = 0; j < 10; j++) { + } + } +} + ]] + local f2 = [[ +void testFunction () { + // for (int j = 0; j < 10; i++) { + // } +} + ]] + write_file(fname, f1, false) + write_file(fname_2, f2, false) + reread() + end) + + it('display results', function() + screen:expect([[ + {1: }{10: 1 }^void testFunction () { │{1: }{10: 1 }void testFunction () { | + {1: }{10: }{2:-------------------------------------------}│{1: }{10: 2 }{4: for (int i = 0; i < 10; i++) { }| + {1: }{10: 2 }{9: }{8:// for (int j = 0; j < 10; i}{9:++) { }│{1: }{10: 3 }{9: }{8:for (int j = 0; j < 10; j}{9:++) { }| + {1: }{10: 3 }{9: }{8:// }{9:} }│{1: }{10: 4 }{9: } }| + {1: }{10: }{2:-------------------------------------------}│{1: }{10: 5 }{4: } }| + {1: }{10: 4 }} │{1: }{10: 6 }} | + {1: }{10: 5 } │{1: }{10: 7 } | + {6:~ }│{6:~ }| + {6:~ }│{6:~ }| + {6:~ }│{6:~ }| + {6:~ }│{6:~ }| + {6:~ }│{6:~ }| + {6:~ }│{6:~ }| + {6:~ }│{6:~ }| + {6:~ }│{6:~ }| + {6:~ }│{6:~ }| + {6:~ }│{6:~ }| + {6:~ }│{6:~ }| + {7:Xtest-functional-diff-screen-1.2 }{3:Xtest-functional-diff-screen-1 }| + :e | + ]]) + end) + end) + describe('a diff that would result in multiple groups before grouping optimization', function() + before_each(function() + feed(':set diffopt+=linematch:30<cr>') + local f1 = [[ +!A +!B +!C + ]] + local f2 = [[ +?Z +?A +?B +?C +?A +?B +?B +?C + ]] + write_file(fname, f1, false) + write_file(fname_2, f2, false) + reread() + end) + + it('display results', function() + screen:expect([[ + {1: }{10: 1 }{4:^?Z }│{1: }{10: }{2:--------------------------------------------}| + {1: }{10: 2 }{8:?}{9:A }│{1: }{10: 1 }{8:!}{9:A }| + {1: }{10: 3 }{8:?}{9:B }│{1: }{10: 2 }{8:!}{9:B }| + {1: }{10: 4 }{8:?}{9:C }│{1: }{10: 3 }{8:!}{9:C }| + {1: }{10: 5 }{4:?A }│{1: }{10: }{2:--------------------------------------------}| + {1: }{10: 6 }{4:?B }│{1: }{10: }{2:--------------------------------------------}| + {1: }{10: 7 }{4:?B }│{1: }{10: }{2:--------------------------------------------}| + {1: }{10: 8 }{4:?C }│{1: }{10: }{2:--------------------------------------------}| + {1: }{10: 9 } │{1: }{10: 4 } | + {6:~ }│{6:~ }| + {6:~ }│{6:~ }| + {6:~ }│{6:~ }| + {6:~ }│{6:~ }| + {6:~ }│{6:~ }| + {6:~ }│{6:~ }| + {6:~ }│{6:~ }| + {6:~ }│{6:~ }| + {6:~ }│{6:~ }| + {7:Xtest-functional-diff-screen-1.2 }{3:Xtest-functional-diff-screen-1 }| + :e | + ]]) + end) + end) + describe('a diff that would result in multiple groups before grouping optimization', function() + before_each(function() + feed(':set diffopt+=linematch:30<cr>') + local f1 = [[ +!A +!B +!C + ]] + local f2 = [[ +?A +?Z +?B +?C +?A +?B +?C +?C + ]] + write_file(fname, f1, false) + write_file(fname_2, f2, false) + reread() + end) + + it('display results', function() + screen:expect([[ + {1: }{10: 1 }{4:^?A }│{1: }{10: }{2:--------------------------------------------}| + {1: }{10: 2 }{4:?Z }│{1: }{10: }{2:--------------------------------------------}| + {1: }{10: 3 }{4:?B }│{1: }{10: }{2:--------------------------------------------}| + {1: }{10: 4 }{4:?C }│{1: }{10: }{2:--------------------------------------------}| + {1: }{10: 5 }{8:?}{9:A }│{1: }{10: 1 }{8:!}{9:A }| + {1: }{10: 6 }{8:?}{9:B }│{1: }{10: 2 }{8:!}{9:B }| + {1: }{10: 7 }{8:?}{9:C }│{1: }{10: 3 }{8:!}{9:C }| + {1: }{10: 8 }{4:?C }│{1: }{10: }{2:--------------------------------------------}| + {1: }{10: 9 } │{1: }{10: 4 } | + {6:~ }│{6:~ }| + {6:~ }│{6:~ }| + {6:~ }│{6:~ }| + {6:~ }│{6:~ }| + {6:~ }│{6:~ }| + {6:~ }│{6:~ }| + {6:~ }│{6:~ }| + {6:~ }│{6:~ }| + {6:~ }│{6:~ }| + {7:Xtest-functional-diff-screen-1.2 }{3:Xtest-functional-diff-screen-1 }| + :e | + ]]) + end) + end) describe('setup a diff with 2 files and set linematch:10', function() before_each(function() feed(':set diffopt+=linematch:10<cr>') diff --git a/test/functional/ui/multibyte_spec.lua b/test/functional/ui/multibyte_spec.lua index 9f413f8bff..5cecd423d7 100644 --- a/test/functional/ui/multibyte_spec.lua +++ b/test/functional/ui/multibyte_spec.lua @@ -17,10 +17,11 @@ describe("multibyte rendering", function() screen = Screen.new(60, 6) screen:attach({rgb=true}) screen:set_default_attr_ids({ - [1] = {bold = true, foreground = Screen.colors.Blue1}, + [1] = {bold = true, foreground = Screen.colors.Blue}, [2] = {background = Screen.colors.WebGray}, [3] = {background = Screen.colors.LightMagenta}, [4] = {bold = true}, + [5] = {foreground = Screen.colors.Blue}, }) end) @@ -119,6 +120,19 @@ describe("multibyte rendering", function() ]]) end) + it('0xffff is shown as 4 hex digits', function() + command([[call setline(1, "\uFFFF!!!")]]) + feed('$') + screen:expect{grid=[[ + {5:<ffff>}!!^! | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + end) + it('works with a lot of unicode (zalgo) text', function() screen:try_resize(65, 10) meths.buf_set_lines(0,0,-1,true, split(dedent [[ diff --git a/test/functional/ui/statuscolumn_spec.lua b/test/functional/ui/statuscolumn_spec.lua index 6624fb008d..d713580ca9 100644 --- a/test/functional/ui/statuscolumn_spec.lua +++ b/test/functional/ui/statuscolumn_spec.lua @@ -3,6 +3,7 @@ local Screen = require('test.functional.ui.screen') local clear = helpers.clear local command = helpers.command local eq = helpers.eq +local exec = helpers.exec local eval = helpers.eval local exec_lua = helpers.exec_lua local feed = helpers.feed @@ -440,44 +441,81 @@ describe('statuscolumn', function() end) for _, model in ipairs(mousemodels) do - it("works with 'statuscolumn' clicks with mousemodel=" .. model, function() - command('set mousemodel=' .. model) - command([[ - function! MyClickFunc(minwid, clicks, button, mods) - let g:testvar = printf("%d %d %s %d", a:minwid, a:clicks, a:button, getmousepos().line) - if a:mods !=# ' ' - let g:testvar ..= '(' .. a:mods .. ')' - endif - endfunction - set stc=%0@MyClickFunc@%=%l%T - ]]) - meths.input_mouse('left', 'press', '', 0, 0, 0) - eq('0 1 l 4', eval("g:testvar")) - meths.input_mouse('left', 'press', '', 0, 0, 0) - eq('0 2 l 4', eval("g:testvar")) - meths.input_mouse('left', 'press', '', 0, 0, 0) - eq('0 3 l 4', eval("g:testvar")) - meths.input_mouse('left', 'press', '', 0, 0, 0) - eq('0 4 l 4', eval("g:testvar")) - meths.input_mouse('right', 'press', '', 0, 3, 0) - eq('0 1 r 7', eval("g:testvar")) - meths.input_mouse('right', 'press', '', 0, 3, 0) - eq('0 2 r 7', eval("g:testvar")) - meths.input_mouse('right', 'press', '', 0, 3, 0) - eq('0 3 r 7', eval("g:testvar")) - meths.input_mouse('right', 'press', '', 0, 3, 0) - eq('0 4 r 7', eval("g:testvar")) - command('set laststatus=2 winbar=%f') - command('let g:testvar=""') - -- Check that winbar click doesn't register as statuscolumn click - meths.input_mouse('right', 'press', '', 0, 0, 0) - eq('', eval("g:testvar")) - -- Check that statusline click doesn't register as statuscolumn click - meths.input_mouse('right', 'press', '', 0, 12, 0) - eq('', eval("g:testvar")) - -- Check that cmdline click doesn't register as statuscolumn click - meths.input_mouse('right', 'press', '', 0, 13, 0) - eq('', eval("g:testvar")) + describe('with mousemodel=' .. model, function() + before_each(function() + command('set mousemodel=' .. model) + exec([[ + function! MyClickFunc(minwid, clicks, button, mods) + let g:testvar = printf("%d %d %s %d", a:minwid, a:clicks, a:button, getmousepos().line) + if a:mods !=# ' ' + let g:testvar ..= '(' .. a:mods .. ')' + endif + endfunction + let g:testvar = '' + ]]) + end) + + it('clicks work with mousemodel=' .. model, function() + meths.set_option_value('statuscolumn', '%0@MyClickFunc@%=%l%T', {}) + meths.input_mouse('left', 'press', '', 0, 0, 0) + eq('0 1 l 4', eval("g:testvar")) + meths.input_mouse('left', 'press', '', 0, 0, 0) + eq('0 2 l 4', eval("g:testvar")) + meths.input_mouse('left', 'press', '', 0, 0, 0) + eq('0 3 l 4', eval("g:testvar")) + meths.input_mouse('left', 'press', '', 0, 0, 0) + eq('0 4 l 4', eval("g:testvar")) + meths.input_mouse('right', 'press', '', 0, 3, 0) + eq('0 1 r 7', eval("g:testvar")) + meths.input_mouse('right', 'press', '', 0, 3, 0) + eq('0 2 r 7', eval("g:testvar")) + meths.input_mouse('right', 'press', '', 0, 3, 0) + eq('0 3 r 7', eval("g:testvar")) + meths.input_mouse('right', 'press', '', 0, 3, 0) + eq('0 4 r 7', eval("g:testvar")) + command('set laststatus=2 winbar=%f') + command('let g:testvar = ""') + -- Check that winbar click doesn't register as statuscolumn click + meths.input_mouse('right', 'press', '', 0, 0, 0) + eq('', eval("g:testvar")) + -- Check that statusline click doesn't register as statuscolumn click + meths.input_mouse('right', 'press', '', 0, 12, 0) + eq('', eval("g:testvar")) + -- Check that cmdline click doesn't register as statuscolumn click + meths.input_mouse('right', 'press', '', 0, 13, 0) + eq('', eval("g:testvar")) + end) + + it('clicks and highlights work with control characters', function() + meths.set_option_value('statuscolumn', '\t%#NonText#\1%0@MyClickFunc@\t\1%T\t%##\1', {}) + screen:expect{grid=[[ + {1:^I}{0:^A^I^A^I}{1:^A}aaaaa | + {1:^I}{0:^A^I^A^I}{1:^A}aaaaa | + {1:^I}{0:^A^I^A^I}{1:^A}aaaaa | + {1:^I}{0:^A^I^A^I}{1:^A}aaaaa | + {1:^I}{0:^A^I^A^I}{1:^A}^aaaaa | + {1:^I}{0:^A^I^A^I}{1:^A}aaaaa | + {1:^I}{0:^A^I^A^I}{1:^A}aaaaa | + {1:^I}{0:^A^I^A^I}{1:^A}aaaaa | + {1:^I}{0:^A^I^A^I}{1:^A}aaaaa | + {1:^I}{0:^A^I^A^I}{1:^A}aaaaa | + {1:^I}{0:^A^I^A^I}{1:^A}aaaaa | + {1:^I}{0:^A^I^A^I}{1:^A}aaaaa | + {1:^I}{0:^A^I^A^I}{1:^A}aaaaa | + | + ]], attr_ids={ + [0] = {foreground = Screen.colors.Blue, bold = true}; -- NonText + [1] = {foreground = Screen.colors.Brown}; -- LineNr + }} + meths.input_mouse('right', 'press', '', 0, 4, 3) + eq('', eval("g:testvar")) + meths.input_mouse('left', 'press', '', 0, 5, 8) + eq('', eval("g:testvar")) + meths.input_mouse('right', 'press', '', 0, 6, 4) + eq('0 1 r 10', eval("g:testvar")) + meths.input_mouse('left', 'press', '', 0, 7, 7) + eq('0 1 l 11', eval("g:testvar")) + end) end) end diff --git a/test/functional/ui/statusline_spec.lua b/test/functional/ui/statusline_spec.lua index 5afa912be6..7967fb6865 100644 --- a/test/functional/ui/statusline_spec.lua +++ b/test/functional/ui/statusline_spec.lua @@ -21,6 +21,10 @@ for _, model in ipairs(mousemodels) do before_each(function() clear() screen = Screen.new(40, 8) + screen:set_default_attr_ids({ + [0] = {bold = true, foreground = Screen.colors.Blue}; -- NonText + [1] = {bold = true, reverse = true}; -- StatusLine + }) screen:attach() command('set laststatus=2 mousemodel=' .. model) exec([=[ @@ -30,163 +34,190 @@ for _, model in ipairs(mousemodels) do let g:testvar ..= '(' .. a:mods .. ')' endif endfunction + let g:testvar = '' ]=]) - end) - - it('works', function() - meths.set_option_value('statusline', 'Not clicky stuff %0@MyClickFunc@Clicky stuff%T', {}) - meths.input_mouse('left', 'press', '', 0, 6, 17) - eq('0 1 l', eval("g:testvar")) - meths.input_mouse('left', 'press', '', 0, 6, 17) - eq('0 2 l', eval("g:testvar")) - meths.input_mouse('left', 'press', '', 0, 6, 17) - eq('0 3 l', eval("g:testvar")) - meths.input_mouse('left', 'press', '', 0, 6, 17) - eq('0 4 l', eval("g:testvar")) - meths.input_mouse('right', 'press', '', 0, 6, 17) - eq('0 1 r', eval("g:testvar")) - meths.input_mouse('right', 'press', '', 0, 6, 17) - eq('0 2 r', eval("g:testvar")) - meths.input_mouse('right', 'press', '', 0, 6, 17) - eq('0 3 r', eval("g:testvar")) - meths.input_mouse('right', 'press', '', 0, 6, 17) - eq('0 4 r', eval("g:testvar")) - end) - - it('works for winbar', function() - meths.set_option_value('winbar', 'Not clicky stuff %0@MyClickFunc@Clicky stuff%T', {}) - meths.input_mouse('left', 'press', '', 0, 0, 17) - eq('0 1 l', eval("g:testvar")) - meths.input_mouse('right', 'press', '', 0, 0, 17) - eq('0 1 r', eval("g:testvar")) - end) - - it('works for winbar in floating window', function() - meths.open_win(0, true, { width=30, height=4, relative='editor', row=1, col=5, - border = "single" }) - meths.set_option_value('winbar', 'Not clicky stuff %0@MyClickFunc@Clicky stuff%T', - { scope = 'local' }) - meths.input_mouse('left', 'press', '', 0, 2, 23) - eq('0 1 l', eval("g:testvar")) - end) - - it('works when there are multiple windows', function() - command('split') - meths.set_option_value('statusline', 'Not clicky stuff %0@MyClickFunc@Clicky stuff%T', {}) - meths.set_option_value('winbar', 'Not clicky stuff %0@MyClickFunc@Clicky stuff%T', {}) - meths.input_mouse('left', 'press', '', 0, 0, 17) - eq('0 1 l', eval("g:testvar")) - meths.input_mouse('right', 'press', '', 0, 4, 17) - eq('0 1 r', eval("g:testvar")) - meths.input_mouse('middle', 'press', '', 0, 3, 17) - eq('0 1 m', eval("g:testvar")) - meths.input_mouse('left', 'press', '', 0, 6, 17) - eq('0 1 l', eval("g:testvar")) - end) - - it('works with Lua function', function() - exec_lua([[ - function clicky_func(minwid, clicks, button, mods) - vim.g.testvar = string.format("%d %d %s", minwid, clicks, button) - end - ]]) - meths.set_option_value('statusline', 'Not clicky stuff %0@v:lua.clicky_func@Clicky stuff%T', {}) - meths.input_mouse('left', 'press', '', 0, 6, 17) - eq('0 1 l', eval("g:testvar")) - end) - - it('ignores unsupported click items', function() - command('tabnew | tabprevious') - meths.set_option_value('statusline', '%2TNot clicky stuff%T', {}) - meths.input_mouse('left', 'press', '', 0, 6, 0) - eq(1, meths.get_current_tabpage().id) - meths.set_option_value('statusline', '%2XNot clicky stuff%X', {}) - meths.input_mouse('left', 'press', '', 0, 6, 0) - eq(2, #meths.list_tabpages()) - end) - - it("right click works when statusline isn't focused #18994", function() - meths.set_option_value('statusline', 'Not clicky stuff %0@MyClickFunc@Clicky stuff%T', {}) - meths.input_mouse('right', 'press', '', 0, 6, 17) - eq('0 1 r', eval("g:testvar")) - meths.input_mouse('right', 'press', '', 0, 6, 17) - eq('0 2 r', eval("g:testvar")) - end) - - it("works with modifiers #18994", function() - meths.set_option_value('statusline', 'Not clicky stuff %0@MyClickFunc@Clicky stuff%T', {}) - -- Note: alternate between left and right mouse buttons to avoid triggering multiclicks - meths.input_mouse('left', 'press', 'S', 0, 6, 17) - eq('0 1 l(s )', eval("g:testvar")) - meths.input_mouse('right', 'press', 'S', 0, 6, 17) - eq('0 1 r(s )', eval("g:testvar")) - meths.input_mouse('left', 'press', 'A', 0, 6, 17) - eq('0 1 l( a )', eval("g:testvar")) - meths.input_mouse('right', 'press', 'A', 0, 6, 17) - eq('0 1 r( a )', eval("g:testvar")) - meths.input_mouse('left', 'press', 'AS', 0, 6, 17) - eq('0 1 l(s a )', eval("g:testvar")) - meths.input_mouse('right', 'press', 'AS', 0, 6, 17) - eq('0 1 r(s a )', eval("g:testvar")) - meths.input_mouse('left', 'press', 'T', 0, 6, 17) - eq('0 1 l( m)', eval("g:testvar")) - meths.input_mouse('right', 'press', 'T', 0, 6, 17) - eq('0 1 r( m)', eval("g:testvar")) - meths.input_mouse('left', 'press', 'TS', 0, 6, 17) - eq('0 1 l(s m)', eval("g:testvar")) - meths.input_mouse('right', 'press', 'TS', 0, 6, 17) - eq('0 1 r(s m)', eval("g:testvar")) - meths.input_mouse('left', 'press', 'C', 0, 6, 17) - eq('0 1 l( c )', eval("g:testvar")) - -- <C-RightMouse> is for tag jump - end) - - it("works for global statusline with vertical splits #19186", function() - command('set laststatus=3') - meths.set_option_value('statusline', '%0@MyClickFunc@Clicky stuff%T %= %0@MyClickFunc@Clicky stuff%T', {}) - command('vsplit') - screen:expect([[ + end) + + it('works', function() + meths.set_option_value('statusline', 'Not clicky stuff %0@MyClickFunc@Clicky stuff%T', {}) + meths.input_mouse('left', 'press', '', 0, 6, 16) + eq('', eval("g:testvar")) + meths.input_mouse('right', 'press', '', 0, 6, 29) + eq('', eval("g:testvar")) + meths.input_mouse('left', 'press', '', 0, 6, 17) + eq('0 1 l', eval("g:testvar")) + meths.input_mouse('left', 'press', '', 0, 6, 17) + eq('0 2 l', eval("g:testvar")) + meths.input_mouse('left', 'press', '', 0, 6, 17) + eq('0 3 l', eval("g:testvar")) + meths.input_mouse('left', 'press', '', 0, 6, 17) + eq('0 4 l', eval("g:testvar")) + meths.input_mouse('right', 'press', '', 0, 6, 28) + eq('0 1 r', eval("g:testvar")) + meths.input_mouse('right', 'press', '', 0, 6, 28) + eq('0 2 r', eval("g:testvar")) + meths.input_mouse('right', 'press', '', 0, 6, 28) + eq('0 3 r', eval("g:testvar")) + meths.input_mouse('right', 'press', '', 0, 6, 28) + eq('0 4 r', eval("g:testvar")) + end) + + it('works with control characters and highlight', function() + meths.set_option_value('statusline', '\t%#NonText#\1%0@MyClickFunc@\t\1%T\t%##\1', {}) + screen:expect{grid=[[ + ^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {1:^I}{0:^A^I^A^I}{1:^A }| + | + ]]} + meths.input_mouse('right', 'press', '', 0, 6, 3) + eq('', eval("g:testvar")) + meths.input_mouse('left', 'press', '', 0, 6, 8) + eq('', eval("g:testvar")) + meths.input_mouse('right', 'press', '', 0, 6, 4) + eq('0 1 r', eval("g:testvar")) + meths.input_mouse('left', 'press', '', 0, 6, 7) + eq('0 1 l', eval("g:testvar")) + end) + + it('works for winbar', function() + meths.set_option_value('winbar', 'Not clicky stuff %0@MyClickFunc@Clicky stuff%T', {}) + meths.input_mouse('left', 'press', '', 0, 0, 17) + eq('0 1 l', eval("g:testvar")) + meths.input_mouse('right', 'press', '', 0, 0, 17) + eq('0 1 r', eval("g:testvar")) + end) + + it('works for winbar in floating window', function() + meths.open_win(0, true, { width=30, height=4, relative='editor', row=1, col=5, + border = "single" }) + meths.set_option_value('winbar', 'Not clicky stuff %0@MyClickFunc@Clicky stuff%T', + { scope = 'local' }) + meths.input_mouse('left', 'press', '', 0, 2, 23) + eq('0 1 l', eval("g:testvar")) + end) + + it('works when there are multiple windows', function() + command('split') + meths.set_option_value('statusline', 'Not clicky stuff %0@MyClickFunc@Clicky stuff%T', {}) + meths.set_option_value('winbar', 'Not clicky stuff %0@MyClickFunc@Clicky stuff%T', {}) + meths.input_mouse('left', 'press', '', 0, 0, 17) + eq('0 1 l', eval("g:testvar")) + meths.input_mouse('right', 'press', '', 0, 4, 17) + eq('0 1 r', eval("g:testvar")) + meths.input_mouse('middle', 'press', '', 0, 3, 17) + eq('0 1 m', eval("g:testvar")) + meths.input_mouse('left', 'press', '', 0, 6, 17) + eq('0 1 l', eval("g:testvar")) + end) + + it('works with Lua function', function() + exec_lua([[ + function clicky_func(minwid, clicks, button, mods) + vim.g.testvar = string.format("%d %d %s", minwid, clicks, button) + end + ]]) + meths.set_option_value('statusline', 'Not clicky stuff %0@v:lua.clicky_func@Clicky stuff%T', {}) + meths.input_mouse('left', 'press', '', 0, 6, 17) + eq('0 1 l', eval("g:testvar")) + end) + + it('ignores unsupported click items', function() + command('tabnew | tabprevious') + meths.set_option_value('statusline', '%2TNot clicky stuff%T', {}) + meths.input_mouse('left', 'press', '', 0, 6, 0) + eq(1, meths.get_current_tabpage().id) + meths.set_option_value('statusline', '%2XNot clicky stuff%X', {}) + meths.input_mouse('left', 'press', '', 0, 6, 0) + eq(2, #meths.list_tabpages()) + end) + + it("right click works when statusline isn't focused #18994", function() + meths.set_option_value('statusline', 'Not clicky stuff %0@MyClickFunc@Clicky stuff%T', {}) + meths.input_mouse('right', 'press', '', 0, 6, 17) + eq('0 1 r', eval("g:testvar")) + meths.input_mouse('right', 'press', '', 0, 6, 17) + eq('0 2 r', eval("g:testvar")) + end) + + it("works with modifiers #18994", function() + meths.set_option_value('statusline', 'Not clicky stuff %0@MyClickFunc@Clicky stuff%T', {}) + -- Note: alternate between left and right mouse buttons to avoid triggering multiclicks + meths.input_mouse('left', 'press', 'S', 0, 6, 17) + eq('0 1 l(s )', eval("g:testvar")) + meths.input_mouse('right', 'press', 'S', 0, 6, 17) + eq('0 1 r(s )', eval("g:testvar")) + meths.input_mouse('left', 'press', 'A', 0, 6, 17) + eq('0 1 l( a )', eval("g:testvar")) + meths.input_mouse('right', 'press', 'A', 0, 6, 17) + eq('0 1 r( a )', eval("g:testvar")) + meths.input_mouse('left', 'press', 'AS', 0, 6, 17) + eq('0 1 l(s a )', eval("g:testvar")) + meths.input_mouse('right', 'press', 'AS', 0, 6, 17) + eq('0 1 r(s a )', eval("g:testvar")) + meths.input_mouse('left', 'press', 'T', 0, 6, 17) + eq('0 1 l( m)', eval("g:testvar")) + meths.input_mouse('right', 'press', 'T', 0, 6, 17) + eq('0 1 r( m)', eval("g:testvar")) + meths.input_mouse('left', 'press', 'TS', 0, 6, 17) + eq('0 1 l(s m)', eval("g:testvar")) + meths.input_mouse('right', 'press', 'TS', 0, 6, 17) + eq('0 1 r(s m)', eval("g:testvar")) + meths.input_mouse('left', 'press', 'C', 0, 6, 17) + eq('0 1 l( c )', eval("g:testvar")) + -- <C-RightMouse> is for tag jump + end) + + it("works for global statusline with vertical splits #19186", function() + command('set laststatus=3') + meths.set_option_value('statusline', '%0@MyClickFunc@Clicky stuff%T %= %0@MyClickFunc@Clicky stuff%T', {}) + command('vsplit') + screen:expect{grid=[[ ^ │ | - ~ │~ | - ~ │~ | - ~ │~ | - ~ │~ | - ~ │~ | - Clicky stuff Clicky stuff| + {0:~ }│{0:~ }| + {0:~ }│{0:~ }| + {0:~ }│{0:~ }| + {0:~ }│{0:~ }| + {0:~ }│{0:~ }| + {1:Clicky stuff Clicky stuff}| | - ]]) - - -- clickable area on the right - meths.input_mouse('left', 'press', '', 0, 6, 35) - eq('0 1 l', eval("g:testvar")) - meths.input_mouse('right', 'press', '', 0, 6, 35) - eq('0 1 r', eval("g:testvar")) - - -- clickable area on the left - meths.input_mouse('left', 'press', '', 0, 6, 5) - eq('0 1 l', eval("g:testvar")) - meths.input_mouse('right', 'press', '', 0, 6, 5) - eq('0 1 r', eval("g:testvar")) - end) - - it('no memory leak with zero-width click labels', function() - command([[ - let &stl = '%@Test@%T%@MyClickFunc@%=%T%@Test@' - ]]) - meths.input_mouse('left', 'press', '', 0, 6, 0) - eq('0 1 l', eval("g:testvar")) - meths.input_mouse('right', 'press', '', 0, 6, 39) - eq('0 1 r', eval("g:testvar")) - end) - - it('no memory leak with truncated click labels', function() - command([[ - let &stl = '%@MyClickFunc@foo%X' .. repeat('a', 40) .. '%<t%@Test@bar%X%@Test@baz' - ]]) - meths.input_mouse('left', 'press', '', 0, 6, 2) - eq('0 1 l', eval("g:testvar")) - end) + ]]} + + -- clickable area on the right + meths.input_mouse('left', 'press', '', 0, 6, 35) + eq('0 1 l', eval("g:testvar")) + meths.input_mouse('right', 'press', '', 0, 6, 35) + eq('0 1 r', eval("g:testvar")) + + -- clickable area on the left + meths.input_mouse('left', 'press', '', 0, 6, 5) + eq('0 1 l', eval("g:testvar")) + meths.input_mouse('right', 'press', '', 0, 6, 5) + eq('0 1 r', eval("g:testvar")) + end) + + it('no memory leak with zero-width click labels', function() + command([[ + let &stl = '%@Test@%T%@MyClickFunc@%=%T%@Test@' + ]]) + meths.input_mouse('left', 'press', '', 0, 6, 0) + eq('0 1 l', eval("g:testvar")) + meths.input_mouse('right', 'press', '', 0, 6, 39) + eq('0 1 r', eval("g:testvar")) end) + + it('no memory leak with truncated click labels', function() + command([[ + let &stl = '%@MyClickFunc@foo%X' .. repeat('a', 40) .. '%<t%@Test@bar%X%@Test@baz' + ]]) + meths.input_mouse('left', 'press', '', 0, 6, 2) + eq('0 1 l', eval("g:testvar")) + end) + end) end describe('global statusline', function() @@ -627,11 +658,19 @@ it('K_EVENT does not trigger a statusline redraw unnecessarily', function() eq(1, eval('g:counter < 50'), 'g:counter=' .. eval('g:counter')) end) -it('statusline is redrawn on recording state change #22683', function() +it('statusline is redrawn on various state changes', function() clear() local screen = Screen.new(40, 4) screen:attach() + + -- recording state change #22683 command('set ls=2 stl=%{repeat(reg_recording(),5)}') + screen:expect([[ + ^ | + ~ | + | + | + ]]) feed('qQ') screen:expect([[ ^ | @@ -639,6 +678,50 @@ it('statusline is redrawn on recording state change #22683', function() QQQQQ | recording @Q | ]]) + feed('q') + screen:expect([[ + ^ | + ~ | + | + | + ]]) + + -- Visual mode change #23932 + command('set ls=2 stl=%{mode(1)}') + screen:expect([[ + ^ | + ~ | + n | + | + ]]) + feed('v') + screen:expect([[ + ^ | + ~ | + v | + -- VISUAL -- | + ]]) + feed('V') + screen:expect([[ + ^ | + ~ | + V | + -- VISUAL LINE -- | + ]]) + feed('<C-V>') + screen:expect([[ + ^ | + ~ | + ^V | + -- VISUAL BLOCK -- | + ]]) + feed('<Esc>') + screen:expect([[ + ^ | + ~ | + n | + | + ]]) end) it('ruler is redrawn in cmdline with redrawstatus #22804', function() diff --git a/test/functional/ui/tabline_spec.lua b/test/functional/ui/tabline_spec.lua index 2cdec62d01..befdb7c5d1 100644 --- a/test/functional/ui/tabline_spec.lua +++ b/test/functional/ui/tabline_spec.lua @@ -1,6 +1,9 @@ local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') local clear, command, eq = helpers.clear, helpers.command, helpers.eq +local insert = helpers.insert +local meths = helpers.meths +local assert_alive = helpers.assert_alive describe('ui/ext_tabline', function() local screen @@ -92,6 +95,10 @@ describe("tabline", function() clear() screen = Screen.new(42, 5) screen:attach() + screen:set_default_attr_ids({ + [0] = {bold = true, foreground = Screen.colors.Blue}; -- NonText + [1] = {reverse = true}; -- TabLineFill + }) end) it('redraws when tabline option is set', function() @@ -100,24 +107,18 @@ describe("tabline", function() screen:expect{grid=[[ {1:asdf }| ^ | - {2:~ }| - {2:~ }| + {0:~ }| + {0:~ }| | - ]], attr_ids={ - [1] = {reverse = true}; - [2] = {bold = true, foreground = Screen.colors.Blue1}; - }} + ]]} command('set tabline=jkl') screen:expect{grid=[[ {1:jkl }| ^ | - {2:~ }| - {2:~ }| + {0:~ }| + {0:~ }| | - ]], attr_ids={ - [1] = {reverse = true}; - [2] = {bold = true, foreground = Screen.colors.Blue}; - }} + ]]} end) it('click definitions do not leak memory #21765', function() @@ -125,4 +126,52 @@ describe("tabline", function() command('set showtabline=2') command('redrawtabline') end) + + it('clicks work with truncated double-width label #24187', function() + insert('tab1') + command('tabnew') + insert('tab2') + command('tabprev') + meths.set_option_value('tabline', '%1T口口%2Ta' .. ('b'):rep(38) .. '%999Xc', {}) + screen:expect{grid=[[ + {1:<abbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbc }| + tab^1 | + {0:~ }| + {0:~ }| + | + ]]} + assert_alive() + meths.input_mouse('left', 'press', '', 0, 0, 1) + screen:expect{grid=[[ + {1:<abbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbc }| + tab^2 | + {0:~ }| + {0:~ }| + | + ]]} + meths.input_mouse('left', 'press', '', 0, 0, 0) + screen:expect{grid=[[ + {1:<abbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbc }| + tab^1 | + {0:~ }| + {0:~ }| + | + ]]} + meths.input_mouse('left', 'press', '', 0, 0, 39) + screen:expect{grid=[[ + {1:<abbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbc }| + tab^2 | + {0:~ }| + {0:~ }| + | + ]]} + meths.input_mouse('left', 'press', '', 0, 0, 40) + screen:expect{grid=[[ + tab^1 | + {0:~ }| + {0:~ }| + {0:~ }| + | + ]]} + end) end) diff --git a/test/functional/vimscript/execute_spec.lua b/test/functional/vimscript/execute_spec.lua index a9a4ad4811..bb28938708 100644 --- a/test/functional/vimscript/execute_spec.lua +++ b/test/functional/vimscript/execute_spec.lua @@ -93,14 +93,10 @@ describe('execute()', function() it('captures errors', function() local ret - ret = exc_exec('call execute(0.0)') - eq('Vim(call):E806: Using a Float as a String', ret) ret = exc_exec('call execute(v:_null_dict)') eq('Vim(call):E731: Using a Dictionary as a String', ret) ret = exc_exec('call execute(function("tr"))') eq('Vim(call):E729: Using a Funcref as a String', ret) - ret = exc_exec('call execute(["echo 42", 0.0, "echo 44"])') - eq('Vim:E806: Using a Float as a String', ret) ret = exc_exec('call execute(["echo 42", v:_null_dict, "echo 44"])') eq('Vim:E731: Using a Dictionary as a String', ret) ret = exc_exec('call execute(["echo 42", function("tr"), "echo 44"])') @@ -330,9 +326,6 @@ describe('execute()', function() it('propagates errors for "" and "silent"', function() local ret - ret = exc_exec('call execute(0.0, "")') - eq('Vim(call):E806: Using a Float as a String', ret) - ret = exc_exec('call execute(v:_null_dict, "silent")') eq('Vim(call):E731: Using a Dictionary as a String', ret) diff --git a/test/functional/vimscript/input_spec.lua b/test/functional/vimscript/input_spec.lua index bd9f7e5381..e1179d29cc 100644 --- a/test/functional/vimscript/input_spec.lua +++ b/test/functional/vimscript/input_spec.lua @@ -552,7 +552,7 @@ describe('confirm()', function() feed(':silent edit foo<cr>') check_and_clear(':silent edit foo |\n') - -- With API (via eval/VimL) call and shortmess+=F + -- With API (via eval/Vimscript) call and shortmess+=F feed(':call nvim_command("edit x")<cr>') check_and_clear(':call nvim_command("edit |\n') diff --git a/test/functional/vimscript/let_spec.lua b/test/functional/vimscript/let_spec.lua index 164fa86452..11417c5846 100644 --- a/test/functional/vimscript/let_spec.lua +++ b/test/functional/vimscript/let_spec.lua @@ -92,6 +92,20 @@ describe(':let', function() ]]) eq(1, eval('1')) end) + + it('can apply operator to boolean option', function() + eq(true, meths.get_option_value('equalalways', {})) + command('let &equalalways -= 1') + eq(false, meths.get_option_value('equalalways', {})) + command('let &equalalways += 1') + eq(true, meths.get_option_value('equalalways', {})) + command('let &equalalways *= 1') + eq(true, meths.get_option_value('equalalways', {})) + command('let &equalalways /= 1') + eq(true, meths.get_option_value('equalalways', {})) + command('let &equalalways %= 1') + eq(false, meths.get_option_value('equalalways', {})) + end) end) describe(':let and :const', function() diff --git a/test/functional/vimscript/writefile_spec.lua b/test/functional/vimscript/writefile_spec.lua index 521a4eb2b1..88c19bd839 100644 --- a/test/functional/vimscript/writefile_spec.lua +++ b/test/functional/vimscript/writefile_spec.lua @@ -145,8 +145,6 @@ describe('writefile()', function() pcall_err(command, ('call writefile(%s, "%s", "b")'):format(arg, fname))) end for _, args in ipairs({'[], %s, "b"', '[], "' .. fname .. '", %s'}) do - eq('Vim(call):E806: Using a Float as a String', - pcall_err(command, ('call writefile(%s)'):format(args:format('0.0')))) eq('Vim(call):E730: Using a List as a String', pcall_err(command, ('call writefile(%s)'):format(args:format('[]')))) eq('Vim(call):E731: Using a Dictionary as a String', diff --git a/test/old/testdir/check.vim b/test/old/testdir/check.vim index 680a59006b..281514db17 100644 --- a/test/old/testdir/check.vim +++ b/test/old/testdir/check.vim @@ -168,7 +168,7 @@ endfunc " Command to check for not running under ASAN command CheckNotAsan call CheckNotAsan() func CheckNotAsan() - if execute('version') =~# '-fsanitize=[a-z,]*\<address\>' + if execute('verbose version') =~# '-fsanitize=[a-z,]*\<address\>' throw 'Skipped: does not work with ASAN' endif endfunc diff --git a/test/old/testdir/setup.vim b/test/old/testdir/setup.vim index 9c53c0466d..94fbf2bef1 100644 --- a/test/old/testdir/setup.vim +++ b/test/old/testdir/setup.vim @@ -3,12 +3,14 @@ if exists('s:did_load') set backspace= set commentstring=/*%s*/ set complete=.,w,b,u,t,i + set define=^\\s*#\\s*define set directory& set directory^=. set display= set fillchars=vert:\|,foldsep:\|,fold:- set formatoptions=tcq set fsync + set include=^\\s*#\\s*include set laststatus=1 set listchars=eol:$ set joinspaces diff --git a/test/old/testdir/test_cmdline.vim b/test/old/testdir/test_cmdline.vim index 96a30a1dd5..58d37b5da7 100644 --- a/test/old/testdir/test_cmdline.vim +++ b/test/old/testdir/test_cmdline.vim @@ -1251,6 +1251,30 @@ func Test_cmdline_complete_various() call assert_equal('"py3file', @:) endfunc +" Test that expanding a pattern doesn't interfere with cmdline completion. +func Test_expand_during_cmdline_completion() + func ExpandStuff() + badd <script>:p:h/README.* + call assert_equal(expand('<script>:p:h') .. '/README.txt', bufname('$')) + $bwipe + call assert_equal('README.txt', expand('README.*')) + call assert_equal(['README.txt'], getcompletion('README.*', 'file')) + endfunc + augroup test_CmdlineChanged + autocmd! + autocmd CmdlineChanged * call ExpandStuff() + augroup END + + call feedkeys(":sign \<Tab>\<Tab>\<Tab>\<Tab>\<C-B>\"\<CR>", 'xt') + call assert_equal('"sign place', @:) + + augroup test_CmdlineChanged + au! + augroup END + augroup! test_CmdlineChanged + delfunc ExpandStuff +endfunc + " Test for 'wildignorecase' func Test_cmdline_wildignorecase() CheckUnix @@ -1790,6 +1814,7 @@ func Test_cmd_bang_E135() augroup test_cmd_filter_E135 au! augroup END + augroup! test_cmd_filter_E135 %bwipe! endfunc @@ -2278,7 +2303,7 @@ endfunc func Test_cmd_map_cmdlineChanged() let g:log = [] cnoremap <F1> l<Cmd><CR>s - augroup test + augroup test_CmdlineChanged autocmd! autocmd CmdlineChanged : let g:log += [getcmdline()] augroup END @@ -2294,9 +2319,10 @@ func Test_cmd_map_cmdlineChanged() unlet g:log cunmap <F1> - augroup test + augroup test_CmdlineChanged autocmd! augroup END + augroup! test_CmdlineChanged endfunc " Test for the 'suffixes' option diff --git a/test/old/testdir/test_eval_stuff.vim b/test/old/testdir/test_eval_stuff.vim index 20eb873326..45dfd57842 100644 --- a/test/old/testdir/test_eval_stuff.vim +++ b/test/old/testdir/test_eval_stuff.vim @@ -230,6 +230,12 @@ func Test_string_concatenation() let a = 'a' let a..=b call assert_equal('ab', a) + + if has('float') + let a = 'A' + let b = 1.234 + call assert_equal('A1.234', a .. b) + endif endfunc " Test fix for issue #4507 diff --git a/test/old/testdir/test_ex_mode.vim b/test/old/testdir/test_ex_mode.vim index 3332bc6ab9..42f08868a0 100644 --- a/test/old/testdir/test_ex_mode.vim +++ b/test/old/testdir/test_ex_mode.vim @@ -244,6 +244,12 @@ func Test_ex_mode_errors() au! CmdLineEnter delfunc ExEnterFunc + + au CmdlineEnter * : + call feedkeys("gQecho 1\r", 'xt') + + au! CmdlineEnter + quit endfunc diff --git a/test/old/testdir/test_execute_func.vim b/test/old/testdir/test_execute_func.vim index cc294b7316..61971ec587 100644 --- a/test/old/testdir/test_execute_func.vim +++ b/test/old/testdir/test_execute_func.vim @@ -1,6 +1,8 @@ " test execute() source view_util.vim +source check.vim +source vim9.vim func NestedEval() let nested = execute('echo "nested\nlines"') @@ -31,7 +33,6 @@ func Test_execute_string() call assert_equal("\nthat", evaled) call assert_fails('call execute("doesnotexist")', 'E492:') - call assert_fails('call execute(3.4)', 'E806:') " Nvim supports execute('... :redir ...'), so this test is intentionally " disabled. " call assert_fails('call execute("call NestedRedir()")', 'E930:') @@ -40,7 +41,11 @@ func Test_execute_string() call assert_equal("\nsomething", execute('echo "something"', 'silent')) call assert_equal("\nsomething", execute('echo "something"', 'silent!')) call assert_equal("", execute('burp', 'silent!')) - call assert_fails('call execute("echo \"x\"", 3.4)', 'E806:') + if has('float') + call assert_fails('call execute(3.4)', 'E492:') + call assert_equal("\nx", execute("echo \"x\"", 3.4)) + call CheckDefExecAndScriptFailure(['execute("echo \"x\"", 3.4)'], 'E806:') + endif endfunc func Test_execute_list() diff --git a/test/old/testdir/test_filetype.vim b/test/old/testdir/test_filetype.vim index 6a508de236..8ec8db269a 100644 --- a/test/old/testdir/test_filetype.vim +++ b/test/old/testdir/test_filetype.vim @@ -40,9 +40,35 @@ func Test_other_type() filetype off endfunc +" If $XDG_CONFIG_HOME is set return "fname" expanded in a list. +" Otherwise return an empty list. +func s:WhenConfigHome(fname) + if exists('$XDG_CONFIG_HOME') + return [expand(a:fname)] + endif + return [] +endfunc + +" Return the name used for the $XDG_CONFIG_HOME directory. +func s:GetConfigHome() + return getcwd() .. '/Xdg_config_home' +endfunc + +" saved value of $XDG_CONFIG_HOME +let s:saveConfigHome = '' + +func s:SetupConfigHome() + " Nvim on Windows may use $XDG_CONFIG_HOME, and runnvim.sh sets it. + " if empty(windowsversion()) + let s:saveConfigHome = $XDG_CONFIG_HOME + call setenv("XDG_CONFIG_HOME", s:GetConfigHome()) + " endif +endfunc + " Filetypes detected just from matching the file name. " First one is checking that these files have no filetype. -let s:filename_checks = { +func s:GetFilenameChecks() abort + return { \ 'none': ['bsd', 'some-bsd'], \ '8th': ['file.8th'], \ 'a2ps': ['/etc/a2ps.cfg', '/etc/a2ps/file.cfg', 'a2psrc', '.a2psrc', 'any/etc/a2ps.cfg', 'any/etc/a2ps/file.cfg'], @@ -94,7 +120,7 @@ let s:filename_checks = { \ 'bzr': ['bzr_log.any', 'bzr_log.file'], \ 'c': ['enlightenment/file.cfg', 'file.qc', 'file.c', 'some-enlightenment/file.cfg'], \ 'cabal': ['file.cabal'], - \ 'cabalconfig': ['cabal.config'], + \ 'cabalconfig': ['cabal.config', expand("$HOME/.config/cabal/config")] + s:WhenConfigHome('$XDG_CONFIG_HOME/cabal/config'), \ 'cabalproject': ['cabal.project', 'cabal.project.local'], \ 'cairo': ['file.cairo'], \ 'calendar': ['calendar', '/.calendar/file', '/share/calendar/any/calendar.file', '/share/calendar/calendar.file', 'any/share/calendar/any/calendar.file', 'any/share/calendar/calendar.file'], @@ -228,10 +254,10 @@ let s:filename_checks = { \ 'gedcom': ['file.ged', 'lltxxxxx.txt', '/tmp/lltmp', '/tmp/lltmp-file', 'any/tmp/lltmp', 'any/tmp/lltmp-file'], \ 'gemtext': ['file.gmi', 'file.gemini'], \ 'gift': ['file.gift'], - \ 'gitattributes': ['file.git/info/attributes', '.gitattributes', '/.config/git/attributes', '/etc/gitattributes', '/usr/local/etc/gitattributes', 'some.git/info/attributes'], + \ 'gitattributes': ['file.git/info/attributes', '.gitattributes', '/.config/git/attributes', '/etc/gitattributes', '/usr/local/etc/gitattributes', 'some.git/info/attributes'] + s:WhenConfigHome('$XDG_CONFIG_HOME/git/attributes'), \ 'gitcommit': ['COMMIT_EDITMSG', 'MERGE_MSG', 'TAG_EDITMSG', 'NOTES_EDITMSG', 'EDIT_DESCRIPTION'], - \ 'gitconfig': ['file.git/config', 'file.git/config.worktree', 'file.git/worktrees/x/config.worktree', '.gitconfig', '.gitmodules', 'file.git/modules//config', '/.config/git/config', '/etc/gitconfig', '/usr/local/etc/gitconfig', '/etc/gitconfig.d/file', 'any/etc/gitconfig.d/file', '/.gitconfig.d/file', 'any/.config/git/config', 'any/.gitconfig.d/file', 'some.git/config', 'some.git/modules/any/config'], - \ 'gitignore': ['file.git/info/exclude', '.gitignore', '/.config/git/ignore', 'some.git/info/exclude'], + \ 'gitconfig': ['file.git/config', 'file.git/config.worktree', 'file.git/worktrees/x/config.worktree', '.gitconfig', '.gitmodules', 'file.git/modules//config', '/.config/git/config', '/etc/gitconfig', '/usr/local/etc/gitconfig', '/etc/gitconfig.d/file', 'any/etc/gitconfig.d/file', '/.gitconfig.d/file', 'any/.config/git/config', 'any/.gitconfig.d/file', 'some.git/config', 'some.git/modules/any/config'] + s:WhenConfigHome('$XDG_CONFIG_HOME/git/config'), + \ 'gitignore': ['file.git/info/exclude', '.gitignore', '/.config/git/ignore', 'some.git/info/exclude'] + s:WhenConfigHome('$XDG_CONFIG_HOME/git/ignore'), \ 'gitolite': ['gitolite.conf', '/gitolite-admin/conf/file', 'any/gitolite-admin/conf/file'], \ 'gitrebase': ['git-rebase-todo'], \ 'gitsendemail': ['.gitsendemail.msg.xxxxxx'], @@ -440,6 +466,7 @@ let s:filename_checks = { \ 'pccts': ['file.g'], \ 'pcmk': ['file.pcmk'], \ 'pdf': ['file.pdf'], + \ 'pem': ['file.pem', 'file.cer', 'file.crt', 'file.csr'], \ 'perl': ['file.plx', 'file.al', 'file.psgi', 'gitolite.rc', '.gitolite.rc', 'example.gitolite.rc', '.latexmkrc', 'latexmkrc'], \ 'pf': ['pf.conf'], \ 'pfmain': ['main.cf', 'main.cf.proto'], @@ -535,7 +562,7 @@ let s:filename_checks = { \ 'services': ['/etc/services', 'any/etc/services'], \ 'setserial': ['/etc/serial.conf', 'any/etc/serial.conf'], \ 'sexplib': ['file.sexp'], - \ 'sh': ['.bashrc', 'file.bash', '/usr/share/doc/bash-completion/filter.sh','/etc/udev/cdsymlinks.conf', 'any/etc/udev/cdsymlinks.conf'], + \ 'sh': ['.bashrc', '.bash_profile', '.bash-profile', '.bash_logout', '.bash-logout', '.bash_aliases', '.bash-aliases', '/tmp/bash-fc-3Ozjlw', '/tmp/bash-fc.3Ozjlw', 'PKGBUILD', 'APKBUILD', 'file.bash', '/usr/share/doc/bash-completion/filter.sh', '/etc/udev/cdsymlinks.conf', 'any/etc/udev/cdsymlinks.conf'], \ 'sieve': ['file.siv', 'file.sieve'], \ 'sil': ['file.sil'], \ 'simula': ['file.sim'], @@ -593,6 +620,7 @@ let s:filename_checks = { \ 'sysctl': ['/etc/sysctl.conf', '/etc/sysctl.d/file.conf', 'any/etc/sysctl.conf', 'any/etc/sysctl.d/file.conf'], \ 'systemd': ['any/systemd/file.automount', 'any/systemd/file.dnssd', 'any/systemd/file.link', 'any/systemd/file.mount', 'any/systemd/file.netdev', 'any/systemd/file.network', 'any/systemd/file.nspawn', 'any/systemd/file.path', 'any/systemd/file.service', 'any/systemd/file.slice', 'any/systemd/file.socket', 'any/systemd/file.swap', 'any/systemd/file.target', 'any/systemd/file.timer', '/etc/systemd/some.conf.d/file.conf', '/etc/systemd/system/some.d/file.conf', '/etc/systemd/system/some.d/.#file', '/etc/systemd/system/.#otherfile', '/home/user/.config/systemd/user/some.d/mine.conf', '/home/user/.config/systemd/user/some.d/.#file', '/home/user/.config/systemd/user/.#otherfile', '/.config/systemd/user/.#', '/.config/systemd/user/.#-file', '/.config/systemd/user/file.d/.#', '/.config/systemd/user/file.d/.#-file', '/.config/systemd/user/file.d/file.conf', '/etc/systemd/file.conf.d/file.conf', '/etc/systemd/system/.#', '/etc/systemd/system/.#-file', '/etc/systemd/system/file.d/.#', '/etc/systemd/system/file.d/.#-file', '/etc/systemd/system/file.d/file.conf', '/systemd/file.automount', '/systemd/file.dnssd', '/systemd/file.link', '/systemd/file.mount', '/systemd/file.netdev', '/systemd/file.network', '/systemd/file.nspawn', '/systemd/file.path', '/systemd/file.service', '/systemd/file.slice', '/systemd/file.socket', '/systemd/file.swap', '/systemd/file.target', '/systemd/file.timer', 'any/.config/systemd/user/.#', 'any/.config/systemd/user/.#-file', 'any/.config/systemd/user/file.d/.#', 'any/.config/systemd/user/file.d/.#-file', 'any/.config/systemd/user/file.d/file.conf', 'any/etc/systemd/file.conf.d/file.conf', 'any/etc/systemd/system/.#', 'any/etc/systemd/system/.#-file', 'any/etc/systemd/system/file.d/.#', 'any/etc/systemd/system/file.d/.#-file', 'any/etc/systemd/system/file.d/file.conf'], \ 'systemverilog': ['file.sv', 'file.svh'], + \ 'trace32': ['file.cmm', 'file.t32'], \ 'tags': ['tags'], \ 'tak': ['file.tak'], \ 'tal': ['file.tal'], @@ -640,6 +668,7 @@ let s:filename_checks = { \ 'upstreamdat': ['upstream.dat', 'UPSTREAM.DAT', 'upstream.file.dat', 'UPSTREAM.FILE.DAT', 'file.upstream.dat', 'FILE.UPSTREAM.DAT'], \ 'upstreaminstalllog': ['upstreaminstall.log', 'UPSTREAMINSTALL.LOG', 'upstreaminstall.file.log', 'UPSTREAMINSTALL.FILE.LOG', 'file.upstreaminstall.log', 'FILE.UPSTREAMINSTALL.LOG'], \ 'upstreamlog': ['fdrupstream.log', 'upstream.log', 'UPSTREAM.LOG', 'upstream.file.log', 'UPSTREAM.FILE.LOG', 'file.upstream.log', 'FILE.UPSTREAM.LOG', 'UPSTREAM-file.log', 'UPSTREAM-FILE.LOG'], + \ 'urlshortcut': ['file.url'], \ 'usd': ['file.usda', 'file.usd'], \ 'usserverlog': ['usserver.log', 'USSERVER.LOG', 'usserver.file.log', 'USSERVER.FILE.LOG', 'file.usserver.log', 'FILE.USSERVER.LOG'], \ 'usw2kagtlog': ['usw2kagt.log', 'USW2KAGT.LOG', 'usw2kagt.file.log', 'USW2KAGT.FILE.LOG', 'file.usw2kagt.log', 'FILE.USW2KAGT.LOG'], @@ -695,14 +724,16 @@ let s:filename_checks = { \ 'zimbu': ['file.zu'], \ 'zimbutempl': ['file.zut'], \ 'zir': ['file.zir'], + \ 'zserio': ['file.zs'], \ 'zsh': ['.zprofile', '/etc/zprofile', '.zfbfmarks', 'file.zsh', '.zcompdump', '.zlogin', '.zlogout', '.zshenv', '.zshrc', '.zcompdump-file', '.zlog', '.zlog-file', '.zsh', '.zsh-file', 'any/etc/zprofile', 'zlog', 'zlog-file', 'zsh', 'zsh-file'], \ \ 'help': [$VIMRUNTIME . '/doc/help.txt'], \ } +endfunc let s:filename_case_checks = { \ 'modula2': ['file.DEF'], - \ 'bzl': ['file.BUILD', 'BUILD'], + \ 'bzl': ['file.BUILD', 'BUILD', 'BUCK'], \ } func CheckItems(checks) @@ -729,8 +760,14 @@ func CheckItems(checks) endfunc func Test_filetype_detection() + call s:SetupConfigHome() + if !empty(s:saveConfigHome) + defer setenv("XDG_CONFIG_HOME", s:saveConfigHome) + endif + call mkdir(s:GetConfigHome(), 'R') + filetype on - call CheckItems(s:filename_checks) + call CheckItems(s:GetFilenameChecks()) if has('fname_case') call CheckItems(s:filename_case_checks) endif @@ -771,6 +808,7 @@ let s:script_checks = { \ 'expect': [['#!/path/expect']], \ 'gnuplot': [['#!/path/gnuplot']], \ 'make': [['#!/path/make']], + \ 'nix': [['#!/path/nix-shell']], \ 'pike': [['#!/path/pike'], \ ['#!/path/pike0'], \ ['#!/path/pike9']], @@ -817,6 +855,7 @@ let s:script_env_checks = { \ 'scheme': [['#!/usr/bin/env VAR=val --ignore-environment scheme']], \ 'python': [['#!/usr/bin/env VAR=val -S python -w -T']], \ 'wml': [['#!/usr/bin/env VAR=val --split-string wml']], + \ 'nix': [['#!/usr/bin/env nix-shell']], \ } func Run_script_detection(test_dict) diff --git a/test/old/testdir/test_float_func.vim b/test/old/testdir/test_float_func.vim index 902a011a9d..f9a7251d59 100644 --- a/test/old/testdir/test_float_func.vim +++ b/test/old/testdir/test_float_func.vim @@ -2,6 +2,7 @@ source check.vim CheckFeature float +source vim9.vim func Test_abs() call assert_equal('1.23', string(abs(1.23))) @@ -238,7 +239,9 @@ func Test_str2float() call assert_equal("str2float('nan')", string(str2float('NaN'))) call assert_equal("str2float('nan')", string(str2float(' nan '))) - call assert_fails("call str2float(1.2)", 'E806:') + call assert_equal(1.2, str2float(1.2)) + call CheckDefExecFailure(['str2float(1.2)'], 'E1013:') + call CheckScriptFailure(['vim9script', 'str2float(1.2)'], 'E806:') call assert_fails("call str2float([])", 'E730:') call assert_fails("call str2float({})", 'E731:') call assert_fails("call str2float(function('string'))", 'E729:') diff --git a/test/old/testdir/test_functions.vim b/test/old/testdir/test_functions.vim index 177fef9e99..0fca0e322e 100644 --- a/test/old/testdir/test_functions.vim +++ b/test/old/testdir/test_functions.vim @@ -153,11 +153,13 @@ func Test_strwidth() call assert_fails('call strwidth({->0})', 'E729:') call assert_fails('call strwidth([])', 'E730:') call assert_fails('call strwidth({})', 'E731:') - if has('float') - call assert_fails('call strwidth(1.2)', 'E806:') - endif endfor + if has('float') + call assert_equal(3, strwidth(1.2)) + call CheckDefExecAndScriptFailure(['echo strwidth(1.2)'], 'E806:') + endif + set ambiwidth& endfunc @@ -221,7 +223,9 @@ func Test_str2nr() call assert_fails('call str2nr([])', 'E730:') call assert_fails('call str2nr({->2})', 'E729:') if has('float') - call assert_fails('call str2nr(1.2)', 'E806:') + call assert_equal(1, str2nr(1.2)) + call CheckDefExecFailure(['echo str2nr(1.2)'], 'E1013:') + call CheckScriptFailure(['vim9script', 'echo str2nr(1.2)'], 'E806:') endif call assert_fails('call str2nr(10, [])', 'E745:') endfunc @@ -372,7 +376,8 @@ func Test_simplify() call assert_fails('call simplify([])', 'E730:') call assert_fails('call simplify({})', 'E731:') if has('float') - call assert_fails('call simplify(1.2)', 'E806:') + call assert_equal('1.2', simplify(1.2)) + call CheckDefExecAndScriptFailure(['echo simplify(1.2)'], 'E806:') endif endfunc @@ -1267,7 +1272,8 @@ func Test_charidx() call assert_equal(1, charidx(a, 3)) call assert_equal(2, charidx(a, 4)) call assert_equal(3, charidx(a, 7)) - call assert_equal(-1, charidx(a, 8)) + call assert_equal(4, charidx(a, 8)) + call assert_equal(-1, charidx(a, 9)) call assert_equal(-1, charidx(a, -1)) " count composing characters @@ -1275,14 +1281,18 @@ func Test_charidx() call assert_equal(2, a->charidx(2, 1)) call assert_equal(3, a->charidx(4, 1)) call assert_equal(5, a->charidx(7, 1)) - call assert_equal(-1, a->charidx(8, 1)) + call assert_equal(6, a->charidx(8, 1)) + call assert_equal(-1, a->charidx(9, 1)) " empty string - call assert_equal(-1, charidx('', 0)) - call assert_equal(-1, charidx('', 0, 1)) + call assert_equal(0, charidx('', 0)) + call assert_equal(-1, charidx('', 1)) + call assert_equal(0, charidx('', 0, 1)) + call assert_equal(-1, charidx('', 1, 1)) " error cases - call assert_equal(-1, charidx(v:_null_string, 0)) + call assert_equal(0, charidx(v:_null_string, 0)) + call assert_equal(-1, charidx(v:_null_string, 1)) call assert_fails('let x = charidx([], 1)', 'E1174:') call assert_fails('let x = charidx("abc", [])', 'E1210:') call assert_fails('let x = charidx("abc", 1, [])', 'E1212:') @@ -1294,10 +1304,10 @@ endfunc func Test_charidx_from_utf16_index() " string with single byte characters let str = "abc" - for i in range(3) + for i in range(4) call assert_equal(i, charidx(str, i, v:false, v:true)) endfor - call assert_equal(-1, charidx(str, 3, v:false, v:true)) + call assert_equal(-1, charidx(str, 4, v:false, v:true)) " string with two byte characters let str = "a©©b" @@ -1305,7 +1315,8 @@ func Test_charidx_from_utf16_index() call assert_equal(1, charidx(str, 1, v:false, v:true)) call assert_equal(2, charidx(str, 2, v:false, v:true)) call assert_equal(3, charidx(str, 3, v:false, v:true)) - call assert_equal(-1, charidx(str, 4, v:false, v:true)) + call assert_equal(4, charidx(str, 4, v:false, v:true)) + call assert_equal(-1, charidx(str, 5, v:false, v:true)) " string with four byte characters let str = "a😊😊b" @@ -1315,38 +1326,48 @@ func Test_charidx_from_utf16_index() call assert_equal(2, charidx(str, 3, v:false, v:true)) call assert_equal(2, charidx(str, 4, v:false, v:true)) call assert_equal(3, charidx(str, 5, v:false, v:true)) - call assert_equal(-1, charidx(str, 6, v:false, v:true)) + call assert_equal(4, charidx(str, 6, v:false, v:true)) + call assert_equal(-1, charidx(str, 7, v:false, v:true)) " string with composing characters let str = '-á-b́' for i in str->strcharlen()->range() call assert_equal(i, charidx(str, i, v:false, v:true)) endfor - call assert_equal(-1, charidx(str, 4, v:false, v:true)) + call assert_equal(4, charidx(str, 4, v:false, v:true)) + call assert_equal(-1, charidx(str, 5, v:false, v:true)) for i in str->strchars()->range() call assert_equal(i, charidx(str, i, v:true, v:true)) endfor - call assert_equal(-1, charidx(str, 6, v:true, v:true)) + call assert_equal(6, charidx(str, 6, v:true, v:true)) + call assert_equal(-1, charidx(str, 7, v:true, v:true)) " string with multiple composing characters let str = '-ą́-ą́' for i in str->strcharlen()->range() call assert_equal(i, charidx(str, i, v:false, v:true)) endfor - call assert_equal(-1, charidx(str, 4, v:false, v:true)) + call assert_equal(4, charidx(str, 4, v:false, v:true)) + call assert_equal(-1, charidx(str, 5, v:false, v:true)) for i in str->strchars()->range() call assert_equal(i, charidx(str, i, v:true, v:true)) endfor - call assert_equal(-1, charidx(str, 8, v:true, v:true)) + call assert_equal(8, charidx(str, 8, v:true, v:true)) + call assert_equal(-1, charidx(str, 9, v:true, v:true)) " empty string - call assert_equal(-1, charidx('', 0, v:false, v:true)) - call assert_equal(-1, charidx('', 0, v:true, v:true)) + call assert_equal(0, charidx('', 0, v:false, v:true)) + call assert_equal(-1, charidx('', 1, v:false, v:true)) + call assert_equal(0, charidx('', 0, v:true, v:true)) + call assert_equal(-1, charidx('', 1, v:true, v:true)) " error cases - call assert_equal(-1, charidx('', 0, v:false, v:true)) - call assert_equal(-1, charidx('', 0, v:true, v:true)) - call assert_equal(-1, charidx(v:_null_string, 0, v:false, v:true)) + call assert_equal(0, charidx('', 0, v:false, v:true)) + call assert_equal(-1, charidx('', 1, v:false, v:true)) + call assert_equal(0, charidx('', 0, v:true, v:true)) + call assert_equal(-1, charidx('', 1, v:true, v:true)) + call assert_equal(0, charidx(v:_null_string, 0, v:false, v:true)) + call assert_equal(-1, charidx(v:_null_string, 1, v:false, v:true)) call assert_fails('let x = charidx("abc", 1, v:false, [])', 'E1212:') call assert_fails('let x = charidx("abc", 1, v:true, [])', 'E1212:') endfunc @@ -1355,10 +1376,10 @@ endfunc func Test_utf16idx_from_byteidx() " UTF-16 index of a string with single byte characters let str = "abc" - for i in range(3) + for i in range(4) call assert_equal(i, utf16idx(str, i)) endfor - call assert_equal(-1, utf16idx(str, 3)) + call assert_equal(-1, utf16idx(str, 4)) " UTF-16 index of a string with two byte characters let str = 'a©©b' @@ -1368,21 +1389,23 @@ func Test_utf16idx_from_byteidx() call assert_equal(2, str->utf16idx(3)) call assert_equal(2, str->utf16idx(4)) call assert_equal(3, str->utf16idx(5)) - call assert_equal(-1, str->utf16idx(6)) + call assert_equal(4, str->utf16idx(6)) + call assert_equal(-1, str->utf16idx(7)) " UTF-16 index of a string with four byte characters let str = 'a😊😊b' call assert_equal(0, utf16idx(str, 0)) - call assert_equal(2, utf16idx(str, 1)) - call assert_equal(2, utf16idx(str, 2)) - call assert_equal(2, utf16idx(str, 3)) - call assert_equal(2, utf16idx(str, 4)) - call assert_equal(4, utf16idx(str, 5)) - call assert_equal(4, utf16idx(str, 6)) - call assert_equal(4, utf16idx(str, 7)) - call assert_equal(4, utf16idx(str, 8)) + call assert_equal(1, utf16idx(str, 1)) + call assert_equal(1, utf16idx(str, 2)) + call assert_equal(1, utf16idx(str, 3)) + call assert_equal(1, utf16idx(str, 4)) + call assert_equal(3, utf16idx(str, 5)) + call assert_equal(3, utf16idx(str, 6)) + call assert_equal(3, utf16idx(str, 7)) + call assert_equal(3, utf16idx(str, 8)) call assert_equal(5, utf16idx(str, 9)) - call assert_equal(-1, utf16idx(str, 10)) + call assert_equal(6, utf16idx(str, 10)) + call assert_equal(-1, utf16idx(str, 11)) " UTF-16 index of a string with composing characters let str = '-á-b́' @@ -1394,7 +1417,8 @@ func Test_utf16idx_from_byteidx() call assert_equal(3, utf16idx(str, 5)) call assert_equal(3, utf16idx(str, 6)) call assert_equal(3, utf16idx(str, 7)) - call assert_equal(-1, utf16idx(str, 8)) + call assert_equal(4, utf16idx(str, 8)) + call assert_equal(-1, utf16idx(str, 9)) call assert_equal(0, utf16idx(str, 0, v:true)) call assert_equal(1, utf16idx(str, 1, v:true)) call assert_equal(2, utf16idx(str, 2, v:true)) @@ -1403,7 +1427,8 @@ func Test_utf16idx_from_byteidx() call assert_equal(4, utf16idx(str, 5, v:true)) call assert_equal(5, utf16idx(str, 6, v:true)) call assert_equal(5, utf16idx(str, 7, v:true)) - call assert_equal(-1, utf16idx(str, 8, v:true)) + call assert_equal(6, utf16idx(str, 8, v:true)) + call assert_equal(-1, utf16idx(str, 9, v:true)) " string with multiple composing characters let str = '-ą́-ą́' @@ -1419,7 +1444,8 @@ func Test_utf16idx_from_byteidx() call assert_equal(3, utf16idx(str, 9)) call assert_equal(3, utf16idx(str, 10)) call assert_equal(3, utf16idx(str, 11)) - call assert_equal(-1, utf16idx(str, 12)) + call assert_equal(4, utf16idx(str, 12)) + call assert_equal(-1, utf16idx(str, 13)) call assert_equal(0, utf16idx(str, 0, v:true)) call assert_equal(1, utf16idx(str, 1, v:true)) call assert_equal(2, utf16idx(str, 2, v:true)) @@ -1432,16 +1458,21 @@ func Test_utf16idx_from_byteidx() call assert_equal(6, utf16idx(str, 9, v:true)) call assert_equal(7, utf16idx(str, 10, v:true)) call assert_equal(7, utf16idx(str, 11, v:true)) - call assert_equal(-1, utf16idx(str, 12, v:true)) + call assert_equal(8, utf16idx(str, 12, v:true)) + call assert_equal(-1, utf16idx(str, 13, v:true)) " empty string - call assert_equal(-1, utf16idx('', 0)) - call assert_equal(-1, utf16idx('', 0, v:true)) + call assert_equal(0, utf16idx('', 0)) + call assert_equal(-1, utf16idx('', 1)) + call assert_equal(0, utf16idx('', 0, v:true)) + call assert_equal(-1, utf16idx('', 1, v:true)) " error cases - call assert_equal(-1, utf16idx("", 0)) + call assert_equal(0, utf16idx("", 0)) + call assert_equal(-1, utf16idx("", 1)) call assert_equal(-1, utf16idx("abc", -1)) - call assert_equal(-1, utf16idx(v:_null_string, 0)) + call assert_equal(0, utf16idx(v:_null_string, 0)) + call assert_equal(-1, utf16idx(v:_null_string, 1)) call assert_fails('let l = utf16idx([], 0)', 'E1174:') call assert_fails('let l = utf16idx("ab", [])', 'E1210:') call assert_fails('let l = utf16idx("ab", 0, [])', 'E1212:') @@ -1453,51 +1484,61 @@ func Test_utf16idx_from_charidx() for i in str->strcharlen()->range() call assert_equal(i, utf16idx(str, i, v:false, v:true)) endfor - call assert_equal(-1, utf16idx(str, 3, v:false, v:true)) + call assert_equal(3, utf16idx(str, 3, v:false, v:true)) + call assert_equal(-1, utf16idx(str, 4, v:false, v:true)) " UTF-16 index of a string with two byte characters let str = "a©©b" for i in str->strcharlen()->range() call assert_equal(i, utf16idx(str, i, v:false, v:true)) endfor - call assert_equal(-1, utf16idx(str, 4, v:false, v:true)) + call assert_equal(4, utf16idx(str, 4, v:false, v:true)) + call assert_equal(-1, utf16idx(str, 5, v:false, v:true)) " UTF-16 index of a string with four byte characters let str = "a😊😊b" call assert_equal(0, utf16idx(str, 0, v:false, v:true)) - call assert_equal(2, utf16idx(str, 1, v:false, v:true)) - call assert_equal(4, utf16idx(str, 2, v:false, v:true)) + call assert_equal(1, utf16idx(str, 1, v:false, v:true)) + call assert_equal(3, utf16idx(str, 2, v:false, v:true)) call assert_equal(5, utf16idx(str, 3, v:false, v:true)) - call assert_equal(-1, utf16idx(str, 4, v:false, v:true)) + call assert_equal(6, utf16idx(str, 4, v:false, v:true)) + call assert_equal(-1, utf16idx(str, 5, v:false, v:true)) " UTF-16 index of a string with composing characters let str = '-á-b́' for i in str->strcharlen()->range() call assert_equal(i, utf16idx(str, i, v:false, v:true)) endfor - call assert_equal(-1, utf16idx(str, 4, v:false, v:true)) + call assert_equal(4, utf16idx(str, 4, v:false, v:true)) + call assert_equal(-1, utf16idx(str, 5, v:false, v:true)) for i in str->strchars()->range() call assert_equal(i, utf16idx(str, i, v:true, v:true)) endfor - call assert_equal(-1, utf16idx(str, 6, v:true, v:true)) + call assert_equal(6, utf16idx(str, 6, v:true, v:true)) + call assert_equal(-1, utf16idx(str, 7, v:true, v:true)) " string with multiple composing characters let str = '-ą́-ą́' for i in str->strcharlen()->range() call assert_equal(i, utf16idx(str, i, v:false, v:true)) endfor - call assert_equal(-1, utf16idx(str, 4, v:false, v:true)) + call assert_equal(4, utf16idx(str, 4, v:false, v:true)) + call assert_equal(-1, utf16idx(str, 5, v:false, v:true)) for i in str->strchars()->range() call assert_equal(i, utf16idx(str, i, v:true, v:true)) endfor - call assert_equal(-1, utf16idx(str, 8, v:true, v:true)) + call assert_equal(8, utf16idx(str, 8, v:true, v:true)) + call assert_equal(-1, utf16idx(str, 9, v:true, v:true)) " empty string - call assert_equal(-1, utf16idx('', 0, v:false, v:true)) - call assert_equal(-1, utf16idx('', 0, v:true, v:true)) + call assert_equal(0, utf16idx('', 0, v:false, v:true)) + call assert_equal(-1, utf16idx('', 1, v:false, v:true)) + call assert_equal(0, utf16idx('', 0, v:true, v:true)) + call assert_equal(-1, utf16idx('', 1, v:true, v:true)) " error cases - call assert_equal(-1, utf16idx(v:_null_string, 0, v:true, v:true)) + call assert_equal(0, utf16idx(v:_null_string, 0, v:true, v:true)) + call assert_equal(-1, utf16idx(v:_null_string, 1, v:true, v:true)) call assert_fails('let l = utf16idx("ab", 0, v:false, [])', 'E1212:') endfunc diff --git a/test/old/testdir/test_glob2regpat.vim b/test/old/testdir/test_glob2regpat.vim index a423a4a9f0..b9c4c12059 100644 --- a/test/old/testdir/test_glob2regpat.vim +++ b/test/old/testdir/test_glob2regpat.vim @@ -1,8 +1,11 @@ " Test glob2regpat() +source vim9.vim + func Test_glob2regpat_invalid() if has('float') - call assert_fails('call glob2regpat(1.33)', 'E806:') + call assert_equal('^1\.33$', glob2regpat(1.33)) + call CheckDefExecAndScriptFailure(['echo glob2regpat(1.33)'], 'E806:') endif call assert_fails('call glob2regpat("}")', 'E219:') call assert_fails('call glob2regpat("{")', 'E220:') diff --git a/test/old/testdir/test_highlight.vim b/test/old/testdir/test_highlight.vim index 8a102f2e65..8ed03732c2 100644 --- a/test/old/testdir/test_highlight.vim +++ b/test/old/testdir/test_highlight.vim @@ -698,7 +698,7 @@ func Test_colorcolumn_sbr() let lines =<< trim END call setline(1, 'The quick brown fox jumped over the lazy dogs') END - call writefile(lines, 'Xtest_colorcolumn_srb') + call writefile(lines, 'Xtest_colorcolumn_srb', 'D') let buf = RunVimInTerminal('-S Xtest_colorcolumn_srb', {'rows': 10,'columns': 40}) call term_sendkeys(buf, ":set co=40 showbreak=+++>\\ cc=40,41,43\<CR>") call TermWait(buf) @@ -706,7 +706,26 @@ func Test_colorcolumn_sbr() " clean up call StopVimInTerminal(buf) - call delete('Xtest_colorcolumn_srb') +endfunc + +func Test_visual_sbr() + CheckScreendump + + " check Visual highlight when 'showbreak' is set + let lines =<< trim END + set showbreak=> + call setline(1, 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.') + exe "normal! z1\<CR>" + END + call writefile(lines, 'Xtest_visual_sbr', 'D') + let buf = RunVimInTerminal('-S Xtest_visual_sbr', {'rows': 6,'columns': 60}) + + call term_sendkeys(buf, "v$") + call VerifyScreenDump(buf, 'Test_visual_sbr_1', {}) + + " clean up + call term_sendkeys(buf, "\<Esc>") + call StopVimInTerminal(buf) endfunc " This test must come before the Test_cursorline test, as it appears this diff --git a/test/old/testdir/test_let.vim b/test/old/testdir/test_let.vim index bf119bdeab..a9cc8a14a4 100644 --- a/test/old/testdir/test_let.vim +++ b/test/old/testdir/test_let.vim @@ -242,7 +242,7 @@ func Test_let_no_type_checking() endfunc func Test_let_termcap() - throw 'skipped: Nvim does not support termcap option' + throw 'Skipped: Nvim does not support termcap options' " Terminal code let old_t_te = &t_te let &t_te = "\<Esc>[yes;" @@ -257,25 +257,45 @@ func Test_let_termcap() let &t_k1 = old_t_k1 endif - call assert_fails('let x = &t_xx', 'E113') + call assert_fails('let x = &t_xx', 'E113:') let &t_xx = "yes" call assert_equal("yes", &t_xx) let &t_xx = "" - call assert_fails('let x = &t_xx', 'E113') + call assert_fails('let x = &t_xx', 'E113:') endfunc func Test_let_option_error() let _w = &tw let &tw = 80 - call assert_fails('let &tw .= 1', 'E734') + call assert_fails('let &tw .= 1', ['E734:', 'E734:']) + call assert_fails('let &tw .= []', ['E734:', 'E734:']) + call assert_fails('let &tw = []', ['E745:', 'E745:']) + call assert_fails('let &tw += []', ['E745:', 'E745:']) call assert_equal(80, &tw) let &tw = _w + let _w = &autoread + let &autoread = 1 + call assert_fails('let &autoread .= 1', ['E734:', 'E734:']) + call assert_fails('let &autoread .= []', ['E734:', 'E734:']) + call assert_fails('let &autoread = []', ['E745:', 'E745:']) + call assert_fails('let &autoread += []', ['E745:', 'E745:']) + call assert_equal(1, &autoread) + let &autoread = _w + let _w = &fillchars let &fillchars = "vert:|" - call assert_fails('let &fillchars += "diff:-"', 'E734') + call assert_fails('let &fillchars += "diff:-"', ['E734:', 'E734:']) + call assert_fails('let &fillchars += []', ['E734:', 'E734:']) + call assert_fails('let &fillchars = []', ['E730:', 'E730:']) + call assert_fails('let &fillchars .= []', ['E730:', 'E730:']) call assert_equal("vert:|", &fillchars) let &fillchars = _w + + call assert_fails('let &nosuchoption = 1', ['E355:', 'E355:']) + call assert_fails('let &nosuchoption = ""', ['E355:', 'E355:']) + call assert_fails('let &nosuchoption = []', ['E355:', 'E355:']) + call assert_fails('let &t_xx = []', ['E730:', 'E730:']) endfunc " Errors with the :let statement diff --git a/test/old/testdir/test_listdict.vim b/test/old/testdir/test_listdict.vim index 11dade18f3..a33725cf5c 100644 --- a/test/old/testdir/test_listdict.vim +++ b/test/old/testdir/test_listdict.vim @@ -60,106 +60,170 @@ endfunc " List identity func Test_list_identity() - let l = [1, 'as''d', [1, 2, function("strlen")], {'a': 1},] - let ll = l - let lx = copy(l) - call assert_true(l == ll) - call assert_false(l isnot ll) - call assert_true(l is ll) - call assert_true(l == lx) - call assert_false(l is lx) - call assert_true(l isnot lx) + let lines =<< trim END + VAR l = [1, 'as''d', [1, 2, function("strlen")], {'a': 1},] + VAR ll = l + VAR lx = copy(l) + call assert_true(l == ll) + call assert_false(l isnot ll) + call assert_true(l is ll) + call assert_true(l == lx) + call assert_false(l is lx) + call assert_true(l isnot lx) + END + call CheckLegacyAndVim9Success(lines) endfunc " removing items with :unlet func Test_list_unlet() - let l = [1, 'as''d', [1, 2, function("strlen")], {'a': 1},] - unlet l[2] - call assert_equal([1, 'as''d', {'a': 1}], l) - let l = range(8) - unlet l[:3] - unlet l[1:] - call assert_equal([4], l) - - " removing items out of range: silently skip items that don't exist - let l = [0, 1, 2, 3] - call assert_fails('unlet l[2:1]', 'E684') + let lines =<< trim END + VAR l = [1, 'as''d', [1, 2, function("strlen")], {'a': 1},] + unlet l[2] + call assert_equal([1, 'as''d', {'a': 1}], l) + LET l = range(8) + unlet l[: 3] + unlet l[1 :] + call assert_equal([4], l) + + #" removing items out of range: silently skip items that don't exist + LET l = [0, 1, 2, 3] + unlet l[2 : 2] + call assert_equal([0, 1, 3], l) + LET l = [0, 1, 2, 3] + unlet l[2 : 3] + call assert_equal([0, 1], l) + LET l = [0, 1, 2, 3] + unlet l[2 : 4] + call assert_equal([0, 1], l) + LET l = [0, 1, 2, 3] + unlet l[2 : 5] + call assert_equal([0, 1], l) + LET l = [0, 1, 2, 3] + unlet l[-2 : 2] + call assert_equal([0, 1, 3], l) + LET l = [0, 1, 2, 3] + unlet l[-3 : 2] + call assert_equal([0, 3], l) + LET l = [0, 1, 2, 3] + unlet l[-4 : 2] + call assert_equal([3], l) + LET l = [0, 1, 2, 3] + unlet l[-5 : 2] + call assert_equal([3], l) + LET l = [0, 1, 2, 3] + unlet l[-6 : 2] + call assert_equal([3], l) + END + call CheckLegacyAndVim9Success(lines) + let l = [0, 1, 2, 3] unlet l[2:2] call assert_equal([0, 1, 3], l) let l = [0, 1, 2, 3] unlet l[2:3] call assert_equal([0, 1], l) - let l = [0, 1, 2, 3] - unlet l[2:4] - call assert_equal([0, 1], l) - let l = [0, 1, 2, 3] - unlet l[2:5] - call assert_equal([0, 1], l) - let l = [0, 1, 2, 3] - call assert_fails('unlet l[-1:2]', 'E684') - let l = [0, 1, 2, 3] - unlet l[-2:2] - call assert_equal([0, 1, 3], l) - let l = [0, 1, 2, 3] - unlet l[-3:2] - call assert_equal([0, 3], l) - let l = [0, 1, 2, 3] - unlet l[-4:2] - call assert_equal([3], l) - let l = [0, 1, 2, 3] - unlet l[-5:2] - call assert_equal([3], l) - let l = [0, 1, 2, 3] - unlet l[-6:2] - call assert_equal([3], l) + + let lines =<< trim END + VAR l = [0, 1, 2, 3] + unlet l[2 : 1] + END + call CheckLegacyAndVim9Failure(lines, 'E684:') + + let lines =<< trim END + VAR l = [0, 1, 2, 3] + unlet l[-1 : 2] + END + call CheckLegacyAndVim9Failure(lines, 'E684:') endfunc " assignment to a list func Test_list_assign() - let l = [0, 1, 2, 3] - let [va, vb] = l[2:3] - call assert_equal([2, 3], [va, vb]) - call assert_fails('let [va, vb] = l', 'E687') - call assert_fails('let [va, vb] = l[1:1]', 'E688') + let lines =<< trim END + VAR l = [0, 1, 2, 3] + VAR va = 0 + VAR vb = 0 + LET [va, vb] = l[2 : 3] + call assert_equal([2, 3], [va, vb]) + END + call CheckLegacyAndVim9Success(lines) + + let lines =<< trim END + let l = [0, 1, 2, 3] + let [va, vb] = l + END + call CheckScriptFailure(lines, 'E687:') + let lines =<< trim END + var l = [0, 1, 2, 3] + var va = 0 + var vb = 0 + [va, vb] = l + END + call CheckScriptFailure(['vim9script'] + lines, 'E687:') + call CheckDefExecFailure(lines, 'E1093: Expected 2 items but got 4') + + let lines =<< trim END + let l = [0, 1, 2, 3] + let [va, vb] = l[1:1] + END + call CheckScriptFailure(lines, 'E688:') + let lines =<< trim END + var l = [0, 1, 2, 3] + var va = 0 + var vb = 0 + [va, vb] = l[1 : 1] + END + call CheckScriptFailure(['vim9script'] + lines, 'E688:') + call CheckDefExecFailure(lines, 'E1093: Expected 2 items but got 1') endfunc " test for range assign func Test_list_range_assign() - let l = [0] - let l[:] = [1, 2] - call assert_equal([1, 2], l) - let l[-4:-1] = [5, 6] - call assert_equal([5, 6], l) + let lines =<< trim END + VAR l = [0] + LET l[:] = [1, 2] + call assert_equal([1, 2], l) + LET l[-4 : -1] = [5, 6] + call assert_equal([5, 6], l) + END + call CheckLegacyAndVim9Success(lines) + + let lines =<< trim END + var l = [7] + l[:] = ['text'] + END + call CheckDefAndScriptFailure(lines, 'E1012:', 2) endfunc " Test removing items in list func Test_list_func_remove() - " Test removing 1 element - let l = [1, 2, 3, 4] - call assert_equal(1, remove(l, 0)) - call assert_equal([2, 3, 4], l) - - let l = [1, 2, 3, 4] - call assert_equal(2, remove(l, 1)) - call assert_equal([1, 3, 4], l) - - let l = [1, 2, 3, 4] - call assert_equal(4, remove(l, -1)) - call assert_equal([1, 2, 3], l) - - " Test removing range of element(s) - let l = [1, 2, 3, 4] - call assert_equal([3], remove(l, 2, 2)) - call assert_equal([1, 2, 4], l) - - let l = [1, 2, 3, 4] - call assert_equal([2, 3], remove(l, 1, 2)) - call assert_equal([1, 4], l) - - let l = [1, 2, 3, 4] - call assert_equal([2, 3], remove(l, -3, -2)) - call assert_equal([1, 4], l) + let lines =<< trim END + #" Test removing 1 element + VAR l = [1, 2, 3, 4] + call assert_equal(1, remove(l, 0)) + call assert_equal([2, 3, 4], l) + + LET l = [1, 2, 3, 4] + call assert_equal(2, remove(l, 1)) + call assert_equal([1, 3, 4], l) + + LET l = [1, 2, 3, 4] + call assert_equal(4, remove(l, -1)) + call assert_equal([1, 2, 3], l) + + #" Test removing range of element(s) + LET l = [1, 2, 3, 4] + call assert_equal([3], remove(l, 2, 2)) + call assert_equal([1, 2, 4], l) + + LET l = [1, 2, 3, 4] + call assert_equal([2, 3], remove(l, 1, 2)) + call assert_equal([1, 4], l) + + LET l = [1, 2, 3, 4] + call assert_equal([2, 3], remove(l, -3, -2)) + call assert_equal([1, 4], l) + END + call CheckLegacyAndVim9Success(lines) " Test invalid cases let l = [1, 2, 3, 4] @@ -172,15 +236,20 @@ endfunc " List add() function func Test_list_add() - let l = [] - call add(l, 1) - call add(l, [2, 3]) - call add(l, []) - call add(l, v:_null_list) - call add(l, {'k' : 3}) - call add(l, {}) - call add(l, v:_null_dict) - call assert_equal([1, [2, 3], [], [], {'k' : 3}, {}, {}], l) + let lines =<< trim END + VAR l = [] + call add(l, 1) + call add(l, [2, 3]) + call add(l, []) + call add(l, v:_null_list) + call add(l, {'k': 3}) + call add(l, {}) + call add(l, v:_null_dict) + call assert_equal([1, [2, 3], [], [], {'k': 3}, {}, {}], l) + END + call CheckLegacyAndVim9Success(lines) + + " weird legacy behavior " call assert_equal(1, add(v:_null_list, 4)) endfunc @@ -188,11 +257,21 @@ endfunc func Test_dict() " Creating Dictionary directly with different types + let lines =<< trim END + VAR d = {'1': 'asd', 'b': [1, 2, function('strlen')], '-1': {'a': 1}, } + call assert_equal("{'1': 'asd', 'b': [1, 2, function('strlen')], '-1': {'a': 1}}", string(d)) + call assert_equal('asd', d.1) + call assert_equal(['-1', '1', 'b'], sort(keys(d))) + call assert_equal(['asd', [1, 2, function('strlen')], {'a': 1}], values(d)) + call extend(d, {3: 33, 1: 99}) + call extend(d, {'b': 'bbb', 'c': 'ccc'}, "keep") + call assert_equal({'c': 'ccc', '1': 99, 'b': [1, 2, function('strlen')], '3': 33, '-1': {'a': 1}}, d) + END + call CheckLegacyAndVim9Success(lines) + let d = {001: 'asd', 'b': [1, 2, function('strlen')], -1: {'a': 1},} call assert_equal("{'1': 'asd', 'b': [1, 2, function('strlen')], '-1': {'a': 1}}", string(d)) - call assert_equal('asd', d.1) - call assert_equal(['-1', '1', 'b'], sort(keys(d))) - call assert_equal(['asd', [1, 2, function('strlen')], {'a': 1}], values(d)) + let v = [] for [key, val] in items(d) call extend(v, [key, val]) @@ -200,12 +279,8 @@ func Test_dict() endfor call assert_equal(['1','asd','b',[1, 2, function('strlen')],'-1',{'a': 1}], v) - call extend(d, {3:33, 1:99}) - call extend(d, {'b':'bbb', 'c':'ccc'}, "keep") - call assert_fails("call extend(d, {3:333,4:444}, 'error')", 'E737') - call assert_equal({'c': 'ccc', '1': 99, 'b': [1, 2, function('strlen')], '3': 33, '-1': {'a': 1}}, d) - call filter(d, 'v:key =~ ''[ac391]''') - call assert_equal({'c': 'ccc', '1': 99, '3': 33, '-1': {'a': 1}}, d) + call extend(d, {3: 33, 1: 99}) + call assert_fails("call extend(d, {3:333,4:444}, 'error')", 'E737:') " duplicate key call assert_fails("let d = {'k' : 10, 'k' : 20}", 'E721:') @@ -230,23 +305,29 @@ endfunc " Dictionary identity func Test_dict_identity() - let d = {001: 'asd', 'b': [1, 2, function('strlen')], -1: {'a': 1},} - let dd = d - let dx = copy(d) - call assert_true(d == dd) - call assert_false(d isnot dd) - call assert_true(d is dd) - call assert_true(d == dx) - call assert_false(d is dx) - call assert_true(d isnot dx) + let lines =<< trim END + VAR d = {'1': 'asd', 'b': [1, 2, function('strlen')], -1: {'a': 1}, } + VAR dd = d + VAR dx = copy(d) + call assert_true(d == dd) + call assert_false(d isnot dd) + call assert_true(d is dd) + call assert_true(d == dx) + call assert_false(d is dx) + call assert_true(d isnot dx) + END + call CheckLegacyAndVim9Success(lines) endfunc " removing items with :unlet func Test_dict_unlet() - let d = {'b':'bbb', '1': 99, '3': 33, '-1': {'a': 1}} - unlet d.b - unlet d[-1] - call assert_equal({'1': 99, '3': 33}, d) + let lines =<< trim END + VAR d = {'b': 'bbb', '1': 99, '3': 33, '-1': {'a': 1}} + unlet d.b + unlet d[-1] + call assert_equal({'1': 99, '3': 33}, d) + END + call CheckLegacyAndVim9Success(lines) endfunc " manipulating a big Dictionary (hashtable.c has a border of 1000 entries) @@ -314,8 +395,30 @@ func Test_dict_assign() let d._ = 2 call assert_equal({'1': 1, '_': 2}, d) - let n = 0 - call assert_fails('let n.key = 3', 'E1203: Dot can only be used on a dictionary: n.key = 3') + let lines =<< trim END + VAR d = {} + LET d.a = 1 + LET d._ = 2 + call assert_equal({'a': 1, '_': 2}, d) + END + call CheckLegacyAndVim9Success(lines) + + let lines =<< trim END + let n = 0 + let n.key = 3 + END + call CheckScriptFailure(lines, 'E1203: Dot can only be used on a dictionary: n.key = 3') + let lines =<< trim END + vim9script + var n = 0 + n.key = 3 + END + call CheckScriptFailure(lines, 'E1203: Dot can only be used on a dictionary: n.key = 3') + let lines =<< trim END + var n = 0 + n.key = 3 + END + call CheckDefFailure(lines, 'E1141:') endfunc " Function in script-local List or Dict @@ -332,13 +435,41 @@ endfunc " Test removing items in a dictionary func Test_dict_func_remove() - let d = {1:'a', 2:'b', 3:'c'} - call assert_equal('b', remove(d, 2)) - call assert_equal({1:'a', 3:'c'}, d) + let lines =<< trim END + VAR d = {1: 'a', 2: 'b', 3: 'c'} + call assert_equal('b', remove(d, 2)) + call assert_equal({1: 'a', 3: 'c'}, d) + END + call CheckLegacyAndVim9Success(lines) - call assert_fails("call remove(d, 1, 2)", 'E118:') - call assert_fails("call remove(d, 'a')", 'E716:') - call assert_fails("call remove(d, [])", 'E730:') + let lines =<< trim END + VAR d = {1: 'a', 3: 'c'} + call remove(d, 1, 2) + END + call CheckLegacyAndVim9Failure(lines, 'E118:') + + let lines =<< trim END + VAR d = {1: 'a', 3: 'c'} + call remove(d, 'a') + END + call CheckLegacyAndVim9Failure(lines, 'E716:') + + let lines =<< trim END + let d = {1: 'a', 3: 'c'} + call remove(d, []) + END + call CheckScriptFailure(lines, 'E730:') + let lines =<< trim END + vim9script + var d = {1: 'a', 3: 'c'} + call remove(d, []) + END + call CheckScriptFailure(lines, 'E1174: String required for argument 2') + let lines =<< trim END + var d = {1: 'a', 3: 'c'} + call remove(d, []) + END + call CheckDefExecFailure(lines, 'E1013: Argument 2: type mismatch, expected string but got list<unknown>') endfunc " Nasty: remove func from Dict that's being called (works) @@ -354,7 +485,7 @@ endfunc func Test_dict_literal_keys() call assert_equal({'one': 1, 'two2': 2, '3three': 3, '44': 4}, #{one: 1, two2: 2, 3three: 3, 44: 4},) - " why *{} cannot be used + " why *{} cannot be used for a literal dictionary let blue = 'blue' call assert_equal('6', trim(execute('echo 2 *{blue: 3}.blue'))) endfunc @@ -535,10 +666,14 @@ func Test_list_locked_var_unlet() call assert_equal(expected[depth][u][1], ps) endfor endfor - " Deleting a list range should fail if the range is locked + + " Deleting a list range with locked items works, but changing the items + " fails. let l = [1, 2, 3, 4] lockvar l[1:2] - call assert_fails('unlet l[1:2]', 'E741:') + call assert_fails('let l[1:2] = [8, 9]', 'E741:') + unlet l[1:2] + call assert_equal([1, 4], l) unlet l endfunc @@ -546,15 +681,13 @@ endfunc " No :unlet after lock on dict: func Test_dict_lock_unlet() - unlet! d let d = {'a': 99, 'b': 100} lockvar 1 d - call assert_fails('unlet d.a', 'E741') + call assert_fails('unlet d.a', 'E741:') endfunc " unlet after lock on dict item func Test_dict_item_lock_unlet() - unlet! d let d = {'a': 99, 'b': 100} lockvar d.a unlet d.a @@ -563,7 +696,6 @@ endfunc " filter() after lock on dict item func Test_dict_lock_filter() - unlet! d let d = {'a': 99, 'b': 100} lockvar d.a call filter(d, 'v:key != "a"') @@ -572,7 +704,6 @@ endfunc " map() after lock on dict func Test_dict_lock_map() - unlet! d let d = {'a': 99, 'b': 100} lockvar 1 d call map(d, 'v:val + 200') @@ -581,16 +712,14 @@ endfunc " No extend() after lock on dict item func Test_dict_lock_extend() - unlet! d let d = {'a': 99, 'b': 100} lockvar d.a - call assert_fails("call extend(d, {'a' : 123})", 'E741') + call assert_fails("call extend(d, {'a' : 123})", 'E741:') call assert_equal({'a': 99, 'b': 100}, d) endfunc " Cannot use += with a locked dict func Test_dict_lock_operator() - unlet! d let d = {} lockvar d call assert_fails("let d += {'k' : 10}", 'E741:') @@ -599,7 +728,7 @@ endfunc " No remove() of write-protected scope-level variable func Tfunc1(this_is_a_long_parameter_name) - call assert_fails("call remove(a:, 'this_is_a_long_parameter_name')", 'E742') + call assert_fails("call remove(a:, 'this_is_a_long_parameter_name')", 'E742:') endfunc func Test_dict_scope_var_remove() call Tfunc1('testval') @@ -607,10 +736,10 @@ endfunc " No extend() of write-protected scope-level variable func Test_dict_scope_var_extend() - call assert_fails("call extend(a:, {'this_is_a_long_parameter_name': 1234})", 'E742') + call assert_fails("call extend(a:, {'this_is_a_long_parameter_name': 1234})", 'E742:') endfunc func Tfunc2(this_is_a_long_parameter_name) - call assert_fails("call extend(a:, {'this_is_a_long_parameter_name': 1234})", 'E742') + call assert_fails("call extend(a:, {'this_is_a_long_parameter_name': 1234})", 'E742:') endfunc func Test_dict_scope_var_extend_overwrite() call Tfunc2('testval') @@ -692,29 +821,29 @@ func Test_func_arg_list() call s:arg_list_test(1, 2, [3, 4], {5: 6}) endfunc -func Test_dict_item_locked() -endfunc - " Tests for reverse(), sort(), uniq() func Test_reverse_sort_uniq() - let l = ['-0', 'A11', 2, 2, 'xaaa', 4, 'foo', 'foo6', 'foo', [0, 1, 2], 'x8', [0, 1, 2], 1.5] - call assert_equal(['-0', 'A11', 2, 'xaaa', 4, 'foo', 'foo6', 'foo', [0, 1, 2], 'x8', [0, 1, 2], 1.5], uniq(copy(l))) - call assert_equal([1.5, [0, 1, 2], 'x8', [0, 1, 2], 'foo', 'foo6', 'foo', 4, 'xaaa', 2, 2, 'A11', '-0'], reverse(l)) - call assert_equal([1.5, [0, 1, 2], 'x8', [0, 1, 2], 'foo', 'foo6', 'foo', 4, 'xaaa', 2, 2, 'A11', '-0'], reverse(reverse(l))) - if has('float') - call assert_equal(['-0', 'A11', 'foo', 'foo', 'foo6', 'x8', 'xaaa', 1.5, 2, 2, 4, [0, 1, 2], [0, 1, 2]], sort(l)) - call assert_equal([[0, 1, 2], [0, 1, 2], 4, 2, 2, 1.5, 'xaaa', 'x8', 'foo6', 'foo', 'foo', 'A11', '-0'], reverse(sort(l))) - call assert_equal(['-0', 'A11', 'foo', 'foo', 'foo6', 'x8', 'xaaa', 1.5, 2, 2, 4, [0, 1, 2], [0, 1, 2]], sort(reverse(sort(l)))) - call assert_equal(['-0', 'A11', 'foo', 'foo6', 'x8', 'xaaa', 1.5, 2, 4, [0, 1, 2]], uniq(sort(l))) - - let l = [7, 9, 'one', 18, 12, 22, 'two', 10.0e-16, -1, 'three', 0xff, 0.22, 'four'] - call assert_equal([-1, 'one', 'two', 'three', 'four', 1.0e-15, 0.22, 7, 9, 12, 18, 22, 255], sort(copy(l), 'n')) - - let l = [7, 9, 18, 12, 22, 10.0e-16, -1, 0xff, 0, -0, 0.22, 'bar', 'BAR', 'Bar', 'Foo', 'FOO', 'foo', 'FOOBAR', {}, []] - call assert_equal(['bar', 'BAR', 'Bar', 'Foo', 'FOO', 'foo', 'FOOBAR', -1, 0, 0, 0.22, 1.0e-15, 12, 18, 22, 255, 7, 9, [], {}], sort(copy(l), 1)) - call assert_equal(['bar', 'BAR', 'Bar', 'Foo', 'FOO', 'foo', 'FOOBAR', -1, 0, 0, 0.22, 1.0e-15, 12, 18, 22, 255, 7, 9, [], {}], sort(copy(l), 'i')) - call assert_equal(['BAR', 'Bar', 'FOO', 'FOOBAR', 'Foo', 'bar', 'foo', -1, 0, 0, 0.22, 1.0e-15, 12, 18, 22, 255, 7, 9, [], {}], sort(copy(l))) - endif + let lines =<< trim END + VAR l = ['-0', 'A11', 2, 2, 'xaaa', 4, 'foo', 'foo6', 'foo', [0, 1, 2], 'x8', [0, 1, 2], 1.5] + call assert_equal(['-0', 'A11', 2, 'xaaa', 4, 'foo', 'foo6', 'foo', [0, 1, 2], 'x8', [0, 1, 2], 1.5], uniq(copy(l))) + call assert_equal([1.5, [0, 1, 2], 'x8', [0, 1, 2], 'foo', 'foo6', 'foo', 4, 'xaaa', 2, 2, 'A11', '-0'], reverse(l)) + call assert_equal([1.5, [0, 1, 2], 'x8', [0, 1, 2], 'foo', 'foo6', 'foo', 4, 'xaaa', 2, 2, 'A11', '-0'], reverse(reverse(l))) + if has('float') + call assert_equal(['-0', 'A11', 'foo', 'foo', 'foo6', 'x8', 'xaaa', 1.5, 2, 2, 4, [0, 1, 2], [0, 1, 2]], sort(l)) + call assert_equal([[0, 1, 2], [0, 1, 2], 4, 2, 2, 1.5, 'xaaa', 'x8', 'foo6', 'foo', 'foo', 'A11', '-0'], reverse(sort(l))) + call assert_equal(['-0', 'A11', 'foo', 'foo', 'foo6', 'x8', 'xaaa', 1.5, 2, 2, 4, [0, 1, 2], [0, 1, 2]], sort(reverse(sort(l)))) + call assert_equal(['-0', 'A11', 'foo', 'foo6', 'x8', 'xaaa', 1.5, 2, 4, [0, 1, 2]], uniq(sort(l))) + + LET l = [7, 9, 'one', 18, 12, 22, 'two', 10.0e-16, -1, 'three', 0xff, 0.22, 'four'] + call assert_equal([-1, 'one', 'two', 'three', 'four', 1.0e-15, 0.22, 7, 9, 12, 18, 22, 255], sort(copy(l), 'n')) + + LET l = [7, 9, 18, 12, 22, 10.0e-16, -1, 0xff, 0, -0, 0.22, 'bar', 'BAR', 'Bar', 'Foo', 'FOO', 'foo', 'FOOBAR', {}, []] + call assert_equal(['bar', 'BAR', 'Bar', 'Foo', 'FOO', 'foo', 'FOOBAR', -1, 0, 0, 0.22, 1.0e-15, 12, 18, 22, 255, 7, 9, [], {}], sort(copy(l), 1)) + call assert_equal(['bar', 'BAR', 'Bar', 'Foo', 'FOO', 'foo', 'FOOBAR', -1, 0, 0, 0.22, 1.0e-15, 12, 18, 22, 255, 7, 9, [], {}], sort(copy(l), 'i')) + call assert_equal(['BAR', 'Bar', 'FOO', 'FOOBAR', 'Foo', 'bar', 'foo', -1, 0, 0, 0.22, 1.0e-15, 12, 18, 22, 255, 7, 9, [], {}], sort(copy(l))) + endif + END + call CheckLegacyAndVim9Success(lines) call assert_fails('call reverse("")', 'E899:') call assert_fails('call uniq([1, 2], {x, y -> []})', 'E745:') @@ -725,28 +854,34 @@ endfunc " reduce a list or a blob func Test_reduce() - call assert_equal(1, reduce([], { acc, val -> acc + val }, 1)) - call assert_equal(10, reduce([1, 3, 5], { acc, val -> acc + val }, 1)) - call assert_equal(2 * (2 * ((2 * 1) + 2) + 3) + 4, reduce([2, 3, 4], { acc, val -> 2 * acc + val }, 1)) - call assert_equal('a x y z', ['x', 'y', 'z']->reduce({ acc, val -> acc .. ' ' .. val}, 'a')) - call assert_equal(#{ x: 1, y: 1, z: 1 }, ['x', 'y', 'z']->reduce({ acc, val -> extend(acc, { val: 1 }) }, {})) - call assert_equal([0, 1, 2, 3], reduce([1, 2, 3], function('add'), [0])) - - let l = ['x', 'y', 'z'] - call assert_equal(42, reduce(l, function('get'), #{ x: #{ y: #{ z: 42 } } })) - call assert_equal(['x', 'y', 'z'], l) - - call assert_equal(1, reduce([1], { acc, val -> acc + val })) - call assert_equal('x y z', reduce(['x', 'y', 'z'], { acc, val -> acc .. ' ' .. val })) - call assert_equal(120, range(1, 5)->reduce({ acc, val -> acc * val })) - call assert_fails("call reduce([], { acc, val -> acc + val })", 'E998: Reduce of an empty List with no initial value') + let lines =<< trim END + call assert_equal(1, reduce([], LSTART acc, val LMIDDLE acc + val LEND, 1)) + call assert_equal(10, reduce([1, 3, 5], LSTART acc, val LMIDDLE acc + val LEND, 1)) + call assert_equal(2 * (2 * ((2 * 1) + 2) + 3) + 4, reduce([2, 3, 4], LSTART acc, val LMIDDLE 2 * acc + val LEND, 1)) + call assert_equal('a x y z', ['x', 'y', 'z']->reduce(LSTART acc, val LMIDDLE acc .. ' ' .. val LEND, 'a')) + call assert_equal([0, 1, 2, 3], reduce([1, 2, 3], function('add'), [0])) + + VAR l = ['x', 'y', 'z'] + call assert_equal(42, reduce(l, function('get'), {'x': {'y': {'z': 42 } } })) + call assert_equal(['x', 'y', 'z'], l) + + call assert_equal(1, reduce([1], LSTART acc, val LMIDDLE acc + val LEND)) + call assert_equal('x y z', reduce(['x', 'y', 'z'], LSTART acc, val LMIDDLE acc .. ' ' .. val LEND)) + call assert_equal(120, range(1, 5)->reduce(LSTART acc, val LMIDDLE acc * val LEND)) + + call assert_equal(1, reduce(0z, LSTART acc, val LMIDDLE acc + val LEND, 1)) + call assert_equal(1 + 0xaf + 0xbf + 0xcf, reduce(0zAFBFCF, LSTART acc, val LMIDDLE acc + val LEND, 1)) + call assert_equal(2 * (2 * 1 + 0xaf) + 0xbf, 0zAFBF->reduce(LSTART acc, val LMIDDLE 2 * acc + val LEND, 1)) + + call assert_equal(0xff, reduce(0zff, LSTART acc, val LMIDDLE acc + val LEND)) + call assert_equal(2 * (2 * 0xaf + 0xbf) + 0xcf, reduce(0zAFBFCF, LSTART acc, val LMIDDLE 2 * acc + val LEND)) + END + call CheckLegacyAndVim9Success(lines) - call assert_equal(1, reduce(0z, { acc, val -> acc + val }, 1)) - call assert_equal(1 + 0xaf + 0xbf + 0xcf, reduce(0zAFBFCF, { acc, val -> acc + val }, 1)) - call assert_equal(2 * (2 * 1 + 0xaf) + 0xbf, 0zAFBF->reduce({ acc, val -> 2 * acc + val }, 1)) + call assert_equal({'x': 1, 'y': 1, 'z': 1 }, ['x', 'y', 'z']->reduce({ acc, val -> extend(acc, { val: 1 }) }, {})) + " vim9 assert_equal({'x': 1, 'y': 1, 'z': 1 }, ['x', 'y', 'z']->reduce((acc, val) => extend(acc, {[val]: 1 }), {})) - call assert_equal(0xff, reduce(0zff, { acc, val -> acc + val })) - call assert_equal(2 * (2 * 0xaf + 0xbf) + 0xcf, reduce(0zAFBFCF, { acc, val -> 2 * acc + val })) + call assert_fails("call reduce([], { acc, val -> acc + val })", 'E998: Reduce of an empty List with no initial value') call assert_fails("call reduce(0z, { acc, val -> acc + val })", 'E998: Reduce of an empty Blob with no initial value') call assert_fails("call reduce({}, { acc, val -> acc + val }, 1)", 'E897:') @@ -776,29 +911,36 @@ endfunc " splitting a string to a List using split() func Test_str_split() - call assert_equal(['aa', 'bb'], split(' aa bb ')) - call assert_equal(['aa', 'bb'], split(' aa bb ', '\W\+', 0)) - call assert_equal(['', 'aa', 'bb', ''], split(' aa bb ', '\W\+', 1)) - call assert_equal(['', '', 'aa', '', 'bb', '', ''], split(' aa bb ', '\W', 1)) - call assert_equal(['aa', '', 'bb'], split(':aa::bb:', ':', 0)) - call assert_equal(['', 'aa', '', 'bb', ''], split(':aa::bb:', ':', 1)) - call assert_equal(['aa', '', 'bb', 'cc', ''], split('aa,,bb, cc,', ',\s*', 1)) - call assert_equal(['a', 'b', 'c'], split('abc', '\zs')) - call assert_equal(['', 'a', '', 'b', '', 'c', ''], split('abc', '\zs', 1)) + let lines =<< trim END + call assert_equal(['aa', 'bb'], split(' aa bb ')) + call assert_equal(['aa', 'bb'], split(' aa bb ', '\W\+', 0)) + call assert_equal(['', 'aa', 'bb', ''], split(' aa bb ', '\W\+', 1)) + call assert_equal(['', '', 'aa', '', 'bb', '', ''], split(' aa bb ', '\W', 1)) + call assert_equal(['aa', '', 'bb'], split(':aa::bb:', ':', 0)) + call assert_equal(['', 'aa', '', 'bb', ''], split(':aa::bb:', ':', 1)) + call assert_equal(['aa', '', 'bb', 'cc', ''], split('aa,,bb, cc,', ',\s*', 1)) + call assert_equal(['a', 'b', 'c'], split('abc', '\zs')) + call assert_equal(['', 'a', '', 'b', '', 'c', ''], split('abc', '\zs', 1)) + call assert_equal(['abc'], split('abc', '\\%(')) + END + call CheckLegacyAndVim9Success(lines) + call assert_fails("call split('abc', [])", 'E730:') call assert_fails("call split('abc', 'b', [])", 'E745:') - call assert_equal(['abc'], split('abc', '\\%(')) endfunc " compare recursively linked list and dict func Test_listdict_compare() - let l = [1, 2, 3, 4] - let d = {'1': 1, '2': l, '3': 3} - let l[1] = d - call assert_true(l == l) - call assert_true(d == d) - call assert_false(l != deepcopy(l)) - call assert_false(d != deepcopy(d)) + let lines =<< trim END + VAR l = [1, 2, 3, '4'] + VAR d = {'1': 1, '2': l, '3': 3} + LET l[1] = d + call assert_true(l == l) + call assert_true(d == d) + call assert_false(l != deepcopy(l)) + call assert_false(d != deepcopy(d)) + END + call CheckLegacyAndVim9Success(lines) " comparison errors call assert_fails('echo [1, 2] =~ {}', 'E691:') @@ -809,14 +951,17 @@ endfunc " compare complex recursively linked list and dict func Test_listdict_compare_complex() - let l = [] - call add(l, l) - let dict4 = {"l": l} - call add(dict4.l, dict4) - let lcopy = deepcopy(l) - let dict4copy = deepcopy(dict4) - call assert_true(l == lcopy) - call assert_true(dict4 == dict4copy) + let lines =<< trim END + VAR l = [] + call add(l, l) + VAR dict4 = {"l": l} + call add(dict4.l, dict4) + VAR lcopy = deepcopy(l) + VAR dict4copy = deepcopy(dict4) + call assert_true(l == lcopy) + call assert_true(dict4 == dict4copy) + END + call CheckLegacyAndVim9Success(lines) endfunc " Test for extending lists and dictionaries @@ -824,33 +969,36 @@ func Test_listdict_extend() " Test extend() with lists " Pass the same List to extend() - let l = [1, 2, 3] - call assert_equal([1, 2, 3, 1, 2, 3], extend(l, l)) - call assert_equal([1, 2, 3, 1, 2, 3], l) + let lines =<< trim END + VAR l = [1, 2, 3] + call assert_equal([1, 2, 3, 1, 2, 3], extend(l, l)) + call assert_equal([1, 2, 3, 1, 2, 3], l) - let l = [1, 2, 3] - call assert_equal([1, 2, 3, 4, 5, 6], extend(l, [4, 5, 6])) - call assert_equal([1, 2, 3, 4, 5, 6], l) + LET l = [1, 2, 3] + call assert_equal([1, 2, 3, 4, 5, 6], extend(l, [4, 5, 6])) + call assert_equal([1, 2, 3, 4, 5, 6], l) - let l = [1, 2, 3] - call extend(l, [4, 5, 6], 0) - call assert_equal([4, 5, 6, 1, 2, 3], l) + LET l = [1, 2, 3] + call extend(l, [4, 5, 6], 0) + call assert_equal([4, 5, 6, 1, 2, 3], l) - let l = [1, 2, 3] - call extend(l, [4, 5, 6], 1) - call assert_equal([1, 4, 5, 6, 2, 3], l) + LET l = [1, 2, 3] + call extend(l, [4, 5, 6], 1) + call assert_equal([1, 4, 5, 6, 2, 3], l) - let l = [1, 2, 3] - call extend(l, [4, 5, 6], 3) - call assert_equal([1, 2, 3, 4, 5, 6], l) + LET l = [1, 2, 3] + call extend(l, [4, 5, 6], 3) + call assert_equal([1, 2, 3, 4, 5, 6], l) - let l = [1, 2, 3] - call extend(l, [4, 5, 6], -1) - call assert_equal([1, 2, 4, 5, 6, 3], l) + LET l = [1, 2, 3] + call extend(l, [4, 5, 6], -1) + call assert_equal([1, 2, 4, 5, 6, 3], l) - let l = [1, 2, 3] - call extend(l, [4, 5, 6], -3) - call assert_equal([4, 5, 6, 1, 2, 3], l) + LET l = [1, 2, 3] + call extend(l, [4, 5, 6], -3) + call assert_equal([4, 5, 6, 1, 2, 3], l) + END + call CheckLegacyAndVim9Success(lines) let l = [1, 2, 3] call assert_fails("call extend(l, [4, 5, 6], 4)", 'E684:') @@ -862,27 +1010,30 @@ func Test_listdict_extend() " Test extend() with dictionaries. " Pass the same Dict to extend() - let d = { 'a': {'b': 'B'}} - call extend(d, d) - call assert_equal({'a': {'b': 'B'}}, d) + let lines =<< trim END + VAR d = {'a': {'b': 'B'}, 'x': 9} + call extend(d, d) + call assert_equal({'a': {'b': 'B'}, 'x': 9}, d) - let d = {'a': 'A', 'b': 'B'} - call assert_equal({'a': 'A', 'b': 0, 'c': 'C'}, extend(d, {'b': 0, 'c':'C'})) - call assert_equal({'a': 'A', 'b': 0, 'c': 'C'}, d) + LET d = {'a': 'A', 'b': 9} + call assert_equal({'a': 'A', 'b': 0, 'c': 'C'}, extend(d, {'b': 0, 'c': 'C'})) + call assert_equal({'a': 'A', 'b': 0, 'c': 'C'}, d) - let d = {'a': 'A', 'b': 'B'} - call extend(d, {'a': 'A', 'b': 0, 'c': 'C'}, "force") - call assert_equal({'a': 'A', 'b': 0, 'c': 'C'}, d) + LET d = {'a': 'A', 'b': 9} + call extend(d, {'a': 'A', 'b': 0, 'c': 'C'}, "force") + call assert_equal({'a': 'A', 'b': 0, 'c': 'C'}, d) - let d = {'a': 'A', 'b': 'B'} - call extend(d, {'b': 0, 'c':'C'}, "keep") - call assert_equal({'a': 'A', 'b': 'B', 'c': 'C'}, d) + LET d = {'a': 'A', 'b': 9} + call extend(d, {'b': 0, 'c': 'C'}, "keep") + call assert_equal({'a': 'A', 'b': 9, 'c': 'C'}, d) + END + call CheckLegacyAndVim9Success(lines) let d = {'a': 'A', 'b': 'B'} call assert_fails("call extend(d, {'b': 0, 'c':'C'}, 'error')", 'E737:') call assert_fails("call extend(d, {'b': 0, 'c':'C'}, 'xxx')", 'E475:') if has('float') - call assert_fails("call extend(d, {'b': 0, 'c':'C'}, 1.2)", 'E806:') + call assert_fails("call extend(d, {'b': 0, 'c':'C'}, 1.2)", 'E475:') endif call assert_equal({'a': 'A', 'b': 'B'}, d) @@ -893,18 +1044,21 @@ func Test_listdict_extend() call assert_fails("call extend(g:, {'-!' : 10})", 'E461:') " Extend a list with itself. - let l = [1, 5, 7] - call extend(l, l, 0) - call assert_equal([1, 5, 7, 1, 5, 7], l) - let l = [1, 5, 7] - call extend(l, l, 1) - call assert_equal([1, 1, 5, 7, 5, 7], l) - let l = [1, 5, 7] - call extend(l, l, 2) - call assert_equal([1, 5, 1, 5, 7, 7], l) - let l = [1, 5, 7] - call extend(l, l, 3) - call assert_equal([1, 5, 7, 1, 5, 7], l) + let lines =<< trim END + VAR l = [1, 5, 7] + call extend(l, l, 0) + call assert_equal([1, 5, 7, 1, 5, 7], l) + LET l = [1, 5, 7] + call extend(l, l, 1) + call assert_equal([1, 1, 5, 7, 5, 7], l) + LET l = [1, 5, 7] + call extend(l, l, 2) + call assert_equal([1, 5, 1, 5, 7, 7], l) + LET l = [1, 5, 7] + call extend(l, l, 3) + call assert_equal([1, 5, 7, 1, 5, 7], l) + END + call CheckLegacyAndVim9Success(lines) endfunc func Test_listdict_extendnew() @@ -1037,26 +1191,30 @@ endfunc " List and dict indexing tests func Test_listdict_index() - call assert_fails('echo function("min")[0]', 'E695:') - call assert_fails('echo v:true[0]', 'E909:') - let d = {'k' : 10} - call assert_fails('echo d.', 'E15:') - call assert_fails('echo d[1:2]', 'E719:') + call CheckLegacyAndVim9Failure(['echo function("min")[0]'], 'E695:') + call CheckLegacyAndVim9Failure(['echo v:true[0]'], 'E909:') + call CheckLegacyAndVim9Failure(['echo v:null[0]'], 'E909:') + call CheckLegacyAndVim9Failure(['VAR d = {"k": 10}', 'echo d.'], ['E15:', 'E1127:', 'E15:']) + call CheckLegacyAndVim9Failure(['VAR d = {"k": 10}', 'echo d[1 : 2]'], 'E719:') + call assert_fails("let v = [4, 6][{-> 1}]", 'E729:') - call assert_fails("let v = range(5)[2:[]]", 'E730:') + call CheckDefAndScriptFailure(['var v = [4, 6][() => 1]'], ['E1012', 'E703:']) + + call CheckLegacyAndVim9Failure(['VAR v = range(5)[2 : []]'], ['E730:', 'E1012:', 'E730:']) + call assert_fails("let v = range(5)[2:{-> 2}(]", ['E15:', 'E116:']) - call assert_fails("let v = range(5)[2:3", 'E111:') - call assert_fails("let l = insert([1,2,3], 4, 10)", 'E684:') - call assert_fails("let l = insert([1,2,3], 4, -10)", 'E684:') - call assert_fails("let l = insert([1,2,3], 4, [])", 'E745:') - let l = [1, 2, 3] - call assert_fails("let l[i] = 3", 'E121:') - call assert_fails("let l[1.1] = 4", 'E806:') - call assert_fails("let l[:i] = [4, 5]", 'E121:') - call assert_fails("let l[:3.2] = [4, 5]", 'E806:') - " Nvim doesn't have test_unknown() - " let t = test_unknown() - " call assert_fails("echo t[0]", 'E685:') + call CheckDefAndScriptFailure(['var v = range(5)[2 : () => 2(]'], 'E15:') + + call CheckLegacyAndVim9Failure(['VAR v = range(5)[2 : 3'], ['E111:', 'E1097:', 'E111:']) + call CheckLegacyAndVim9Failure(['VAR l = insert([1, 2, 3], 4, 10)'], 'E684:') + call CheckLegacyAndVim9Failure(['VAR l = insert([1, 2, 3], 4, -10)'], 'E684:') + call CheckLegacyAndVim9Failure(['VAR l = insert([1, 2, 3], 4, [])'], ['E745:', 'E1013:', 'E1210:']) + + call CheckLegacyAndVim9Failure(['VAR l = [1, 2, 3]', 'LET l[i] = 3'], ['E121:', 'E1001:', 'E121:']) + call CheckLegacyAndVim9Failure(['VAR l = [1, 2, 3]', 'LET l[1.1] = 4'], ['E805:', 'E1012:', 'E805:']) + call CheckLegacyAndVim9Failure(['VAR l = [1, 2, 3]', 'LET l[: i] = [4, 5]'], ['E121:', 'E1001:', 'E121:']) + call CheckLegacyAndVim9Failure(['VAR l = [1, 2, 3]', 'LET l[: 3.2] = [4, 5]'], ['E805:', 'E1012:', 'E805:']) + " call CheckLegacyAndVim9Failure(['VAR t = test_unknown()', 'echo t[0]'], 'E685:') endfunc " Test for a null list @@ -1103,9 +1261,9 @@ func Test_null_dict() call assert_equal({}, d) call assert_equal(0, len(d)) call assert_equal(1, empty(d)) - call assert_equal(0, items(d)) - call assert_equal(0, keys(d)) - call assert_equal(0, values(d)) + call assert_equal([], items(d)) + call assert_equal([], keys(d)) + call assert_equal([], values(d)) call assert_false(has_key(d, 'k')) call assert_equal('{}', string(d)) call assert_fails('let x = d[10]') diff --git a/test/old/testdir/test_messages.vim b/test/old/testdir/test_messages.vim index a20571807b..31c8b9942b 100644 --- a/test/old/testdir/test_messages.vim +++ b/test/old/testdir/test_messages.vim @@ -341,6 +341,41 @@ func Test_message_more_scrollback() call StopVimInTerminal(buf) endfunc +func Test_message_not_cleared_after_mode() + CheckRunVimInTerminal + + let lines =<< trim END + nmap <silent> gx :call DebugSilent('normal')<CR> + vmap <silent> gx :call DebugSilent('visual')<CR> + function DebugSilent(arg) + echomsg "from DebugSilent" a:arg + endfunction + set showmode + set cmdheight=1 + call test_settime(1) + call setline(1, ['one', 'NoSuchFile', 'three']) + END + call writefile(lines, 'XmessageMode', 'D') + let buf = RunVimInTerminal('-S XmessageMode', {'rows': 10}) + + call term_sendkeys(buf, 'gx') + call TermWait(buf) + call VerifyScreenDump(buf, 'Test_message_not_cleared_after_mode_1', {}) + + " removing the mode message used to also clear the intended message + call term_sendkeys(buf, 'vEgx') + call TermWait(buf) + call VerifyScreenDump(buf, 'Test_message_not_cleared_after_mode_2', {}) + + " removing the mode message used to also clear the error message + call term_sendkeys(buf, ":set cmdheight=2\<CR>") + call term_sendkeys(buf, '2GvEgf') + call TermWait(buf) + call VerifyScreenDump(buf, 'Test_message_not_cleared_after_mode_3', {}) + + call StopVimInTerminal(buf) +endfunc + " Test verbose message before echo command func Test_echo_verbose_system() CheckRunVimInTerminal diff --git a/test/old/testdir/test_options.vim b/test/old/testdir/test_options.vim index ada44e5eed..6390b9c37a 100644 --- a/test/old/testdir/test_options.vim +++ b/test/old/testdir/test_options.vim @@ -380,7 +380,7 @@ func Test_set_completion() call assert_equal('"set filetype=' .. getcompletion('a*', 'filetype')->join(), @:) endfunc -func Test_set_errors() +func Test_set_option_errors() call assert_fails('set scroll=-1', 'E49:') call assert_fails('set backupcopy=', 'E474:') call assert_fails('set regexpengine=3', 'E474:') @@ -482,7 +482,7 @@ func Test_set_errors() if has('python') || has('python3') call assert_fails('set pyxversion=6', 'E474:') endif - call assert_fails("let &tabstop='ab'", 'E521:') + call assert_fails("let &tabstop='ab'", ['E521:', 'E521:']) call assert_fails('set spellcapcheck=%\\(', 'E54:') call assert_fails('set sessionoptions=curdir,sesdir', 'E474:') call assert_fails('set foldmarker={{{,', 'E474:') @@ -506,6 +506,12 @@ func Test_set_errors() " call assert_fails('set t_#-&', 'E522:') call assert_fails('let &formatoptions = "?"', 'E539:') call assert_fails('call setbufvar("", "&formatoptions", "?")', 'E539:') + call assert_fails('call setwinvar(0, "&scrolloff", [])', ['E745:', 'E745:']) + call assert_fails('call setwinvar(0, "&list", [])', ['E745:', 'E745:']) + call assert_fails('call setwinvar(0, "&listchars", [])', ['E730:', 'E730:']) + call assert_fails('call setwinvar(0, "&nosuchoption", 0)', ['E355:', 'E355:']) + call assert_fails('call setwinvar(0, "&nosuchoption", "")', ['E355:', 'E355:']) + call assert_fails('call setwinvar(0, "&nosuchoption", [])', ['E355:', 'E355:']) endfunc func CheckWasSet(name) @@ -933,12 +939,16 @@ func Test_local_scrolloff() wincmd w call assert_equal(5, &so) wincmd w + call assert_equal(3, &so) setlocal so< call assert_equal(5, &so) + setglob so=8 + call assert_equal(8, &so) + call assert_equal(-1, &l:so) setlocal so=0 call assert_equal(0, &so) setlocal so=-1 - call assert_equal(5, &so) + call assert_equal(8, &so) call assert_equal(7, &siso) setlocal siso=3 @@ -946,12 +956,16 @@ func Test_local_scrolloff() wincmd w call assert_equal(7, &siso) wincmd w + call assert_equal(3, &siso) setlocal siso< call assert_equal(7, &siso) + setglob siso=4 + call assert_equal(4, &siso) + call assert_equal(-1, &l:siso) setlocal siso=0 call assert_equal(0, &siso) setlocal siso=-1 - call assert_equal(7, &siso) + call assert_equal(4, &siso) close set so& diff --git a/test/old/testdir/test_quickfix.vim b/test/old/testdir/test_quickfix.vim index 9e8e87fbde..6378ee8770 100644 --- a/test/old/testdir/test_quickfix.vim +++ b/test/old/testdir/test_quickfix.vim @@ -167,19 +167,21 @@ func XlistTests(cchar) \ {'lnum':20,'col':10,'type':'e','text':'Error','nr':22}, \ {'lnum':30,'col':15,'type':'i','text':'Info','nr':33}, \ {'lnum':40,'col':20,'type':'x', 'text':'Other','nr':44}, - \ {'lnum':50,'col':25,'type':"\<C-A>",'text':'one','nr':55}]) + \ {'lnum':50,'col':25,'type':"\<C-A>",'text':'one','nr':55}, + \ {'lnum':0,'type':'e','text':'Check type field is output even when lnum==0. ("error" was not output by v9.0.0736.)','nr':66}]) let l = split(execute('Xlist', ""), "\n") call assert_equal([' 1:10 col 5 warning 11: Warning', \ ' 2:20 col 10 error 22: Error', \ ' 3:30 col 15 info 33: Info', \ ' 4:40 col 20 x 44: Other', - \ ' 5:50 col 25 55: one'], l) + \ ' 5:50 col 25 55: one', + \ ' 6 error 66: Check type field is output even when lnum==0. ("error" was not output by v9.0.0736.)'], l) " Test for module names, one needs to explicitly set `'valid':v:true` so call g:Xsetlist([ - \ {'lnum':10,'col':5,'type':'W','module':'Data.Text','text':'ModuleWarning','nr':11,'valid':v:true}, - \ {'lnum':20,'col':10,'type':'W','module':'Data.Text','filename':'Data/Text.hs','text':'ModuleWarning','nr':22,'valid':v:true}, - \ {'lnum':30,'col':15,'type':'W','filename':'Data/Text.hs','text':'FileWarning','nr':33,'valid':v:true}]) + \ {'lnum':10,'col':5,'type':'W','module':'Data.Text','text':'ModuleWarning','nr':11,'valid':v:true}, + \ {'lnum':20,'col':10,'type':'W','module':'Data.Text','filename':'Data/Text.hs','text':'ModuleWarning','nr':22,'valid':v:true}, + \ {'lnum':30,'col':15,'type':'W','filename':'Data/Text.hs','text':'FileWarning','nr':33,'valid':v:true}]) let l = split(execute('Xlist', ""), "\n") call assert_equal([' 1 Data.Text:10 col 5 warning 11: ModuleWarning', \ ' 2 Data.Text:20 col 10 warning 22: ModuleWarning', @@ -6304,4 +6306,11 @@ func Test_setqflist_stopinsert() bwipe! endfunc +func Test_quickfix_buffer_contents() + call setqflist([{'filename':'filename', 'pattern':'pattern', 'text':'text'}]) + copen + call assert_equal(['filename|pattern| text'], getline(1, '$')) " The assert failed with Vim v9.0.0736; '| text' did not appear after the pattern. + call setqflist([], 'f') +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/test/old/testdir/test_scroll_opt.vim b/test/old/testdir/test_scroll_opt.vim index d7be074168..f60c0ddb59 100644 --- a/test/old/testdir/test_scroll_opt.vim +++ b/test/old/testdir/test_scroll_opt.vim @@ -836,4 +836,26 @@ func Test_smoothscroll_multi_skipcol() call StopVimInTerminal(buf) endfunc +" this was dividing by zero bug in scroll_cursor_bot +func Test_smoothscroll_zero_width_scroll_cursor_bot() + CheckScreendump + + let lines =<< trim END + silent normal yy + silent normal 19p + set cpoptions+=n + vsplit + vertical resize 0 + set foldcolumn=1 + set number + set smoothscroll + silent normal 20G + END + call writefile(lines, 'XSmoothScrollZeroBot', 'D') + let buf = RunVimInTerminal('-u NONE -S XSmoothScrollZeroBot', #{rows: 19}) + call VerifyScreenDump(buf, 'Test_smoothscroll_zero_bot', {}) + + call StopVimInTerminal(buf) +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/test/old/testdir/test_spell.vim b/test/old/testdir/test_spell.vim index 59b433d6e1..7576e57c3f 100644 --- a/test/old/testdir/test_spell.vim +++ b/test/old/testdir/test_spell.vim @@ -281,7 +281,7 @@ func Test_compl_with_CTRL_X_CTRL_K_using_spell() set spell& spelllang& dictionary& ignorecase& endfunc -func Test_spellreall() +func Test_spellrepall() new set spell call assert_fails('spellrepall', 'E752:') diff --git a/test/old/testdir/test_startup.vim b/test/old/testdir/test_startup.vim index da467ea23f..d2ffbd0e2f 100644 --- a/test/old/testdir/test_startup.vim +++ b/test/old/testdir/test_startup.vim @@ -125,15 +125,15 @@ func Test_help_arg() " check if couple of lines are there let found = [] for line in lines - if line =~ '-R.*Read-only mode' - call add(found, 'Readonly mode') + if line =~ '-l.*Execute Lua' + call add(found, 'Execute Lua') endif " Watch out for a second --version line in the Gnome version. if line =~ '--version.*Print version information' call add(found, "--version") endif endfor - call assert_equal(['Readonly mode', '--version'], found) + call assert_equal(['Execute Lua', '--version'], found) endif call delete('Xtestout') endfunc diff --git a/test/old/testdir/test_substitute.vim b/test/old/testdir/test_substitute.vim index 75bd3cff6f..a6640aac30 100644 --- a/test/old/testdir/test_substitute.vim +++ b/test/old/testdir/test_substitute.vim @@ -148,215 +148,6 @@ func Test_substitute_repeat() bwipe! endfunc -" Tests for *sub-replace-special* and *sub-replace-expression* on :substitute. - -" Execute a list of :substitute command tests -func Run_SubCmd_Tests(tests) - enew! - for t in a:tests - let start = line('.') + 1 - let end = start + len(t[2]) - 1 - exe "normal o" . t[0] - call cursor(start, 1) - exe t[1] - call assert_equal(t[2], getline(start, end), t[1]) - endfor - enew! -endfunc - -func Test_sub_cmd_1() - set magic - set cpo& - - " List entry format: [input, cmd, output] - let tests = [['A', 's/A/&&/', ['AA']], - \ ['B', 's/B/\&/', ['&']], - \ ['C123456789', 's/C\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)/\0\9\8\7\6\5\4\3\2\1/', ['C123456789987654321']], - \ ['D', 's/D/d/', ['d']], - \ ['E', 's/E/~/', ['d']], - \ ['F', 's/F/\~/', ['~']], - \ ['G', 's/G/\ugg/', ['Gg']], - \ ['H', 's/H/\Uh\Eh/', ['Hh']], - \ ['I', 's/I/\lII/', ['iI']], - \ ['J', 's/J/\LJ\EJ/', ['jJ']], - \ ['K', 's/K/\Uk\ek/', ['Kk']], - \ ['lLl', "s/L/\<C-V>\<C-M>/", ["l\<C-V>", 'l']], - \ ['mMm', 's/M/\r/', ['m', 'm']], - \ ['nNn', "s/N/\\\<C-V>\<C-M>/", ["n\<C-V>", 'n']], - \ ['oOo', 's/O/\n/', ["o\no"]], - \ ['pPp', 's/P/\b/', ["p\<C-H>p"]], - \ ['qQq', 's/Q/\t/', ["q\tq"]], - \ ['rRr', 's/R/\\/', ['r\r']], - \ ['sSs', 's/S/\c/', ['scs']], - \ ['tTt', "s/T/\<C-V>\<C-J>/", ["t\<C-V>\<C-J>t"]], - \ ['U', 's/U/\L\uuUu\l\EU/', ['UuuU']], - \ ['V', 's/V/\U\lVvV\u\Ev/', ['vVVv']], - \ ['\', 's/\\/\\\\/', ['\\']] - \ ] - call Run_SubCmd_Tests(tests) -endfunc - -func Test_sub_cmd_2() - set nomagic - set cpo& - - " List entry format: [input, cmd, output] - let tests = [['A', 's/A/&&/', ['&&']], - \ ['B', 's/B/\&/', ['B']], - \ ['C123456789', 's/\mC\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)/\0\9\8\7\6\5\4\3\2\1/', ['C123456789987654321']], - \ ['D', 's/D/d/', ['d']], - \ ['E', 's/E/~/', ['~']], - \ ['F', 's/F/\~/', ['~']], - \ ['G', 's/G/\ugg/', ['Gg']], - \ ['H', 's/H/\Uh\Eh/', ['Hh']], - \ ['I', 's/I/\lII/', ['iI']], - \ ['J', 's/J/\LJ\EJ/', ['jJ']], - \ ['K', 's/K/\Uk\ek/', ['Kk']], - \ ['lLl', "s/L/\<C-V>\<C-M>/", ["l\<C-V>", 'l']], - \ ['mMm', 's/M/\r/', ['m', 'm']], - \ ['nNn', "s/N/\\\<C-V>\<C-M>/", ["n\<C-V>", 'n']], - \ ['oOo', 's/O/\n/', ["o\no"]], - \ ['pPp', 's/P/\b/', ["p\<C-H>p"]], - \ ['qQq', 's/Q/\t/', ["q\tq"]], - \ ['rRr', 's/R/\\/', ['r\r']], - \ ['sSs', 's/S/\c/', ['scs']], - \ ['tTt', "s/T/\<C-V>\<C-J>/", ["t\<C-V>\<C-J>t"]], - \ ['U', 's/U/\L\uuUu\l\EU/', ['UuuU']], - \ ['V', 's/V/\U\lVvV\u\Ev/', ['vVVv']], - \ ['\', 's/\\/\\\\/', ['\\']] - \ ] - call Run_SubCmd_Tests(tests) -endfunc - -func Test_sub_cmd_3() - set nomagic - set cpo& - - " List entry format: [input, cmd, output] - let tests = [['aAa', "s/A/\\='\\'/", ['a\a']], - \ ['bBb', "s/B/\\='\\\\'/", ['b\\b']], - \ ['cCc', "s/C/\\='\<C-V>\<C-M>'/", ["c\<C-V>", 'c']], - \ ['dDd', "s/D/\\='\\\<C-V>\<C-M>'/", ["d\\\<C-V>", 'd']], - \ ['eEe', "s/E/\\='\\\\\<C-V>\<C-M>'/", ["e\\\\\<C-V>", 'e']], - \ ['fFf', "s/F/\\='\r'/", ['f', 'f']], - \ ['gGg', "s/G/\\='\<C-V>\<C-J>'/", ["g\<C-V>", 'g']], - \ ['hHh', "s/H/\\='\\\<C-V>\<C-J>'/", ["h\\\<C-V>", 'h']], - \ ['iIi', "s/I/\\='\\\\\<C-V>\<C-J>'/", ["i\\\\\<C-V>", 'i']], - \ ['jJj', "s/J/\\='\n'/", ['j', 'j']], - \ ['kKk', 's/K/\="\r"/', ['k', 'k']], - \ ['lLl', 's/L/\="\n"/', ['l', 'l']] - \ ] - call Run_SubCmd_Tests(tests) -endfunc - -" Test for submatch() on :substitute. -func Test_sub_cmd_4() - set magic& - set cpo& - - " List entry format: [input, cmd, output] - let tests = [ ['aAa', "s/A/\\=substitute(submatch(0), '.', '\\', '')/", - \ ['a\a']], - \ ['bBb', "s/B/\\=substitute(submatch(0), '.', '\\', '')/", - \ ['b\b']], - \ ['cCc', "s/C/\\=substitute(submatch(0), '.', '\<C-V>\<C-M>', '')/", - \ ["c\<C-V>", 'c']], - \ ['dDd', "s/D/\\=substitute(submatch(0), '.', '\\\<C-V>\<C-M>', '')/", - \ ["d\<C-V>", 'd']], - \ ['eEe', "s/E/\\=substitute(submatch(0), '.', '\\\\\<C-V>\<C-M>', '')/", - \ ["e\\\<C-V>", 'e']], - \ ['fFf', "s/F/\\=substitute(submatch(0), '.', '\\r', '')/", - \ ['f', 'f']], - \ ['gGg', 's/G/\=substitute(submatch(0), ".", "\<C-V>\<C-J>", "")/', - \ ["g\<C-V>", 'g']], - \ ['hHh', 's/H/\=substitute(submatch(0), ".", "\\\<C-V>\<C-J>", "")/', - \ ["h\<C-V>", 'h']], - \ ['iIi', 's/I/\=substitute(submatch(0), ".", "\\\\\<C-V>\<C-J>", "")/', - \ ["i\\\<C-V>", 'i']], - \ ['jJj', "s/J/\\=substitute(submatch(0), '.', '\\n', '')/", - \ ['j', 'j']], - \ ['kKk', "s/K/\\=substitute(submatch(0), '.', '\\r', '')/", - \ ['k', 'k']], - \ ['lLl', "s/L/\\=substitute(submatch(0), '.', '\\n', '')/", - \ ['l', 'l']], - \ ] - call Run_SubCmd_Tests(tests) -endfunc - -func Test_sub_cmd_5() - set magic& - set cpo& - - " List entry format: [input, cmd, output] - let tests = [ ['A123456789', 's/A\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)/\=submatch(0) . submatch(9) . submatch(8) . submatch(7) . submatch(6) . submatch(5) . submatch(4) . submatch(3) . submatch(2) . submatch(1)/', ['A123456789987654321']], - \ ['B123456789', 's/B\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)/\=string([submatch(0, 1), submatch(9, 1), submatch(8, 1), submatch(7, 1), submatch(6, 1), submatch(5, 1), submatch(4, 1), submatch(3, 1), submatch(2, 1), submatch(1, 1)])/', ["[['B123456789'], ['9'], ['8'], ['7'], ['6'], ['5'], ['4'], ['3'], ['2'], ['1']]"]], - \ ] - call Run_SubCmd_Tests(tests) -endfunc - -" Test for *:s%* on :substitute. -func Test_sub_cmd_6() - set magic& - " Nvim: no "/" flag in 'cpoptions'. - " set cpo+=/ - - " List entry format: [input, cmd, output] - let tests = [ ['A', 's/A/a/', ['a']], - \ ['B', 's/B/%/', ['a']], - \ ] - " call Run_SubCmd_Tests(tests) - - set cpo-=/ - let tests = [ ['C', 's/C/c/', ['c']], - \ ['D', 's/D/%/', ['%']], - \ ] - call Run_SubCmd_Tests(tests) - - set cpo& -endfunc - -" Test for :s replacing \n with line break. -func Test_sub_cmd_7() - set magic& - set cpo& - - " List entry format: [input, cmd, output] - let tests = [ ["A\<C-V>\<C-M>A", 's/A./\=submatch(0)/', ['A', 'A']], - \ ["B\<C-V>\<C-J>B", 's/B./\=submatch(0)/', ['B', 'B']], - \ ["C\<C-V>\<C-J>C", 's/C./\=strtrans(string(submatch(0, 1)))/', [strtrans("['C\<C-J>']C")]], - \ ["D\<C-V>\<C-J>\nD", 's/D.\nD/\=strtrans(string(submatch(0, 1)))/', [strtrans("['D\<C-J>', 'D']")]], - \ ["E\<C-V>\<C-J>\n\<C-V>\<C-J>\n\<C-V>\<C-J>\n\<C-V>\<C-J>\n\<C-V>\<C-J>E", 's/E\_.\{-}E/\=strtrans(string(submatch(0, 1)))/', [strtrans("['E\<C-J>', '\<C-J>', '\<C-J>', '\<C-J>', '\<C-J>E']")]], - \ ] - call Run_SubCmd_Tests(tests) - - exe "normal oQ\nQ\<Esc>k" - call assert_fails('s/Q[^\n]Q/\=submatch(0)."foobar"/', 'E486') - enew! -endfunc - -func TitleString() - let check = 'foo' =~ 'bar' - return "" -endfunc - -func Test_sub_cmd_8() - set titlestring=%{TitleString()} - - enew! - call append(0, ['', 'test_one', 'test_two']) - call cursor(1,1) - /^test_one/s/.*/\="foo\nbar"/ - call assert_equal('foo', getline(2)) - call assert_equal('bar', getline(3)) - call feedkeys(':/^test_two/s/.*/\="foo\nbar"/c', "t") - call feedkeys("\<CR>y", "xt") - call assert_equal('foo', getline(4)) - call assert_equal('bar', getline(5)) - - enew! - set titlestring& -endfunc - " Test %s/\n// which is implemented as a special case to use a " more efficient join rather than doing a regular substitution. func Test_substitute_join() @@ -666,7 +457,223 @@ func Test_substitute_partial() " 20 arguments plus one is too many let Replacer = function('SubReplacer20', repeat(['foo'], 20)) - call assert_fails("call substitute('123', '2', Replacer, 'g')", 'E118') + call assert_fails("call substitute('123', '2', Replacer, 'g')", 'E118:') +endfunc + +func Test_substitute_float() + CheckFeature float + + call assert_equal('number 1.23', substitute('number ', '$', { -> 1.23 }, '')) + " vim9 assert_equal('number 1.23', substitute('number ', '$', () => 1.23, '')) +endfunc + +" Tests for *sub-replace-special* and *sub-replace-expression* on :substitute. + +" Execute a list of :substitute command tests +func Run_SubCmd_Tests(tests) + enew! + for t in a:tests + let start = line('.') + 1 + let end = start + len(t[2]) - 1 + exe "normal o" . t[0] + call cursor(start, 1) + exe t[1] + call assert_equal(t[2], getline(start, end), t[1]) + endfor + enew! +endfunc + +func Test_sub_cmd_1() + set magic + set cpo& + + " List entry format: [input, cmd, output] + let tests = [['A', 's/A/&&/', ['AA']], + \ ['B', 's/B/\&/', ['&']], + \ ['C123456789', 's/C\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)/\0\9\8\7\6\5\4\3\2\1/', ['C123456789987654321']], + \ ['D', 's/D/d/', ['d']], + \ ['E', 's/E/~/', ['d']], + \ ['F', 's/F/\~/', ['~']], + \ ['G', 's/G/\ugg/', ['Gg']], + \ ['H', 's/H/\Uh\Eh/', ['Hh']], + \ ['I', 's/I/\lII/', ['iI']], + \ ['J', 's/J/\LJ\EJ/', ['jJ']], + \ ['K', 's/K/\Uk\ek/', ['Kk']], + \ ['lLl', "s/L/\<C-V>\<C-M>/", ["l\<C-V>", 'l']], + \ ['mMm', 's/M/\r/', ['m', 'm']], + \ ['nNn', "s/N/\\\<C-V>\<C-M>/", ["n\<C-V>", 'n']], + \ ['oOo', 's/O/\n/', ["o\no"]], + \ ['pPp', 's/P/\b/', ["p\<C-H>p"]], + \ ['qQq', 's/Q/\t/', ["q\tq"]], + \ ['rRr', 's/R/\\/', ['r\r']], + \ ['sSs', 's/S/\c/', ['scs']], + \ ['tTt', "s/T/\<C-V>\<C-J>/", ["t\<C-V>\<C-J>t"]], + \ ['U', 's/U/\L\uuUu\l\EU/', ['UuuU']], + \ ['V', 's/V/\U\lVvV\u\Ev/', ['vVVv']], + \ ['\', 's/\\/\\\\/', ['\\']] + \ ] + call Run_SubCmd_Tests(tests) +endfunc + +func Test_sub_cmd_2() + set nomagic + set cpo& + + " List entry format: [input, cmd, output] + let tests = [['A', 's/A/&&/', ['&&']], + \ ['B', 's/B/\&/', ['B']], + \ ['C123456789', 's/\mC\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)/\0\9\8\7\6\5\4\3\2\1/', ['C123456789987654321']], + \ ['D', 's/D/d/', ['d']], + \ ['E', 's/E/~/', ['~']], + \ ['F', 's/F/\~/', ['~']], + \ ['G', 's/G/\ugg/', ['Gg']], + \ ['H', 's/H/\Uh\Eh/', ['Hh']], + \ ['I', 's/I/\lII/', ['iI']], + \ ['J', 's/J/\LJ\EJ/', ['jJ']], + \ ['K', 's/K/\Uk\ek/', ['Kk']], + \ ['lLl', "s/L/\<C-V>\<C-M>/", ["l\<C-V>", 'l']], + \ ['mMm', 's/M/\r/', ['m', 'm']], + \ ['nNn', "s/N/\\\<C-V>\<C-M>/", ["n\<C-V>", 'n']], + \ ['oOo', 's/O/\n/', ["o\no"]], + \ ['pPp', 's/P/\b/', ["p\<C-H>p"]], + \ ['qQq', 's/Q/\t/', ["q\tq"]], + \ ['rRr', 's/R/\\/', ['r\r']], + \ ['sSs', 's/S/\c/', ['scs']], + \ ['tTt', "s/T/\<C-V>\<C-J>/", ["t\<C-V>\<C-J>t"]], + \ ['U', 's/U/\L\uuUu\l\EU/', ['UuuU']], + \ ['V', 's/V/\U\lVvV\u\Ev/', ['vVVv']], + \ ['\', 's/\\/\\\\/', ['\\']] + \ ] + call Run_SubCmd_Tests(tests) +endfunc + +func Test_sub_cmd_3() + set nomagic + set cpo& + + " List entry format: [input, cmd, output] + let tests = [['aAa', "s/A/\\='\\'/", ['a\a']], + \ ['bBb', "s/B/\\='\\\\'/", ['b\\b']], + \ ['cCc', "s/C/\\='\<C-V>\<C-M>'/", ["c\<C-V>", 'c']], + \ ['dDd', "s/D/\\='\\\<C-V>\<C-M>'/", ["d\\\<C-V>", 'd']], + \ ['eEe', "s/E/\\='\\\\\<C-V>\<C-M>'/", ["e\\\\\<C-V>", 'e']], + \ ['fFf', "s/F/\\='\r'/", ['f', 'f']], + \ ['gGg', "s/G/\\='\<C-V>\<C-J>'/", ["g\<C-V>", 'g']], + \ ['hHh', "s/H/\\='\\\<C-V>\<C-J>'/", ["h\\\<C-V>", 'h']], + \ ['iIi', "s/I/\\='\\\\\<C-V>\<C-J>'/", ["i\\\\\<C-V>", 'i']], + \ ['jJj', "s/J/\\='\n'/", ['j', 'j']], + \ ['kKk', 's/K/\="\r"/', ['k', 'k']], + \ ['lLl', 's/L/\="\n"/', ['l', 'l']] + \ ] + call Run_SubCmd_Tests(tests) +endfunc + +" Test for submatch() on :substitute. +func Test_sub_cmd_4() + set magic& + set cpo& + + " List entry format: [input, cmd, output] + let tests = [ ['aAa', "s/A/\\=substitute(submatch(0), '.', '\\', '')/", + \ ['a\a']], + \ ['bBb', "s/B/\\=substitute(submatch(0), '.', '\\', '')/", + \ ['b\b']], + \ ['cCc', "s/C/\\=substitute(submatch(0), '.', '\<C-V>\<C-M>', '')/", + \ ["c\<C-V>", 'c']], + \ ['dDd', "s/D/\\=substitute(submatch(0), '.', '\\\<C-V>\<C-M>', '')/", + \ ["d\<C-V>", 'd']], + \ ['eEe', "s/E/\\=substitute(submatch(0), '.', '\\\\\<C-V>\<C-M>', '')/", + \ ["e\\\<C-V>", 'e']], + \ ['fFf', "s/F/\\=substitute(submatch(0), '.', '\\r', '')/", + \ ['f', 'f']], + \ ['gGg', 's/G/\=substitute(submatch(0), ".", "\<C-V>\<C-J>", "")/', + \ ["g\<C-V>", 'g']], + \ ['hHh', 's/H/\=substitute(submatch(0), ".", "\\\<C-V>\<C-J>", "")/', + \ ["h\<C-V>", 'h']], + \ ['iIi', 's/I/\=substitute(submatch(0), ".", "\\\\\<C-V>\<C-J>", "")/', + \ ["i\\\<C-V>", 'i']], + \ ['jJj', "s/J/\\=substitute(submatch(0), '.', '\\n', '')/", + \ ['j', 'j']], + \ ['kKk', "s/K/\\=substitute(submatch(0), '.', '\\r', '')/", + \ ['k', 'k']], + \ ['lLl', "s/L/\\=substitute(submatch(0), '.', '\\n', '')/", + \ ['l', 'l']], + \ ] + call Run_SubCmd_Tests(tests) +endfunc + +func Test_sub_cmd_5() + set magic& + set cpo& + + " List entry format: [input, cmd, output] + let tests = [ ['A123456789', 's/A\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)/\=submatch(0) . submatch(9) . submatch(8) . submatch(7) . submatch(6) . submatch(5) . submatch(4) . submatch(3) . submatch(2) . submatch(1)/', ['A123456789987654321']], + \ ['B123456789', 's/B\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)/\=string([submatch(0, 1), submatch(9, 1), submatch(8, 1), submatch(7, 1), submatch(6, 1), submatch(5, 1), submatch(4, 1), submatch(3, 1), submatch(2, 1), submatch(1, 1)])/', ["[['B123456789'], ['9'], ['8'], ['7'], ['6'], ['5'], ['4'], ['3'], ['2'], ['1']]"]], + \ ] + call Run_SubCmd_Tests(tests) +endfunc + +" Test for *:s%* on :substitute. +func Test_sub_cmd_6() + set magic& + " Nvim: no "/" flag in 'cpoptions'. + " set cpo+=/ + + " List entry format: [input, cmd, output] + let tests = [ ['A', 's/A/a/', ['a']], + \ ['B', 's/B/%/', ['a']], + \ ] + " call Run_SubCmd_Tests(tests) + + set cpo-=/ + let tests = [ ['C', 's/C/c/', ['c']], + \ ['D', 's/D/%/', ['%']], + \ ] + call Run_SubCmd_Tests(tests) + + set cpo& +endfunc + +" Test for :s replacing \n with line break. +func Test_sub_cmd_7() + set magic& + set cpo& + + " List entry format: [input, cmd, output] + let tests = [ ["A\<C-V>\<C-M>A", 's/A./\=submatch(0)/', ['A', 'A']], + \ ["B\<C-V>\<C-J>B", 's/B./\=submatch(0)/', ['B', 'B']], + \ ["C\<C-V>\<C-J>C", 's/C./\=strtrans(string(submatch(0, 1)))/', [strtrans("['C\<C-J>']C")]], + \ ["D\<C-V>\<C-J>\nD", 's/D.\nD/\=strtrans(string(submatch(0, 1)))/', [strtrans("['D\<C-J>', 'D']")]], + \ ["E\<C-V>\<C-J>\n\<C-V>\<C-J>\n\<C-V>\<C-J>\n\<C-V>\<C-J>\n\<C-V>\<C-J>E", 's/E\_.\{-}E/\=strtrans(string(submatch(0, 1)))/', [strtrans("['E\<C-J>', '\<C-J>', '\<C-J>', '\<C-J>', '\<C-J>E']")]], + \ ] + call Run_SubCmd_Tests(tests) + + exe "normal oQ\nQ\<Esc>k" + call assert_fails('s/Q[^\n]Q/\=submatch(0)."foobar"/', 'E486') + enew! +endfunc + +func TitleString() + let check = 'foo' =~ 'bar' + return "" +endfunc + +func Test_sub_cmd_8() + set titlestring=%{TitleString()} + + enew! + call append(0, ['', 'test_one', 'test_two']) + call cursor(1,1) + /^test_one/s/.*/\="foo\nbar"/ + call assert_equal('foo', getline(2)) + call assert_equal('bar', getline(3)) + call feedkeys(':/^test_two/s/.*/\="foo\nbar"/c', "t") + call feedkeys("\<CR>y", "xt") + call assert_equal('foo', getline(4)) + call assert_equal('bar', getline(5)) + + enew! + set titlestring& endfunc func Test_sub_cmd_9() diff --git a/test/old/testdir/test_tabline.vim b/test/old/testdir/test_tabline.vim index d9bef09067..d423f8a22c 100644 --- a/test/old/testdir/test_tabline.vim +++ b/test/old/testdir/test_tabline.vim @@ -133,7 +133,7 @@ func Test_tabline_empty_group() tabnew redraw! - tabclose + bw! set tabline= endfunc @@ -204,4 +204,28 @@ func Test_tabline_showcmd() call StopVimInTerminal(buf) endfunc +func TruncTabLine() + return '%1T口口%2Ta' .. repeat('b', &columns - 4) .. '%999X%#TabLine#c' +endfunc + +" Test 'tabline' with truncated double-width label at the start. +func Test_tabline_truncated_double_width() + tabnew + redraw + call assert_match('X$', Screenline(1)) + let attr_TabLineFill = screenattr(1, &columns - 1) + let attr_TabLine = screenattr(1, &columns) + call assert_notequal(attr_TabLine, attr_TabLineFill) + + set tabline=%!TruncTabLine() + redraw + call assert_equal('<a' .. repeat('b', &columns - 4) .. 'c', Screenline(1)) + call assert_equal(attr_TabLineFill, screenattr(1, &columns - 2)) + call assert_equal(attr_TabLine, screenattr(1, &columns - 1)) + call assert_equal(attr_TabLine, screenattr(1, &columns)) + + bw! + set tabline= +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/test/old/testdir/test_vimscript.vim b/test/old/testdir/test_vimscript.vim index c8085cc396..62487ae5d5 100644 --- a/test/old/testdir/test_vimscript.vim +++ b/test/old/testdir/test_vimscript.vim @@ -6997,7 +6997,7 @@ func Test_compound_assignment_operators() call assert_equal(6, &scrolljump) let &scrolljump %= 5 call assert_equal(1, &scrolljump) - call assert_fails('let &scrolljump .= "j"', 'E734:') + call assert_fails('let &scrolljump .= "j"', ['E734:', 'E734:']) set scrolljump&vim let &foldlevelstart = 2 diff --git a/test/old/testdir/vim9.vim b/test/old/testdir/vim9.vim index 7b9ec4bfc4..1b436d7b3c 100644 --- a/test/old/testdir/vim9.vim +++ b/test/old/testdir/vim9.vim @@ -2,6 +2,14 @@ " Use a different file name for each run. let s:sequence = 1 +func CheckDefFailure(lines, error, lnum = -3) + return +endfunc + +func CheckDefExecFailure(lines, error, lnum = -3) + return +endfunc + func CheckScriptFailure(lines, error, lnum = -3) if get(a:lines, 0, '') ==# 'vim9script' return diff --git a/test/unit/eval/typval_spec.lua b/test/unit/eval/typval_spec.lua index cde5a731cf..a60700287f 100644 --- a/test/unit/eval/typval_spec.lua +++ b/test/unit/eval/typval_spec.lua @@ -101,12 +101,13 @@ local function check_emsg(f, msg) saved_last_msg_hist = nil end local ret = {f()} + local last_msg = lib.last_msg_hist ~= nil and ffi.string(lib.last_msg_hist.msg) or nil if msg ~= nil then - eq(msg, ffi.string(lib.last_msg_hist.msg)) + eq(msg, last_msg) neq(saved_last_msg_hist, lib.last_msg_hist) else if saved_last_msg_hist ~= lib.last_msg_hist then - eq(nil, ffi.string(lib.last_msg_hist.msg)) + eq(nil, last_msg) else eq(saved_last_msg_hist, lib.last_msg_hist) end @@ -1429,15 +1430,16 @@ describe('typval.c', function() end describe('str()', function() itp('returns correct string', function() - local l = list(int(1), int(2), int(3), int(4), int(5)) + local l = list(int(1), 2.5, int(3), int(4), int(5)) alloc_log:clear() eq('1', tv_list_find_str(l, -5)) + eq('2.5', tv_list_find_str(l, 1)) eq('5', tv_list_find_str(l, 4)) eq('3', tv_list_find_str(l, 2)) eq('3', tv_list_find_str(l, -3)) - alloc_log:check({}) + alloc_log:check({a.freed(alloc_log.null)}) end) itp('returns string when used with VAR_STRING items', function() local l = list('1', '2', '3', '4', '5') @@ -1465,14 +1467,12 @@ describe('typval.c', function() eq(nil, tv_list_find_str(l, 5, 'E684: List index out of range: 5')) end) itp('fails with error message on invalid types', function() - local l = list(1, empty_list, {}) + local l = list(empty_list, {}) - eq('', tv_list_find_str(l, 0, 'E806: Using a Float as a String')) - eq('', tv_list_find_str(l, 1, 'E730: Using a List as a String')) - eq('', tv_list_find_str(l, 2, 'E731: Using a Dictionary as a String')) + eq('', tv_list_find_str(l, 0, 'E730: Using a List as a String')) + eq('', tv_list_find_str(l, 1, 'E731: Using a Dictionary as a String')) eq('', tv_list_find_str(l, -1, 'E731: Using a Dictionary as a String')) eq('', tv_list_find_str(l, -2, 'E730: Using a List as a String')) - eq('', tv_list_find_str(l, -3, 'E806: Using a Float as a String')) end) end) end) @@ -1765,18 +1765,24 @@ describe('typval.c', function() neq(s42, s43) eq(s43, dis.te.di_tv.vval.v_string) alloc_log:check({}) - eq('', ffi.string(check_emsg(function() return lib.tv_dict_get_string(d, 't', false) end, - 'E806: Using a Float as a String'))) + local s44 = check_emsg(function() return lib.tv_dict_get_string(d, 't', false) end, + nil) + eq('44.0', ffi.string(s44)) + alloc_log:check({a.freed(alloc_log.null)}) end) itp('allocates a string copy when requested', function() - local function tv_dict_get_string_alloc(d, key, emsg) + local function tv_dict_get_string_alloc(d, key, emsg, is_float) alloc_log:clear() local ret = check_emsg(function() return lib.tv_dict_get_string(d, key, true) end, emsg) local s_ret = (ret ~= nil) and ffi.string(ret) or nil if not emsg then if s_ret then - alloc_log:check({a.str(ret, s_ret)}) + if is_float then + alloc_log:check({a.freed(alloc_log.null), a.str(ret, s_ret)}) + else + alloc_log:check({a.str(ret, s_ret)}) + end else alloc_log:check({}) end @@ -1792,18 +1798,22 @@ describe('typval.c', function() eq('42', tv_dict_get_string_alloc(d, 'tes')) eq('45', tv_dict_get_string_alloc(d, 'xx')) eq('43', tv_dict_get_string_alloc(d, 'te')) - eq('', tv_dict_get_string_alloc(d, 't', 'E806: Using a Float as a String')) + eq('44.0', tv_dict_get_string_alloc(d, 't', nil, true)) end) end) describe('get_string_buf()', function() - local function tv_dict_get_string_buf(d, key, buf, emsg) + local function tv_dict_get_string_buf(d, key, is_float, buf, emsg) buf = buf or ffi.gc(lib.xmalloc(lib.NUMBUFLEN), lib.xfree) alloc_log:clear() local ret = check_emsg(function() return lib.tv_dict_get_string_buf(d, key, buf) end, emsg) local s_ret = (ret ~= nil) and ffi.string(ret) or nil if not emsg then - alloc_log:check({}) + if is_float then + alloc_log:check({a.freed(alloc_log.null)}) + else + alloc_log:check({}) + end end return s_ret, ret, buf end @@ -1827,16 +1837,16 @@ describe('typval.c', function() s, r, b = tv_dict_get_string_buf(d, 'test') neq(r, b) eq('tset', s) - s, r, b = tv_dict_get_string_buf(d, 't', nil, 'E806: Using a Float as a String') - neq(r, b) - eq('', s) + s, r, b = tv_dict_get_string_buf(d, 't', true) + eq(r, b) + eq('1.0', s) s, r, b = tv_dict_get_string_buf(d, 'te') eq(r, b) eq('2', s) end) end) describe('get_string_buf_chk()', function() - local function tv_dict_get_string_buf_chk(d, key, len, buf, def, emsg) + local function tv_dict_get_string_buf_chk(d, key, is_float, len, buf, def, emsg) buf = buf or ffi.gc(lib.xmalloc(lib.NUMBUFLEN), lib.xfree) def = def or ffi.gc(lib.xstrdup('DEFAULT'), lib.xfree) len = len or #key @@ -1845,7 +1855,11 @@ describe('typval.c', function() emsg) local s_ret = (ret ~= nil) and ffi.string(ret) or nil if not emsg then - alloc_log:check({}) + if is_float then + alloc_log:check({a.freed(alloc_log.null)}) + else + alloc_log:check({}) + end end return s_ret, ret, buf, def end @@ -1870,10 +1884,10 @@ describe('typval.c', function() neq(r, b) neq(r, def) eq('tset', s) - s, r, b, def = tv_dict_get_string_buf_chk(d, 'test', 1, nil, nil, 'E806: Using a Float as a String') - neq(r, b) + s, r, b, def = tv_dict_get_string_buf_chk(d, 'test', true, 1) + eq(r, b) neq(r, def) - eq(nil, s) + eq('1.0', s) s, r, b, def = tv_dict_get_string_buf_chk(d, 'te') eq(r, b) neq(r, def) @@ -2831,7 +2845,7 @@ describe('typval.c', function() alloc_log:clear() for _, v in ipairs({ {lib.VAR_NUMBER, nil}, - {lib.VAR_FLOAT, 'E806: Using a Float as a String'}, + {lib.VAR_FLOAT, nil}, {lib.VAR_PARTIAL, 'E729: Using a Funcref as a String'}, {lib.VAR_FUNC, 'E729: Using a Funcref as a String'}, {lib.VAR_LIST, 'E730: Using a List as a String'}, @@ -2978,15 +2992,47 @@ describe('typval.c', function() end end) end) + + local function test_string_fn(input, fn) + for _, v in ipairs(input) do + -- Using to_cstr in place of Neovim allocated string, cannot + -- tv_clear() that. + local tv = ffi.gc(typvalt(v[1], v[2]), nil) + alloc_log:check({}) + local emsg = v[3] + local ret = v[4] + eq(ret, check_emsg(function() + local res, buf = fn(tv) + if tv.v_type == lib.VAR_NUMBER or tv.v_type == lib.VAR_FLOAT + or tv.v_type == lib.VAR_SPECIAL or tv.v_type == lib.VAR_BOOL then + eq(buf, res) + else + neq(buf, res) + end + if res ~= nil then + return ffi.string(res) + else + return nil + end + end, emsg)) + if emsg then + alloc_log:clear() + elseif tv.v_type == lib.VAR_FLOAT then + alloc_log:check({a.freed(alloc_log.null)}) + else + alloc_log:check({}) + end + end + end describe('string()', function() itp('works', function() local buf = lib.tv_get_string(lua2typvalt(int(1))) local buf_chk = lib.tv_get_string_chk(lua2typvalt(int(1))) neq(buf, buf_chk) - for _, v in ipairs({ + test_string_fn({ {lib.VAR_NUMBER, {v_number=42}, nil, '42'}, {lib.VAR_STRING, {v_string=to_cstr('100500')}, nil, '100500'}, - {lib.VAR_FLOAT, {v_float=42.53}, 'E806: Using a Float as a String', ''}, + {lib.VAR_FLOAT, {v_float=42.53}, nil, '42.53'}, {lib.VAR_PARTIAL, {v_partial=NULL}, 'E729: Using a Funcref as a String', ''}, {lib.VAR_FUNC, {v_string=NULL}, 'E729: Using a Funcref as a String', ''}, {lib.VAR_LIST, {v_list=NULL}, 'E730: Using a List as a String', ''}, @@ -2995,42 +3041,18 @@ describe('typval.c', function() {lib.VAR_BOOL, {v_bool=lib.kBoolVarTrue}, nil, 'v:true'}, {lib.VAR_BOOL, {v_bool=lib.kBoolVarFalse}, nil, 'v:false'}, {lib.VAR_UNKNOWN, nil, 'E908: Using an invalid value as a String', ''}, - }) do - -- Using to_cstr in place of Neovim allocated string, cannot - -- tv_clear() that. - local tv = ffi.gc(typvalt(v[1], v[2]), nil) - alloc_log:check({}) - local emsg = v[3] - local ret = v[4] - eq(ret, check_emsg(function() - local res = lib.tv_get_string(tv) - if tv.v_type == lib.VAR_NUMBER or tv.v_type == lib.VAR_SPECIAL - or tv.v_type == lib.VAR_BOOL then - eq(buf, res) - else - neq(buf, res) - end - if res ~= nil then - return ffi.string(res) - else - return nil - end - end, emsg)) - if emsg then - alloc_log:clear() - else - alloc_log:check({}) - end - end + }, function(tv) + return lib.tv_get_string(tv), buf + end) end) end) describe('string_chk()', function() itp('works', function() local buf = lib.tv_get_string_chk(lua2typvalt(int(1))) - for _, v in ipairs({ + test_string_fn({ {lib.VAR_NUMBER, {v_number=42}, nil, '42'}, {lib.VAR_STRING, {v_string=to_cstr('100500')}, nil, '100500'}, - {lib.VAR_FLOAT, {v_float=42.53}, 'E806: Using a Float as a String', nil}, + {lib.VAR_FLOAT, {v_float=42.53}, nil, '42.53'}, {lib.VAR_PARTIAL, {v_partial=NULL}, 'E729: Using a Funcref as a String', nil}, {lib.VAR_FUNC, {v_string=NULL}, 'E729: Using a Funcref as a String', nil}, {lib.VAR_LIST, {v_list=NULL}, 'E730: Using a List as a String', nil}, @@ -3039,40 +3061,17 @@ describe('typval.c', function() {lib.VAR_BOOL, {v_bool=lib.kBoolVarTrue}, nil, 'v:true'}, {lib.VAR_BOOL, {v_bool=lib.kBoolVarFalse}, nil, 'v:false'}, {lib.VAR_UNKNOWN, nil, 'E908: Using an invalid value as a String', nil}, - }) do - -- Using to_cstr, cannot free with tv_clear - local tv = ffi.gc(typvalt(v[1], v[2]), nil) - alloc_log:check({}) - local emsg = v[3] - local ret = v[4] - eq(ret, check_emsg(function() - local res = lib.tv_get_string_chk(tv) - if tv.v_type == lib.VAR_NUMBER or tv.v_type == lib.VAR_SPECIAL - or tv.v_type == lib.VAR_BOOL then - eq(buf, res) - else - neq(buf, res) - end - if res ~= nil then - return ffi.string(res) - else - return nil - end - end, emsg)) - if emsg then - alloc_log:clear() - else - alloc_log:check({}) - end - end + }, function(tv) + return lib.tv_get_string_chk(tv), buf + end) end) end) describe('string_buf()', function() itp('works', function() - for _, v in ipairs({ + test_string_fn({ {lib.VAR_NUMBER, {v_number=42}, nil, '42'}, {lib.VAR_STRING, {v_string=to_cstr('100500')}, nil, '100500'}, - {lib.VAR_FLOAT, {v_float=42.53}, 'E806: Using a Float as a String', ''}, + {lib.VAR_FLOAT, {v_float=42.53}, nil, '42.53'}, {lib.VAR_PARTIAL, {v_partial=NULL}, 'E729: Using a Funcref as a String', ''}, {lib.VAR_FUNC, {v_string=NULL}, 'E729: Using a Funcref as a String', ''}, {lib.VAR_LIST, {v_list=NULL}, 'E730: Using a List as a String', ''}, @@ -3081,41 +3080,18 @@ describe('typval.c', function() {lib.VAR_BOOL, {v_bool=lib.kBoolVarTrue}, nil, 'v:true'}, {lib.VAR_BOOL, {v_bool=lib.kBoolVarFalse}, nil, 'v:false'}, {lib.VAR_UNKNOWN, nil, 'E908: Using an invalid value as a String', ''}, - }) do - -- Using to_cstr, cannot free with tv_clear - local tv = ffi.gc(typvalt(v[1], v[2]), nil) - alloc_log:check({}) - local emsg = v[3] - local ret = v[4] - eq(ret, check_emsg(function() - local buf = ffi.new('char[?]', lib.NUMBUFLEN, {0}) - local res = lib.tv_get_string_buf(tv, buf) - if tv.v_type == lib.VAR_NUMBER or tv.v_type == lib.VAR_SPECIAL - or tv.v_type == lib.VAR_BOOL then - eq(buf, res) - else - neq(buf, res) - end - if res ~= nil then - return ffi.string(res) - else - return nil - end - end, emsg)) - if emsg then - alloc_log:clear() - else - alloc_log:check({}) - end - end + }, function(tv) + local buf = ffi.new('char[?]', lib.NUMBUFLEN, {0}) + return lib.tv_get_string_buf(tv, buf), buf + end) end) end) describe('string_buf_chk()', function() itp('works', function() - for _, v in ipairs({ + test_string_fn({ {lib.VAR_NUMBER, {v_number=42}, nil, '42'}, {lib.VAR_STRING, {v_string=to_cstr('100500')}, nil, '100500'}, - {lib.VAR_FLOAT, {v_float=42.53}, 'E806: Using a Float as a String', nil}, + {lib.VAR_FLOAT, {v_float=42.53}, nil, '42.53'}, {lib.VAR_PARTIAL, {v_partial=NULL}, 'E729: Using a Funcref as a String', nil}, {lib.VAR_FUNC, {v_string=NULL}, 'E729: Using a Funcref as a String', nil}, {lib.VAR_LIST, {v_list=NULL}, 'E730: Using a List as a String', nil}, @@ -3124,33 +3100,10 @@ describe('typval.c', function() {lib.VAR_BOOL, {v_bool=lib.kBoolVarTrue}, nil, 'v:true'}, {lib.VAR_BOOL, {v_bool=lib.kBoolVarFalse}, nil, 'v:false'}, {lib.VAR_UNKNOWN, nil, 'E908: Using an invalid value as a String', nil}, - }) do - -- Using to_cstr, cannot free with tv_clear - local tv = ffi.gc(typvalt(v[1], v[2]), nil) - alloc_log:check({}) - local emsg = v[3] - local ret = v[4] - eq(ret, check_emsg(function() - local buf = ffi.new('char[?]', lib.NUMBUFLEN, {0}) - local res = lib.tv_get_string_buf_chk(tv, buf) - if tv.v_type == lib.VAR_NUMBER or tv.v_type == lib.VAR_SPECIAL - or tv.v_type == lib.VAR_BOOL then - eq(buf, res) - else - neq(buf, res) - end - if res ~= nil then - return ffi.string(res) - else - return nil - end - end, emsg)) - if emsg then - alloc_log:clear() - else - alloc_log:check({}) - end - end + }, function(tv) + local buf = ffi.new('char[?]', lib.NUMBUFLEN, {0}) + return lib.tv_get_string_buf_chk(tv, buf), buf + end) end) end) end) |