diff options
66 files changed, 2080 insertions, 1789 deletions
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 518a30158b..aad76d0979 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -102,7 +102,7 @@ jobs: libs=($(otool -L nvim-osx64/bin/nvim | sed 1d | sed -E -e 's|^[[:space:]]*||' -e 's| .*||')) echo "libs:" for lib in "${libs[@]}"; do - if echo "$lib" | grep -q -E 'libSystem|CoreFoundation' 2>/dev/null; then + if echo "$lib" | grep -q -E 'libSystem|CoreServices' 2>/dev/null; then echo " [skipped] $lib" else echo " $lib" diff --git a/CMakeLists.txt b/CMakeLists.txt index 2224ddd5c6..dad0093cf0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -618,29 +618,34 @@ find_program(SHELLCHECK_PRG shellcheck) include(DefCmdTarget) def_cmd_target(lintlua ${LUACHECK_PRG} LUACHECK_PRG true) if(LUACHECK_PRG) - add_custom_command(OUTPUT lintlua-cmd APPEND COMMAND ${LUACHECK_PRG} -q runtime/ scripts/ src/ test/) + add_custom_command(TARGET lintlua + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + COMMAND ${LUACHECK_PRG} -q runtime/ scripts/ src/ test/) endif() if(STYLUA_PRG) - add_custom_command(OUTPUT lintlua-cmd APPEND COMMAND ${STYLUA_PRG} --color=always --check runtime/) + add_custom_command(TARGET lintlua + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + COMMAND ${STYLUA_PRG} --color=always --check runtime/) else() - add_custom_command(OUTPUT lintlua-cmd APPEND COMMAND ${CMAKE_COMMAND} -E echo "STYLUA_PRG not found") + add_custom_command(TARGET lintlua COMMAND ${CMAKE_COMMAND} -E echo "STYLUA_PRG not found") endif() def_cmd_target(lintpy ${FLAKE8_PRG} FLAKE8_PRG false) if(FLAKE8_PRG) - add_custom_command(OUTPUT lintpy-cmd APPEND COMMAND ${FLAKE8_PRG} contrib/ scripts/ src/ test/) + add_custom_command(TARGET lintpy + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + COMMAND ${FLAKE8_PRG} contrib/ scripts/ src/ test/) endif() def_cmd_target(lintsh ${SHELLCHECK_PRG} SHELLCHECK_PRG false) if(SHELLCHECK_PRG) - add_custom_command(OUTPUT lintsh-cmd APPEND COMMAND ${SHELLCHECK_PRG} scripts/vim-patch.sh) + add_custom_command(TARGET lintsh + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + COMMAND ${SHELLCHECK_PRG} scripts/vim-patch.sh) endif() include(InstallHelpers) -file(GLOB MANPAGES - RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} - man/nvim.1) install_helper( - FILES ${MANPAGES} + FILES ${CMAKE_SOURCE_DIR}/man/nvim.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1) # @@ -676,17 +681,6 @@ if(BUSTED_PRG) set(FUNCTIONALTEST_PREREQS nvim printenv-test printargs-test shell-test streams-test tty-test ${GENERATED_HELP_TAGS}) set(BENCHMARK_PREREQS nvim tty-test) - # Useful for automated build systems, if they want to manually run the tests. - add_custom_target(unittest-prereqs - DEPENDS ${UNITTEST_PREREQS}) - set_target_properties(unittest-prereqs PROPERTIES FOLDER test) - - add_custom_target(functionaltest-prereqs - DEPENDS ${FUNCTIONALTEST_PREREQS}) - - add_custom_target(benchmark-prereqs - DEPENDS ${BENCHMARK_PREREQS}) - check_lua_module(${LUA_PRG} "ffi" LUA_HAS_FFI) if(LUA_HAS_FFI) add_custom_target(unittest @@ -731,8 +725,7 @@ if(BUSTED_PRG) -P ${PROJECT_SOURCE_DIR}/cmake/RunTests.cmake DEPENDS ${FUNCTIONALTEST_PREREQS} ${TEST_TARGET_ARGS}) - set_target_properties(functionaltest functionaltest-prereqs - PROPERTIES FOLDER test) + set_target_properties(functionaltest PROPERTIES FOLDER test) add_custom_target(benchmark COMMAND ${CMAKE_COMMAND} @@ -747,7 +740,7 @@ if(BUSTED_PRG) -P ${PROJECT_SOURCE_DIR}/cmake/RunTests.cmake DEPENDS ${BENCHMARK_PREREQS} ${TEST_TARGET_ARGS}) - set_target_properties(benchmark benchmark-prereqs PROPERTIES FOLDER test) + set_target_properties(benchmark PROPERTIES FOLDER test) endif() if(BUSTED_LUA_PRG) @@ -767,17 +760,8 @@ if(BUSTED_LUA_PRG) set_target_properties(functionaltest-lua PROPERTIES FOLDER test) endif() -#add uninstall target -if(NOT TARGET uninstall) - configure_file( - "cmake/UninstallHelper.cmake.in" - "${CMAKE_CURRENT_BINARY_DIR}/UninstallHelper.cmake" - IMMEDIATE @ONLY) - - add_custom_target(uninstall - COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/UninstallHelper.cmake) -endif() - +add_custom_target(uninstall + COMMAND ${CMAKE_COMMAND} -P ${PROJECT_SOURCE_DIR}/cmake/UninstallHelper.cmake) if(${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_CURRENT_SOURCE_DIR}) add_subdirectory(packaging) diff --git a/cmake/DefCmdTarget.cmake b/cmake/DefCmdTarget.cmake index 1ee5cdd60e..48e90cf5c8 100644 --- a/cmake/DefCmdTarget.cmake +++ b/cmake/DefCmdTarget.cmake @@ -1,27 +1,19 @@ -# Defines a target named ${target} and a command with (symbolic) output -# ${target}-cmd. If ${prg} is undefined the target prints "not found". +# Defines a target named ${target}. If ${prg} is undefined the target prints +# "not found". # -# - Use add_custom_command(…APPEND) to build the command after this. -# - Use add_custom_target(…DEPENDS) to run the command from a target. +# - Use add_custom_command(TARGET <target_name> ...) to append a command to the +# target. function(def_cmd_target target prg prg_name prg_fatal) - # Define a mostly-empty command, which can be appended-to. - add_custom_command(OUTPUT ${target}-cmd - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - COMMAND ${CMAKE_COMMAND} -E echo "${target}" - ) - # Symbolic (does not generate an artifact). - set_source_files_properties(${target}-cmd PROPERTIES SYMBOLIC "true") + add_custom_target(${target}) - if(prg OR NOT prg_fatal) - add_custom_target(${target} - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - DEPENDS ${target}-cmd) - if(NOT prg) - add_custom_command(OUTPUT ${target}-cmd APPEND + if(NOT prg) + if(prg_fatal) + add_custom_command(TARGET ${target} + COMMAND ${CMAKE_COMMAND} -E echo "${target}: ${prg_name} not found" + COMMAND false) + else() + add_custom_command(TARGET ${target} COMMAND ${CMAKE_COMMAND} -E echo "${target}: SKIP: ${prg_name} not found") endif() - else() - add_custom_target(${target} false - COMMENT "${target}: ${prg_name} not found") endif() endfunction() diff --git a/cmake/FindLua.cmake b/cmake/FindLua.cmake deleted file mode 100644 index 7ba13e1f56..0000000000 --- a/cmake/FindLua.cmake +++ /dev/null @@ -1,197 +0,0 @@ -# Distributed under the OSI-approved BSD 3-Clause License. See accompanying -# file Copyright.txt or https://cmake.org/licensing for details. - -#.rst: -# FindLua -# ------- -# -# -# -# Locate Lua library This module defines -# -# :: -# -# LUA_FOUND - if false, do not try to link to Lua -# LUA_LIBRARIES - both lua and lualib -# LUA_INCLUDE_DIR - where to find lua.h -# LUA_VERSION_STRING - the version of Lua found -# LUA_VERSION_MAJOR - the major version of Lua -# LUA_VERSION_MINOR - the minor version of Lua -# LUA_VERSION_PATCH - the patch version of Lua -# -# -# -# Note that the expected include convention is -# -# :: -# -# #include "lua.h" -# -# and not -# -# :: -# -# #include <lua/lua.h> -# -# This is because, the lua location is not standardized and may exist in -# locations other than lua/ - -unset(_lua_include_subdirs) -unset(_lua_library_names) -unset(_lua_append_versions) - -# this is a function only to have all the variables inside go away automatically -function(_lua_set_version_vars) - set(LUA_VERSIONS5 5.4 5.3 5.2 5.1 5.0) - - if (Lua_FIND_VERSION_EXACT) - if (Lua_FIND_VERSION_COUNT GREATER 1) - set(_lua_append_versions ${Lua_FIND_VERSION_MAJOR}.${Lua_FIND_VERSION_MINOR}) - endif () - elseif (Lua_FIND_VERSION) - # once there is a different major version supported this should become a loop - if (NOT Lua_FIND_VERSION_MAJOR GREATER 5) - if (Lua_FIND_VERSION_COUNT EQUAL 1) - set(_lua_append_versions ${LUA_VERSIONS5}) - else () - foreach (subver IN LISTS LUA_VERSIONS5) - if (NOT subver VERSION_LESS ${Lua_FIND_VERSION}) - list(APPEND _lua_append_versions ${subver}) - endif () - endforeach () - endif () - endif () - else () - # once there is a different major version supported this should become a loop - set(_lua_append_versions ${LUA_VERSIONS5}) - endif () - - list(APPEND _lua_include_subdirs "include/lua" "include") - - foreach (ver IN LISTS _lua_append_versions) - string(REGEX MATCH "^([0-9]+)\\.([0-9]+)$" _ver "${ver}") - list(APPEND _lua_include_subdirs - include/lua${CMAKE_MATCH_1}${CMAKE_MATCH_2} - include/lua${CMAKE_MATCH_1}.${CMAKE_MATCH_2} - include/lua-${CMAKE_MATCH_1}.${CMAKE_MATCH_2} - ) - endforeach () - - set(_lua_include_subdirs "${_lua_include_subdirs}" PARENT_SCOPE) - set(_lua_append_versions "${_lua_append_versions}" PARENT_SCOPE) -endfunction(_lua_set_version_vars) - -function(_lua_check_header_version _hdr_file) - # At least 5.[012] have different ways to express the version - # so all of them need to be tested. Lua 5.2 defines LUA_VERSION - # and LUA_RELEASE as joined by the C preprocessor, so avoid those. - file(STRINGS "${_hdr_file}" lua_version_strings - REGEX "^#define[ \t]+LUA_(RELEASE[ \t]+\"Lua [0-9]|VERSION([ \t]+\"Lua [0-9]|_[MR])).*") - - string(REGEX REPLACE ".*;#define[ \t]+LUA_VERSION_MAJOR[ \t]+\"([0-9])\"[ \t]*;.*" "\\1" LUA_VERSION_MAJOR ";${lua_version_strings};") - if (LUA_VERSION_MAJOR MATCHES "^[0-9]+$") - string(REGEX REPLACE ".*;#define[ \t]+LUA_VERSION_MINOR[ \t]+\"([0-9])\"[ \t]*;.*" "\\1" LUA_VERSION_MINOR ";${lua_version_strings};") - string(REGEX REPLACE ".*;#define[ \t]+LUA_VERSION_RELEASE[ \t]+\"([0-9])\"[ \t]*;.*" "\\1" LUA_VERSION_PATCH ";${lua_version_strings};") - set(LUA_VERSION_STRING "${LUA_VERSION_MAJOR}.${LUA_VERSION_MINOR}.${LUA_VERSION_PATCH}") - else () - string(REGEX REPLACE ".*;#define[ \t]+LUA_RELEASE[ \t]+\"Lua ([0-9.]+)\"[ \t]*;.*" "\\1" LUA_VERSION_STRING ";${lua_version_strings};") - if (NOT LUA_VERSION_STRING MATCHES "^[0-9.]+$") - string(REGEX REPLACE ".*;#define[ \t]+LUA_VERSION[ \t]+\"Lua ([0-9.]+)\"[ \t]*;.*" "\\1" LUA_VERSION_STRING ";${lua_version_strings};") - endif () - string(REGEX REPLACE "^([0-9]+)\\.[0-9.]*$" "\\1" LUA_VERSION_MAJOR "${LUA_VERSION_STRING}") - string(REGEX REPLACE "^[0-9]+\\.([0-9]+)[0-9.]*$" "\\1" LUA_VERSION_MINOR "${LUA_VERSION_STRING}") - string(REGEX REPLACE "^[0-9]+\\.[0-9]+\\.([0-9]).*" "\\1" LUA_VERSION_PATCH "${LUA_VERSION_STRING}") - endif () - foreach (ver IN LISTS _lua_append_versions) - if (ver STREQUAL "${LUA_VERSION_MAJOR}.${LUA_VERSION_MINOR}") - set(LUA_VERSION_MAJOR ${LUA_VERSION_MAJOR} PARENT_SCOPE) - set(LUA_VERSION_MINOR ${LUA_VERSION_MINOR} PARENT_SCOPE) - set(LUA_VERSION_PATCH ${LUA_VERSION_PATCH} PARENT_SCOPE) - set(LUA_VERSION_STRING ${LUA_VERSION_STRING} PARENT_SCOPE) - return() - endif () - endforeach () -endfunction(_lua_check_header_version) - -_lua_set_version_vars() - -if (LUA_INCLUDE_DIR AND EXISTS "${LUA_INCLUDE_DIR}/lua.h") - _lua_check_header_version("${LUA_INCLUDE_DIR}/lua.h") -endif () - -if (NOT LUA_VERSION_STRING) - foreach (subdir IN LISTS _lua_include_subdirs) - unset(LUA_INCLUDE_PREFIX CACHE) - find_path(LUA_INCLUDE_PREFIX ${subdir}/lua.h - HINTS - ENV LUA_DIR - PATHS - ~/Library/Frameworks - /Library/Frameworks - /sw # Fink - /opt/local # DarwinPorts - /opt/csw # Blastwave - /opt - ) - if (LUA_INCLUDE_PREFIX) - _lua_check_header_version("${LUA_INCLUDE_PREFIX}/${subdir}/lua.h") - if (LUA_VERSION_STRING) - set(LUA_INCLUDE_DIR "${LUA_INCLUDE_PREFIX}/${subdir}") - break() - endif () - endif () - endforeach () -endif () -unset(_lua_include_subdirs) -unset(_lua_append_versions) - -if (LUA_VERSION_STRING) - set(_lua_library_names - lua${LUA_VERSION_MAJOR}${LUA_VERSION_MINOR} - lua${LUA_VERSION_MAJOR}.${LUA_VERSION_MINOR} - lua-${LUA_VERSION_MAJOR}.${LUA_VERSION_MINOR} - lua.${LUA_VERSION_MAJOR}.${LUA_VERSION_MINOR} - ) -endif () - -find_library(LUA_LIBRARY - NAMES ${_lua_library_names} lua - HINTS - ENV LUA_DIR - PATH_SUFFIXES lib - PATHS - ~/Library/Frameworks - /Library/Frameworks - /sw - /opt/local - /opt/csw - /opt -) -unset(_lua_library_names) - -if (LUA_LIBRARY) - # include the math library for Unix - if (UNIX AND NOT APPLE AND NOT BEOS) - find_library(LUA_MATH_LIBRARY m) - set(LUA_LIBRARIES "${LUA_LIBRARY};${LUA_MATH_LIBRARY}") - - # include dl library for statically-linked Lua library - get_filename_component(LUA_LIB_EXT ${LUA_LIBRARY} EXT) - if(LUA_LIB_EXT STREQUAL CMAKE_STATIC_LIBRARY_SUFFIX) - list(APPEND LUA_LIBRARIES ${CMAKE_DL_LIBS}) - endif() - - # For Windows and Mac, don't need to explicitly include the math library - else () - set(LUA_LIBRARIES "${LUA_LIBRARY}") - endif () -endif () - -include(FindPackageHandleStandardArgs) -# handle the QUIETLY and REQUIRED arguments and set LUA_FOUND to TRUE if -# all listed variables are TRUE -FIND_PACKAGE_HANDLE_STANDARD_ARGS(Lua - REQUIRED_VARS LUA_LIBRARIES LUA_INCLUDE_DIR - VERSION_VAR LUA_VERSION_STRING) - -mark_as_advanced(LUA_INCLUDE_DIR LUA_LIBRARY LUA_MATH_LIBRARY) diff --git a/cmake/UninstallHelper.cmake b/cmake/UninstallHelper.cmake new file mode 100644 index 0000000000..9a3d30af59 --- /dev/null +++ b/cmake/UninstallHelper.cmake @@ -0,0 +1,13 @@ +if(NOT EXISTS "${CMAKE_BINARY_DIR}/install_manifest.txt") + message(FATAL_ERROR "Cannot find install manifest: ${CMAKE_BINARY_DIR}/install_manifest.txt") +endif() + +file(STRINGS "${CMAKE_BINARY_DIR}/install_manifest.txt" files) +foreach(file ${files}) + message(STATUS "Uninstalling $ENV{DESTDIR}${file}") + if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") + file(REMOVE $ENV{DESTDIR}${file}) + else() + message(STATUS "File $ENV{DESTDIR}${file} does not exist.") + endif() +endforeach() diff --git a/cmake/UninstallHelper.cmake.in b/cmake/UninstallHelper.cmake.in deleted file mode 100644 index c2d34d4796..0000000000 --- a/cmake/UninstallHelper.cmake.in +++ /dev/null @@ -1,21 +0,0 @@ -if(NOT EXISTS "@CMAKE_BINARY_DIR@/install_manifest.txt") - message(FATAL_ERROR "Cannot find install manifest: @CMAKE_BINARY_DIR@/install_manifest.txt") -endif() - -file(READ "@CMAKE_BINARY_DIR@/install_manifest.txt" files) -string(REGEX REPLACE "\n" ";" files "${files}") -foreach(file ${files}) - message(STATUS "Uninstalling $ENV{DESTDIR}${file}") - if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") - exec_program( - "@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\"" - OUTPUT_VARIABLE rm_out - RETURN_VALUE rm_retval - ) - if(NOT "${rm_retval}" STREQUAL 0) - message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}") - endif() - else(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") - message(STATUS "File $ENV{DESTDIR}${file} does not exist.") - endif() -endforeach() diff --git a/runtime/autoload/health/provider.vim b/runtime/autoload/health/provider.vim index a01cb9631c..5cda7cfd03 100644 --- a/runtime/autoload/health/provider.vim +++ b/runtime/autoload/health/provider.vim @@ -203,7 +203,7 @@ function! s:version_info(python) abort let nvim_path = s:trim(s:system([ \ a:python, '-c', \ 'import sys; ' . - \ 'sys.path = list(filter(lambda x: x != "", sys.path)); ' . + \ 'sys.path = [p for p in sys.path if p != ""]; ' . \ 'import neovim; print(neovim.__file__)'])) if s:shell_error || empty(nvim_path) return [python_version, 'unable to load neovim Python module', pypi_version, diff --git a/runtime/autoload/provider/pythonx.vim b/runtime/autoload/provider/pythonx.vim index 048f898e62..6211b457d6 100644 --- a/runtime/autoload/provider/pythonx.vim +++ b/runtime/autoload/provider/pythonx.vim @@ -8,7 +8,7 @@ let s:loaded_pythonx_provider = 1 function! provider#pythonx#Require(host) abort " Python host arguments let prog = provider#python3#Prog() - let args = [prog, '-c', 'import sys; sys.path = list(filter(lambda x: x != "", sys.path)); import neovim; neovim.start_host()'] + let args = [prog, '-c', 'import sys; sys.path = [p for p in sys.path if p != ""]; import neovim; neovim.start_host()'] " Collect registered Python plugins into args @@ -63,7 +63,7 @@ endfunction function! s:import_module(prog, module) abort let prog_version = system([a:prog, '-c' , printf( \ 'import sys; ' . - \ 'sys.path = list(filter(lambda x: x != "", sys.path)); ' . + \ 'sys.path = [p for p in sys.path if p != ""]; ' . \ 'sys.stdout.write(str(sys.version_info[0]) + "." + str(sys.version_info[1])); ' . \ 'import pkgutil; ' . \ 'exit(2*int(pkgutil.get_loader("%s") is None))', diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt index 331a4fe700..1d7a783bf1 100644 --- a/runtime/doc/api.txt +++ b/runtime/doc/api.txt @@ -2037,15 +2037,17 @@ nvim_get_option_value({name}, {*opts}) *nvim_get_option_value()* 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 buffer - or window. To get a buffer-local or window-local option for a - specific buffer or window, use |nvim_buf_get_option()| or - |nvim_win_get_option()|. + or window, unless "buf" or "win" is set in {opts}. Parameters: ~ {name} Option name {opts} Optional parameters - • scope: One of 'global' or 'local'. Analogous to + • scope: One of "global" or "local". Analogous to |:setglobal| and |:setlocal|, respectively. + • win: |window-ID|. Used for getting window local + options. + • buf: Buffer number. Used for getting buffer + local options. Implies {scope} is "local". Return: ~ Option value diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt index 54eac87070..ac42b315a4 100644 --- a/runtime/doc/builtin.txt +++ b/runtime/doc/builtin.txt @@ -2364,7 +2364,7 @@ fnamemodify({fname}, {mods}) *fnamemodify()* Example: > :echo fnamemodify("main.c", ":p:h") < results in: > - /home/mool/vim/vim/src + /home/user/vim/vim/src < If {mods} is empty or an unsupported modifier is used then {fname} is returned. Note: Environment variables don't work in {fname}, use diff --git a/runtime/doc/deprecated.txt b/runtime/doc/deprecated.txt index e328bd28b5..eb6d9b6dc9 100644 --- a/runtime/doc/deprecated.txt +++ b/runtime/doc/deprecated.txt @@ -24,12 +24,12 @@ Commands ~ *:wviminfo* Deprecated alias to |:wshada| command. Environment Variables ~ -*$NVIM_LISTEN_ADDRESS* $NVIM_LISTEN_ADDRESS is a deprecated way to set the - |--listen| address of Nvim, and also had a conflicting - purpose as a way to detect a parent Nvim (use |$NVIM| - for that). It is unset by |terminal| and |jobstart()| - (unless explicitly given by the "env" option). - Ignored if --listen is given. +*$NVIM_LISTEN_ADDRESS* Deprecated way to + * set the server name (use |--listen| instead) + * get the server name (use |v:servername| instead) + * detect a parent Nvim (use |$NVIM| instead) + Unset by |terminal| and |jobstart()| (unless explicitly + given by the "env" option). Ignored if --listen is given. Events ~ *BufCreate* Use |BufAdd| instead. diff --git a/runtime/doc/map.txt b/runtime/doc/map.txt index 2206d13053..05cf30e078 100644 --- a/runtime/doc/map.txt +++ b/runtime/doc/map.txt @@ -345,15 +345,7 @@ Note: mapping is recursive. - 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. -- In Select mode, |:map| and |:vmap| command mappings are executed in - Visual mode. Use |:smap| to handle Select mode differently. One particular - edge case: > - :vnoremap <C-K> <Esc> -< This ends Visual mode when in Visual mode, but in Select mode it does not - work, because Select mode is restored after executing the mapped keys. You - need to use: > - :snoremap <C-K> <Esc> -< + *E5520* <Cmd> commands must terminate, that is, they must be followed by <CR> in the {rhs} of the mapping definition. |Command-line| mode is never entered. diff --git a/runtime/doc/visual.txt b/runtime/doc/visual.txt index 193c70e70a..905ae49887 100644 --- a/runtime/doc/visual.txt +++ b/runtime/doc/visual.txt @@ -499,6 +499,13 @@ work both in Visual mode and in Select mode. When these are used in Select mode Vim automatically switches to Visual mode, so that the same behavior as in Visual mode is effective. If you don't want this use |:xmap| or |:smap|. +One particular edge case: > + :vnoremap <C-K> <Esc> +This ends Visual mode when in Visual mode, but in Select mode it does not +work, because Select mode is restored after executing the mapped keys. You +need to use: > + :snoremap <C-K> <Esc> +< Users will expect printable characters to replace the selected area. Therefore avoid mapping printable characters in Select mode. Or use |:sunmap| after |:map| and |:vmap| to remove it for Select mode. diff --git a/runtime/indent/confini.vim b/runtime/indent/confini.vim deleted file mode 100644 index 50b3dd20c0..0000000000 --- a/runtime/indent/confini.vim +++ /dev/null @@ -1,10 +0,0 @@ -" Vim indent file -" Language: confini - -" Quit if an indent file was already loaded. -if exists("b:did_indent") - finish -endif - -" Use the cfg indenting, it's similar enough. -runtime! indent/cfg.vim diff --git a/runtime/indent/systemd.vim b/runtime/indent/systemd.vim deleted file mode 100644 index a05a87bb1c..0000000000 --- a/runtime/indent/systemd.vim +++ /dev/null @@ -1,10 +0,0 @@ -" Vim indent file -" Language: systemd.unit(5) - -" Only load this indent file when no other was loaded. -if exists("b:did_indent") - finish -endif - -" Looks a lot like dosini files. -runtime! indent/dosini.vim diff --git a/runtime/indent/yaml.vim b/runtime/indent/yaml.vim index 1f798416ed..d732c37c05 100644 --- a/runtime/indent/yaml.vim +++ b/runtime/indent/yaml.vim @@ -2,7 +2,7 @@ " Language: YAML " Maintainer: Nikolai Pavlov <zyx.vim@gmail.com> " Last Update: Lukas Reineke -" Last Change: 2022 May 02 +" Last Change: 2022 Jun 17 " Only load this indent file when no other was loaded. if exists('b:did_indent') @@ -44,30 +44,30 @@ function s:FindPrevLEIndentedLineMatchingRegex(lnum, regex) return plilnum endfunction -let s:mapkeyregex='\v^\s*\#@!\S@=%(\''%([^'']|\''\'')*\'''. - \ '|\"%([^"\\]|\\.)*\"'. +let s:mapkeyregex = '\v^\s*\#@!\S@=%(\''%([^'']|\''\'')*\''' .. + \ '|\"%([^"\\]|\\.)*\"' .. \ '|%(%(\:\ )@!.)*)\:%(\ |$)' -let s:liststartregex='\v^\s*%(\-%(\ |$))' +let s:liststartregex = '\v^\s*%(\-%(\ |$))' let s:c_ns_anchor_char = '\v%([\n\r\uFEFF \t,[\]{}]@!\p)' -let s:c_ns_anchor_name = s:c_ns_anchor_char.'+' -let s:c_ns_anchor_property = '\v\&'.s:c_ns_anchor_name +let s:c_ns_anchor_name = s:c_ns_anchor_char .. '+' +let s:c_ns_anchor_property = '\v\&' .. s:c_ns_anchor_name let s:ns_word_char = '\v[[:alnum:]_\-]' -let s:ns_tag_char = '\v%(%\x\x|'.s:ns_word_char.'|[#/;?:@&=+$.~*''()])' -let s:c_named_tag_handle = '\v\!'.s:ns_word_char.'+\!' +let s:ns_tag_char = '\v%(%\x\x|' .. s:ns_word_char .. '|[#/;?:@&=+$.~*''()])' +let s:c_named_tag_handle = '\v\!' .. s:ns_word_char .. '+\!' let s:c_secondary_tag_handle = '\v\!\!' let s:c_primary_tag_handle = '\v\!' -let s:c_tag_handle = '\v%('.s:c_named_tag_handle. - \ '|'.s:c_secondary_tag_handle. - \ '|'.s:c_primary_tag_handle.')' -let s:c_ns_shorthand_tag = '\v'.s:c_tag_handle . s:ns_tag_char.'+' +let s:c_tag_handle = '\v%(' .. s:c_named_tag_handle. + \ '|' .. s:c_secondary_tag_handle. + \ '|' .. s:c_primary_tag_handle .. ')' +let s:c_ns_shorthand_tag = '\v' .. s:c_tag_handle .. s:ns_tag_char .. '+' let s:c_non_specific_tag = '\v\!' -let s:ns_uri_char = '\v%(%\x\x|'.s:ns_word_char.'\v|[#/;?:@&=+$,.!~*''()[\]])' -let s:c_verbatim_tag = '\v\!\<'.s:ns_uri_char.'+\>' -let s:c_ns_tag_property = '\v'.s:c_verbatim_tag. - \ '\v|'.s:c_ns_shorthand_tag. - \ '\v|'.s:c_non_specific_tag +let s:ns_uri_char = '\v%(%\x\x|' .. s:ns_word_char .. '\v|[#/;?:@&=+$,.!~*''()[\]])' +let s:c_verbatim_tag = '\v\!\<' .. s:ns_uri_char.. '+\>' +let s:c_ns_tag_property = '\v' .. s:c_verbatim_tag. + \ '\v|' .. s:c_ns_shorthand_tag. + \ '\v|' .. s:c_non_specific_tag let s:block_scalar_header = '\v[|>]%([+-]?[1-9]|[1-9]?[+-])?' @@ -142,9 +142,9 @@ function GetYAMLIndent(lnum) " - List with " multiline scalar return previndent+2 - elseif prevline =~# s:mapkeyregex . '\v\s*%(%('.s:c_ns_tag_property. - \ '\v|'.s:c_ns_anchor_property. - \ '\v|'.s:block_scalar_header. + elseif prevline =~# s:mapkeyregex .. '\v\s*%(%(' .. s:c_ns_tag_property .. + \ '\v|' .. s:c_ns_anchor_property .. + \ '\v|' .. s:block_scalar_header .. \ '\v)%(\s+|\s*%(\#.*)?$))*' " Mapping with: value " that is multiline scalar diff --git a/runtime/lua/vim/_meta.lua b/runtime/lua/vim/_meta.lua index 59cf837a1d..715a7e5561 100644 --- a/runtime/lua/vim/_meta.lua +++ b/runtime/lua/vim/_meta.lua @@ -91,11 +91,11 @@ do -- buffer option accessor return new_buf_opt_accessor(k) end - return a.nvim_buf_get_option(bufnr or 0, k) + return a.nvim_get_option_value(k, { buf = bufnr or 0 }) end local function set(k, v) - return a.nvim_buf_set_option(bufnr or 0, k, v) + return a.nvim_set_option_value(k, v, { buf = bufnr or 0 }) end return make_meta_accessor(get, set, nil, function(k) @@ -121,11 +121,11 @@ do -- window option accessor if winnr == nil and type(k) == 'number' then return new_win_opt_accessor(k) end - return a.nvim_win_get_option(winnr or 0, k) + return a.nvim_get_option_value(k, { win = winnr or 0 }) end local function set(k, v) - return a.nvim_win_set_option(winnr or 0, k, v) + return a.nvim_set_option_value(k, v, { win = winnr or 0 }) end return make_meta_accessor(get, set, nil, function(k) diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index 536580c604..320d6a2a5b 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -24,13 +24,6 @@ local function starsetf(ft, opts) end ---@private -local function getline(bufnr, start_lnum, end_lnum) - end_lnum = end_lnum or start_lnum - local lines = api.nvim_buf_get_lines(bufnr, start_lnum - 1, end_lnum, false) - return table.concat(lines) or '' -end - ----@private --- Get a single line or line-range from the buffer. --- ---@param bufnr number|nil The buffer to get the lines from @@ -127,6 +120,15 @@ local extension = { end return 'aspvbs' end, + asm = function(path, bufnr) + return require('vim.filetype.detect').asm(bufnr) + end, + lst = function(path, bufnr) + return require('vim.filetype.detect').asm(bufnr) + end, + mac = function(path, bufnr) + return require('vim.filetype.detect').asm(bufnr) + end, ['asn1'] = 'asn', asn = 'asn', asp = function(path, bufnr) @@ -142,14 +144,32 @@ local extension = { ref = 'b', imp = 'b', mch = 'b', + bas = function(path, bufnr) + return require('vim.filetype.detect').bas(bufnr) + end, + bi = function(path, bufnr) + return require('vim.filetype.detect').bas(bufnr) + end, + bm = function(path, bufnr) + return require('vim.filetype.detect').bas(bufnr) + end, bc = 'bc', bdf = 'bdf', beancount = 'beancount', bib = 'bib', + com = function(path, bufnr) + return require('vim.filetype.detect').bindzone(bufnr, 'dcl') + end, + db = function(path, bufnr) + return require('vim.filetype.detect').bindzone(bufnr, '') + end, bicep = 'bicep', bl = 'blank', bsdl = 'bsdl', bst = 'bst', + btm = function(path, bufnr) + return (vim.g.dosbatch_syntax_for_btm and vim.g.dosbatch_syntax_for_btm ~= 0) and 'dosbatch' or 'btm' + end, bzl = 'bzl', bazel = 'bzl', BUILD = 'bzl', @@ -163,6 +183,9 @@ local extension = { hgrc = 'cfg', chf = 'ch', chai = 'chaiscript', + ch = function(path, bufnr) + return require('vim.filetype.detect').change(bufnr) + end, chs = 'chaskell', chopro = 'chordpro', crd = 'chordpro', @@ -184,15 +207,22 @@ local extension = { atg = 'coco', recipe = 'conaryrecipe', hook = function(path, bufnr) - if getline(bufnr, 1) == '[Trigger]' then - return 'conf' - end + return M.getlines(bufnr, 1) == '[Trigger]' and 'conf' end, mklx = 'context', mkiv = 'context', mkii = 'context', mkxl = 'context', mkvi = 'context', + control = function(path, bufnr) + return require('vim.filetype.detect').control(bufnr) + end, + copyright = function(path, bufnr) + return require('vim.filetype.detect').copyright(bufnr) + end, + csh = function(path, bufnr) + return require('vim.filetype.detect').csh(path, bufnr) + end, moc = 'cpp', hh = 'cpp', tlh = 'cpp', @@ -206,16 +236,10 @@ local extension = { hxx = 'cpp', hpp = 'cpp', cpp = function(path, bufnr) - if vim.g.cynlib_syntax_for_cpp then - return 'cynlib' - end - return 'cpp' + return vim.g.cynlib_syntax_for_cpp and 'cynlib' or 'cpp' end, cc = function(path, bufnr) - if vim.g.cynlib_syntax_for_cc then - return 'cynlib' - end - return 'cpp' + return vim.g.cynlib_syntax_for_cc and 'cynlib' or 'cpp' end, crm = 'crm', csx = 'cs', @@ -237,6 +261,15 @@ local extension = { drt = 'dart', ds = 'datascript', dcd = 'dcd', + decl = function(path, bufnr) + return require('vim.filetype.detect').decl(bufnr) + end, + dec = function(path, bufnr) + return require('vim.filetype.detect').decl(bufnr) + end, + dcl = function(path, bufnr) + return require('vim.filetype.detect').decl(bufnr) or 'clean' + end, def = 'def', desc = 'desc', directory = 'desktop', @@ -253,17 +286,40 @@ local extension = { drac = 'dracula', drc = 'dracula', dtd = 'dtd', + d = function(path, bufnr) + return require('vim.filetype.detect').dtrace(bufnr) + end, dts = 'dts', dtsi = 'dts', dylan = 'dylan', intr = 'dylanintr', lid = 'dylanlid', + e = function(path, bufnr) + return require('vim.filetype.detect').e(bufnr) + end, + E = function(path, bufnr) + return require('vim.filetype.detect').e(bufnr) + end, ecd = 'ecd', + edf = 'edif', + edfi = 'edif', + edo = 'edif', + edn = function(path, bufnr) + return require('vim.filetype.detect').edn(bufnr) + end, eex = 'eelixir', leex = 'eelixir', + am = function(path, bufnr) + if not path:lower():find('makefile%.am$') then + return 'elf' + end + end, exs = 'elixir', elm = 'elm', elv = 'elvish', + ent = function(path, bufnr) + return require('vim.filetype.detect').ent(bufnr) + end, epp = 'epuppet', erl = 'erlang', hrl = 'erlang', @@ -273,6 +329,36 @@ local extension = { ec = 'esqlc', EC = 'esqlc', strl = 'esterel', + eu = function(path, bufnr) + return vim.g.filetype_euphoria or 'euphoria3' + end, + EU = function(path, bufnr) + return vim.g.filetype_euphoria or 'euphoria3' + end, + ew = function(path, bufnr) + return vim.g.filetype_euphoria or 'euphoria3' + end, + EW = function(path, bufnr) + return vim.g.filetype_euphoria or 'euphoria3' + end, + EX = function(path, bufnr) + return vim.g.filetype_euphoria or 'euphoria3' + end, + exu = function(path, bufnr) + return vim.g.filetype_euphoria or 'euphoria3' + end, + EXU = function(path, bufnr) + return vim.g.filetype_euphoria or 'euphoria3' + end, + exw = function(path, bufnr) + return vim.g.filetype_euphoria or 'euphoria3' + end, + EXW = function(path, bufnr) + return vim.g.filetype_euphoria or 'euphoria3' + end, + ex = function(path, bufnr) + return require('vim.filetype.detect').ex(bufnr) + end, exp = 'expect', factor = 'factor', fal = 'falcon', @@ -308,7 +394,13 @@ local extension = { ['f08'] = 'fortran', fpc = 'fpcmake', fsl = 'framescript', + frm = function(path, bufnr) + return require('vim.filetype.detect').frm(bufnr) + end, fb = 'freebasic', + fs = function(path, bufnr) + return require('vim.filetype.detect').fs(bufnr) + end, fsi = 'fsharp', fsx = 'fsharp', fusion = 'fusion', @@ -350,6 +442,9 @@ local extension = { ht = 'haste', htpp = 'hastepreproc', hb = 'hb', + h = function(path, bufnr) + return require('vim.filetype.detect').header(bufnr) + end, sum = 'hercules', errsum = 'hercules', ev = 'hercules', @@ -362,14 +457,56 @@ local extension = { hog = 'hog', hws = 'hollywood', hoon = 'hoon', + cpt = function(path, bufnr) + return require('vim.filetype.detect').html(bufnr) + end, + dtml = function(path, bufnr) + return require('vim.filetype.detect').html(bufnr) + end, + htm = function(path, bufnr) + return require('vim.filetype.detect').html(bufnr) + end, + html = function(path, bufnr) + return require('vim.filetype.detect').html(bufnr) + end, + pt = function(path, bufnr) + return require('vim.filetype.detect').html(bufnr) + end, + shtml = function(path, bufnr) + return require('vim.filetype.detect').html(bufnr) + end, + stm = function(path, bufnr) + return require('vim.filetype.detect').html(bufnr) + end, htt = 'httest', htb = 'httest', + hw = function(path, bufnr) + return require('vim.filetype.detect').hw(bufnr) + end, + module = function(path, bufnr) + return require('vim.filetype.detect').hw(bufnr) + end, + pkg = function(path, bufnr) + return require('vim.filetype.detect').hw(bufnr) + end, iba = 'ibasic', ibi = 'ibasic', icn = 'icon', + idl = function(path, bufnr) + return require('vim.filetype.detect').idl(bufnr) + end, + inc = function(path, bufnr) + return require('vim.filetype.detect').inc(bufnr) + end, inf = 'inform', INF = 'inform', ii = 'initng', + inp = function(path, bufnr) + return require('vim.filetype.detect').inp(bufnr) + end, + ms = function(path, bufnr) + return require('vim.filetype.detect').nroff(bufnr) or 'xmath' + end, iss = 'iss', mst = 'ist', ist = 'ist', @@ -445,14 +582,26 @@ local extension = { lou = 'lout', ulpc = 'lpc', lpc = 'lpc', + c = function(path, bufnr) + return require('vim.filetype.detect').lpc(bufnr) + end, sig = 'lprolog', lsl = 'lsl', lss = 'lss', nse = 'lua', rockspec = 'lua', lua = 'lua', - quake = 'm3quake', + m = function(path, bufnr) + return require('vim.filetype.detect').m(bufnr) + end, at = 'm4', + mc = function(path, bufnr) + return require('vim.filetype.detect').mc(bufnr) + end, + quake = 'm3quake', + ['m4'] = function(path, bufnr) + return require('vim.filetype.detect').m4(path) + end, eml = 'mail', mk = 'make', mak = 'make', @@ -487,8 +636,14 @@ local extension = { mib = 'mib', mix = 'mix', mixal = 'mix', + mm = function(path, bufnr) + return require('vim.filetype.detect').mm(bufnr) + end, nb = 'mma', mmp = 'mmp', + mms = function(path, bufnr) + return require('vim.filetype.detect').mms(bufnr) + end, DEF = 'modula2', ['m2'] = 'modula2', mi = 'modula2', @@ -559,6 +714,9 @@ local extension = { pike = 'pike', pmod = 'pike', rcp = 'pilrc', + PL = function(path, bufnr) + return require('vim.filetype.detect').pl(bufnr) + end, pli = 'pli', ['pl1'] = 'pli', ['p36'] = 'plm', @@ -604,6 +762,9 @@ local extension = { ptl = 'python', ql = 'ql', qll = 'ql', + R = function(path, bufnr) + return require('vim.filetype.detect').r(bufnr) + end, rad = 'radiance', mat = 'radiance', ['pod6'] = 'raku', @@ -685,6 +846,24 @@ local extension = { sdl = 'sdl', sed = 'sed', sexp = 'sexplib', + bash = function(path, bufnr) + return require('vim.filetype.detect').sh(path, bufnr, 'bash') + end, + ebuild = function(path, bufnr) + return require('vim.filetype.detect').sh(path, bufnr, 'bash') + end, + eclass = function(path, bufnr) + return require('vim.filetype.detect').sh(path, bufnr, 'bash') + end, + env = function(path, bufnr) + return require('vim.filetype.detect').sh(path, bufnr) + end, + ksh = function(path, bufnr) + return require('vim.filetype.detect').sh(path, bufnr, 'ksh') + end, + sh = function(path, bufnr) + return require('vim.filetype.detect').sh(path, bufnr) + end, sieve = 'sieve', siv = 'sieve', sil = 'sil', @@ -767,14 +946,7 @@ local extension = { latex = 'tex', sty = 'tex', cls = function(path, bufnr) - local line = getline(bufnr, 1) - if line:find('^%%') then - return 'tex' - elseif line:find('^#') and line:lower():find('rexx') then - return 'rexx' - else - return 'st' - end + return require('vim.filetype.detect').cls(bufnr) end, texi = 'texinfo', txi = 'texinfo', @@ -793,11 +965,7 @@ local extension = { tutor = 'tutor', twig = 'twig', ts = function(path, bufnr) - if getline(bufnr, 1):find('<%?xml') then - return 'xml' - else - return 'typescript' - end + return M.getlines(bufnr, 1):find('<%?xml') and 'xml' or 'typescript' end, tsx = 'typescriptreact', uc = 'uc', @@ -855,10 +1023,7 @@ local extension = { wpl = 'xml', xmi = 'xml', xpm = function(path, bufnr) - if getline(bufnr, 1):find('XPM2') then - return 'xpm2' - end - return 'xpm' + return M.getlines(bufnr, 1):find('XPM2') and 'xpm2' or 'xpm' end, ['xpm2'] = 'xpm2', xqy = 'xquery', @@ -882,172 +1047,29 @@ local extension = { zut = 'zimbutempl', zsh = 'zsh', vala = 'vala', - E = function(path, bufnr) - return require('vim.filetype.detect').e(bufnr) - end, - EU = function(path, bufnr) - return require('vim.filetype.detect').euphoria(bufnr) - end, - EW = function(path, bufnr) - return require('vim.filetype.detect').euphoria(bufnr) - end, - EX = function(path, bufnr) - return require('vim.filetype.detect').euphoria(bufnr) - end, - EXU = function(path, bufnr) - return require('vim.filetype.detect').euphoria(bufnr) - end, - EXW = function(path, bufnr) - return require('vim.filetype.detect').euphoria(bufnr) + web = function(path, bufnr) + return require('vim.filetype.detect').web(bufnr) end, - PL = function(path, bufnr) + pl = function(path, bufnr) return require('vim.filetype.detect').pl(bufnr) end, - R = function(path, bufnr) - return require('vim.filetype.detect').r(bufnr) - end, - asm = function(path, bufnr) - return require('vim.filetype.detect').asm(bufnr) - end, - bas = function(path, bufnr) - return require('vim.filetype.detect').bas(bufnr) - end, - bi = function(path, bufnr) - return require('vim.filetype.detect').bas(bufnr) - end, - bm = function(path, bufnr) - return require('vim.filetype.detect').bas(bufnr) - end, - bash = function(path, bufnr) - return require('vim.filetype.detect').sh(path, bufnr, 'bash') - end, - btm = function(path, bufnr) - return require('vim.filetype.detect').btm(bufnr) - end, - c = function(path, bufnr) - return require('vim.filetype.detect').lpc(bufnr) - end, - ch = function(path, bufnr) - return require('vim.filetype.detect').change(bufnr) - end, - com = function(path, bufnr) - return require('vim.filetype.detect').bindzone(bufnr, 'dcl') - end, - cpt = function(path, bufnr) - return require('vim.filetype.detect').html(bufnr) - end, - csh = function(path, bufnr) - return require('vim.filetype.detect').csh(path, bufnr) - end, - d = function(path, bufnr) - return require('vim.filetype.detect').dtrace(bufnr) - end, - db = function(path, bufnr) - return require('vim.filetype.detect').bindzone(bufnr, '') - end, - dtml = function(path, bufnr) - return require('vim.filetype.detect').html(bufnr) - end, - e = function(path, bufnr) - return require('vim.filetype.detect').e(bufnr) - end, - ebuild = function(path, bufnr) - return require('vim.filetype.detect').sh(path, bufnr, 'bash') - end, - eclass = function(path, bufnr) - return require('vim.filetype.detect').sh(path, bufnr, 'bash') - end, - ent = function(path, bufnr) - return require('vim.filetype.detect').ent(bufnr) - end, - env = function(path, bufnr) - return require('vim.filetype.detect').sh(path, bufnr, getline(bufnr, 1)) - end, - eu = function(path, bufnr) - return require('vim.filetype.detect').euphoria(bufnr) - end, - ew = function(path, bufnr) - return require('vim.filetype.detect').euphoria(bufnr) - end, - ex = function(path, bufnr) - return require('vim.filetype.detect').ex(bufnr) - end, - exu = function(path, bufnr) - return require('vim.filetype.detect').euphoria(bufnr) - end, - exw = function(path, bufnr) - return require('vim.filetype.detect').euphoria(bufnr) - end, - frm = function(path, bufnr) - return require('vim.filetype.detect').frm(bufnr) - end, - fs = function(path, bufnr) - return require('vim.filetype.detect').fs(bufnr) - end, - h = function(path, bufnr) - return require('vim.filetype.detect').header(bufnr) - end, - htm = function(path, bufnr) - return require('vim.filetype.detect').html(bufnr) - end, - html = function(path, bufnr) - return require('vim.filetype.detect').html(bufnr) + pp = function(path, bufnr) + return require('vim.filetype.detect').pp(bufnr) end, i = function(path, bufnr) return require('vim.filetype.detect').progress_asm(bufnr) end, - idl = function(path, bufnr) - return require('vim.filetype.detect').idl(bufnr) - end, - inc = function(path, bufnr) - return require('vim.filetype.detect').inc(bufnr) - end, - inp = function(path, bufnr) - return require('vim.filetype.detect').inp(bufnr) - end, - ksh = function(path, bufnr) - return require('vim.filetype.detect').sh(path, bufnr, 'ksh') - end, - lst = function(path, bufnr) - return require('vim.filetype.detect').asm(bufnr) - end, - m = function(path, bufnr) - return require('vim.filetype.detect').m(bufnr) - end, - mac = function(path, bufnr) - return require('vim.filetype.detect').asm(bufnr) - end, - mc = function(path, bufnr) - return require('vim.filetype.detect').mc(bufnr) - end, - mm = function(path, bufnr) - return require('vim.filetype.detect').mm(bufnr) - end, - mms = function(path, bufnr) - return require('vim.filetype.detect').mms(bufnr) + w = function(path, bufnr) + return require('vim.filetype.detect').progress_cweb(bufnr) end, p = function(path, bufnr) return require('vim.filetype.detect').progress_pascal(bufnr) end, - patch = function(path, bufnr) - local firstline = getline(bufnr, 1) - if string.find(firstline, '^From ' .. string.rep('%x', 40) .. '+ Mon Sep 17 00:00:00 2001$') then - return 'gitsendemail' - else - return 'diff' - end - end, - pl = function(path, bufnr) - return require('vim.filetype.detect').pl(bufnr) - end, - pp = function(path, bufnr) - return require('vim.filetype.detect').pp(bufnr) - end, pro = function(path, bufnr) return require('vim.filetype.detect').proto(bufnr, 'idlang') end, - pt = function(path, bufnr) - return require('vim.filetype.detect').html('idlang') + patch = function(path, bufnr) + return require('vim.filetype.detect').patch(bufnr) end, r = function(path, bufnr) return require('vim.filetype.detect').r(bufnr) @@ -1056,7 +1078,7 @@ local extension = { return require('vim.filetype.detect').redif(bufnr) end, rules = function(path, bufnr) - return require('vim.filetype.detect').rules(path, bufnr) + return require('vim.filetype.detect').rules(path) end, sc = function(path, bufnr) return require('vim.filetype.detect').sc(bufnr) @@ -1064,20 +1086,14 @@ local extension = { scd = function(path, bufnr) return require('vim.filetype.detect').scd(bufnr) end, - sh = function(path, bufnr) - return require('vim.filetype.detect').sh(path, bufnr, getline(bufnr, 1)) - end, - shtml = function(path, bufnr) - return require('vim.filetype.detect').html(bufnr) + tcsh = function(path, bufnr) + return require('vim.filetype.detect').shell(path, bufnr, 'tcsh') end, sql = function(path, bufnr) - return require('vim.filetype.detect').sql(bufnr) - end, - stm = function(path, bufnr) - return require('vim.filetype.detect').html(bufnr) + return vim.g.filetype_sql and vim.g.filetype_sql or 'sql' end, - tcsh = function(path, bufnr) - return require('vim.filetype.detect').shell(path, bufnr, 'tcsh') + zsql = function(path, bufnr) + return vim.g.filetype_sql and vim.g.filetype_sql or 'sql' end, tex = function(path, bufnr) return require('vim.filetype.detect').tex(path, bufnr) @@ -1085,8 +1101,8 @@ local extension = { tf = function(path, bufnr) return require('vim.filetype.detect').tf(bufnr) end, - w = function(path, bufnr) - return require('vim.filetype.detect').progress_cweb(bufnr) + txt = function(path, bufnr) + return require('vim.filetype.detect').txt(bufnr) end, xml = function(path, bufnr) return require('vim.filetype.detect').xml(bufnr) @@ -1094,137 +1110,51 @@ local extension = { y = function(path, bufnr) return require('vim.filetype.detect').y(bufnr) end, - zsql = function(path, bufnr) - return require('vim.filetype.detect').sql(bufnr) - end, - txt = function(path, bufnr) - --helpfiles match *.txt, but should have a modeline as last line - if not getline(bufnr, -1):find('vim:.*ft=help') then - return 'text' - end - end, cmd = function(path, bufnr) - if getline(bufnr, 1):find('^/%*') then - return 'rexx' - end - return 'dosbatch' + return M.getlines(bufnr, 1):find('^/%*') and 'rexx' or 'dosbatch' end, rul = function(path, bufnr) return require('vim.filetype.detect').rul(bufnr) end, cpy = function(path, bufnr) - if getline(bufnr, 1):find('^##') then - return 'python' - end - return 'cobol' + return M.getlines(bufnr, 1):find('^##') and 'python' or 'cobol' end, dsl = function(path, bufnr) - if getline(bufnr, 1):find('^%s*<!') then - return 'dsl' - end - return 'structurizr' - end, - edf = 'edif', - edfi = 'edif', - edo = 'edif', - edn = function(path, bufnr) - return require('vim.filetype.detect').edn(bufnr) + return M.getlines(bufnr, 1):find('^%s*<!') and 'dsl' or 'structurizr' end, smil = function(path, bufnr) - if getline(bufnr, 1):find('<%?%s*xml.*%?>') then - return 'xml' - end - return 'smil' + return M.getlines(bufnr, 1):find('<%?%s*xml.*%?>') and 'xml' or 'smil' end, smi = function(path, bufnr) return require('vim.filetype.detect').smi(bufnr) end, install = function(path, bufnr) - if getline(bufnr, 1):lower():find('<%?php') then - return 'php' - end - return require('vim.filetype.detect').sh(path, bufnr, 'bash') + return require('vim.filetype.detect').install(path, bufnr) end, pm = function(path, bufnr) - local line = getline(bufnr, 1) - if line:find('XPM2') then - return 'xpm2' - elseif line:find('XPM') then - return 'xpm' - else - return 'perl' - end + return require('vim.filetype.detect').pm(bufnr) end, me = function(path, bufnr) - local filename = vim.fn.fnamemodify(path, ':t'):lower() - if filename ~= 'read.me' and filename ~= 'click.me' then - return 'nroff' - end + return require('vim.filetype.detect').me(path) end, reg = function(path, bufnr) - local line = getline(bufnr, 1):lower() - if line:find('^regedit[0-9]*%s*$') or line:find('^windows registry editor version %d*%.%d*%s*$') then - return 'registry' - end - end, - decl = function(path, bufnr) - return require('vim.filetype.detect').decl(bufnr) - end, - dec = function(path, bufnr) - return require('vim.filetype.detect').decl(bufnr) - end, - dcl = function(path, bufnr) - local decl = require('vim.filetype.detect').decl(bufnr) - if decl then - return decl - end - return 'clean' - end, - web = function(path, bufnr) - return require('vim.filetype.detect').web(bufnr) + return require('vim.filetype.detect').reg(bufnr) end, ttl = function(path, bufnr) - local line = getline(bufnr, 1):lower() - if line:find('^@?prefix') or line:find('^@?base') then - return 'turtle' - end - return 'teraterm' - end, - am = function(path, bufnr) - if not path:lower():find('makefile%.am$') then - return 'elf' - end + return require('vim.filetype.detect').ttl(bufnr) end, - ['m4'] = function(path, bufnr) - local path_lower = path:lower() - if not path_lower:find('html%.m4$') and not path_lower:find('fvwm2rc') then - return 'm4' - end - end, - hw = function(path, bufnr) - return require('vim.filetype.detect').hw(bufnr) - end, - module = function(path, bufnr) - return require('vim.filetype.detect').hw(bufnr) - end, - pkg = function(path, bufnr) - return require('vim.filetype.detect').hw(bufnr) - end, - ms = function(path, bufnr) - if not require('vim.filetype.detect').nroff(bufnr) then - return 'xmath' + rc = function(path, bufnr) + if not path:find('/etc/Muttrc%.d/') then + return 'rc' end end, - t = function(path, bufnr) - if not require('vim.filetype.detect').nroff(bufnr) and not require('vim.filetype.detect').perl(path, bufnr) then - return 'tads' + rch = function(path, bufnr) + if not path:find('/etc/Muttrc%.d/') then + return 'rc' end end, class = function(path, bufnr) - -- Check if not a Java class (starts with '\xca\xfe\xba\xbe') - if not getline(bufnr, 1):find('^\202\254\186\190') then - return 'stata' - end + require('vim.filetype.detect').class(bufnr) end, sgml = function(path, bufnr) return require('vim.filetype.detect').sgml(bufnr) @@ -1232,27 +1162,20 @@ local extension = { sgm = function(path, bufnr) return require('vim.filetype.detect').sgml(bufnr) end, - rc = function(path, bufnr) - if not path:find('/etc/Muttrc%.d/') then - return 'rc' - end - end, - rch = function(path, bufnr) - if not path:find('/etc/Muttrc%.d/') then - return 'rc' + t = function(path, bufnr) + if not require('vim.filetype.detect').nroff(bufnr) and not require('vim.filetype.detect').perl(path, bufnr) then + return 'tads' end end, - control = function(path, bufnr) - return require('vim.filetype.detect').control(bufnr) - end, - copyright = function(path, bufnr) - return require('vim.filetype.detect').copyright(bufnr) - end, -- Ignored extensions bak = function(path, bufnr) local root = vim.fn.fnamemodify(path, ':r') return M.match(root, bufnr) end, + ['dpkg-bak'] = function(path, bufnr) + local root = vim.fn.fnamemodify(path, ':r') + return M.match(root, bufnr) + end, ['dpkg-dist'] = function(path, bufnr) local root = vim.fn.fnamemodify(path, ':r') return M.match(root, bufnr) @@ -1265,9 +1188,11 @@ local extension = { local root = vim.fn.fnamemodify(path, ':r') return M.match(root, bufnr) end, - ['dpkg-bak'] = function(path, bufnr) - local root = vim.fn.fnamemodify(path, ':r') - return M.match(root, bufnr) + ['in'] = function(path, bufnr) + if vim.fs.basename(path) ~= 'configure.in' then + local root = vim.fn.fnamemodify(path, ':r') + return M.match(root, bufnr) + end end, new = function(path, bufnr) local root = vim.fn.fnamemodify(path, ':r') @@ -1297,12 +1222,6 @@ local extension = { local root = vim.fn.fnamemodify(path, ':r') return M.match(root, bufnr) end, - ['in'] = function(path, bufnr) - if vim.fs.basename(path) ~= 'configure.in' then - local root = vim.fn.fnamemodify(path, ':r') - return M.match(root, bufnr) - end - end, -- END EXTENSION } @@ -1322,6 +1241,13 @@ local filename = { ['.arch-inventory'] = 'arch', ['GNUmakefile.am'] = 'automake', ['named.root'] = 'bindzone', + ['.*/bind/db%..*'] = starsetf('bindzone'), + ['.*/named/db%..*'] = starsetf('bindzone'), + ['cabal%.project%..*'] = starsetf('cabalproject'), + ['sgml%.catalog.*'] = starsetf('catalog'), + ['/etc/hostname%..*'] = starsetf('config'), + ['.*/etc/cron%.d/.*'] = starsetf('crontab'), + ['crontab%..*'] = starsetf('crontab'), WORKSPACE = 'bzl', BUILD = 'bzl', ['cabal.project'] = 'cabalproject', @@ -1335,9 +1261,28 @@ local filename = { ['/etc/defaults/cdrdao'] = 'cdrdaoconf', ['cfengine.conf'] = 'cfengine', ['CMakeLists.txt'] = 'cmake', + ['.alias'] = function(path, bufnr) + return require('vim.filetype.detect').csh(path, bufnr) + end, + ['.cshrc'] = function(path, bufnr) + return require('vim.filetype.detect').csh(path, bufnr) + end, + ['.login'] = function(path, bufnr) + return require('vim.filetype.detect').csh(path, bufnr) + end, + ['csh.cshrc'] = function(path, bufnr) + return require('vim.filetype.detect').csh(path, bufnr) + end, + ['csh.login'] = function(path, bufnr) + return require('vim.filetype.detect').csh(path, bufnr) + end, + ['csh.logout'] = function(path, bufnr) + return require('vim.filetype.detect').csh(path, bufnr) + end, ['auto.master'] = 'conf', ['configure.in'] = 'config', ['configure.ac'] = 'config', + crontab = 'crontab', ['.cvsrc'] = 'cvsrc', ['/debian/changelog'] = 'debchangelog', ['changelog.dch'] = 'debchangelog', @@ -1348,6 +1293,9 @@ local filename = { ['/debian/copyright'] = 'debcopyright', ['/etc/apt/sources.list'] = 'debsources', ['denyhosts.conf'] = 'denyhosts', + ['.*/debian/patches/.*'] = function(path, bufnr) + return require('vim.filetype.detect').dep3patch(path, bufnr) + end, ['dict.conf'] = 'dictconf', ['.dictrc'] = 'dictconf', ['/etc/DIR_COLORS'] = 'dircolors', @@ -1524,6 +1472,9 @@ local filename = { end, ['.procmailrc'] = 'procmail', ['.procmail'] = 'procmail', + ['indent.pro'] = function(path, bufnr) + return require('vim.filetype.detect').proto(bufnr, 'indent') + end, ['/etc/protocols'] = 'protocols', ['INDEX'] = function(path, bufnr) return require('vim.filetype.detect').psf(bufnr) @@ -1551,9 +1502,46 @@ local filename = { ['.screenrc'] = 'screen', ['/etc/sensors3.conf'] = 'sensors', ['/etc/sensors.conf'] = 'sensors', + ['.*/etc/sensors%.d/[^.].*'] = starsetf('sensors'), ['/etc/services'] = 'services', ['/etc/serial.conf'] = 'setserial', ['/etc/udev/cdsymlinks.conf'] = 'sh', + ['bash.bashrc'] = function(path, bufnr) + return require('vim.filetype.detect').sh(path, bufnr, 'bash') + end, + bashrc = function(path, bufnr) + return require('vim.filetype.detect').sh(path, bufnr, 'bash') + end, + ['.bashrc'] = function(path, bufnr) + return require('vim.filetype.detect').sh(path, bufnr, 'bash') + end, + ['.env'] = function(path, bufnr) + return require('vim.filetype.detect').sh(path, bufnr) + end, + ['.kshrc'] = function(path, bufnr) + return require('vim.filetype.detect').sh(path, bufnr, 'ksh') + end, + ['.profile'] = function(path, bufnr) + return require('vim.filetype.detect').sh(path, bufnr) + end, + ['/etc/profile'] = function(path, bufnr) + return require('vim.filetype.detect').sh(path, bufnr) + end, + APKBUILD = function(path, bufnr) + return require('vim.filetype.detect').sh(path, bufnr, 'bash') + end, + PKGBUILD = function(path, bufnr) + return require('vim.filetype.detect').sh(path, bufnr, 'bash') + end, + ['.tcshrc'] = function(path, bufnr) + return require('vim.filetype.detect').shell(path, bufnr, 'tcsh') + end, + ['tcsh.login'] = function(path, bufnr) + return require('vim.filetype.detect').shell(path, bufnr, 'tcsh') + end, + ['tcsh.tcshrc'] = function(path, bufnr) + return require('vim.filetype.detect').shell(path, bufnr, 'tcsh') + end, ['/etc/slp.conf'] = 'slpconf', ['/etc/slp.reg'] = 'slpreg', ['/etc/slp.spi'] = 'slpspi', @@ -1615,6 +1603,9 @@ local filename = { vim.b[b].xf86conf_xfree86_version = 4 end end, + ['XF86Config'] = function(path, bufnr) + return require('vim.filetype.detect').xfree86() + end, ['/etc/xinetd.conf'] = 'xinetd', fglrxrc = 'xml', ['/etc/blkid.tab'] = 'xml', @@ -1627,67 +1618,6 @@ local filename = { ['.zcompdump'] = 'zsh', ['.zshenv'] = 'zsh', ['.zfbfmarks'] = 'zsh', - ['.alias'] = function(path, bufnr) - return require('vim.filetype.detect').csh(path, bufnr) - end, - ['.bashrc'] = function(path, bufnr) - return require('vim.filetype.detect').sh(path, bufnr, 'bash') - end, - ['.cshrc'] = function(path, bufnr) - return require('vim.filetype.detect').csh(path, bufnr) - end, - ['.env'] = function(path, bufnr) - return require('vim.filetype.detect').sh(path, bufnr, getline(bufnr, 1)) - end, - ['.kshrc'] = function(path, bufnr) - return require('vim.filetype.detect').sh(path, bufnr, 'ksh') - end, - ['.login'] = function(path, bufnr) - return require('vim.filetype.detect').csh(path, bufnr) - end, - ['.profile'] = function(path, bufnr) - return require('vim.filetype.detect').sh(path, bufnr, getline(bufnr, 1)) - end, - ['.tcshrc'] = function(path, bufnr) - return require('vim.filetype.detect').shell(path, bufnr, 'tcsh') - end, - ['/etc/profile'] = function(path, bufnr) - return require('vim.filetype.detect').sh(path, bufnr, getline(bufnr, 1)) - end, - APKBUILD = function(path, bufnr) - return require('vim.filetype.detect').sh(path, bufnr, 'bash') - end, - PKGBUILD = function(path, bufnr) - return require('vim.filetype.detect').sh(path, bufnr, 'bash') - end, - ['bash.bashrc'] = function(path, bufnr) - return require('vim.filetype.detect').sh(path, bufnr, 'bash') - end, - bashrc = function(path, bufnr) - return require('vim.filetype.detect').sh(path, bufnr, 'bash') - end, - crontab = 'crontab', - ['csh.cshrc'] = function(path, bufnr) - return require('vim.filetype.detect').csh(path, bufnr) - end, - ['csh.login'] = function(path, bufnr) - return require('vim.filetype.detect').csh(path, bufnr) - end, - ['csh.logout'] = function(path, bufnr) - return require('vim.filetype.detect').csh(path, bufnr) - end, - ['indent.pro'] = function(path, bufnr) - return require('vim.filetype.detect').proto(bufnr, 'indent') - end, - ['tcsh.login'] = function(path, bufnr) - return require('vim.filetype.detect').shell(path, bufnr, 'tcsh') - end, - ['tcsh.tcshrc'] = function(path, bufnr) - return require('vim.filetype.detect').shell(path, bufnr, 'tcsh') - end, - ['XF86Config'] = function(path, bufnr) - return require('vim.filetype.detect').xfree86(bufnr) - end, -- END FILENAME } @@ -1699,7 +1629,31 @@ local pattern = { ['.*/etc/asound%.conf'] = 'alsaconf', ['.*/etc/apache2/sites%-.*/.*%.com'] = 'apache', ['.*/etc/httpd/.*%.conf'] = 'apache', + ['.*/etc/apache2/.*%.conf.*'] = starsetf('apache'), + ['.*/etc/apache2/conf%..*/.*'] = starsetf('apache'), + ['.*/etc/apache2/mods%-.*/.*'] = starsetf('apache'), + ['.*/etc/apache2/sites%-.*/.*'] = starsetf('apache'), + ['access%.conf.*'] = starsetf('apache'), + ['apache%.conf.*'] = starsetf('apache'), + ['apache2%.conf.*'] = starsetf('apache'), + ['httpd%.conf.*'] = starsetf('apache'), + ['srm%.conf.*'] = starsetf('apache'), + ['.*/etc/httpd/conf%..*/.*'] = starsetf('apache'), + ['.*/etc/httpd/conf%.d/.*%.conf.*'] = starsetf('apache'), + ['.*/etc/httpd/mods%-.*/.*'] = starsetf('apache'), + ['.*/etc/httpd/sites%-.*/.*'] = starsetf('apache'), + ['.*/etc/proftpd/.*%.conf.*'] = starsetf('apachestyle'), + ['.*/etc/proftpd/conf%..*/.*'] = starsetf('apachestyle'), + ['proftpd%.conf.*'] = starsetf('apachestyle'), + ['.*asterisk/.*%.conf.*'] = starsetf('asterisk'), + ['.*asterisk.*/.*voicemail%.conf.*'] = starsetf('asteriskvm'), ['.*/%.aptitude/config'] = 'aptconf', + ['.*%.[aA]'] = function(path, bufnr) + return require('vim.filetype.detect').asm(bufnr) + end, + ['.*%.[sS]'] = function(path, bufnr) + return require('vim.filetype.detect').asm(bufnr) + end, ['[mM]akefile%.am'] = 'automake', ['.*bsd'] = 'bsdl', ['bzr_log%..*'] = 'bzr', @@ -1711,6 +1665,14 @@ local pattern = { ['.*/etc/cdrdao%.conf'] = 'cdrdaoconf', ['.*/etc/default/cdrdao'] = 'cdrdaoconf', ['.*hgrc'] = 'cfg', + ['.*%.[Cc][Ff][Gg]'] = { + function(path, bufnr) + return require('vim.filetype.detect').cfg(bufnr) + end, + -- Decrease the priority to avoid conflicts with more specific patterns + -- such as '.*/etc/a2ps/.*%.cfg', '.*enlightenment/.*%.cfg', etc. + { priority = -1 }, + }, ['.*%.%.ch'] = 'chill', ['.*%.cmake%.in'] = 'cmake', -- */cmus/rc and */.cmus/rc @@ -1719,7 +1681,24 @@ local pattern = { ['.*/%.?cmus/.*%.theme'] = 'cmusrc', ['.*/%.cmus/autosave'] = 'cmusrc', ['.*/%.cmus/command%-history'] = 'cmusrc', + ['%.cshrc.*'] = function(path, bufnr) + return require('vim.filetype.detect').csh(path, bufnr) + end, + ['%.login.*'] = function(path, bufnr) + return require('vim.filetype.detect').csh(path, bufnr) + end, ['cvs%d+'] = 'cvs', + ['.*%.[Dd][Aa][Tt]'] = function(path, bufnr) + return require('vim.filetype.detect').dat(path, bufnr) + end, + ['[cC]hange[lL]og.*'] = starsetf(function(path, bufnr) + require('vim.filetype.detect').changelog(bufnr) + end), + ['.*/etc/dnsmasq%.d/.*'] = starsetf('dnsmasq'), + ['Containerfile%..*'] = starsetf('dockerfile'), + ['Dockerfile%..*'] = starsetf('dockerfile'), + ['.*/etc/yum%.repos%.d/.*'] = starsetf('dosini'), + ['drac%..*'] = starsetf('dracula'), ['.*/debian/changelog'] = 'debchangelog', ['.*/debian/control'] = 'debcontrol', ['.*/debian/copyright'] = 'debcopyright', @@ -1736,6 +1715,52 @@ local pattern = { ['.*/dtrace/.*%.d'] = 'dtrace', ['.*esmtprc'] = 'esmtprc', ['.*Eterm/.*%.cfg'] = 'eterm', + ['[a-zA-Z0-9].*Dict'] = function(path, bufnr) + return require('vim.filetype.detect').foam(bufnr) + end, + ['[a-zA-Z0-9].*Dict%..*'] = function(path, bufnr) + return require('vim.filetype.detect').foam(bufnr) + end, + ['[a-zA-Z].*Properties'] = function(path, bufnr) + return require('vim.filetype.detect').foam(bufnr) + end, + ['[a-zA-Z].*Properties%..*'] = function(path, bufnr) + return require('vim.filetype.detect').foam(bufnr) + end, + ['.*Transport%..*'] = function(path, bufnr) + return require('vim.filetype.detect').foam(bufnr) + end, + ['.*/constant/g'] = function(path, bufnr) + return require('vim.filetype.detect').foam(bufnr) + end, + ['.*/0/.*'] = function(path, bufnr) + return require('vim.filetype.detect').foam(bufnr) + end, + ['.*/0%.orig/.*'] = function(path, bufnr) + return require('vim.filetype.detect').foam(bufnr) + end, + ['.*/%.fvwm/.*'] = starsetf('fvwm'), + ['.*fvwmrc.*'] = starsetf(function(path, bufnr) + return 'fvwm', function(b) + vim.b[b].fvwm_version = 1 + end + end), + ['.*fvwm95.*%.hook'] = starsetf(function(path, bufnr) + return 'fvwm', function(b) + vim.b[b].fvwm_version = 1 + end + end), + ['.*fvwm2rc.*'] = starsetf(function(path, bufnr) + return require('vim.filetype.detect').fvwm(path) + end), + ['.*/tmp/lltmp.*'] = starsetf('gedcom'), + ['/etc/gitconfig%.d/.*'] = starsetf('gitconfig'), + ['.*/gitolite%-admin/conf/.*'] = starsetf('gitolite'), + ['tmac%..*'] = starsetf('nroff'), + ['.*/%.gitconfig%.d/.*'] = starsetf('gitconfig'), + ['.*%.git/.*'] = function(path, bufnr) + require('vim.filetype.detect').git(bufnr) + end, ['.*%.git/modules/.*/config'] = 'gitconfig', ['.*%.git/config'] = 'gitconfig', ['.*/etc/gitconfig'] = 'gitconfig', @@ -1766,13 +1791,15 @@ local pattern = { ['.*/etc/gshadow'] = 'group', ['.*/etc/group%.edit'] = 'group', ['.*/var/backups/gshadow%.bak'] = 'group', - ['.*/etc/group-'] = 'group', - ['.*/etc/gshadow-'] = 'group', + ['.*/etc/group%-'] = 'group', + ['.*/etc/gshadow%-'] = 'group', ['.*/var/backups/group%.bak'] = 'group', ['.*/etc/gshadow%.edit'] = 'group', ['.*/boot/grub/grub%.conf'] = 'grub', ['.*/boot/grub/menu%.lst'] = 'grub', ['.*/etc/grub%.conf'] = 'grub', + -- gtkrc* and .gtkrc* + ['%.?gtkrc.*'] = starsetf('gtkrc'), [vim.env.VIMRUNTIME .. '/doc/.*%.txt'] = 'help', ['hg%-editor%-.*%.txt'] = 'hgcommit', ['.*/etc/host%.conf'] = 'hostconf', @@ -1785,8 +1812,15 @@ local pattern = { ['.*/%.sway/config'] = 'i3config', ['.*/%.icewm/menu'] = 'icemenu', ['.*/etc/initng/.*/.*%.i'] = 'initng', + ['JAM.*%..*'] = starsetf('jam'), + ['Prl.*%..*'] = starsetf('jam'), ['.*%.properties_..'] = 'jproperties', ['.*%.properties_.._..'] = 'jproperties', + ['.*%.properties_.._.._.*'] = starsetf('jproperties'), + ['Kconfig%..*'] = starsetf('kconfig'), + ['.*%.[Ss][Uu][Bb]'] = 'krl', + ['lilo%.conf.*'] = starsetf('lilo'), + ['.*/etc/logcheck/.*%.d.*/.*'] = starsetf('logcheck'), ['.*lftp/rc'] = 'lftp', ['.*/%.libao'] = 'libao', ['.*/etc/libao%.conf'] = 'libao', @@ -1796,52 +1830,99 @@ local pattern = { ['.*/LiteStep/.*/.*%.rc'] = 'litestep', ['.*/etc/login%.access'] = 'loginaccess', ['.*/etc/login%.defs'] = 'logindefs', + ['%.letter%.%d+'] = 'mail', + ['%.article%.%d+'] = 'mail', + ['/tmp/SLRN[0-9A-Z.]+'] = 'mail', + ['ae%d+%.txt'] = 'mail', + ['pico%.%d+'] = 'mail', + ['mutt%-.*%-%w+'] = 'mail', + ['muttng%-.*%-%w+'] = 'mail', + ['neomutt%-.*%-%w+'] = 'mail', + ['mutt' .. string.rep('[%w_-]', 6)] = 'mail', + ['neomutt' .. string.rep('[%w_-]', 6)] = 'mail', + ['snd%.%d+'] = 'mail', + ['reportbug%-.*'] = starsetf('mail'), ['.*/etc/mail/aliases'] = 'mailaliases', ['.*/etc/aliases'] = 'mailaliases', ['.*[mM]akefile'] = 'make', + ['[mM]akefile.*'] = starsetf('make'), ['.*/etc/man%.conf'] = 'manconf', + ['.*%.[Mm][Oo][Dd]'] = function(path, bufnr) + return require('vim.filetype.detect').mod(path, bufnr) + end, ['.*/etc/modules%.conf'] = 'modconf', ['.*/etc/conf%.modules'] = 'modconf', ['.*/etc/modules'] = 'modconf', + ['.*/etc/modprobe%..*'] = starsetf('modconf'), + ['.*/etc/modutils/.*'] = starsetf(function(path, bufnr) + if vim.fn.executable(vim.fn.expand(path)) ~= 1 then + return 'modconf' + end + end), ['.*%.[mi][3g]'] = 'modula3', + ['Muttrc'] = 'muttrc', + ['Muttngrc'] = 'muttrc', + ['.*/etc/Muttrc%.d/.*'] = starsetf('muttrc'), ['.*/%.mplayer/config'] = 'mplayerconf', + ['Muttrc.*'] = starsetf('muttrc'), + ['Muttngrc.*'] = starsetf('muttrc'), + -- muttrc* and .muttrc* + ['%.?muttrc.*'] = starsetf('muttrc'), + -- muttngrc* and .muttngrc* + ['%.?muttngrc.*'] = starsetf('muttrc'), + ['.*/%.mutt/muttrc.*'] = starsetf('muttrc'), + ['.*/%.muttng/muttrc.*'] = starsetf('muttrc'), + ['.*/%.muttng/muttngrc.*'] = starsetf('muttrc'), ['rndc.*%.conf'] = 'named', ['rndc.*%.key'] = 'named', ['named.*%.conf'] = 'named', ['.*/etc/nanorc'] = 'nanorc', ['.*%.NS[ACGLMNPS]'] = 'natural', + ['Neomuttrc.*'] = starsetf('neomuttrc'), + -- neomuttrc* and .neomuttrc* + ['%.?neomuttrc.*'] = starsetf('neomuttrc'), + ['.*/%.neomutt/neomuttrc.*'] = starsetf('neomuttrc'), ['nginx.*%.conf'] = 'nginx', ['.*/etc/nginx/.*'] = 'nginx', ['.*nginx%.conf'] = 'nginx', ['.*/nginx/.*%.conf'] = 'nginx', ['.*/usr/local/nginx/conf/.*'] = 'nginx', + ['.*%.[1-9]'] = function(path, bufnr) + return require('vim.filetype.detect').nroff(bufnr) + end, ['.*%.ml%.cppo'] = 'ocaml', ['.*%.mli%.cppo'] = 'ocaml', ['.*%.opam%.template'] = 'opam', ['.*%.[Oo][Pp][Ll]'] = 'opl', ['.*/etc/pam%.conf'] = 'pamconf', - ['.*/etc/passwd-'] = 'passwd', + ['.*/etc/pam%.d/.*'] = starsetf('pamconf'), + ['.*/etc/passwd%-'] = 'passwd', ['.*/etc/shadow'] = 'passwd', ['.*/etc/shadow%.edit'] = 'passwd', ['.*/var/backups/shadow%.bak'] = 'passwd', ['.*/var/backups/passwd%.bak'] = 'passwd', ['.*/etc/passwd'] = 'passwd', ['.*/etc/passwd%.edit'] = 'passwd', - ['.*/etc/shadow-'] = 'passwd', + ['.*/etc/shadow%-'] = 'passwd', + ['%.?gitolite%.rc'] = 'perl', + ['example%.gitolite%.rc'] = 'perl', ['.*%.php%d'] = 'php', ['.*/%.pinforc'] = 'pinfo', ['.*/etc/pinforc'] = 'pinfo', + ['.*%.[Pp][Rr][Gg]'] = function(path, bufnr) + return require('vim.filetype.detect').prg(bufnr) + end, ['.*/etc/protocols'] = 'protocols', ['.*printcap.*'] = starsetf(function(path, bufnr) - if vim.fn.did_filetype() == 0 then - return 'ptcap', function(b) - vim.b[b].ptcap_type = 'print' - end - end + return require('vim.filetype.detect').printcap('print') end), ['.*baseq[2-3]/.*%.cfg'] = 'quake', ['.*quake[1-3]/.*%.cfg'] = 'quake', ['.*id1/.*%.cfg'] = 'quake', + ['.*/queries/.*%.scm'] = 'query', -- tree-sitter queries (Neovim only) + ['.*,v'] = 'rcs', + ['%.reminders.*'] = starsetf('remind'), + ['[rR]akefile.*'] = starsetf('ruby'), ['[rR]antfile'] = 'ruby', ['[rR]akefile'] = 'ruby', ['.*/etc/sensors%.conf'] = 'sensors', @@ -1849,6 +1930,31 @@ local pattern = { ['.*/etc/services'] = 'services', ['.*/etc/serial%.conf'] = 'setserial', ['.*/etc/udev/cdsymlinks%.conf'] = 'sh', + ['%.bash[_%-]aliases'] = function(path, bufnr) + return require('vim.filetype.detect').sh(path, bufnr, 'bash') + end, + ['%.bash[_%-]logout'] = function(path, bufnr) + return require('vim.filetype.detect').sh(path, bufnr, 'bash') + end, + ['%.bash[_%-]profile'] = function(path, bufnr) + return require('vim.filetype.detect').sh(path, bufnr, 'bash') + end, + ['%.kshrc.*'] = function(path, bufnr) + return require('vim.filetype.detect').sh(path, bufnr, 'ksh') + end, + ['%.profile.*'] = function(path, bufnr) + return require('vim.filetype.detect').sh(path, bufnr) + end, + ['.*/etc/profile'] = function(path, bufnr) + return require('vim.filetype.detect').sh(path, bufnr) + end, + ['bash%-fc[%-%.]'] = function(path, bufnr) + return require('vim.filetype.detect').sh(path, bufnr, 'bash') + end, + ['%.tcshrc.*'] = function(path, bufnr) + return require('vim.filetype.detect').shell(path, bufnr, 'tcsh') + end, + ['.*/etc/sudoers%.d/.*'] = starsetf('sudoers'), ['.*%._sst%.meta'] = 'sisu', ['.*%.%-sst%.meta'] = 'sisu', ['.*%.sst%.meta'] = 'sisu', @@ -1858,9 +1964,15 @@ local pattern = { ['.*/etc/ssh/ssh_config%.d/.*%.conf'] = 'sshconfig', ['.*/%.ssh/config'] = 'sshconfig', ['.*/etc/ssh/sshd_config%.d/.*%.conf'] = 'sshdconfig', + ['.*%.[Ss][Rr][Cc]'] = function(path, bufnr) + return require('vim.filetype.detect').src(bufnr) + end, ['.*/etc/sudoers'] = 'sudoers', ['svn%-commit.*%.tmp'] = 'svn', ['.*%.swift%.gyb'] = 'swiftgyb', + ['.*%.[Ss][Yy][Ss]'] = function(path, bufnr) + return require('vim.filetype.detect').sys(bufnr) + end, ['.*/etc/sysctl%.conf'] = 'sysctl', ['.*/etc/sysctl%.d/.*%.conf'] = 'sysctl', ['.*/systemd/.*%.automount'] = 'systemd', @@ -1885,11 +1997,7 @@ local pattern = { ['.*/%.config/systemd/user/.*%.d/%.#.*'] = 'systemd', ['.*/%.config/systemd/user/%.#.*'] = 'systemd', ['.*termcap.*'] = starsetf(function(path, bufnr) - if vim.fn.did_filetype() == 0 then - return 'ptcap', function(b) - vim.b[b].ptcap_type = 'term' - end - end + return require('vim.filetype.detect').printcap('term') end), ['.*%.t%.html'] = 'tilde', ['%.?tmux.*%.conf'] = 'tmux', @@ -1907,7 +2015,13 @@ local pattern = { ['.*/%.config/upstart/.*%.conf'] = 'upstart', ['.*/%.init/.*%.conf'] = 'upstart', ['.*/usr/share/upstart/.*%.override'] = 'upstart', + ['.*%.[Ll][Oo][Gg]'] = function(path, bufnr) + return require('vim.filetype.detect').log(path) + end, + ['.*%.vhdl_[0-9].*'] = starsetf('vhdl'), ['.*%.ws[fc]'] = 'wsh', + ['.*/Xresources/.*'] = starsetf('xdefaults'), + ['.*/app%-defaults/.*'] = starsetf('xdefaults'), ['.*/etc/xinetd%.conf'] = 'xinetd', ['.*/etc/blkid%.tab'] = 'xml', ['.*/etc/blkid%.tab%.old'] = 'xml', @@ -1917,213 +2031,10 @@ local pattern = { ['.*/etc/xdg/menus/.*%.menu'] = 'xml', ['.*Xmodmap'] = 'xmodmap', ['.*/etc/zprofile'] = 'zsh', - ['%.bash[_-]aliases'] = function(path, bufnr) - return require('vim.filetype.detect').sh(path, bufnr, 'bash') - end, - ['%.bash[_-]logout'] = function(path, bufnr) - return require('vim.filetype.detect').sh(path, bufnr, 'bash') - end, - ['%.bash[_-]profile'] = function(path, bufnr) - return require('vim.filetype.detect').sh(path, bufnr, 'bash') - end, - ['%.cshrc.*'] = function(path, bufnr) - return require('vim.filetype.detect').csh(path, bufnr) - end, - ['%.gtkrc.*'] = starsetf('gtkrc'), - ['%.kshrc.*'] = function(path, bufnr) - return require('vim.filetype.detect').sh(path, bufnr, 'ksh') - end, - ['%.login.*'] = function(path, bufnr) - return require('vim.filetype.detect').csh(path, bufnr) - end, - ['Muttrc.*'] = starsetf('muttrc'), - ['Muttngrc.*'] = starsetf('muttrc'), - -- muttrc* and .muttrc* - ['%.?muttrc.*'] = starsetf('muttrc'), - -- muttngrc* and .muttngrc* - ['%.?muttngrc.*'] = starsetf('muttrc'), - ['.*/%.mutt/muttrc.*'] = starsetf('muttrc'), - ['.*/%.muttng/muttrc.*'] = starsetf('muttrc'), - ['.*/%.muttng/muttngrc.*'] = starsetf('muttrc'), - ['Neomuttrc.*'] = starsetf('neomuttrc'), - -- neomuttrc* and .neomuttrc* - ['%.?neomuttrc.*'] = starsetf('neomuttrc'), - ['.*/%.neomutt/neomuttrc.*'] = starsetf('neomuttrc'), - ['%.profile.*'] = function(path, bufnr) - return require('vim.filetype.detect').sh(path, bufnr, getline(bufnr, 1)) - end, - ['%.reminders.*'] = starsetf('remind'), - ['%.tcshrc.*'] = function(path, bufnr) - return require('vim.filetype.detect').shell(path, bufnr, 'tcsh') - end, - ['%.zcompdump.*'] = starsetf('zsh'), - ['%.zlog.*'] = starsetf('zsh'), - ['%.zsh.*'] = starsetf('zsh'), - ['.*%.[1-9]'] = function(path, bufnr) - return require('vim.filetype.detect').nroff(bufnr) - end, - ['.*%.[aA]'] = function(path, bufnr) - return require('vim.filetype.detect').asm(bufnr) - end, - ['.*%.[sS]'] = function(path, bufnr) - return require('vim.filetype.detect').asm(bufnr) - end, - ['.*%.properties_.._.._.*'] = starsetf('jproperties'), - ['.*%.vhdl_[0-9].*'] = starsetf('vhdl'), - ['.*/%.fvwm/.*'] = starsetf('fvwm'), - ['.*fvwmrc.*'] = starsetf(function(path, bufnr) - return 'fvwm', function(b) - vim.b[b].fvwm_version = 1 - end - end), - ['.*fvwm95.*%.hook'] = starsetf(function(path, bufnr) - return 'fvwm', function(b) - vim.b[b].fvwm_version = 1 - end - end), - ['.*/%.gitconfig%.d/.*'] = starsetf('gitconfig'), - ['.*/Xresources/.*'] = starsetf('xdefaults'), - ['.*/app%-defaults/.*'] = starsetf('xdefaults'), - ['.*/bind/db%..*'] = starsetf('bindzone'), - ['.*/debian/patches/.*'] = function(path, bufnr) - return require('vim.filetype.detect').dep3patch(path, bufnr) - end, - ['.*/etc/Muttrc%.d/.*'] = starsetf('muttrc'), - ['.*/etc/apache2/.*%.conf.*'] = starsetf('apache'), - ['.*/etc/apache2/conf%..*/.*'] = starsetf('apache'), - ['.*/etc/apache2/mods%-.*/.*'] = starsetf('apache'), - ['.*/etc/apache2/sites%-.*/.*'] = starsetf('apache'), - ['.*/etc/cron%.d/.*'] = starsetf('crontab'), - ['.*/etc/dnsmasq%.d/.*'] = starsetf('dnsmasq'), - ['.*/etc/httpd/conf%..*/.*'] = starsetf('apache'), - ['.*/etc/httpd/conf%.d/.*%.conf.*'] = starsetf('apache'), - ['.*/etc/httpd/mods%-.*/.*'] = starsetf('apache'), - ['.*/etc/httpd/sites%-.*/.*'] = starsetf('apache'), - ['.*/etc/logcheck/.*%.d.*/.*'] = starsetf('logcheck'), - ['.*/etc/modprobe%..*'] = starsetf('modconf'), - ['.*/etc/modutils/.*'] = starsetf(function(path, bufnr) - if vim.fn.executable(vim.fn.expand(path)) ~= 1 then - return 'modconf' - end - end), - ['.*/etc/pam%.d/.*'] = starsetf('pamconf'), - ['.*/etc/profile'] = function(path, bufnr) - return require('vim.filetype.detect').sh(path, bufnr, getline(bufnr, 1)) - end, - ['.*/etc/proftpd/.*%.conf.*'] = starsetf('apachestyle'), - ['.*/etc/proftpd/conf%..*/.*'] = starsetf('apachestyle'), - ['.*/etc/sudoers%.d/.*'] = starsetf('sudoers'), - ['.*/etc/xinetd%.d/.*'] = starsetf('xinetd'), - ['.*/etc/yum%.repos%.d/.*'] = starsetf('dosini'), - ['.*/gitolite%-admin/conf/.*'] = starsetf('gitolite'), - ['.*/named/db%..*'] = starsetf('bindzone'), - ['.*/tmp/lltmp.*'] = starsetf('gedcom'), - ['.*asterisk.*/.*voicemail%.conf.*'] = starsetf('asteriskvm'), - ['.*asterisk/.*%.conf.*'] = starsetf('asterisk'), ['.*vimrc.*'] = starsetf('vim'), - ['.*xmodmap.*'] = starsetf('xmodmap'), - ['/etc/gitconfig%.d/.*'] = starsetf('gitconfig'), - ['/etc/hostname%..*'] = starsetf('config'), - ['Containerfile%..*'] = starsetf('dockerfile'), - ['Dockerfile%..*'] = starsetf('dockerfile'), - ['JAM.*%..*'] = starsetf('jam'), - ['Kconfig%..*'] = starsetf('kconfig'), - ['Prl.*%..*'] = starsetf('jam'), ['Xresources.*'] = starsetf('xdefaults'), - ['[mM]akefile.*'] = starsetf('make'), - ['[rR]akefile.*'] = starsetf('ruby'), - ['access%.conf.*'] = starsetf('apache'), - ['apache%.conf.*'] = starsetf('apache'), - ['apache2%.conf.*'] = starsetf('apache'), - ['bash%-fc[-%.]'] = function(path, bufnr) - return require('vim.filetype.detect').sh(path, bufnr, 'bash') - end, - ['cabal%.project%..*'] = starsetf('cabalproject'), - ['crontab%..*'] = starsetf('crontab'), - ['drac%..*'] = starsetf('dracula'), - ['gtkrc.*'] = starsetf('gtkrc'), - ['httpd%.conf.*'] = starsetf('apache'), - ['lilo%.conf.*'] = starsetf('lilo'), - ['Muttrc'] = 'muttrc', - ['Muttngrc'] = 'muttrc', - ['proftpd%.conf.*'] = starsetf('apachestyle'), - ['reportbug%-.*'] = starsetf('mail'), - ['sgml%.catalog.*'] = starsetf('catalog'), - ['srm%.conf.*'] = starsetf('apache'), - ['tmac%..*'] = starsetf('nroff'), - ['zlog.*'] = starsetf('zsh'), - ['zsh.*'] = starsetf('zsh'), - ['ae%d+%.txt'] = 'mail', - ['snd%.%d+'] = 'mail', - ['%.letter%.%d+'] = 'mail', - ['%.article%.%d+'] = 'mail', - ['pico%.%d+'] = 'mail', - ['mutt%-.*%-%w+'] = 'mail', - ['muttng%-.*%-%w+'] = 'mail', - ['neomutt%-.*%-%w+'] = 'mail', - ['mutt' .. string.rep('[%w_-]', 6)] = 'mail', - ['neomutt' .. string.rep('[%w_-]', 6)] = 'mail', - ['/tmp/SLRN[0-9A-Z.]+'] = 'mail', - ['[a-zA-Z0-9].*Dict'] = function(path, bufnr) - return require('vim.filetype.detect').foam(bufnr) - end, - ['[a-zA-Z0-9].*Dict%..*'] = function(path, bufnr) - return require('vim.filetype.detect').foam(bufnr) - end, - ['[a-zA-Z].*Properties'] = function(path, bufnr) - return require('vim.filetype.detect').foam(bufnr) - end, - ['[a-zA-Z].*Properties%..*'] = function(path, bufnr) - return require('vim.filetype.detect').foam(bufnr) - end, - ['.*Transport%..*'] = function(path, bufnr) - return require('vim.filetype.detect').foam(bufnr) - end, - ['.*/constant/g'] = function(path, bufnr) - return require('vim.filetype.detect').foam(bufnr) - end, - ['.*/0/.*'] = function(path, bufnr) - return require('vim.filetype.detect').foam(bufnr) - end, - ['.*/0%.orig/.*'] = function(path, bufnr) - return require('vim.filetype.detect').foam(bufnr) - end, - ['.*/etc/sensors%.d/[^.].*'] = starsetf('sensors'), - ['.*%.git/.*'] = function(path, bufnr) - local firstline = getline(bufnr, 1) - if firstline:find('^' .. string.rep('%x', 40) .. '+ ') or firstline:sub(1, 5) == 'ref: ' then - return 'git' - end - end, - ['.*%.[Cc][Ff][Gg]'] = { - function(path, bufnr) - return require('vim.filetype.detect').cfg(bufnr) - end, - -- Decrease the priority to avoid conflicts with more specific patterns - -- such as '.*/etc/a2ps/.*%.cfg', '.*enlightenment/.*%.cfg', etc. - { priority = -1 }, - }, - ['.*%.[Dd][Aa][Tt]'] = function(path, bufnr) - return require('vim.filetype.detect').dat(path, bufnr) - end, - ['.*%.[Mm][Oo][Dd]'] = function(path, bufnr) - return require('vim.filetype.detect').mod(path, bufnr) - end, - ['.*%.[Ss][Rr][Cc]'] = function(path, bufnr) - return require('vim.filetype.detect').src(bufnr) - end, - ['.*%.[Ss][Uu][Bb]'] = 'krl', - ['.*%.[Pp][Rr][Gg]'] = function(path, bufnr) - return require('vim.filetype.detect').prg(bufnr) - end, - ['.*%.[Ss][Yy][Ss]'] = function(path, bufnr) - return require('vim.filetype.detect').sys(bufnr) - end, - ['%.?gitolite%.rc'] = 'perl', - ['example%.gitolite%.rc'] = 'perl', - -- Neovim only - ['.*/queries/.*%.scm'] = 'query', -- tree-sitter queries - ['.*,v'] = 'rcs', + ['.*/etc/xinetd%.d/.*'] = starsetf('xinetd'), + ['.*xmodmap.*'] = starsetf('xmodmap'), ['.*/xorg%.conf%.d/.*%.conf'] = function(path, bufnr) return 'xf86config', function(b) vim.b[b].xf86conf_xfree86_version = 4 @@ -2138,37 +2049,11 @@ local pattern = { ['XF86Config.*'] = starsetf(function(path, bufnr) return require('vim.filetype.detect').xfree86(bufnr) end), - ['[cC]hange[lL]og.*'] = starsetf(function(path, bufnr) - local line = getline(bufnr, 1):lower() - if line:find('; urgency=') then - return 'debchangelog' - else - return 'changelog' - end - end), - ['.*fvwm2rc.*'] = starsetf(function(path, bufnr) - if vim.fn.fnamemodify(path, ':e') == 'm4' then - return 'fvwm2m4' - else - return 'fvwm', function(b) - vim.b[b].fvwm_version = 2 - end - end - end), - ['.*%.[Ll][Oo][Gg]'] = function(path, bufnr) - -- Innovation Data Processing - -- (refactor of filetype.vim since the patterns are case-insensitive) - path = path:lower() - if M.findany(path, { 'upstream%.log', 'upstream%..*%.log', '.*%.upstream%.log', 'upstream%-.*%.log' }) then - return 'upstreamlog' - elseif M.findany(path, { 'upstreaminstall%.log', 'upstreaminstall%..*%.log', '.*%.upstreaminstall%.log' }) then - return 'upstreaminstalllog' - elseif M.findany(path, { 'usserver%.log', 'usserver%..*%.log', '.*%.usserver%.log' }) then - return 'usserverlog' - elseif M.findany(path, { 'usw2kagt%.log', 'usws2kagt%..*%.log', '.*%.usws2kagt%.log' }) then - return 'usw2kagtlog' - end - end, + ['%.zcompdump.*'] = starsetf('zsh'), + -- .zlog* and zlog* + ['%.?zlog.*'] = starsetf('zsh'), + -- .zsh* and zsh* + ['%.?zsh.*'] = starsetf('zsh'), -- Ignored extension ['.*~'] = function(path, bufnr) local short = path:gsub('~$', '', 1) diff --git a/runtime/lua/vim/filetype/detect.lua b/runtime/lua/vim/filetype/detect.lua index 7e5ed0f4d1..48dd3cb088 100644 --- a/runtime/lua/vim/filetype/detect.lua +++ b/runtime/lua/vim/filetype/detect.lua @@ -1,6 +1,9 @@ --- Contains filetype detection functions converted to Lua from Vim's autoload/runtime/dist/ft.vim file. +-- Contains filetype detection functions for use in filetype.lua that are either: +-- * used more than once or +-- * complex (e.g. check more than one line or use conditionals). +-- Simple one-line checks, such as a check for a string in the first line are better inlined in filetype.lua. --- Here are a few guidelines to follow when porting a new function: +-- A few guidelines to follow when porting a new function: -- * Sort the function alphabetically and omit 'ft' or 'check' from the new function name. -- * Use ':find' instead of ':match' / ':sub' if possible. -- * When '=~' is used to match a pattern, there are two possibilities: @@ -45,6 +48,17 @@ function M.asm(bufnr) end end +-- Active Server Pages (with Perl or Visual Basic Script) +function M.asp(bufnr) + if vim.g.filetype_asp then + return vim.g.filetype_asp + elseif table.concat(getlines(bufnr, 1, 3)):lower():find('perlscript') then + return 'aspperl' + else + return 'aspvbs' + end +end + -- Checks the first 5 lines for a asmsyntax=foo override. -- Only whitespace characters can be present immediately before or after this statement. function M.asm_syntax(bufnr) @@ -91,17 +105,8 @@ function M.bindzone(bufnr, default) local lines = table.concat(getlines(bufnr, 1, 4)) if findany(lines, { '^; <<>> DiG [0-9%.]+.* <<>>', '%$ORIGIN', '%$TTL', 'IN%s+SOA' }) then return 'bindzone' - else - return default - end -end - -function M.btm(bufnr) - if vim.g.dosbatch_syntax_for_btm and vim.g.dosbatch_syntax_for_btm ~= 0 then - return 'dosbatch' - else - return 'btm' end + return default end -- Returns true if file content looks like RAPID @@ -151,6 +156,46 @@ function M.change(bufnr) return 'chill' end +function M.changelog(bufnr) + local line = getlines(bufnr, 1):lower() + if line:find('; urgency=') then + return 'debchangelog' + end + return 'changelog' +end + +function M.class(bufnr) + -- Check if not a Java class (starts with '\xca\xfe\xba\xbe') + if not getlines(bufnr, 1):find('^\202\254\186\190') then + return 'stata' + end +end + +function M.cls(bufnr) + local line = getlines(bufnr, 1) + if line:find('^%%') then + return 'tex' + elseif line:find('^#') and line:lower():find('rexx') then + return 'rexx' + else + return 'st' + end +end + +-- Debian Control +function M.control(bufnr) + if getlines(bufnr, 1):find('^Source:') then + return 'debcontrol' + end +end + +-- Debian Copyright +function M.copyright(bufnr) + if getlines(bufnr, 1):find('^Format:') then + return 'debcopyright' + end +end + function M.csh(path, bufnr) if vim.fn.did_filetype() ~= 0 then -- Filetype was already detected @@ -180,6 +225,14 @@ function M.dat(path, bufnr) end end +function M.decl(bufnr) + for _, line in ipairs(getlines(bufnr, 1, 3)) do + if line:lower():find('^<!sgml') then + return 'sgmldecl' + end + end +end + -- This function is called for all files under */debian/patches/*, make sure not -- to non-dep3patch files, such as README and other text files. function M.dep3patch(path, bufnr) @@ -240,6 +293,15 @@ function M.e(bufnr) return 'eiffel' end +function M.edn(bufnr) + local line = getlines(bufnr, 1) + if matchregex(line, [[\c^\s*(\s*edif\>]]) then + return 'edif' + else + return 'clojure' + end +end + -- This function checks for valid cl syntax in the first five lines. -- Look for either an opening comment, '#', or a block start, '{'. -- If not found, assume SGML. @@ -256,14 +318,6 @@ function M.ent(bufnr) return 'dtd' end -function M.euphoria(bufnr) - if vim.g.filetype_euphoria then - return vim.g.filetype_euphoria - else - return 'euphoria3' - end -end - function M.ex(bufnr) if vim.g.filetype_euphoria then return vim.g.filetype_euphoria @@ -303,6 +357,15 @@ function M.frm(bufnr) end end +function M.fvwm(path) + if vim.fn.fnamemodify(path, ':e') == 'm4' then + return 'fvwm2m4' + end + return 'fvwm', function(bufnr) + vim.b[bufnr].fvwm_version = 2 + end +end + -- Distinguish between Forth and F#. function M.fs(bufnr) if vim.g.filetype_fs then @@ -316,6 +379,13 @@ function M.fs(bufnr) end end +function M.git(bufnr) + local line = getlines(bufnr, 1) + if line:find('^' .. string.rep('%x', 40) .. '+ ') or line:sub(1, 5) == 'ref: ' then + return 'git' + end +end + function M.header(bufnr) for _, line in ipairs(getlines(bufnr, 1, 200)) do if findany(line:lower(), { '^@interface', '^@end', '^@class' }) then @@ -346,6 +416,14 @@ function M.html(bufnr) return 'html' end +-- Virata Config Script File or Drupal module +function M.hw(bufnr) + if getlines(bufnr, 1):lower():find('<%?php') then + return 'php' + end + return 'virata' +end + function M.idl(bufnr) for _, line in ipairs(getlines(bufnr, 1, 50)) do if findany(line:lower(), { '^%s*import%s+"unknwn"%.idl', '^%s*import%s+"objidl"%.idl' }) then @@ -396,6 +474,28 @@ function M.inp(bufnr) end end +function M.install(path, bufnr) + if getlines(bufnr, 1):lower():find('<%?php') then + return 'php' + end + return M.sh(path, bufnr, 'bash') +end + +-- Innovation Data Processing +-- (refactor of filetype.vim since the patterns are case-insensitive) +function M.log(path) + path = path:lower() + if findany(path, { 'upstream%.log', 'upstream%..*%.log', '.*%.upstream%.log', 'upstream%-.*%.log' }) then + return 'upstreamlog' + elseif findany(path, { 'upstreaminstall%.log', 'upstreaminstall%..*%.log', '.*%.upstreaminstall%.log' }) then + return 'upstreaminstalllog' + elseif findany(path, { 'usserver%.log', 'usserver%..*%.log', '.*%.usserver%.log' }) then + return 'usserverlog' + elseif findany(path, { 'usw2kagt%.log', 'usws2kagt%..*%.log', '.*%.usws2kagt%.log' }) then + return 'usw2kagtlog' + end +end + function M.lpc(bufnr) if vim.g.lpc_syntax_for_c then for _, line in ipairs(getlines(bufnr, 1, 12)) do @@ -467,6 +567,13 @@ function M.m(bufnr) end end +function M.m4(path) + path = path:lower() + if not path:find('html%.m4$') and not path:find('fvwm2rc') then + return 'm4' + end +end + -- Rely on the file to start with a comment. -- MS message text files use ';', Sendmail files use '#' or 'dnl' function M.mc(bufnr) @@ -482,6 +589,13 @@ function M.mc(bufnr) return 'm4' end +function M.me(path) + local filename = vim.fn.fnamemodify(path, ':t'):lower() + if filename ~= 'read.me' and filename ~= 'click.me' then + return 'nroff' + end +end + function M.mm(bufnr) for _, line in ipairs(getlines(bufnr, 1, 20)) do if matchregex(line, [[\c^\s*\(#\s*\(include\|import\)\>\|@import\>\|/\*\)]]) then @@ -533,15 +647,29 @@ function M.mod(path, bufnr) end end +function M.news(bufnr) + if getlines(bufnr, 1):lower():find('; urgency=') then + return 'debchangelog' + end +end + -- This function checks if one of the first five lines start with a dot. In --- that case it is probably an nroff file: 'filetype' is set and true is returned. +-- that case it is probably an nroff file. function M.nroff(bufnr) for _, line in ipairs(getlines(bufnr, 1, 5)) do if line:find('^%.') then - return true + return 'nroff' end end - return false +end + +function M.patch(bufnr) + local firstline = getlines(bufnr, 1) + if string.find(firstline, '^From ' .. string.rep('%x', 40) .. '+ Mon Sep 17 00:00:00 2001$') then + return 'gitsendemail' + else + return 'diff' + end end -- If the file has an extension of 't' and is in a directory 't' or 'xt' then @@ -551,18 +679,17 @@ end function M.perl(path, bufnr) local dirname = vim.fn.expand(path, '%:p:h:t') if vim.fn.expand(dirname, '%:e') == 't' and (dirname == 't' or dirname == 'xt') then - return true + return 'perl' end local first_line = getlines(bufnr, 1) if first_line:find('^#') and first_line:lower():find('perl') then - return true + return 'perl' end for _, line in ipairs(getlines(bufnr, 1, 30)) do if matchregex(line, [[\c^use\s\s*\k]]) then - return true + return 'perl' end end - return false end function M.pl(bufnr) @@ -583,6 +710,17 @@ function M.pl(bufnr) end end +function M.pm(bufnr) + local line = getlines(bufnr, 1) + if line:find('XPM2') then + return 'xpm2' + elseif line:find('XPM') then + return 'xpm' + else + return 'perl' + end +end + function M.pp(bufnr) if vim.g.filetype_pp then return vim.g.filetype_pp @@ -606,6 +744,14 @@ function M.prg(bufnr) end end +function M.printcap(ptcap_type) + if vim.fn.did_filetype() == 0 then + return 'ptcap', function(bufnr) + vim.b[bufnr].ptcap_type = ptcap_type + end + end +end + -- This function checks for an assembly comment in the first ten lines. -- If not found, assume Progress. function M.progress_asm(bufnr) @@ -670,6 +816,19 @@ function M.proto(bufnr, default) end end +-- Software Distributor Product Specification File (POSIX 1387.2-1995) +function M.psf(bufnr) + local line = getlines(bufnr, 1):lower() + if + findany( + line, + { '^%s*distribution%s*$', '^%s*installed_software%s*$', '^%s*root%s*$', '^%s*bundle%s*$', '^%s*product%s*$' } + ) + then + return 'psf' + end +end + function M.r(bufnr) local lines = getlines(bufnr, 1, 50) -- Rebol is easy to recognize, check for that first @@ -705,8 +864,23 @@ function M.redif(bufnr) end end +function M.reg(bufnr) + local line = getlines(bufnr, 1):lower() + if line:find('^regedit[0-9]*%s*$') or line:find('^windows registry editor version %d*%.%d*%s*$') then + return 'registry' + end +end + +-- Diva (with Skill) or InstallShield +function M.rul(bufnr) + if table.concat(getlines(bufnr, 1, 6)):lower():find('installshield') then + return 'ishd' + end + return 'diva' +end + local udev_rules_pattern = '^%s*udev_rules%s*=%s*"([%^"]+)/*".*' -function M.rules(path, bufnr) +function M.rules(path) path = path:lower() if findany(path, { @@ -770,7 +944,20 @@ function M.scd(bufnr) end end --- Also called from filetype.lua +function M.sgml(bufnr) + local lines = table.concat(getlines(bufnr, 1, 5)) + if lines:find('linuxdoc') then + return 'smgllnx' + elseif lines:find('<!DOCTYPE.*DocBook') then + return 'docbk', function(b) + vim.b[b].docbk_type = 'sgml' + vim.b[b].docbk_ver = 4 + end + else + return 'sgml' + end +end + function M.sh(path, bufnr, name) if vim.fn.did_filetype() ~= 0 or path:find(vim.g.ft_ignore_pat) then -- Filetype was already detected or detection should be skipped @@ -779,6 +966,7 @@ function M.sh(path, bufnr, name) local on_detect + name = name or getlines(bufnr, 1) if matchregex(name, [[\<csh\>]]) then -- Some .sh scripts contain #!/bin/csh. return M.shell(path, bufnr, 'csh') @@ -832,11 +1020,13 @@ function M.shell(path, bufnr, name) return name end -function M.sql(bufnr) - if vim.g.filetype_sql then - return vim.g.filetype_sql +-- SMIL or SNMP MIB file +function M.smi(bufnr) + local line = getlines(bufnr, 1) + if matchregex(line, [[\c\<smil\>]]) then + return 'smil' else - return 'sql' + return 'mib' end end @@ -919,6 +1109,43 @@ function M.tf(bufnr) return 'tf' end +function M.ttl(bufnr) + local line = getlines(bufnr, 1):lower() + if line:find('^@?prefix') or line:find('^@?base') then + return 'turtle' + end + return 'teraterm' +end + +function M.txt(bufnr) + -- helpfiles match *.txt, but should have a modeline as last line + if not getlines(bufnr, -1):find('vim:.*ft=help') then + return 'text' + end +end + +-- WEB (*.web is also used for Winbatch: Guess, based on expecting "%" comment +-- lines in a WEB file). +function M.web(bufnr) + for _, line in ipairs(getlines(bufnr, 1, 5)) do + if line:find('^%%') then + return 'web' + end + end + return 'winbatch' +end + +-- XFree86 config +function M.xfree86() + return 'xf86conf', + function(bufnr) + local line = getlines(bufnr, 1) + if matchregex(line, [[\<XConfigurator\>]]) then + vim.b[bufnr].xf86conf_xfree86_version = 3 + end + end +end + function M.xml(bufnr) for _, line in ipairs(getlines(bufnr, 1, 100)) do local is_docbook4 = line:find('<!DOCTYPE.*DocBook') @@ -950,124 +1177,6 @@ function M.y(bufnr) return 'yacc' end -function M.decl(bufnr) - for _, line in ipairs(getlines(bufnr, 1, 3)) do - if line:lower():find('^<!sgml') then - return 'sgmldecl' - end - end -end - -function M.sgml(bufnr) - local lines = table.concat(getlines(bufnr, 1, 5)) - if lines:find('linuxdoc') then - return 'smgllnx' - elseif lines:find('<!DOCTYPE.*DocBook') then - return 'docbk', function(b) - vim.b[b].docbk_type = 'sgml' - vim.b[b].docbk_ver = 4 - end - else - return 'sgml' - end -end - -function M.web(bufnr) - for _, line in ipairs(getlines(bufnr, 1, 5)) do - if line:find('^%%') then - return 'web' - end - end - return 'winbatch' -end - -function M.rul(bufnr) - if table.concat(getlines(bufnr, 1, 6)):lower():find('installshield') then - return 'ishd' - end - return 'diva' -end - -function M.asp(bufnr) - if vim.g.filetype_asp then - return vim.g.filetype_asp - elseif table.concat(getlines(bufnr, 1, 3)):lower():find('perlscript') then - return 'aspperl' - else - return 'aspvbs' - end -end - -function M.edn(bufnr) - local line = getlines(bufnr, 1) - if matchregex(line, [[\c^\s*(\s*edif\>]]) then - return 'edif' - else - return 'clojure' - end -end - -function M.smi(bufnr) - local line = getlines(bufnr, 1) - if matchregex(line, [[\c\<smil\>]]) then - return 'smil' - else - return 'mib' - end -end - -function M.hw(bufnr) - if getlines(bufnr, 1):lower():find('<%?php') then - return 'php' - end - return 'virata' -end - -function M.news(bufnr) - if getlines(bufnr, 1):lower():find('; urgency=') then - return 'debchangelog' - end -end - --- Debian Control -function M.control(bufnr) - if getlines(bufnr, 1):find('^Source:') then - return 'debcontrol' - end -end - --- Debian Copyright -function M.copyright(bufnr) - if getlines(bufnr, 1):find('^Format:') then - return 'debcopyright' - end -end - --- Software Distributor Product Specification File (POSIX 1387.2-1995) -function M.psf(bufnr) - local line = getlines(bufnr, 1):lower() - if - findany( - line, - { '^%s*distribution%s*$', '^%s*installed_software%s*$', '^%s*root%s*$', '^%s*bundle%s*$', '^%s*product%s*$' } - ) - then - return 'psf' - end -end - --- XFree86 config -function M.xfree86(bufnr) - local line = getlines(bufnr, 1) - local on_detect - if matchregex(line, [[\<XConfigurator\>]]) then - on_detect = function(b) - vim.b[b].xf86conf_xfree86_version = 3 - end - end - return 'xf86conf', on_detect -end - -- luacheck: pop -- luacheck: pop diff --git a/runtime/syntax/vim.vim b/runtime/syntax/vim.vim index 9917a7f694..76ad1d523d 100644 --- a/runtime/syntax/vim.vim +++ b/runtime/syntax/vim.vim @@ -368,7 +368,7 @@ syn match vimSetMod contained "&vim\=\|[!&?<]\|all&" " Let: {{{2 " === syn keyword vimLet let unl[et] skipwhite nextgroup=vimVar,vimFuncVar,vimLetHereDoc -VimFoldh syn region vimLetHereDoc matchgroup=vimLetHereDocStart start='=<<\s\+\%(trim\>\)\=\s*\z(\L\S*\)' matchgroup=vimLetHereDocStop end='^\s*\z1\s*$' +VimFoldh syn region vimLetHereDoc matchgroup=vimLetHereDocStart start='=<<\s\+\%(trim\%(\s\+eval\)\=\|eval\%(\s\+trim\)\=\)\=\s*\z(\L\S*\)' matchgroup=vimLetHereDocStop end='^\s*\z1\s*$' " Abbreviations: {{{2 " ============= diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 0d0fba265b..37f3aa1a23 100755 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -82,10 +82,10 @@ set(LINT_SUPPRESSES_ARCHIVE ${LINT_SUPPRESSES_ROOT}/errors.tar.gz) set(LINT_SUPPRESSES_TOUCH_FILE "${TOUCHES_DIR}/unpacked-clint-errors-archive") set(LINT_SUPPRESSES_INSTALL_SCRIPT "${PROJECT_SOURCE_DIR}/cmake/InstallClintErrors.cmake") -file(GLOB UNICODE_FILES ${UNICODE_DIR}/*.txt) -file(GLOB API_HEADERS api/*.h) +glob_wrapper(UNICODE_FILES ${UNICODE_DIR}/*.txt) +glob_wrapper(API_HEADERS api/*.h) list(REMOVE_ITEM API_HEADERS ${CMAKE_CURRENT_LIST_DIR}/api/ui_events.in.h) -file(GLOB MSGPACK_RPC_HEADERS msgpack_rpc/*.h) +glob_wrapper(MSGPACK_RPC_HEADERS msgpack_rpc/*.h) include_directories(${GENERATED_DIR}) include_directories(${CACHED_GENERATED_DIR}) @@ -97,10 +97,10 @@ file(MAKE_DIRECTORY ${GENERATED_INCLUDES_DIR}) file(MAKE_DIRECTORY ${LINT_SUPPRESSES_ROOT}) file(MAKE_DIRECTORY ${LINT_SUPPRESSES_ROOT}/src) -file(GLOB NVIM_SOURCES *.c) -file(GLOB NVIM_HEADERS *.h) -file(GLOB EXTERNAL_SOURCES ../xdiff/*.c ../mpack/*.c ../cjson/*.c) -file(GLOB EXTERNAL_HEADERS ../xdiff/*.h ../mpack/*.h ../cjson/*.h) +glob_wrapper(NVIM_SOURCES *.c) +glob_wrapper(NVIM_HEADERS *.h) +glob_wrapper(EXTERNAL_SOURCES ../xdiff/*.c ../mpack/*.c ../cjson/*.c) +glob_wrapper(EXTERNAL_HEADERS ../xdiff/*.h ../mpack/*.h ../cjson/*.h) foreach(subdir os @@ -120,13 +120,13 @@ foreach(subdir file(MAKE_DIRECTORY ${GENERATED_DIR}/${subdir}) file(MAKE_DIRECTORY ${GENERATED_INCLUDES_DIR}/${subdir}) - file(GLOB sources ${subdir}/*.c) - file(GLOB headers ${subdir}/*.h) + glob_wrapper(sources ${subdir}/*.c) + glob_wrapper(headers ${subdir}/*.h) list(APPEND NVIM_SOURCES ${sources}) list(APPEND NVIM_HEADERS ${headers}) endforeach() -file(GLOB UNIT_TEST_FIXTURES ${PROJECT_SOURCE_DIR}/test/unit/fixtures/*.c) +glob_wrapper(UNIT_TEST_FIXTURES ${PROJECT_SOURCE_DIR}/test/unit/fixtures/*.c) # Sort file lists to ensure generated files are created in the same order from # build to build. @@ -805,7 +805,7 @@ add_custom_target(lintc DEPENDS ${LINT_TARGETS}) def_cmd_target(lintuncrustify ${UNCRUSTIFY_PRG} UNCRUSTIFY_PRG false) if(UNCRUSTIFY_PRG) - add_custom_command(OUTPUT lintuncrustify-cmd APPEND + add_custom_command(TARGET lintuncrustify COMMAND ${CMAKE_COMMAND} -D UNCRUSTIFY_PRG=${UNCRUSTIFY_PRG} -D PROJECT_SOURCE_DIR=${PROJECT_SOURCE_DIR} diff --git a/src/nvim/api/options.c b/src/nvim/api/options.c index 61d4becd32..19ce25f676 100644 --- a/src/nvim/api/options.c +++ b/src/nvim/api/options.c @@ -21,17 +21,73 @@ # include "api/options.c.generated.h" #endif +static int validate_option_value_args(Dict(option) *opts, int *scope, int *opt_type, void **from, + Error *err) +{ + if (opts->scope.type == kObjectTypeString) { + if (!strcmp(opts->scope.data.string.data, "local")) { + *scope = OPT_LOCAL; + } else if (!strcmp(opts->scope.data.string.data, "global")) { + *scope = OPT_GLOBAL; + } else { + api_set_error(err, kErrorTypeValidation, "invalid scope: must be 'local' or 'global'"); + return FAIL; + } + } else if (HAS_KEY(opts->scope)) { + api_set_error(err, kErrorTypeValidation, "invalid value for key: scope"); + return FAIL; + } + + *opt_type = SREQ_GLOBAL; + + if (opts->win.type == kObjectTypeInteger) { + *opt_type = SREQ_WIN; + *from = find_window_by_handle((int)opts->win.data.integer, err); + if (ERROR_SET(err)) { + return FAIL; + } + } else if (HAS_KEY(opts->win)) { + api_set_error(err, kErrorTypeValidation, "invalid value for key: win"); + return FAIL; + } + + if (opts->buf.type == kObjectTypeInteger) { + *scope = OPT_LOCAL; + *opt_type = SREQ_BUF; + *from = find_buffer_by_handle((int)opts->buf.data.integer, err); + if (ERROR_SET(err)) { + return FAIL; + } + } else if (HAS_KEY(opts->buf)) { + api_set_error(err, kErrorTypeValidation, "invalid value for key: buf"); + return FAIL; + } + + if (HAS_KEY(opts->scope) && HAS_KEY(opts->buf)) { + api_set_error(err, kErrorTypeValidation, "scope and buf cannot be used together"); + return FAIL; + } + + if (HAS_KEY(opts->win) && HAS_KEY(opts->buf)) { + api_set_error(err, kErrorTypeValidation, "buf and win cannot be used together"); + return FAIL; + } + + return OK; +} + /// 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 -/// buffer or window. To get a buffer-local or window-local option for a -/// specific buffer or window, use |nvim_buf_get_option()| or -/// |nvim_win_get_option()|. +/// buffer or window, unless "buf" or "win" is set in {opts}. /// /// @param name Option name /// @param opts Optional parameters -/// - scope: One of 'global' or 'local'. Analogous to +/// - scope: One of "global" or "local". Analogous to /// |:setglobal| and |:setlocal|, respectively. +/// - win: |window-ID|. Used for getting window local options. +/// - buf: Buffer number. Used for getting buffer local options. +/// Implies {scope} is "local". /// @param[out] err Error details, if any /// @return Option value Object nvim_get_option_value(String name, Dict(option) *opts, Error *err) @@ -40,23 +96,21 @@ Object nvim_get_option_value(String name, Dict(option) *opts, Error *err) Object rv = OBJECT_INIT; int scope = 0; - if (opts->scope.type == kObjectTypeString) { - if (!strcmp(opts->scope.data.string.data, "local")) { - scope = OPT_LOCAL; - } else if (!strcmp(opts->scope.data.string.data, "global")) { - scope = OPT_GLOBAL; - } else { - api_set_error(err, kErrorTypeValidation, "invalid scope: must be 'local' or 'global'"); - goto end; - } - } else if (HAS_KEY(opts->scope)) { - api_set_error(err, kErrorTypeValidation, "invalid value for key: scope"); - goto end; + int opt_type = SREQ_GLOBAL; + void *from = NULL; + if (!validate_option_value_args(opts, &scope, &opt_type, &from, err)) { + return rv; } long numval = 0; char *stringval = NULL; - switch (get_option_value(name.data, &numval, &stringval, scope)) { + int result = access_option_value_for(name.data, &numval, &stringval, scope, opt_type, from, + true, err); + if (ERROR_SET(err)) { + return rv; + } + + switch (result) { case 0: rv = STRING_OBJ(cstr_as_string(stringval)); break; @@ -78,10 +132,9 @@ Object nvim_get_option_value(String name, Dict(option) *opts, Error *err) break; default: api_set_error(err, kErrorTypeValidation, "unknown option '%s'", name.data); - goto end; + return rv; } -end: return rv; } @@ -103,47 +156,9 @@ void nvim_set_option_value(String name, Object value, Dict(option) *opts, Error FUNC_API_SINCE(9) { int scope = 0; - if (opts->scope.type == kObjectTypeString) { - if (!strcmp(opts->scope.data.string.data, "local")) { - scope = OPT_LOCAL; - } else if (!strcmp(opts->scope.data.string.data, "global")) { - scope = OPT_GLOBAL; - } else { - api_set_error(err, kErrorTypeValidation, "invalid scope: must be 'local' or 'global'"); - return; - } - } else if (HAS_KEY(opts->scope)) { - api_set_error(err, kErrorTypeValidation, "invalid value for key: scope"); - return; - } - int opt_type = SREQ_GLOBAL; void *to = NULL; - - if (opts->win.type == kObjectTypeInteger) { - opt_type = SREQ_WIN; - to = find_window_by_handle((int)opts->win.data.integer, err); - } else if (HAS_KEY(opts->win)) { - api_set_error(err, kErrorTypeValidation, "invalid value for key: win"); - return; - } - - if (opts->buf.type == kObjectTypeInteger) { - scope = OPT_LOCAL; - opt_type = SREQ_BUF; - to = find_buffer_by_handle((int)opts->buf.data.integer, err); - } else if (HAS_KEY(opts->buf)) { - api_set_error(err, kErrorTypeValidation, "invalid value for key: buf"); - return; - } - - if (HAS_KEY(opts->scope) && HAS_KEY(opts->buf)) { - api_set_error(err, kErrorTypeValidation, "scope and buf cannot be used together"); - return; - } - - if (HAS_KEY(opts->win) && HAS_KEY(opts->buf)) { - api_set_error(err, kErrorTypeValidation, "buf and win cannot be used together"); + if (!validate_option_value_args(opts, &scope, &opt_type, &to, err)) { return; } @@ -168,7 +183,7 @@ void nvim_set_option_value(String name, Object value, Dict(option) *opts, Error return; } - set_option_value_for(name.data, numval, stringval, scope, opt_type, to, err); + access_option_value_for(name.data, &numval, &stringval, scope, opt_type, to, false, err); } /// Gets the option information for all options. @@ -405,7 +420,7 @@ void set_option_to(uint64_t channel_id, void *to, int type, String name, Object } } - int numval = 0; + long numval = 0; char *stringval = NULL; if (flags & SOPT_BOOL) { @@ -450,16 +465,34 @@ void set_option_to(uint64_t channel_id, void *to, int type, String name, Object ? 0 : (type == SREQ_GLOBAL) ? OPT_GLOBAL : OPT_LOCAL; - set_option_value_for(name.data, numval, stringval, - opt_flags, type, to, err); + access_option_value_for(name.data, &numval, &stringval, opt_flags, type, to, false, err); }); } -void set_option_value_for(char *key, long numval, char *stringval, int opt_flags, int opt_type, - void *from, Error *err) +static int access_option_value(char *key, long *numval, char **stringval, int opt_flags, bool get, + Error *err) +{ + if (get) { + return get_option_value(key, numval, stringval, opt_flags); + } else { + char *errmsg; + if ((errmsg = set_option_value(key, *numval, *stringval, opt_flags))) { + if (try_end(err)) { + return 0; + } + + api_set_error(err, kErrorTypeException, "%s", errmsg); + } + return 0; + } +} + +static int access_option_value_for(char *key, long *numval, char **stringval, int opt_flags, + int opt_type, void *from, bool get, Error *err) { switchwin_T switchwin; aco_save_T aco; + int result = 0; try_start(); switch (opt_type) { @@ -468,42 +501,31 @@ void set_option_value_for(char *key, long numval, char *stringval, int opt_flags == FAIL) { restore_win_noblock(&switchwin, true); if (try_end(err)) { - return; + return result; } api_set_error(err, kErrorTypeException, "Problem while switching windows"); - return; + return result; } - set_option_value_err(key, numval, stringval, opt_flags, err); + result = access_option_value(key, numval, stringval, opt_flags, get, err); restore_win_noblock(&switchwin, true); break; case SREQ_BUF: aucmd_prepbuf(&aco, (buf_T *)from); - set_option_value_err(key, numval, stringval, opt_flags, err); + result = access_option_value(key, numval, stringval, opt_flags, get, err); aucmd_restbuf(&aco); break; case SREQ_GLOBAL: - set_option_value_err(key, numval, stringval, opt_flags, err); + result = access_option_value(key, numval, stringval, opt_flags, get, err); break; } if (ERROR_SET(err)) { - return; + return result; } try_end(err); -} -static void set_option_value_err(char *key, long numval, char *stringval, int opt_flags, Error *err) -{ - char *errmsg; - - if ((errmsg = set_option_value(key, numval, stringval, opt_flags))) { - if (try_end(err)) { - return; - } - - api_set_error(err, kErrorTypeException, "%s", errmsg); - } + return result; } diff --git a/src/nvim/api/private/defs.h b/src/nvim/api/private/defs.h index 396fab721d..b1e0dd364c 100644 --- a/src/nvim/api/private/defs.h +++ b/src/nvim/api/private/defs.h @@ -6,9 +6,10 @@ #include <string.h> #include "nvim/func_attr.h" +#include "nvim/lib/kvec.h" #include "nvim/types.h" -#define ARRAY_DICT_INIT { .size = 0, .capacity = 0, .items = NULL } +#define ARRAY_DICT_INIT KV_INITIAL_VALUE #define STRING_INIT { .data = NULL, .size = 0 } #define OBJECT_INIT { .type = kObjectTypeNil } #define ERROR_INIT { .type = kErrorTypeNone, .msg = NULL } @@ -84,18 +85,10 @@ REMOTE_TYPE(Window); REMOTE_TYPE(Tabpage); typedef struct object Object; - -typedef struct { - Object *items; - size_t size, capacity; -} Array; +typedef kvec_t(Object) Array; typedef struct key_value_pair KeyValuePair; - -typedef struct { - KeyValuePair *items; - size_t size, capacity; -} Dictionary; +typedef kvec_t(KeyValuePair) Dictionary; typedef enum { kObjectTypeNil = 0, diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index ff6a4c37e8..9cadef0385 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -669,6 +669,29 @@ void api_free_string(String value) xfree(value.data); } +Array arena_array(Arena *arena, size_t max_size) +{ + Array arr = ARRAY_DICT_INIT; + kv_fixsize_arena(arena, arr, max_size); + return arr; +} + +Dictionary arena_dict(Arena *arena, size_t max_size) +{ + Dictionary dict = ARRAY_DICT_INIT; + kv_fixsize_arena(arena, dict, max_size); + return dict; +} + +String arena_string(Arena *arena, String str) +{ + if (str.size) { + return cbuf_as_string(arena_memdupz(arena, str.data, str.size), str.size); + } else { + return (String)STRING_INIT; + } +} + void api_free_object(Object value) { switch (value.type) { diff --git a/src/nvim/api/private/helpers.h b/src/nvim/api/private/helpers.h index 8423db4970..a4348d8b44 100644 --- a/src/nvim/api/private/helpers.h +++ b/src/nvim/api/private/helpers.h @@ -63,17 +63,31 @@ #define PUT(dict, k, v) \ kv_push(dict, ((KeyValuePair) { .key = cstr_to_string(k), .value = v })) +#define PUT_C(dict, k, v) \ + kv_push_c(dict, ((KeyValuePair) { .key = cstr_as_string(k), .value = v })) + #define PUT_BOOL(dict, name, condition) PUT(dict, name, BOOLEAN_OBJ(condition)); #define ADD(array, item) \ kv_push(array, item) +#define ADD_C(array, item) \ + kv_push_c(array, item) + #define FIXED_TEMP_ARRAY(name, fixsize) \ Array name = ARRAY_DICT_INIT; \ Object name##__items[fixsize]; \ name.size = fixsize; \ name.items = name##__items; \ +#define MAXSIZE_TEMP_ARRAY(name, maxsize) \ + Array name = ARRAY_DICT_INIT; \ + Object name##__items[maxsize]; \ + name.capacity = maxsize; \ + name.items = name##__items; \ + +#define cbuf_as_string(d, s) ((String) { .data = d, .size = s }) + #define STATIC_CSTR_AS_STRING(s) ((String) { .data = s, .size = sizeof(s) - 1 }) /// Create a new String instance, putting data in allocated memory diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c index dc04eedebc..54ce838b9b 100644 --- a/src/nvim/api/ui.c +++ b/src/nvim/api/ui.c @@ -9,11 +9,13 @@ #include "nvim/api/private/defs.h" #include "nvim/api/private/helpers.h" #include "nvim/api/ui.h" +#include "nvim/channel.h" #include "nvim/cursor_shape.h" #include "nvim/highlight.h" #include "nvim/map.h" #include "nvim/memory.h" #include "nvim/msgpack_rpc/channel.h" +#include "nvim/msgpack_rpc/helpers.h" #include "nvim/option.h" #include "nvim/popupmnu.h" #include "nvim/screen.h" @@ -21,14 +23,32 @@ #include "nvim/vim.h" #include "nvim/window.h" -#ifdef INCLUDE_GENERATED_DECLARATIONS -# include "api/ui.c.generated.h" -# include "ui_events_remote.generated.h" -#endif - typedef struct { uint64_t channel_id; - Array buffer; + +#define UI_BUF_SIZE 4096 ///< total buffer size for pending msgpack data. + /// guranteed size available for each new event (so packing of simple events + /// and the header of grid_line will never fail) +#define EVENT_BUF_SIZE 256 + char buf[UI_BUF_SIZE]; ///< buffer of packed but not yet sent msgpack data + char *buf_wptr; ///< write head of buffer + const char *cur_event; ///< name of current event (might get multiple arglists) + Array call_buf; ///< buffer for constructing a single arg list (max 16 elements!) + + // state for write_cb, while packing a single arglist to msgpack. This + // might fail due to buffer overflow. + size_t pack_totlen; + bool buf_overflow; + char *temp_buf; + + // We start packing the two outermost msgpack arrays before knowing the total + // number of elements. Thus track the location where array size will need + // to be written in the msgpack buffer, once the specifc array is finished. + char *nevents_pos; + char *ncalls_pos; + uint32_t nevents; ///< number of distinct events (top-level args to "redraw" + uint32_t ncalls; ///< number of calls made to the current event (plus one for the name!) + bool flushed_events; ///< events where sent to client without "flush" event int hl_id; // Current highlight for legacy put event. Integer cursor_row, cursor_col; // Intended visible cursor position. @@ -38,8 +58,76 @@ typedef struct { bool wildmenu_active; } UIData; +#define BUF_POS(data) ((size_t)((data)->buf_wptr - (data)->buf)) + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "api/ui.c.generated.h" +# include "ui_events_remote.generated.h" +#endif + static PMap(uint64_t) connected_uis = MAP_INIT; +#define mpack_w(b, byte) *(*b)++ = (char)(byte); +static void mpack_w2(char **b, uint32_t v) +{ + *(*b)++ = (char)((v >> 8) & 0xff); + *(*b)++ = (char)(v & 0xff); +} + +static void mpack_w4(char **b, uint32_t v) +{ + *(*b)++ = (char)((v >> 24) & 0xff); + *(*b)++ = (char)((v >> 16) & 0xff); + *(*b)++ = (char)((v >> 8) & 0xff); + *(*b)++ = (char)(v & 0xff); +} + +static void mpack_uint(char **buf, uint32_t val) +{ + if (val > 0xffff) { + mpack_w(buf, 0xce); + mpack_w4(buf, val); + } else if (val > 0xff) { + mpack_w(buf, 0xcd); + mpack_w2(buf, val); + } else if (val > 0x7f) { + mpack_w(buf, 0xcc); + mpack_w(buf, val); + } else { + mpack_w(buf, val); + } +} + +static void mpack_array(char **buf, uint32_t len) +{ + if (len < 0x10) { + mpack_w(buf, 0x90 | len); + } else if (len < 0x10000) { + mpack_w(buf, 0xdc); + mpack_w2(buf, len); + } else { + mpack_w(buf, 0xdd); + mpack_w4(buf, len); + } +} + +static char *mpack_array_dyn16(char **buf) +{ + mpack_w(buf, 0xdc); + char *pos = *buf; + mpack_w2(buf, 0xFFEF); + return pos; +} + +static void mpack_str(char **buf, const char *str) +{ + assert(sizeof(schar_T) - 1 < 0x20); + size_t len = STRLEN(str); + mpack_w(buf, 0xa0 | len); + memcpy(*buf, str, len); + *buf += len; +} + void remote_ui_disconnect(uint64_t channel_id) { UI *ui = pmap_get(uint64_t)(&connected_uis, channel_id); @@ -47,9 +135,9 @@ void remote_ui_disconnect(uint64_t channel_id) return; } UIData *data = ui->data; - api_free_array(data->buffer); // Destroy pending screen updates. + kv_destroy(data->call_buf); pmap_del(uint64_t)(&connected_uis, channel_id); - xfree(ui->data); + xfree(data); ui->data = NULL; // Flag UI as "stopped". ui_detach_impl(ui, channel_id); xfree(ui); @@ -159,10 +247,19 @@ void nvim_ui_attach(uint64_t channel_id, Integer width, Integer height, Dictiona UIData *data = xmalloc(sizeof(UIData)); data->channel_id = channel_id; - data->buffer = (Array)ARRAY_DICT_INIT; + data->cur_event = NULL; data->hl_id = 0; data->client_col = -1; + data->nevents_pos = NULL; + data->nevents = 0; + data->flushed_events = false; + data->ncalls_pos = NULL; + data->ncalls = 0; + data->buf_wptr = data->buf; + data->temp_buf = NULL; data->wildmenu_active = false; + data->call_buf = (Array)ARRAY_DICT_INIT; + kv_ensure_space(data->call_buf, 16); ui->data = data; pmap_put(uint64_t)(&connected_uis, channel_id, ui); @@ -433,34 +530,128 @@ void nvim_ui_pum_set_bounds(uint64_t channel_id, Float width, Float height, Floa ui->pum_pos = true; } -/// Pushes data into UI.UIData, to be consumed later by remote_ui_flush(). -static void push_call(UI *ui, const char *name, Array args) +static void flush_event(UIData *data) +{ + if (data->cur_event) { + mpack_w2(&data->ncalls_pos, data->ncalls); + data->cur_event = NULL; + } + if (!data->nevents_pos) { + assert(BUF_POS(data) == 0); + char **buf = &data->buf_wptr; + // [2, "redraw", [...]] + mpack_array(buf, 3); + mpack_uint(buf, 2); + mpack_str(buf, "redraw"); + data->nevents_pos = mpack_array_dyn16(buf); + } +} + +static inline int write_cb(void *vdata, const char *buf, size_t len) +{ + UIData *data = (UIData *)vdata; + if (!buf) { + return 0; + } + + data->pack_totlen += len; + if (!data->temp_buf && UI_BUF_SIZE - BUF_POS(data) < len) { + data->buf_overflow = true; + return 0; + } + + memcpy(data->buf_wptr, buf, len); + data->buf_wptr += len; + + return 0; +} + +static bool prepare_call(UI *ui, const char *name) { - Array call = ARRAY_DICT_INIT; UIData *data = ui->data; - // To optimize data transfer(especially for "put"), we bundle adjacent + if (BUF_POS(data) > UI_BUF_SIZE - EVENT_BUF_SIZE) { + remote_ui_flush_buf(ui); + } + + // To optimize data transfer(especially for "grid_line"), we bundle adjacent // calls to same method together, so only add a new call entry if the last // method call is different from "name" - if (kv_size(data->buffer)) { - call = kv_A(data->buffer, kv_size(data->buffer) - 1).data.array; - } - if (!kv_size(call) || strcmp(kv_A(call, 0).data.string.data, name)) { - call = (Array)ARRAY_DICT_INIT; - ADD(data->buffer, ARRAY_OBJ(call)); - ADD(call, STRING_OBJ(cstr_to_string(name))); + if (!data->cur_event || !strequal(data->cur_event, name)) { + flush_event(data); + data->cur_event = name; + char **buf = &data->buf_wptr; + data->ncalls_pos = mpack_array_dyn16(buf); + mpack_str(buf, name); + data->nevents++; + data->ncalls = 1; + return true; } - ADD(call, ARRAY_OBJ(args)); - kv_A(data->buffer, kv_size(data->buffer) - 1).data.array = call; + return false; +} + +/// Pushes data into UI.UIData, to be consumed later by remote_ui_flush(). +static void push_call(UI *ui, const char *name, Array args) +{ + UIData *data = ui->data; + bool pending = data->nevents_pos; + char *buf_pos_save = data->buf_wptr; + + bool new_event = prepare_call(ui, name); + + msgpack_packer pac; + data->pack_totlen = 0; + data->buf_overflow = false; + msgpack_packer_init(&pac, data, write_cb); + msgpack_rpc_from_array(args, &pac); + if (data->buf_overflow) { + data->buf_wptr = buf_pos_save; + if (new_event) { + data->cur_event = NULL; + data->nevents--; + } + if (pending) { + remote_ui_flush_buf(ui); + } + + if (data->pack_totlen > UI_BUF_SIZE - strlen(name) - 20) { + // TODO(bfredl): manually testable by setting UI_BUF_SIZE to 1024 (mode_info_set) + data->temp_buf = xmalloc(20 + strlen(name) + data->pack_totlen); + data->buf_wptr = data->temp_buf; + char **buf = &data->buf_wptr; + mpack_array(buf, 3); + mpack_uint(buf, 2); + mpack_str(buf, "redraw"); + mpack_array(buf, 1); + mpack_array(buf, 2); + mpack_str(buf, name); + } else { + prepare_call(ui, name); + } + data->pack_totlen = 0; + data->buf_overflow = false; + msgpack_rpc_from_array(args, &pac); + + if (data->temp_buf) { + size_t size = (size_t)(data->buf_wptr - data->temp_buf); + WBuffer *buf = wstream_new_buffer(data->temp_buf, size, 1, xfree); + rpc_write_raw(data->channel_id, buf); + data->temp_buf = NULL; + data->buf_wptr = data->buf; + data->nevents_pos = NULL; + } + } + data->ncalls++; } static void remote_ui_grid_clear(UI *ui, Integer grid) { - Array args = ARRAY_DICT_INIT; + UIData *data = ui->data; + Array args = data->call_buf; if (ui->ui_ext[kUILinegrid]) { - ADD(args, INTEGER_OBJ(grid)); + ADD_C(args, INTEGER_OBJ(grid)); } const char *name = ui->ui_ext[kUILinegrid] ? "grid_clear" : "clear"; push_call(ui, name, args); @@ -468,12 +659,13 @@ static void remote_ui_grid_clear(UI *ui, Integer grid) static void remote_ui_grid_resize(UI *ui, Integer grid, Integer width, Integer height) { - Array args = ARRAY_DICT_INIT; + UIData *data = ui->data; + Array args = data->call_buf; if (ui->ui_ext[kUILinegrid]) { - ADD(args, INTEGER_OBJ(grid)); + ADD_C(args, INTEGER_OBJ(grid)); } - ADD(args, INTEGER_OBJ(width)); - ADD(args, INTEGER_OBJ(height)); + ADD_C(args, INTEGER_OBJ(width)); + ADD_C(args, INTEGER_OBJ(height)); const char *name = ui->ui_ext[kUILinegrid] ? "grid_resize" : "resize"; push_call(ui, name, args); } @@ -481,35 +673,36 @@ static void remote_ui_grid_resize(UI *ui, Integer grid, Integer width, Integer h static void remote_ui_grid_scroll(UI *ui, Integer grid, Integer top, Integer bot, Integer left, Integer right, Integer rows, Integer cols) { + UIData *data = ui->data; if (ui->ui_ext[kUILinegrid]) { - Array args = ARRAY_DICT_INIT; - ADD(args, INTEGER_OBJ(grid)); - ADD(args, INTEGER_OBJ(top)); - ADD(args, INTEGER_OBJ(bot)); - ADD(args, INTEGER_OBJ(left)); - ADD(args, INTEGER_OBJ(right)); - ADD(args, INTEGER_OBJ(rows)); - ADD(args, INTEGER_OBJ(cols)); + Array args = data->call_buf; + ADD_C(args, INTEGER_OBJ(grid)); + ADD_C(args, INTEGER_OBJ(top)); + ADD_C(args, INTEGER_OBJ(bot)); + ADD_C(args, INTEGER_OBJ(left)); + ADD_C(args, INTEGER_OBJ(right)); + ADD_C(args, INTEGER_OBJ(rows)); + ADD_C(args, INTEGER_OBJ(cols)); push_call(ui, "grid_scroll", args); } else { - Array args = ARRAY_DICT_INIT; - ADD(args, INTEGER_OBJ(top)); - ADD(args, INTEGER_OBJ(bot - 1)); - ADD(args, INTEGER_OBJ(left)); - ADD(args, INTEGER_OBJ(right - 1)); + Array args = data->call_buf; + ADD_C(args, INTEGER_OBJ(top)); + ADD_C(args, INTEGER_OBJ(bot - 1)); + ADD_C(args, INTEGER_OBJ(left)); + ADD_C(args, INTEGER_OBJ(right - 1)); push_call(ui, "set_scroll_region", args); - args = (Array)ARRAY_DICT_INIT; - ADD(args, INTEGER_OBJ(rows)); + args = data->call_buf; + ADD_C(args, INTEGER_OBJ(rows)); push_call(ui, "scroll", args); // some clients have "clear" being affected by scroll region, // so reset it. - args = (Array)ARRAY_DICT_INIT; - ADD(args, INTEGER_OBJ(0)); - ADD(args, INTEGER_OBJ(ui->height - 1)); - ADD(args, INTEGER_OBJ(0)); - ADD(args, INTEGER_OBJ(ui->width - 1)); + args = data->call_buf; + ADD_C(args, INTEGER_OBJ(0)); + ADD_C(args, INTEGER_OBJ(ui->height - 1)); + ADD_C(args, INTEGER_OBJ(0)); + ADD_C(args, INTEGER_OBJ(ui->width - 1)); push_call(ui, "set_scroll_region", args); } } @@ -520,26 +713,27 @@ static void remote_ui_default_colors_set(UI *ui, Integer rgb_fg, Integer rgb_bg, if (!ui->ui_ext[kUITermColors]) { HL_SET_DEFAULT_COLORS(rgb_fg, rgb_bg, rgb_sp); } - Array args = ARRAY_DICT_INIT; - ADD(args, INTEGER_OBJ(rgb_fg)); - ADD(args, INTEGER_OBJ(rgb_bg)); - ADD(args, INTEGER_OBJ(rgb_sp)); - ADD(args, INTEGER_OBJ(cterm_fg)); - ADD(args, INTEGER_OBJ(cterm_bg)); + UIData *data = ui->data; + Array args = data->call_buf; + ADD_C(args, INTEGER_OBJ(rgb_fg)); + ADD_C(args, INTEGER_OBJ(rgb_bg)); + ADD_C(args, INTEGER_OBJ(rgb_sp)); + ADD_C(args, INTEGER_OBJ(cterm_fg)); + ADD_C(args, INTEGER_OBJ(cterm_bg)); push_call(ui, "default_colors_set", args); // Deprecated if (!ui->ui_ext[kUILinegrid]) { - args = (Array)ARRAY_DICT_INIT; - ADD(args, INTEGER_OBJ(ui->rgb ? rgb_fg : cterm_fg - 1)); + args = data->call_buf; + ADD_C(args, INTEGER_OBJ(ui->rgb ? rgb_fg : cterm_fg - 1)); push_call(ui, "update_fg", args); - args = (Array)ARRAY_DICT_INIT; - ADD(args, INTEGER_OBJ(ui->rgb ? rgb_bg : cterm_bg - 1)); + args = data->call_buf; + ADD_C(args, INTEGER_OBJ(ui->rgb ? rgb_bg : cterm_bg - 1)); push_call(ui, "update_bg", args); - args = (Array)ARRAY_DICT_INIT; - ADD(args, INTEGER_OBJ(ui->rgb ? rgb_sp : -1)); + args = data->call_buf; + ADD_C(args, INTEGER_OBJ(ui->rgb ? rgb_sp : -1)); push_call(ui, "update_sp", args); } } @@ -550,25 +744,29 @@ static void remote_ui_hl_attr_define(UI *ui, Integer id, HlAttrs rgb_attrs, HlAt if (!ui->ui_ext[kUILinegrid]) { return; } - Array args = ARRAY_DICT_INIT; - ADD(args, INTEGER_OBJ(id)); - ADD(args, DICTIONARY_OBJ(hlattrs2dict(rgb_attrs, true))); - ADD(args, DICTIONARY_OBJ(hlattrs2dict(cterm_attrs, false))); + UIData *data = ui->data; + Array args = data->call_buf; + ADD_C(args, INTEGER_OBJ(id)); + ADD_C(args, DICTIONARY_OBJ(hlattrs2dict(rgb_attrs, true))); + ADD_C(args, DICTIONARY_OBJ(hlattrs2dict(cterm_attrs, false))); if (ui->ui_ext[kUIHlState]) { - ADD(args, ARRAY_OBJ(copy_array(info))); + ADD_C(args, ARRAY_OBJ(info)); } else { - ADD(args, ARRAY_OBJ((Array)ARRAY_DICT_INIT)); + ADD_C(args, ARRAY_OBJ((Array)ARRAY_DICT_INIT)); } push_call(ui, "hl_attr_define", args); + // TODO(bfredl): could be elided + api_free_dictionary(kv_A(args, 1).data.dictionary); + api_free_dictionary(kv_A(args, 2).data.dictionary); } static void remote_ui_highlight_set(UI *ui, int id) { - Array args = ARRAY_DICT_INIT; UIData *data = ui->data; + Array args = data->call_buf; if (data->hl_id == id) { return; @@ -576,18 +774,20 @@ static void remote_ui_highlight_set(UI *ui, int id) data->hl_id = id; Dictionary hl = hlattrs2dict(syn_attr2entry(id), ui->rgb); - ADD(args, DICTIONARY_OBJ(hl)); + ADD_C(args, DICTIONARY_OBJ(hl)); push_call(ui, "highlight_set", args); + api_free_dictionary(kv_A(args, 0).data.dictionary); } /// "true" cursor used only for input focus static void remote_ui_grid_cursor_goto(UI *ui, Integer grid, Integer row, Integer col) { if (ui->ui_ext[kUILinegrid]) { - Array args = ARRAY_DICT_INIT; - ADD(args, INTEGER_OBJ(grid)); - ADD(args, INTEGER_OBJ(row)); - ADD(args, INTEGER_OBJ(col)); + UIData *data = ui->data; + Array args = data->call_buf; + ADD_C(args, INTEGER_OBJ(grid)); + ADD_C(args, INTEGER_OBJ(row)); + ADD_C(args, INTEGER_OBJ(col)); push_call(ui, "grid_cursor_goto", args); } else { UIData *data = ui->data; @@ -606,9 +806,9 @@ static void remote_ui_cursor_goto(UI *ui, Integer row, Integer col) } data->client_row = row; data->client_col = col; - Array args = ARRAY_DICT_INIT; - ADD(args, INTEGER_OBJ(row)); - ADD(args, INTEGER_OBJ(col)); + Array args = data->call_buf; + ADD_C(args, INTEGER_OBJ(row)); + ADD_C(args, INTEGER_OBJ(col)); push_call(ui, "cursor_goto", args); } @@ -616,8 +816,8 @@ static void remote_ui_put(UI *ui, const char *cell) { UIData *data = ui->data; data->client_col++; - Array args = ARRAY_DICT_INIT; - ADD(args, STRING_OBJ(cstr_to_string(cell))); + Array args = data->call_buf; + ADD_C(args, STRING_OBJ(cstr_as_string((char *)cell))); push_call(ui, "put", args); } @@ -627,41 +827,64 @@ static void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startc { UIData *data = ui->data; if (ui->ui_ext[kUILinegrid]) { - Array args = ARRAY_DICT_INIT; - ADD(args, INTEGER_OBJ(grid)); - ADD(args, INTEGER_OBJ(row)); - ADD(args, INTEGER_OBJ(startcol)); - Array cells = ARRAY_DICT_INIT; - int repeat = 0; + prepare_call(ui, "grid_line"); + data->ncalls++; + + char **buf = &data->buf_wptr; + mpack_array(buf, 4); + mpack_uint(buf, (uint32_t)grid); + mpack_uint(buf, (uint32_t)row); + mpack_uint(buf, (uint32_t)startcol); + char *lenpos = mpack_array_dyn16(buf); + + uint32_t repeat = 0; size_t ncells = (size_t)(endcol - startcol); int last_hl = -1; + uint32_t nelem = 0; for (size_t i = 0; i < ncells; i++) { repeat++; if (i == ncells - 1 || attrs[i] != attrs[i + 1] || STRCMP(chunk[i], chunk[i + 1])) { - Array cell = ARRAY_DICT_INIT; - ADD(cell, STRING_OBJ(cstr_to_string((const char *)chunk[i]))); - if (attrs[i] != last_hl || repeat > 1) { - ADD(cell, INTEGER_OBJ(attrs[i])); - last_hl = attrs[i]; + if (UI_BUF_SIZE - BUF_POS(data) < 2 * (1 + 2 + sizeof(schar_T) + 5 + 5)) { + // close to overflowing the redraw buffer. finish this event, + // flush, and start a new "grid_line" event at the current position. + // For simplicity leave place for the final "clear" element + // as well, hence the factor of 2 in the check. + mpack_w2(&lenpos, nelem); + remote_ui_flush_buf(ui); + + prepare_call(ui, "grid_line"); + data->ncalls++; + mpack_array(buf, 4); + mpack_uint(buf, (uint32_t)grid); + mpack_uint(buf, (uint32_t)row); + mpack_uint(buf, (uint32_t)startcol + (uint32_t)i - repeat + 1); + lenpos = mpack_array_dyn16(buf); + nelem = 0; + last_hl = -1; } - if (repeat > 1) { - ADD(cell, INTEGER_OBJ(repeat)); + uint32_t csize = (repeat > 1) ? 3 : ((attrs[i] != last_hl) ? 2 : 1); + nelem++; + mpack_array(buf, csize); + mpack_str(buf, (const char *)chunk[i]); + if (csize >= 2) { + mpack_uint(buf, (uint32_t)attrs[i]); + if (csize >= 3) { + mpack_uint(buf, repeat); + } } - ADD(cells, ARRAY_OBJ(cell)); + last_hl = attrs[i]; repeat = 0; } } if (endcol < clearcol) { - Array cell = ARRAY_DICT_INIT; - ADD(cell, STRING_OBJ(cstr_to_string(" "))); - ADD(cell, INTEGER_OBJ(clearattr)); - ADD(cell, INTEGER_OBJ(clearcol - endcol)); - ADD(cells, ARRAY_OBJ(cell)); + nelem++; + mpack_array(buf, 3); + mpack_str(buf, " "); + mpack_uint(buf, (uint32_t)clearattr); + mpack_uint(buf, (uint32_t)(clearcol - endcol)); } - ADD(args, ARRAY_OBJ(cells)); - - push_call(ui, "grid_line", args); + mpack_w2(&lenpos, nelem); } else { for (int i = 0; i < endcol - startcol; i++) { remote_ui_cursor_goto(ui, row, startcol + i); @@ -688,16 +911,47 @@ static void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startc } } +/// Flush the internal packing buffer to the client. +/// +/// This might happen multiple times before the actual ui_flush, if the +/// total redraw size is large! +static void remote_ui_flush_buf(UI *ui) +{ + UIData *data = ui->data; + if (!data->nevents_pos) { + return; + } + if (data->cur_event) { + flush_event(data); + } + mpack_w2(&data->nevents_pos, data->nevents); + data->nevents = 0; + data->nevents_pos = NULL; + + // TODO(bfredl): elide copy by a length one free-list like the arena + size_t size = BUF_POS(data); + WBuffer *buf = wstream_new_buffer(xmemdup(data->buf, size), size, 1, xfree); + rpc_write_raw(data->channel_id, buf); + data->buf_wptr = data->buf; + // we have sent events to the client, but possibly not yet the final "flush" + // event. + data->flushed_events = true; +} + +/// An intentional flush (vsync) when Nvim is finished redrawing the screen +/// +/// Clients can know this happened by a final "flush" event at the end of the +/// "redraw" batch. static void remote_ui_flush(UI *ui) { UIData *data = ui->data; - if (data->buffer.size > 0) { + if (data->nevents > 0 || data->flushed_events) { if (!ui->ui_ext[kUILinegrid]) { remote_ui_cursor_goto(ui, data->cursor_row, data->cursor_col); } push_call(ui, "flush", (Array)ARRAY_DICT_INIT); - rpc_send_event(data->channel_id, "redraw", data->buffer); - data->buffer = (Array)ARRAY_DICT_INIT; + remote_ui_flush_buf(ui); + data->flushed_events = false; } } @@ -732,7 +986,7 @@ static Array translate_firstarg(UI *ui, Array args) return new_args; } -static void remote_ui_event(UI *ui, char *name, Array args, bool *args_consumed) +static void remote_ui_event(UI *ui, char *name, Array args) { UIData *data = ui->data; if (!ui->ui_ext[kUILinegrid]) { @@ -741,21 +995,24 @@ static void remote_ui_event(UI *ui, char *name, Array args, bool *args_consumed) if (strequal(name, "cmdline_show")) { Array new_args = translate_firstarg(ui, args); push_call(ui, name, new_args); + api_free_array(new_args); return; } else if (strequal(name, "cmdline_block_show")) { - Array new_args = ARRAY_DICT_INIT; + Array new_args = data->call_buf; Array block = args.items[0].data.array; Array new_block = ARRAY_DICT_INIT; for (size_t i = 0; i < block.size; i++) { ADD(new_block, ARRAY_OBJ(translate_contents(ui, block.items[i].data.array))); } - ADD(new_args, ARRAY_OBJ(new_block)); + ADD_C(new_args, ARRAY_OBJ(new_block)); push_call(ui, name, new_args); + api_free_array(new_block); return; } else if (strequal(name, "cmdline_block_append")) { Array new_args = translate_firstarg(ui, args); push_call(ui, name, new_args); + api_free_array(new_args); return; } } @@ -766,18 +1023,19 @@ static void remote_ui_event(UI *ui, char *name, Array args, bool *args_consumed) data->wildmenu_active = (args.items[4].data.integer == -1) || !ui->ui_ext[kUIPopupmenu]; if (data->wildmenu_active) { - Array new_args = ARRAY_DICT_INIT; + Array new_args = data->call_buf; Array items = args.items[0].data.array; Array new_items = ARRAY_DICT_INIT; for (size_t i = 0; i < items.size; i++) { ADD(new_items, copy_object(items.items[i].data.array.items[0])); } - ADD(new_args, ARRAY_OBJ(new_items)); + ADD_C(new_args, ARRAY_OBJ(new_items)); push_call(ui, "wildmenu_show", new_args); + api_free_array(new_items); if (args.items[1].data.integer != -1) { - Array new_args2 = ARRAY_DICT_INIT; - ADD(new_args2, args.items[1]); - push_call(ui, "wildmenu_select", new_args); + Array new_args2 = data->call_buf; + ADD_C(new_args2, args.items[1]); + push_call(ui, "wildmenu_select", new_args2); } return; } @@ -792,18 +1050,7 @@ static void remote_ui_event(UI *ui, char *name, Array args, bool *args_consumed) } } - Array my_args = ARRAY_DICT_INIT; - // Objects are currently single-reference - // make a copy, but only if necessary - if (*args_consumed) { - for (size_t i = 0; i < args.size; i++) { - ADD(my_args, copy_object(args.items[i])); - } - } else { - my_args = args; - *args_consumed = true; - } - push_call(ui, name, my_args); + push_call(ui, name, args); } static void remote_ui_inspect(UI *ui, Dictionary *info) diff --git a/src/nvim/api/ui_events.in.h b/src/nvim/api/ui_events.in.h index 0030f9edf7..8b7e01e1c3 100644 --- a/src/nvim/api/ui_events.in.h +++ b/src/nvim/api/ui_events.in.h @@ -99,7 +99,7 @@ void raw_line(Integer grid, Integer row, Integer startcol, LineFlags flags, const schar_T *chunk, const sattr_T *attrs) FUNC_API_NOEXPORT FUNC_API_COMPOSITOR_IMPL; -void event(char *name, Array args, bool *args_consumed) +void event(char *name, Array args) FUNC_API_NOEXPORT; void win_pos(Integer grid, Window win, Integer startrow, diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 3a24f2b405..b7df1398f5 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -1293,7 +1293,8 @@ void nvim_unsubscribe(uint64_t channel_id, String event) Integer nvim_get_color_by_name(String name) FUNC_API_SINCE(1) { - return name_to_color(name.data); + int dummy; + return name_to_color(name.data, &dummy); } /// Returns a map of color names and RGB values. diff --git a/src/nvim/cursor_shape.c b/src/nvim/cursor_shape.c index 0248c25dfa..c7c7e6ae58 100644 --- a/src/nvim/cursor_shape.c +++ b/src/nvim/cursor_shape.c @@ -43,43 +43,45 @@ cursorentry_T shape_table[SHAPE_IDX_COUNT] = }; /// Converts cursor_shapes into an Array of Dictionaries +/// @param arena initialized arena where memory will be alocated +/// /// @return Array of the form {[ "cursor_shape": ... ], ...} -Array mode_style_array(void) +Array mode_style_array(Arena *arena) { - Array all = ARRAY_DICT_INIT; + Array all = arena_array(arena, SHAPE_IDX_COUNT); for (int i = 0; i < SHAPE_IDX_COUNT; i++) { - Dictionary dic = ARRAY_DICT_INIT; cursorentry_T *cur = &shape_table[i]; + Dictionary dic = arena_dict(arena, 3 + ((cur->used_for & SHAPE_CURSOR) ? 9 : 0)); + PUT_C(dic, "name", STRING_OBJ(cstr_as_string(cur->full_name))); + PUT_C(dic, "short_name", STRING_OBJ(cstr_as_string(cur->name))); if (cur->used_for & SHAPE_MOUSE) { - PUT(dic, "mouse_shape", INTEGER_OBJ(cur->mshape)); + PUT_C(dic, "mouse_shape", INTEGER_OBJ(cur->mshape)); } if (cur->used_for & SHAPE_CURSOR) { String shape_str; switch (cur->shape) { case SHAPE_BLOCK: - shape_str = cstr_to_string("block"); break; + shape_str = cstr_as_string("block"); break; case SHAPE_VER: - shape_str = cstr_to_string("vertical"); break; + shape_str = cstr_as_string("vertical"); break; case SHAPE_HOR: - shape_str = cstr_to_string("horizontal"); break; + shape_str = cstr_as_string("horizontal"); break; default: - shape_str = cstr_to_string("unknown"); + shape_str = cstr_as_string("unknown"); } - PUT(dic, "cursor_shape", STRING_OBJ(shape_str)); - PUT(dic, "cell_percentage", INTEGER_OBJ(cur->percentage)); - PUT(dic, "blinkwait", INTEGER_OBJ(cur->blinkwait)); - PUT(dic, "blinkon", INTEGER_OBJ(cur->blinkon)); - PUT(dic, "blinkoff", INTEGER_OBJ(cur->blinkoff)); - PUT(dic, "hl_id", INTEGER_OBJ(cur->id)); - PUT(dic, "id_lm", INTEGER_OBJ(cur->id_lm)); - PUT(dic, "attr_id", INTEGER_OBJ(cur->id ? syn_id2attr(cur->id) : 0)); - PUT(dic, "attr_id_lm", INTEGER_OBJ(cur->id_lm ? syn_id2attr(cur->id_lm) : 0)); + PUT_C(dic, "cursor_shape", STRING_OBJ(shape_str)); + PUT_C(dic, "cell_percentage", INTEGER_OBJ(cur->percentage)); + PUT_C(dic, "blinkwait", INTEGER_OBJ(cur->blinkwait)); + PUT_C(dic, "blinkon", INTEGER_OBJ(cur->blinkon)); + PUT_C(dic, "blinkoff", INTEGER_OBJ(cur->blinkoff)); + PUT_C(dic, "hl_id", INTEGER_OBJ(cur->id)); + PUT_C(dic, "id_lm", INTEGER_OBJ(cur->id_lm)); + PUT_C(dic, "attr_id", INTEGER_OBJ(cur->id ? syn_id2attr(cur->id) : 0)); + PUT_C(dic, "attr_id_lm", INTEGER_OBJ(cur->id_lm ? syn_id2attr(cur->id_lm) : 0)); } - PUT(dic, "name", STRING_OBJ(cstr_to_string(cur->full_name))); - PUT(dic, "short_name", STRING_OBJ(cstr_to_string(cur->name))); - ADD(all, DICTIONARY_OBJ(dic)); + ADD_C(all, DICTIONARY_OBJ(dic)); } return all; diff --git a/src/nvim/decoration.c b/src/nvim/decoration.c index b1a22b3294..e7c76fe38e 100644 --- a/src/nvim/decoration.c +++ b/src/nvim/decoration.c @@ -69,7 +69,7 @@ void bufhl_add_hl_pos_offset(buf_T *buf, int src_id, int hl_id, lpos_T pos_start void decor_redraw(buf_T *buf, int row1, int row2, Decoration *decor) { if (row2 >= row1) { - if (!decor || decor->hl_id || decor_has_sign(decor)) { + if (!decor || decor->hl_id || decor_has_sign(decor) || decor->conceal) { redraw_buf_range_later(buf, row1 + 1, row2 + 1); } } diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 28b1a7580d..49db5c3716 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -2826,10 +2826,13 @@ int parse_cmd_address(exarg_T *eap, char **errormsg, bool silent) curwin->w_cursor.lnum = eap->line2; // Don't leave the cursor on an illegal line or column, but do - // accept zero as address, so 0;/PATTERN/ works correctly. + // accept zero as address, so 0;/PATTERN/ works correctly + // (where zero usually means to use the first line). // Check the cursor position before returning. if (eap->line2 > 0) { check_cursor(); + } else { + check_cursor_col(); } need_check_cursor = true; } @@ -7272,7 +7275,7 @@ void tabpage_close_other(tabpage_T *tp, int forceit) redraw_tabline = true; if (h != tabline_height()) { - shell_new_rows(); + win_new_screen_rows(); } } diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 3065dc462b..211eeff45e 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -3309,45 +3309,53 @@ draw_cmdline_no_arabicshape: static void ui_ext_cmdline_show(CmdlineInfo *line) { + Arena arena = ARENA_EMPTY; + arena_start(&arena, &ui_ext_fixblk); Array content = ARRAY_DICT_INIT; if (cmdline_star) { + content = arena_array(&arena, 1); size_t len = 0; for (char_u *p = ccline.cmdbuff; *p; MB_PTR_ADV(p)) { len++; } - char *buf = xmallocz(len); + char *buf = arena_alloc(&arena, len, false); memset(buf, '*', len); - Array item = ARRAY_DICT_INIT; - ADD(item, INTEGER_OBJ(0)); - ADD(item, STRING_OBJ(((String) { .data = buf, .size = len }))); - ADD(content, ARRAY_OBJ(item)); + Array item = arena_array(&arena, 2); + ADD_C(item, INTEGER_OBJ(0)); + ADD_C(item, STRING_OBJ(cbuf_as_string(buf, len))); + ADD_C(content, ARRAY_OBJ(item)); } else if (kv_size(line->last_colors.colors)) { + content = arena_array(&arena, kv_size(line->last_colors.colors)); for (size_t i = 0; i < kv_size(line->last_colors.colors); i++) { CmdlineColorChunk chunk = kv_A(line->last_colors.colors, i); - Array item = ARRAY_DICT_INIT; - ADD(item, INTEGER_OBJ(chunk.attr)); + Array item = arena_array(&arena, 2); + ADD_C(item, INTEGER_OBJ(chunk.attr)); assert(chunk.end >= chunk.start); - ADD(item, STRING_OBJ(cbuf_to_string((char *)line->cmdbuff + chunk.start, - (size_t)(chunk.end - chunk.start)))); - ADD(content, ARRAY_OBJ(item)); + ADD_C(item, STRING_OBJ(cbuf_as_string((char *)line->cmdbuff + chunk.start, + (size_t)(chunk.end - chunk.start)))); + ADD_C(content, ARRAY_OBJ(item)); } } else { - Array item = ARRAY_DICT_INIT; - ADD(item, INTEGER_OBJ(0)); - ADD(item, STRING_OBJ(cstr_to_string((char *)(line->cmdbuff)))); - ADD(content, ARRAY_OBJ(item)); + Array item = arena_array(&arena, 2); + ADD_C(item, INTEGER_OBJ(0)); + ADD_C(item, STRING_OBJ(cstr_as_string((char *)(line->cmdbuff)))); + content = arena_array(&arena, 1); + ADD_C(content, ARRAY_OBJ(item)); } + char charbuf[2] = { (char)line->cmdfirstc, 0 }; ui_call_cmdline_show(content, line->cmdpos, - cchar_to_string((char)line->cmdfirstc), - cstr_to_string((char *)(line->cmdprompt)), + cstr_as_string(charbuf), + cstr_as_string((char *)(line->cmdprompt)), line->cmdindent, line->level); if (line->special_char) { - ui_call_cmdline_special_char(cchar_to_string(line->special_char), + charbuf[0] = line->special_char; + ui_call_cmdline_special_char(cstr_as_string(charbuf), line->special_shift, line->level); } + arena_mem_free(arena_finish(&arena), &ui_ext_fixblk); } void ui_ext_cmdline_block_append(size_t indent, const char *line) @@ -3363,9 +3371,9 @@ void ui_ext_cmdline_block_append(size_t indent, const char *line) ADD(content, ARRAY_OBJ(item)); ADD(cmdline_block, ARRAY_OBJ(content)); if (cmdline_block.size > 1) { - ui_call_cmdline_block_append(copy_array(content)); + ui_call_cmdline_block_append(content); } else { - ui_call_cmdline_block_show(copy_array(cmdline_block)); + ui_call_cmdline_block_show(cmdline_block); } } @@ -3385,7 +3393,7 @@ void cmdline_screen_cleared(void) } if (cmdline_block.size) { - ui_call_cmdline_block_show(copy_array(cmdline_block)); + ui_call_cmdline_block_show(cmdline_block); } int prev_level = ccline.level - 1; @@ -3442,7 +3450,8 @@ void putcmdline(char c, int shift) } msg_no_more = false; } else if (ccline.redraw_state != kCmdRedrawAll) { - ui_call_cmdline_special_char(cchar_to_string(c), shift, + char charbuf[2] = { c, 0 }; + ui_call_cmdline_special_char(cstr_as_string(charbuf), shift, ccline.level); } cursorcmd(); diff --git a/src/nvim/extmark.c b/src/nvim/extmark.c index b9e0d18f20..1639f72990 100644 --- a/src/nvim/extmark.c +++ b/src/nvim/extmark.c @@ -68,6 +68,7 @@ void extmark_set(buf_T *buf, uint32_t ns_id, uint32_t *idp, int row, colnr_T col if (decor) { if (kv_size(decor->virt_text) || kv_size(decor->virt_lines) + || decor->conceal || decor_has_sign(decor) || decor->ui_watched) { decor_full = true; diff --git a/src/nvim/generators/gen_api_ui_events.lua b/src/nvim/generators/gen_api_ui_events.lua index 99dfac05e8..93bbaab74c 100755 --- a/src/nvim/generators/gen_api_ui_events.lua +++ b/src/nvim/generators/gen_api_ui_events.lua @@ -36,20 +36,12 @@ local function write_signature(output, ev, prefix, notype) output:write(')') end -local function write_arglist(output, ev, need_copy) - output:write(' Array args = ARRAY_DICT_INIT;\n') +local function write_arglist(output, ev) for j = 1, #ev.parameters do local param = ev.parameters[j] local kind = string.upper(param[1]) - local do_copy = need_copy and (kind == "ARRAY" or kind == "DICTIONARY" or kind == "STRING" or kind == "OBJECT") - output:write(' ADD(args, ') - if do_copy then - output:write('copy_object(') - end + output:write(' ADD_C(args, ') output:write(kind..'_OBJ('..param[2]..')') - if do_copy then - output:write(')') - end output:write(');\n') end end @@ -119,7 +111,9 @@ for i = 1, #events do remote_output:write('static void remote_ui_'..ev.name) write_signature(remote_output, ev, 'UI *ui') remote_output:write('\n{\n') - write_arglist(remote_output, ev, true) + remote_output:write(' UIData *data = ui->data;\n') + remote_output:write(' Array args = data->call_buf;\n') + write_arglist(remote_output, ev) remote_output:write(' push_call(ui, "'..ev.name..'", args);\n') remote_output:write('}\n\n') end @@ -186,9 +180,10 @@ for i = 1, #events do write_signature(call_output, ev, '') call_output:write('\n{\n') if ev.remote_only then - write_arglist(call_output, ev, false) + call_output:write(' Array args = call_buf;\n') + write_arglist(call_output, ev) call_output:write(' UI_LOG('..ev.name..');\n') - call_output:write(' ui_event("'..ev.name..'", args);\n') + call_output:write(' ui_call_event("'..ev.name..'", args);\n') elseif ev.compositor_impl then call_output:write(' UI_CALL') write_signature(call_output, ev, '!ui->composed, '..ev.name..', ui', true) diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index 7f783fd72f..57d25e5c45 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -2122,6 +2122,7 @@ static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth) const bool save_may_garbage_collect = may_garbage_collect; const int save_cursor_row = ui_current_row(); const int save_cursor_col = ui_current_col(); + const handle_T save_cursor_grid = ui_cursor_grid(); const int prev_did_emsg = did_emsg; vgetc_busy = 0; @@ -2135,7 +2136,7 @@ static int handle_mapping(int *keylenp, bool *timedout, int *mapdepth) // The mapping may do anything, but we expect it to take care of // redrawing. Do put the cursor back where it was. - ui_cursor_goto(save_cursor_row, save_cursor_col); + ui_grid_cursor_goto(save_cursor_grid, save_cursor_row, save_cursor_col); ui_flush(); // If an error was displayed and the expression returns an empty diff --git a/src/nvim/hardcopy.c b/src/nvim/hardcopy.c index 00a3c3cdab..aeccac060a 100644 --- a/src/nvim/hardcopy.c +++ b/src/nvim/hardcopy.c @@ -392,7 +392,7 @@ static uint32_t prt_get_color(int hl_id, int modec) const char *color = highlight_color(hl_id, "fg#", 'g'); if (color != NULL) { - RgbValue rgb = name_to_color(color); + RgbValue rgb = name_to_color(color, &colorindex); if (rgb != -1) { return (uint32_t)rgb; } diff --git a/src/nvim/highlight.c b/src/nvim/highlight.c index ee79958034..229da03cb4 100644 --- a/src/nvim/highlight.c +++ b/src/nvim/highlight.c @@ -962,7 +962,8 @@ int object_to_color(Object val, char *key, bool rgb, Error *err) } int color; if (rgb) { - color = name_to_color(str.data); + int dummy; + color = name_to_color(str.data, &dummy); } else { color = name_to_ctermcolor(str.data); } diff --git a/src/nvim/highlight_group.c b/src/nvim/highlight_group.c index 9d61141e98..93d6e65040 100644 --- a/src/nvim/highlight_group.c +++ b/src/nvim/highlight_group.c @@ -30,6 +30,9 @@ // builtin |highlight-groups| static garray_T highlight_ga = GA_EMPTY_INIT_VALUE; +// arena for object with same lifetime as highlight_ga (aka hl_table) +Arena highlight_arena = ARENA_EMPTY; + Map(cstr_t, int) highlight_unames = MAP_INIT; /// The "term", "cterm" and "gui" arguments can be any combination of the @@ -66,21 +69,25 @@ typedef struct { RgbValue sg_rgb_fg; ///< RGB foreground color RgbValue sg_rgb_bg; ///< RGB background color RgbValue sg_rgb_sp; ///< RGB special color - char *sg_rgb_fg_name; ///< RGB foreground color name - char *sg_rgb_bg_name; ///< RGB background color name - char *sg_rgb_sp_name; ///< RGB special color name + int sg_rgb_fg_idx; ///< RGB foreground color index + int sg_rgb_bg_idx; ///< RGB background color index + int sg_rgb_sp_idx; ///< RGB special color index int sg_blend; ///< blend level (0-100 inclusive), -1 if unset } HlGroup; +enum { + kColorIdxNone = -1, + kColorIdxHex = -2, + kColorIdxFg = -3, + kColorIdxBg = -4, +}; + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "highlight_group.c.generated.h" #endif -static inline HlGroup *HL_TABLE(void) -{ - return ((HlGroup *)((highlight_ga.ga_data))); -} +#define hl_table ((HlGroup *)((highlight_ga.ga_data))) // The default highlight groups. These are compiled-in for fast startup and // they still work when the runtime files can't be found. @@ -464,13 +471,13 @@ int highlight_num_groups(void) /// Returns the name of a highlight group. char_u *highlight_group_name(int id) { - return HL_TABLE()[id].sg_name; + return hl_table[id].sg_name; } /// Returns the ID of the link to a highlight group. int highlight_link_id(int id) { - return HL_TABLE()[id].sg_link; + return hl_table[id].sg_link; } /// Create default links for Nvim* highlight groups used for cmdline coloring @@ -673,7 +680,7 @@ void set_hl_group(int id, HlAttrs attrs, Dict(highlight) *dict, int link_id) return; } - HlGroup *g = &HL_TABLE()[idx]; + HlGroup *g = &hl_table[idx]; if (link_id > 0) { g->sg_cleared = false; @@ -698,34 +705,21 @@ void set_hl_group(int id, HlAttrs attrs, Dict(highlight) *dict, int link_id) g->sg_rgb_sp = attrs.rgb_sp_color; struct { - char **dest; RgbValue val; Object name; + int *dest; RgbValue val; Object name; } cattrs[] = { - { &g->sg_rgb_fg_name, g->sg_rgb_fg, HAS_KEY(dict->fg) ? dict->fg : dict->foreground }, - { &g->sg_rgb_bg_name, g->sg_rgb_bg, HAS_KEY(dict->bg) ? dict->bg : dict->background }, - { &g->sg_rgb_sp_name, g->sg_rgb_sp, HAS_KEY(dict->sp) ? dict->sp : dict->special }, + { &g->sg_rgb_fg_idx, g->sg_rgb_fg, HAS_KEY(dict->fg) ? dict->fg : dict->foreground }, + { &g->sg_rgb_bg_idx, g->sg_rgb_bg, HAS_KEY(dict->bg) ? dict->bg : dict->background }, + { &g->sg_rgb_sp_idx, g->sg_rgb_sp, HAS_KEY(dict->sp) ? dict->sp : dict->special }, { NULL, -1, NIL }, }; - char hex_name[8]; - char *name; - for (int j = 0; cattrs[j].dest; j++) { if (cattrs[j].val < 0) { - XFREE_CLEAR(*cattrs[j].dest); - continue; - } - - if (cattrs[j].name.type == kObjectTypeString && cattrs[j].name.data.string.size) { - name = cattrs[j].name.data.string.data; + *cattrs[j].dest = kColorIdxNone; + } else if (cattrs[j].name.type == kObjectTypeString && cattrs[j].name.data.string.size) { + name_to_color(cattrs[j].name.data.string.data, cattrs[j].dest); } else { - snprintf(hex_name, sizeof(hex_name), "#%06x", cattrs[j].val); - name = hex_name; - } - - if (!*cattrs[j].dest - || STRCMP(*cattrs[j].dest, name) != 0) { - xfree(*cattrs[j].dest); - *cattrs[j].dest = xstrdup(name); + *cattrs[j].dest = kColorIdxHex; } } @@ -860,7 +854,7 @@ void do_highlight(const char *line, const bool forceit, const bool init) } if (from_id > 0) { - hlgroup = &HL_TABLE()[from_id - 1]; + hlgroup = &hl_table[from_id - 1]; if (dodefault && (forceit || hlgroup->sg_deflink == 0)) { hlgroup->sg_deflink = to_id; hlgroup->sg_deflink_sctx = current_sctx; @@ -931,19 +925,19 @@ void do_highlight(const char *line, const bool forceit, const bool init) } // Make a copy so we can check if any attribute actually changed - item_before = HL_TABLE()[idx]; - is_normal_group = (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0); + item_before = hl_table[idx]; + is_normal_group = (STRCMP(hl_table[idx].sg_name_u, "NORMAL") == 0); // Clear the highlighting for ":hi clear {group}" and ":hi clear". if (doclear || (forceit && init)) { highlight_clear(idx); if (!doclear) { - HL_TABLE()[idx].sg_set = 0; + hl_table[idx].sg_set = 0; } } - char *key = NULL; - char *arg = NULL; + char key[64]; + char arg[512]; if (!doclear) { while (!ends_excmd((uint8_t)(*linep))) { key_start = linep; @@ -958,15 +952,21 @@ void do_highlight(const char *line, const bool forceit, const bool init) while (*linep && !ascii_iswhite(*linep) && *linep != '=') { linep++; } - xfree(key); - key = (char *)vim_strnsave_up((const char_u *)key_start, - (size_t)(linep - key_start)); + size_t key_len = (size_t)(linep - key_start); + if (key_len > sizeof key - 1) { + semsg(_("E423: Illegal argument")); + error = true; + break; + } + memcpy(key, key_start, key_len); + key[key_len] = NUL; + vim_strup((char_u *)key); linep = (const char *)skipwhite(linep); if (strcmp(key, "NONE") == 0) { - if (!init || HL_TABLE()[idx].sg_set == 0) { + if (!init || hl_table[idx].sg_set == 0) { if (!init) { - HL_TABLE()[idx].sg_set |= SG_CTERM + SG_GUI; + hl_table[idx].sg_set |= SG_CTERM + SG_GUI; } highlight_clear(idx); } @@ -1000,8 +1000,14 @@ void do_highlight(const char *line, const bool forceit, const bool init) error = true; break; } - xfree(arg); - arg = xstrndup(arg_start, (size_t)(linep - arg_start)); + size_t arg_len = (size_t)(linep - arg_start); + if (arg_len > sizeof arg - 1) { + semsg(_("E423: Illegal argument")); + error = true; + break; + } + memcpy(arg, arg_start, arg_len); + arg[arg_len] = NUL; if (*linep == '\'') { linep++; @@ -1036,34 +1042,34 @@ void do_highlight(const char *line, const bool forceit, const bool init) break; } if (*key == 'C') { - if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM)) { + if (!init || !(hl_table[idx].sg_set & SG_CTERM)) { if (!init) { - HL_TABLE()[idx].sg_set |= SG_CTERM; + hl_table[idx].sg_set |= SG_CTERM; } - HL_TABLE()[idx].sg_cterm = attr; - HL_TABLE()[idx].sg_cterm_bold = false; + hl_table[idx].sg_cterm = attr; + hl_table[idx].sg_cterm_bold = false; } } else if (*key == 'G') { - if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI)) { + if (!init || !(hl_table[idx].sg_set & SG_GUI)) { if (!init) { - HL_TABLE()[idx].sg_set |= SG_GUI; + hl_table[idx].sg_set |= SG_GUI; } - HL_TABLE()[idx].sg_gui = attr; + hl_table[idx].sg_gui = attr; } } } else if (STRCMP(key, "FONT") == 0) { // in non-GUI fonts are simply ignored } else if (STRCMP(key, "CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0) { - if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM)) { + if (!init || !(hl_table[idx].sg_set & SG_CTERM)) { if (!init) { - HL_TABLE()[idx].sg_set |= SG_CTERM; + hl_table[idx].sg_set |= SG_CTERM; } // When setting the foreground color, and previously the "bold" // flag was set for a light color, reset it now - if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold) { - HL_TABLE()[idx].sg_cterm &= ~HL_BOLD; - HL_TABLE()[idx].sg_cterm_bold = false; + if (key[5] == 'F' && hl_table[idx].sg_cterm_bold) { + hl_table[idx].sg_cterm &= ~HL_BOLD; + hl_table[idx].sg_cterm_bold = false; } if (ascii_isdigit(*arg)) { @@ -1107,21 +1113,21 @@ void do_highlight(const char *line, const bool forceit, const bool init) // set/reset bold attribute to get light foreground // colors (on some terminals, e.g. "linux") if (bold == kTrue) { - HL_TABLE()[idx].sg_cterm |= HL_BOLD; - HL_TABLE()[idx].sg_cterm_bold = true; + hl_table[idx].sg_cterm |= HL_BOLD; + hl_table[idx].sg_cterm_bold = true; } else if (bold == kFalse) { - HL_TABLE()[idx].sg_cterm &= ~HL_BOLD; + hl_table[idx].sg_cterm &= ~HL_BOLD; } } // Add one to the argument, to avoid zero. Zero is used for // "NONE", then "color" is -1. if (key[5] == 'F') { - HL_TABLE()[idx].sg_cterm_fg = color + 1; + hl_table[idx].sg_cterm_fg = color + 1; if (is_normal_group) { cterm_normal_fg_color = color + 1; } } else { - HL_TABLE()[idx].sg_cterm_bg = color + 1; + hl_table[idx].sg_cterm_bg = color + 1; if (is_normal_group) { cterm_normal_bg_color = color + 1; if (!ui_rgb_attached()) { @@ -1148,95 +1154,94 @@ void do_highlight(const char *line, const bool forceit, const bool init) } } } else if (strcmp(key, "GUIFG") == 0) { - char **namep = &HL_TABLE()[idx].sg_rgb_fg_name; + int *indexp = &hl_table[idx].sg_rgb_fg_idx; - if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI)) { + if (!init || !(hl_table[idx].sg_set & SG_GUI)) { if (!init) { - HL_TABLE()[idx].sg_set |= SG_GUI; + hl_table[idx].sg_set |= SG_GUI; } - if (*namep == NULL || STRCMP(*namep, arg) != 0) { - xfree(*namep); - if (strcmp(arg, "NONE") != 0) { - *namep = xstrdup(arg); - HL_TABLE()[idx].sg_rgb_fg = name_to_color(arg); - } else { - *namep = NULL; - HL_TABLE()[idx].sg_rgb_fg = -1; - } - did_change = true; + RgbValue old_color = hl_table[idx].sg_rgb_fg; + int old_idx = hl_table[idx].sg_rgb_fg_idx; + + if (strcmp(arg, "NONE") != 0) { + hl_table[idx].sg_rgb_fg = name_to_color(arg, indexp); + } else { + hl_table[idx].sg_rgb_fg = -1; + hl_table[idx].sg_rgb_fg_idx = kColorIdxNone; } + + did_change = hl_table[idx].sg_rgb_fg != old_color || hl_table[idx].sg_rgb_fg != old_idx; } if (is_normal_group) { - normal_fg = HL_TABLE()[idx].sg_rgb_fg; + normal_fg = hl_table[idx].sg_rgb_fg; } } else if (STRCMP(key, "GUIBG") == 0) { - char **const namep = &HL_TABLE()[idx].sg_rgb_bg_name; + int *indexp = &hl_table[idx].sg_rgb_bg_idx; - if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI)) { + if (!init || !(hl_table[idx].sg_set & SG_GUI)) { if (!init) { - HL_TABLE()[idx].sg_set |= SG_GUI; + hl_table[idx].sg_set |= SG_GUI; } - if (*namep == NULL || STRCMP(*namep, arg) != 0) { - xfree(*namep); - if (STRCMP(arg, "NONE") != 0) { - *namep = xstrdup(arg); - HL_TABLE()[idx].sg_rgb_bg = name_to_color(arg); - } else { - *namep = NULL; - HL_TABLE()[idx].sg_rgb_bg = -1; - } - did_change = true; + RgbValue old_color = hl_table[idx].sg_rgb_bg; + int old_idx = hl_table[idx].sg_rgb_bg_idx; + + if (STRCMP(arg, "NONE") != 0) { + hl_table[idx].sg_rgb_bg = name_to_color(arg, indexp); + } else { + hl_table[idx].sg_rgb_bg = -1; + hl_table[idx].sg_rgb_bg_idx = kColorIdxNone; } + + did_change = hl_table[idx].sg_rgb_bg != old_color || hl_table[idx].sg_rgb_bg != old_idx; } if (is_normal_group) { - normal_bg = HL_TABLE()[idx].sg_rgb_bg; + normal_bg = hl_table[idx].sg_rgb_bg; } } else if (strcmp(key, "GUISP") == 0) { - char **const namep = &HL_TABLE()[idx].sg_rgb_sp_name; + int *indexp = &hl_table[idx].sg_rgb_sp_idx; - if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI)) { + if (!init || !(hl_table[idx].sg_set & SG_GUI)) { if (!init) { - HL_TABLE()[idx].sg_set |= SG_GUI; + hl_table[idx].sg_set |= SG_GUI; } - if (*namep == NULL || STRCMP(*namep, arg) != 0) { - xfree(*namep); - if (strcmp(arg, "NONE") != 0) { - *namep = xstrdup(arg); - HL_TABLE()[idx].sg_rgb_sp = name_to_color(arg); - } else { - *namep = NULL; - HL_TABLE()[idx].sg_rgb_sp = -1; - } - did_change = true; + RgbValue old_color = hl_table[idx].sg_rgb_sp; + int old_idx = hl_table[idx].sg_rgb_sp_idx; + + if (strcmp(arg, "NONE") != 0) { + hl_table[idx].sg_rgb_sp = name_to_color(arg, indexp); + } else { + hl_table[idx].sg_rgb_sp = -1; } + + did_change = hl_table[idx].sg_rgb_sp != old_color || hl_table[idx].sg_rgb_sp != old_idx; } if (is_normal_group) { - normal_sp = HL_TABLE()[idx].sg_rgb_sp; + normal_sp = hl_table[idx].sg_rgb_sp; } } else if (strcmp(key, "START") == 0 || strcmp(key, "STOP") == 0) { // Ignored for now } else if (strcmp(key, "BLEND") == 0) { if (strcmp(arg, "NONE") != 0) { - HL_TABLE()[idx].sg_blend = (int)strtol(arg, NULL, 10); + hl_table[idx].sg_blend = (int)strtol(arg, NULL, 10); } else { - HL_TABLE()[idx].sg_blend = -1; + hl_table[idx].sg_blend = -1; } } else { semsg(_("E423: Illegal argument: %s"), key_start); error = true; break; } - HL_TABLE()[idx].sg_cleared = false; + hl_table[idx].sg_cleared = false; // When highlighting has been given for a group, don't link it. - if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK)) { - HL_TABLE()[idx].sg_link = 0; + if (!init || !(hl_table[idx].sg_set & SG_LINK)) { + hl_table[idx].sg_link = 0; } // Continue with next argument. @@ -1244,40 +1249,33 @@ void do_highlight(const char *line, const bool forceit, const bool init) } } - // If there is an error, and it's a new entry, remove it from the table. - if (error && idx == highlight_ga.ga_len) { - syn_unadd_group(); - } else { - if (!error && is_normal_group) { - // Need to update all groups, because they might be using "bg" and/or - // "fg", which have been changed now. - highlight_attr_set_all(); - - if (!ui_has(kUILinegrid) && starting == 0) { - // Older UIs assume that we clear the screen after normal group is - // changed - ui_refresh(); - } else { - // TUI and newer UIs will repaint the screen themselves. NOT_VALID - // redraw below will still handle usages of guibg=fg etc. - ui_default_colors_set(); - } - did_highlight_changed = true; - redraw_all_later(NOT_VALID); + if (!error && is_normal_group) { + // Need to update all groups, because they might be using "bg" and/or + // "fg", which have been changed now. + highlight_attr_set_all(); + + if (!ui_has(kUILinegrid) && starting == 0) { + // Older UIs assume that we clear the screen after normal group is + // changed + ui_refresh(); } else { - set_hl_attr(idx); + // TUI and newer UIs will repaint the screen themselves. NOT_VALID + // redraw below will still handle usages of guibg=fg etc. + ui_default_colors_set(); } - HL_TABLE()[idx].sg_script_ctx = current_sctx; - HL_TABLE()[idx].sg_script_ctx.sc_lnum += sourcing_lnum; - nlua_set_sctx(&HL_TABLE()[idx].sg_script_ctx); + did_highlight_changed = true; + redraw_all_later(NOT_VALID); + } else { + set_hl_attr(idx); } - xfree(key); - xfree(arg); + hl_table[idx].sg_script_ctx = current_sctx; + hl_table[idx].sg_script_ctx.sc_lnum += sourcing_lnum; + nlua_set_sctx(&hl_table[idx].sg_script_ctx); // Only call highlight_changed() once, after a sequence of highlight // commands, and only if an attribute actually changed if ((did_change - || memcmp(&HL_TABLE()[idx], &item_before, sizeof(item_before)) != 0) + || memcmp(&hl_table[idx], &item_before, sizeof(item_before)) != 0) && !did_highlight_changed) { // Do not trigger a redraw when highlighting is changed while // redrawing. This may happen when evaluating 'statusline' changes the @@ -1292,13 +1290,9 @@ void do_highlight(const char *line, const bool forceit, const bool init) #if defined(EXITFREE) void free_highlight(void) { - for (int i = 0; i < highlight_ga.ga_len; i++) { - highlight_clear(i); - xfree(HL_TABLE()[i].sg_name); - xfree(HL_TABLE()[i].sg_name_u); - } ga_clear(&highlight_ga); map_destroy(cstr_t, int)(&highlight_unames); + arena_mem_free(arena_finish(&highlight_arena), NULL); } #endif @@ -1319,39 +1313,39 @@ void restore_cterm_colors(void) /// @return TRUE if highlight group "idx" has any settings. static int hl_has_settings(int idx, bool check_link) { - return HL_TABLE()[idx].sg_cleared == 0 - && (HL_TABLE()[idx].sg_attr != 0 - || HL_TABLE()[idx].sg_cterm_fg != 0 - || HL_TABLE()[idx].sg_cterm_bg != 0 - || HL_TABLE()[idx].sg_rgb_fg_name != NULL - || HL_TABLE()[idx].sg_rgb_bg_name != NULL - || HL_TABLE()[idx].sg_rgb_sp_name != NULL - || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK))); + return hl_table[idx].sg_cleared == 0 + && (hl_table[idx].sg_attr != 0 + || hl_table[idx].sg_cterm_fg != 0 + || hl_table[idx].sg_cterm_bg != 0 + || hl_table[idx].sg_rgb_fg_idx != kColorIdxNone + || hl_table[idx].sg_rgb_bg_idx != kColorIdxNone + || hl_table[idx].sg_rgb_sp_idx != kColorIdxNone + || (check_link && (hl_table[idx].sg_set & SG_LINK))); } /// Clear highlighting for one group. static void highlight_clear(int idx) { - HL_TABLE()[idx].sg_cleared = true; - - HL_TABLE()[idx].sg_attr = 0; - HL_TABLE()[idx].sg_cterm = 0; - HL_TABLE()[idx].sg_cterm_bold = false; - HL_TABLE()[idx].sg_cterm_fg = 0; - HL_TABLE()[idx].sg_cterm_bg = 0; - HL_TABLE()[idx].sg_gui = 0; - HL_TABLE()[idx].sg_rgb_fg = -1; - HL_TABLE()[idx].sg_rgb_bg = -1; - HL_TABLE()[idx].sg_rgb_sp = -1; - XFREE_CLEAR(HL_TABLE()[idx].sg_rgb_fg_name); - XFREE_CLEAR(HL_TABLE()[idx].sg_rgb_bg_name); - XFREE_CLEAR(HL_TABLE()[idx].sg_rgb_sp_name); - HL_TABLE()[idx].sg_blend = -1; + hl_table[idx].sg_cleared = true; + + hl_table[idx].sg_attr = 0; + hl_table[idx].sg_cterm = 0; + hl_table[idx].sg_cterm_bold = false; + hl_table[idx].sg_cterm_fg = 0; + hl_table[idx].sg_cterm_bg = 0; + hl_table[idx].sg_gui = 0; + hl_table[idx].sg_rgb_fg = -1; + hl_table[idx].sg_rgb_bg = -1; + hl_table[idx].sg_rgb_sp = -1; + hl_table[idx].sg_rgb_fg_idx = kColorIdxNone; + hl_table[idx].sg_rgb_bg_idx = kColorIdxNone; + hl_table[idx].sg_rgb_sp_idx = kColorIdxNone; + hl_table[idx].sg_blend = -1; // Restore default link and context if they exist. Otherwise clears. - HL_TABLE()[idx].sg_link = HL_TABLE()[idx].sg_deflink; + hl_table[idx].sg_link = hl_table[idx].sg_deflink; // Since we set the default link, set the location to where the default // link was set. - HL_TABLE()[idx].sg_script_ctx = HL_TABLE()[idx].sg_deflink_sctx; + hl_table[idx].sg_script_ctx = hl_table[idx].sg_deflink_sctx; } /// \addtogroup LIST_XXX @@ -1363,7 +1357,7 @@ static void highlight_clear(int idx) static void highlight_list_one(const int id) { - const HlGroup *sgp = &HL_TABLE()[id - 1]; // index is ID minus one + const HlGroup *sgp = &hl_table[id - 1]; // index is ID minus one bool didh = false; if (message_filtered(sgp->sg_name)) { @@ -1379,12 +1373,13 @@ static void highlight_list_one(const int id) didh = highlight_list_arg(id, didh, LIST_ATTR, sgp->sg_gui, NULL, "gui"); - didh = highlight_list_arg(id, didh, LIST_STRING, - 0, sgp->sg_rgb_fg_name, "guifg"); - didh = highlight_list_arg(id, didh, LIST_STRING, - 0, sgp->sg_rgb_bg_name, "guibg"); - didh = highlight_list_arg(id, didh, LIST_STRING, - 0, sgp->sg_rgb_sp_name, "guisp"); + char hexbuf[8]; + didh = highlight_list_arg(id, didh, LIST_STRING, 0, + coloridx_to_name(sgp->sg_rgb_fg_idx, sgp->sg_rgb_fg, hexbuf), "guifg"); + didh = highlight_list_arg(id, didh, LIST_STRING, 0, + coloridx_to_name(sgp->sg_rgb_bg_idx, sgp->sg_rgb_bg, hexbuf), "guibg"); + didh = highlight_list_arg(id, didh, LIST_STRING, 0, + coloridx_to_name(sgp->sg_rgb_sp_idx, sgp->sg_rgb_sp, hexbuf), "guisp"); didh = highlight_list_arg(id, didh, LIST_INT, sgp->sg_blend + 1, NULL, "blend"); @@ -1394,7 +1389,7 @@ static void highlight_list_one(const int id) didh = true; msg_puts_attr("links to", HL_ATTR(HLF_D)); msg_putchar(' '); - msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name); + msg_outtrans(hl_table[hl_table[id - 1].sg_link - 1].sg_name); } if (!didh) { @@ -1410,11 +1405,11 @@ Dictionary get_global_hl_defs(void) Dictionary rv = ARRAY_DICT_INIT; for (int i = 1; i <= highlight_ga.ga_len && !got_int; i++) { Dictionary attrs = ARRAY_DICT_INIT; - HlGroup *h = &HL_TABLE()[i - 1]; + HlGroup *h = &hl_table[i - 1]; if (h->sg_attr > 0) { attrs = hlattrs2dict(syn_attr2entry(h->sg_attr), true); } else if (h->sg_link > 0) { - const char *link = (const char *)HL_TABLE()[h->sg_link - 1].sg_name; + const char *link = (const char *)hl_table[h->sg_link - 1].sg_name; PUT(attrs, "link", STRING_OBJ(cstr_to_string(link))); } PUT(rv, (const char *)h->sg_name, DICTIONARY_OBJ(attrs)); @@ -1428,7 +1423,7 @@ Dictionary get_global_hl_defs(void) /// @param type one of \ref LIST_XXX /// @param iarg integer argument used if \p type == LIST_INT /// @param sarg string used if \p type == LIST_STRING -static bool highlight_list_arg(const int id, bool didh, const int type, int iarg, char *const sarg, +static bool highlight_list_arg(const int id, bool didh, const int type, int iarg, const char *sarg, const char *const name) { char buf[100]; @@ -1437,7 +1432,7 @@ static bool highlight_list_arg(const int id, bool didh, const int type, int iarg return false; } if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0)) { - char *ts = buf; + const char *ts = buf; if (type == LIST_INT) { snprintf((char *)buf, sizeof(buf), "%d", iarg - 1); } else if (type == LIST_STRING) { @@ -1485,9 +1480,9 @@ const char *highlight_has_attr(const int id, const int flag, const int modec) } if (modec == 'g') { - attr = HL_TABLE()[id - 1].sg_gui; + attr = hl_table[id - 1].sg_gui; } else { - attr = HL_TABLE()[id - 1].sg_cterm; + attr = hl_table[id - 1].sg_cterm; } return (attr & flag) ? "1" : NULL; @@ -1528,11 +1523,11 @@ const char *highlight_color(const int id, const char *const what, const int mode if (modec == 'g') { if (what[2] == '#' && ui_rgb_attached()) { if (fg) { - n = HL_TABLE()[id - 1].sg_rgb_fg; + n = hl_table[id - 1].sg_rgb_fg; } else if (sp) { - n = HL_TABLE()[id - 1].sg_rgb_sp; + n = hl_table[id - 1].sg_rgb_sp; } else { - n = HL_TABLE()[id - 1].sg_rgb_bg; + n = hl_table[id - 1].sg_rgb_bg; } if (n < 0 || n > 0xffffff) { return NULL; @@ -1541,21 +1536,21 @@ const char *highlight_color(const int id, const char *const what, const int mode return name; } if (fg) { - return (const char *)HL_TABLE()[id - 1].sg_rgb_fg_name; - } - if (sp) { - return (const char *)HL_TABLE()[id - 1].sg_rgb_sp_name; + return coloridx_to_name(hl_table[id - 1].sg_rgb_fg_idx, hl_table[id - 1].sg_rgb_fg, name); + } else if (sp) { + return coloridx_to_name(hl_table[id - 1].sg_rgb_sp_idx, hl_table[id - 1].sg_rgb_sp, name); + } else { + return coloridx_to_name(hl_table[id - 1].sg_rgb_bg_idx, hl_table[id - 1].sg_rgb_bg, name); } - return (const char *)HL_TABLE()[id - 1].sg_rgb_bg_name; } if (font || sp) { return NULL; } if (modec == 'c') { if (fg) { - n = HL_TABLE()[id - 1].sg_cterm_fg - 1; + n = hl_table[id - 1].sg_cterm_fg - 1; } else { - n = HL_TABLE()[id - 1].sg_cterm_bg - 1; + n = hl_table[id - 1].sg_cterm_bg - 1; } if (n < 0) { return NULL; @@ -1586,7 +1581,7 @@ bool syn_list_header(const bool did_header, const int outlen, const int id, bool if (got_int) { return true; } - msg_outtrans(HL_TABLE()[id - 1].sg_name); + msg_outtrans(hl_table[id - 1].sg_name); name_col = msg_col; endcol = 15; } else if ((ui_has(kUIMessages) || msg_silent) && !force_newline) { @@ -1630,7 +1625,7 @@ bool syn_list_header(const bool did_header, const int outlen, const int id, bool static void set_hl_attr(int idx) { HlAttrs at_en = HLATTRS_INIT; - HlGroup *sgp = HL_TABLE() + idx; + HlGroup *sgp = hl_table + idx; at_en.cterm_ae_attr = (int16_t)sgp->sg_cterm; at_en.cterm_fg_color = sgp->sg_cterm_fg; @@ -1639,9 +1634,9 @@ static void set_hl_attr(int idx) // FIXME(tarruda): The "unset value" for rgb is -1, but since hlgroup is // initialized with 0(by garray functions), check for sg_rgb_{f,b}g_name // before setting attr_entry->{f,g}g_color to a other than -1 - at_en.rgb_fg_color = sgp->sg_rgb_fg_name ? sgp->sg_rgb_fg : -1; - at_en.rgb_bg_color = sgp->sg_rgb_bg_name ? sgp->sg_rgb_bg : -1; - at_en.rgb_sp_color = sgp->sg_rgb_sp_name ? sgp->sg_rgb_sp : -1; + at_en.rgb_fg_color = sgp->sg_rgb_fg_idx != kColorIdxNone ? sgp->sg_rgb_fg : -1; + at_en.rgb_bg_color = sgp->sg_rgb_bg_idx != kColorIdxNone ? sgp->sg_rgb_bg : -1; + at_en.rgb_sp_color = sgp->sg_rgb_sp_idx != kColorIdxNone ? sgp->sg_rgb_sp : -1; at_en.hl_blend = sgp->sg_blend; sgp->sg_attr = hl_get_syn_attr(0, idx + 1, at_en); @@ -1708,7 +1703,7 @@ char_u *syn_id2name(int id) if (id <= 0 || id > highlight_ga.ga_len) { return (char_u *)""; } - return HL_TABLE()[id - 1].sg_name; + return hl_table[id - 1].sg_name; } /// Find highlight group name in the table and return its ID. @@ -1726,7 +1721,7 @@ int syn_check_group(const char *name, size_t len) } int id = syn_name2id_len((char_u *)name, len); if (id == 0) { // doesn't exist yet - return syn_add_group(vim_strnsave((char_u *)name, len)); + return syn_add_group(name, len); } return id; } @@ -1735,18 +1730,16 @@ int syn_check_group(const char *name, size_t len) /// /// @param name must be an allocated string, it will be consumed. /// @return 0 for failure, else the allocated group id -/// @see syn_check_group syn_unadd_group -static int syn_add_group(char_u *name) +/// @see syn_check_group +static int syn_add_group(const char *name, size_t len) { - char_u *p; - // Check that the name is ASCII letters, digits and underscore. - for (p = name; *p != NUL; p++) { - if (!vim_isprintc(*p)) { + for (size_t i = 0; i < len; i++) { + int c = (int8_t)name[i]; + if (!vim_isprintc(c)) { emsg(_("E669: Unprintable character in group name")); - xfree(name); return 0; - } else if (!ASCII_ISALNUM(*p) && *p != '_') { + } else if (!ASCII_ISALNUM(c) && c != '_') { // This is an error, but since there previously was no check only give a warning. msg_source(HL_ATTR(HLF_W)); msg(_("W18: Invalid character in group name")); @@ -1762,46 +1755,36 @@ static int syn_add_group(char_u *name) if (highlight_ga.ga_len >= MAX_HL_ID) { emsg(_("E849: Too many highlight and syntax groups")); - xfree(name); return 0; } - char *const name_up = (char *)vim_strsave_up(name); - // Append another syntax_highlight entry. HlGroup *hlgp = GA_APPEND_VIA_PTR(HlGroup, &highlight_ga); memset(hlgp, 0, sizeof(*hlgp)); - hlgp->sg_name = name; + hlgp->sg_name = (char_u *)arena_memdupz(&highlight_arena, name, len); hlgp->sg_rgb_bg = -1; hlgp->sg_rgb_fg = -1; hlgp->sg_rgb_sp = -1; + hlgp->sg_rgb_bg_idx = kColorIdxNone; + hlgp->sg_rgb_fg_idx = kColorIdxNone; + hlgp->sg_rgb_sp_idx = kColorIdxNone; hlgp->sg_blend = -1; - hlgp->sg_name_u = name_up; + hlgp->sg_name_u = arena_memdupz(&highlight_arena, name, len); + vim_strup((char_u *)hlgp->sg_name_u); int id = highlight_ga.ga_len; // ID is index plus one - map_put(cstr_t, int)(&highlight_unames, name_up, id); + map_put(cstr_t, int)(&highlight_unames, hlgp->sg_name_u, id); return id; } -/// When, just after calling syn_add_group(), an error is discovered, this -/// function deletes the new name. -static void syn_unadd_group(void) -{ - highlight_ga.ga_len--; - HlGroup *item = &HL_TABLE()[highlight_ga.ga_len]; - map_del(cstr_t, int)(&highlight_unames, item->sg_name_u); - xfree(item->sg_name); - xfree(item->sg_name_u); -} - /// Translate a group ID to highlight attributes. /// @see syn_attr2entry int syn_id2attr(int hl_id) { hl_id = syn_get_final_id(hl_id); - HlGroup *sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one + HlGroup *sgp = &hl_table[hl_id - 1]; // index is ID minus one int attr = ns_get_hl(-1, hl_id, false, sgp->sg_set); if (attr >= 0) { @@ -1822,7 +1805,7 @@ int syn_get_final_id(int hl_id) // Follow links until there is no more. // Look out for loops! Break after 100 links. for (count = 100; --count >= 0;) { - HlGroup *sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one + HlGroup *sgp = &hl_table[hl_id - 1]; // index is ID minus one // ACHTUNG: when using "tmp" attribute (no link) the function might be // called twice. it needs be smart enough to remember attr only to @@ -1848,15 +1831,21 @@ int syn_get_final_id(int hl_id) void highlight_attr_set_all(void) { for (int idx = 0; idx < highlight_ga.ga_len; idx++) { - HlGroup *sgp = &HL_TABLE()[idx]; - if (sgp->sg_rgb_bg_name != NULL) { - sgp->sg_rgb_bg = name_to_color(sgp->sg_rgb_bg_name); + HlGroup *sgp = &hl_table[idx]; + if (sgp->sg_rgb_bg_idx == kColorIdxFg) { + sgp->sg_rgb_bg = normal_fg; + } else if (sgp->sg_rgb_bg_idx == kColorIdxBg) { + sgp->sg_rgb_bg = normal_bg; } - if (sgp->sg_rgb_fg_name != NULL) { - sgp->sg_rgb_fg = name_to_color(sgp->sg_rgb_fg_name); + if (sgp->sg_rgb_fg_idx == kColorIdxFg) { + sgp->sg_rgb_fg = normal_fg; + } else if (sgp->sg_rgb_fg_idx == kColorIdxBg) { + sgp->sg_rgb_fg = normal_bg; } - if (sgp->sg_rgb_sp_name != NULL) { - sgp->sg_rgb_sp = name_to_color(sgp->sg_rgb_sp_name); + if (sgp->sg_rgb_sp_idx == kColorIdxFg) { + sgp->sg_rgb_sp = normal_fg; + } else if (sgp->sg_rgb_sp_idx == kColorIdxBg) { + sgp->sg_rgb_sp = normal_bg; } set_hl_attr(idx); } @@ -1866,7 +1855,7 @@ void highlight_attr_set_all(void) static void combine_stl_hlt(int id, int id_S, int id_alt, int hlcnt, int i, int hlf, int *table) FUNC_ATTR_NONNULL_ALL { - HlGroup *const hlt = HL_TABLE(); + HlGroup *const hlt = hl_table; if (id_alt == 0) { memset(&hlt[hlcnt + i], 0, sizeof(HlGroup)); @@ -1951,7 +1940,7 @@ void highlight_changed(void) hlcnt = highlight_ga.ga_len; if (id_S == -1) { // Make sure id_S is always valid to simplify code below. Use the last entry - memset(&HL_TABLE()[hlcnt + 9], 0, sizeof(HlGroup)); + memset(&hl_table[hlcnt + 9], 0, sizeof(HlGroup)); id_S = hlcnt + 10; } for (int i = 0; i < 9; i++) { @@ -2048,7 +2037,7 @@ const char *get_highlight_name_ext(expand_T *xp, int idx, bool skip_cleared) } // Items are never removed from the table, skip the ones that were cleared. - if (skip_cleared && idx < highlight_ga.ga_len && HL_TABLE()[idx].sg_cleared) { + if (skip_cleared && idx < highlight_ga.ga_len && hl_table[idx].sg_cleared) { return ""; } @@ -2066,7 +2055,7 @@ const char *get_highlight_name_ext(expand_T *xp, int idx, bool skip_cleared) } else if (idx >= highlight_ga.ga_len) { return NULL; } - return (const char *)HL_TABLE()[idx].sg_name; + return (const char *)hl_table[idx].sg_name; } color_name_table_T color_name_table[] = { @@ -2758,29 +2747,55 @@ color_name_table_T color_name_table[] = { /// hex value /// /// @param[in] name string value to convert to RGB +/// @param[out] idx index in color table or special value /// return the hex value or -1 if could not find a correct value -RgbValue name_to_color(const char *name) +RgbValue name_to_color(const char *name, int *idx) { if (name[0] == '#' && isxdigit(name[1]) && isxdigit(name[2]) && isxdigit(name[3]) && isxdigit(name[4]) && isxdigit(name[5]) && isxdigit(name[6]) && name[7] == NUL) { // rgb hex string + *idx = kColorIdxHex; return (RgbValue)strtol((char *)(name + 1), NULL, 16); } else if (!STRICMP(name, "bg") || !STRICMP(name, "background")) { + *idx = kColorIdxBg; return normal_bg; } else if (!STRICMP(name, "fg") || !STRICMP(name, "foreground")) { + *idx = kColorIdxFg; return normal_fg; } for (int i = 0; color_name_table[i].name != NULL; i++) { if (!STRICMP(name, color_name_table[i].name)) { + *idx = i; return color_name_table[i].color; } } + *idx = kColorIdxNone; return -1; } +const char *coloridx_to_name(int idx, int val, char hexbuf[8]) +{ + if (idx >= 0) { + return color_name_table[idx].name; + } + switch (idx) { + case kColorIdxNone: + return NULL; + case kColorIdxFg: + return "fg"; + case kColorIdxBg: + return "bg"; + case kColorIdxHex: + snprintf(hexbuf, 7 + 1, "#%06x", val); + return hexbuf; + default: + abort(); + } +} + int name_to_ctermcolor(const char *name) { int i; diff --git a/src/nvim/indent.c b/src/nvim/indent.c index 010d2fe869..d71b3adb5c 100644 --- a/src/nvim/indent.c +++ b/src/nvim/indent.c @@ -697,8 +697,10 @@ int get_lisp_indent(void) && lisp_match(that + 1)) { amount += 2; } else { - that++; - amount++; + if (*that != NUL) { + that++; + amount++; + } firsttry = amount; while (ascii_iswhite(*that)) { diff --git a/src/nvim/lib/kvec.h b/src/nvim/lib/kvec.h index 4238088b1c..b5b3adf7d2 100644 --- a/src/nvim/lib/kvec.h +++ b/src/nvim/lib/kvec.h @@ -121,6 +121,9 @@ #define kv_push(v, x) \ (*kv_pushp(v) = (x)) +#define kv_pushp_c(v) ((v).items + ((v).size++)) +#define kv_push_c(v, x) (*kv_pushp_c(v) = (x)) + #define kv_a(v, i) \ (*(((v).capacity <= (size_t)(i) \ ? ((v).capacity = (v).size = (i) + 1, \ diff --git a/src/nvim/memory.c b/src/nvim/memory.c index dd06419391..a615802b36 100644 --- a/src/nvim/memory.c +++ b/src/nvim/memory.c @@ -605,6 +605,14 @@ void arena_mem_free(ArenaMem mem, ArenaMem *reuse_blk) } } +char *arena_memdupz(Arena *arena, const char *buf, size_t size) +{ + char *mem = arena_alloc(arena, size + 1, false); + memcpy(mem, buf, size); + mem[size] = NUL; + return mem; +} + #if defined(EXITFREE) # include "nvim/buffer.h" @@ -786,6 +794,7 @@ void free_all_mem(void) decor_free_all_mem(); nlua_free_all_mem(); + ui_free_all_mem(); } #endif diff --git a/src/nvim/memory.h b/src/nvim/memory.h index 0c048e1b5b..63d607c2ce 100644 --- a/src/nvim/memory.h +++ b/src/nvim/memory.h @@ -51,6 +51,10 @@ typedef struct { // inits an empty arena. use arena_start() to actually allocate space! #define ARENA_EMPTY { .cur_blk = NULL, .pos = 0, .size = 0 } +#define kv_fixsize_arena(a, v, s) \ + ((v).capacity = (s), \ + (v).items = (void *)arena_alloc(a, sizeof((v).items[0]) * (v).capacity, true)) + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "memory.h.generated.h" #endif diff --git a/src/nvim/message.c b/src/nvim/message.c index acc3122e93..e192af6aad 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -1068,6 +1068,7 @@ void ex_messages(void *const eap_p) } } ui_call_msg_history_show(entries); + api_free_array(entries); msg_ext_history_visible = true; wait_return(false); } else { @@ -1262,11 +1263,11 @@ void wait_return(int redraw) msg_ext_keep_after_cmdline = true; } - // If the window size changed set_shellsize() will redraw the screen. + // If the screen size changed screen_resize() will redraw the screen. // Otherwise the screen is only redrawn if 'redraw' is set and no ':' // typed. tmpState = State; - State = oldState; // restore State before set_shellsize + State = oldState; // restore State before screen_resize() setmouse(); msg_check(); need_wait_return = false; @@ -3136,12 +3137,13 @@ void msg_ext_ui_flush(void) msg_ext_emit_chunk(); if (msg_ext_chunks.size > 0) { - ui_call_msg_show(cstr_to_string(msg_ext_kind), + ui_call_msg_show(cstr_as_string((char *)msg_ext_kind), msg_ext_chunks, msg_ext_overwrite); if (!msg_ext_overwrite) { msg_ext_visible++; } msg_ext_kind = NULL; + api_free_array(msg_ext_chunks); msg_ext_chunks = (Array)ARRAY_DICT_INIT; msg_ext_cur_len = 0; msg_ext_overwrite = false; @@ -3155,6 +3157,7 @@ void msg_ext_flush_showmode(void) if (ui_has(kUIMessages)) { msg_ext_emit_chunk(); ui_call_msg_showmode(msg_ext_chunks); + api_free_array(msg_ext_chunks); msg_ext_chunks = (Array)ARRAY_DICT_INIT; msg_ext_cur_len = 0; } diff --git a/src/nvim/move.c b/src/nvim/move.c index 9d0099a2f8..cc811fca18 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -1518,12 +1518,10 @@ void set_empty_rows(win_T *wp, int used) } } -/* - * Recompute topline to put the cursor at the bottom of the window. - * Scroll at least "min_scroll" lines. - * If "set_topbot" is true, set topline and botline first (for "zb"). - * This is messy stuff!!! - */ +/// Recompute topline to put the cursor at the bottom of the window. +/// When scrolling scroll at least "min_scroll" lines. +/// If "set_topbot" is true, set topline and botline first (for "zb"). +/// This is messy stuff!!! void scroll_cursor_bot(int min_scroll, int set_topbot) { int used; diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c index b4f39c4c61..388fa2584c 100644 --- a/src/nvim/msgpack_rpc/channel.c +++ b/src/nvim/msgpack_rpc/channel.c @@ -360,6 +360,17 @@ free_ret: api_clear_error(&error); } +bool rpc_write_raw(uint64_t id, WBuffer *buffer) +{ + Channel *channel = find_rpc_channel(id); + if (!channel) { + wstream_release_wbuffer(buffer); + return false; + } + + return channel_write(channel, buffer); +} + static bool channel_write(Channel *channel, WBuffer *buffer) { bool success; diff --git a/src/nvim/msgpack_rpc/unpacker.c b/src/nvim/msgpack_rpc/unpacker.c index 419349e74d..26c1843026 100644 --- a/src/nvim/msgpack_rpc/unpacker.c +++ b/src/nvim/msgpack_rpc/unpacker.c @@ -11,10 +11,6 @@ # include "msgpack_rpc/unpacker.c.generated.h" #endif -#define kv_fixsize_arena(a, v, s) \ - ((v).capacity = (s), \ - (v).items = (void *)arena_alloc(a, sizeof((v).items[0]) * (v).capacity, true)) - Object unpack(const char *data, size_t size, Error *err) { Unpacker unpacker; diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 35a7ee3319..8e4f78818b 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -2755,13 +2755,13 @@ static void display_showcmd(void) showcmd_is_clear = (len == 0); if (ui_has(kUIMessages)) { - Array content = ARRAY_DICT_INIT; + MAXSIZE_TEMP_ARRAY(content, 1); + MAXSIZE_TEMP_ARRAY(chunk, 2); if (len > 0) { - Array chunk = ARRAY_DICT_INIT; // placeholder for future highlight support - ADD(chunk, INTEGER_OBJ(0)); - ADD(chunk, STRING_OBJ(cstr_to_string((char *)showcmd_buf))); - ADD(content, ARRAY_OBJ(chunk)); + ADD_C(chunk, INTEGER_OBJ(0)); + ADD_C(chunk, STRING_OBJ(cstr_as_string((char *)showcmd_buf))); + ADD_C(content, ARRAY_OBJ(chunk)); } ui_call_msg_showcmd(content); return; diff --git a/src/nvim/option.c b/src/nvim/option.c index 075dd3d4bc..c97c027740 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -4528,7 +4528,7 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, char *errbuf, last_status(false); // (re)set last window status line. } else if (pp == &p_stal) { // (re)set tab page line - shell_new_rows(); // recompute window positions and heights + win_new_screen_rows(); // recompute window positions and heights } else if (pp == &curwin->w_p_fdl) { newFoldLevel(); } else if (pp == &curwin->w_p_fml) { @@ -4617,7 +4617,7 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, char *errbuf, p_columns = MIN_COLUMNS; } - // True max size is defined by check_shellsize() + // True max size is defined by check_screensize() p_lines = MIN(p_lines, INT_MAX); p_columns = MIN(p_columns, INT_MAX); @@ -4635,7 +4635,7 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, char *errbuf, // messages. Rows = (int)p_lines; Columns = (int)p_columns; - check_shellsize(); + check_screensize(); if (cmdline_row > Rows - p_ch && Rows > p_ch) { assert(p_ch >= 0 && Rows - p_ch <= INT_MAX); cmdline_row = (int)(Rows - p_ch); diff --git a/src/nvim/popupmnu.c b/src/nvim/popupmnu.c index 7233a3bb72..db1054eb1f 100644 --- a/src/nvim/popupmnu.c +++ b/src/nvim/popupmnu.c @@ -151,17 +151,20 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed, i if (pum_external) { if (array_changed) { - Array arr = ARRAY_DICT_INIT; + Arena arena = ARENA_EMPTY; + arena_start(&arena, &ui_ext_fixblk); + Array arr = arena_array(&arena, (size_t)size); for (int i = 0; i < size; i++) { - Array item = ARRAY_DICT_INIT; - ADD(item, STRING_OBJ(cstr_to_string((char *)array[i].pum_text))); - ADD(item, STRING_OBJ(cstr_to_string((char *)array[i].pum_kind))); - ADD(item, STRING_OBJ(cstr_to_string((char *)array[i].pum_extra))); - ADD(item, STRING_OBJ(cstr_to_string((char *)array[i].pum_info))); - ADD(arr, ARRAY_OBJ(item)); + Array item = arena_array(&arena, 4); + ADD_C(item, STRING_OBJ(cstr_as_string((char *)array[i].pum_text))); + ADD_C(item, STRING_OBJ(cstr_as_string((char *)array[i].pum_kind))); + ADD_C(item, STRING_OBJ(cstr_as_string((char *)array[i].pum_extra))); + ADD_C(item, STRING_OBJ(cstr_as_string((char *)array[i].pum_info))); + ADD_C(arr, ARRAY_OBJ(item)); } ui_call_popupmenu_show(arr, selected, pum_win_row, cursor_col, pum_anchor_grid); + arena_mem_free(arena_finish(&arena), &ui_ext_fixblk); } else { ui_call_popupmenu_select(selected); return; @@ -438,7 +441,7 @@ void pum_redraw(void) if (ui_has(kUIMultigrid)) { const char *anchor = pum_above ? "SW" : "NW"; int row_off = pum_above ? -pum_height : 0; - ui_call_win_float_pos(pum_grid.handle, -1, cstr_to_string(anchor), + ui_call_win_float_pos(pum_grid.handle, -1, cstr_as_string((char *)anchor), pum_anchor_grid, pum_row - row_off, pum_col - col_off, false, pum_grid.zindex); } diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 2ee7cd44f7..98f8e30391 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -5578,9 +5578,9 @@ void check_for_delay(bool check_msg_scroll) /// /// There may be some time between setting Rows and Columns and (re)allocating /// default_grid arrays. This happens when starting up and when -/// (manually) changing the shell size. Always use default_grid.rows and +/// (manually) changing the screen size. Always use default_grid.rows and /// default_grid.Columns to access items in default_grid.chars[]. Use Rows -/// and Columns for positioning text etc. where the final size of the shell is +/// and Columns for positioning text etc. where the final size of the screen is /// needed. void screenalloc(void) { @@ -5615,14 +5615,14 @@ retry: */ ++RedrawingDisabled; - // win_new_shellsize will recompute floats position, but tell the + // win_new_screensize will recompute floats position, but tell the // compositor to not redraw them yet ui_comp_set_screen_valid(false); if (msg_grid.chars) { msg_grid_invalid = true; } - win_new_shellsize(); // fit the windows in the new sized shell + win_new_screensize(); // fit the windows in the new sized screen comp_col(); // recompute columns for shown command and ruler @@ -6341,35 +6341,49 @@ void draw_tabline(void) void ui_ext_tabline_update(void) { - Array tabs = ARRAY_DICT_INIT; + Arena arena = ARENA_EMPTY; + arena_start(&arena, &ui_ext_fixblk); + + size_t n_tabs = 0; FOR_ALL_TABS(tp) { - Dictionary tab_info = ARRAY_DICT_INIT; - PUT(tab_info, "tab", TABPAGE_OBJ(tp->handle)); + n_tabs++; + } + + Array tabs = arena_array(&arena, n_tabs); + FOR_ALL_TABS(tp) { + Dictionary tab_info = arena_dict(&arena, 2); + PUT_C(tab_info, "tab", TABPAGE_OBJ(tp->handle)); win_T *cwp = (tp == curtab) ? curwin : tp->tp_curwin; get_trans_bufname(cwp->w_buffer); - PUT(tab_info, "name", STRING_OBJ(cstr_to_string((char *)NameBuff))); + PUT_C(tab_info, "name", STRING_OBJ(arena_string(&arena, cstr_as_string((char *)NameBuff)))); + + ADD_C(tabs, DICTIONARY_OBJ(tab_info)); + } - ADD(tabs, DICTIONARY_OBJ(tab_info)); + size_t n_buffers = 0; + FOR_ALL_BUFFERS(buf) { + n_buffers += buf->b_p_bl ? 1 : 0; } - Array buffers = ARRAY_DICT_INIT; + Array buffers = arena_array(&arena, n_buffers); FOR_ALL_BUFFERS(buf) { // Do not include unlisted buffers if (!buf->b_p_bl) { continue; } - Dictionary buffer_info = ARRAY_DICT_INIT; - PUT(buffer_info, "buffer", BUFFER_OBJ(buf->handle)); + Dictionary buffer_info = arena_dict(&arena, 2); + PUT_C(buffer_info, "buffer", BUFFER_OBJ(buf->handle)); get_trans_bufname(buf); - PUT(buffer_info, "name", STRING_OBJ(cstr_to_string((char *)NameBuff))); + PUT_C(buffer_info, "name", STRING_OBJ(arena_string(&arena, cstr_as_string((char *)NameBuff)))); - ADD(buffers, DICTIONARY_OBJ(buffer_info)); + ADD_C(buffers, DICTIONARY_OBJ(buffer_info)); } ui_call_tabline_update(curtab->handle, tabs, curbuf->handle, buffers); + arena_mem_free(arena_finish(&arena), &ui_ext_fixblk); } /* @@ -6618,11 +6632,11 @@ static void win_redr_ruler(win_T *wp, bool always) } if (ui_has(kUIMessages) && !part_of_status) { - Array content = ARRAY_DICT_INIT; - Array chunk = ARRAY_DICT_INIT; - ADD(chunk, INTEGER_OBJ(attr)); - ADD(chunk, STRING_OBJ(cstr_to_string((char *)buffer))); - ADD(content, ARRAY_OBJ(chunk)); + MAXSIZE_TEMP_ARRAY(content, 1); + MAXSIZE_TEMP_ARRAY(chunk, 2); + ADD_C(chunk, INTEGER_OBJ(attr)); + ADD_C(chunk, STRING_OBJ(cstr_as_string((char *)buffer))); + ADD_C(content, ARRAY_OBJ(chunk)); ui_call_msg_ruler(content); did_show_ext_ruler = true; } else { @@ -6744,7 +6758,7 @@ static void margin_columns_win(win_T *wp, int *left_col, int *right_col) prev_col_off = cur_col_off; } -/// Set dimensions of the Nvim application "shell". +/// Set dimensions of the Nvim application "screen". void screen_resize(int width, int height) { // Avoid recursiveness, can happen when setting the window size causes @@ -6775,7 +6789,7 @@ void screen_resize(int width, int height) Rows = height; Columns = width; - check_shellsize(); + check_screensize(); int max_p_ch = Rows - min_rows() + 1; if (!ui_has(kUIMessages) && p_ch > 0 && p_ch > max_p_ch) { p_ch = max_p_ch ? max_p_ch : 1; @@ -6852,49 +6866,23 @@ void screen_resize(int width, int height) resizing_screen = false; } -/// Check if the new Nvim application "shell" dimensions are valid. +/// Check if the new Nvim application "screen" dimensions are valid. /// Correct it if it's too small or way too big. -void check_shellsize(void) +void check_screensize(void) { + // Limit Rows and Columns to avoid an overflow in Rows * Columns. if (Rows < min_rows()) { // need room for one window and command line Rows = min_rows(); + } else if (Rows > 1000) { + Rows = 1000; } - limit_screen_size(); -} -// Limit Rows and Columns to avoid an overflow in Rows * Columns. -void limit_screen_size(void) -{ if (Columns < MIN_COLUMNS) { Columns = MIN_COLUMNS; } else if (Columns > 10000) { Columns = 10000; } - - if (Rows > 1000) { - Rows = 1000; - } -} - -void win_new_shellsize(void) -{ - static long old_Rows = 0; - static long old_Columns = 0; - - if (old_Rows != Rows) { - // If 'window' uses the whole screen, keep it using that. - // Don't change it when set with "-w size" on the command line. - if (p_window == old_Rows - 1 || (old_Rows == 0 && p_window == 0)) { - p_window = Rows - 1; - } - old_Rows = Rows; - shell_new_rows(); // update window sizes - } - if (old_Columns != Columns) { - old_Columns = Columns; - shell_new_columns(); // update window sizes - } } win_T *get_win_by_grid_handle(handle_T handle) diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index 47305b6709..0d43458b5b 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -2381,8 +2381,6 @@ static void update_si_attr(int idx) } else { sip->si_attr = CUR_STATE(idx - 1).si_attr; sip->si_trans_id = CUR_STATE(idx - 1).si_trans_id; - sip->si_h_startpos = CUR_STATE(idx - 1).si_h_startpos; - sip->si_h_endpos = CUR_STATE(idx - 1).si_h_endpos; if (sip->si_cont_list == NULL) { sip->si_flags |= HL_TRANS_CONT; sip->si_cont_list = CUR_STATE(idx - 1).si_cont_list; diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index b9fdddf235..85517a71a4 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -257,7 +257,8 @@ Terminal *terminal_open(buf_T *buf, TerminalOptions opts) snprintf(var, sizeof(var), "terminal_color_%d", i); char *name = get_config_string(var); if (name) { - color_val = name_to_color(name); + int dummy; + color_val = name_to_color(name, &dummy); xfree(name); if (color_val != -1) { diff --git a/src/nvim/testdir/test_cmdline.vim b/src/nvim/testdir/test_cmdline.vim index b2c752376f..d26c80077d 100644 --- a/src/nvim/testdir/test_cmdline.vim +++ b/src/nvim/testdir/test_cmdline.vim @@ -636,6 +636,14 @@ func Test_illegal_address2() call delete('Xtest.vim') endfunc +func Test_mark_from_line_zero() + " this was reading past the end of the first (empty) line + new + norm oxxxx + call assert_fails("0;'(", 'E20:') + bwipe! +endfunc + func Test_cmdline_complete_wildoptions() help call feedkeys(":tag /\<c-a>\<c-b>\"\<cr>", 'tx') diff --git a/src/nvim/testdir/test_lispwords.vim b/src/nvim/testdir/test_lispwords.vim index ff710b2716..4144fb0521 100644 --- a/src/nvim/testdir/test_lispwords.vim +++ b/src/nvim/testdir/test_lispwords.vim @@ -1,4 +1,5 @@ -" Tests for 'lispwords' settings being global-local +" Tests for 'lispwords' settings being global-local. +" And other lisp indent stuff. set nocompatible viminfo+=nviminfo @@ -85,4 +86,13 @@ func Test_lisp_indent() set nolisp endfunc +func Test_lisp_indent_works() + " This was reading beyond the end of the line + new + exe "norm a\tü(\<CR>=" + set lisp + norm == + bwipe! +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_mksession.vim b/src/nvim/testdir/test_mksession.vim index c55ba391a5..cf90e416c4 100644 --- a/src/nvim/testdir/test_mksession.vim +++ b/src/nvim/testdir/test_mksession.vim @@ -363,21 +363,29 @@ func Test_mkview_open_folds() call append(0, ['a', 'b', 'c']) 1,3fold + write! Xtestfile + + call assert_notequal(-1, foldclosed(1)) + call assert_notequal(-1, foldclosed(2)) + call assert_notequal(-1, foldclosed(3)) + + " Save the view with folds closed + mkview! Xtestview + " zR affects 'foldlevel', make sure the option is applied after the folds " have been recreated. + " Open folds to ensure they get closed when restoring the view normal zR - write! Xtestfile call assert_equal(-1, foldclosed(1)) call assert_equal(-1, foldclosed(2)) call assert_equal(-1, foldclosed(3)) - mkview! Xtestview source Xtestview - call assert_equal(-1, foldclosed(1)) - call assert_equal(-1, foldclosed(2)) - call assert_equal(-1, foldclosed(3)) + call assert_notequal(-1, foldclosed(1)) + call assert_notequal(-1, foldclosed(2)) + call assert_notequal(-1, foldclosed(3)) call delete('Xtestview') call delete('Xtestfile') diff --git a/src/nvim/testdir/test_syntax.vim b/src/nvim/testdir/test_syntax.vim index 6bef61ae8f..c46bba04e3 100644 --- a/src/nvim/testdir/test_syntax.vim +++ b/src/nvim/testdir/test_syntax.vim @@ -778,6 +778,82 @@ func Test_search_syntax_skip() bwipe! endfunc +func Test_syn_contained_transparent() + " Comments starting with "Regression:" show the result when the highlighting + " span of the containing item is assigned to the contained region. + syntax on + + let l:case = "Transparent region contained in region" + new + syntax region X start=/\[/ end=/\]/ contained transparent + syntax region Y start=/(/ end=/)/ contains=X + + call setline(1, "==(--[~~]--)==") + let l:expected = " YYYYYYYYYY " + eval AssertHighlightGroups(1, 1, l:expected, 1, l:case) + syntax clear Y X + bw! + + let l:case = "Transparent region extends region" + new + syntax region X start=/\[/ end=/\]/ contained transparent + syntax region Y start=/(/ end=/)/ end=/e/ contains=X + + call setline(1, "==(--[~~e~~]--)==") + let l:expected = " YYYYYYYYYYYYY " + " Regression: " YYYYYYY YYY " + eval AssertHighlightGroups(1, 1, l:expected, 1, l:case) + syntax clear Y X + bw! + + let l:case = "Nested transparent regions extend region" + new + syntax region X start=/\[/ end=/\]/ contained transparent + syntax region Y start=/(/ end=/)/ end=/e/ contains=X + + call setline(1, "==(--[~~e~~[~~e~~]~~e~~]--)==") + let l:expected = " YYYYYYYYYYYYYYYYYYYYYYYYY " + " Regression: " YYYYYYY YYYYYYYYY " + eval AssertHighlightGroups(1, 1, l:expected, 1, l:case) + syntax clear Y X + bw! + + let l:case = "Transparent region contained in match" + new + syntax region X start=/\[/ end=/\]/ contained transparent + syntax match Y /(.\{-})/ contains=X + + call setline(1, "==(--[~~]--)==") + let l:expected = " YYYYYYYYYY " + eval AssertHighlightGroups(1, 1, l:expected, 1, l:case) + syntax clear Y X + bw! + + let l:case = "Transparent region extends match" + new + syntax region X start=/\[/ end=/\]/ contained transparent + syntax match Y /(.\{-}[e)]/ contains=X + + call setline(1, "==(--[~~e~~]--)==") + let l:expected = " YYYYYYYYYY " + " Regression: " YYYYYYY " + eval AssertHighlightGroups(1, 1, l:expected, 1, l:case) + syntax clear Y X + bw! + + let l:case = "Nested transparent regions extend match" + new + syntax region X start=/\[/ end=/\]/ contained transparent + syntax match Y /(.\{-}[e)]/ contains=X + + call setline(1, "==(--[~~e~~[~~e~~]~~e~~]--)==") + let l:expected = " YYYYYYYYYYYYYYYYYYYYYY " + " Regression: " YYYYYYY YYYYYY " + eval AssertHighlightGroups(1, 1, l:expected, 1, l:case) + syntax clear Y X + bw! +endfunc + func Test_syn_include_contains_TOP() let l:case = "TOP in included syntax means its group list name" new diff --git a/src/nvim/ui.c b/src/nvim/ui.c index bdcb7af7e9..a49e9df9ee 100644 --- a/src/nvim/ui.c +++ b/src/nvim/ui.c @@ -64,6 +64,8 @@ static handle_T cursor_grid_handle = DEFAULT_GRID_HANDLE; static bool has_mouse = false; static int pending_has_mouse = -1; +static Array call_buf = ARRAY_DICT_INIT; + #if MIN_LOG_LEVEL > LOGLVL_DBG # define UI_LOG(funname) #else @@ -123,6 +125,12 @@ void ui_init(void) default_grid.handle = 1; msg_grid_adj.target = &default_grid; ui_comp_init(); + kv_ensure_space(call_buf, 16); +} + +void ui_free_all_mem(void) +{ + kv_destroy(call_buf); } void ui_builtin_start(void) @@ -173,15 +181,6 @@ bool ui_active(void) return ui_count > 1; } -void ui_event(char *name, Array args) -{ - bool args_consumed = false; - ui_call_event(name, args, &args_consumed); - if (!args_consumed) { - api_free_array(args); - } -} - void ui_refresh(void) { if (!ui_active()) { @@ -496,6 +495,11 @@ int ui_current_col(void) return cursor_col; } +handle_T ui_cursor_grid(void) +{ + return cursor_grid_handle; +} + void ui_flush(void) { cmdline_ui_flush(); @@ -508,13 +512,15 @@ void ui_flush(void) pending_cursor_update = false; } if (pending_mode_info_update) { - Array style = mode_style_array(); + Arena arena = ARENA_EMPTY; + arena_start(&arena, &ui_ext_fixblk); + Array style = mode_style_array(&arena); bool enabled = (*p_guicursor != NUL); ui_call_mode_info_set(enabled, style); - api_free_array(style); + arena_mem_free(arena_finish(&arena), &ui_ext_fixblk); pending_mode_info_update = false; } - if (pending_mode_update) { + if (pending_mode_update && !starting) { char *full_name = shape_table[ui_mode_idx].full_name; ui_call_mode_change(cstr_as_string(full_name), ui_mode_idx); pending_mode_update = false; diff --git a/src/nvim/ui.h b/src/nvim/ui.h index 1d278010e8..7dd2f5bce3 100644 --- a/src/nvim/ui.h +++ b/src/nvim/ui.h @@ -8,6 +8,7 @@ #include "nvim/api/private/defs.h" #include "nvim/globals.h" #include "nvim/highlight_defs.h" +#include "nvim/memory.h" typedef enum { kUICmdline = 0, @@ -46,6 +47,8 @@ enum { typedef int LineFlags; +EXTERN ArenaMem ui_ext_fixblk INIT(= NULL); + struct ui_t { bool rgb; bool override; ///< Force highest-requested UI capabilities. diff --git a/src/nvim/window.c b/src/nvim/window.c index 1487759d60..1a1f62f2c0 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -893,7 +893,7 @@ void ui_ext_win_position(win_T *wp) wp->w_grid_alloc.zindex = wp->w_float_config.zindex; if (ui_has(kUIMultigrid)) { - String anchor = cstr_to_string(float_anchor_str[c.anchor]); + String anchor = cstr_as_string((char *)float_anchor_str[c.anchor]); ui_call_win_float_pos(wp->w_grid_alloc.handle, wp->handle, anchor, grid->handle, row, col, c.focusable, wp->w_grid_alloc.zindex); @@ -967,8 +967,8 @@ static int check_split_disallowed(void) * "flags": * WSP_ROOM: require enough room for new window * WSP_VERT: vertical split. - * WSP_TOP: open window at the top-left of the shell (help window). - * WSP_BOT: open window at the bottom-right of the shell (quickfix window). + * WSP_TOP: open window at the top-left of the screen (help window). + * WSP_BOT: open window at the bottom-right of the screen (quickfix window). * WSP_HELP: creating the help window, keep layout snapshot * * return FAIL for failure, OK otherwise @@ -2499,7 +2499,7 @@ void close_windows(buf_T *buf, bool keep_curwin) redraw_tabline = true; if (h != tabline_height()) { - shell_new_rows(); + win_new_screen_rows(); } } @@ -2606,7 +2606,7 @@ static bool close_last_window_tabpage(win_T *win, bool free_buf, tabpage_T *prev win_close_othertab(win, free_buf, prev_curtab); if (h != tabline_height()) { - shell_new_rows(); + win_new_screen_rows(); } } entering_window(curwin); @@ -4350,10 +4350,10 @@ static void enter_tabpage(tabpage_T *tp, buf_T *old_curbuf, bool trigger_enter_a } if (curtab->tp_old_Rows != Rows || (old_off != firstwin->w_winrow)) { - shell_new_rows(); + win_new_screen_rows(); } if (curtab->tp_old_Columns != Columns && starting == 0) { - shell_new_columns(); // update window widths + win_new_screen_cols(); // update window widths } lastused_tabpage = old_curtab; @@ -5277,11 +5277,29 @@ static void frame_remove(frame_T *frp) } } -/* - * Called from win_new_shellsize() after Rows changed. - * This only does the current tab page, others must be done when made active. - */ -void shell_new_rows(void) +void win_new_screensize(void) +{ + static long old_Rows = 0; + static long old_Columns = 0; + + if (old_Rows != Rows) { + // If 'window' uses the whole screen, keep it using that. + // Don't change it when set with "-w size" on the command line. + if (p_window == old_Rows - 1 || (old_Rows == 0 && p_window == 0)) { + p_window = Rows - 1; + } + old_Rows = Rows; + win_new_screen_rows(); // update window sizes + } + if (old_Columns != Columns) { + old_Columns = Columns; + win_new_screen_cols(); // update window sizes + } +} +/// Called from win_new_screensize() after Rows changed. +/// +/// This only does the current tab page, others must be done when made active. +void win_new_screen_rows(void) { int h = (int)ROWS_AVAIL; @@ -5305,10 +5323,8 @@ void shell_new_rows(void) curtab->tp_ch_used = p_ch; } -/* - * Called from win_new_shellsize() after Columns changed. - */ -void shell_new_columns(void) +/// Called from win_new_screensize() after Columns changed. +void win_new_screen_cols(void) { if (firstwin == NULL) { // not initialized yet return; diff --git a/test/functional/api/highlight_spec.lua b/test/functional/api/highlight_spec.lua index 8235374f22..c57c1b12dc 100644 --- a/test/functional/api/highlight_spec.lua +++ b/test/functional/api/highlight_spec.lua @@ -293,19 +293,20 @@ describe("API: set highlight", function() eq('Test_hl2 xxx cterm=undercurl,italic,reverse,strikethrough ctermfg=8 ctermbg=15 gui=bold,underline,underlineline,undercurl,underdot,underdash,italic,reverse,strikethrough guifg=#ff0000 guibg=#0032aa', exec_capture('highlight Test_hl2')) - -- Colors are stored exactly as they are defined. + -- Colors are stored with the name they are defined, but + -- with canonical casing meths.set_hl(0, 'Test_hl3', { bg = 'reD', fg = 'bLue'}) - eq('Test_hl3 xxx guifg=bLue guibg=reD', + eq('Test_hl3 xxx guifg=Blue guibg=Red', exec_capture('highlight Test_hl3')) end) it ("can modify a highlight in the global namespace", function() meths.set_hl(0, 'Test_hl3', { bg = 'red', fg = 'blue'}) - eq('Test_hl3 xxx guifg=blue guibg=red', + eq('Test_hl3 xxx guifg=Blue guibg=Red', exec_capture('highlight Test_hl3')) meths.set_hl(0, 'Test_hl3', { bg = 'red' }) - eq('Test_hl3 xxx guibg=red', + eq('Test_hl3 xxx guibg=Red', exec_capture('highlight Test_hl3')) meths.set_hl(0, 'Test_hl3', { ctermbg = 9, ctermfg = 12}) @@ -327,7 +328,7 @@ describe("API: set highlight", function() pcall_err(meths.set_hl, 0, 'Test_hl3', {ctermfg='bleu'})) meths.set_hl(0, 'Test_hl3', {fg='#FF00FF'}) - eq('Test_hl3 xxx guifg=#FF00FF', + eq('Test_hl3 xxx guifg=#ff00ff', exec_capture('highlight Test_hl3')) eq("'#FF00FF' is not a valid color", @@ -340,7 +341,7 @@ describe("API: set highlight", function() end meths.set_hl(0, 'Test_hl3', {fg='#FF00FF', blend=50}) - eq('Test_hl3 xxx guifg=#FF00FF blend=50', + eq('Test_hl3 xxx guifg=#ff00ff blend=50', exec_capture('highlight Test_hl3')) end) diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index ea5725d836..ef6798dea3 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -1453,10 +1453,21 @@ describe('API', function() it('set local window options', function() -- Different to nvim_win_set_option nvim('set_option_value', 'colorcolumn', '4,3', {win=0, scope='local'}) - eq('4,3', nvim('get_option_value', 'colorcolumn', {scope = 'local'})) + eq('4,3', nvim('get_option_value', 'colorcolumn', {win = 0, scope = 'local'})) command("set modified hidden") command("enew") -- edit new buffer, window option is reset - eq('', nvim('get_option_value', 'colorcolumn', {scope = 'local'})) + eq('', nvim('get_option_value', 'colorcolumn', {win = 0, scope = 'local'})) + end) + + it('get buffer or window-local options', function() + nvim('command', 'new') + local buf = nvim('get_current_buf').id + nvim('buf_set_option', buf, 'tagfunc', 'foobar') + eq('foobar', nvim('get_option_value', 'tagfunc', {buf = buf})) + + local win = nvim('get_current_win').id + nvim('win_set_option', win, 'number', true) + eq(true, nvim('get_option_value', 'number', {win = win})) end) end) diff --git a/test/functional/ex_cmds/map_spec.lua b/test/functional/ex_cmds/map_spec.lua index eae36b9ae9..c6bdd017bd 100644 --- a/test/functional/ex_cmds/map_spec.lua +++ b/test/functional/ex_cmds/map_spec.lua @@ -86,7 +86,7 @@ n asdf1 qwert end) end) -describe(':*map cursor and redrawing', function() +describe('Screen', function() local screen before_each(function() clear() @@ -149,6 +149,18 @@ describe(':*map cursor and redrawing', function() ]]) end) + it('cursor position does not move after empty-string :cmap <expr> #19046', function() + command([[cnoremap <expr> <F2> '']]) + feed(':<F2>') + screen:expect([[ + | + ~ | + ~ | + ~ | + :^ | + ]]) + end) + it('cursor is restored after :map <expr> which redraws statusline vim-patch:8.1.2336', function() exec([[ call setline(1, ['one', 'two', 'three']) @@ -157,12 +169,12 @@ describe(':*map cursor and redrawing', function() hi! link StatusLine ErrorMsg noremap <expr> <C-B> Func() func Func() - let g:on = !get(g:, 'on', 0) - redraws - return '' + let g:on = !get(g:, 'on', 0) + redraws + return '' endfunc func Status() - return get(g:, 'on', 0) ? '[on]' : '' + return get(g:, 'on', 0) ? '[on]' : '' endfunc set stl=%{Status()} ]]) diff --git a/test/functional/fixtures/CMakeLists.txt b/test/functional/fixtures/CMakeLists.txt index 270540de2e..6010fcaf1e 100644 --- a/test/functional/fixtures/CMakeLists.txt +++ b/test/functional/fixtures/CMakeLists.txt @@ -4,7 +4,7 @@ target_link_libraries(tty-test ${LIBUV_LIBRARIES}) add_executable(shell-test EXCLUDE_FROM_ALL shell-test.c) add_executable(printargs-test EXCLUDE_FROM_ALL printargs-test.c) add_executable(printenv-test EXCLUDE_FROM_ALL printenv-test.c) -if(WIN32) +if(MINGW) set_target_properties(printenv-test PROPERTIES LINK_FLAGS -municode) endif() diff --git a/test/functional/lua/vim_spec.lua b/test/functional/lua/vim_spec.lua index 1af46b7c2f..646c5ac8ca 100644 --- a/test/functional/lua/vim_spec.lua +++ b/test/functional/lua/vim_spec.lua @@ -1396,10 +1396,12 @@ describe('lua stdlib', function() ]] eq('', funcs.luaeval "vim.bo.filetype") eq(true, funcs.luaeval "vim.bo[BUF].modifiable") - matches("Invalid option name: 'nosuchopt'$", + matches("unknown option 'nosuchopt'$", pcall_err(exec_lua, 'return vim.bo.nosuchopt')) matches("Expected lua string$", pcall_err(exec_lua, 'return vim.bo[0][0].autoread')) + matches("Invalid buffer id: %-1$", + pcall_err(exec_lua, 'return vim.bo[-1].filetype')) end) it('vim.wo', function() @@ -1415,10 +1417,12 @@ describe('lua stdlib', function() eq(0, funcs.luaeval "vim.wo.cole") eq(0, funcs.luaeval "vim.wo[0].cole") eq(0, funcs.luaeval "vim.wo[1001].cole") - matches("Invalid option name: 'notanopt'$", + matches("unknown option 'notanopt'$", pcall_err(exec_lua, 'return vim.wo.notanopt')) matches("Expected lua string$", pcall_err(exec_lua, 'return vim.wo[0][0].list')) + matches("Invalid window id: %-1$", + pcall_err(exec_lua, 'return vim.wo[-1].list')) eq(2, funcs.luaeval "vim.wo[1000].cole") exec_lua [[ vim.wo[1000].cole = 0 diff --git a/test/functional/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua index cf4845474f..9af5d386db 100644 --- a/test/functional/ui/decorations_spec.lua +++ b/test/functional/ui/decorations_spec.lua @@ -483,6 +483,7 @@ describe('extmark decorations', function() [23] = {foreground = Screen.colors.Magenta1, background = Screen.colors.LightGrey}; [24] = {bold = true}; [25] = {background = Screen.colors.LightRed}; + [26] = {background=Screen.colors.DarkGrey, foreground=Screen.colors.LightGrey}; } ns = meths.create_namespace 'test' @@ -864,6 +865,20 @@ end]] ]]} helpers.assert_alive() end) + + it('conceal #19007', function() + screen:try_resize(50, 5) + insert('foo\n') + command('let &conceallevel=2') + meths.buf_set_extmark(0, ns, 0, 0, {end_col=0, end_row=2, conceal='X'}) + screen:expect([[ + {26:X} | + ^ | + {1:~ }| + {1:~ }| + | + ]]) + end) end) describe('decorations: virtual lines', function() diff --git a/test/functional/ui/multibyte_spec.lua b/test/functional/ui/multibyte_spec.lua index c3c79ea574..d4e237bcb4 100644 --- a/test/functional/ui/multibyte_spec.lua +++ b/test/functional/ui/multibyte_spec.lua @@ -6,6 +6,9 @@ local feed = helpers.feed local feed_command = helpers.feed_command local insert = helpers.insert local funcs = helpers.funcs +local meths = helpers.meths +local split = helpers.split +local dedent = helpers.dedent describe("multibyte rendering", function() local screen @@ -115,6 +118,35 @@ describe("multibyte rendering", function() {4:-- INSERT --} | ]]) 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 [[ + L̓̉̑̒̌̚ơ̗̌̒̄̀ŕ̈̈̎̐̕è̇̅̄̄̐m̖̟̟̅̄̚ ̛̓̑̆̇̍i̗̟̞̜̅̐p̗̞̜̉̆̕s̟̜̘̍̑̏ū̟̞̎̃̉ḿ̘̙́́̐ ̖̍̌̇̉̚d̞̄̃̒̉̎ò́̌̌̂̐l̞̀̄̆̌̚ȯ̖̞̋̀̐r̓̇̌̃̃̚ ̗̘̀̏̍́s̜̀̎̎̑̕i̟̗̐̄̄̚t̝̎̆̓̐̒ ̘̇̔̓̊̚ȃ̛̟̗̏̅m̜̟̙̞̈̓é̘̞̟̔̆t̝̂̂̈̑̔,̜̜̖̅̄̍ ̛̗̊̓̆̚c̟̍̆̍̈̔ȯ̖̖̝̑̀n̜̟̎̊̃̚s̟̏̇̎̒̚e̙̐̈̓̌̚c̙̍̈̏̅̕ť̇̄̇̆̓e̛̓̌̈̓̈t̟̍̀̉̆̅u̝̞̎̂̄̚r̘̀̅̈̅̐ ̝̞̓́̇̉ã̏̀̆̅̕d̛̆̐̉̆̋ȉ̞̟̍̃̚p̛̜̊̍̂̓ȋ̏̅̃̋̚ṥ̛̏̃̕č̛̞̝̀̂í̗̘̌́̎n̔̎́̒̂̕ǧ̗̜̋̇̂ ̛̜̔̄̎̃ê̛̔̆̇̕l̘̝̏̐̊̏ĩ̛̍̏̏̄t̟̐́̀̐̎,̙̘̍̆̉̐ ̋̂̏̄̌̅s̙̓̌̈́̇e̛̗̋̒̎̏d̜̗̊̍̊̚ + ď̘̋̌̌̕ǒ̝̗̔̇̕ ̙̍́̄̄̉è̛̛̞̌̌i̜̖̐̈̆̚ȕ̇̈̓̃̓ŝ̛̞̙̉̋m̜̐̂̄̋̂ȯ̈̎̎̅̕d̜̙̓̔̋̑ ̞̗̄̂̂̚t̝̊́̃́̄e̛̘̜̞̓̑m̊̅̏̉̌̕p̛̈̂̇̀̐ỏ̙̘̈̉̔r̘̞̋̍̃̚ ̝̄̀̇̅̇ỉ̛̖̍̓̈n̛̛̝̎̕̕c̛̛̊̅́̐ĭ̗̓̀̍̐d̞̜̋̐̅̚i̟̙̇̄̊̄d̞̊̂̀̇̚ủ̝̉̑̃̕n̜̏̇̄̐̋ť̗̜̞̋̉ ̝̒̓̌̓̚ȕ̖̙̀̚̕t̖̘̎̉̂̌ ̛̝̄̍̌̂l̛̟̝̃̑̋á̛̝̝̔̅b̝̙̜̗̅̒ơ̖̌̒̄̆r̒̇̓̎̈̄e̛̛̖̅̏̇ ̖̗̜̝̃́e̛̛̘̅̔̌ẗ̛̙̗̐̕ ̖̟̇̋̌̈d̞̙̀̉̑̕ŏ̝̂́̐̑l̞̟̗̓̓̀ơ̘̎̃̄̂r̗̗̖̔̆̍ẻ̖̝̞̋̅ ̜̌̇̍̈̊m̈̉̇̄̒̀a̜̞̘̔̅̆g̗̖̈̃̈̉n̙̖̄̈̉̄â̛̝̜̄̃ ̛́̎̕̕̚ā̊́́̆̌l̟̙̞̃̒́i̖̇̎̃̀̋q̟̇̒̆́̊ủ́̌̇̑̚ã̛̘̉̐̚.̛́̏̐̍̊ + U̝̙̎̈̐̆t̜̍̌̀̔̏ ̞̉̍̇̈̃e̟̟̊̄̕̕n̝̜̒̓̆̕i̖̒̌̅̇̚m̞̊̃̔̊̂ ̛̜̊̎̄̂a̘̜̋̒̚̚d̟̊̎̇̂̍ ̜̖̏̑̉̕m̜̒̎̅̄̚i̝̖̓̂̍̕n̙̉̒̑̀̔ỉ̖̝̌̒́m̛̖̘̅̆̎ ̖̉̎̒̌̕v̖̞̀̔́̎e̖̙̗̒̎̉n̛̗̝̎̀̂ȉ̞̗̒̕̚ȧ̟̜̝̅̚m̆̉̐̐̇̈,̏̐̎́̍́ ̜̞̙̘̏̆q̙̖̙̅̓̂ủ̇́̀̔̚í̙̟̟̏̐s̖̝̍̏̂̇ ̛̘̋̈̕̕ń̛̞̜̜̎o̗̜̔̔̈̆s̞̘̘̄̒̋t̛̅̋́̔̈ȓ̓̒́̇̅ủ̜̄̃̒̍d̙̝̘̊̏̚ ̛̟̞̄́̔e̛̗̝̍̃̀x̞̖̃̄̂̅e̖̅̇̐̔̃r̗̞̖̔̎̚c̘̜̖̆̊̏ï̙̝̙̂̕t̖̏́̓̋̂ă̖̄̆̑̒t̜̟̍̉̑̏i̛̞̞̘̒̑ǒ̜̆̅̃̉ṅ̖̜̒̎̚ + u̗̞̓̔̈̏ĺ̟̝́̎̚l̛̜̅̌̎̆a̒̑̆̔̇̃m̜̗̈̊̎̚ċ̘̋̇̂̚ơ̟̖̊́̕ ̖̟̍̉̏̚l̙̔̓̀̅̏ä̞̗̘̙̅ḃ̟̎̄̃̕o̞̎̓̓̓̚r̗̜̊̓̈̒ï̗̜̃̃̅s̀̒̌̂̎̂ ̖̗̗̋̎̐n̝̟̝̘̄̚i̜̒̀̒̐̕s̘̘̄̊̃̀ī̘̜̏̌̕ ̗̖̞̐̈̒ư̙̞̄́̌t̟̘̖̙̊̚ ̌̅̋̆̚̚ä̇̊̇̕̕l̝̞̘̋̔̅i̍̋́̆̑̈q̛̆̐̈̐̚ư̏̆̊́̚î̜̝̑́̊p̗̓̅̑̆̏ ̆́̓̔̋̋e̟̊̋̏̓̚x̗̍̑̊̎̈ ̟̞̆̄̂̍ë̄̎̄̃̅a̛̜̅́̃̈ ̔̋̀̎̐̀c̖̖̍̀̒̂ơ̛̙̖̄̒m̘̔̍̏̆̕ḿ̖̙̝̏̂ȍ̓̋̈̀̕d̆̂̊̅̓̚o̖̔̌̑̚̕ ̙̆́̔̊̒c̖̘̖̀̄̍o̓̄̑̐̓̒ñ̞̒̎̈̚s̞̜̘̈̄̄e̙̊̀̇̌̋q̐̒̓́̔̃ư̗̟̔̔̚å̖̙̞̄̏t̛̙̟̒̇̏.̙̗̓̃̓̎ + D̜̖̆̏̌̌ư̑̃̌̍̕i̝̊̊̊̊̄s̛̙̒́̌̇ ̛̃̔̄̆̌ă̘̔̅̅̀ú̟̟̟̃̃t̟̂̄̈̈̃e̘̅̌̒̂̆ ̖̟̐̉̉̌î̟̟̙̜̇r̛̙̞̗̄̌ú̗̗̃̌̎r̛̙̘̉̊̕e̒̐̔̃̓̋ ̊̊̍̋̑̉d̛̝̙̉̀̓o̘̜̐̐̓̐l̞̋̌̆̍́o̊̊̐̃̃̚ṙ̛̖̘̃̕ ̞̊̀̍̒̕ȉ́̑̐̇̅ǹ̜̗̜̞̏ ̛̜̐̄̄̚r̜̖̈̇̅̋ĕ̗̉̃̔̚p̟̝̀̓̔̆r̜̈̆̇̃̃e̘̔̔̏̎̓h̗̒̉̑̆̚ė̛̘̘̈̐n̘̂̀̒̕̕d̗̅̂̋̅́ê̗̜̜̜̕r̟̋̄̐̅̂i̛̔̌̒̂̕t̛̗̓̎̀̎ ̙̗̀̉̂̚ȉ̟̗̐̓̚n̙̂̍̏̓̉ ̙̘̊̋̍̕v̜̖̀̎̆̐ő̜̆̉̃̎l̑̋̒̉̔̆ư̙̓̓́̚p̝̘̖̎̏̒t̛̘̝̞̂̓ȁ̘̆̔́̊t̖̝̉̒̐̎e̞̟̋̀̅̄ ̆̌̃̀̑̔v̝̘̝̍̀̇ȅ̝̊̄̓̕l̞̝̑̔̂̋ĭ̝̄̅̆̍t̝̜̉̂̈̇ + ē̟̊̇̕̚s̖̘̘̒̄̑s̛̘̀̊̆̇e̛̝̘̒̏̚ ̉̅̑̂̐̎c̛̟̙̎̋̓i̜̇̒̏̆̆l̟̄́̆̊̌l̍̊̋̃̆̌ủ̗̙̒̔̚m̛̘̘̖̅̍ ̖̙̈̎̂̕d̞̟̏̋̈̔ơ̟̝̌̃̄l̗̙̝̂̉̒õ̒̃̄̄̚ŕ̗̏̏̊̍ê̞̝̞̋̈ ̜̔̒̎̃̚e̞̟̞̒̃̄ư̖̏̄̑̃ ̛̗̜̄̓̎f̛̖̞̅̓̃ü̞̏̆̋̕g̜̝̞̑̑̆i̛̘̐̐̅̚à̜̖̌̆̎t̙̙̎̉̂̍ ̋̔̈̎̎̉n̞̓́̔̊̕ư̘̅̋̔̚l̗̍̒̄̀̚l̞̗̘̙̓̍â̘̔̒̎̚ ̖̓̋̉̃̆p̛̛̘̋̌̀ä̙̔́̒̕r̟̟̖̋̐̋ì̗̙̎̓̓ȃ̔̋̑̚̕t̄́̎̓̂̋ư̏̈̂̑̃r̖̓̋̊̚̚.̒̆̑̆̊̎ ̘̜̍̐̂̚E̞̅̐̇́̂x̄́̈̌̉̕ć̘̃̉̃̕è̘̂̑̏̑p̝̘̑̂̌̆t̔̐̅̍̌̂ȇ̞̈̐̚̕ű̝̞̜́̚ŕ̗̝̉̆́ + š̟́̔̏̀ȉ̝̟̝̏̅n̑̆̇̒̆̚t̝̒́̅̋̏ ̗̑̌̋̇̚ơ̙̗̟̆̅c̙̞̙̎̊̎c̘̟̍̔̊̊a̛̒̓̉́̐e̜̘̙̒̅̇ć̝̝̂̇̕ả̓̍̎̂̚t̗̗̗̟̒̃ ̘̒̓̐̇́c̟̞̉̐̓̄ȕ̙̗̅́̏p̛̍̋̈́̅i̖̓̒̍̈̄d̞̃̈̌̆̐a̛̗̝̎̋̉t̞̙̀̊̆̇a̛̙̒̆̉̚t̜̟̘̉̓̚ ̝̘̗̐̇̕n̛̘̑̏̂́ō̑̋̉̏́ň̞̊̆̄̃ ̙̙̙̜̄̏p̒̆̋̋̓̏r̖̖̅̉́̚ơ̜̆̑̈̚i̟̒̀̃̂̌d̛̏̃̍̋̚ë̖̞̙̗̓n̛̘̓̒̅̎t̟̗̙̊̆̚,̘̙̔̊̚̕ ̟̗̘̜̑̔s̜̝̍̀̓̌û̞̙̅̇́n̘̗̝̒̃̎t̗̅̀̅̊̈ ̗̖̅̅̀̄i̛̖̍̅̋̂n̙̝̓̓̎̚ ̞̋̅̋̃̚c̗̒̀̆̌̎ū̞̂̑̌̓ĺ̛̐̍̑́p̝̆̌̎̈̚a̖̙̒̅̈̌ ̝̝̜̂̈̀q̝̖̔̍̒̚ư̔̐̂̎̊ǐ̛̟̖̘̕ + o̖̜̔̋̅̚f̛̊̀̉́̕f̏̉̀̔̃̃i̘̍̎̐̔̎c̙̅̑̂̐̅ȋ̛̜̀̒̚a̋̍̇̏̀̋ ̖̘̒̅̃̒d̗̘̓̈̇̋é̝́̎̒̄š̙̒̊̉̋e̖̓̐̀̍̕r̗̞̂̅̇̄ù̘̇̐̉̀n̐̑̀̄̍̐t̟̀̂̊̄̚ ̟̝̂̍̏́m̜̗̈̂̏̚ő̞̊̑̇̒l̘̑̏́̔̄l̛̛̇̃̋̊i̓̋̒̃̉̌t̛̗̜̏̀̋ ̙̟̒̂̌̐a̙̝̔̆̏̅n̝̙̙̗̆̅i̍̔́̊̃̕m̖̝̟̒̍̚ ̛̃̃̑̌́ǐ̘̉̔̅̚d̝̗̀̌̏̒ ̖̝̓̑̊̚ȇ̞̟̖̌̕š̙̙̈̔̀t̂̉̒̍̄̄ ̝̗̊̋̌̄l̛̞̜̙̘̔å̝̍̂̍̅b̜̆̇̈̉̌ǒ̜̙̎̃̆r̝̀̄̍́̕ư̋̊́̊̕m̜̗̒̐̕̚.̟̘̀̒̌̚]], + '\n')) + + -- tests that we can handle overflow of the buffer + -- for redraw events (4096 bytes) gracefully + screen:expect{grid=[[ + ^L̓̉̑̒̌̚ơ̗̌̒̄̀ŕ̈̈̎̐̕è̇̅̄̄̐m̖̟̟̅̄̚ ̛̓̑̆̇̍i̗̟̞̜̅̐p̗̞̜̉̆̕s̟̜̘̍̑̏ū̟̞̎̃̉ḿ̘̙́́̐ ̖̍̌̇̉̚d̞̄̃̒̉̎ò́̌̌̂̐l̞̀̄̆̌̚ȯ̖̞̋̀̐r̓̇̌̃̃̚ ̗̘̀̏̍́s̜̀̎̎̑̕i̟̗̐̄̄̚t̝̎̆̓̐̒ ̘̇̔̓̊̚ȃ̛̟̗̏̅m̜̟̙̞̈̓é̘̞̟̔̆t̝̂̂̈̑̔,̜̜̖̅̄̍ ̛̗̊̓̆̚c̟̍̆̍̈̔ȯ̖̖̝̑̀n̜̟̎̊̃̚s̟̏̇̎̒̚e̙̐̈̓̌̚c̙̍̈̏̅̕ť̇̄̇̆̓e̛̓̌̈̓̈t̟̍̀̉̆̅u̝̞̎̂̄̚r̘̀̅̈̅̐ ̝̞̓́̇̉ã̏̀̆̅̕d̛̆̐̉̆̋ȉ̞̟̍̃̚p̛̜̊̍̂̓ȋ̏̅̃̋̚ṥ̛̏̃̕č̛̞̝̀̂í̗̘̌́̎n̔̎́̒̂̕ǧ̗̜̋̇̂ ̛̜̔̄̎̃ê̛̔̆̇̕l̘̝̏̐̊̏ĩ̛̍̏̏̄t̟̐́̀̐̎,̙̘̍̆̉̐ ̋̂̏̄̌̅s̙̓̌̈́̇e̛̗̋̒̎̏d̜̗̊̍̊̚ | + ď̘̋̌̌̕ǒ̝̗̔̇̕ ̙̍́̄̄̉è̛̛̞̌̌i̜̖̐̈̆̚ȕ̇̈̓̃̓ŝ̛̞̙̉̋m̜̐̂̄̋̂ȯ̈̎̎̅̕d̜̙̓̔̋̑ ̞̗̄̂̂̚t̝̊́̃́̄e̛̘̜̞̓̑m̊̅̏̉̌̕p̛̈̂̇̀̐ỏ̙̘̈̉̔r̘̞̋̍̃̚ ̝̄̀̇̅̇ỉ̛̖̍̓̈n̛̛̝̎̕̕c̛̛̊̅́̐ĭ̗̓̀̍̐d̞̜̋̐̅̚i̟̙̇̄̊̄d̞̊̂̀̇̚ủ̝̉̑̃̕n̜̏̇̄̐̋ť̗̜̞̋̉ ̝̒̓̌̓̚ȕ̖̙̀̚̕t̖̘̎̉̂̌ ̛̝̄̍̌̂l̛̟̝̃̑̋á̛̝̝̔̅b̝̙̜̗̅̒ơ̖̌̒̄̆r̒̇̓̎̈̄e̛̛̖̅̏̇ ̖̗̜̝̃́e̛̛̘̅̔̌ẗ̛̙̗̐̕ ̖̟̇̋̌̈d̞̙̀̉̑̕ŏ̝̂́̐̑l̞̟̗̓̓̀ơ̘̎̃̄̂r̗̗̖̔̆̍ẻ̖̝̞̋̅ ̜̌̇̍̈̊m̈̉̇̄̒̀a̜̞̘̔̅̆g̗̖̈̃̈̉n̙̖̄̈̉̄â̛̝̜̄̃ ̛́̎̕̕̚ā̊́́̆̌l̟̙̞̃̒́i̖̇̎̃̀̋q̟̇̒̆́̊ủ́̌̇̑̚ã̛̘̉̐̚.̛́̏̐̍̊ | + U̝̙̎̈̐̆t̜̍̌̀̔̏ ̞̉̍̇̈̃e̟̟̊̄̕̕n̝̜̒̓̆̕i̖̒̌̅̇̚m̞̊̃̔̊̂ ̛̜̊̎̄̂a̘̜̋̒̚̚d̟̊̎̇̂̍ ̜̖̏̑̉̕m̜̒̎̅̄̚i̝̖̓̂̍̕n̙̉̒̑̀̔ỉ̖̝̌̒́m̛̖̘̅̆̎ ̖̉̎̒̌̕v̖̞̀̔́̎e̖̙̗̒̎̉n̛̗̝̎̀̂ȉ̞̗̒̕̚ȧ̟̜̝̅̚m̆̉̐̐̇̈,̏̐̎́̍́ ̜̞̙̘̏̆q̙̖̙̅̓̂ủ̇́̀̔̚í̙̟̟̏̐s̖̝̍̏̂̇ ̛̘̋̈̕̕ń̛̞̜̜̎o̗̜̔̔̈̆s̞̘̘̄̒̋t̛̅̋́̔̈ȓ̓̒́̇̅ủ̜̄̃̒̍d̙̝̘̊̏̚ ̛̟̞̄́̔e̛̗̝̍̃̀x̞̖̃̄̂̅e̖̅̇̐̔̃r̗̞̖̔̎̚c̘̜̖̆̊̏ï̙̝̙̂̕t̖̏́̓̋̂ă̖̄̆̑̒t̜̟̍̉̑̏i̛̞̞̘̒̑ǒ̜̆̅̃̉ṅ̖̜̒̎̚ | + u̗̞̓̔̈̏ĺ̟̝́̎̚l̛̜̅̌̎̆a̒̑̆̔̇̃m̜̗̈̊̎̚ċ̘̋̇̂̚ơ̟̖̊́̕ ̖̟̍̉̏̚l̙̔̓̀̅̏ä̞̗̘̙̅ḃ̟̎̄̃̕o̞̎̓̓̓̚r̗̜̊̓̈̒ï̗̜̃̃̅s̀̒̌̂̎̂ ̖̗̗̋̎̐n̝̟̝̘̄̚i̜̒̀̒̐̕s̘̘̄̊̃̀ī̘̜̏̌̕ ̗̖̞̐̈̒ư̙̞̄́̌t̟̘̖̙̊̚ ̌̅̋̆̚̚ä̇̊̇̕̕l̝̞̘̋̔̅i̍̋́̆̑̈q̛̆̐̈̐̚ư̏̆̊́̚î̜̝̑́̊p̗̓̅̑̆̏ ̆́̓̔̋̋e̟̊̋̏̓̚x̗̍̑̊̎̈ ̟̞̆̄̂̍ë̄̎̄̃̅a̛̜̅́̃̈ ̔̋̀̎̐̀c̖̖̍̀̒̂ơ̛̙̖̄̒m̘̔̍̏̆̕ḿ̖̙̝̏̂ȍ̓̋̈̀̕d̆̂̊̅̓̚o̖̔̌̑̚̕ ̙̆́̔̊̒c̖̘̖̀̄̍o̓̄̑̐̓̒ñ̞̒̎̈̚s̞̜̘̈̄̄e̙̊̀̇̌̋q̐̒̓́̔̃ư̗̟̔̔̚å̖̙̞̄̏t̛̙̟̒̇̏.̙̗̓̃̓̎ | + D̜̖̆̏̌̌ư̑̃̌̍̕i̝̊̊̊̊̄s̛̙̒́̌̇ ̛̃̔̄̆̌ă̘̔̅̅̀ú̟̟̟̃̃t̟̂̄̈̈̃e̘̅̌̒̂̆ ̖̟̐̉̉̌î̟̟̙̜̇r̛̙̞̗̄̌ú̗̗̃̌̎r̛̙̘̉̊̕e̒̐̔̃̓̋ ̊̊̍̋̑̉d̛̝̙̉̀̓o̘̜̐̐̓̐l̞̋̌̆̍́o̊̊̐̃̃̚ṙ̛̖̘̃̕ ̞̊̀̍̒̕ȉ́̑̐̇̅ǹ̜̗̜̞̏ ̛̜̐̄̄̚r̜̖̈̇̅̋ĕ̗̉̃̔̚p̟̝̀̓̔̆r̜̈̆̇̃̃e̘̔̔̏̎̓h̗̒̉̑̆̚ė̛̘̘̈̐n̘̂̀̒̕̕d̗̅̂̋̅́ê̗̜̜̜̕r̟̋̄̐̅̂i̛̔̌̒̂̕t̛̗̓̎̀̎ ̙̗̀̉̂̚ȉ̟̗̐̓̚n̙̂̍̏̓̉ ̙̘̊̋̍̕v̜̖̀̎̆̐ő̜̆̉̃̎l̑̋̒̉̔̆ư̙̓̓́̚p̝̘̖̎̏̒t̛̘̝̞̂̓ȁ̘̆̔́̊t̖̝̉̒̐̎e̞̟̋̀̅̄ ̆̌̃̀̑̔v̝̘̝̍̀̇ȅ̝̊̄̓̕l̞̝̑̔̂̋ĭ̝̄̅̆̍t̝̜̉̂̈̇ | + ē̟̊̇̕̚s̖̘̘̒̄̑s̛̘̀̊̆̇e̛̝̘̒̏̚ ̉̅̑̂̐̎c̛̟̙̎̋̓i̜̇̒̏̆̆l̟̄́̆̊̌l̍̊̋̃̆̌ủ̗̙̒̔̚m̛̘̘̖̅̍ ̖̙̈̎̂̕d̞̟̏̋̈̔ơ̟̝̌̃̄l̗̙̝̂̉̒õ̒̃̄̄̚ŕ̗̏̏̊̍ê̞̝̞̋̈ ̜̔̒̎̃̚e̞̟̞̒̃̄ư̖̏̄̑̃ ̛̗̜̄̓̎f̛̖̞̅̓̃ü̞̏̆̋̕g̜̝̞̑̑̆i̛̘̐̐̅̚à̜̖̌̆̎t̙̙̎̉̂̍ ̋̔̈̎̎̉n̞̓́̔̊̕ư̘̅̋̔̚l̗̍̒̄̀̚l̞̗̘̙̓̍â̘̔̒̎̚ ̖̓̋̉̃̆p̛̛̘̋̌̀ä̙̔́̒̕r̟̟̖̋̐̋ì̗̙̎̓̓ȃ̔̋̑̚̕t̄́̎̓̂̋ư̏̈̂̑̃r̖̓̋̊̚̚.̒̆̑̆̊̎ ̘̜̍̐̂̚E̞̅̐̇́̂x̄́̈̌̉̕ć̘̃̉̃̕è̘̂̑̏̑p̝̘̑̂̌̆t̔̐̅̍̌̂ȇ̞̈̐̚̕ű̝̞̜́̚ŕ̗̝̉̆́ | + š̟́̔̏̀ȉ̝̟̝̏̅n̑̆̇̒̆̚t̝̒́̅̋̏ ̗̑̌̋̇̚ơ̙̗̟̆̅c̙̞̙̎̊̎c̘̟̍̔̊̊a̛̒̓̉́̐e̜̘̙̒̅̇ć̝̝̂̇̕ả̓̍̎̂̚t̗̗̗̟̒̃ ̘̒̓̐̇́c̟̞̉̐̓̄ȕ̙̗̅́̏p̛̍̋̈́̅i̖̓̒̍̈̄d̞̃̈̌̆̐a̛̗̝̎̋̉t̞̙̀̊̆̇a̛̙̒̆̉̚t̜̟̘̉̓̚ ̝̘̗̐̇̕n̛̘̑̏̂́ō̑̋̉̏́ň̞̊̆̄̃ ̙̙̙̜̄̏p̒̆̋̋̓̏r̖̖̅̉́̚ơ̜̆̑̈̚i̟̒̀̃̂̌d̛̏̃̍̋̚ë̖̞̙̗̓n̛̘̓̒̅̎t̟̗̙̊̆̚,̘̙̔̊̚̕ ̟̗̘̜̑̔s̜̝̍̀̓̌û̞̙̅̇́n̘̗̝̒̃̎t̗̅̀̅̊̈ ̗̖̅̅̀̄i̛̖̍̅̋̂n̙̝̓̓̎̚ ̞̋̅̋̃̚c̗̒̀̆̌̎ū̞̂̑̌̓ĺ̛̐̍̑́p̝̆̌̎̈̚a̖̙̒̅̈̌ ̝̝̜̂̈̀q̝̖̔̍̒̚ư̔̐̂̎̊ǐ̛̟̖̘̕ | + o̖̜̔̋̅̚f̛̊̀̉́̕f̏̉̀̔̃̃i̘̍̎̐̔̎c̙̅̑̂̐̅ȋ̛̜̀̒̚a̋̍̇̏̀̋ ̖̘̒̅̃̒d̗̘̓̈̇̋é̝́̎̒̄š̙̒̊̉̋e̖̓̐̀̍̕r̗̞̂̅̇̄ù̘̇̐̉̀n̐̑̀̄̍̐t̟̀̂̊̄̚ ̟̝̂̍̏́m̜̗̈̂̏̚ő̞̊̑̇̒l̘̑̏́̔̄l̛̛̇̃̋̊i̓̋̒̃̉̌t̛̗̜̏̀̋ ̙̟̒̂̌̐a̙̝̔̆̏̅n̝̙̙̗̆̅i̍̔́̊̃̕m̖̝̟̒̍̚ ̛̃̃̑̌́ǐ̘̉̔̅̚d̝̗̀̌̏̒ ̖̝̓̑̊̚ȇ̞̟̖̌̕š̙̙̈̔̀t̂̉̒̍̄̄ ̝̗̊̋̌̄l̛̞̜̙̘̔å̝̍̂̍̅b̜̆̇̈̉̌ǒ̜̙̎̃̆r̝̀̄̍́̕ư̋̊́̊̕m̜̗̒̐̕̚.̟̘̀̒̌̚ | + {1:~ }| + | + ]]} + end) end) describe('multibyte rendering: statusline', function() |