Merge branch 'master' into colored-cmdline

This commit is contained in:
ZyX 2017-07-15 18:56:45 +03:00
commit 69719e658c
112 changed files with 7029 additions and 3745 deletions

View File

@ -51,14 +51,14 @@ endif()
# Set default build type. # Set default build type.
if(NOT CMAKE_BUILD_TYPE) if(NOT CMAKE_BUILD_TYPE)
message(STATUS "CMAKE_BUILD_TYPE not given, defaulting to 'Dev'.") message(STATUS "CMAKE_BUILD_TYPE not given, defaulting to 'Debug'.")
set(CMAKE_BUILD_TYPE "Dev" CACHE STRING "Choose the type of build." FORCE) set(CMAKE_BUILD_TYPE "Debug" CACHE STRING "Choose the type of build." FORCE)
endif() endif()
# Set available build types for CMake GUIs. # Set available build types for CMake GUIs.
# A different build type can still be set by -DCMAKE_BUILD_TYPE=... # A different build type can still be set by -DCMAKE_BUILD_TYPE=...
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY set_property(CACHE CMAKE_BUILD_TYPE PROPERTY
STRINGS "Debug" "Dev" "Release" "MinSizeRel" "RelWithDebInfo") STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo")
# If not in a git repo (e.g., a tarball) these tokens define the complete # If not in a git repo (e.g., a tarball) these tokens define the complete
# version string, else they are combined with the result of `git describe`. # version string, else they are combined with the result of `git describe`.
@ -107,46 +107,24 @@ if(NOT CMAKE_C_FLAGS_RELWITHDEBINFO MATCHES DMIN_LOG_LEVEL)
set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO} -DMIN_LOG_LEVEL=3") set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO} -DMIN_LOG_LEVEL=3")
endif() endif()
# Enable assertions for RelWithDebInfo. if(CMAKE_COMPILER_IS_GNUCC)
if(CMAKE_C_FLAGS_RELWITHDEBINFO MATCHES DNDEBUG) check_c_compiler_flag(-Og HAS_OG_FLAG)
else()
set(HAS_OG_FLAG 0)
endif()
# Set custom build flags for RelWithDebInfo.
# -DNDEBUG purposely omitted because we want assertions.
if(HAS_OG_FLAG)
set(CMAKE_C_FLAGS_RELWITHDEBINFO "-Og -g"
CACHE STRING "Flags used by the compiler during release-with-debug builds." FORCE)
elseif(NOT MSVC)
set(CMAKE_C_FLAGS_RELWITHDEBINFO "-O2 -g"
CACHE STRING "Flags used by the compiler during release-with-debug builds." FORCE)
elseif(CMAKE_C_FLAGS_RELWITHDEBINFO MATCHES DNDEBUG)
string(REPLACE "-DNDEBUG" "" CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO}") string(REPLACE "-DNDEBUG" "" CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO}")
endif() endif()
# Set build flags for custom Dev build type.
# -DNDEBUG purposely omitted because we want assertions.
if(MSVC)
SET(CMAKE_C_FLAGS_DEV ""
CACHE STRING "Flags used by the compiler during development (optimized, but with debug info and logging) builds."
FORCE)
else()
if(CMAKE_COMPILER_IS_GNUCC)
check_c_compiler_flag(-Og HAS_OG_FLAG)
else()
set(HAS_OG_FLAG 0)
endif()
if(HAS_OG_FLAG)
set(CMAKE_C_FLAGS_DEV "-Og -g"
CACHE STRING "Flags used by the compiler during development (optimized, but with debug info and logging) builds."
FORCE)
else()
set(CMAKE_C_FLAGS_DEV "-O2 -g"
CACHE STRING "Flags used by the compiler during development (optimized, but with debug info and logging) builds."
FORCE)
endif()
endif()
SET(CMAKE_EXE_LINKER_FLAGS_DEV ""
CACHE STRING "Flags used for linking binaries during development (optimized, but with debug info and logging) builds."
FORCE)
SET(CMAKE_SHARED_LINKER_FLAGS_DEV ""
CACHE STRING "Flags used by the shared libraries linker during development (optimized, but with debug info and logging) builds."
FORCE)
MARK_AS_ADVANCED(
CMAKE_C_FLAGS_DEV
CMAKE_EXE_LINKER_FLAGS_DEV
CMAKE_SHARED_LINKER_FLAGS_DEV)
# Enable -Wconversion. # Enable -Wconversion.
if(NOT MSVC) if(NOT MSVC)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wconversion") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wconversion")
@ -600,9 +578,26 @@ if(LUACHECK_PRG)
add_custom_target(testlint add_custom_target(testlint
COMMAND ${CMAKE_COMMAND} COMMAND ${CMAKE_COMMAND}
-DLUACHECK_PRG=${LUACHECK_PRG} -DLUACHECK_PRG=${LUACHECK_PRG}
-DTEST_DIR=${CMAKE_CURRENT_SOURCE_DIR}/test -DLUAFILES_DIR=${CMAKE_CURRENT_SOURCE_DIR}/test
-DIGNORE_PATTERN="*/preload.lua"
-DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME} -DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME}
-P ${PROJECT_SOURCE_DIR}/cmake/RunTestsLint.cmake) -P ${PROJECT_SOURCE_DIR}/cmake/RunLuacheck.cmake)
add_custom_target(
blobcodelint
COMMAND
${CMAKE_COMMAND}
-DLUACHECK_PRG=${LUACHECK_PRG}
-DLUAFILES_DIR=${CMAKE_CURRENT_SOURCE_DIR}/src/nvim/lua
-DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME}
-DREAD_GLOBALS=vim
-P ${PROJECT_SOURCE_DIR}/cmake/RunLuacheck.cmake
)
# TODO(ZyX-I): Run linter for all lua code in src
add_custom_target(
lualint
DEPENDS blobcodelint
)
endif() endif()
set(CPACK_PACKAGE_NAME "Neovim") set(CPACK_PACKAGE_NAME "Neovim")

View File

@ -1,16 +1,19 @@
<!-- Before reporting: search existing issues and check the FAQ. -->
- `nvim --version`: - `nvim --version`:
- Vim (version: ) behaves differently? - Vim (version: ) behaves differently?
- Operating system/version: - Operating system/version:
- Terminal name/version: - Terminal name/version:
- `$TERM`: - `$TERM`:
### Actual behaviour
### Expected behaviour
### Steps to reproduce using `nvim -u NORC` ### Steps to reproduce using `nvim -u NORC`
``` ```
nvim -u NORC nvim -u NORC
``` ```
### Actual behaviour
### Expected behaviour

View File

@ -107,6 +107,9 @@ functionaltest-lua: | nvim
testlint: | build/.ran-cmake deps testlint: | build/.ran-cmake deps
$(BUILD_CMD) -C build testlint $(BUILD_CMD) -C build testlint
lualint: | build/.ran-cmake deps
$(BUILD_CMD) -C build lualint
unittest: | nvim unittest: | nvim
+$(BUILD_CMD) -C build unittest +$(BUILD_CMD) -C build unittest
@ -138,6 +141,6 @@ check-single-includes: build/.ran-cmake
appimage: appimage:
bash scripts/genappimage.sh bash scripts/genappimage.sh
lint: check-single-includes clint testlint lint: check-single-includes clint testlint lualint
.PHONY: test testlint functionaltest unittest lint clint clean distclean nvim libnvim cmake deps install appimage .PHONY: test testlint lualint functionaltest unittest lint clint clean distclean nvim libnvim cmake deps install appimage

View File

@ -20,6 +20,12 @@ run_test 'top_make testlint' testlint
exit_suite --continue exit_suite --continue
enter_suite 'lualint'
run_test 'top_make lualint' lualint
exit_suite --continue
enter_suite single-includes enter_suite single-includes
CLICOLOR_FORCE=1 run_test_wd \ CLICOLOR_FORCE=1 run_test_wd \

View File

@ -27,6 +27,9 @@ find_path(JEMALLOC_INCLUDE_DIR jemalloc/jemalloc.h
if(JEMALLOC_USE_STATIC) if(JEMALLOC_USE_STATIC)
list(APPEND JEMALLOC_NAMES list(APPEND JEMALLOC_NAMES
"${CMAKE_STATIC_LIBRARY_PREFIX}jemalloc${CMAKE_STATIC_LIBRARY_SUFFIX}") "${CMAKE_STATIC_LIBRARY_PREFIX}jemalloc${CMAKE_STATIC_LIBRARY_SUFFIX}")
elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
list(INSERT JEMALLOC_NAMES 0
"${CMAKE_STATIC_LIBRARY_PREFIX}jemalloc${CMAKE_STATIC_LIBRARY_SUFFIX}")
endif() endif()
list(APPEND JEMALLOC_NAMES jemalloc) list(APPEND JEMALLOC_NAMES jemalloc)

22
cmake/RunLuacheck.cmake Normal file
View File

@ -0,0 +1,22 @@
set(LUACHECK_ARGS -q "${LUAFILES_DIR}")
if(DEFINED IGNORE_PATTERN)
list(APPEND LUACHECK_ARGS --exclude-files "${LUAFILES_DIR}/${IGNORE_PATTERN}")
endif()
if(DEFINED CHECK_PATTERN)
list(APPEND LUACHECK_ARGS --include-files "${LUAFILES_DIR}/${CHECK_PATTERN}")
endif()
if(DEFINED READ_GLOBALS)
list(APPEND LUACHECK_ARGS --read-globals "${READ_GLOBALS}")
endif()
execute_process(
COMMAND "${LUACHECK_PRG}" ${LUACHECK_ARGS}
WORKING_DIRECTORY "${LUAFILES_DIR}"
ERROR_VARIABLE err
RESULT_VARIABLE res
)
if(NOT res EQUAL 0)
message(STATUS "Output to stderr:\n${err}")
message(FATAL_ERROR "Linting tests failed with error: ${res}.")
endif()

View File

@ -1,13 +0,0 @@
set(IGNORE_FILES "${TEST_DIR}/*/preload.lua")
execute_process(
COMMAND ${LUACHECK_PRG} -q ${TEST_DIR} --exclude-files ${IGNORE_FILES}
WORKING_DIRECTORY ${TEST_DIR}
ERROR_VARIABLE err
RESULT_VARIABLE res
${EXTRA_ARGS})
if(NOT res EQUAL 0)
message(STATUS "Output to stderr:\n${err}")
message(FATAL_ERROR "Linting tests failed with error: ${res}.")
endif()

View File

@ -13,26 +13,21 @@
# Sets the build type; defaults to Debug. Valid values: # Sets the build type; defaults to Debug. Valid values:
# #
# - Debug: Disables optimizations (-O0), enables debug information and logging. # - Debug: Disables optimizations (-O0), enables debug information.
# #
# - Dev: Enables all optimizations that do not interfere with # - RelWithDebInfo: Enables optimizations (-Og or -O2) with debug information.
# debugging (-Og if available, -O2 and -g if not).
# Enables debug information and logging.
#
# - RelWithDebInfo: Enables optimizations (-O2) and debug information.
# Disables logging.
# #
# - MinSizeRel: Enables all -O2 optimization that do not typically # - MinSizeRel: Enables all -O2 optimization that do not typically
# increase code size, and performs further optimizations # increase code size, and performs further optimizations
# designed to reduce code size (-Os). # designed to reduce code size (-Os).
# Disables debug information and logging. # Disables debug information.
# #
# - Release: Same as RelWithDebInfo, but disables debug information. # - Release: Same as RelWithDebInfo, but disables debug information.
# #
# CMAKE_BUILD_TYPE := Debug # CMAKE_BUILD_TYPE := Debug
# The default log level is 1 (INFO) (unless CMAKE_BUILD_TYPE is "Release").
# Log levels: 0 (DEBUG), 1 (INFO), 2 (WARNING), 3 (ERROR) # Log levels: 0 (DEBUG), 1 (INFO), 2 (WARNING), 3 (ERROR)
# Default is 1 (INFO) unless CMAKE_BUILD_TYPE is Release or RelWithDebInfo.
# CMAKE_EXTRA_FLAGS += -DMIN_LOG_LEVEL=1 # CMAKE_EXTRA_FLAGS += -DMIN_LOG_LEVEL=1
# By default, nvim uses bundled versions of its required third-party # By default, nvim uses bundled versions of its required third-party

View File

@ -33,7 +33,8 @@ function! health#check(plugin_names) abort
setlocal wrap breakindent setlocal wrap breakindent
setlocal filetype=markdown setlocal filetype=markdown
setlocal conceallevel=2 concealcursor=nc setlocal conceallevel=2 concealcursor=nc
setlocal keywordprg=:help iskeyword=@,48-57,_,192-255,-,# setlocal keywordprg=:help
let &l:iskeyword='!-~,^*,^|,^",192-255'
call s:enhance_syntax() call s:enhance_syntax()
if empty(healthchecks) if empty(healthchecks)

View File

@ -125,6 +125,10 @@ function! s:check_clipboard() abort
call health#report_warn( call health#report_warn(
\ 'No clipboard tool found. Clipboard registers will not work.', \ 'No clipboard tool found. Clipboard registers will not work.',
\ [':help clipboard']) \ [':help clipboard'])
elseif exists('g:clipboard') && (type({}) != type(g:clipboard)
\ || !has_key(g:clipboard, 'copy') || !has_key(g:clipboard, 'paste'))
call health#report_error(
\ 'g:clipboard exists but is malformed. It must be a dictionary with the keys documented at :help g:clipboard')
else else
call health#report_ok('Clipboard tool found: '. clipboard_tool) call health#report_ok('Clipboard tool found: '. clipboard_tool)
endif endif

View File

@ -8,7 +8,7 @@ let s:paste = {}
" ownership of the selection, so we know how long the cache is valid. " ownership of the selection, so we know how long the cache is valid.
let s:selection = { 'owner': 0, 'data': [] } let s:selection = { 'owner': 0, 'data': [] }
function! s:selection.on_exit(jobid, data, event) function! s:selection.on_exit(jobid, data, event) abort
" At this point this nvim instance might already have launched " At this point this nvim instance might already have launched
" a new provider instance. Don't drop ownership in this case. " a new provider instance. Don't drop ownership in this case.
if self.owner == a:jobid if self.owner == a:jobid
@ -18,7 +18,7 @@ endfunction
let s:selections = { '*': s:selection, '+': copy(s:selection)} let s:selections = { '*': s:selection, '+': copy(s:selection)}
function! s:try_cmd(cmd, ...) function! s:try_cmd(cmd, ...) abort
let argv = split(a:cmd, " ") let argv = split(a:cmd, " ")
let out = a:0 ? systemlist(argv, a:1, 1) : systemlist(argv, [''], 1) let out = a:0 ? systemlist(argv, a:1, 1) : systemlist(argv, [''], 1)
if v:shell_error if v:shell_error
@ -34,7 +34,7 @@ function! s:try_cmd(cmd, ...)
endfunction endfunction
" Returns TRUE if `cmd` exits with success, else FALSE. " Returns TRUE if `cmd` exits with success, else FALSE.
function! s:cmd_ok(cmd) function! s:cmd_ok(cmd) abort
call system(a:cmd) call system(a:cmd)
return v:shell_error == 0 return v:shell_error == 0
endfunction endfunction
@ -47,7 +47,12 @@ function! provider#clipboard#Error() abort
endfunction endfunction
function! provider#clipboard#Executable() abort function! provider#clipboard#Executable() abort
if has('mac') && executable('pbcopy') if exists('g:clipboard')
let s:copy = get(g:clipboard, 'copy', { '+': v:null, '*': v:null })
let s:paste = get(g:clipboard, 'paste', { '+': v:null, '*': v:null })
let s:cache_enabled = get(g:clipboard, 'cache_enabled', 1)
return get(g:clipboard, 'name', 'g:clipboard')
elseif has('mac') && executable('pbcopy')
let s:copy['+'] = 'pbcopy' let s:copy['+'] = 'pbcopy'
let s:paste['+'] = 'pbpaste' let s:paste['+'] = 'pbpaste'
let s:copy['*'] = s:copy['+'] let s:copy['*'] = s:copy['+']
@ -102,14 +107,14 @@ endif
let s:clipboard = {} let s:clipboard = {}
function! s:clipboard.get(reg) function! s:clipboard.get(reg) abort
if s:selections[a:reg].owner > 0 if s:selections[a:reg].owner > 0
return s:selections[a:reg].data return s:selections[a:reg].data
end end
return s:try_cmd(s:paste[a:reg]) return s:try_cmd(s:paste[a:reg])
endfunction endfunction
function! s:clipboard.set(lines, regtype, reg) function! s:clipboard.set(lines, regtype, reg) abort
if a:reg == '"' if a:reg == '"'
call s:clipboard.set(a:lines,a:regtype,'+') call s:clipboard.set(a:lines,a:regtype,'+')
if s:copy['*'] != s:copy['+'] if s:copy['*'] != s:copy['+']
@ -144,6 +149,6 @@ function! s:clipboard.set(lines, regtype, reg)
let selection.owner = jobid let selection.owner = jobid
endfunction endfunction
function! provider#clipboard#Call(method, args) function! provider#clipboard#Call(method, args) abort
return call(s:clipboard[a:method],a:args,s:clipboard) return call(s:clipboard[a:method],a:args,s:clipboard)
endfunction endfunction

View File

@ -165,7 +165,16 @@ nvim_input({keys}) *nvim_input()*
*nvim_replace_termcodes()* *nvim_replace_termcodes()*
nvim_replace_termcodes({str}, {from_part}, {do_lt}, {special}) nvim_replace_termcodes({str}, {from_part}, {do_lt}, {special})
Replaces any terminal codes with the internal representation Replaces terminal codes and |keycodes| (<CR>, <Esc>, ...) in a
string with the internal representation.
Parameters:~
{str} String to be converted.
{from_part} Legacy Vim parameter. Usually true.
{do_lt} Also translate <lt>. Does nothing if
`special` is false.
{special} Replace |keycodes|, e.g. <CR> becomes a "\n"
char.
nvim_command_output({str}) *nvim_command_output()* nvim_command_output({str}) *nvim_command_output()*
TODO: Documentation TODO: Documentation
@ -182,8 +191,10 @@ nvim_eval({expr}) *nvim_eval()*
Evaluation result or expanded object Evaluation result or expanded object
nvim_call_function({fname}, {args}) *nvim_call_function()* nvim_call_function({fname}, {args}) *nvim_call_function()*
Calls a VimL function with the given arguments. On VimL error: Calls a VimL function with the given arguments
Returns a generic error; v:errmsg is not updated.
On VimL error: Returns a generic error; v:errmsg is not
updated.
Parameters:~ Parameters:~
{fname} Function to call {fname} Function to call
@ -192,6 +203,23 @@ nvim_call_function({fname}, {args}) *nvim_call_function()*
Return:~ Return:~
Result of the function call Result of the function call
nvim_execute_lua({code}, {args}) *nvim_execute_lua()*
Execute lua code. Parameters might be passed, they are
available inside the chunk as `...`. The chunk can return a
value.
To evaluate an expression, it must be prefixed with "return ".
For instance, to call a lua function with arguments sent in
and get its return value back, use the code "return
my_function(...)".
Parameters:~
{code} lua code to execute
{args} Arguments to the code
Return:~
Return value of lua code if present or NIL.
nvim_strwidth({str}) *nvim_strwidth()* nvim_strwidth({str}) *nvim_strwidth()*
Calculates the number of display cells occupied by `text`. Calculates the number of display cells occupied by `text`.
<Tab> counts as one cell. <Tab> counts as one cell.
@ -382,6 +410,17 @@ nvim_get_mode() *nvim_get_mode()*
Return:~ Return:~
Dictionary { "mode": String, "blocking": Boolean } Dictionary { "mode": String, "blocking": Boolean }
nvim_get_keymap({mode}) *nvim_get_keymap()*
Get a list of dictionaries describing global (i.e. non-buffer)
mappings Note that the "buffer" key will be 0 to represent
false.
Parameters:~
{mode} The abbreviation for the mode
Return:~
An array of maparg() like dictionaries describing mappings
nvim_get_api_info() *nvim_get_api_info()* nvim_get_api_info() *nvim_get_api_info()*
TODO: Documentation TODO: Documentation
@ -414,6 +453,54 @@ nvim_call_atomic({calls}) *nvim_call_atomic()*
error ocurred, the values from all preceding calls will error ocurred, the values from all preceding calls will
still be returned. still be returned.
nvim__id({obj}) *nvim__id()*
Returns object given as argument
This API function is used for testing. One should not rely on
its presence in plugins.
Parameters:~
{obj} Object to return.
Return:~
its argument.
nvim__id_array({arr}) *nvim__id_array()*
Returns array given as argument
This API function is used for testing. One should not rely on
its presence in plugins.
Parameters:~
{arr} Array to return.
Return:~
its argument.
nvim__id_dictionary({dct}) *nvim__id_dictionary()*
Returns dictionary given as argument
This API function is used for testing. One should not rely on
its presence in plugins.
Parameters:~
{dct} Dictionary to return.
Return:~
its argument.
nvim__id_float({flt}) *nvim__id_float()*
Returns floating-point value given as argument
This API function is used for testing. One should not rely on
its presence in plugins.
Parameters:~
{flt} Value to return.
Return:~
its argument.
============================================================================== ==============================================================================
Buffer Functions *api-buffer* Buffer Functions *api-buffer*
@ -492,6 +579,18 @@ nvim_buf_get_changedtick({buffer}) *nvim_buf_get_changedtick()*
Return:~ Return:~
b:changedtickvalue. b:changedtickvalue.
nvim_buf_get_keymap({buffer}, {mode}) *nvim_buf_get_keymap()*
Get a list of dictionaries describing buffer-local mappings
Note that the buffer key in the dictionary will represent the
buffer handle where the mapping is present
Parameters:~
{mode} The abbreviation for the mode
{buffer_id} Buffer handle
Return:~
An array of maparg() like dictionaries describing mappings
nvim_buf_set_var({buffer}, {name}, {value}) *nvim_buf_set_var()* nvim_buf_set_var({buffer}, {name}, {value}) *nvim_buf_set_var()*
Sets a buffer-scoped (b:) variable Sets a buffer-scoped (b:) variable

View File

@ -389,12 +389,26 @@ CTRL-L A match is done on the pattern in front of the cursor. If
If there are multiple matches the longest common part is If there are multiple matches the longest common part is
inserted in place of the pattern. If the result is shorter inserted in place of the pattern. If the result is shorter
than the pattern, no completion is done. than the pattern, no completion is done.
*/_CTRL-L*
When 'incsearch' is set, entering a search pattern for "/" or When 'incsearch' is set, entering a search pattern for "/" or
"?" and the current match is displayed then CTRL-L will add "?" and the current match is displayed then CTRL-L will add
one character from the end of the current match. If one character from the end of the current match. If
'ignorecase' and 'smartcase' are set and the command line has 'ignorecase' and 'smartcase' are set and the command line has
no uppercase characters, the added character is converted to no uppercase characters, the added character is converted to
lowercase. lowercase.
*c_CTRL-G* */_CTRL-G*
CTRL-G When 'incsearch' is set, entering a search pattern for "/" or
"?" and the current match is displayed then CTRL-G will move
to the next match (does not take |search-offset| into account)
Use CTRL-T to move to the previous match. Hint: on a regular
keyboard T is above G.
*c_CTRL-T* */_CTRL-T*
CTRL-T When 'incsearch' is set, entering a search pattern for "/" or
"?" and the current match is displayed then CTRL-T will move
to the previous match (does not take |search-offset| into
account).
Use CTRL-G to move to the next match. Hint: on a regular
keyboard T is above G.
The 'wildchar' option defaults to <Tab> (CTRL-E when in Vi compatible mode; in The 'wildchar' option defaults to <Tab> (CTRL-E when in Vi compatible mode; in
a previous version <Esc> was used). In the pattern standard wildcards '*' and a previous version <Esc> was used). In the pattern standard wildcards '*' and

View File

@ -39,10 +39,16 @@ Functions ~
*highlightID()* Obsolete name for |hlID()|. *highlightID()* Obsolete name for |hlID()|.
*last_buffer_nr()* Obsolete name for bufnr("$"). *last_buffer_nr()* Obsolete name for bufnr("$").
Modifiers ~
*:menu-<special>*
*:menu-special* <> notation is always enabled. |cpo-<|
*:map-<special>*
*:map-special* <> notation is always enabled. |cpo-<|
Options ~ Options ~
*'fe'* 'fenc'+'enc' before Vim 6.0; no longer used. *'fe'* 'fenc'+'enc' before Vim 6.0; no longer used.
*'langnoremap'* Deprecated alias to 'nolangremap'. *'langnoremap'* Deprecated alias to 'nolangremap'.
*'vi'* *'vi'*
*'viminfo'* Deprecated alias to 'shada' option. *'viminfo'* Deprecated alias to 'shada' option.
vim:tw=78:ts=8:ft=help:norl: vim:noet:tw=78:ts=8:ft=help:norl:

View File

@ -1852,10 +1852,11 @@ v:t_number Value of Number type. Read-only. See: |type()|
v:t_string Value of String type. Read-only. See: |type()| v:t_string Value of String type. Read-only. See: |type()|
*v:termresponse* *termresponse-variable* *v:termresponse* *termresponse-variable*
v:termresponse The escape sequence returned by the terminal for the |t_RV| v:termresponse The escape sequence returned by the terminal for the DA
termcap entry. It is set when Vim receives an escape sequence (request primary device attributes) control sequence. It is
that starts with ESC [ or CSI and ends in a 'c', with only set when Vim receives an escape sequence that starts with ESC
digits, ';' and '.' in between. [ or CSI and ends in a 'c', with only digits, ';' and '.' in
between.
When this option is set, the TermResponse autocommand event is When this option is set, the TermResponse autocommand event is
fired, so that you can react to the response from the fired, so that you can react to the response from the
terminal. terminal.

View File

@ -490,9 +490,6 @@ expression register: >
:amenu Insert.foobar "='foobar'<CR>P :amenu Insert.foobar "='foobar'<CR>P
Note that the '<' and 'k' flags in 'cpoptions' also apply here (when
included they make the <> form and raw key codes not being recognized).
Note that <Esc> in Cmdline mode executes the command, like in a mapping. This Note that <Esc> in Cmdline mode executes the command, like in a mapping. This
is Vi compatible. Use CTRL-C to quit Cmdline mode. is Vi compatible. Use CTRL-C to quit Cmdline mode.
@ -504,21 +501,13 @@ The ":set ic" will not be echoed when using this menu. Messages from the
executed command are still given though. To shut them up too, add a ":silent" executed command are still given though. To shut them up too, add a ":silent"
in the executed command: > in the executed command: >
:menu <silent> Search.Header :exe ":silent normal /Header\r"<CR> :menu <silent> Search.Header :exe ":silent normal /Header\r"<CR>
"<silent>" may also appear just after "<special>" or "<script>". "<silent>" may also appear just after "<script>".
*:menu-<special>* *:menu-special*
Define a menu with <> notation for special keys, even though the "<" flag
may appear in 'cpoptions'. This is useful if the side effect of setting
'cpoptions' is not desired. Example: >
:menu <special> Search.Header /Header<CR>
"<special>" must appear as the very first argument to the ":menu" command or
just after "<silent>" or "<script>".
*:menu-<script>* *:menu-script* *:menu-<script>* *:menu-script*
The "to" part of the menu will be inspected for mappings. If you don't want The "to" part of the menu will be inspected for mappings. If you don't want
this, use the ":noremenu" command (or the similar one for a specific mode). this, use the ":noremenu" command (or the similar one for a specific mode).
If you do want to use script-local mappings, add "<script>" as the very first If you do want to use script-local mappings, add "<script>" as the very first
argument to the ":menu" command or just after "<silent>" or "<special>". argument to the ":menu" command or just after "<silent>".
*menu-priority* *menu-priority*
You can give a priority to a menu. Menus with a higher priority go more to You can give a priority to a menu. Menus with a higher priority go more to

View File

@ -9,7 +9,147 @@ Lua Interface to Nvim *lua* *Lua*
Type <M-]> to see the table of contents. Type <M-]> to see the table of contents.
============================================================================== ==============================================================================
1. Commands *lua-commands* 1. Importing modules *lua-require*
Neovim lua interface automatically adjusts `package.path` and `package.cpath`
according to effective &runtimepath value. Adjustment happens after
'runtimepath' is changed. `package.path` is adjusted by simply appending
`/lua/?.lua` and `/lua/?/init.lua` to each directory from 'runtimepath' (`/`
is actually the first character of `package.config`).
Similarly to `package.path`, modified directories from `runtimepath` are also
added to `package.cpath`. In this case, instead of appending `/lua/?.lua` and
`/lua/?/init.lua` to each runtimepath, all unique `?`-containing suffixes of
the existing `package.cpath` are used. Here is an example:
1. Given that
- 'runtimepath' contains `/foo/bar,/xxx;yyy/baz,/abc`;
- initial (defined at compile time or derived from
`$LUA_CPATH`/`$LUA_INIT`) `package.cpath` contains
`./?.so;/def/ghi/a?d/j/g.elf;/def/?.so`.
2. It finds `?`-containing suffixes `/?.so`, `/a?d/j/g.elf` and `/?.so`, in
order: parts of the path starting from the first path component containing
question mark and preceding path separator.
3. The suffix of `/def/?.so`, namely `/?.so` is not unique, as its the same
as the suffix of the first path from `package.path` (i.e. `./?.so`). Which
leaves `/?.so` and `/a?d/j/g.elf`, in this order.
4. 'runtimepath' has three paths: `/foo/bar`, `/xxx;yyy/baz` and `/abc`. The
second one contains semicolon which is a paths separator so it is out,
leaving only `/foo/bar` and `/abc`, in order.
5. The cartesian product of paths from 4. and suffixes from 3. is taken,
giving four variants. In each variant `/lua` path segment is inserted
between path and suffix, leaving
- `/foo/bar/lua/?.so`
- `/foo/bar/lua/a?d/j/g.elf`
- `/abc/lua/?.so`
- `/abc/lua/a?d/j/g.elf`
6. New paths are prepended to the original `package.cpath`.
The result will look like this:
`/foo/bar,/xxx;yyy/baz,/abc` ('runtimepath')
× `./?.so;/def/ghi/a?d/j/g.elf;/def/?.so` (`package.cpath`)
= `/foo/bar/lua/?.so;/foo/bar/lua/a?d/j/g.elf;/abc/lua/?.so;/abc/lua/a?d/j/g.elf;./?.so;/def/ghi/a?d/j/g.elf;/def/?.so`
Note: to keep up with 'runtimepath' updates paths added at previous update are
remembered and removed at the next update, while all paths derived from the
new 'runtimepath' are prepended as described above. This allows removing
paths when path is removed from 'runtimepath', adding paths when they are
added and reordering `package.path`/`package.cpath` content if 'runtimepath'
was reordered.
Note 2: even though adjustments happens automatically Neovim does not track
current values of `package.path` or `package.cpath`. If you happened to
delete some paths from there you need to reset 'runtimepath' to make them
readded. Just running `let &runtimepath = &runtimepath` should work.
Note 3: skipping paths from 'runtimepath' which contain semicolons applies
both to `package.path` and `package.cpath`. Given that there is a number of
badly written plugins using shell which will not work with paths containing
semicolons it is better to not have them in 'runtimepath' at all.
------------------------------------------------------------------------------
1.1. Example of the plugin which uses lua modules: *lua-require-example*
The following example plugin adds a command `:MakeCharBlob` which transforms
current buffer into a long `unsigned char` array. Lua contains transformation
function in a module `lua/charblob.lua` which is imported in
`autoload/charblob.vim` (`require("charblob")`). Example plugin is supposed
to be put into any directory from 'runtimepath', e.g. `~/.config/nvim` (in
this case `lua/charblob.lua` means `~/.config/nvim/lua/charblob.lua`).
autoload/charblob.vim: >
function charblob#encode_buffer()
call setline(1, luaeval(
\ 'require("charblob").encode(unpack(_A))',
\ [getline(1, '$'), &textwidth, ' ']))
endfunction
plugin/charblob.vim: >
if exists('g:charblob_loaded')
finish
endif
let g:charblob_loaded = 1
command MakeCharBlob :call charblob#encode_buffer()
lua/charblob.lua: >
local function charblob_bytes_iter(lines)
local init_s = {
next_line_idx = 1,
next_byte_idx = 1,
lines = lines,
}
local function next(s, _)
if lines[s.next_line_idx] == nil then
return nil
end
if s.next_byte_idx > #(lines[s.next_line_idx]) then
s.next_line_idx = s.next_line_idx + 1
s.next_byte_idx = 1
return ('\n'):byte()
end
local ret = lines[s.next_line_idx]:byte(s.next_byte_idx)
if ret == ('\n'):byte() then
ret = 0 -- See :h NL-used-for-NUL.
end
s.next_byte_idx = s.next_byte_idx + 1
return ret
end
return next, init_s, nil
end
local function charblob_encode(lines, textwidth, indent)
local ret = {
'const unsigned char blob[] = {',
indent,
}
for byte in charblob_bytes_iter(lines) do
-- .- space + number (width 3) + comma
if #(ret[#ret]) + 5 > textwidth then
ret[#ret + 1] = indent
else
ret[#ret] = ret[#ret] .. ' '
end
ret[#ret] = ret[#ret] .. (('%3u,'):format(byte))
end
ret[#ret + 1] = '};'
return ret
end
return {
bytes_iter = charblob_bytes_iter,
encode = charblob_encode,
}
==============================================================================
2. Commands *lua-commands*
*:lua* *:lua*
:[range]lua {chunk} :[range]lua {chunk}

View File

@ -442,8 +442,8 @@ available on a few terminals.
Note: There are two codes for the delete key. 127 is the decimal ASCII value Note: There are two codes for the delete key. 127 is the decimal ASCII value
for the delete key, which is always recognized. Some delete keys send another for the delete key, which is always recognized. Some delete keys send another
value, in which case this value is obtained from the termcap entry "kD". Both value, in which case this value is obtained from the |terminfo| entry "key_dc".
values have the same effect. Both values have the same effect.
Note: The keypad keys are used in the same way as the corresponding "normal" Note: The keypad keys are used in the same way as the corresponding "normal"
keys. For example, <kHome> has the same effect as <Home>. If a keypad key keys. For example, <kHome> has the same effect as <Home>. If a keypad key

View File

@ -102,36 +102,30 @@ function. Here's a more object-oriented version of the above:
> >
let Shell = {} let Shell = {}
function Shell.on_stdout(job_id, data) dict function Shell.on_stdout(_job_id, data, event)
call append(line('$'), self.get_name().' stdout: '.join(a:data)) call append(line('$'),
\ printf('[%s] %s: %s', a:event, self.name, join(a:data[:-2])))
endfunction endfunction
function Shell.on_stderr(job_id, data) dict let Shell.on_stderr = function(Shell.on_stdout)
call append(line('$'), self.get_name().' stderr: '.join(a:data))
function Shell.on_exit(job_id, _data, event)
let msg = printf('job %d ("%s") finished', a:job_id, self.name)
call append(line('$'), printf('[%s] BOOM!', a:event))
call append(line('$'), printf('[%s] %s!', a:event, msg))
endfunction endfunction
function Shell.on_exit(job_id, data) dict function Shell.new(name, cmd)
call append(line('$'), self.get_name().' exited') let object = extend(copy(g:Shell), {'name': a:name})
let object.cmd = ['sh', '-c', a:cmd]
let object.id = jobstart(object.cmd, object)
$
return object
endfunction endfunction
function Shell.get_name() dict let instance = Shell.new('bomb',
return 'shell '.self.name \ 'for i in $(seq 9 -1 1); do echo $i 1>&$((i % 2 + 1)); sleep 1; done')
endfunction <
function Shell.new(name, ...) dict
let instance = extend(copy(g:Shell), {'name': a:name})
let argv = ['bash']
if a:0 > 0
let argv += ['-c', a:1]
endif
let instance.id = jobstart(argv, instance)
return instance
endfunction
let s1 = Shell.new('1')
let s2 = Shell.new('2', 'for i in {1..10}; do echo hello $i!; sleep 1; done')
To send data to the job's stdin, one can use the |jobsend()| function, like To send data to the job's stdin, one can use the |jobsend()| function, like
this: this:
> >

View File

@ -149,7 +149,7 @@ type "a", then "bar" will get inserted.
1.2 SPECIAL ARGUMENTS *:map-arguments* 1.2 SPECIAL ARGUMENTS *:map-arguments*
"<buffer>", "<nowait>", "<silent>", "<special>", "<script>", "<expr>" and "<buffer>", "<nowait>", "<silent>", "<script>", "<expr>" and
"<unique>" can be used in any order. They must appear right after the "<unique>" can be used in any order. They must appear right after the
command, before any other arguments. command, before any other arguments.
@ -189,12 +189,6 @@ Prompts will still be given, e.g., for inputdialog().
Using "<silent>" for an abbreviation is possible, but will cause redrawing of Using "<silent>" for an abbreviation is possible, but will cause redrawing of
the command line to fail. the command line to fail.
*:map-<special>* *:map-special*
Define a mapping with <> notation for special keys, even though the "<" flag
may appear in 'cpoptions'. This is useful if the side effect of setting
'cpoptions' is not desired. Example: >
:map <special> <F12> /Header<CR>
<
*:map-<script>* *:map-script* *:map-<script>* *:map-script*
If the first argument to one of these commands is "<script>" and it is used to If the first argument to one of these commands is "<script>" and it is used to
define a new mapping or abbreviation, the mapping will only remap characters define a new mapping or abbreviation, the mapping will only remap characters
@ -443,17 +437,15 @@ There are two ways to map a special key:
1. The Vi-compatible method: Map the key code. Often this is a sequence that 1. The Vi-compatible method: Map the key code. Often this is a sequence that
starts with <Esc>. To enter a mapping like this you type ":map " and then starts with <Esc>. To enter a mapping like this you type ":map " and then
you have to type CTRL-V before hitting the function key. Note that when you have to type CTRL-V before hitting the function key. Note that when
the key code for the key is in the termcap, it will automatically be the key code for the key is in the |terminfo| entry, it will automatically
translated into the internal code and become the second way of mapping be translated into the internal code and become the second way of mapping.
(unless the 'k' flag is included in 'cpoptions').
2. The second method is to use the internal code for the function key. To 2. The second method is to use the internal code for the function key. To
enter such a mapping type CTRL-K and then hit the function key, or use enter such a mapping type CTRL-K and then hit the function key, or use
the form "#1", "#2", .. "#9", "#0", "<Up>", "<S-Down>", "<S-F7>", etc. the form "#1", "#2", .. "#9", "#0", "<Up>", "<S-Down>", "<S-F7>", etc.
(see table of keys |key-notation|, all keys from <Up> can be used). The (see table of keys |key-notation|, all keys from <Up> can be used). The
first ten function keys can be defined in two ways: Just the number, like first ten function keys can be defined in two ways: Just the number, like
"#2", and with "<F>", like "<F2>". Both stand for function key 2. "#0" "#2", and with "<F>", like "<F2>". Both stand for function key 2. "#0"
refers to function key 10. The <> form cannot be used when 'cpoptions' refers to function key 10.
includes the '<' flag.
DETAIL: Vim first checks if a sequence from the keyboard is mapped. If it DETAIL: Vim first checks if a sequence from the keyboard is mapped. If it
isn't the terminal key codes are tried. If a terminal code is found it is isn't the terminal key codes are tried. If a terminal code is found it is
@ -571,9 +563,9 @@ Since the '|' character is used to separate a map command from the next
command, you will have to do something special to include a '|' in {rhs}. command, you will have to do something special to include a '|' in {rhs}.
There are three methods: There are three methods:
use works when example ~ use works when example ~
<Bar> '<' is not in 'cpoptions' :map _l :!ls <Bar> more^M <Bar> always :map _l :!ls <Bar> more^M
\| 'b' is not in 'cpoptions' :map _l :!ls \| more^M \| 'b' is not in 'cpoptions' :map _l :!ls \| more^M
^V| always, in Vim and Vi :map _l :!ls ^V| more^M ^V| always :map _l :!ls ^V| more^M
(here ^V stands for CTRL-V; to get one CTRL-V you have to type it twice; you (here ^V stands for CTRL-V; to get one CTRL-V you have to type it twice; you
cannot use the <> notation "<C-V>" here). cannot use the <> notation "<C-V>" here).
@ -628,8 +620,7 @@ out about, ^D is CTRL-D).
1.8 EXAMPLES *map-examples* 1.8 EXAMPLES *map-examples*
A few examples (given as you type them, for "<CR>" you type four characters; A few examples (as you type them: for "<CR>" you type four characters). >
the '<' flag must not be present in 'cpoptions' for this to work). >
:map <F3> o#include :map <F3> o#include
:map <M-g> /foo<CR>cwbar<Esc> :map <M-g> /foo<CR>cwbar<Esc>
@ -881,7 +872,6 @@ character is mostly ignored otherwise.
It is possible to move the cursor after an abbreviation: > It is possible to move the cursor after an abbreviation: >
:iab if if ()<Left> :iab if if ()<Left>
This does not work if 'cpoptions' includes the '<' flag. |<>|
You can even do more complicated things. For example, to consume the space You can even do more complicated things. For example, to consume the space
typed after an abbreviation: > typed after an abbreviation: >
@ -1029,8 +1019,7 @@ functions used in one script use the same name as in other scripts. To avoid
this, they can be made local to the script. this, they can be made local to the script.
*<SID>* *<SNR>* *E81* *<SID>* *<SNR>* *E81*
The string "<SID>" can be used in a mapping or menu. This requires that the The string "<SID>" can be used in a mapping or menu.
'<' flag is not present in 'cpoptions'.
When executing the map command, Vim will replace "<SID>" with the special When executing the map command, Vim will replace "<SID>" with the special
key code <SNR>, followed by a number that's unique for the script, and an key code <SNR>, followed by a number that's unique for the script, and an
underscore. Example: > underscore. Example: >

View File

@ -24,10 +24,7 @@ achieve special effects. These options come in three forms:
:se[t] all Show all but terminal options. :se[t] all Show all but terminal options.
:se[t] termcap Show all terminal options. Note that in the GUI the :se[t] termcap Do nothing. Nvim uses |terminfo|.
key codes are not shown, because they are generated
internally and can't be changed. Changing the terminal
codes in the GUI is not useful either...
*E518* *E519* *E518* *E519*
:se[t] {option}? Show value of {option}. :se[t] {option}? Show value of {option}.
@ -1571,7 +1568,6 @@ A jump table for the options with a short description can be found at |Q_op|.
results in X being mapped to: results in X being mapped to:
'B' included: "\^[" (^[ is a real <Esc>) 'B' included: "\^[" (^[ is a real <Esc>)
'B' excluded: "<Esc>" (5 characters) 'B' excluded: "<Esc>" (5 characters)
('<' excluded in both cases)
*cpo-c* *cpo-c*
c Searching continues at the end of any match at the c Searching continues at the end of any match at the
cursor position, but not further than the start of the cursor position, but not further than the start of the
@ -1621,15 +1617,6 @@ A jump table for the options with a short description can be found at |Q_op|.
J A |sentence| has to be followed by two spaces after J A |sentence| has to be followed by two spaces after
the '.', '!' or '?'. A <Tab> is not recognized as the '.', '!' or '?'. A <Tab> is not recognized as
white space. white space.
*cpo-k*
k Disable the recognition of raw key codes in
mappings, abbreviations, and the "to" part of menu
commands. For example, if <Key> sends ^[OA (where ^[
is <Esc>), the command ":map X ^[OA" results in X
being mapped to:
'k' included: "^[OA" (3 characters)
'k' excluded: "<Key>" (one key code)
Also see the '<' flag below.
*cpo-K* *cpo-K*
K Don't wait for a key code to complete when it is K Don't wait for a key code to complete when it is
halfway through a mapping. This breaks mapping halfway through a mapping. This breaks mapping
@ -1763,14 +1750,6 @@ A jump table for the options with a short description can be found at |Q_op|.
+ When included, a ":write file" command will reset the + When included, a ":write file" command will reset the
'modified' flag of the buffer, even though the buffer 'modified' flag of the buffer, even though the buffer
itself may still be different from its file. itself may still be different from its file.
*cpo-<*
< Disable the recognition of special key codes in |<>|
form in mappings, abbreviations, and the "to" part of
menu commands. For example, the command
":map X <Tab>" results in X being mapped to:
'<' included: "<Tab>" (5 characters)
'<' excluded: "^I" (^I is a real <Tab>)
Also see the 'k' flag above.
*cpo->* *cpo->*
> When appending to a register, put a line break before > When appending to a register, put a line break before
the appended text. the appended text.
@ -2756,14 +2735,10 @@ A jump table for the options with a short description can be found at |Q_op|.
*'guicursor'* *'gcr'* *E545* *E546* *E548* *E549* *'guicursor'* *'gcr'* *E545* *E546* *E548* *E549*
'guicursor' 'gcr' string (default "n-v-c-sm:block,i-ci-ve:ver25,r-cr-o:hor20") 'guicursor' 'gcr' string (default "n-v-c-sm:block,i-ci-ve:ver25,r-cr-o:hor20")
global global
Configures the cursor style for each mode. Works in the GUI and some Configures the cursor style for each mode. Works in the GUI and many
terminals. terminals. See |cursor-shape| for details.
With tmux you might need this in ~/.tmux.conf (see terminal-overrides To disable cursor-styling, reset the option: >
in the tmux(1) manual page): >
set -ga terminal-overrides ',*:Ss=\E[%p1%d q:Se=\E[2 q'
< To disable cursor-styling, reset the option: >
:set guicursor= :set guicursor=
< To enable mode shapes, "Cursor" highlight, and blinking: > < To enable mode shapes, "Cursor" highlight, and blinking: >
@ -6272,7 +6247,7 @@ A jump table for the options with a short description can be found at |Q_op|.
for any key that can follow <c-f> in a mapping. for any key that can follow <c-f> in a mapping.
*'ttimeout'* *'nottimeout'* *'ttimeout'* *'nottimeout'*
'ttimeout' boolean (default off) 'ttimeout' boolean (default on)
global global
This option and 'ttimeoutlen' determine the behavior when part of a This option and 'ttimeoutlen' determine the behavior when part of a
key code sequence has been received by the terminal UI. For example, key code sequence has been received by the terminal UI. For example,
@ -6287,7 +6262,7 @@ A jump table for the options with a short description can be found at |Q_op|.
complete. complete.
*'ttimeoutlen'* *'ttm'* *'ttimeoutlen'* *'ttm'*
'ttimeoutlen' 'ttm' number (default -1) 'ttimeoutlen' 'ttm' number (default 50)
global global
The time in milliseconds that is waited for a key code The time in milliseconds that is waited for a key code
sequence to complete. Also used for CTRL-\ CTRL-N and CTRL-\ CTRL-G sequence to complete. Also used for CTRL-\ CTRL-N and CTRL-\ CTRL-G

View File

@ -116,29 +116,48 @@ To use the RVM "system" Ruby installation: >
============================================================================== ==============================================================================
Clipboard integration *provider-clipboard* *clipboard* Clipboard integration *provider-clipboard* *clipboard*
Nvim has no direct connection to the system clipboard. Instead it is Nvim has no direct connection to the system clipboard. Instead it depends on
accessible through a |provider| which transparently uses shell commands for a |provider| which transparently uses shell commands to communicate with the
communicating with the clipboard. system clipboard or any other clipboard "backend".
Clipboard access is implicitly enabled if any of the following clipboard tools To ALWAYS use the clipboard for ALL operations (instead of interacting with
are found in your `$PATH`. the '+' and/or '*' registers explicitly): >
- xclip
- xsel (newer alternative to xclip)
- pbcopy/pbpaste (macOS)
- lemonade (for SSH) https://github.com/pocke/lemonade
- doitclient (for SSH) http://www.chiark.greenend.org.uk/~sgtatham/doit/
The presence of a suitable clipboard tool implicitly enables the '+' and '*'
registers.
If you want to ALWAYS use the clipboard for ALL operations (as opposed
to interacting with the '+' and/or '*' registers explicitly), set the
following option:
>
set clipboard+=unnamedplus set clipboard+=unnamedplus
< <
See 'clipboard' for details and more options. See 'clipboard' for details and options.
*clipboard-tool*
The presence of a working clipboard tool implicitly enables the '+' and '*'
registers. Nvim looks for these clipboard tools, in order of priority:
- |g:clipboard|
- pbcopy/pbpaste (macOS)
- xclip
- xsel (newer alternative to xclip)
- lemonade (for SSH) https://github.com/pocke/lemonade
- doitclient (for SSH) http://www.chiark.greenend.org.uk/~sgtatham/doit/
- win32yank (Windows)
- tmux (if $TMUX is set)
*g:clipboard*
To configure a custom clipboard tool, set `g:clipboard` to a dictionary: >
let g:clipboard = {
\ 'name': 'myClipboard',
\ 'copy': {
\ '+': 'tmux load-buffer -',
\ '*': 'tmux load-buffer -',
\ },
\ 'paste': {
\ '+': 'tmux save-buffer -',
\ '*': 'tmux save-buffer -',
\ },
\ 'cache_enabled': 1,
\ }
If `cache_enabled` is |TRUE| then when a selection is copied, Nvim will cache
the selection until the copy command process dies. When pasting, if the copy
process has not died, the cached selection is applied.
============================================================================== ==============================================================================
X11 selection mechanism *clipboard-x11* *x11-selection* X11 selection mechanism *clipboard-x11* *x11-selection*

View File

@ -571,8 +571,8 @@ In Insert or Command-line mode:
*Q_op* Options *Q_op* Options
|:set| :se[t] show all modified options |:set| :se[t] show all modified options
|:set| :se[t] all show all non-termcap options |:set| :se[t] all show all options
|:set| :se[t] termcap show all termcap options |:set| :se[t] termcap Do nothing. (|terminfo|)
|:set| :se[t] {option} set boolean option (switch it on), |:set| :se[t] {option} set boolean option (switch it on),
show string or number option show string or number option
|:set| :se[t] no{option} reset boolean option (switch it off) |:set| :se[t] no{option} reset boolean option (switch it off)

View File

@ -214,7 +214,7 @@ argument.
:set to display option values. :set to display option values.
When 'verbose' is non-zero messages are printed (for When 'verbose' is non-zero messages are printed (for
debugging, to stderr). debugging, to stderr).
$TERM is not used. $TERM (see |TERM|) is not used.
If Vim appears to be stuck try typing "qa!<Enter>". You don't If Vim appears to be stuck try typing "qa!<Enter>". You don't
get a prompt thus you can't see Vim is waiting for you to type get a prompt thus you can't see Vim is waiting for you to type
something. something.
@ -355,7 +355,7 @@ argument.
At startup, Vim checks environment variables and files and sets values At startup, Vim checks environment variables and files and sets values
accordingly. Vim proceeds in this order: accordingly. Vim proceeds in this order:
1. Set the 'shell' option *SHELL* *COMSPEC* *TERM* 1. Set the 'shell' option *SHELL* *COMSPEC*
The environment variable SHELL, if it exists, is used to set the The environment variable SHELL, if it exists, is used to set the
'shell' option. On Windows, the COMSPEC variable is used 'shell' option. On Windows, the COMSPEC variable is used
if SHELL is not set. if SHELL is not set.

View File

@ -4696,7 +4696,7 @@ cterm={attr-list} *highlight-cterm*
ctermfg={color-nr} *highlight-ctermfg* *E421* ctermfg={color-nr} *highlight-ctermfg* *E421*
ctermbg={color-nr} *highlight-ctermbg* ctermbg={color-nr} *highlight-ctermbg*
The {color-nr} argument is a color number. Its range is zero to The {color-nr} argument is a color number. Its range is zero to
(not including) the number given by the termcap entry "Co". (not including) the number of |terminfo-colors| available.
The actual color with this number depends on the type of terminal The actual color with this number depends on the type of terminal
and its settings. Sometimes the color also depends on the settings of and its settings. Sometimes the color also depends on the settings of
"cterm". For example, on some systems "cterm=bold ctermfg=3" gives "cterm". For example, on some systems "cterm=bold ctermfg=3" gives
@ -4768,9 +4768,8 @@ ctermbg={color-nr} *highlight-ctermbg*
delete the "g:colors_name" variable when you don't want this. delete the "g:colors_name" variable when you don't want this.
When you have set "ctermfg" or "ctermbg" for the Normal group, Vim When you have set "ctermfg" or "ctermbg" for the Normal group, Vim
needs to reset the color when exiting. This is done with the "op" needs to reset the color when exiting. This is done with the
termcap entry |t_op|. If this doesn't work correctly, try setting the "orig_pair" |terminfo| entry.
't_op' option in your vimrc.
*E419* *E420* *E419* *E420*
When Vim knows the normal foreground and background colors, "fg" and When Vim knows the normal foreground and background colors, "fg" and
"bg" can be used as color names. This only works after setting the "bg" can be used as color names. This only works after setting the
@ -5207,10 +5206,7 @@ To test your color setup, a file has been included in the Vim distribution.
To use it, execute this command: > To use it, execute this command: >
:runtime syntax/colortest.vim :runtime syntax/colortest.vim
Some versions of xterm (and other terminals, like the Linux console) can Nvim uses |256-color| and |true-color| terminal capabilities whereever possible.
output lighter foreground colors, even though the number of colors is defined
at 8. Therefore Vim sets the "cterm=bold" attribute for light foreground
colors, when 't_Co' is 8.
============================================================================== ==============================================================================
18. When syntax is slow *:syntime* 18. When syntax is slow *:syntime*

View File

@ -20,21 +20,194 @@ Startup *startup-terminal*
When Vim is started a default terminal type is assumed. for MS-DOS this is When Vim is started a default terminal type is assumed. for MS-DOS this is
the pc terminal, for Unix an ansi terminal. the pc terminal, for Unix an ansi terminal.
*termcap* *terminfo* *E557* *E558* *E559* *terminfo* *E557* *E558* *E559*
On Unix the terminfo database or termcap file is used. This is referred to as On Unix the terminfo database is used. There is no access to the terminfo
"termcap" in all the documentation. settings with |:set|.
The Unibilium library (used by Nvim to read terminfo) allows you to override
an out-of-date system terminfo database with one in your $HOME/.terminfo/
directory, in part or in whole.
Building your own up-to-date terminfo database is usually as simple as running
this as a non-superuser:
>
wget http://invisible-island.net/datafiles/current/terminfo.src.gz
gunzip terminfo.src.gz
tic terminfo.src
<
*TERM*
If you experience terminal difficulties, first ensure that you have set the
correct terminal type in your $TERM environment variable so that Nvim is
pulling the correct entry from the terminfo database in the first place.
Per the terminfo source file from ncurses:
For these terminals Set $TERM to |builtin-terms|?
iTerm.app "iterm" or "iTerm.app" Y
anything libvte based "vte" or "vte-256color" Y
(e.g. GNOME Terminal) ("gnome" and "gnome-256color" are
available as aliases for these)
tmux "tmux" or "tmux-256color" Y
screen "screen" or "screen-256color" Y
PuTTY "putty" or "putty-256color" Y
Terminal.app "nsterm" N
Linux virtual terminal "linux" or "linux-256color" Y
Describing any of these as "xterm" or "xterm-256colour" will not describe the
terminal correctly to Nvim, and will cause various kinds of problematic
behaviours.
Setting your $TERM environment variable to the correct value also avoids the
problem that SSH does not mirror arbitrary client-end environment variables
such as $COLORTERM, $XTERM_VERSION, $VTE_VERSION, $KONSOLE_PROFILE_NAME, and
$TERM_PROGRAM to the server end, whereas it does send the $TERM environment
variable.
See |terminfo| for dealing with out of date terminfo databases.
*builtin-terms* *builtin_terms*
If a |terminfo| database is not available, or no entry for the terminal type is
found in that database, Nvim will look up the terminal type in a compiled-in
mini-database of terminfo entries for "xterm", "putty", "screen", "tmux",
"rxvt", "iterm", "interix", "linux", "st", "vte", "gnome", and "ansi".
The lookup matches the initial portion of the terminal type, so (for example)
"putty-256color" and "putty" will both be mapped to the built-in "putty"
entry. The built-in terminfo entries describe the terminal as 256-colour
capable if possible. See |termcap-colors|.
If no built-in terminfo record matches the terminal type, the built-in "ansi"
terminfo record is used as a final fallback.
The built-in mini-database is not combined with an external terminfo database,
nor can it be used in preference to one. You can thus entirely override any
omissions or out-of-date information in the built-in terminfo database by
supplying an external one with entries for the terminal type.
Settings depending on terminal *term-dependent-settings* Settings depending on terminal *term-dependent-settings*
If you want to set options or mappings, depending on the terminal name, you If you want to set options or mappings, depending on the terminal name, you
can do this best in your vimrc. Example: > can do this best in your init.vim. Example: >
if &term == "xterm" if $TERM =~ '^\(rxvt\|screen\|interix\|putty\)\(-.*\)\?$'
... xterm maps and settings ... set notermguicolors
elseif &term =~ "vt10." elseif $TERM =~ '^\(tmux\|iterm\|vte\|gnome\)\(-.*\)\?$'
... vt100, vt102 maps and settings ... set termguicolors
endif elseif $TERM =~ '^\(xterm\)\(-.*\)\?$'
if $XTERM_VERSION != ''
set termguicolors
elseif $KONSOLE_PROFILE_NAME != ''
set termguicolors
elseif $VTE_VERSION != ''
set termguicolors
else
set notermguicolors
endif
elseif $TERM =~ ...
... and so forth ...
endif
< <
*scroll-region* *xterm-scroll-region*
Where possible, Nvim will use the terminal's ability to set a scroll region in
order to redraw faster when a window is scrolled. If the terminal's terminfo
description describes an ability to set top and bottom scroll margins, that is
used.
This will not speed up scrolling in a window that is not the full width of the
terminal. Xterm has an extra ability, not described by terminfo, to set left
and right scroll margins as well. If Nvim detects that the terminal is Xterm,
it will make use of this ability to speed up scrolling that is not the full
width of the terminal.
This ability is only present in genuine Xterm, not in the many terminal
emulators that incorrectly describe themselves as xterm. Nvim's detection of
genuine Xterm will not work over an SSH connection, because the environment
variable, set by genuine Xterm, that it looks for is not automatically
replicated over an SSH login session.
*256-color* *terminfo-colors* *termcap-colors*
Nvim can make use of 256-colour terminals and tries to do so whereever it can.
If the |terminfo| description of the terminal says that it supports fewer
colours, Nvim will override this for many terminal types, including "linux"
(whose virtual terminals have had 256-colour support since version 4.8) and
anything (even if falsely) claiming to be "xterm". It will also set 256
colours when the COLORTERM or TERM environment variables contain the string
"256" somewhere.
Nvim similarly assumes that any terminal emulator that sets the COLORTERM
environment variable at all, to anything, is capable of at least 16-colour
operation; and it will override |terminfo| saying that it has fewer colours
available.
*true-color* *xterm-true-color*
Nvim supports using true (24-bit) colours in the terminal, on terminals that
support it. It uses the same |terminfo| extensions that were proposed by
Rüdiger Sonderfeld in 2013 for this: "setrgbf" and "setrgbb". If your
terminfo definition specifies these, then nothing more is required.
If your terminfo definition is missing them, then Nvim will decide whether to
add them to your terminfo definition, using the ISO 8613-6:1994/ITU T.416:1993
control sequences for setting RGB colours, but modified to use semicolons
instead of colons unless the terminal is known to follow the standard.
(Semicolons cause ambiguities that the standard avoided by specifying colons
as a sub-parameter delimiter. A historical misunderstanding meant that many
terminal emulators ended up using semicolons for many years, though.)
A new convention, pioneered in 2016 by tmux, is the "Tc" terminfo extension.
If your terminal's terminfo definition has this flag, Nvim will add
constructed "setrgbf" and "setrgbb" capabilities as if they had been in the
terminfo definition.
If your terminal's terminfo definition does not (yet) have this flag, Nvim
will fall back to looking at the TERM and other environment variables. It
will add constructed "setrgbf" and "setrgbb" capabilities in the case of the
the "rxvt", "linux", "st", "tmux", and "iterm" terminal types, or when
Konsole, genuine Xterm, a libvte terminal emulator version 0.36 or later, or a
terminal emulator that sets the COLORTERM environment variable to "truecolor"
is detected.
*xterm-resize*
Nvim can resize the terminal display on some terminals that implement an
extension pioneered by the dtterm program. |terminfo| does not have a flag
for this extension. So Nvim simply assumes that (all) "dtterm", "xterm",
"teraterm", "rxvt" terminal types, and Konsole, are capable of this.
*cursor-shape* *terminfo-cursor-shape* *termcap-cursor-shape*
Nvim will adjust the shape of the cursor from a block to a line when in insert
mode (or as specified by the 'guicursor' option), on terminals that support
it. It uses the same |terminfo| extensions that were pioneered by tmux for
this: "Ss" and "Se". If your terminfo definition specifies these, as some
(such as those based upon "xterm+tmux") do, then nothing more is required.
If your terminfo definition is missing them, then Nvim will decide whether to
add them to your terminfo definition, by looking at the TERM and other
environment variables. For the "rxvt", "putty", "linux", "screen",
"teraterm", and "iterm" terminal types, or when Konsole, a libvte-based
terminal emulator, or genuine Xterm are detected, it will add constructed
"Ss" and "Se" capabilities.
Note: Sometimes it will appear that Nvim when run within tmux is not changing
the cursor, but in fact it is tmux receiving instructions from Nvim to change
the cursor and not knowing what to do in turn. tmux has to translate what it
receives from Nvim into whatever control sequence is appropriate for the
terminal that it is outputting to. It shares a common mechanism with Nvim, of
using the "Ss" and "Se" capabilities from terminfo (for the output terminal)
if they are present. Unlike Nvim, if they are not present in terminfo you
will have to add them by setting the tmux "terminal-overrides" setting in
$HOME/.tmux.conf .
See the tmux(1) manual page for the details of how and what to do in the tmux
configuration file. It will look something like: >
set -ga terminal-overrides '*:Ss=\E[%p1%d q:Se=\E[ q'
<or (alas!) for Konsole specifically, something more complex like: >
set -ga terminal-overrides \
'xterm*:\E]50;CursorShape=%?%p1%{3}%<%t%{0}%e%{1}%;%d\007'
<but these are only rough examples that do not include all of the other stuff
that occurs in that setting.
*cs7-problem* *cs7-problem*
Note: If the terminal settings are changed after running Vim, you might have Note: If the terminal settings are changed after running Vim, you might have
an illegal combination of settings. This has been reported on Solaris 2.5 an illegal combination of settings. This has been reported on Solaris 2.5
@ -69,20 +242,6 @@ them as a cursor key. When you type you normally are not that fast, so they
are recognized as individual typed commands, even though Vim receives the same are recognized as individual typed commands, even though Vim receives the same
sequence of bytes. sequence of bytes.
*xterm-8bit* *xterm-8-bit*
Xterm can be run in a mode where it uses 8-bit escape sequences. The CSI code
is used instead of <Esc>[. The advantage is that an <Esc> can quickly be
recognized in Insert mode, because it can't be confused with the start of a
special key.
For the builtin termcap entries, Vim checks if the 'term' option contains
"8bit" anywhere. It then uses 8-bit characters for the termcap entries, the
mouse and a few other things. You would normally set $TERM in your shell to
"xterm-8bit" and Vim picks this up and adjusts to the 8-bit setting
automatically.
When Vim receives a response to the "request version" sequence and it
starts with CSI, it assumes that the terminal is in 8-bit mode and will
convert all key sequences to their 8-bit variants.
============================================================================== ==============================================================================
Window size *window-size* Window size *window-size*
@ -93,7 +252,7 @@ On Unix systems, three methods are tried to get the window size:
- an ioctl call (TIOCGSIZE or TIOCGWINSZ, depends on your system) - an ioctl call (TIOCGSIZE or TIOCGWINSZ, depends on your system)
- the environment variables "LINES" and "COLUMNS" - the environment variables "LINES" and "COLUMNS"
- from the termcap entries "li" and "co" - from the |terminfo| entries "lines" and "columns"
If everything fails a default size of 24 lines and 80 columns is assumed. If If everything fails a default size of 24 lines and 80 columns is assumed. If
a window-resize signal is received the size will be set again. If the window a window-resize signal is received the size will be set again. If the window
@ -116,30 +275,27 @@ cursor position is shown in the status line. If you are using horizontal
scrolling ('wrap' option off) consider setting 'sidescroll' to a small scrolling ('wrap' option off) consider setting 'sidescroll' to a small
number. number.
If you have a slow terminal you may want to reset the 'showcmd' option. If you have a slow terminal you may want to reset the 'showcmd' and 'ruler'
The command characters will not be shown in the status line. If the terminal options. The command characters and cursor positions will not be shown in the
scrolls very slowly, set the 'scrolljump' to 5 or so. If the cursor is moved status line (which involves a lot of cursor motions and attribute changes for
off the screen (e.g., with "j") Vim will scroll 5 lines at a time. Another every keypress or movement). If the terminal scrolls very slowly, set the
possibility is to reduce the number of lines that Vim uses with the command 'scrolljump' to 5 or so. If the cursor is moved off the screen (e.g., with
"z{height}<CR>". "j") Vim will scroll 5 lines at a time. Another possibility is to reduce the
number of lines that Vim uses with the command "z{height}<CR>".
If the characters from the terminal are arriving with more than 1 second If the characters from the terminal are arriving with more than 1 second
between them you might want to set the 'timeout' and/or 'ttimeout' option. between them you might want to set the 'timeout' and/or 'ttimeout' option.
See the "Options" chapter |options|. See the "Options" chapter |options|.
If you are using a color terminal that is slow, use this command: > If you are using a color terminal that is slow when displaying lines beyond
the end of a buffer, this is because Nvim is drawing the whitespace twice, in
two sets of colours and attributes. To prevent this, use this command: >
hi NonText cterm=NONE ctermfg=NONE hi NonText cterm=NONE ctermfg=NONE
This avoids that spaces are sent when they have different attributes. On most This draws the spaces with the default colours and attributes, which allows the
terminals you can't see this anyway. second pass of drawing to be optimized away. Note: Although in theory the
colours of whitespace are immaterial, in practice they change the colours of
If you are using Vim over a slow serial line, you might want to try running cursors and selections that cross them. This may have a visible, but minor,
Vim inside the "screen" program. Screen will optimize the terminal I/O quite effect on some UIs.
a bit.
If you are testing termcap options, but you cannot see what is happening,
you might want to set the 'writedelay' option. When non-zero, one character
is sent to the terminal at a time (does not work for MS-DOS). This makes the
screen updating a lot slower, making it possible to see what is happening.
============================================================================== ==============================================================================
Using the mouse *mouse-using* Using the mouse *mouse-using*

View File

@ -113,7 +113,6 @@ screen, you can use CTRL-X CTRL-E and CTRL-X CTRL-Y to scroll the screen.
To make this easier, you could use these mappings: > To make this easier, you could use these mappings: >
:inoremap <C-E> <C-X><C-E> :inoremap <C-E> <C-X><C-E>
:inoremap <C-Y> <C-X><C-Y> :inoremap <C-Y> <C-X><C-Y>
(Type this literally, make sure the '<' flag is not in 'cpoptions').
You then lose the ability to copy text from the line above/below the cursor You then lose the ability to copy text from the line above/below the cursor
|i_CTRL-E|. |i_CTRL-E|.
@ -129,8 +128,6 @@ If you like the scrolling to go a bit smoother, you can use these mappings: >
:map <C-U> <C-Y><C-Y><C-Y><C-Y><C-Y><C-Y><C-Y><C-Y><C-Y><C-Y><C-Y><C-Y><C-Y><C-Y><C-Y><C-Y> :map <C-U> <C-Y><C-Y><C-Y><C-Y><C-Y><C-Y><C-Y><C-Y><C-Y><C-Y><C-Y><C-Y><C-Y><C-Y><C-Y><C-Y>
:map <C-D> <C-E><C-E><C-E><C-E><C-E><C-E><C-E><C-E><C-E><C-E><C-E><C-E><C-E><C-E><C-E><C-E> :map <C-D> <C-E><C-E><C-E><C-E><C-E><C-E><C-E><C-E><C-E><C-E><C-E><C-E><C-E><C-E><C-E><C-E>
(Type this literally, make sure the '<' flag is not in 'cpoptions').
============================================================================== ==============================================================================
Correcting common typing mistakes *type-mistakes* Correcting common typing mistakes *type-mistakes*
@ -282,9 +279,7 @@ For Emacs-style editing on the command-line: >
:cnoremap <Esc><C-B> <S-Left> :cnoremap <Esc><C-B> <S-Left>
" forward one word " forward one word
:cnoremap <Esc><C-F> <S-Right> :cnoremap <Esc><C-F> <S-Right>
<
NOTE: This requires that the '<' flag is excluded from 'cpoptions'. |<>|
*format-bullet-list* *format-bullet-list*
This mapping will format any bullet list. It requires that there is an empty This mapping will format any bullet list. It requires that there is an empty
line above and below each list entry. The expression commands are used to line above and below each list entry. The expression commands are used to
@ -300,8 +295,7 @@ be able to give comments to the parts of the mapping. >
:execute m |" define the mapping :execute m |" define the mapping
(<> notation |<>|. Note that this is all typed literally. ^W is "^" "W", not (<> notation |<>|. Note that this is all typed literally. ^W is "^" "W", not
CTRL-W. You can copy/paste this into Vim if '<' is not included in CTRL-W.)
'cpoptions'.)
Note that the last comment starts with |", because the ":execute" command Note that the last comment starts with |", because the ":execute" command
doesn't accept a comment directly. doesn't accept a comment directly.

View File

@ -156,6 +156,15 @@ are always available and may be used simultaneously in separate plugins. The
`neovim` pip package must be installed to use Python plugins in Nvim (see `neovim` pip package must be installed to use Python plugins in Nvim (see
|provider-python|). |provider-python|).
Because of general |256-color| usage whereever possible, Nvim will even use
256-colour capability on Linux virtual terminals. Vim uses only 8 colours
plus bright foreground on Linux VTs.
Vim combines what is in its |builtin-terms| with what it reads from termcap,
and has a |ttybuiltin| setting to control how that combination works. Nvim
uses either one or the other of an external |terminfo| entry or the built-in
one. It does not attempt to mix data from the two.
|:!| does not support "interactive" commands. Use |:terminal| instead. |:!| does not support "interactive" commands. Use |:terminal| instead.
(GUI Vim has a similar limitation, see ":help gui-pty" in Vim.) (GUI Vim has a similar limitation, see ":help gui-pty" in Vim.)
@ -244,6 +253,8 @@ Lua interface (|if_lua.txt|):
while calling lua chunk: [string "<VimL compiled string>"]:1: TEST” in while calling lua chunk: [string "<VimL compiled string>"]:1: TEST” in
Neovim. Neovim.
- Lua has direct access to Nvim |API| via `vim.api`. - Lua has direct access to Nvim |API| via `vim.api`.
- Lua package.path and package.cpath are automatically updated according to
'runtimepath': |lua-require|.
- Currently, most legacy Vim features are missing. - Currently, most legacy Vim features are missing.
|input()| and |inputdialog()| gained support for each others features (return |input()| and |inputdialog()| gained support for each others features (return
@ -281,6 +292,25 @@ Nvim does not have special `t_XX` options nor <t_XX> keycodes to configure
terminal capabilities. Instead Nvim treats the terminal as any other UI. For terminal capabilities. Instead Nvim treats the terminal as any other UI. For
example, 'guicursor' sets the terminal cursor style if possible. example, 'guicursor' sets the terminal cursor style if possible.
*'term'* *E529* *E530* *E531*
The 'term' option has a fixed value, present only for script compatibility and
intentionally not the same as any known terminal type name. It should be a
rare case in Nvim where one needs |term-dependent-settings|, for which use the
|TERM| environment variable.
*termcap*
Nvim never uses the termcap database and only uses |terminfo|. See
|builtin-terms| for what happens on operating systems without a terminfo
database.
*xterm-8bit* *xterm-8-bit*
Xterm can be run in a mode where it uses true 8-bit CSI. Supporting this
requires autodetection of whether the terminal is in UTF-8 mode or non-UTF-8
mode, as the 8-bit CSI character has to be written differently in each case.
Vim issues a "request version" sequence to the terminal at startup and looks
at how the terminal is sending CSI. Nvim does not issue such a sequence and
always uses 7-bit control sequences.
'ttyfast': 'ttyfast':
":set ttyfast" is ignored ":set ttyfast" is ignored
":set nottyfast" is an error ":set nottyfast" is an error
@ -308,7 +338,7 @@ Test functions:
Other options: Other options:
'antialias' 'antialias'
'cpoptions' ("g", "w", "H", "*", "-", "j", and all POSIX flags were removed) 'cpoptions' (g j k H w < * - and all POSIX flags were removed)
'encoding' ("utf-8" is always used) 'encoding' ("utf-8" is always used)
'esckeys' 'esckeys'
'guioptions' "t" flag was removed 'guioptions' "t" flag was removed
@ -322,7 +352,6 @@ Other options:
'shelltype' 'shelltype'
*'shortname'* *'sn'* *'noshortname'* *'nosn'* *'shortname'* *'sn'* *'noshortname'* *'nosn'*
*'swapsync'* *'sws'* *'swapsync'* *'sws'*
*'term'* *E529* *E530* *E531*
*'termencoding'* *'tenc'* (Vim 7.4.852 also removed this for Windows) *'termencoding'* *'tenc'* (Vim 7.4.852 also removed this for Windows)
'textauto' 'textauto'
'textmode' 'textmode'

View File

@ -271,7 +271,7 @@ mode. For example, if you would like the "/" command not to extend the Visual
area, but instead take the highlighted text and search for that: > area, but instead take the highlighted text and search for that: >
:vmap / y/<C-R>"<CR> :vmap / y/<C-R>"<CR>
(In the <> notation |<>|, when typing it you should type it literally; you (In the <> notation |<>|, when typing it you should type it literally; you
need to remove the 'B' and '<' flags from 'cpoptions'.) need to remove the 'B' flag from 'cpoptions'.)
If you want to give a register name using the """ command, do this just before If you want to give a register name using the """ command, do this just before
typing the operator character: "v{move-around}"xd". typing the operator character: "v{move-around}"xd".
@ -375,7 +375,7 @@ Here is an example, to replace the selected text with the output of "date": >
:vmap _a <Esc>`>a<CR><Esc>`<i<CR><Esc>!!date<CR>kJJ :vmap _a <Esc>`>a<CR><Esc>`<i<CR><Esc>!!date<CR>kJJ
(In the <> notation |<>|, when typing it you should type it literally; you (In the <> notation |<>|, when typing it you should type it literally; you
need to remove the 'B' and '<' flags from 'cpoptions') need to remove the 'B' flag from 'cpoptions')
What this does is: What this does is:
<Esc> stop Visual mode <Esc> stop Visual mode
@ -392,7 +392,7 @@ selected text: >
:vmap X y/<C-R>"<CR> :vmap X y/<C-R>"<CR>
(In the <> notation |<>|, when typing it you should type it literally; you (In the <> notation |<>|, when typing it you should type it literally; you
need to remove the 'B' and '<' flags from 'cpoptions') need to remove the 'B' flag from 'cpoptions')
Note that special characters (like '.' and '*') will cause problems. Note that special characters (like '.' and '*') will cause problems.

View File

@ -117,7 +117,7 @@ check if the 'highlight' option contains "si". In version 3.0, this meant to
invert the status line. Now it should be "sr", reverse the status line, as invert the status line. Now it should be "sr", reverse the status line, as
"si" now stands for italic! If italic is not available on your terminal, the "si" now stands for italic! If italic is not available on your terminal, the
status line is inverted anyway; you will only see this problem on terminals status line is inverted anyway; you will only see this problem on terminals
that have termcap codes for italics. that have |terminfo| capabilities for italics.
============================================================================== ==============================================================================
3. Opening and closing a window *opening-window* *E36* 3. Opening and closing a window *opening-window* *E36*

View File

@ -11,13 +11,16 @@ let b:did_ftplugin = 1
let s:cpo_save = &cpo let s:cpo_save = &cpo
set cpo&vim set cpo&vim
let b:undo_ftplugin = "setl fo< tw< cole< cocu<" let b:undo_ftplugin = "setl fo< tw< cole< cocu< keywordprg<"
setlocal formatoptions+=tcroql textwidth=78 setlocal formatoptions+=tcroql textwidth=78
if has("conceal") if has("conceal")
setlocal cole=2 cocu=nc setlocal cole=2 cocu=nc
endif endif
" Prefer Vim help instead of manpages.
setlocal keywordprg=:help
if !exists('g:no_plugin_maps') if !exists('g:no_plugin_maps')
function! s:show_toc() abort function! s:show_toc() abort
let bufname = bufname('%') let bufname = bufname('%')

View File

@ -14,7 +14,7 @@ let b:did_ftplugin = 1
let s:cpo_save = &cpo let s:cpo_save = &cpo
set cpo-=C set cpo-=C
let b:undo_ftplugin = "setl fo< isk< com< tw< commentstring<" let b:undo_ftplugin = "setl fo< isk< com< tw< commentstring< keywordprg<"
\ . "| unlet! b:match_ignorecase b:match_words b:match_skip" \ . "| unlet! b:match_ignorecase b:match_words b:match_skip"
" Set 'formatoptions' to break comment lines but not other lines, " Set 'formatoptions' to break comment lines but not other lines,
@ -36,6 +36,9 @@ endif
" Comments start with a double quote " Comments start with a double quote
setlocal commentstring=\"%s setlocal commentstring=\"%s
" Prefer Vim help instead of manpages.
setlocal keywordprg=:help
" Move around functions. " Move around functions.
nnoremap <silent><buffer> [[ m':call search('^\s*fu\%[nction]\>', "bW")<CR> nnoremap <silent><buffer> [[ m':call search('^\s*fu\%[nction]\>', "bW")<CR>
vnoremap <silent><buffer> [[ m':<C-U>exe "normal! gv"<Bar>call search('^\s*fu\%[nction]\>', "bW")<CR> vnoremap <silent><buffer> [[ m':<C-U>exe "normal! gv"<Bar>call search('^\s*fu\%[nction]\>', "bW")<CR>

View File

@ -21,7 +21,7 @@ syn keyword vimTodo contained COMBAK FIXME TODO XXX
syn cluster vimCommentGroup contains=vimTodo,@Spell syn cluster vimCommentGroup contains=vimTodo,@Spell
" Special and plugin vim commands {{{2 " Special and plugin vim commands {{{2
syn match vimCommand contained "\<z[-+^.=]\=" syn match vimCommand contained "\<z[-+^.=]\=\>"
syn keyword vimOnlyCommand contained fix[del] op[en] sh[ell] P[rint] syn keyword vimOnlyCommand contained fix[del] op[en] sh[ell] P[rint]
syn keyword vimStdPlugin contained DiffOrig Man N[ext] S TOhtml XMLent XMLns syn keyword vimStdPlugin contained DiffOrig Man N[ext] S TOhtml XMLent XMLns

View File

@ -22,7 +22,7 @@ UNIDIR=${1:-$UNIDIR_DEFAULT}
DOWNLOAD_URL_BASE=${2:-$DOWNLOAD_URL_BASE_DEFAULT} DOWNLOAD_URL_BASE=${2:-$DOWNLOAD_URL_BASE_DEFAULT}
for filename in $data_files ; do for filename in $data_files ; do
curl -o "$UNIDIR/$filename" "$DOWNLOAD_URL_BASE/UNIDATA/$filename" curl -L -o "$UNIDIR/$filename" "$DOWNLOAD_URL_BASE/UNIDATA/$filename"
( (
cd "$UNIDIR" cd "$UNIDIR"
git add $filename git add $filename
@ -30,7 +30,7 @@ for filename in $data_files ; do
done done
for filename in $emoji_files ; do for filename in $emoji_files ; do
curl -o "$UNIDIR/$filename" "$DOWNLOAD_URL_BASE/emoji/3.0/$filename" curl -L -o "$UNIDIR/$filename" "$DOWNLOAD_URL_BASE/emoji/latest/$filename"
( (
cd "$UNIDIR" cd "$UNIDIR"
git add $filename git add $filename

View File

@ -19,8 +19,11 @@ get_jobs_num() {
help() { help() {
echo 'Usage:' echo 'Usage:'
echo ' pvscheck.sh [--pvs URL] [--deps] [target-directory [branch]]' echo ' pvscheck.sh [--pvs URL] [--deps] [--environment-cc]'
echo ' pvscheck.sh [--pvs URL] [--recheck|--only-analyse] [target-directory]' echo ' [target-directory [branch]]'
echo ' pvscheck.sh [--pvs URL] [--recheck] [--environment-cc]'
echo ' [target-directory]'
echo ' pvscheck.sh [--pvs URL] --only-analyse [target-directory]'
echo ' pvscheck.sh [--pvs URL] --pvs-install {target-directory}' echo ' pvscheck.sh [--pvs URL] --pvs-install {target-directory}'
echo ' pvscheck.sh --patch [--only-build]' echo ' pvscheck.sh --patch [--only-build]'
echo echo
@ -35,6 +38,9 @@ help() {
echo ' Without this it assumes all dependencies are already' echo ' Without this it assumes all dependencies are already'
echo ' installed.' echo ' installed.'
echo echo
echo ' --environment-cc: (for regular run and --recheck) Do not export'
echo ' CC=clang. Build is still run with CFLAGS=-O0.'
echo
echo ' --only-build: (for --patch) Only patch files in ./build directory.' echo ' --only-build: (for --patch) Only patch files in ./build directory.'
echo echo
echo ' --pvs-install: Only install PVS-studio to the specified location.' echo ' --pvs-install: Only install PVS-studio to the specified location.'
@ -270,8 +276,11 @@ install_pvs() {(
create_compile_commands() {( create_compile_commands() {(
local tgt="$1" ; shift local tgt="$1" ; shift
local deps="$1" ; shift local deps="$1" ; shift
local environment_cc="$1" ; shift
export CC=clang if test -z "$environment_cc" ; then
export CC=clang
fi
export CFLAGS=' -O0 ' export CFLAGS=' -O0 '
if test -z "$deps" ; then if test -z "$deps" ; then
@ -356,19 +365,21 @@ do_check() {
local branch="$1" ; shift local branch="$1" ; shift
local pvs_url="$1" ; shift local pvs_url="$1" ; shift
local deps="$1" ; shift local deps="$1" ; shift
local environment_cc="$1" ; shift
git clone --branch="$branch" . "$tgt" git clone --branch="$branch" . "$tgt"
install_pvs "$tgt" "$pvs_url" install_pvs "$tgt" "$pvs_url"
do_recheck "$tgt" "$deps" do_recheck "$tgt" "$deps" "$environment_cc"
} }
do_recheck() { do_recheck() {
local tgt="$1" ; shift local tgt="$1" ; shift
local deps="$1" ; shift local deps="$1" ; shift
local environment_cc="$1" ; shift
create_compile_commands "$tgt" "$deps" create_compile_commands "$tgt" "$deps" "$environment_cc"
do_analysis "$tgt" do_analysis "$tgt"
} }
@ -408,6 +419,7 @@ main() {
only-analyse store_const \ only-analyse store_const \
pvs-install store_const \ pvs-install store_const \
deps store_const \ deps store_const \
environment-cc store_const \
-- \ -- \
'modify realdir tgt "$PWD/../neovim-pvs"' \ 'modify realdir tgt "$PWD/../neovim-pvs"' \
'store branch master' \ 'store branch master' \
@ -426,11 +438,11 @@ main() {
elif test -n "$pvs_install" ; then elif test -n "$pvs_install" ; then
install_pvs "$tgt" "$pvs_url" install_pvs "$tgt" "$pvs_url"
elif test -n "$recheck" ; then elif test -n "$recheck" ; then
do_recheck "$tgt" "$deps" do_recheck "$tgt" "$deps" "$environment_cc"
elif test -n "$only_analyse" ; then elif test -n "$only_analyse" ; then
do_analysis "$tgt" do_analysis "$tgt"
else else
do_check "$tgt" "$branch" "$pvs_url" "$deps" do_check "$tgt" "$branch" "$pvs_url" "$deps" "$environment_cc"
fi fi
} }

View File

@ -310,7 +310,7 @@ list_vim_patches() {
# Get missing Vim commits # Get missing Vim commits
local vim_commits local vim_commits
vim_commits="$(cd "${VIM_SOURCE_DIR}" && git log --reverse --format='%H' v7.4.1979..HEAD)" vim_commits="$(cd "${VIM_SOURCE_DIR}" && git log --reverse --format='%H' v8.0.0000..HEAD)"
local vim_commit local vim_commit
for vim_commit in ${vim_commits}; do for vim_commit in ${vim_commits}; do
@ -320,6 +320,7 @@ list_vim_patches() {
vim_tag="$(cd "${VIM_SOURCE_DIR}" && git describe --tags --exact-match "${vim_commit}" 2>/dev/null)" || true vim_tag="$(cd "${VIM_SOURCE_DIR}" && git describe --tags --exact-match "${vim_commit}" 2>/dev/null)" || true
if [[ -n "${vim_tag}" ]]; then if [[ -n "${vim_tag}" ]]; then
local patch_number="${vim_tag:5}" # Remove prefix like "v7.4." local patch_number="${vim_tag:5}" # Remove prefix like "v7.4."
patch_number="$(echo ${patch_number} | sed 's/^0*//g')" # Remove prefix "0"
# Tagged Vim patch, check version.c: # Tagged Vim patch, check version.c:
is_missing="$(sed -n '/static const int included_patches/,/}/p' "${NVIM_SOURCE_DIR}/src/nvim/version.c" | is_missing="$(sed -n '/static const int included_patches/,/}/p' "${NVIM_SOURCE_DIR}/src/nvim/version.c" |
grep -x -e "[[:space:]]*//[[:space:]]${patch_number} NA.*" -e "[[:space:]]*${patch_number}," >/dev/null && echo "false" || echo "true")" grep -x -e "[[:space:]]*//[[:space:]]${patch_number} NA.*" -e "[[:space:]]*${patch_number}," >/dev/null && echo "false" || echo "true")"

View File

@ -141,7 +141,7 @@ Object dict_get_value(dict_T *dict, String key, Error *err)
dictitem_T *const di = tv_dict_find(dict, key.data, (ptrdiff_t)key.size); dictitem_T *const di = tv_dict_find(dict, key.data, (ptrdiff_t)key.size);
if (di == NULL) { if (di == NULL) {
api_set_error(err, kErrorTypeValidation, "Key not found"); api_set_error(err, kErrorTypeValidation, "Key '%s' not found", key.data);
return (Object)OBJECT_INIT; return (Object)OBJECT_INIT;
} }

View File

@ -136,9 +136,13 @@ Integer nvim_input(String keys)
return (Integer)input_enqueue(keys); return (Integer)input_enqueue(keys);
} }
/// Replaces terminal codes and key codes (<CR>, <Esc>, ...) in a string with /// Replaces terminal codes and |keycodes| (<CR>, <Esc>, ...) in a string with
/// the internal representation. /// the internal representation.
/// ///
/// @param str String to be converted.
/// @param from_part Legacy Vim parameter. Usually true.
/// @param do_lt Also translate <lt>. Ignored if `special` is false.
/// @param special Replace |keycodes|, e.g. <CR> becomes a "\n" char.
/// @see replace_termcodes /// @see replace_termcodes
/// @see cpoptions /// @see cpoptions
String nvim_replace_termcodes(String str, Boolean from_part, Boolean do_lt, String nvim_replace_termcodes(String str, Boolean from_part, Boolean do_lt,
@ -151,12 +155,6 @@ String nvim_replace_termcodes(String str, Boolean from_part, Boolean do_lt,
} }
char *ptr = NULL; char *ptr = NULL;
// Set 'cpoptions' the way we want it.
// FLAG_CPO_BSLASH set - backslashes are *not* treated specially
// FLAG_CPO_KEYCODE set - keycodes are *not* reverse-engineered
// FLAG_CPO_SPECI unset - <Key> sequences *are* interpreted
// The third from end parameter of replace_termcodes() is true so that the
// <lt> sequence is recognised - needed for a real backslash.
replace_termcodes((char_u *)str.data, str.size, (char_u **)&ptr, replace_termcodes((char_u *)str.data, str.size, (char_u **)&ptr,
from_part, do_lt, special, CPO_TO_CPO_FLAGS); from_part, do_lt, special, CPO_TO_CPO_FLAGS);
return cstr_as_string(ptr); return cstr_as_string(ptr);
@ -300,7 +298,7 @@ ArrayOf(String) nvim_list_runtime_paths(void)
FUNC_API_SINCE(1) FUNC_API_SINCE(1)
{ {
Array rv = ARRAY_DICT_INIT; Array rv = ARRAY_DICT_INIT;
uint8_t *rtp = p_rtp; char_u *rtp = p_rtp;
if (*rtp == NUL) { if (*rtp == NUL) {
// No paths // No paths
@ -314,13 +312,14 @@ ArrayOf(String) nvim_list_runtime_paths(void)
} }
rtp++; rtp++;
} }
rv.size++;
// Allocate memory for the copies // Allocate memory for the copies
rv.items = xmalloc(sizeof(Object) * rv.size); rv.items = xmalloc(sizeof(*rv.items) * rv.size);
// Reset the position // Reset the position
rtp = p_rtp; rtp = p_rtp;
// Start copying // Start copying
for (size_t i = 0; i < rv.size && *rtp != NUL; i++) { for (size_t i = 0; i < rv.size; i++) {
rv.items[i].type = kObjectTypeString; rv.items[i].type = kObjectTypeString;
rv.items[i].data.string.data = xmalloc(MAXPATHL); rv.items[i].data.string.data = xmalloc(MAXPATHL);
// Copy the path from 'runtimepath' to rv.items[i] // Copy the path from 'runtimepath' to rv.items[i]
@ -709,7 +708,7 @@ void nvim_unsubscribe(uint64_t channel_id, String event)
Integer nvim_get_color_by_name(String name) Integer nvim_get_color_by_name(String name)
FUNC_API_SINCE(1) FUNC_API_SINCE(1)
{ {
return name_to_color((uint8_t *)name.data); return name_to_color((char_u *)name.data);
} }
Dictionary nvim_get_color_map(void) Dictionary nvim_get_color_map(void)
@ -871,7 +870,7 @@ static void write_msg(String message, bool to_err)
#define PUSH_CHAR(i, pos, line_buf, msg) \ #define PUSH_CHAR(i, pos, line_buf, msg) \
if (message.data[i] == NL || pos == LINE_BUFFER_SIZE - 1) { \ if (message.data[i] == NL || pos == LINE_BUFFER_SIZE - 1) { \
line_buf[pos] = NUL; \ line_buf[pos] = NUL; \
msg((uint8_t *)line_buf); \ msg((char_u *)line_buf); \
pos = 0; \ pos = 0; \
continue; \ continue; \
} \ } \

View File

@ -91,6 +91,57 @@ static char *e_auabort = N_("E855: Autocommands caused command to abort");
// Number of times free_buffer() was called. // Number of times free_buffer() was called.
static int buf_free_count = 0; static int buf_free_count = 0;
// Read data from buffer for retrying.
static int
read_buffer(
int read_stdin, // read file from stdin, otherwise fifo
exarg_T *eap, // for forced 'ff' and 'fenc' or NULL
int flags) // extra flags for readfile()
{
int retval = OK;
linenr_T line_count;
//
// Read from the buffer which the text is already filled in and append at
// the end. This makes it possible to retry when 'fileformat' or
// 'fileencoding' was guessed wrong.
//
line_count = curbuf->b_ml.ml_line_count;
retval = readfile(
read_stdin ? NULL : curbuf->b_ffname,
read_stdin ? NULL : curbuf->b_fname,
(linenr_T)line_count, (linenr_T)0, (linenr_T)MAXLNUM, eap,
flags | READ_BUFFER);
if (retval == OK) {
// Delete the binary lines.
while (--line_count >= 0) {
ml_delete((linenr_T)1, false);
}
} else {
// Delete the converted lines.
while (curbuf->b_ml.ml_line_count > line_count) {
ml_delete(line_count, false);
}
}
// Put the cursor on the first line.
curwin->w_cursor.lnum = 1;
curwin->w_cursor.col = 0;
if (read_stdin) {
// Set or reset 'modified' before executing autocommands, so that
// it can be changed there.
if (!readonlymode && !bufempty()) {
changed();
} else if (retval != FAIL) {
unchanged(curbuf, false);
}
apply_autocmds_retval(EVENT_STDINREADPOST, NULL, NULL, false,
curbuf, &retval);
}
return retval;
}
/* /*
* Open current buffer, that is: open the memfile and read the file into * Open current buffer, that is: open the memfile and read the file into
* memory. * memory.
@ -106,6 +157,7 @@ open_buffer (
int retval = OK; int retval = OK;
bufref_T old_curbuf; bufref_T old_curbuf;
long old_tw = curbuf->b_p_tw; long old_tw = curbuf->b_p_tw;
int read_fifo = false;
/* /*
* The 'readonly' flag is only set when BF_NEVERLOADED is being reset. * The 'readonly' flag is only set when BF_NEVERLOADED is being reset.
@ -156,13 +208,45 @@ open_buffer (
if (curbuf->b_ffname != NULL) { if (curbuf->b_ffname != NULL) {
int old_msg_silent = msg_silent; int old_msg_silent = msg_silent;
#ifdef UNIX
int save_bin = curbuf->b_p_bin;
int perm;
perm = os_getperm((const char *)curbuf->b_ffname);
if (perm >= 0 && (0
# ifdef S_ISFIFO
|| S_ISFIFO(perm)
# endif
# ifdef S_ISSOCK
|| S_ISSOCK(perm)
# endif
# ifdef OPEN_CHR_FILES
|| (S_ISCHR(perm)
&& is_dev_fd_file(curbuf->b_ffname))
# endif
)
) {
read_fifo = true;
}
if (read_fifo) {
curbuf->b_p_bin = true;
}
#endif
if (shortmess(SHM_FILEINFO)) { if (shortmess(SHM_FILEINFO)) {
msg_silent = 1; msg_silent = 1;
} }
retval = readfile(curbuf->b_ffname, curbuf->b_fname, retval = readfile(curbuf->b_ffname, curbuf->b_fname,
(linenr_T)0, (linenr_T)0, (linenr_T)MAXLNUM, eap, (linenr_T)0, (linenr_T)0, (linenr_T)MAXLNUM, eap,
flags | READ_NEW); flags | READ_NEW | (read_fifo ? READ_FIFO : 0));
#ifdef UNIX
if (read_fifo) {
curbuf->b_p_bin = save_bin;
if (retval == OK) {
retval = read_buffer(false, eap, flags);
}
}
#endif
msg_silent = old_msg_silent; msg_silent = old_msg_silent;
// Help buffer is filtered. // Help buffer is filtered.
@ -171,7 +255,6 @@ open_buffer (
} }
} else if (read_stdin) { } else if (read_stdin) {
int save_bin = curbuf->b_p_bin; int save_bin = curbuf->b_p_bin;
linenr_T line_count;
/* /*
* First read the text in binary mode into the buffer. * First read the text in binary mode into the buffer.
@ -185,41 +268,13 @@ open_buffer (
flags | (READ_NEW + READ_STDIN)); flags | (READ_NEW + READ_STDIN));
curbuf->b_p_bin = save_bin; curbuf->b_p_bin = save_bin;
if (retval == OK) { if (retval == OK) {
line_count = curbuf->b_ml.ml_line_count; retval = read_buffer(true, eap, flags);
retval = readfile(NULL, NULL, (linenr_T)line_count,
(linenr_T)0, (linenr_T)MAXLNUM, eap,
flags | READ_BUFFER);
if (retval == OK) {
/* Delete the binary lines. */
while (--line_count >= 0)
ml_delete((linenr_T)1, FALSE);
} else {
/* Delete the converted lines. */
while (curbuf->b_ml.ml_line_count > line_count)
ml_delete(line_count, FALSE);
}
/* Put the cursor on the first line. */
curwin->w_cursor.lnum = 1;
curwin->w_cursor.col = 0;
// Set or reset 'modified' before executing autocommands, so that
// it can be changed there.
if (!readonlymode && !bufempty()) {
changed();
} else if (retval == OK) {
unchanged(curbuf, false);
}
if (retval == OK) {
apply_autocmds_retval(EVENT_STDINREADPOST, NULL, NULL, false,
curbuf, &retval);
}
} }
} }
/* if first time loading this buffer, init b_chartab[] */ /* if first time loading this buffer, init b_chartab[] */
if (curbuf->b_flags & BF_NEVERLOADED) { if (curbuf->b_flags & BF_NEVERLOADED) {
(void)buf_init_chartab(curbuf, FALSE); (void)buf_init_chartab(curbuf, false);
parse_cino(curbuf); parse_cino(curbuf);
} }
@ -234,7 +289,7 @@ open_buffer (
|| modified_was_set // ":set modified" used in autocmd || modified_was_set // ":set modified" used in autocmd
|| (aborting() && vim_strchr(p_cpo, CPO_INTMOD) != NULL)) { || (aborting() && vim_strchr(p_cpo, CPO_INTMOD) != NULL)) {
changed(); changed();
} else if (retval == OK && !read_stdin) { } else if (retval != FAIL && !read_stdin && !read_fifo) {
unchanged(curbuf, false); unchanged(curbuf, false);
} }
save_file_ff(curbuf); // keep this fileformat save_file_ff(curbuf); // keep this fileformat
@ -416,8 +471,8 @@ void close_buffer(win_T *win, buf_T *buf, int action, int abort_if_last)
return; return;
} }
/* When the buffer becomes hidden, but is not unloaded, trigger // When the buffer becomes hidden, but is not unloaded, trigger
* BufHidden */ // BufHidden
if (!unload_buf) { if (!unload_buf) {
buf->b_locked++; buf->b_locked++;
if (apply_autocmds(EVENT_BUFHIDDEN, buf->b_fname, buf->b_fname, false, if (apply_autocmds(EVENT_BUFHIDDEN, buf->b_fname, buf->b_fname, false,

View File

@ -29,6 +29,6 @@ typedef struct {
} BufhlLineInfo; } BufhlLineInfo;
#define BUFHL_CMP(a, b) ((int)(((a)->line - (b)->line))) #define BUFHL_CMP(a, b) ((int)(((a)->line - (b)->line)))
KBTREE_INIT(bufhl, BufhlLine *, BUFHL_CMP, 10) KBTREE_INIT(bufhl, BufhlLine *, BUFHL_CMP, 10) // -V512
typedef kbtree_t(bufhl) BufhlInfo; typedef kbtree_t(bufhl) BufhlInfo;
#endif // NVIM_BUFHL_DEFS_H #endif // NVIM_BUFHL_DEFS_H

View File

@ -981,10 +981,8 @@ int win_lbr_chartabsize(win_T *wp, char_u *line, char_u *s, colnr_T col, int *he
mb_ptr_adv(s); mb_ptr_adv(s);
c = *s; c = *s;
if (!((c != NUL) if (!(c != NUL
&& (vim_isbreak(c) && (vim_isbreak(c) || col2 == col || !vim_isbreak(*ps)))) {
|| (!vim_isbreak(c)
&& ((col2 == col) || !vim_isbreak(*ps)))))) {
break; break;
} }

View File

@ -462,7 +462,7 @@ static void insert_enter(InsertState *s)
// Always update o_lnum, so that a "CTRL-O ." that adds a line // Always update o_lnum, so that a "CTRL-O ." that adds a line
// still puts the cursor back after the inserted text. // still puts the cursor back after the inserted text.
if (ins_at_eol && gchar_cursor() == NUL) { if (ins_at_eol) {
o_lnum = curwin->w_cursor.lnum; o_lnum = curwin->w_cursor.lnum;
} }

View File

@ -12119,15 +12119,17 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact)
mode = get_map_mode((char_u **)&which, 0); mode = get_map_mode((char_u **)&which, 0);
keys = replace_termcodes(keys, STRLEN(keys), &keys_buf, true, true, false, keys = replace_termcodes(keys, STRLEN(keys), &keys_buf, true, true, true,
CPO_TO_CPO_FLAGS); CPO_TO_CPO_FLAGS);
rhs = check_map(keys, mode, exact, false, abbr, &mp, &buffer_local); rhs = check_map(keys, mode, exact, false, abbr, &mp, &buffer_local);
xfree(keys_buf); xfree(keys_buf);
if (!get_dict) { if (!get_dict) {
/* Return a string. */ // Return a string.
if (rhs != NULL) if (rhs != NULL) {
rettv->vval.v_string = str2special_save(rhs, FALSE); rettv->vval.v_string = (char_u *)str2special_save(
(const char *)rhs, false, false);
}
} else { } else {
tv_dict_alloc_ret(rettv); tv_dict_alloc_ret(rettv);
@ -12162,7 +12164,8 @@ void mapblock_fill_dict(dict_T *const dict,
bool compatible) bool compatible)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_ALL
{ {
char_u *lhs = str2special_save(mp->m_keys, true); char *const lhs = str2special_save((const char *)mp->m_keys,
compatible, !compatible);
char *const mapmode = map_mode_to_chars(mp->m_mode); char *const mapmode = map_mode_to_chars(mp->m_mode);
varnumber_T noremap_value; varnumber_T noremap_value;
@ -12176,18 +12179,21 @@ void mapblock_fill_dict(dict_T *const dict,
noremap_value = mp->m_noremap == REMAP_SCRIPT ? 2 : !!mp->m_noremap; noremap_value = mp->m_noremap == REMAP_SCRIPT ? 2 : !!mp->m_noremap;
} }
tv_dict_add_str(dict, S_LEN("lhs"), (const char *)lhs); if (compatible) {
tv_dict_add_str(dict, S_LEN("rhs"), (const char *)mp->m_orig_str); tv_dict_add_str(dict, S_LEN("rhs"), (const char *)mp->m_orig_str);
} else {
tv_dict_add_allocated_str(dict, S_LEN("rhs"),
str2special_save((const char *)mp->m_str, false,
true));
}
tv_dict_add_allocated_str(dict, S_LEN("lhs"), lhs);
tv_dict_add_nr(dict, S_LEN("noremap"), noremap_value); tv_dict_add_nr(dict, S_LEN("noremap"), noremap_value);
tv_dict_add_nr(dict, S_LEN("expr"), mp->m_expr ? 1 : 0); tv_dict_add_nr(dict, S_LEN("expr"), mp->m_expr ? 1 : 0);
tv_dict_add_nr(dict, S_LEN("silent"), mp->m_silent ? 1 : 0); tv_dict_add_nr(dict, S_LEN("silent"), mp->m_silent ? 1 : 0);
tv_dict_add_nr(dict, S_LEN("sid"), (varnumber_T)mp->m_script_ID); tv_dict_add_nr(dict, S_LEN("sid"), (varnumber_T)mp->m_script_ID);
tv_dict_add_nr(dict, S_LEN("buffer"), (varnumber_T)buffer_value); tv_dict_add_nr(dict, S_LEN("buffer"), (varnumber_T)buffer_value);
tv_dict_add_nr(dict, S_LEN("nowait"), mp->m_nowait ? 1 : 0); tv_dict_add_nr(dict, S_LEN("nowait"), mp->m_nowait ? 1 : 0);
tv_dict_add_str(dict, S_LEN("mode"), mapmode); tv_dict_add_allocated_str(dict, S_LEN("mode"), mapmode);
xfree(lhs);
xfree(mapmode);
} }
/* /*
@ -12453,7 +12459,7 @@ static void f_matchadd(typval_T *argvars, typval_T *rettv, FunPtr fptr)
return; return;
} }
if (id >= 1 && id <= 3) { if (id >= 1 && id <= 3) {
EMSGN("E798: ID is reserved for \":match\": %" PRId64, id); EMSGN(_("E798: ID is reserved for \":match\": %" PRId64), id);
return; return;
} }
@ -12510,7 +12516,7 @@ static void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr)
// id == 3 is ok because matchaddpos() is supposed to substitute :3match // id == 3 is ok because matchaddpos() is supposed to substitute :3match
if (id == 1 || id == 2) { if (id == 1 || id == 2) {
EMSGN("E798: ID is reserved for \"match\": %" PRId64, id); EMSGN(_("E798: ID is reserved for \"match\": %" PRId64), id);
return; return;
} }

View File

@ -1386,12 +1386,33 @@ int tv_dict_add_str(dict_T *const d,
const char *const key, const size_t key_len, const char *const key, const size_t key_len,
const char *const val) const char *const val)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_ALL
{
return tv_dict_add_allocated_str(d, key, key_len, xstrdup(val));
}
/// Add a string entry to dictionary
///
/// Unlike tv_dict_add_str() saves val to the new dictionary item in place of
/// creating a new copy.
///
/// @warning String will be freed even in case addition fails.
///
/// @param[out] d Dictionary to add entry to.
/// @param[in] key Key to add.
/// @param[in] key_len Key length.
/// @param[in] val String to add.
///
/// @return OK in case of success, FAIL when key already exists.
int tv_dict_add_allocated_str(dict_T *const d,
const char *const key, const size_t key_len,
char *const val)
FUNC_ATTR_NONNULL_ALL
{ {
dictitem_T *const item = tv_dict_item_alloc_len(key, key_len); dictitem_T *const item = tv_dict_item_alloc_len(key, key_len);
item->di_tv.v_lock = VAR_UNLOCKED; item->di_tv.v_lock = VAR_UNLOCKED;
item->di_tv.v_type = VAR_STRING; item->di_tv.v_type = VAR_STRING;
item->di_tv.vval.v_string = (char_u *)xstrdup(val); item->di_tv.vval.v_string = (char_u *)val;
if (tv_dict_add(d, item) == FAIL) { if (tv_dict_add(d, item) == FAIL) {
tv_dict_item_free(item); tv_dict_item_free(item);
return FAIL; return FAIL;

View File

@ -21,9 +21,9 @@
# include "event/process.c.generated.h" # include "event/process.c.generated.h"
#endif #endif
// Time (ns) for a process to exit cleanly before we send TERM/KILL. // Time for a process to exit cleanly before we send KILL.
#define TERM_TIMEOUT 1000000000 // For pty processes SIGTERM is sent first (in case SIGHUP was not enough).
#define KILL_TIMEOUT (TERM_TIMEOUT * 2) #define KILL_TIMEOUT_MS 2000
#define CLOSE_PROC_STREAM(proc, stream) \ #define CLOSE_PROC_STREAM(proc, stream) \
do { \ do { \
@ -125,8 +125,6 @@ void process_teardown(Loop *loop) FUNC_ATTR_NONNULL_ALL
// Close handles to process without killing it. // Close handles to process without killing it.
CREATE_EVENT(loop->events, process_close_handles, 1, proc); CREATE_EVENT(loop->events, process_close_handles, 1, proc);
} else { } else {
uv_kill(proc->pid, SIGTERM);
proc->term_sent = true;
process_stop(proc); process_stop(proc);
} }
} }
@ -238,6 +236,8 @@ void process_stop(Process *proc) FUNC_ATTR_NONNULL_ALL
// stdout/stderr, they will be closed when it exits(possibly due to being // stdout/stderr, they will be closed when it exits(possibly due to being
// terminated after a timeout) // terminated after a timeout)
process_close_in(proc); process_close_in(proc);
ILOG("Sending SIGTERM to pid %d", proc->pid);
uv_kill(proc->pid, SIGTERM);
break; break;
case kProcessTypePty: case kProcessTypePty:
// close all streams for pty processes to send SIGHUP to the process // close all streams for pty processes to send SIGHUP to the process
@ -251,9 +251,10 @@ void process_stop(Process *proc) FUNC_ATTR_NONNULL_ALL
Loop *loop = proc->loop; Loop *loop = proc->loop;
if (!loop->children_stop_requests++) { if (!loop->children_stop_requests++) {
// When there's at least one stop request pending, start a timer that // When there's at least one stop request pending, start a timer that
// will periodically check if a signal should be send to a to the job // will periodically check if a signal should be send to the job.
DLOG("Starting job kill timer"); ILOG("Starting job kill timer");
uv_timer_start(&loop->children_kill_timer, children_kill_cb, 100, 100); uv_timer_start(&loop->children_kill_timer, children_kill_cb,
KILL_TIMEOUT_MS, KILL_TIMEOUT_MS);
} }
} }
@ -269,15 +270,15 @@ static void children_kill_cb(uv_timer_t *handle)
if (!proc->stopped_time) { if (!proc->stopped_time) {
continue; continue;
} }
uint64_t elapsed = now - proc->stopped_time; uint64_t elapsed = (now - proc->stopped_time) / 1000000 + 1;
if (!proc->term_sent && elapsed >= TERM_TIMEOUT) { if (elapsed >= KILL_TIMEOUT_MS) {
ILOG("Sending SIGTERM to pid %d", proc->pid); int sig = proc->type == kProcessTypePty && elapsed < KILL_TIMEOUT_MS * 2
uv_kill(proc->pid, SIGTERM); ? SIGTERM
proc->term_sent = true; : SIGKILL;
} else if (elapsed >= KILL_TIMEOUT) { ILOG("Sending %s to pid %d", sig == SIGTERM ? "SIGTERM" : "SIGKILL",
ILOG("Sending SIGKILL to pid %d", proc->pid); proc->pid);
uv_kill(proc->pid, SIGKILL); uv_kill(proc->pid, sig);
} }
} }
} }

View File

@ -26,7 +26,7 @@ struct process {
Stream *in, *out, *err; Stream *in, *out, *err;
process_exit_cb cb; process_exit_cb cb;
internal_process_cb internal_exit_cb, internal_close_cb; internal_process_cb internal_exit_cb, internal_close_cb;
bool closed, term_sent, detach; bool closed, detach;
MultiQueue *events; MultiQueue *events;
}; };
@ -48,7 +48,6 @@ static inline Process process_init(Loop *loop, ProcessType type, void *data)
.err = NULL, .err = NULL,
.cb = NULL, .cb = NULL,
.closed = false, .closed = false,
.term_sent = false,
.internal_close_cb = NULL, .internal_close_cb = NULL,
.internal_exit_cb = NULL, .internal_exit_cb = NULL,
.detach = false .detach = false

View File

@ -66,6 +66,7 @@ int socket_watcher_init(Loop *loop, SocketWatcher *watcher,
watcher->uv.tcp.addrinfo = request.addrinfo; watcher->uv.tcp.addrinfo = request.addrinfo;
uv_tcp_init(&loop->uv, &watcher->uv.tcp.handle); uv_tcp_init(&loop->uv, &watcher->uv.tcp.handle);
uv_tcp_nodelay(&watcher->uv.tcp.handle, true);
watcher->stream = STRUCT_CAST(uv_stream_t, &watcher->uv.tcp.handle); watcher->stream = STRUCT_CAST(uv_stream_t, &watcher->uv.tcp.handle);
} else { } else {
uv_pipe_init(&loop->uv, &watcher->uv.pipe.handle, 0); uv_pipe_init(&loop->uv, &watcher->uv.pipe.handle, 0);
@ -104,9 +105,10 @@ int socket_watcher_start(SocketWatcher *watcher, int backlog, socket_cb cb)
// contain 0 in this case, unless uv_tcp_getsockname() is used first. // contain 0 in this case, unless uv_tcp_getsockname() is used first.
uv_tcp_getsockname(&watcher->uv.tcp.handle, (struct sockaddr *)&sas, uv_tcp_getsockname(&watcher->uv.tcp.handle, (struct sockaddr *)&sas,
&(int){ sizeof(sas) }); &(int){ sizeof(sas) });
uint16_t port = (uint16_t)((sas.ss_family == AF_INET) uint16_t port = (uint16_t)(
? ((struct sockaddr_in *)&sas)->sin_port (sas.ss_family == AF_INET)
: ((struct sockaddr_in6 *)&sas)->sin6_port); ? (STRUCT_CAST(struct sockaddr_in, &sas))->sin_port
: (STRUCT_CAST(struct sockaddr_in6, &sas))->sin6_port);
// v:servername uses the string from watcher->addr // v:servername uses the string from watcher->addr
size_t len = strlen(watcher->addr); size_t len = strlen(watcher->addr);
snprintf(watcher->addr+len, sizeof(watcher->addr)-len, ":%" PRIu16, snprintf(watcher->addr+len, sizeof(watcher->addr)-len, ":%" PRIu16,
@ -146,6 +148,7 @@ int socket_watcher_accept(SocketWatcher *watcher, Stream *stream)
if (watcher->stream->type == UV_TCP) { if (watcher->stream->type == UV_TCP) {
client = STRUCT_CAST(uv_stream_t, &stream->uv.tcp); client = STRUCT_CAST(uv_stream_t, &stream->uv.tcp);
uv_tcp_init(watcher->uv.tcp.handle.loop, (uv_tcp_t *)client); uv_tcp_init(watcher->uv.tcp.handle.loop, (uv_tcp_t *)client);
uv_tcp_nodelay((uv_tcp_t *)client, true);
} else { } else {
client = STRUCT_CAST(uv_stream_t, &stream->uv.pipe); client = STRUCT_CAST(uv_stream_t, &stream->uv.pipe);
uv_pipe_init(watcher->uv.pipe.handle.loop, (uv_pipe_t *)client, 0); uv_pipe_init(watcher->uv.pipe.handle.loop, (uv_pipe_t *)client, 0);
@ -237,6 +240,7 @@ bool socket_connect(Loop *loop, Stream *stream,
tcp_retry: tcp_retry:
uv_tcp_init(&loop->uv, tcp); uv_tcp_init(&loop->uv, tcp);
uv_tcp_nodelay(tcp, true);
uv_tcp_connect(&req, tcp, addrinfo->ai_addr, connect_cb); uv_tcp_connect(&req, tcp, addrinfo->ai_addr, connect_cb);
uv_stream = (uv_stream_t *)tcp; uv_stream = (uv_stream_t *)tcp;
@ -244,7 +248,7 @@ tcp_retry:
uv_pipe_t *pipe = &stream->uv.pipe; uv_pipe_t *pipe = &stream->uv.pipe;
uv_pipe_init(&loop->uv, pipe, 0); uv_pipe_init(&loop->uv, pipe, 0);
uv_pipe_connect(&req, pipe, address, connect_cb); uv_pipe_connect(&req, pipe, address, connect_cb);
uv_stream = (uv_stream_t *)pipe; uv_stream = STRUCT_CAST(uv_stream_t, pipe);
} }
status = 1; status = 1;
LOOP_PROCESS_EVENTS_UNTIL(&main_loop, NULL, timeout, status != 1); LOOP_PROCESS_EVENTS_UNTIL(&main_loop, NULL, timeout, status != 1);

View File

@ -3332,7 +3332,8 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout)
// Check for a match on each line. // Check for a match on each line.
linenr_T line2 = eap->line2; linenr_T line2 = eap->line2;
for (linenr_T lnum = eap->line1; for (linenr_T lnum = eap->line1;
lnum <= line2 && !(got_quit || aborting()); lnum <= line2 && !(got_quit || aborting())
&& (!preview || matched_lines.size <= (size_t)p_cwh);
lnum++) { lnum++) {
long nmatch = vim_regexec_multi(&regmatch, curwin, curbuf, lnum, long nmatch = vim_regexec_multi(&regmatch, curwin, curbuf, lnum,
(colnr_T)0, NULL); (colnr_T)0, NULL);
@ -5022,8 +5023,9 @@ static void helptags_one(char_u *dir, char_u *ext, char_u *tagfname,
if (gen_expand_wildcards(1, buff_list, &filecount, &files, if (gen_expand_wildcards(1, buff_list, &filecount, &files,
EW_FILE|EW_SILENT) == FAIL EW_FILE|EW_SILENT) == FAIL
|| filecount == 0) { || filecount == 0) {
if (!got_int) if (!got_int) {
EMSG2("E151: No match: %s", NameBuff); EMSG2(_("E151: No match: %s"), NameBuff);
}
return; return;
} }
@ -5222,7 +5224,7 @@ static void do_helptags(char_u *dirname, bool add_help_tags)
if (gen_expand_wildcards(1, buff_list, &filecount, &files, if (gen_expand_wildcards(1, buff_list, &filecount, &files,
EW_FILE|EW_SILENT) == FAIL EW_FILE|EW_SILENT) == FAIL
|| filecount == 0) { || filecount == 0) {
EMSG2("E151: No match: %s", NameBuff); EMSG2(_("E151: No match: %s"), NameBuff);
xfree(dirname); xfree(dirname);
return; return;
} }

View File

@ -4747,7 +4747,7 @@ static int uc_add_command(char_u *name, size_t name_len, char_u *rep,
char_u *rep_buf = NULL; char_u *rep_buf = NULL;
garray_T *gap; garray_T *gap;
replace_termcodes(rep, STRLEN(rep), &rep_buf, false, false, false, replace_termcodes(rep, STRLEN(rep), &rep_buf, false, false, true,
CPO_TO_CPO_FLAGS); CPO_TO_CPO_FLAGS);
if (rep_buf == NULL) { if (rep_buf == NULL) {
/* Can't replace termcodes - try using the string as is */ /* Can't replace termcodes - try using the string as is */

View File

@ -106,12 +106,20 @@ typedef struct command_line_state {
char_u *lookfor; // string to match char_u *lookfor; // string to match
int hiscnt; // current history line in use int hiscnt; // current history line in use
int histype; // history type to be used int histype; // history type to be used
pos_T old_cursor; pos_T search_start; // where 'incsearch' starts searching
pos_T save_cursor;
colnr_T old_curswant; colnr_T old_curswant;
colnr_T init_curswant;
colnr_T old_leftcol; colnr_T old_leftcol;
colnr_T init_leftcol;
linenr_T old_topline; linenr_T old_topline;
linenr_T init_topline;
int old_topfill; int old_topfill;
int init_topfill;
linenr_T old_botline; linenr_T old_botline;
linenr_T init_botline;
pos_T match_start;
pos_T match_end;
int did_incsearch; int did_incsearch;
int incsearch_postponed; int incsearch_postponed;
int did_wild_list; // did wild_list() recently int did_wild_list; // did wild_list() recently
@ -191,6 +199,12 @@ static uint8_t *command_line_enter(int firstc, long count, int indent)
s->save_State = State; s->save_State = State;
s->save_p_icm = vim_strsave(p_icm); s->save_p_icm = vim_strsave(p_icm);
s->ignore_drag_release = true; s->ignore_drag_release = true;
s->match_start = curwin->w_cursor;
s->init_curswant = curwin->w_curswant;
s->init_leftcol = curwin->w_leftcol;
s->init_topline = curwin->w_topline;
s->init_topfill = curwin->w_topfill;
s->init_botline = curwin->w_botline;
if (s->firstc == -1) { if (s->firstc == -1) {
s->firstc = NUL; s->firstc = NUL;
@ -204,7 +218,9 @@ static uint8_t *command_line_enter(int firstc, long count, int indent)
ccline.prompt_id = last_prompt_id++; ccline.prompt_id = last_prompt_id++;
ccline.overstrike = false; // always start in insert mode ccline.overstrike = false; // always start in insert mode
s->old_cursor = curwin->w_cursor; // needs to be restored later clearpos(&s->match_end);
s->save_cursor = curwin->w_cursor; // may be restored later
s->search_start = curwin->w_cursor;
s->old_curswant = curwin->w_curswant; s->old_curswant = curwin->w_curswant;
s->old_leftcol = curwin->w_leftcol; s->old_leftcol = curwin->w_leftcol;
s->old_topline = curwin->w_topline; s->old_topline = curwin->w_topline;
@ -307,7 +323,16 @@ static uint8_t *command_line_enter(int firstc, long count, int indent)
ccline.xpc = NULL; ccline.xpc = NULL;
if (s->did_incsearch) { if (s->did_incsearch) {
curwin->w_cursor = s->old_cursor; if (s->gotesc) {
curwin->w_cursor = s->save_cursor;
} else {
if (!equalpos(s->save_cursor, s->search_start)) {
// put the '" mark at the original position
curwin->w_cursor = s->save_cursor;
setpcmark();
}
curwin->w_cursor = s->search_start; // -V519
}
curwin->w_curswant = s->old_curswant; curwin->w_curswant = s->old_curswant;
curwin->w_leftcol = s->old_leftcol; curwin->w_leftcol = s->old_leftcol;
curwin->w_topline = s->old_topline; curwin->w_topline = s->old_topline;
@ -882,6 +907,118 @@ static int command_line_execute(VimState *state, int key)
return command_line_handle_key(s); return command_line_handle_key(s);
} }
static void command_line_next_incsearch(CommandLineState *s, bool next_match)
{
ui_busy_start();
ui_flush();
pos_T t;
int search_flags = SEARCH_KEEP + SEARCH_NOOF + SEARCH_PEEK;
if (next_match) {
t = s->match_end;
search_flags += SEARCH_COL;
} else {
t = s->match_start;
}
emsg_off++;
s->i = searchit(curwin, curbuf, &t,
next_match ? FORWARD : BACKWARD,
ccline.cmdbuff, s->count, search_flags,
RE_SEARCH, 0, NULL);
emsg_off--;
ui_busy_stop();
if (s->i) {
s->search_start = s->match_start;
s->match_end = t;
s->match_start = t;
if (!next_match && s->firstc == '/') {
// move just before the current match, so that
// when nv_search finishes the cursor will be
// put back on the match
s->search_start = t;
(void)decl(&s->search_start);
}
if (lt(t, s->search_start) && next_match) {
// wrap around
s->search_start = t;
if (s->firstc == '?') {
(void)incl(&s->search_start);
} else {
(void)decl(&s->search_start);
}
}
set_search_match(&s->match_end);
curwin->w_cursor = s->match_start;
changed_cline_bef_curs();
update_topline();
validate_cursor();
highlight_match = true;
s->old_curswant = curwin->w_curswant;
s->old_leftcol = curwin->w_leftcol;
s->old_topline = curwin->w_topline;
s->old_topfill = curwin->w_topfill;
s->old_botline = curwin->w_botline;
update_screen(NOT_VALID);
redrawcmdline();
} else {
vim_beep(BO_ERROR);
}
return;
}
static void command_line_next_histidx(CommandLineState *s, bool next_match)
{
s->j = (int)STRLEN(s->lookfor);
for (;; ) {
// one step backwards
if (!next_match) {
if (s->hiscnt == hislen) {
// first time
s->hiscnt = hisidx[s->histype];
} else if (s->hiscnt == 0 && hisidx[s->histype] != hislen - 1) {
s->hiscnt = hislen - 1;
} else if (s->hiscnt != hisidx[s->histype] + 1) {
s->hiscnt--;
} else {
// at top of list
s->hiscnt = s->i;
break;
}
} else { // one step forwards
// on last entry, clear the line
if (s->hiscnt == hisidx[s->histype]) {
s->hiscnt = hislen;
break;
}
// not on a history line, nothing to do
if (s->hiscnt == hislen) {
break;
}
if (s->hiscnt == hislen - 1) {
// wrap around
s->hiscnt = 0;
} else {
s->hiscnt++;
}
}
if (s->hiscnt < 0 || history[s->histype][s->hiscnt].hisstr == NULL) {
s->hiscnt = s->i;
break;
}
if ((s->c != K_UP && s->c != K_DOWN)
|| s->hiscnt == s->i
|| STRNCMP(history[s->histype][s->hiscnt].hisstr,
s->lookfor, (size_t)s->j) == 0) {
break;
}
}
}
static int command_line_handle_key(CommandLineState *s) static int command_line_handle_key(CommandLineState *s)
{ {
// Big switch for a typed command line character. // Big switch for a typed command line character.
@ -954,6 +1091,16 @@ static int command_line_handle_key(CommandLineState *s)
// Truncate at the end, required for multi-byte chars. // Truncate at the end, required for multi-byte chars.
ccline.cmdbuff[ccline.cmdlen] = NUL; ccline.cmdbuff[ccline.cmdlen] = NUL;
if (ccline.cmdlen == 0) {
s->search_start = s->save_cursor;
// save view settings, so that the screen won't be restored at the
// wrong position
s->old_curswant = s->init_curswant;
s->old_leftcol = s->init_leftcol;
s->old_topline = s->init_topline;
s->old_topfill = s->init_topfill;
s->old_botline = s->init_botline;
}
redrawcmd(); redrawcmd();
} else if (ccline.cmdlen == 0 && s->c != Ctrl_W } else if (ccline.cmdlen == 0 && s->c != Ctrl_W
&& ccline.cmdprompt == NULL && s->indent == 0) { && ccline.cmdprompt == NULL && s->indent == 0) {
@ -972,6 +1119,7 @@ static int command_line_handle_key(CommandLineState *s)
} }
msg_putchar(' '); // delete ':' msg_putchar(' '); // delete ':'
} }
s->search_start = s->save_cursor;
redraw_cmdline = true; redraw_cmdline = true;
return 0; // back to cmd mode return 0; // back to cmd mode
} }
@ -1026,6 +1174,9 @@ static int command_line_handle_key(CommandLineState *s)
// Truncate at the end, required for multi-byte chars. // Truncate at the end, required for multi-byte chars.
ccline.cmdbuff[ccline.cmdlen] = NUL; ccline.cmdbuff[ccline.cmdlen] = NUL;
if (ccline.cmdlen == 0) {
s->search_start = s->save_cursor;
}
redrawcmd(); redrawcmd();
return command_line_changed(s); return command_line_changed(s);
@ -1258,24 +1409,27 @@ static int command_line_handle_key(CommandLineState *s)
case Ctrl_L: case Ctrl_L:
if (p_is && !cmd_silent && (s->firstc == '/' || s->firstc == '?')) { if (p_is && !cmd_silent && (s->firstc == '/' || s->firstc == '?')) {
// Add a character from under the cursor for 'incsearch' // Add a character from under the cursor for 'incsearch'
if (s->did_incsearch && !equalpos(curwin->w_cursor, s->old_cursor)) { if (s->did_incsearch) {
s->c = gchar_cursor(); curwin->w_cursor = s->match_end;
// If 'ignorecase' and 'smartcase' are set and the if (!equalpos(curwin->w_cursor, s->search_start)) {
// command line has no uppercase characters, convert s->c = gchar_cursor();
// the character to lowercase // If 'ignorecase' and 'smartcase' are set and the
if (p_ic && p_scs && !pat_has_uppercase(ccline.cmdbuff)) { // command line has no uppercase characters, convert
s->c = mb_tolower(s->c); // the character to lowercase
} if (p_ic && p_scs
&& !pat_has_uppercase(ccline.cmdbuff)) {
if (s->c != NUL) { s->c = mb_tolower(s->c);
if (s->c == s->firstc }
|| vim_strchr((char_u *)(p_magic ? "\\^$.*[" : "\\^$"), s->c) if (s->c != NUL) {
!= NULL) { if (s->c == s->firstc
// put a backslash before special characters || vim_strchr((char_u *)(p_magic ? "\\^$.*[" : "\\^$"), s->c)
stuffcharReadbuff(s->c); != NULL) {
s->c = '\\'; // put a backslash before special characters
stuffcharReadbuff(s->c);
s->c = '\\';
}
break;
} }
break;
} }
} }
return command_line_not_changed(s); return command_line_not_changed(s);
@ -1294,7 +1448,7 @@ static int command_line_handle_key(CommandLineState *s)
0, s->firstc != '@') == FAIL) { 0, s->firstc != '@') == FAIL) {
break; break;
} }
return command_line_changed(s); return command_line_not_changed(s);
} }
// fallthrough // fallthrough
@ -1319,55 +1473,9 @@ static int command_line_handle_key(CommandLineState *s)
s->lookfor[ccline.cmdpos] = NUL; s->lookfor[ccline.cmdpos] = NUL;
} }
s->j = (int)STRLEN(s->lookfor); bool next_match = (s->c == K_DOWN || s->c == K_S_DOWN || s->c == Ctrl_N
for (;; ) { || s->c == K_PAGEDOWN || s->c == K_KPAGEDOWN);
// one step backwards command_line_next_histidx(s, next_match);
if (s->c == K_UP|| s->c == K_S_UP || s->c == Ctrl_P
|| s->c == K_PAGEUP || s->c == K_KPAGEUP) {
if (s->hiscnt == hislen) {
// first time
s->hiscnt = hisidx[s->histype];
} else if (s->hiscnt == 0 && hisidx[s->histype] != hislen - 1) {
s->hiscnt = hislen - 1;
} else if (s->hiscnt != hisidx[s->histype] + 1) {
--s->hiscnt;
} else {
// at top of list
s->hiscnt = s->i;
break;
}
} else { // one step forwards
// on last entry, clear the line
if (s->hiscnt == hisidx[s->histype]) {
s->hiscnt = hislen;
break;
}
// not on a history line, nothing to do
if (s->hiscnt == hislen) {
break;
}
if (s->hiscnt == hislen - 1) {
// wrap around
s->hiscnt = 0;
} else {
++s->hiscnt;
}
}
if (s->hiscnt < 0 || history[s->histype][s->hiscnt].hisstr == NULL) {
s->hiscnt = s->i;
break;
}
if ((s->c != K_UP && s->c != K_DOWN)
|| s->hiscnt == s->i
|| STRNCMP(history[s->histype][s->hiscnt].hisstr,
s->lookfor, (size_t)s->j) == 0) {
break;
}
}
if (s->hiscnt != s->i) { if (s->hiscnt != s->i) {
// jumped to other entry // jumped to other entry
@ -1435,6 +1543,17 @@ static int command_line_handle_key(CommandLineState *s)
beep_flush(); beep_flush();
return command_line_not_changed(s); return command_line_not_changed(s);
case Ctrl_G: // next match
case Ctrl_T: // previous match
if (p_is && !cmd_silent && (s->firstc == '/' || s->firstc == '?')) {
if (char_avail()) {
return 1;
}
command_line_next_incsearch(s, s->c == Ctrl_G);
return command_line_not_changed(s);
}
break;
case Ctrl_V: case Ctrl_V:
case Ctrl_Q: case Ctrl_Q:
s->ignore_drag_release = true; s->ignore_drag_release = true;
@ -1549,7 +1668,7 @@ static int command_line_changed(CommandLineState *s)
return 1; return 1;
} }
s->incsearch_postponed = false; s->incsearch_postponed = false;
curwin->w_cursor = s->old_cursor; // start at old position curwin->w_cursor = s->search_start; // start at old position
// If there is no command line, don't do anything // If there is no command line, don't do anything
if (ccline.cmdlen == 0) { if (ccline.cmdlen == 0) {
@ -1594,16 +1713,11 @@ static int command_line_changed(CommandLineState *s)
if (s->i != 0) { if (s->i != 0) {
pos_T save_pos = curwin->w_cursor; pos_T save_pos = curwin->w_cursor;
// First move cursor to end of match, then to the start. This s->match_start = curwin->w_cursor;
// moves the whole match onto the screen when 'nowrap' is set. set_search_match(&curwin->w_cursor);
curwin->w_cursor.lnum += search_match_lines;
curwin->w_cursor.col = search_match_endcol;
if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count) {
curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
coladvance((colnr_T)MAXCOL);
}
validate_cursor(); validate_cursor();
end_pos = curwin->w_cursor; end_pos = curwin->w_cursor;
s->match_end = end_pos;
curwin->w_cursor = save_pos; curwin->w_cursor = save_pos;
} else { } else {
end_pos = curwin->w_cursor; // shutup gcc 4 end_pos = curwin->w_cursor; // shutup gcc 4
@ -1645,7 +1759,7 @@ static int command_line_changed(CommandLineState *s)
emsg_silent--; // Unblock error reporting emsg_silent--; // Unblock error reporting
// Restore the window "view". // Restore the window "view".
curwin->w_cursor = s->old_cursor; curwin->w_cursor = s->save_cursor;
curwin->w_curswant = s->old_curswant; curwin->w_curswant = s->old_curswant;
curwin->w_leftcol = s->old_leftcol; curwin->w_leftcol = s->old_leftcol;
curwin->w_topline = s->old_topline; curwin->w_topline = s->old_topline;
@ -5793,3 +5907,15 @@ histentry_T *hist_get_array(const uint8_t history_type, int **const new_hisidx,
*new_hisnum = &(hisnum[history_type]); *new_hisnum = &(hisnum[history_type]);
return history[history_type]; return history[history_type];
} }
static void set_search_match(pos_T *t)
{
// First move cursor to end of match, then to the start. This
// moves the whole match onto the screen when 'nowrap' is set.
t->lnum += search_match_lines;
t->col = search_match_endcol;
if (t->lnum > curbuf->b_ml.ml_line_count) {
t->lnum = curbuf->b_ml.ml_line_count;
coladvance((colnr_T)MAXCOL);
}
}

View File

@ -247,6 +247,7 @@ void filemess(buf_T *buf, char_u *name, char_u *s, int attr)
* stdin) * stdin)
* READ_DUMMY read into a dummy buffer (to check if file contents changed) * READ_DUMMY read into a dummy buffer (to check if file contents changed)
* READ_KEEP_UNDO don't clear undo info or read it from a file * READ_KEEP_UNDO don't clear undo info or read it from a file
* READ_FIFO read from fifo/socket instead of a file
* *
* return FAIL for failure, NOTDONE for directory (failure), or OK * return FAIL for failure, NOTDONE for directory (failure), or OK
*/ */
@ -267,6 +268,7 @@ readfile (
int filtering = (flags & READ_FILTER); int filtering = (flags & READ_FILTER);
int read_stdin = (flags & READ_STDIN); int read_stdin = (flags & READ_STDIN);
int read_buffer = (flags & READ_BUFFER); int read_buffer = (flags & READ_BUFFER);
int read_fifo = (flags & READ_FIFO);
int set_options = newfile || read_buffer int set_options = newfile || read_buffer
|| (eap != NULL && eap->read_edit); || (eap != NULL && eap->read_edit);
linenr_T read_buf_lnum = 1; /* next line to read from curbuf */ linenr_T read_buf_lnum = 1; /* next line to read from curbuf */
@ -426,7 +428,7 @@ readfile (
} }
} }
if (!read_buffer && !read_stdin) { if (!read_buffer && !read_stdin && !read_fifo) {
perm = os_getperm((const char *)fname); perm = os_getperm((const char *)fname);
#ifdef UNIX #ifdef UNIX
// On Unix it is possible to read a directory, so we have to // On Unix it is possible to read a directory, so we have to
@ -468,8 +470,8 @@ readfile (
if (check_readonly && !readonlymode) if (check_readonly && !readonlymode)
curbuf->b_p_ro = FALSE; curbuf->b_p_ro = FALSE;
if (newfile && !read_stdin && !read_buffer) { if (newfile && !read_stdin && !read_buffer && !read_fifo) {
/* Remember time of file. */ // Remember time of file.
FileInfo file_info; FileInfo file_info;
if (os_fileinfo((char *)fname, &file_info)) { if (os_fileinfo((char *)fname, &file_info)) {
buf_store_file_info(curbuf, &file_info); buf_store_file_info(curbuf, &file_info);
@ -895,6 +897,7 @@ retry:
* and we can't do it internally or with iconv(). * and we can't do it internally or with iconv().
*/ */
if (fio_flags == 0 && !read_stdin && !read_buffer && *p_ccv != NUL if (fio_flags == 0 && !read_stdin && !read_buffer && *p_ccv != NUL
&& !read_fifo
# ifdef USE_ICONV # ifdef USE_ICONV
&& iconv_fd == (iconv_t)-1 && iconv_fd == (iconv_t)-1
# endif # endif
@ -935,7 +938,7 @@ retry:
/* Set "can_retry" when it's possible to rewind the file and try with /* Set "can_retry" when it's possible to rewind the file and try with
* another "fenc" value. It's FALSE when no other "fenc" to try, reading * another "fenc" value. It's FALSE when no other "fenc" to try, reading
* stdin or fixed at a specific encoding. */ * stdin or fixed at a specific encoding. */
can_retry = (*fenc != NUL && !read_stdin && !keep_dest_enc); can_retry = (*fenc != NUL && !read_stdin && !keep_dest_enc && !read_fifo);
if (!skip_read) { if (!skip_read) {
linerest = 0; linerest = 0;
@ -947,6 +950,7 @@ retry:
&& curbuf->b_ffname != NULL && curbuf->b_ffname != NULL
&& curbuf->b_p_udf && curbuf->b_p_udf
&& !filtering && !filtering
&& !read_fifo
&& !read_stdin && !read_stdin
&& !read_buffer); && !read_buffer);
if (read_undo_file) if (read_undo_file)
@ -1919,7 +1923,7 @@ failed:
u_read_undo(NULL, hash, fname); u_read_undo(NULL, hash, fname);
} }
if (!read_stdin && !read_buffer) { if (!read_stdin && !read_fifo && (!read_buffer || sfname != NULL)) {
int m = msg_scroll; int m = msg_scroll;
int n = msg_scrolled; int n = msg_scrolled;
@ -1937,7 +1941,7 @@ failed:
if (filtering) { if (filtering) {
apply_autocmds_exarg(EVENT_FILTERREADPOST, NULL, sfname, apply_autocmds_exarg(EVENT_FILTERREADPOST, NULL, sfname,
false, curbuf, eap); false, curbuf, eap);
} else if (newfile) { } else if (newfile || (read_buffer && sfname != NULL)) {
apply_autocmds_exarg(EVENT_BUFREADPOST, NULL, sfname, apply_autocmds_exarg(EVENT_BUFREADPOST, NULL, sfname,
false, curbuf, eap); false, curbuf, eap);
if (!au_did_filetype && *curbuf->b_p_ft != NUL) { if (!au_did_filetype && *curbuf->b_p_ft != NUL) {
@ -1970,7 +1974,7 @@ failed:
/// Do not accept "/dev/fd/[012]", opening these may hang Vim. /// Do not accept "/dev/fd/[012]", opening these may hang Vim.
/// ///
/// @param fname file name to check /// @param fname file name to check
static bool is_dev_fd_file(char_u *fname) bool is_dev_fd_file(char_u *fname)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
{ {
return STRNCMP(fname, "/dev/fd/", 8) == 0 return STRNCMP(fname, "/dev/fd/", 8) == 0

View File

@ -4,13 +4,14 @@
#include "nvim/buffer_defs.h" #include "nvim/buffer_defs.h"
#include "nvim/os/os.h" #include "nvim/os/os.h"
/* Values for readfile() flags */ // Values for readfile() flags
#define READ_NEW 0x01 /* read a file into a new buffer */ #define READ_NEW 0x01 // read a file into a new buffer
#define READ_FILTER 0x02 /* read filter output */ #define READ_FILTER 0x02 // read filter output
#define READ_STDIN 0x04 /* read from stdin */ #define READ_STDIN 0x04 // read from stdin
#define READ_BUFFER 0x08 /* read from curbuf (converting stdin) */ #define READ_BUFFER 0x08 // read from curbuf (converting stdin)
#define READ_DUMMY 0x10 /* reading into a dummy buffer */ #define READ_DUMMY 0x10 // reading into a dummy buffer
#define READ_KEEP_UNDO 0x20 /* keep undo info*/ #define READ_KEEP_UNDO 0x20 // keep undo info
#define READ_FIFO 0x40 // read from fifo or socket
#define READ_STRING(x, y) (char_u *)read_string((x), (size_t)(y)) #define READ_STRING(x, y) (char_u *)read_string((x), (size_t)(y))

View File

@ -1806,7 +1806,7 @@ static int vgetorpeek(int advance)
* <M-a> and then changing 'encoding'. Beware * <M-a> and then changing 'encoding'. Beware
* that 0x80 is escaped. */ * that 0x80 is escaped. */
char_u *p1 = mp->m_keys; char_u *p1 = mp->m_keys;
char_u *p2 = mb_unescape(&p1); char_u *p2 = (char_u *)mb_unescape((const char **)&p1);
if (has_mbyte && p2 != NULL && MB_BYTE2LEN(c1) > MB_PTR2LEN(p2)) if (has_mbyte && p2 != NULL && MB_BYTE2LEN(c1) > MB_PTR2LEN(p2))
mlen = 0; mlen = 0;
@ -2537,7 +2537,6 @@ do_map (
bool unique = false; bool unique = false;
bool nowait = false; bool nowait = false;
bool silent = false; bool silent = false;
bool special = false;
bool expr = false; bool expr = false;
int noremap; int noremap;
char_u *orig_rhs; char_u *orig_rhs;
@ -2583,12 +2582,9 @@ do_map (
continue; continue;
} }
/* // Ignore obsolete "<special>" modifier.
* Check for "<special>": accept special keys in <>
*/
if (STRNCMP(keys, "<special>", 9) == 0) { if (STRNCMP(keys, "<special>", 9) == 0) {
keys = skipwhite(keys + 9); keys = skipwhite(keys + 9);
special = true;
continue; continue;
} }
@ -2657,7 +2653,7 @@ do_map (
// needs to be freed later (*keys_buf and *arg_buf). // needs to be freed later (*keys_buf and *arg_buf).
// replace_termcodes() also removes CTRL-Vs and sometimes backslashes. // replace_termcodes() also removes CTRL-Vs and sometimes backslashes.
if (haskey) { if (haskey) {
keys = replace_termcodes(keys, STRLEN(keys), &keys_buf, true, true, special, keys = replace_termcodes(keys, STRLEN(keys), &keys_buf, true, true, true,
CPO_TO_CPO_FLAGS); CPO_TO_CPO_FLAGS);
} }
orig_rhs = rhs; orig_rhs = rhs;
@ -2665,7 +2661,7 @@ do_map (
if (STRICMP(rhs, "<nop>") == 0) { // "<Nop>" means nothing if (STRICMP(rhs, "<nop>") == 0) { // "<Nop>" means nothing
rhs = (char_u *)""; rhs = (char_u *)"";
} else { } else {
rhs = replace_termcodes(rhs, STRLEN(rhs), &arg_buf, false, true, special, rhs = replace_termcodes(rhs, STRLEN(rhs), &arg_buf, false, true, true,
CPO_TO_CPO_FLAGS); CPO_TO_CPO_FLAGS);
} }
} }
@ -3245,7 +3241,7 @@ bool map_to_exists(const char *const str, const char *const modechars,
char_u *buf; char_u *buf;
char_u *const rhs = replace_termcodes((const char_u *)str, strlen(str), &buf, char_u *const rhs = replace_termcodes((const char_u *)str, strlen(str), &buf,
false, true, false, false, true, true,
CPO_TO_CPO_FLAGS); CPO_TO_CPO_FLAGS);
#define MAPMODE(mode, modechars, chr, modeflags) \ #define MAPMODE(mode, modechars, chr, modeflags) \
@ -3999,12 +3995,10 @@ int put_escstr(FILE *fd, char_u *strstart, int what)
return OK; return OK;
} }
for (; *str != NUL; ++str) { for (; *str != NUL; str++) {
char_u *p; // Check for a multi-byte character, which may contain escaped
// K_SPECIAL and CSI bytes.
/* Check for a multi-byte character, which may contain escaped const char *p = mb_unescape((const char **)&str);
* K_SPECIAL and CSI bytes */
p = mb_unescape(&str);
if (p != NULL) { if (p != NULL) {
while (*p != NUL) while (*p != NUL)
if (fputc(*p++, fd) < 0) if (fputc(*p++, fd) < 0)
@ -4160,8 +4154,7 @@ void add_map(char_u *map, int mode)
} }
// Translate an internal mapping/abbreviation representation into the // Translate an internal mapping/abbreviation representation into the
// corresponding external one recognized by :map/:abbrev commands; // corresponding external one recognized by :map/:abbrev commands.
// respects the current B/k/< settings of 'cpoption'.
// //
// This function is called when expanding mappings/abbreviations on the // This function is called when expanding mappings/abbreviations on the
// command-line, and for building the "Ambiguous mapping..." error message. // command-line, and for building the "Ambiguous mapping..." error message.
@ -4181,7 +4174,6 @@ static char_u * translate_mapping (
ga_init(&ga, 1, 40); ga_init(&ga, 1, 40);
bool cpo_bslash = !(cpo_flags&FLAG_CPO_BSLASH); bool cpo_bslash = !(cpo_flags&FLAG_CPO_BSLASH);
bool cpo_special = !(cpo_flags&FLAG_CPO_SPECI);
for (; *str; ++str) { for (; *str; ++str) {
int c = *str; int c = *str;
@ -4194,7 +4186,7 @@ static char_u * translate_mapping (
} }
if (c == K_SPECIAL && str[1] != NUL && str[2] != NUL) { if (c == K_SPECIAL && str[1] != NUL && str[2] != NUL) {
if (expmap && cpo_special) { if (expmap) {
ga_clear(&ga); ga_clear(&ga);
return NULL; return NULL;
} }
@ -4205,8 +4197,8 @@ static char_u * translate_mapping (
} }
str += 2; str += 2;
} }
if (IS_SPECIAL(c) || modifiers) { /* special key */ if (IS_SPECIAL(c) || modifiers) { // special key
if (expmap && cpo_special) { if (expmap) {
ga_clear(&ga); ga_clear(&ga);
return NULL; return NULL;
} }
@ -4216,7 +4208,7 @@ static char_u * translate_mapping (
} }
if (c == ' ' || c == '\t' || c == Ctrl_J || c == Ctrl_V if (c == ' ' || c == '\t' || c == Ctrl_J || c == Ctrl_V
|| (c == '<' && !cpo_special) || (c == '\\' && !cpo_bslash)) { || (c == '\\' && !cpo_bslash)) {
ga_append(&ga, cpo_bslash ? Ctrl_V : '\\'); ga_append(&ga, cpo_bslash ? Ctrl_V : '\\');
} }

View File

@ -756,9 +756,9 @@ int get_mouse_button(int code, bool *is_click, bool *is_drag)
/// Replace any terminal code strings with the equivalent internal /// Replace any terminal code strings with the equivalent internal
/// representation /// representation
/// ///
/// This is used for the "from" and "to" part of a mapping, and the "to" part of /// Used for the "from" and "to" part of a mapping, and the "to" part of
/// a menu command. Any strings like "<C-UP>" are also replaced, unless /// a menu command. Any strings like "<C-UP>" are also replaced, unless
/// 'cpoptions' contains '<'. K_SPECIAL by itself is replaced by K_SPECIAL /// `special` is false. K_SPECIAL by itself is replaced by K_SPECIAL
/// KS_SPECIAL KE_FILLER. /// KS_SPECIAL KE_FILLER.
/// ///
/// @param[in] from What characters to replace. /// @param[in] from What characters to replace.
@ -771,7 +771,7 @@ int get_mouse_button(int code, bool *is_click, bool *is_drag)
/// When cpo_flags contains #FLAG_CPO_BSLASH, a backslash /// When cpo_flags contains #FLAG_CPO_BSLASH, a backslash
/// can be used in place of <C-v>. All other <C-v> /// can be used in place of <C-v>. All other <C-v>
/// characters are removed. /// characters are removed.
/// @param[in] special If true, always accept <key> notation. /// @param[in] special Replace keycodes, e.g. <CR> becomes a "\n" char.
/// @param[in] cpo_flags Relevant flags derived from p_cpo, see /// @param[in] cpo_flags Relevant flags derived from p_cpo, see
/// #CPO_TO_CPO_FLAGS. /// #CPO_TO_CPO_FLAGS.
/// ///
@ -790,11 +790,9 @@ char_u *replace_termcodes(const char_u *from, const size_t from_len,
const char_u *src; const char_u *src;
const char_u *const end = from + from_len - 1; const char_u *const end = from + from_len - 1;
int do_backslash; // backslash is a special character int do_backslash; // backslash is a special character
int do_special; // recognize <> key codes
char_u *result; // buffer for resulting string char_u *result; // buffer for resulting string
do_backslash = !(cpo_flags&FLAG_CPO_BSLASH); do_backslash = !(cpo_flags&FLAG_CPO_BSLASH);
do_special = !(cpo_flags&FLAG_CPO_SPECI) || special;
// Allocate space for the translation. Worst case a single character is // Allocate space for the translation. Worst case a single character is
// replaced by 6 bytes (shifted special key), plus a NUL at the end. // replaced by 6 bytes (shifted special key), plus a NUL at the end.
@ -817,10 +815,9 @@ char_u *replace_termcodes(const char_u *from, const size_t from_len,
// Copy each byte from *from to result[dlen] // Copy each byte from *from to result[dlen]
while (src <= end) { while (src <= end) {
// If 'cpoptions' does not contain '<', check for special key codes, // Check for special <> keycodes, like "<C-S-LeftMouse>"
// like "<C-S-LeftMouse>" if (special && (do_lt || ((end - src) >= 3
if (do_special && (do_lt || ((end - src) >= 3 && STRNCMP(src, "<lt>", 4) != 0))) {
&& STRNCMP(src, "<lt>", 4) != 0))) {
// Replace <SID> by K_SNR <script-nr> _. // Replace <SID> by K_SNR <script-nr> _.
// (room: 5 * 6 = 30 bytes; needed: 3 + <nr> + 1 <= 14) // (room: 5 * 6 = 30 bytes; needed: 3 + <nr> + 1 <= 14)
if (end - src >= 4 && STRNICMP(src, "<SID>", 5) == 0) { if (end - src >= 4 && STRNICMP(src, "<SID>", 5) == 0) {
@ -846,7 +843,7 @@ char_u *replace_termcodes(const char_u *from, const size_t from_len,
} }
} }
if (do_special) { if (special) {
char_u *p, *s, len; char_u *p, *s, len;
// Replace <Leader> by the value of "mapleader". // Replace <Leader> by the value of "mapleader".

View File

@ -464,13 +464,9 @@ enum key_extra {
#define MAX_KEY_CODE_LEN 6 #define MAX_KEY_CODE_LEN 6
#define FLAG_CPO_BSLASH 0x01 #define FLAG_CPO_BSLASH 0x01
#define FLAG_CPO_SPECI 0x02 #define CPO_TO_CPO_FLAGS ((vim_strchr(p_cpo, CPO_BSLASH) == NULL) \
#define CPO_TO_CPO_FLAGS (((vim_strchr(p_cpo, CPO_BSLASH) == NULL) \ ? 0 \
? 0 \ : FLAG_CPO_BSLASH)
: FLAG_CPO_BSLASH)| \
(vim_strchr(p_cpo, CPO_SPECI) == NULL \
? 0 \
: FLAG_CPO_SPECI))
#ifdef INCLUDE_GENERATED_DECLARATIONS #ifdef INCLUDE_GENERATED_DECLARATIONS
# include "keymap.h.generated.h" # include "keymap.h.generated.h"

View File

@ -14,6 +14,7 @@
#include "nvim/api/vim.h" #include "nvim/api/vim.h"
#include "nvim/vim.h" #include "nvim/vim.h"
#include "nvim/ex_getln.h" #include "nvim/ex_getln.h"
#include "nvim/ex_cmds2.h"
#include "nvim/message.h" #include "nvim/message.h"
#include "nvim/memline.h" #include "nvim/memline.h"
#include "nvim/buffer_defs.h" #include "nvim/buffer_defs.h"
@ -284,7 +285,9 @@ static int nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
/// ///
/// Crashes NeoVim if initialization fails. Should be called once per lua /// Crashes NeoVim if initialization fails. Should be called once per lua
/// interpreter instance. /// interpreter instance.
static lua_State *init_lua(void) ///
/// @return New lua interpreter instance.
static lua_State *nlua_init(void)
FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT
{ {
lua_State *lstate = luaL_newstate(); lua_State *lstate = luaL_newstate();
@ -297,7 +300,43 @@ static lua_State *init_lua(void)
return lstate; return lstate;
} }
static lua_State *global_lstate = NULL; /// Enter lua interpreter
///
/// Calls nlua_init() if needed. Is responsible for pre-lua call initalization
/// like updating `package.[c]path` with directories derived from &runtimepath.
///
/// @return Interprter instance to use. Will either be initialized now or taken
/// from previous initalization.
static lua_State *nlua_enter(void)
FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT
{
static lua_State *global_lstate = NULL;
if (global_lstate == NULL) {
global_lstate = nlua_init();
}
lua_State *const lstate = global_lstate;
// Last used p_rtp value. Must not be dereferenced because value pointed to
// may already be freed. Used to check whether &runtimepath option value
// changed.
static const void *last_p_rtp = NULL;
if (last_p_rtp != (const void *)p_rtp) {
// stack: (empty)
lua_getglobal(lstate, "vim");
// stack: vim
lua_getfield(lstate, -1, "_update_package_paths");
// stack: vim, vim._update_package_paths
if (lua_pcall(lstate, 0, 0, 0)) {
// stack: vim, error
nlua_error(lstate, _("E5117: Error while updating package paths: %.*s"));
// stack: vim
}
// stack: vim
lua_pop(lstate, 1);
// stack: (empty)
last_p_rtp = (const void *)p_rtp;
}
return lstate;
}
/// Execute lua string /// Execute lua string
/// ///
@ -308,11 +347,7 @@ static lua_State *global_lstate = NULL;
void executor_exec_lua(const String str, typval_T *const ret_tv) void executor_exec_lua(const String str, typval_T *const ret_tv)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_ALL
{ {
if (global_lstate == NULL) { NLUA_CALL_C_FUNCTION_2(nlua_enter(), nlua_exec_lua_string, 0,
global_lstate = init_lua();
}
NLUA_CALL_C_FUNCTION_2(global_lstate, nlua_exec_lua_string, 0,
(void *)&str, ret_tv); (void *)&str, ret_tv);
} }
@ -551,11 +586,7 @@ void executor_eval_lua(const String str, typval_T *const arg,
typval_T *const ret_tv) typval_T *const ret_tv)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_ALL
{ {
if (global_lstate == NULL) { NLUA_CALL_C_FUNCTION_3(nlua_enter(), nlua_eval_lua_string, 0,
global_lstate = init_lua();
}
NLUA_CALL_C_FUNCTION_3(global_lstate, nlua_eval_lua_string, 0,
(void *)&str, arg, ret_tv); (void *)&str, arg, ret_tv);
} }
@ -570,12 +601,8 @@ void executor_eval_lua(const String str, typval_T *const arg,
/// @return Return value of the execution. /// @return Return value of the execution.
Object executor_exec_lua_api(const String str, const Array args, Error *err) Object executor_exec_lua_api(const String str, const Array args, Error *err)
{ {
if (global_lstate == NULL) {
global_lstate = init_lua();
}
Object retval = NIL; Object retval = NIL;
NLUA_CALL_C_FUNCTION_4(global_lstate, nlua_exec_lua_string_api, 0, NLUA_CALL_C_FUNCTION_4(nlua_enter(), nlua_exec_lua_string_api, 0,
(void *)&str, (void *)&args, &retval, err); (void *)&str, (void *)&args, &retval, err);
return retval; return retval;
} }
@ -609,9 +636,6 @@ void ex_lua(exarg_T *const eap)
void ex_luado(exarg_T *const eap) void ex_luado(exarg_T *const eap)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_ALL
{ {
if (global_lstate == NULL) {
global_lstate = init_lua();
}
if (u_save(eap->line1 - 1, eap->line2 + 1) == FAIL) { if (u_save(eap->line1 - 1, eap->line2 + 1) == FAIL) {
EMSG(_("cannot save undo information")); EMSG(_("cannot save undo information"));
return; return;
@ -621,7 +645,7 @@ void ex_luado(exarg_T *const eap)
.data = (char *)eap->arg, .data = (char *)eap->arg,
}; };
const linenr_T range[] = { eap->line1, eap->line2 }; const linenr_T range[] = { eap->line1, eap->line2 };
NLUA_CALL_C_FUNCTION_2(global_lstate, nlua_exec_luado_string, 0, NLUA_CALL_C_FUNCTION_2(nlua_enter(), nlua_exec_luado_string, 0,
(void *)&cmd, (void *)range); (void *)&cmd, (void *)range);
} }
@ -633,9 +657,6 @@ void ex_luado(exarg_T *const eap)
void ex_luafile(exarg_T *const eap) void ex_luafile(exarg_T *const eap)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_ALL
{ {
if (global_lstate == NULL) { NLUA_CALL_C_FUNCTION_1(nlua_enter(), nlua_exec_lua_file, 0,
global_lstate = init_lua();
}
NLUA_CALL_C_FUNCTION_1(global_lstate, nlua_exec_lua_file, 0,
(void *)eap->arg); (void *)eap->arg);
} }

View File

@ -1,2 +1,64 @@
-- TODO(ZyX-I): Create compatibility layer. -- TODO(ZyX-I): Create compatibility layer.
return {} --{{{1 package.path updater function
-- Last inserted paths. Used to clear out items from package.[c]path when they
-- are no longer in &runtimepath.
local last_nvim_paths = {}
local function _update_package_paths()
local cur_nvim_paths = {}
local rtps = vim.api.nvim_list_runtime_paths()
local sep = package.config:sub(1, 1)
for _, key in ipairs({'path', 'cpath'}) do
local orig_str = package[key] .. ';'
local pathtrails_ordered = {}
local orig = {}
-- Note: ignores trailing item without trailing `;`. Not using something
-- simpler in order to preserve empty items (stand for default path).
for s in orig_str:gmatch('[^;]*;') do
s = s:sub(1, -2) -- Strip trailing semicolon
orig[#orig + 1] = s
end
if key == 'path' then
-- /?.lua and /?/init.lua
pathtrails_ordered = {sep .. '?.lua', sep .. '?' .. sep .. 'init.lua'}
else
local pathtrails = {}
for _, s in ipairs(orig) do
-- Find out path patterns. pathtrail should contain something like
-- /?.so, \?.dll. This allows not to bother determining what correct
-- suffixes are.
local pathtrail = s:match('[/\\][^/\\]*%?.*$')
if pathtrail and not pathtrails[pathtrail] then
pathtrails[pathtrail] = true
pathtrails_ordered[#pathtrails_ordered + 1] = pathtrail
end
end
end
local new = {}
for _, rtp in ipairs(rtps) do
if not rtp:match(';') then
for _, pathtrail in pairs(pathtrails_ordered) do
local new_path = rtp .. sep .. 'lua' .. pathtrail
-- Always keep paths from &runtimepath at the start:
-- append them here disregarding orig possibly containing one of them.
new[#new + 1] = new_path
cur_nvim_paths[new_path] = true
end
end
end
for _, orig_path in ipairs(orig) do
-- Handle removing obsolete paths originating from &runtimepath: such
-- paths either belong to cur_nvim_paths and were already added above or
-- to last_nvim_paths and should not be added at all if corresponding
-- entry was removed from &runtimepath list.
if not (cur_nvim_paths[orig_path] or last_nvim_paths[orig_path]) then
new[#new + 1] = orig_path
end
end
package[key] = table.concat(new, ';')
end
last_nvim_paths = cur_nvim_paths
end
--{{{1 Module definition
return {
_update_package_paths = _update_package_paths,
}

View File

@ -57,6 +57,7 @@
#include "nvim/os/input.h" #include "nvim/os/input.h"
#include "nvim/os/os.h" #include "nvim/os/os.h"
#include "nvim/os/time.h" #include "nvim/os/time.h"
#include "nvim/os/fileio.h"
#include "nvim/event/loop.h" #include "nvim/event/loop.h"
#include "nvim/os/signal.h" #include "nvim/os/signal.h"
#include "nvim/event/process.h" #include "nvim/event/process.h"
@ -766,16 +767,26 @@ static void command_line_scan(mparm_T *parmp)
version(); version();
mch_exit(0); mch_exit(0);
} else if (STRICMP(argv[0] + argv_idx, "api-info") == 0) { } else if (STRICMP(argv[0] + argv_idx, "api-info") == 0) {
msgpack_sbuffer* b = msgpack_sbuffer_new(); FileDescriptor fp;
msgpack_packer* p = msgpack_packer_new(b, msgpack_sbuffer_write); const int fof_ret = file_open_fd(&fp, OS_STDOUT_FILENO, true);
msgpack_packer *p = msgpack_packer_new(&fp, msgpack_file_write);
if (fof_ret != 0) {
emsgf(_("E5421: Failed to open stdin: %s"), os_strerror(fof_ret));
}
if (p == NULL) {
emsgf(_(e_outofmem));
}
Object md = DICTIONARY_OBJ(api_metadata()); Object md = DICTIONARY_OBJ(api_metadata());
msgpack_rpc_from_object(md, p); msgpack_rpc_from_object(md, p);
for (size_t i = 0; i < b->size; i++) {
putchar(b->data[i]);
}
msgpack_packer_free(p); msgpack_packer_free(p);
const int ff_ret = file_flush(&fp);
if (ff_ret < 0) {
msgpack_file_write_error(ff_ret);
}
mch_exit(0); mch_exit(0);
} else if (STRICMP(argv[0] + argv_idx, "headless") == 0) { } else if (STRICMP(argv[0] + argv_idx, "headless") == 0) {
parmp->headless = true; parmp->headless = true;

View File

@ -1739,52 +1739,55 @@ int mb_charlen_len(char_u *str, int len)
return count; return count;
} }
/* /// Try to unescape a multibyte character
* Try to un-escape a multi-byte character. ///
* Used for the "to" and "from" part of a mapping. /// Used for the rhs and lhs of the mappings.
* Return the un-escaped string if it is a multi-byte character, and advance ///
* "pp" to just after the bytes that formed it. /// @param[in,out] pp String to unescape. Is advanced to just after the bytes
* Return NULL if no multi-byte char was found. /// that form a multibyte character.
*/ ///
char_u * mb_unescape(char_u **pp) /// @return Unescaped string if it is a multibyte character, NULL if no
/// multibyte character was found. Returns a static buffer, always one
/// and the same.
const char *mb_unescape(const char **const pp)
FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL
{ {
static char_u buf[6]; static char buf[6];
int n; size_t buf_idx = 0;
int m = 0; uint8_t *str = (uint8_t *)(*pp);
char_u *str = *pp;
/* Must translate K_SPECIAL KS_SPECIAL KE_FILLER to K_SPECIAL and CSI // Must translate K_SPECIAL KS_SPECIAL KE_FILLER to K_SPECIAL and CSI
* KS_EXTRA KE_CSI to CSI. // KS_EXTRA KE_CSI to CSI.
* Maximum length of a utf-8 character is 4 bytes. */ // Maximum length of a utf-8 character is 4 bytes.
for (n = 0; str[n] != NUL && m < 4; ++n) { for (size_t str_idx = 0; str[str_idx] != NUL && buf_idx < 4; str_idx++) {
if (str[n] == K_SPECIAL if (str[str_idx] == K_SPECIAL
&& str[n + 1] == KS_SPECIAL && str[str_idx + 1] == KS_SPECIAL
&& str[n + 2] == KE_FILLER) { && str[str_idx + 2] == KE_FILLER) {
buf[m++] = K_SPECIAL; buf[buf_idx++] = (char)K_SPECIAL;
n += 2; str_idx += 2;
} else if ((str[n] == K_SPECIAL } else if ((str[str_idx] == K_SPECIAL)
) && str[str_idx + 1] == KS_EXTRA
&& str[n + 1] == KS_EXTRA && str[str_idx + 2] == KE_CSI) {
&& str[n + 2] == (int)KE_CSI) { buf[buf_idx++] = (char)CSI;
buf[m++] = CSI; str_idx += 2;
n += 2; } else if (str[str_idx] == K_SPECIAL) {
} else if (str[n] == K_SPECIAL break; // A special key can't be a multibyte char.
) } else {
break; /* a special key can't be a multibyte char */ buf[buf_idx++] = (char)str[str_idx];
else }
buf[m++] = str[n]; buf[buf_idx] = NUL;
buf[m] = NUL;
/* Return a multi-byte character if it's found. An illegal sequence // Return a multi-byte character if it's found. An illegal sequence
* will result in a 1 here. */ // will result in a 1 here.
if ((*mb_ptr2len)(buf) > 1) { if (utf_ptr2len((const char_u *)buf) > 1) {
*pp = str + n + 1; *pp = (const char *)str + str_idx + 1;
return buf; return buf;
} }
/* Bail out quickly for ASCII. */ // Bail out quickly for ASCII.
if (buf[0] < 128) if ((uint8_t)buf[0] < 128) {
break; break;
}
} }
return NULL; return NULL;
} }

View File

@ -60,7 +60,6 @@ ex_menu (
char_u *map_to; char_u *map_to;
int noremap; int noremap;
bool silent = false; bool silent = false;
bool special = false;
int unmenu; int unmenu;
char_u *map_buf; char_u *map_buf;
char_u *arg; char_u *arg;
@ -86,7 +85,7 @@ ex_menu (
continue; continue;
} }
if (STRNCMP(arg, "<special>", 9) == 0) { if (STRNCMP(arg, "<special>", 9) == 0) {
special = true; // Ignore obsolete "<special>" modifier.
arg = skipwhite(arg + 9); arg = skipwhite(arg + 9);
continue; continue;
} }
@ -222,7 +221,7 @@ ex_menu (
map_buf = NULL; // Menu tips are plain text. map_buf = NULL; // Menu tips are plain text.
} else { } else {
map_to = replace_termcodes(map_to, STRLEN(map_to), &map_buf, false, true, map_to = replace_termcodes(map_to, STRLEN(map_to), &map_buf, false, true,
special, CPO_TO_CPO_FLAGS); true, CPO_TO_CPO_FLAGS);
} }
menuarg.modes = modes; menuarg.modes = modes;
menuarg.noremap[0] = noremap; menuarg.noremap[0] = noremap;

View File

@ -1196,7 +1196,7 @@ int msg_outtrans_len_attr(char_u *msgstr, int len, int attr)
len -= mb_l - 1; len -= mb_l - 1;
str += mb_l; str += mb_l;
} else { } else {
s = transchar_byte(*str); s = transchar_byte((uint8_t)(*str));
if (s[1] != NUL) { if (s[1] != NUL) {
// Unprintable char: print the printable chars so far and the // Unprintable char: print the printable chars so far and the
// translation of the unprintable char. // translation of the unprintable char.
@ -1269,7 +1269,7 @@ msg_outtrans_special (
string = "<Space>"; string = "<Space>";
str++; str++;
} else { } else {
string = (const char *)str2special((char_u **)&str, from); string = str2special((const char **)&str, from, false);
} }
const int len = vim_strsize((char_u *)string); const int len = vim_strsize((char_u *)string);
// Highlight special keys // Highlight special keys
@ -1281,108 +1281,125 @@ msg_outtrans_special (
return retval; return retval;
} }
/* /// Convert string, replacing key codes with printables
* Return the lhs or rhs of a mapping, with the key codes turned into printable ///
* strings, in an allocated string. /// Used for lhs or rhs of mappings.
*/ ///
char_u * /// @param[in] str String to convert.
str2special_save ( /// @param[in] replace_spaces Convert spaces into `<Space>`, normally used fo
char_u *str, /// lhs, but not rhs.
int is_lhs /* TRUE for lhs, FALSE for rhs */ /// @param[in] replace_lt Convert `<` into `<lt>`.
) ///
/// @return [allocated] Converted string.
char *str2special_save(const char *const str, const bool replace_spaces,
const bool replace_lt)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_MALLOC
FUNC_ATTR_NONNULL_RET
{ {
garray_T ga; garray_T ga;
char_u *p = str;
ga_init(&ga, 1, 40); ga_init(&ga, 1, 40);
while (*p != NUL)
ga_concat(&ga, str2special(&p, is_lhs)); const char *p = str;
while (*p != NUL) {
ga_concat(&ga, (const char_u *)str2special(&p, replace_spaces, replace_lt));
}
ga_append(&ga, NUL); ga_append(&ga, NUL);
return (char_u *)ga.ga_data; return (char *)ga.ga_data;
} }
/* /// Convert character, replacing key one key code with printable representation
* Return the printable string for the key codes at "*sp". ///
* Used for translating the lhs or rhs of a mapping to printable chars. /// @param[in,out] sp String to convert. Is advanced to the next key code.
* Advances "sp" to the next code. /// @param[in] replace_spaces Convert spaces into <Space>, normally used for
*/ /// lhs, but not rhs.
char_u * /// @param[in] replace_lt Convert `<` into `<lt>`.
str2special ( ///
char_u **sp, /// @return Converted key code, in a static buffer. Buffer is always one and the
int from /* TRUE for lhs of mapping */ /// same, so save converted string somewhere before running str2special
) /// for the second time.
const char *str2special(const char **const sp, const bool replace_spaces,
const bool replace_lt)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_RET
{ {
int c; static char buf[7];
static char_u buf[7];
char_u *str = *sp;
int modifiers = 0;
int special = FALSE;
if (has_mbyte) { // Try to un-escape a multi-byte character. Return the un-escaped
char_u *p; // string if it is a multi-byte character.
const char *const p = mb_unescape(sp);
/* Try to un-escape a multi-byte character. Return the un-escaped if (p != NULL) {
* string if it is a multi-byte character. */ return p;
p = mb_unescape(sp);
if (p != NULL)
return p;
} }
c = *str; const char *str = *sp;
int c = (uint8_t)(*str);
int modifiers = 0;
bool special = false;
if (c == K_SPECIAL && str[1] != NUL && str[2] != NUL) { if (c == K_SPECIAL && str[1] != NUL && str[2] != NUL) {
if (str[1] == KS_MODIFIER) { if ((uint8_t)str[1] == KS_MODIFIER) {
modifiers = str[2]; modifiers = (uint8_t)str[2];
str += 3; str += 3;
c = *str; c = (uint8_t)(*str);
} }
if (c == K_SPECIAL && str[1] != NUL && str[2] != NUL) { if (c == K_SPECIAL && str[1] != NUL && str[2] != NUL) {
c = TO_SPECIAL(str[1], str[2]); c = TO_SPECIAL((uint8_t)str[1], (uint8_t)str[2]);
str += 2; str += 2;
if (c == KS_ZERO) /* display <Nul> as ^@ or <Nul> */ if (c == KS_ZERO) { // display <Nul> as ^@ or <Nul>
c = NUL; c = NUL;
}
}
if (IS_SPECIAL(c) || modifiers) { // Special key.
special = true;
} }
if (IS_SPECIAL(c) || modifiers) /* special key */
special = TRUE;
} }
if (has_mbyte && !IS_SPECIAL(c)) { if (!IS_SPECIAL(c)) {
int len = (*mb_ptr2len)(str); const int len = utf_ptr2len((const char_u *)str);
/* For multi-byte characters check for an illegal byte. */ // Check for an illegal byte.
if (has_mbyte && MB_BYTE2LEN(*str) > len) { if (MB_BYTE2LEN((uint8_t)(*str)) > len) {
transchar_nonprint(buf, c); transchar_nonprint((char_u *)buf, c);
*sp = str + 1; *sp = str + 1;
return buf; return buf;
} }
/* Since 'special' is TRUE the multi-byte character 'c' will be // Since 'special' is TRUE the multi-byte character 'c' will be
* processed by get_special_key_name() */ // processed by get_special_key_name().
c = (*mb_ptr2char)(str); c = utf_ptr2char((const char_u *)str);
*sp = str + len; *sp = str + len;
} else } else {
*sp = str + 1; *sp = str + 1;
}
/* Make unprintable characters in <> form, also <M-Space> and <Tab>. // Make unprintable characters in <> form, also <M-Space> and <Tab>.
* Use <Space> only for lhs of a mapping. */ if (special
if (special || char2cells(c) > 1 || (from && c == ' ')) || char2cells(c) > 1
return get_special_key_name(c, modifiers); || (replace_spaces && c == ' ')
|| (replace_lt && c == '<')) {
return (const char *)get_special_key_name(c, modifiers);
}
buf[0] = c; buf[0] = c;
buf[1] = NUL; buf[1] = NUL;
return buf; return buf;
} }
/* /// Convert string, replacing key codes with printables
* Translate a key sequence into special key names. ///
*/ /// @param[in] str String to convert.
void str2specialbuf(char_u *sp, char_u *buf, int len) /// @param[out] buf Buffer to save results to.
/// @param[in] len Buffer length.
void str2specialbuf(const char *sp, char *buf, size_t len)
FUNC_ATTR_NONNULL_ALL
{ {
char_u *s;
*buf = NUL;
while (*sp) { while (*sp) {
s = str2special(&sp, FALSE); const char *s = str2special(&sp, false, false);
if ((int)(STRLEN(s) + STRLEN(buf)) < len) const size_t s_len = strlen(s);
STRCAT(buf, s); if (s_len <= len) {
break;
}
memcpy(buf, s, s_len);
buf += s_len;
len -= s_len;
} }
*buf = NUL;
} }
/* /*

View File

@ -1451,9 +1451,8 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
/* Never redo "zf" (define fold). */ /* Never redo "zf" (define fold). */
if ((vim_strchr(p_cpo, CPO_YANK) != NULL || oap->op_type != OP_YANK) if ((vim_strchr(p_cpo, CPO_YANK) != NULL || oap->op_type != OP_YANK)
&& ((!VIsual_active || oap->motion_force) && ((!VIsual_active || oap->motion_force)
/* Also redo Operator-pending Visual mode mappings */ // Also redo Operator-pending Visual mode mappings.
|| (VIsual_active && cap->cmdchar == ':' || (cap->cmdchar == ':' && oap->op_type != OP_COLON))
&& oap->op_type != OP_COLON))
&& cap->cmdchar != 'D' && cap->cmdchar != 'D'
&& oap->op_type != OP_FOLD && oap->op_type != OP_FOLD
&& oap->op_type != OP_FOLDOPEN && oap->op_type != OP_FOLDOPEN
@ -5231,6 +5230,7 @@ static void nv_dollar(cmdarg_T *cap)
static void nv_search(cmdarg_T *cap) static void nv_search(cmdarg_T *cap)
{ {
oparg_T *oap = cap->oap; oparg_T *oap = cap->oap;
pos_T save_cursor = curwin->w_cursor;
if (cap->cmdchar == '?' && cap->oap->op_type == OP_ROT13) { if (cap->cmdchar == '?' && cap->oap->op_type == OP_ROT13) {
/* Translate "g??" to "g?g?" */ /* Translate "g??" to "g?g?" */
@ -5240,6 +5240,8 @@ static void nv_search(cmdarg_T *cap)
return; return;
} }
// When using 'incsearch' the cursor may be moved to set a different search
// start position.
cap->searchbuf = getcmdline(cap->cmdchar, cap->count1, 0); cap->searchbuf = getcmdline(cap->cmdchar, cap->count1, 0);
if (cap->searchbuf == NULL) { if (cap->searchbuf == NULL) {
@ -5248,7 +5250,8 @@ static void nv_search(cmdarg_T *cap)
} }
(void)normal_search(cap, cap->cmdchar, cap->searchbuf, (void)normal_search(cap, cap->cmdchar, cap->searchbuf,
(cap->arg ? 0 : SEARCH_MARK)); (cap->arg || !equalpos(save_cursor, curwin->w_cursor))
? 0 : SEARCH_MARK);
} }
/* /*

View File

@ -969,10 +969,13 @@ void set_init_2(bool headless)
p_window = Rows - 1; p_window = Rows - 1;
} }
set_number_default("window", Rows - 1); set_number_default("window", Rows - 1);
#if 0
// This bodges around problems that should be fixed in the TUI layer.
if (!headless && !os_term_is_nice()) { if (!headless && !os_term_is_nice()) {
set_string_option_direct((char_u *)"guicursor", -1, (char_u *)"", set_string_option_direct((char_u *)"guicursor", -1, (char_u *)"",
OPT_GLOBAL, SID_NONE); OPT_GLOBAL, SID_NONE);
} }
#endif
parse_shape_opt(SHAPE_CURSOR); // set cursor shapes from 'guicursor' parse_shape_opt(SHAPE_CURSOR); // set cursor shapes from 'guicursor'
(void)parse_printoptions(); // parse 'printoptions' default value (void)parse_printoptions(); // parse 'printoptions' default value
} }
@ -3031,7 +3034,7 @@ did_set_string_option (
/* 'pastetoggle': translate key codes like in a mapping */ /* 'pastetoggle': translate key codes like in a mapping */
else if (varp == &p_pt) { else if (varp == &p_pt) {
if (*p_pt) { if (*p_pt) {
(void)replace_termcodes(p_pt, STRLEN(p_pt), &p, true, true, false, (void)replace_termcodes(p_pt, STRLEN(p_pt), &p, true, true, true,
CPO_TO_CPO_FLAGS); CPO_TO_CPO_FLAGS);
if (p != NULL) { if (p != NULL) {
if (new_value_alloced) if (new_value_alloced)
@ -5175,9 +5178,13 @@ static int put_setstring(FILE *fd, char *cmd, char *name, char_u **valuep, int e
* CTRL-V or backslash */ * CTRL-V or backslash */
if (valuep == &p_pt) { if (valuep == &p_pt) {
s = *valuep; s = *valuep;
while (*s != NUL) while (*s != NUL) {
if (put_escstr(fd, str2special(&s, FALSE), 2) == FAIL) if (put_escstr(fd, (char_u *)str2special((const char **)&s, false,
false), 2)
== FAIL) {
return FAIL; return FAIL;
}
}
} else if (expand) { } else if (expand) {
buf = xmalloc(MAXPATHL); buf = xmalloc(MAXPATHL);
home_replace(NULL, *valuep, buf, MAXPATHL, FALSE); home_replace(NULL, *valuep, buf, MAXPATHL, FALSE);
@ -6173,15 +6180,16 @@ option_value2string (
} }
} else { // P_STRING } else { // P_STRING
varp = *(char_u **)(varp); varp = *(char_u **)(varp);
if (varp == NULL) /* just in case */ if (varp == NULL) { // Just in case.
NameBuff[0] = NUL; NameBuff[0] = NUL;
else if (opp->flags & P_EXPAND) } else if (opp->flags & P_EXPAND) {
home_replace(NULL, varp, NameBuff, MAXPATHL, FALSE); home_replace(NULL, varp, NameBuff, MAXPATHL, false);
/* Translate 'pastetoggle' into special key names */ // Translate 'pastetoggle' into special key names.
else if ((char_u **)opp->var == &p_pt) } else if ((char_u **)opp->var == &p_pt) {
str2specialbuf(p_pt, NameBuff, MAXPATHL); str2specialbuf((const char *)p_pt, (char *)NameBuff, MAXPATHL);
else } else {
STRLCPY(NameBuff, varp, MAXPATHL); STRLCPY(NameBuff, varp, MAXPATHL);
}
} }
} }

View File

@ -81,58 +81,56 @@
#define DFLT_FO_VIM "tcqj" #define DFLT_FO_VIM "tcqj"
#define FO_ALL "tcroq2vlb1mMBn,awj" /* for do_set() */ #define FO_ALL "tcroq2vlb1mMBn,awj" /* for do_set() */
/* characters for the p_cpo option: */ // characters for the p_cpo option:
#define CPO_ALTREAD 'a' /* ":read" sets alternate file name */ #define CPO_ALTREAD 'a' // ":read" sets alternate file name
#define CPO_ALTWRITE 'A' /* ":write" sets alternate file name */ #define CPO_ALTWRITE 'A' // ":write" sets alternate file name
#define CPO_BAR 'b' /* "\|" ends a mapping */ #define CPO_BAR 'b' // "\|" ends a mapping
#define CPO_BSLASH 'B' /* backslash in mapping is not special */ #define CPO_BSLASH 'B' // backslash in mapping is not special
#define CPO_SEARCH 'c' #define CPO_SEARCH 'c'
#define CPO_CONCAT 'C' /* Don't concatenate sourced lines */ #define CPO_CONCAT 'C' // Don't concatenate sourced lines
#define CPO_DOTTAG 'd' /* "./tags" in 'tags' is in current dir */ #define CPO_DOTTAG 'd' // "./tags" in 'tags' is in current dir
#define CPO_DIGRAPH 'D' /* No digraph after "r", "f", etc. */ #define CPO_DIGRAPH 'D' // No digraph after "r", "f", etc.
#define CPO_EXECBUF 'e' #define CPO_EXECBUF 'e'
#define CPO_EMPTYREGION 'E' /* operating on empty region is an error */ #define CPO_EMPTYREGION 'E' // operating on empty region is an error
#define CPO_FNAMER 'f' /* set file name for ":r file" */ #define CPO_FNAMER 'f' // set file name for ":r file"
#define CPO_FNAMEW 'F' /* set file name for ":w file" */ #define CPO_FNAMEW 'F' // set file name for ":w file"
#define CPO_INTMOD 'i' /* interrupt a read makes buffer modified */ #define CPO_INTMOD 'i' // interrupt a read makes buffer modified
#define CPO_INDENT 'I' /* remove auto-indent more often */ #define CPO_INDENT 'I' // remove auto-indent more often
#define CPO_ENDOFSENT 'J' /* need two spaces to detect end of sentence */ #define CPO_ENDOFSENT 'J' // need two spaces to detect end of sentence
#define CPO_KEYCODE 'k' /* don't recognize raw key code in mappings */ #define CPO_KOFFSET 'K' // don't wait for key code in mappings
#define CPO_KOFFSET 'K' /* don't wait for key code in mappings */ #define CPO_LITERAL 'l' // take char after backslash in [] literal
#define CPO_LITERAL 'l' /* take char after backslash in [] literal */ #define CPO_LISTWM 'L' // 'list' changes wrapmargin
#define CPO_LISTWM 'L' /* 'list' changes wrapmargin */
#define CPO_SHOWMATCH 'm' #define CPO_SHOWMATCH 'm'
#define CPO_MATCHBSL 'M' /* "%" ignores use of backslashes */ #define CPO_MATCHBSL 'M' // "%" ignores use of backslashes
#define CPO_NUMCOL 'n' /* 'number' column also used for text */ #define CPO_NUMCOL 'n' // 'number' column also used for text
#define CPO_LINEOFF 'o' #define CPO_LINEOFF 'o'
#define CPO_OVERNEW 'O' /* silently overwrite new file */ #define CPO_OVERNEW 'O' // silently overwrite new file
#define CPO_LISP 'p' /* 'lisp' indenting */ #define CPO_LISP 'p' // 'lisp' indenting
#define CPO_FNAMEAPP 'P' /* set file name for ":w >>file" */ #define CPO_FNAMEAPP 'P' // set file name for ":w >>file"
#define CPO_JOINCOL 'q' /* with "3J" use column after first join */ #define CPO_JOINCOL 'q' // with "3J" use column after first join
#define CPO_REDO 'r' #define CPO_REDO 'r'
#define CPO_REMMARK 'R' /* remove marks when filtering */ #define CPO_REMMARK 'R' // remove marks when filtering
#define CPO_BUFOPT 's' #define CPO_BUFOPT 's'
#define CPO_BUFOPTGLOB 'S' #define CPO_BUFOPTGLOB 'S'
#define CPO_TAGPAT 't' #define CPO_TAGPAT 't'
#define CPO_UNDO 'u' /* "u" undoes itself */ #define CPO_UNDO 'u' // "u" undoes itself
#define CPO_BACKSPACE 'v' /* "v" keep deleted text */ #define CPO_BACKSPACE 'v' // "v" keep deleted text
#define CPO_FWRITE 'W' /* "w!" doesn't overwrite readonly files */ #define CPO_FWRITE 'W' // "w!" doesn't overwrite readonly files
#define CPO_ESC 'x' #define CPO_ESC 'x'
#define CPO_REPLCNT 'X' /* "R" with a count only deletes chars once */ #define CPO_REPLCNT 'X' // "R" with a count only deletes chars once
#define CPO_YANK 'y' #define CPO_YANK 'y'
#define CPO_KEEPRO 'Z' /* don't reset 'readonly' on ":w!" */ #define CPO_KEEPRO 'Z' // don't reset 'readonly' on ":w!"
#define CPO_DOLLAR '$' #define CPO_DOLLAR '$'
#define CPO_FILTER '!' #define CPO_FILTER '!'
#define CPO_MATCH '%' #define CPO_MATCH '%'
#define CPO_PLUS '+' /* ":write file" resets 'modified' */ #define CPO_PLUS '+' // ":write file" resets 'modified'
#define CPO_SPECI '<' /* don't recognize <> in mappings */ #define CPO_REGAPPEND '>' // insert NL when appending to a register
#define CPO_REGAPPEND '>' /* insert NL when appending to a register */ #define CPO_SCOLON ';' // using "," and ";" will skip over char if
#define CPO_SCOLON ';' /* using "," and ";" will skip over char if // cursor would not move
* cursor would not move */
#define CPO_CHANGEW '_' // "cw" special-case #define CPO_CHANGEW '_' // "cw" special-case
// default values for Vim and Vi // default values for Vim and Vi
#define CPO_VIM "aABceFs_" #define CPO_VIM "aABceFs_"
#define CPO_VI "aAbBcCdDeEfFiIJkKlLmMnoOpPqrRsStuvWxXyZ$!%+<>;_" #define CPO_VI "aAbBcCdDeEfFiIJKlLmMnoOpPqrRsStuvWxXyZ$!%+>;_"
/* characters for p_ww option: */ /* characters for p_ww option: */
#define WW_ALL "bshl<>[],~" #define WW_ALL "bshl<>[],~"

View File

@ -2513,14 +2513,14 @@ return {
vi_def=true, vi_def=true,
vim=true, vim=true,
varname='p_ttimeout', varname='p_ttimeout',
defaults={if_true={vi=false}} defaults={if_true={vi=true}}
}, },
{ {
full_name='ttimeoutlen', abbreviation='ttm', full_name='ttimeoutlen', abbreviation='ttm',
type='number', scope={'global'}, type='number', scope={'global'},
vi_def=true, vi_def=true,
varname='p_ttm', varname='p_ttm',
defaults={if_true={vi=-1}} defaults={if_true={vi=50}}
}, },
{ {
full_name='ttyfast', abbreviation='tf', full_name='ttyfast', abbreviation='tf',

View File

@ -26,6 +26,7 @@
#include "nvim/globals.h" #include "nvim/globals.h"
#include "nvim/rbuffer.h" #include "nvim/rbuffer.h"
#include "nvim/macros.h" #include "nvim/macros.h"
#include "nvim/message.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS #ifdef INCLUDE_GENERATED_DECLARATIONS
# include "os/fileio.c.generated.h" # include "os/fileio.c.generated.h"
@ -48,7 +49,6 @@ int file_open(FileDescriptor *const ret_fp, const char *const fname,
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
{ {
int os_open_flags = 0; int os_open_flags = 0;
int fd;
TriState wr = kNone; TriState wr = kNone;
// -V:FLAG:501 // -V:FLAG:501
#define FLAG(flags, flag, fcntl_flags, wrval, cond) \ #define FLAG(flags, flag, fcntl_flags, wrval, cond) \
@ -73,14 +73,35 @@ int file_open(FileDescriptor *const ret_fp, const char *const fname,
FLAG(flags, kFileNoSymlink, O_NOFOLLOW, kNone, true); FLAG(flags, kFileNoSymlink, O_NOFOLLOW, kNone, true);
#endif #endif
#undef FLAG #undef FLAG
// wr is used for kFileReadOnly flag, but on
// QB:neovim-qb-slave-ubuntu-12-04-64bit it still errors out with
// `error: variable wr set but not used [-Werror=unused-but-set-variable]`
(void)wr;
fd = os_open(fname, os_open_flags, mode); const int fd = os_open(fname, os_open_flags, mode);
if (fd < 0) { if (fd < 0) {
return fd; return fd;
} }
return file_open_fd(ret_fp, fd, (wr == kTrue));
}
ret_fp->wr = (wr == kTrue); /// Wrap file descriptor with FileDescriptor structure
///
/// @warning File descriptor wrapped like this must not be accessed by other
/// means.
///
/// @param[out] ret_fp Address where information needed for reading from or
/// writing to a file is saved
/// @param[in] fd File descriptor to wrap.
/// @param[in] wr True if fd is opened for writing only, false if it is read
/// only.
///
/// @return Error code (@see os_strerror()) or 0. Currently always returns 0.
int file_open_fd(FileDescriptor *const ret_fp, const int fd, const bool wr)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
{
ret_fp->wr = wr;
ret_fp->fd = fd; ret_fp->fd = fd;
ret_fp->eof = false; ret_fp->eof = false;
ret_fp->rv = rbuffer_new(kRWBufferSize); ret_fp->rv = rbuffer_new(kRWBufferSize);
@ -114,6 +135,26 @@ FileDescriptor *file_open_new(int *const error, const char *const fname,
return fp; return fp;
} }
/// Like file_open_fd(), but allocate and return ret_fp
///
/// @param[out] error Error code, @see os_strerror(). Is set to zero on
/// success.
/// @param[in] fd File descriptor to wrap.
/// @param[in] wr True if fd is opened for writing only, false if it is read
/// only.
///
/// @return [allocated] Opened file or NULL in case of error.
FileDescriptor *file_open_fd_new(int *const error, const int fd, const bool wr)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_MALLOC FUNC_ATTR_WARN_UNUSED_RESULT
{
FileDescriptor *const fp = xmalloc(sizeof(*fp));
if ((*error = file_open_fd(fp, fd, wr)) != 0) {
xfree(fp);
return NULL;
}
return fp;
}
/// Close file and free its buffer /// Close file and free its buffer
/// ///
/// @param[in,out] fp File to close. /// @param[in,out] fp File to close.
@ -345,3 +386,32 @@ ptrdiff_t file_skip(FileDescriptor *const fp, const size_t size)
return (ptrdiff_t)read_bytes; return (ptrdiff_t)read_bytes;
} }
/// Msgpack callback for writing to a file
///
/// @param data File to write to.
/// @param[in] buf Data to write.
/// @param[in] len Length of the data to write.
///
/// @return 0 in case of success, -1 in case of error.
int msgpack_file_write(void *data, const char *buf, size_t len)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
{
assert(len < PTRDIFF_MAX);
const ptrdiff_t written_bytes = file_write((FileDescriptor *)data, buf, len);
if (written_bytes < 0) {
return msgpack_file_write_error((int)written_bytes);
}
return 0;
}
/// Print error which occurs when failing to write msgpack data
///
/// @param[in] error Error code of the error to print.
///
/// @return -1 (error return for msgpack_packer callbacks).
int msgpack_file_write_error(const int error)
{
emsgf(_("E5420: Failed to write to file: %s"), os_strerror(error));
return -1;
}

View File

@ -13,6 +13,13 @@
# include "nvim/os/unix_defs.h" # include "nvim/os/unix_defs.h"
#endif #endif
/// File descriptor number used for standard IO streams
enum {
OS_STDIN_FILENO = STDIN_FILENO,
OS_STDOUT_FILENO = STDOUT_FILENO,
OS_STDERR_FILENO = STDERR_FILENO,
};
#define BASENAMELEN (NAME_MAX - 5) #define BASENAMELEN (NAME_MAX - 5)
// Use the system path length if it makes sense. // Use the system path length if it makes sense.

View File

@ -91,4 +91,14 @@ typedef SSIZE_T ssize_t;
# define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR) # define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR)
#endif #endif
#ifndef STDIN_FILENO
# define STDIN_FILENO 0
#endif
#ifndef STDOUT_FILENO
# define STDOUT_FILENO 1
#endif
#ifndef STDERR_FILENO
# define STDERR_FILENO 2
#endif
#endif // NVIM_OS_WIN_DEFS_H #endif // NVIM_OS_WIN_DEFS_H

View File

@ -6928,9 +6928,10 @@ char_u *reg_submatch(int no)
STRNCPY(retval + len, reg_getline_submatch(lnum), STRNCPY(retval + len, reg_getline_submatch(lnum),
submatch_mmatch->endpos[no].col); submatch_mmatch->endpos[no].col);
len += submatch_mmatch->endpos[no].col; len += submatch_mmatch->endpos[no].col;
if (round == 2) if (round == 2) {
retval[len] = NUL; retval[len] = NUL; // -V595
++len; }
len++;
} }
if (retval == NULL) { if (retval == NULL) {

View File

@ -3913,7 +3913,7 @@ addstate (
int k; int k;
int found = FALSE; int found = FALSE;
nfa_thread_T *thread; nfa_thread_T *thread;
lpos_T save_lpos; struct multipos save_multipos;
int save_in_use; int save_in_use;
char_u *save_ptr; char_u *save_ptr;
int i; int i;
@ -4127,15 +4127,13 @@ skip_add:
/* avoid compiler warnings */ /* avoid compiler warnings */
save_ptr = NULL; save_ptr = NULL;
save_lpos.lnum = 0; memset(&save_multipos, 0, sizeof(save_multipos));
save_lpos.col = 0;
/* Set the position (with "off" added) in the subexpression. Save /* Set the position (with "off" added) in the subexpression. Save
* and restore it when it was in use. Otherwise fill any gap. */ * and restore it when it was in use. Otherwise fill any gap. */
if (REG_MULTI) { if (REG_MULTI) {
if (subidx < sub->in_use) { if (subidx < sub->in_use) {
save_lpos.lnum = sub->list.multi[subidx].start_lnum; save_multipos = sub->list.multi[subidx];
save_lpos.col = sub->list.multi[subidx].start_col;
save_in_use = -1; save_in_use = -1;
} else { } else {
save_in_use = sub->in_use; save_in_use = sub->in_use;
@ -4178,9 +4176,8 @@ skip_add:
sub = &subs->norm; sub = &subs->norm;
if (save_in_use == -1) { if (save_in_use == -1) {
if (REG_MULTI){ if (REG_MULTI) {
sub->list.multi[subidx].start_lnum = save_lpos.lnum; sub->list.multi[subidx] = save_multipos;
sub->list.multi[subidx].start_col = save_lpos.col;
} }
else else
sub->list.line[subidx].start = save_ptr; sub->list.line[subidx].start = save_ptr;
@ -4234,8 +4231,7 @@ skip_add:
if (sub->in_use <= subidx) if (sub->in_use <= subidx)
sub->in_use = subidx + 1; sub->in_use = subidx + 1;
if (REG_MULTI) { if (REG_MULTI) {
save_lpos.lnum = sub->list.multi[subidx].end_lnum; save_multipos = sub->list.multi[subidx];
save_lpos.col = sub->list.multi[subidx].end_col;
if (off == -1) { if (off == -1) {
sub->list.multi[subidx].end_lnum = reglnum + 1; sub->list.multi[subidx].end_lnum = reglnum + 1;
sub->list.multi[subidx].end_col = 0; sub->list.multi[subidx].end_col = 0;
@ -4249,9 +4245,8 @@ skip_add:
} else { } else {
save_ptr = sub->list.line[subidx].end; save_ptr = sub->list.line[subidx].end;
sub->list.line[subidx].end = reginput + off; sub->list.line[subidx].end = reginput + off;
/* avoid compiler warnings */ // avoid compiler warnings
save_lpos.lnum = 0; memset(&save_multipos, 0, sizeof(save_multipos));
save_lpos.col = 0;
} }
subs = addstate(l, state->out, subs, pim, off_arg); subs = addstate(l, state->out, subs, pim, off_arg);
@ -4261,9 +4256,8 @@ skip_add:
else else
sub = &subs->norm; sub = &subs->norm;
if (REG_MULTI){ if (REG_MULTI) {
sub->list.multi[subidx].end_lnum = save_lpos.lnum; sub->list.multi[subidx] = save_multipos;
sub->list.multi[subidx].end_col = save_lpos.col;
} }
else else
sub->list.line[subidx].end = save_ptr; sub->list.line[subidx].end = save_ptr;

View File

@ -4230,7 +4230,6 @@ win_line (
* (regardless of the xn,am settings). * (regardless of the xn,am settings).
* Only do this if the cursor is on the current line * Only do this if the cursor is on the current line
* (something has been written in it). * (something has been written in it).
* Don't do this for the GUI.
* Don't do this for double-width characters. * Don't do this for double-width characters.
* Don't do this for a window not at the right screen border. * Don't do this for a window not at the right screen border.
*/ */
@ -5846,12 +5845,12 @@ static void screen_char(unsigned off, int row, int col)
if (row >= screen_Rows || col >= screen_Columns) if (row >= screen_Rows || col >= screen_Columns)
return; return;
/* Outputting the last character on the screen may scrollup the screen. // Outputting the last character on the screen may scrollup the screen.
* Don't to it! Mark the character invalid (update it when scrolled up) */ // Don't to it! Mark the character invalid (update it when scrolled up)
// FIXME: The premise here is not actually true (cf. deferred wrap).
if (row == screen_Rows - 1 && col == screen_Columns - 1 if (row == screen_Rows - 1 && col == screen_Columns - 1
/* account for first command-line character in rightleft mode */ // account for first command-line character in rightleft mode
&& !cmdmsg_rl && !cmdmsg_rl) {
) {
ScreenAttrs[off] = (sattr_T)-1; ScreenAttrs[off] = (sattr_T)-1;
return; return;
} }

View File

@ -3413,8 +3413,16 @@ shada_read_next_item_start:
return mru_ret; return mru_ret;
} }
const size_t length = (size_t) length_u64; if (length_u64 > PTRDIFF_MAX) {
entry->timestamp = (Timestamp) timestamp_u64; emsgf(_(RCERR "Error while reading ShaDa file: "
"there is an item at position %" PRIu64 " "
"that is stated to be too long"),
initial_fpos);
return kSDReadStatusNotShaDa;
}
const size_t length = (size_t)length_u64;
entry->timestamp = (Timestamp)timestamp_u64;
if (type_u64 == 0) { if (type_u64 == 0) {
// kSDItemUnknown cannot possibly pass that far because it is -1 and that // kSDItemUnknown cannot possibly pass that far because it is -1 and that

View File

@ -1433,12 +1433,10 @@ spell_move_to (
// the cursor. // the cursor.
if (dir == BACKWARD if (dir == BACKWARD
|| lnum != wp->w_cursor.lnum || lnum != wp->w_cursor.lnum
|| (lnum == wp->w_cursor.lnum || wrapped
&& (wrapped || ((colnr_T)(curline
|| ((colnr_T)(curline ? p - buf + (ptrdiff_t)len
? p - buf + (ptrdiff_t)len : p - buf) > wp->w_cursor.col)) {
: p - buf)
> wp->w_cursor.col)))) {
if (has_syntax) { if (has_syntax) {
col = (int)(p - buf); col = (int)(p - buf);
(void)syn_get_id(wp, lnum, (colnr_T)col, (void)syn_get_id(wp, lnum, (colnr_T)col,
@ -2070,7 +2068,7 @@ char_u *did_set_spelllang(win_T *wp)
// destroying the buffer we are using... // destroying the buffer we are using...
if (!bufref_valid(&bufref)) { if (!bufref_valid(&bufref)) {
ret_msg = ret_msg =
(char_u *)"E797: SpellFileMissing autocommand deleted buffer"; (char_u *)N_("E797: SpellFileMissing autocommand deleted buffer");
goto theend; goto theend;
} }
} }
@ -3635,7 +3633,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so
// word). // word).
depth = 0; depth = 0;
sp = &stack[0]; sp = &stack[0];
memset(sp, 0, sizeof(trystate_T)); memset(sp, 0, sizeof(trystate_T)); // -V512
sp->ts_curi = 1; sp->ts_curi = 1;
if (soundfold) { if (soundfold) {

View File

@ -1666,8 +1666,9 @@ syn_current_attr (
* If we found a match after the last column, use it. * If we found a match after the last column, use it.
*/ */
if (next_match_idx >= 0 && next_match_col >= (int)current_col if (next_match_idx >= 0 && next_match_col >= (int)current_col
&& next_match_col != MAXCOL) && next_match_col != MAXCOL) {
(void)push_next_match(NULL); (void)push_next_match();
}
current_finished = TRUE; current_finished = TRUE;
current_state_stored = FALSE; current_state_stored = FALSE;
@ -1985,9 +1986,10 @@ syn_current_attr (
* endless loop). */ * endless loop). */
GA_APPEND(int, &zero_width_next_ga, next_match_idx); GA_APPEND(int, &zero_width_next_ga, next_match_idx);
next_match_idx = -1; next_match_idx = -1;
} else } else {
cur_si = push_next_match(cur_si); cur_si = push_next_match();
found_match = TRUE; }
found_match = true;
} }
} }
} }
@ -2167,9 +2169,10 @@ static int did_match_already(int idx, garray_T *gap)
/* /*
* Push the next match onto the stack. * Push the next match onto the stack.
*/ */
static stateitem_T *push_next_match(stateitem_T *cur_si) static stateitem_T *push_next_match(void)
{ {
synpat_T *spp; stateitem_T *cur_si;
synpat_T *spp;
int save_flags; int save_flags;
spp = &(SYN_ITEMS(syn_block)[next_match_idx]); spp = &(SYN_ITEMS(syn_block)[next_match_idx]);

View File

@ -229,7 +229,7 @@ Terminal *terminal_open(TerminalOptions opts)
rv->invalid_start = 0; rv->invalid_start = 0;
rv->invalid_end = opts.height; rv->invalid_end = opts.height;
refresh_screen(rv, curbuf); refresh_screen(rv, curbuf);
set_option_value("buftype", 0, "terminal", OPT_LOCAL); set_option_value("buftype", 0, "terminal", OPT_LOCAL); // -V666
// Default settings for terminal buffers // Default settings for terminal buffers
curbuf->b_p_ma = false; // 'nomodifiable' curbuf->b_p_ma = false; // 'nomodifiable'

View File

@ -62,6 +62,8 @@ NEW_TESTS ?= \
test_signs.res \ test_signs.res \
test_smartindent.res \ test_smartindent.res \
test_stat.res \ test_stat.res \
test_startup.res \
test_startup_utf8.res \
test_substitute.res \ test_substitute.res \
test_syntax.res \ test_syntax.res \
test_tabpage.res \ test_tabpage.res \

View File

@ -98,6 +98,21 @@ func Test_recursive_substitute()
bwipe! bwipe!
endfunc endfunc
func Test_nested_backrefs()
" Check example in change.txt.
new
for re in range(0, 2)
exe 'set re=' . re
call setline(1, 'aa ab x')
1s/\(\(a[a-d] \)*\)\(x\)/-\1- -\2- -\3-/
call assert_equal('-aa ab - -ab - -x-', getline(1))
call assert_equal('-aa ab - -ab - -x-', substitute('aa ab x', '\(\(a[a-d] \)*\)\(x\)', '-\1- -\2- -\3-', ''))
endfor
bwipe!
set re=0
endfunc
func Test_eow_with_optional() func Test_eow_with_optional()
let expected = ['abc def', 'abc', 'def', '', '', '', '', '', '', ''] let expected = ['abc def', 'abc', 'def', '', '', '', '', '', '', '']
for re in range(0, 2) for re in range(0, 2)

View File

@ -1,5 +1,278 @@
" Test for the search command " Test for the search command
func Test_search_cmdline()
" See test/functional/legacy/search_spec.lua
throw 'skipped: Nvim does not support test_disable_char_avail()'
if !exists('+incsearch')
return
endif
" need to disable char_avail,
" so that expansion of commandline works
call test_disable_char_avail(1)
new
call setline(1, [' 1', ' 2 these', ' 3 the', ' 4 their', ' 5 there', ' 6 their', ' 7 the', ' 8 them', ' 9 these', ' 10 foobar'])
" Test 1
" CTRL-N / CTRL-P skips through the previous search history
set noincsearch
:1
call feedkeys("/foobar\<cr>", 'tx')
call feedkeys("/the\<cr>",'tx')
call assert_equal('the', @/)
call feedkeys("/thes\<C-P>\<C-P>\<cr>",'tx')
call assert_equal('foobar', @/)
" Test 2
" Ctrl-G goes from one match to the next
" until the end of the buffer
set incsearch nowrapscan
:1
" first match
call feedkeys("/the\<cr>", 'tx')
call assert_equal(' 2 these', getline('.'))
:1
" second match
call feedkeys("/the\<C-G>\<cr>", 'tx')
call assert_equal(' 3 the', getline('.'))
call assert_equal([0, 0, 0, 0], getpos('"'))
:1
" third match
call feedkeys("/the".repeat("\<C-G>", 2)."\<cr>", 'tx')
call assert_equal(' 4 their', getline('.'))
:1
" fourth match
call feedkeys("/the".repeat("\<C-G>", 3)."\<cr>", 'tx')
call assert_equal(' 5 there', getline('.'))
:1
" fifth match
call feedkeys("/the".repeat("\<C-G>", 4)."\<cr>", 'tx')
call assert_equal(' 6 their', getline('.'))
:1
" sixth match
call feedkeys("/the".repeat("\<C-G>", 5)."\<cr>", 'tx')
call assert_equal(' 7 the', getline('.'))
:1
" seventh match
call feedkeys("/the".repeat("\<C-G>", 6)."\<cr>", 'tx')
call assert_equal(' 8 them', getline('.'))
:1
" eigth match
call feedkeys("/the".repeat("\<C-G>", 7)."\<cr>", 'tx')
call assert_equal(' 9 these', getline('.'))
:1
" no further match
call feedkeys("/the".repeat("\<C-G>", 8)."\<cr>", 'tx')
call assert_equal(' 9 these', getline('.'))
call assert_equal([0, 0, 0, 0], getpos('"'))
" Test 3
" Ctrl-G goes from one match to the next
" and continues back at the top
set incsearch wrapscan
:1
" first match
call feedkeys("/the\<cr>", 'tx')
call assert_equal(' 2 these', getline('.'))
:1
" second match
call feedkeys("/the\<C-G>\<cr>", 'tx')
call assert_equal(' 3 the', getline('.'))
:1
" third match
call feedkeys("/the".repeat("\<C-G>", 2)."\<cr>", 'tx')
call assert_equal(' 4 their', getline('.'))
:1
" fourth match
call feedkeys("/the".repeat("\<C-G>", 3)."\<cr>", 'tx')
call assert_equal(' 5 there', getline('.'))
:1
" fifth match
call feedkeys("/the".repeat("\<C-G>", 4)."\<cr>", 'tx')
call assert_equal(' 6 their', getline('.'))
:1
" sixth match
call feedkeys("/the".repeat("\<C-G>", 5)."\<cr>", 'tx')
call assert_equal(' 7 the', getline('.'))
:1
" seventh match
call feedkeys("/the".repeat("\<C-G>", 6)."\<cr>", 'tx')
call assert_equal(' 8 them', getline('.'))
:1
" eigth match
call feedkeys("/the".repeat("\<C-G>", 7)."\<cr>", 'tx')
call assert_equal(' 9 these', getline('.'))
:1
" back at first match
call feedkeys("/the".repeat("\<C-G>", 8)."\<cr>", 'tx')
call assert_equal(' 2 these', getline('.'))
" Test 4
" CTRL-T goes to the previous match
set incsearch nowrapscan
$
" first match
call feedkeys("?the\<cr>", 'tx')
call assert_equal(' 9 these', getline('.'))
$
" first match
call feedkeys("?the\<C-G>\<cr>", 'tx')
call assert_equal(' 9 these', getline('.'))
$
" second match
call feedkeys("?the".repeat("\<C-T>", 1)."\<cr>", 'tx')
call assert_equal(' 8 them', getline('.'))
$
" last match
call feedkeys("?the".repeat("\<C-T>", 7)."\<cr>", 'tx')
call assert_equal(' 2 these', getline('.'))
$
" last match
call feedkeys("?the".repeat("\<C-T>", 8)."\<cr>", 'tx')
call assert_equal(' 2 these', getline('.'))
" Test 5
" CTRL-T goes to the previous match
set incsearch wrapscan
$
" first match
call feedkeys("?the\<cr>", 'tx')
call assert_equal(' 9 these', getline('.'))
$
" first match at the top
call feedkeys("?the\<C-G>\<cr>", 'tx')
call assert_equal(' 2 these', getline('.'))
$
" second match
call feedkeys("?the".repeat("\<C-T>", 1)."\<cr>", 'tx')
call assert_equal(' 8 them', getline('.'))
$
" last match
call feedkeys("?the".repeat("\<C-T>", 7)."\<cr>", 'tx')
call assert_equal(' 2 these', getline('.'))
$
" back at the bottom of the buffer
call feedkeys("?the".repeat("\<C-T>", 8)."\<cr>", 'tx')
call assert_equal(' 9 these', getline('.'))
" Test 6
" CTRL-L adds to the search pattern
set incsearch wrapscan
1
" first match
call feedkeys("/the\<c-l>\<cr>", 'tx')
call assert_equal(' 2 these', getline('.'))
1
" go to next match of 'thes'
call feedkeys("/the\<c-l>\<C-G>\<cr>", 'tx')
call assert_equal(' 9 these', getline('.'))
1
" wrap around
call feedkeys("/the\<c-l>\<C-G>\<C-G>\<cr>", 'tx')
call assert_equal(' 2 these', getline('.'))
1
" wrap around
set nowrapscan
call feedkeys("/the\<c-l>\<C-G>\<C-G>\<cr>", 'tx')
call assert_equal(' 9 these', getline('.'))
" Test 7
" <bs> remove from match, but stay at current match
set incsearch wrapscan
1
" first match
call feedkeys("/thei\<cr>", 'tx')
call assert_equal(' 4 their', getline('.'))
1
" delete one char, add another
call feedkeys("/thei\<bs>s\<cr>", 'tx')
call assert_equal(' 2 these', getline('.'))
1
" delete one char, add another, go to previous match, add one char
call feedkeys("/thei\<bs>s\<bs>\<C-T>\<c-l>\<cr>", 'tx')
call assert_equal(' 9 these', getline('.'))
1
" delete all chars, start from the beginning again
call feedkeys("/them". repeat("\<bs>",4).'the\>'."\<cr>", 'tx')
call assert_equal(' 3 the', getline('.'))
" clean up
call test_disable_char_avail(0)
bw!
endfunc
func Test_search_cmdline2()
" See test/functional/legacy/search_spec.lua
throw 'skipped: Nvim does not support test_disable_char_avail()'
if !exists('+incsearch')
return
endif
" need to disable char_avail,
" so that expansion of commandline works
call test_disable_char_avail(1)
new
call setline(1, [' 1', ' 2 these', ' 3 the theother'])
" Test 1
" Ctrl-T goes correctly back and forth
set incsearch
1
" first match
call feedkeys("/the\<cr>", 'tx')
call assert_equal(' 2 these', getline('.'))
1
" go to next match (on next line)
call feedkeys("/the\<C-G>\<cr>", 'tx')
call assert_equal(' 3 the theother', getline('.'))
1
" go to next match (still on line 3)
call feedkeys("/the\<C-G>\<C-G>\<cr>", 'tx')
call assert_equal(' 3 the theother', getline('.'))
1
" go to next match (still on line 3)
call feedkeys("/the\<C-G>\<C-G>\<C-G>\<cr>", 'tx')
call assert_equal(' 3 the theother', getline('.'))
1
" go to previous match (on line 3)
call feedkeys("/the\<C-G>\<C-G>\<C-G>\<C-T>\<cr>", 'tx')
call assert_equal(' 3 the theother', getline('.'))
1
" go to previous match (on line 3)
call feedkeys("/the\<C-G>\<C-G>\<C-G>\<C-T>\<C-T>\<cr>", 'tx')
call assert_equal(' 3 the theother', getline('.'))
1
" go to previous match (on line 2)
call feedkeys("/the\<C-G>\<C-G>\<C-G>\<C-T>\<C-T>\<C-T>\<cr>", 'tx')
call assert_equal(' 2 these', getline('.'))
" Test 2: keep the view,
" after deleting a character from the search cmd
call setline(1, [' 1', ' 2 these', ' 3 the', ' 4 their', ' 5 there', ' 6 their', ' 7 the', ' 8 them', ' 9 these', ' 10 foobar'])
resize 5
1
call feedkeys("/foo\<bs>\<cr>", 'tx')
redraw
call assert_equal({'lnum': 10, 'leftcol': 0, 'col': 4, 'topfill': 0, 'topline': 6, 'coladd': 0, 'skipcol': 0, 'curswant': 4}, winsaveview())
" remove all history entries
for i in range(10)
call histdel('/')
endfor
" Test 3: reset the view,
" after deleting all characters from the search cmd
norm! 1gg0
" unfortunately, neither "/foo\<c-w>\<cr>", nor "/foo\<bs>\<bs>\<bs>\<cr>",
" nor "/foo\<c-u>\<cr>" works to delete the commandline.
" In that case Vim should return "E35 no previous regular expression",
" but it looks like Vim still sees /foo and therefore the test fails.
" Therefore, disableing this test
"call assert_fails(feedkeys("/foo\<c-w>\<cr>", 'tx'), 'E35')
"call assert_equal({'lnum': 1, 'leftcol': 0, 'col': 0, 'topfill': 0, 'topline': 1, 'coladd': 0, 'skipcol': 0, 'curswant': 0}, winsaveview())
" clean up
set noincsearch
call test_disable_char_avail(0)
bw!
endfunc
func Test_use_sub_pat() func Test_use_sub_pat()
split split
let @/ = '' let @/ = ''

View File

@ -75,7 +75,7 @@ func Test_help_arg()
" check if couple of lines are there " check if couple of lines are there
let found = [] let found = []
for line in lines for line in lines
if line =~ '-R.*Readonly mode' if line =~ '-R.*Read-only mode'
call add(found, 'Readonly mode') call add(found, 'Readonly mode')
endif endif
" Watch out for a second --version line in the Gnome version. " Watch out for a second --version line in the Gnome version.

View File

@ -0,0 +1,64 @@
" Tests for startup using utf-8.
if !has('multi_byte')
finish
endif
source shared.vim
func Test_read_stdin_utf8()
let linesin = ['テスト', '€ÀÈÌÒÙ']
call writefile(linesin, 'Xtestin')
let before = [
\ 'set enc=utf-8',
\ 'set fencs=cp932,utf-8',
\ ]
let after = [
\ 'write ++enc=utf-8 Xtestout',
\ 'quit!',
\ ]
if has('win32')
let pipecmd = 'type Xtestin | '
else
let pipecmd = 'cat Xtestin | '
endif
if RunVimPiped(before, after, '-', pipecmd)
let lines = readfile('Xtestout')
call assert_equal(linesin, lines)
else
call assert_equal('', 'RunVimPiped failed.')
endif
call delete('Xtestout')
call delete('Xtestin')
endfunc
func Test_read_fifo_utf8()
if !has('unix')
return
endif
" Using bash/zsh's process substitution.
if executable('bash')
set shell=bash
elseif executable('zsh')
set shell=zsh
else
return
endif
let linesin = ['テスト', '€ÀÈÌÒÙ']
call writefile(linesin, 'Xtestin')
let before = [
\ 'set enc=utf-8',
\ 'set fencs=cp932,utf-8',
\ ]
let after = [
\ 'write ++enc=utf-8 Xtestout',
\ 'quit!',
\ ]
if RunVim(before, after, '<(cat Xtestin)')
let lines = readfile('Xtestout')
call assert_equal(linesin, lines)
else
call assert_equal('', 'RunVim failed.')
endif
call delete('Xtestout')
call delete('Xtestin')
endfunc

121
src/nvim/tui/terminfo.c Normal file

File diff suppressed because one or more lines are too long

10
src/nvim/tui/terminfo.h Normal file
View File

@ -0,0 +1,10 @@
#ifndef NVIM_TUI_TERMINFO_H
#define NVIM_TUI_TERMINFO_H
#include <unibilium.h>
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "tui/terminfo.h.generated.h"
#endif
#endif // NVIM_TUI_TERMINFO_H

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -10,14 +10,14 @@ extern char* longVersion;
// //
// Vim version number, name, etc. Patchlevel is defined in version.c. // Vim version number, name, etc. Patchlevel is defined in version.c.
// //
#define VIM_VERSION_MAJOR 7 #define VIM_VERSION_MAJOR 8
#define VIM_VERSION_MINOR 4 #define VIM_VERSION_MINOR 0
#define VIM_VERSION_100 (VIM_VERSION_MAJOR * 100 + VIM_VERSION_MINOR) #define VIM_VERSION_100 (VIM_VERSION_MAJOR * 100 + VIM_VERSION_MINOR)
// used for the runtime directory name // used for the runtime directory name
#define VIM_VERSION_NODOT "vim74" #define VIM_VERSION_NODOT "vim80"
// swap file compatibility (max. length is 6 chars) // swap file compatibility (max. length is 6 chars)
#define VIM_VERSION_SHORT "7.4" #define VIM_VERSION_SHORT "8.0"
#ifdef INCLUDE_GENERATED_DECLARATIONS #ifdef INCLUDE_GENERATED_DECLARATIONS
# include "version.h.generated.h" # include "version.h.generated.h"

View File

@ -319,4 +319,8 @@ enum { FOLD_TEXT_LEN = 51 }; //!< buffer size for get_foldtext()
// Lowest number used for window ID. Cannot have this many windows per tab. // Lowest number used for window ID. Cannot have this many windows per tab.
#define LOWEST_WIN_ID 1000 #define LOWEST_WIN_ID 1000
#if defined(__FreeBSD__) && defined(S_ISCHR)
# define OPEN_CHR_FILES
#endif
#endif /* NVIM_VIM_H */ #endif /* NVIM_VIM_H */

View File

@ -5532,8 +5532,8 @@ int match_add(win_T *wp, const char *const grp, const char *const pat,
return -1; return -1;
} }
if (id < -1 || id == 0) { if (id < -1 || id == 0) {
EMSGN("E799: Invalid ID: %" PRId64 EMSGN(_("E799: Invalid ID: %" PRId64
" (must be greater than or equal to 1)", " (must be greater than or equal to 1)"),
id); id);
return -1; return -1;
} }
@ -5541,7 +5541,7 @@ int match_add(win_T *wp, const char *const grp, const char *const pat,
cur = wp->w_match_head; cur = wp->w_match_head;
while (cur != NULL) { while (cur != NULL) {
if (cur->id == id) { if (cur->id == id) {
EMSGN("E801: ID already taken: %" PRId64, id); EMSGN(_("E801: ID already taken: %" PRId64), id);
return -1; return -1;
} }
cur = cur->next; cur = cur->next;
@ -5705,10 +5705,11 @@ int match_delete(win_T *wp, int id, int perr)
int rtype = SOME_VALID; int rtype = SOME_VALID;
if (id < 1) { if (id < 1) {
if (perr == TRUE) if (perr) {
EMSGN("E802: Invalid ID: %" PRId64 EMSGN(_("E802: Invalid ID: %" PRId64
" (must be greater than or equal to 1)", " (must be greater than or equal to 1)"),
id); id);
}
return -1; return -1;
} }
while (cur != NULL && cur->id != id) { while (cur != NULL && cur->id != id) {
@ -5716,8 +5717,9 @@ int match_delete(win_T *wp, int id, int perr)
cur = cur->next; cur = cur->next;
} }
if (cur == NULL) { if (cur == NULL) {
if (perr == TRUE) if (perr) {
EMSGN("E803: ID not found: %" PRId64, id); EMSGN(_("E803: ID not found: %" PRId64), id);
}
return -1; return -1;
} }
if (cur == prev) if (cur == prev)

View File

@ -1,5 +1,6 @@
local helpers = require('test.functional.helpers')(after_each) local helpers = require('test.functional.helpers')(after_each)
local global_helpers = require('test.helpers')
local clear = helpers.clear local clear = helpers.clear
local command = helpers.command local command = helpers.command
local curbufmeths = helpers.curbufmeths local curbufmeths = helpers.curbufmeths
@ -8,13 +9,7 @@ local funcs = helpers.funcs
local meths = helpers.meths local meths = helpers.meths
local source = helpers.source local source = helpers.source
local function local_copy(t) local shallowcopy = global_helpers.shallowcopy
local copy = {}
for k,v in pairs(t) do
copy[k] = v
end
return copy
end
describe('get_keymap', function() describe('get_keymap', function()
before_each(clear) before_each(clear)
@ -22,16 +17,16 @@ describe('get_keymap', function()
-- Basic mapping and table to be used to describe results -- Basic mapping and table to be used to describe results
local foo_bar_string = 'nnoremap foo bar' local foo_bar_string = 'nnoremap foo bar'
local foo_bar_map_table = { local foo_bar_map_table = {
lhs='foo', lhs='foo',
silent=0, silent=0,
rhs='bar', rhs='bar',
expr=0, expr=0,
sid=0, sid=0,
buffer=0, buffer=0,
nowait=0, nowait=0,
mode='n', mode='n',
noremap=1, noremap=1,
} }
it('returns empty list when no map', function() it('returns empty list when no map', function()
eq({}, meths.get_keymap('n')) eq({}, meths.get_keymap('n'))
@ -50,7 +45,7 @@ describe('get_keymap', function()
-- Add another mapping -- Add another mapping
command('nnoremap foo_longer bar_longer') command('nnoremap foo_longer bar_longer')
local foolong_bar_map_table = local_copy(foo_bar_map_table) local foolong_bar_map_table = shallowcopy(foo_bar_map_table)
foolong_bar_map_table['lhs'] = 'foo_longer' foolong_bar_map_table['lhs'] = 'foo_longer'
foolong_bar_map_table['rhs'] = 'bar_longer' foolong_bar_map_table['rhs'] = 'bar_longer'
@ -72,7 +67,7 @@ describe('get_keymap', function()
command('inoremap foo bar') command('inoremap foo bar')
-- The table will be the same except for the mode -- The table will be the same except for the mode
local insert_table = local_copy(foo_bar_map_table) local insert_table = shallowcopy(foo_bar_map_table)
insert_table['mode'] = 'i' insert_table['mode'] = 'i'
eq({insert_table}, meths.get_keymap('i')) eq({insert_table}, meths.get_keymap('i'))
@ -81,11 +76,11 @@ describe('get_keymap', function()
it('considers scope', function() it('considers scope', function()
-- change the map slightly -- change the map slightly
command('nnoremap foo_longer bar_longer') command('nnoremap foo_longer bar_longer')
local foolong_bar_map_table = local_copy(foo_bar_map_table) local foolong_bar_map_table = shallowcopy(foo_bar_map_table)
foolong_bar_map_table['lhs'] = 'foo_longer' foolong_bar_map_table['lhs'] = 'foo_longer'
foolong_bar_map_table['rhs'] = 'bar_longer' foolong_bar_map_table['rhs'] = 'bar_longer'
local buffer_table = local_copy(foo_bar_map_table) local buffer_table = shallowcopy(foo_bar_map_table)
buffer_table['buffer'] = 1 buffer_table['buffer'] = 1
command('nnoremap <buffer> foo bar') command('nnoremap <buffer> foo bar')
@ -98,7 +93,7 @@ describe('get_keymap', function()
it('considers scope for overlapping maps', function() it('considers scope for overlapping maps', function()
command('nnoremap foo bar') command('nnoremap foo bar')
local buffer_table = local_copy(foo_bar_map_table) local buffer_table = shallowcopy(foo_bar_map_table)
buffer_table['buffer'] = 1 buffer_table['buffer'] = 1
command('nnoremap <buffer> foo bar') command('nnoremap <buffer> foo bar')
@ -121,7 +116,7 @@ describe('get_keymap', function()
command('nnoremap <buffer> foo bar') command('nnoremap <buffer> foo bar')
-- Final buffer will have buffer mappings -- Final buffer will have buffer mappings
local buffer_table = local_copy(foo_bar_map_table) local buffer_table = shallowcopy(foo_bar_map_table)
buffer_table['buffer'] = final_buffer buffer_table['buffer'] = final_buffer
eq({buffer_table}, meths.buf_get_keymap(final_buffer, 'n')) eq({buffer_table}, meths.buf_get_keymap(final_buffer, 'n'))
eq({buffer_table}, meths.buf_get_keymap(0, 'n')) eq({buffer_table}, meths.buf_get_keymap(0, 'n'))
@ -243,4 +238,73 @@ describe('get_keymap', function()
eq('<F12>', meths.get_keymap('n')[1]['lhs']) eq('<F12>', meths.get_keymap('n')[1]['lhs'])
eq(':let g:maparg_test_var = 1<CR>', meths.get_keymap('n')[1]['rhs']) eq(':let g:maparg_test_var = 1<CR>', meths.get_keymap('n')[1]['rhs'])
end) end)
it('works correctly despite various &cpo settings', function()
local cpo_table = {
silent=0,
expr=0,
sid=0,
buffer=0,
nowait=0,
noremap=1,
}
local function cpomap(lhs, rhs, mode)
local ret = shallowcopy(cpo_table)
ret.lhs = lhs
ret.rhs = rhs
ret.mode = mode
return ret
end
command('set cpo+=B')
command('nnoremap \\<C-a><C-a><LT>C-a>\\ \\<C-b><C-b><LT>C-b>\\')
command('nnoremap <special> \\<C-c><C-c><LT>C-c>\\ \\<C-d><C-d><LT>C-d>\\')
command('set cpo+=B')
command('xnoremap \\<C-a><C-a><LT>C-a>\\ \\<C-b><C-b><LT>C-b>\\')
command('xnoremap <special> \\<C-c><C-c><LT>C-c>\\ \\<C-d><C-d><LT>C-d>\\')
command('set cpo-=B')
command('snoremap \\<C-a><C-a><LT>C-a>\\ \\<C-b><C-b><LT>C-b>\\')
command('snoremap <special> \\<C-c><C-c><LT>C-c>\\ \\<C-d><C-d><LT>C-d>\\')
command('set cpo-=B')
command('onoremap \\<C-a><C-a><LT>C-a>\\ \\<C-b><C-b><LT>C-b>\\')
command('onoremap <special> \\<C-c><C-c><LT>C-c>\\ \\<C-d><C-d><LT>C-d>\\')
for _, cmd in ipairs({
'set cpo-=B',
'set cpo+=B',
}) do
command(cmd)
eq({cpomap('\\<C-C><C-C><lt>C-c>\\', '\\<C-D><C-D><lt>C-d>\\', 'n'),
cpomap('\\<C-A><C-A><lt>C-a>\\', '\\<C-B><C-B><lt>C-b>\\', 'n')},
meths.get_keymap('n'))
eq({cpomap('\\<C-C><C-C><lt>C-c>\\', '\\<C-D><C-D><lt>C-d>\\', 'x'),
cpomap('\\<C-A><C-A><lt>C-a>\\', '\\<C-B><C-B><lt>C-b>\\', 'x')},
meths.get_keymap('x'))
eq({cpomap('<lt>C-c><C-C><lt>C-c> ', '<lt>C-d><C-D><lt>C-d>', 's'),
cpomap('<lt>C-a><C-A><lt>C-a> ', '<lt>C-b><C-B><lt>C-b>', 's')},
meths.get_keymap('s'))
eq({cpomap('<lt>C-c><C-C><lt>C-c> ', '<lt>C-d><C-D><lt>C-d>', 'o'),
cpomap('<lt>C-a><C-A><lt>C-a> ', '<lt>C-b><C-B><lt>C-b>', 'o')},
meths.get_keymap('o'))
end
end)
it('always uses space for space and bar for bar', function()
local space_table = {
lhs='| |',
rhs='| |',
mode='n',
silent=0,
expr=0,
sid=0,
buffer=0,
nowait=0,
noremap=1,
}
command('nnoremap \\|<Char-0x20><Char-32><Space><Bar> \\|<Char-0x20><Char-32><Space> <Bar>')
eq({space_table}, meths.get_keymap('n'))
end)
end) end)

View File

@ -282,8 +282,13 @@ describe('server -> client', function()
end) end)
end) end)
describe('when connecting to its own pipe adress', function() describe('connecting to its own pipe address', function()
it('it does not deadlock', function() it('does not deadlock', function()
if not os.getenv("TRAVIS") and helpers.os_name() == "osx" then
-- It does, in fact, deadlock on QuickBuild. #6851
pending("deadlocks on QuickBuild", function() end)
return
end
local address = funcs.serverlist()[1] local address = funcs.serverlist()[1]
local first = string.sub(address,1,1) local first = string.sub(address,1,1)
ok(first == '/' or first == '\\') ok(first == '/' or first == '\\')

View File

@ -327,11 +327,11 @@ describe('api', function()
{'nvim_get_mode', {}}, {'nvim_get_mode', {}},
{'nvim_eval', {'1'}}, {'nvim_eval', {'1'}},
} }
eq({{{mode='n', blocking=false}, eq({ { {mode='n', blocking=false},
13, 13,
{mode='n', blocking=false}, -- TODO: should be blocked=true {mode='n', blocking=false}, -- TODO: should be blocked=true
1}, 1 },
NIL}, meths.call_atomic(req)) NIL}, meths.call_atomic(req))
eq({mode='r', blocking=true}, nvim("get_mode")) eq({mode='r', blocking=true}, nvim("get_mode"))
end) end)
-- TODO: bug #6166 -- TODO: bug #6166
@ -373,6 +373,11 @@ describe('api', function()
'<NL>x<Esc>x<CR>x<lt>x', true, true, true)) '<NL>x<Esc>x<CR>x<lt>x', true, true, true))
end) end)
it('does not convert keycodes if special=false', function()
eq('<NL>x<Esc>x<CR>x<lt>x', helpers.nvim('replace_termcodes',
'<NL>x<Esc>x<CR>x<lt>x', true, true, false))
end)
it('does not crash when transforming an empty string', function() it('does not crash when transforming an empty string', function()
-- Actually does not test anything, because current code will use NULL for -- Actually does not test anything, because current code will use NULL for
-- an empty string. -- an empty string.
@ -391,13 +396,13 @@ describe('api', function()
-- notice the special char(…) \xe2\80\xa6 -- notice the special char(…) \xe2\80\xa6
nvim('feedkeys', ':let x1="…"\n', '', true) nvim('feedkeys', ':let x1="…"\n', '', true)
-- Both replace_termcodes and feedkeys escape \x80 -- Both nvim_replace_termcodes and nvim_feedkeys escape \x80
local inp = helpers.nvim('replace_termcodes', ':let x2="…"<CR>', true, true, true) local inp = helpers.nvim('replace_termcodes', ':let x2="…"<CR>', true, true, true)
nvim('feedkeys', inp, '', true) nvim('feedkeys', inp, '', true) -- escape_csi=true
-- Disabling CSI escaping in feedkeys -- nvim_feedkeys with CSI escaping disabled
inp = helpers.nvim('replace_termcodes', ':let x3="…"<CR>', true, true, true) inp = helpers.nvim('replace_termcodes', ':let x3="…"<CR>', true, true, true)
nvim('feedkeys', inp, '', false) nvim('feedkeys', inp, '', false) -- escape_csi=false
helpers.stop() helpers.stop()
end end
@ -588,6 +593,36 @@ describe('api', function()
end) end)
end) end)
describe('list_runtime_paths', function()
it('returns nothing with empty &runtimepath', function()
meths.set_option('runtimepath', '')
eq({}, meths.list_runtime_paths())
end)
it('returns single runtimepath', function()
meths.set_option('runtimepath', 'a')
eq({'a'}, meths.list_runtime_paths())
end)
it('returns two runtimepaths', function()
meths.set_option('runtimepath', 'a,b')
eq({'a', 'b'}, meths.list_runtime_paths())
end)
it('returns empty strings when appropriate', function()
meths.set_option('runtimepath', 'a,,b')
eq({'a', '', 'b'}, meths.list_runtime_paths())
meths.set_option('runtimepath', ',a,b')
eq({'', 'a', 'b'}, meths.list_runtime_paths())
meths.set_option('runtimepath', 'a,b,')
eq({'a', 'b', ''}, meths.list_runtime_paths())
end)
it('truncates too long paths', function()
local long_path = ('/a'):rep(8192)
meths.set_option('runtimepath', long_path)
local paths_list = meths.list_runtime_paths()
neq({long_path}, paths_list)
eq({long_path:sub(1, #(paths_list[1]))}, paths_list)
end)
end)
it('can throw exceptions', function() it('can throw exceptions', function()
local status, err = pcall(nvim, 'get_option', 'invalid-option') local status, err = pcall(nvim, 'get_option', 'invalid-option')
eq(false, status) eq(false, status)

View File

@ -14,13 +14,52 @@ describe('TermClose event', function()
nvim('set_option', 'shellcmdflag', 'EXE') nvim('set_option', 'shellcmdflag', 'EXE')
end) end)
it('triggers when terminal job ends', function() it('triggers when fast-exiting terminal job stops', function()
command('autocmd TermClose * let g:test_termclose = 23') command('autocmd TermClose * let g:test_termclose = 23')
command('terminal') command('terminal')
command('call jobstop(b:terminal_job_id)') command('call jobstop(b:terminal_job_id)')
retry(nil, nil, function() eq(23, eval('g:test_termclose')) end) retry(nil, nil, function() eq(23, eval('g:test_termclose')) end)
end) end)
it('triggers when long-running terminal job gets stopped', function()
nvim('set_option', 'shell', 'sh')
command('autocmd TermClose * let g:test_termclose = 23')
command('terminal')
command('call jobstop(b:terminal_job_id)')
retry(nil, nil, function() eq(23, eval('g:test_termclose')) end)
end)
it('kills job trapping SIGTERM', function()
nvim('set_option', 'shell', 'sh')
nvim('set_option', 'shellcmdflag', '-c')
command([[ let g:test_job = jobstart('trap "" TERM && echo 1 && sleep 60', { ]]
.. [[ 'on_stdout': {-> execute('let g:test_job_started = 1')}, ]]
.. [[ 'on_exit': {-> execute('let g:test_job_exited = 1')}}) ]])
retry(nil, nil, function() eq(1, eval('get(g:, "test_job_started", 0)')) end)
local start = os.time()
command('call jobstop(g:test_job)')
retry(nil, nil, function() eq(1, eval('get(g:, "test_job_exited", 0)')) end)
local duration = os.time() - start
eq(2, duration)
end)
it('kills pty job trapping SIGHUP and SIGTERM', function()
nvim('set_option', 'shell', 'sh')
nvim('set_option', 'shellcmdflag', '-c')
command([[ let g:test_job = jobstart('trap "" HUP TERM && echo 1 && sleep 60', { ]]
.. [[ 'pty': 1,]]
.. [[ 'on_stdout': {-> execute('let g:test_job_started = 1')}, ]]
.. [[ 'on_exit': {-> execute('let g:test_job_exited = 1')}}) ]])
retry(nil, nil, function() eq(1, eval('get(g:, "test_job_started", 0)')) end)
local start = os.time()
command('call jobstop(g:test_job)')
retry(nil, nil, function() eq(1, eval('get(g:, "test_job_exited", 0)')) end)
local duration = os.time() - start
eq(4, duration)
end)
it('reports the correct <abuf>', function() it('reports the correct <abuf>', function()
command('set hidden') command('set hidden')
command('autocmd TermClose * let g:abuf = expand("<abuf>")') command('autocmd TermClose * let g:abuf = expand("<abuf>")')

View File

@ -8,7 +8,7 @@ describe('hostname()', function()
it('returns hostname string', function() it('returns hostname string', function()
local actual = call('hostname') local actual = call('hostname')
ok(string.len(actual) > 1) ok(string.len(actual) > 0)
if call('executable', 'hostname') == 1 then if call('executable', 'hostname') == 1 then
local expected = string.gsub(call('system', 'hostname'), '[\n\r]', '') local expected = string.gsub(call('system', 'hostname'), '[\n\r]', '')
helpers.eq(expected, actual) helpers.eq(expected, actual)

View File

@ -1,11 +1,12 @@
local helpers = require('test.functional.helpers')(after_each) local helpers = require('test.functional.helpers')(after_each)
local clear = helpers.clear local clear = helpers.clear
local eq = helpers.eq local eq = helpers.eq
local eval = helpers.eval local eval = helpers.eval
local funcs = helpers.funcs local funcs = helpers.funcs
local nvim = helpers.nvim local nvim = helpers.nvim
local source = helpers.source local source = helpers.source
local command = helpers.command
describe('maparg()', function() describe('maparg()', function()
before_each(clear) before_each(clear)
@ -117,4 +118,42 @@ describe('maparg()', function()
eq(1, map_dict['expr']) eq(1, map_dict['expr'])
eq('i', map_dict['mode']) eq('i', map_dict['mode'])
end) end)
it('works with combining characters', function()
-- Using addacutes to make combining character better visible
local function ac(s)
local acute = '\204\129' -- U+0301 COMBINING ACUTE ACCENT
local ret = s:gsub('`', acute)
return ret
end
command(ac([[
nnoremap a b`
nnoremap c` d
nnoremap e` f`
]]))
eq(ac('b`'), funcs.maparg(ac('a')))
eq(ac(''), funcs.maparg(ac('c')))
eq(ac('d'), funcs.maparg(ac('c`')))
eq(ac('f`'), funcs.maparg(ac('e`')))
local function acmap(lhs, rhs)
return {
lhs = ac(lhs),
rhs = ac(rhs),
buffer = 0,
expr = 0,
mode = 'n',
noremap = 1,
nowait = 0,
sid = 0,
silent = 0,
}
end
eq({}, funcs.maparg(ac('c'), 'n', 0, 1))
eq(acmap('a', 'b`'), funcs.maparg(ac('a'), 'n', 0, 1))
eq(acmap('c`', 'd'), funcs.maparg(ac('c`'), 'n', 0, 1))
eq(acmap('e`', 'f`'), funcs.maparg(ac('e`'), 'n', 0, 1))
end)
end) end)

View File

@ -0,0 +1,321 @@
local helpers = require('test.functional.helpers')(after_each)
local eq = helpers.eq
local NIL = helpers.NIL
local eval = helpers.eval
local clear = helpers.clear
local meths = helpers.meths
local funcs = helpers.funcs
local source = helpers.source
local dedent = helpers.dedent
local command = helpers.command
local exc_exec = helpers.exc_exec
local redir_exec = helpers.redir_exec
describe(':echo', function()
before_each(function()
clear()
source([[
function String(s)
return execute('echo a:s')[1:]
endfunction
]])
end)
describe('used to represent floating-point values', function()
it('dumps NaN values', function()
eq('str2float(\'nan\')', eval('String(str2float(\'nan\'))'))
end)
it('dumps infinite values', function()
eq('str2float(\'inf\')', eval('String(str2float(\'inf\'))'))
eq('-str2float(\'inf\')', eval('String(str2float(\'-inf\'))'))
end)
it('dumps regular values', function()
eq('1.5', funcs.String(1.5))
eq('1.56e-20', funcs.String(1.56000e-020))
eq('0.0', eval('String(0.0)'))
end)
it('dumps special v: values', function()
eq('v:true', eval('String(v:true)'))
eq('v:false', eval('String(v:false)'))
eq('v:null', eval('String(v:null)'))
eq('v:true', funcs.String(true))
eq('v:false', funcs.String(false))
eq('v:null', funcs.String(NIL))
end)
it('dumps values with at most six digits after the decimal point',
function()
eq('1.234568e-20', funcs.String(1.23456789123456789123456789e-020))
eq('1.234568', funcs.String(1.23456789123456789123456789))
end)
it('dumps values with at most seven digits before the decimal point',
function()
eq('1234567.891235', funcs.String(1234567.89123456789123456789))
eq('1.234568e7', funcs.String(12345678.9123456789123456789))
end)
it('dumps negative values', function()
eq('-1.5', funcs.String(-1.5))
eq('-1.56e-20', funcs.String(-1.56000e-020))
eq('-1.234568e-20', funcs.String(-1.23456789123456789123456789e-020))
eq('-1.234568', funcs.String(-1.23456789123456789123456789))
eq('-1234567.891235', funcs.String(-1234567.89123456789123456789))
eq('-1.234568e7', funcs.String(-12345678.9123456789123456789))
end)
end)
describe('used to represent numbers', function()
it('dumps regular values', function()
eq('0', funcs.String(0))
eq('-1', funcs.String(-1))
eq('1', funcs.String(1))
end)
it('dumps large values', function()
eq('2147483647', funcs.String(2^31-1))
eq('-2147483648', funcs.String(-2^31))
end)
end)
describe('used to represent strings', function()
it('dumps regular strings', function()
eq('test', funcs.String('test'))
end)
it('dumps empty strings', function()
eq('', funcs.String(''))
end)
it('dumps strings with \' inside', function()
eq('\'\'\'', funcs.String('\'\'\''))
eq('a\'b\'\'', funcs.String('a\'b\'\''))
eq('\'b\'\'d', funcs.String('\'b\'\'d'))
eq('a\'b\'c\'d', funcs.String('a\'b\'c\'d'))
end)
it('dumps NULL strings', function()
eq('', eval('String($XXX_UNEXISTENT_VAR_XXX)'))
end)
it('dumps NULL lists', function()
eq('[]', eval('String(v:_null_list)'))
end)
it('dumps NULL dictionaries', function()
eq('{}', eval('String(v:_null_dict)'))
end)
end)
describe('used to represent funcrefs', function()
before_each(function()
source([[
function Test1()
endfunction
function s:Test2() dict
endfunction
function g:Test3() dict
endfunction
let g:Test2_f = function('s:Test2')
]])
end)
it('dumps references to built-in functions', function()
eq('function', eval('String(function("function"))'))
end)
it('dumps references to user functions', function()
eq('Test1', eval('String(function("Test1"))'))
eq('g:Test3', eval('String(function("g:Test3"))'))
end)
it('dumps references to script functions', function()
eq('<SNR>2_Test2', eval('String(Test2_f)'))
end)
it('dumps partials with self referencing a partial', function()
source([[
function TestDict() dict
endfunction
let d = {}
let TestDictRef = function('TestDict', d)
let d.tdr = TestDictRef
]])
eq(dedent([[
function('TestDict', {'tdr': function('TestDict', {...@1})})
function('TestDict', {'tdr': function('TestDict', {...@1})})]]),
redir_exec('echo String(d.tdr)'))
end)
it('dumps automatically created partials', function()
eq('function(\'<SNR>2_Test2\', {\'f\': function(\'<SNR>2_Test2\')})',
eval('String({"f": Test2_f}.f)'))
eq('function(\'<SNR>2_Test2\', [1], {\'f\': function(\'<SNR>2_Test2\', [1])})',
eval('String({"f": function(Test2_f, [1])}.f)'))
end)
it('dumps manually created partials', function()
eq('function(\'Test3\', [1, 2], {})',
eval('String(function("Test3", [1, 2], {}))'))
eq('function(\'Test3\', {})',
eval('String(function("Test3", {}))'))
eq('function(\'Test3\', [1, 2])',
eval('String(function("Test3", [1, 2]))'))
end)
it('does not crash or halt when dumping partials with reference cycles in self',
function()
meths.set_var('d', {v=true})
eq(dedent([[
{'p': function('<SNR>2_Test2', {...@0}), 'f': function('<SNR>2_Test2'), 'v': v:true}
{'p': function('<SNR>2_Test2', {...@0}), 'f': function('<SNR>2_Test2'), 'v': v:true}]]),
redir_exec('echo String(extend(extend(g:d, {"f": g:Test2_f}), {"p": g:d.f}))'))
end)
it('does not show errors when dumping partials referencing the same dictionary',
function()
command('let d = {}')
-- Regression for “eval/typval_encode: Dump empty dictionary before
-- checking for refcycle”, results in error.
eq('[function(\'tr\', {}), function(\'tr\', {})]', eval('String([function("tr", d), function("tr", d)])'))
-- Regression for “eval: Work with reference cycles in partials (self)
-- properly”, results in crash.
eval('extend(d, {"a": 1})')
eq('[function(\'tr\', {\'a\': 1}), function(\'tr\', {\'a\': 1})]', eval('String([function("tr", d), function("tr", d)])'))
end)
it('does not crash or halt when dumping partials with reference cycles in arguments',
function()
meths.set_var('l', {})
eval('add(l, l)')
-- Regression: the below line used to crash (add returns original list and
-- there was error in dumping partials). Tested explicitly in
-- test/unit/api/private_helpers_spec.lua.
eval('add(l, function("Test1", l))')
eq(dedent([=[
function('Test1', [[[...@2], function('Test1', [[...@2]])], function('Test1', [[[...@4], function('Test1', [[...@4]])]])])
function('Test1', [[[...@2], function('Test1', [[...@2]])], function('Test1', [[[...@4], function('Test1', [[...@4]])]])])]=]),
redir_exec('echo String(function("Test1", l))'))
end)
it('does not crash or halt when dumping partials with reference cycles in self and arguments',
function()
meths.set_var('d', {v=true})
meths.set_var('l', {})
eval('add(l, l)')
eval('add(l, function("Test1", l))')
eval('add(l, function("Test1", d))')
eq(dedent([=[
{'p': function('<SNR>2_Test2', [[[...@3], function('Test1', [[...@3]]), function('Test1', {...@0})], function('Test1', [[[...@5], function('Test1', [[...@5]]), function('Test1', {...@0})]]), function('Test1', {...@0})], {...@0}), 'f': function('<SNR>2_Test2'), 'v': v:true}
{'p': function('<SNR>2_Test2', [[[...@3], function('Test1', [[...@3]]), function('Test1', {...@0})], function('Test1', [[[...@5], function('Test1', [[...@5]]), function('Test1', {...@0})]]), function('Test1', {...@0})], {...@0}), 'f': function('<SNR>2_Test2'), 'v': v:true}]=]),
redir_exec('echo String(extend(extend(g:d, {"f": g:Test2_f}), {"p": function(g:d.f, l)}))'))
end)
end)
describe('used to represent lists', function()
it('dumps empty list', function()
eq('[]', funcs.String({}))
end)
it('dumps nested lists', function()
eq('[[[[[]]]]]', funcs.String({{{{{}}}}}))
end)
it('dumps nested non-empty lists', function()
eq('[1, [[3, [[5], 4]], 2]]', funcs.String({1, {{3, {{5}, 4}}, 2}}))
end)
it('does not error when dumping recursive lists', function()
meths.set_var('l', {})
eval('add(l, l)')
eq(0, exc_exec('echo String(l)'))
end)
it('dumps recursive lists without error', function()
meths.set_var('l', {})
eval('add(l, l)')
eq('\n[[...@0]]\n[[...@0]]', redir_exec('echo String(l)'))
eq('\n[[[...@1]]]\n[[[...@1]]]', redir_exec('echo String([l])'))
end)
end)
describe('used to represent dictionaries', function()
it('dumps empty dictionary', function()
eq('{}', eval('String({})'))
end)
it('dumps list with two same empty dictionaries, also in partials', function()
command('let d = {}')
eq('[{}, {}]', eval('String([d, d])'))
eq('[function(\'tr\', {}), {}]', eval('String([function("tr", d), d])'))
eq('[{}, function(\'tr\', {})]', eval('String([d, function("tr", d)])'))
end)
it('dumps non-empty dictionary', function()
eq('{\'t\'\'est\': 1}', funcs.String({['t\'est']=1}))
end)
it('does not error when dumping recursive dictionaries', function()
meths.set_var('d', {d=1})
eval('extend(d, {"d": d})')
eq(0, exc_exec('echo String(d)'))
end)
it('dumps recursive dictionaries without the error', function()
meths.set_var('d', {d=1})
eval('extend(d, {"d": d})')
eq('\n{\'d\': {...@0}}\n{\'d\': {...@0}}',
redir_exec('echo String(d)'))
eq('\n{\'out\': {\'d\': {...@1}}}\n{\'out\': {\'d\': {...@1}}}',
redir_exec('echo String({"out": d})'))
end)
end)
describe('used to represent special values', function()
local function chr(n)
return ('%c'):format(n)
end
local function ctrl(c)
return ('%c'):format(c:upper():byte() - 0x40)
end
it('displays hex as hex', function()
-- Regression: due to missing (uint8_t) cast \x80 was represented as
-- ~@<80>.
eq('<80>', funcs.String(chr(0x80)))
eq('<81>', funcs.String(chr(0x81)))
eq('<8e>', funcs.String(chr(0x8e)))
eq('<c2>', funcs.String(('«'):sub(1, 1)))
eq('«', funcs.String(('«'):sub(1, 2)))
end)
it('displays ASCII control characters using ^X notation', function()
eq('^C', funcs.String(ctrl('c')))
eq('^A', funcs.String(ctrl('a')))
eq('^F', funcs.String(ctrl('f')))
end)
it('prints CR, NL and tab as-is', function()
eq('\n', funcs.String('\n'))
eq('\r', funcs.String('\r'))
eq('\t', funcs.String('\t'))
end)
it('prints non-printable UTF-8 in <> notation', function()
-- SINGLE SHIFT TWO, unicode control
eq('<8e>', funcs.String(funcs.nr2char(0x8E)))
-- Surrogate pair: U+1F0A0 PLAYING CARD BACK is represented in UTF-16 as
-- 0xD83C 0xDCA0. This is not valid in UTF-8.
eq('<d83c>', funcs.String(funcs.nr2char(0xD83C)))
eq('<dca0>', funcs.String(funcs.nr2char(0xDCA0)))
eq('<d83c><dca0>', funcs.String(funcs.nr2char(0xD83C) .. funcs.nr2char(0xDCA0)))
end)
end)
end)

File diff suppressed because one or more lines are too long

View File

@ -319,7 +319,14 @@ end
-- Dedent the given text and write it to the file name. -- Dedent the given text and write it to the file name.
local function write_file(name, text, dont_dedent) local function write_file(name, text, dont_dedent)
local file = io.open(name, 'w') local file = io.open(name, 'w')
if not dont_dedent then if type(text) == 'table' then
-- Byte blob
local bytes = text
text = ''
for _, char in ipairs(bytes) do
text = ('%s%c'):format(text, char)
end
elseif not dont_dedent then
text = dedent(text) text = dedent(text)
end end
file:write(text) file:write(text)
@ -337,11 +344,23 @@ local function read_file(name)
return ret return ret
end end
local sourced_fnames = {}
local function source(code) local function source(code)
local fname = tmpname() local fname = tmpname()
write_file(fname, code) write_file(fname, code)
nvim_command('source '..fname) nvim_command('source '..fname)
os.remove(fname) -- DO NOT REMOVE FILE HERE.
-- do_source() has a habit of checking whether files are “same” by using inode
-- and device IDs. If you run two source() calls in quick succession there is
-- a good chance that underlying filesystem will reuse the inode, making files
-- appear as “symlinks” to do_source when it checks FileIDs. With current
-- setup linux machines (both QB, travis and mine(ZyX-I) with XFS) do reuse
-- inodes, Mac OS machines (again, both QB and travis) do not.
--
-- Files appearing as “symlinks” mean that both the first and the second
-- source() calls will use same SID, which may fail some tests which check for
-- exact numbers after `<SNR>` in e.g. function names.
sourced_fnames[#sourced_fnames + 1] = fname
return fname return fname
end end
@ -581,6 +600,24 @@ local function missing_provider(provider)
end end
end end
local function alter_slashes(obj)
if not iswin() then
return obj
end
if type(obj) == 'string' then
local ret = obj:gsub('/', '\\')
return ret
elseif type(obj) == 'table' then
local ret = {}
for k, v in pairs(obj) do
ret[k] = alter_slashes(v)
end
return ret
else
assert(false, 'Could only alter slashes for tables of strings and strings')
end
end
local module = { local module = {
prepend_argv = prepend_argv, prepend_argv = prepend_argv,
clear = clear, clear = clear,
@ -649,11 +686,15 @@ local module = {
NIL = mpack.NIL, NIL = mpack.NIL,
get_pathsep = get_pathsep, get_pathsep = get_pathsep,
missing_provider = missing_provider, missing_provider = missing_provider,
alter_slashes = alter_slashes,
} }
return function(after_each) return function(after_each)
if after_each then if after_each then
after_each(function() after_each(function()
for _, fname in ipairs(sourced_fnames) do
os.remove(fname)
end
check_logs() check_logs()
check_cores('build/bin/nvim') check_cores('build/bin/nvim')
end) end)

View File

@ -0,0 +1,43 @@
local helpers = require('test.functional.helpers')(after_each)
local clear = helpers.clear
local eq = helpers.eq
local eval = helpers.eval
local expect = helpers.expect
local feed = helpers.feed
local insert = helpers.insert
describe('insert-mode Ctrl-O', function()
before_each(clear)
it('enters command mode for one command', function()
feed('ihello world<C-o>')
feed(':let ctrlo = "test"<CR>')
feed('iii')
expect('hello worldiii')
eq(1, eval('ctrlo ==# "test"'))
end)
it('re-enters insert mode at the end of the line when running startinsert', function()
-- #6962
feed('ihello world<C-o>')
feed(':startinsert<CR>')
feed('iii')
expect('hello worldiii')
end)
it('re-enters insert mode at the beginning of the line when running startinsert', function()
insert('hello world')
feed('0<C-o>')
feed(':startinsert<CR>')
feed('aaa')
expect('aaahello world')
end)
it('re-enters insert mode in the middle of the line when running startinsert', function()
insert('hello world')
feed('bi<C-o>')
feed(':startinsert<CR>')
feed('ooo')
expect('hello oooworld')
end)
end)

View File

@ -0,0 +1,474 @@
local helpers = require('test.functional.helpers')(after_each)
local Screen = require('test.functional.ui.screen')
local clear = helpers.clear
local command = helpers.command
local eq = helpers.eq
local eval = helpers.eval
local feed = helpers.feed
local funcs = helpers.funcs
describe('search cmdline', function()
local screen
before_each(function()
clear()
command('set nohlsearch')
screen = Screen.new(20, 3)
screen:attach()
screen:set_default_attr_ids({
inc = {reverse = true}
})
end)
local function tenlines()
funcs.setline(1, {
' 1', ' 2 these', ' 3 the', ' 4 their', ' 5 there',
' 6 their', ' 7 the', ' 8 them', ' 9 these', ' 10 foobar'
})
command('1')
end
it('history can be navigated with <C-N>/<C-P>', function()
tenlines()
command('set noincsearch')
feed('/foobar<CR>')
feed('/the<CR>')
eq('the', eval('@/'))
feed('/thes<C-P><C-P><CR>')
eq('foobar', eval('@/'))
end)
describe('can traverse matches', function()
before_each(tenlines)
local function forwarditer(wrapscan)
command('set incsearch '..wrapscan)
feed('/the')
screen:expect([[
1 |
2 {inc:the}se |
/the^ |
]])
feed('<C-G>')
screen:expect([[
2 these |
3 {inc:the} |
/the^ |
]])
eq({0, 0, 0, 0}, funcs.getpos('"'))
feed('<C-G>')
screen:expect([[
3 the |
4 {inc:the}ir |
/the^ |
]])
feed('<C-G>')
screen:expect([[
4 their |
5 {inc:the}re |
/the^ |
]])
feed('<C-G>')
screen:expect([[
5 there |
6 {inc:the}ir |
/the^ |
]])
feed('<C-G>')
screen:expect([[
6 their |
7 {inc:the} |
/the^ |
]])
feed('<C-G>')
screen:expect([[
7 the |
8 {inc:the}m |
/the^ |
]])
feed('<C-G>')
screen:expect([[
8 them |
9 {inc:the}se |
/the^ |
]])
feed('<C-G>')
if wrapscan == 'wrapscan' then
screen:expect([[
2 {inc:the}se |
3 the |
/the^ |
]])
else
screen:expect([[
8 them |
9 {inc:the}se |
/the^ |
]])
feed('<CR>')
eq({0, 0, 0, 0}, funcs.getpos('"'))
end
end
local function backiter(wrapscan)
command('set incsearch '..wrapscan)
command('$')
feed('?the')
screen:expect([[
9 {inc:the}se |
10 foobar |
?the^ |
]])
if wrapscan == 'wrapscan' then
feed('<C-G>')
screen:expect([[
2 {inc:the}se |
3 the |
?the^ |
]])
feed('<CR>')
screen:expect([[
2 ^these |
3 the |
?the |
]])
else
feed('<C-G>')
screen:expect([[
9 {inc:the}se |
10 foobar |
?the^ |
]])
feed('<CR>')
screen:expect([[
9 ^these |
10 foobar |
?the |
]])
end
command('$')
feed('?the')
screen:expect([[
9 {inc:the}se |
10 foobar |
?the^ |
]])
feed('<C-T>')
screen:expect([[
8 {inc:the}m |
9 these |
?the^ |
]])
for i = 1, 6 do
feed('<C-T>')
-- Avoid sleep just before expect, otherwise expect will take the full
-- timeout
if i ~= 6 then
screen:sleep(1)
end
end
screen:expect([[
2 {inc:the}se |
3 the |
?the^ |
]])
feed('<C-T>')
if wrapscan == 'wrapscan' then
screen:expect([[
9 {inc:the}se |
10 foobar |
?the^ |
]])
else
screen:expect([[
2 {inc:the}se |
3 the |
?the^ |
]])
end
end
it("using <C-G> and 'nowrapscan'", function()
forwarditer('nowrapscan')
end)
it("using <C-G> and 'wrapscan'", function()
forwarditer('wrapscan')
end)
it("using <C-T> and 'nowrapscan'", function()
backiter('nowrapscan')
end)
it("using <C-T> and 'wrapscan'", function()
backiter('wrapscan')
end)
end)
it('expands pattern with <C-L>', function()
tenlines()
command('set incsearch wrapscan')
feed('/the')
screen:expect([[
1 |
2 {inc:the}se |
/the^ |
]])
feed('<C-L>')
screen:expect([[
1 |
2 {inc:thes}e |
/thes^ |
]])
feed('<C-G>')
screen:expect([[
9 {inc:thes}e |
10 foobar |
/thes^ |
]])
feed('<C-G>')
screen:expect([[
2 {inc:thes}e |
3 the |
/thes^ |
]])
feed('<CR>')
screen:expect([[
2 ^these |
3 the |
/thes |
]])
command('1')
command('set nowrapscan')
feed('/the')
screen:expect([[
1 |
2 {inc:the}se |
/the^ |
]])
feed('<C-L>')
screen:expect([[
1 |
2 {inc:thes}e |
/thes^ |
]])
feed('<C-G>')
screen:expect([[
9 {inc:thes}e |
10 foobar |
/thes^ |
]])
feed('<C-G><CR>')
screen:expect([[
9 ^these |
10 foobar |
/thes |
]])
end)
it('reduces pattern with <BS> and keeps cursor position', function()
tenlines()
command('set incsearch wrapscan')
-- First match
feed('/thei')
screen:expect([[
4 {inc:thei}r |
5 there |
/thei^ |
]])
-- Match from initial cursor position when modifying search
feed('<BS>')
screen:expect([[
1 |
2 {inc:the}se |
/the^ |
]])
-- New text advances to next match
feed('s')
screen:expect([[
1 |
2 {inc:thes}e |
/thes^ |
]])
-- Stay on this match when deleting a character
feed('<BS>')
screen:expect([[
1 |
2 {inc:the}se |
/the^ |
]])
-- Advance to previous match
feed('<C-T>')
screen:expect([[
9 {inc:the}se |
10 foobar |
/the^ |
]])
-- Extend search to include next character
feed('<C-L>')
screen:expect([[
9 {inc:thes}e |
10 foobar |
/thes^ |
]])
-- Deleting all characters resets the cursor position
feed('<BS><BS><BS><BS>')
screen:expect([[
1 |
2 these |
/^ |
]])
feed('the')
screen:expect([[
1 |
2 {inc:the}se |
/the^ |
]])
feed('\\>')
screen:expect([[
2 these |
3 {inc:the} |
/the\>^ |
]])
end)
it('can traverse matches in the same line with <C-G>/<C-T>', function()
funcs.setline(1, { ' 1', ' 2 these', ' 3 the theother' })
command('1')
command('set incsearch')
-- First match
feed('/the')
screen:expect([[
1 |
2 {inc:the}se |
/the^ |
]])
-- Next match, different line
feed('<C-G>')
screen:expect([[
2 these |
3 {inc:the} theother |
/the^ |
]])
-- Next match, same line
feed('<C-G>')
screen:expect([[
2 these |
3 the {inc:the}other |
/the^ |
]])
feed('<C-G>')
screen:expect([[
2 these |
3 the theo{inc:the}r |
/the^ |
]])
-- Previous match, same line
feed('<C-T>')
screen:expect([[
2 these |
3 the {inc:the}other |
/the^ |
]])
feed('<C-T>')
screen:expect([[
2 these |
3 {inc:the} theother |
/the^ |
]])
-- Previous match, different line
feed('<C-T>')
screen:expect([[
2 {inc:the}se |
3 the theother |
/the^ |
]])
end)
it('keeps the view after deleting a char from the search', function()
screen:detach()
screen = Screen.new(20, 6)
screen:attach()
screen:set_default_attr_ids({
inc = {reverse = true}
})
screen:set_default_attr_ignore({
{bold=true, reverse=true}, {bold=true, foreground=Screen.colors.Blue1}
})
tenlines()
feed('/foo')
screen:expect([[
6 their |
7 the |
8 them |
9 these |
10 {inc:foo}bar |
/foo^ |
]])
feed('<BS>')
screen:expect([[
6 their |
7 the |
8 them |
9 these |
10 {inc:fo}obar |
/fo^ |
]])
feed('<CR>')
screen:expect([[
6 their |
7 the |
8 them |
9 these |
10 ^foobar |
/fo |
]])
eq({lnum = 10, leftcol = 0, col = 4, topfill = 0, topline = 6,
coladd = 0, skipcol = 0, curswant = 4},
funcs.winsaveview())
end)
it('restores original view after failed search', function()
screen:detach()
screen = Screen.new(40, 3)
screen:attach()
screen:set_default_attr_ids({
inc = {reverse = true},
err = { foreground = Screen.colors.Grey100, background = Screen.colors.Red },
more = { bold = true, foreground = Screen.colors.SeaGreen4 },
})
tenlines()
feed('0')
feed('/foo')
screen:expect([[
9 these |
10 {inc:foo}bar |
/foo^ |
]])
feed('<C-W>')
screen:expect([[
1 |
2 these |
/^ |
]])
feed('<CR>')
screen:expect([[
/ |
{err:E35: No previous regular expression} |
{more:Press ENTER or type command to continue}^ |
]])
feed('<CR>')
eq({lnum = 1, leftcol = 0, col = 0, topfill = 0, topline = 1,
coladd = 0, skipcol = 0, curswant = 0},
funcs.winsaveview())
end)
end)

View File

@ -3,14 +3,17 @@ local helpers = require('test.functional.helpers')(after_each)
local Screen = require('test.functional.ui.screen') local Screen = require('test.functional.ui.screen')
local eq = helpers.eq local eq = helpers.eq
local neq = helpers.neq
local NIL = helpers.NIL local NIL = helpers.NIL
local feed = helpers.feed local feed = helpers.feed
local clear = helpers.clear local clear = helpers.clear
local funcs = helpers.funcs local funcs = helpers.funcs
local meths = helpers.meths local meths = helpers.meths
local iswin = helpers.iswin
local command = helpers.command local command = helpers.command
local write_file = helpers.write_file local write_file = helpers.write_file
local redir_exec = helpers.redir_exec local redir_exec = helpers.redir_exec
local alter_slashes = helpers.alter_slashes
local screen local screen
@ -173,3 +176,119 @@ describe('debug.debug', function()
]]) ]])
end) end)
end) end)
describe('package.path/package.cpath', function()
local sl = alter_slashes
local function get_new_paths(sufs, runtimepaths)
runtimepaths = runtimepaths or meths.list_runtime_paths()
local new_paths = {}
local sep = package.config:sub(1, 1)
for _, v in ipairs(runtimepaths) do
for _, suf in ipairs(sufs) do
new_paths[#new_paths + 1] = v .. sep .. 'lua' .. suf
end
end
return new_paths
end
local function execute_lua(cmd, ...)
return meths.execute_lua(cmd, {...})
end
local function eval_lua(expr, ...)
return meths.execute_lua('return ' .. expr, {...})
end
local function set_path(which, value)
return execute_lua('package[select(1, ...)] = select(2, ...)', which, value)
end
it('contains directories from &runtimepath on first invocation', function()
local new_paths = get_new_paths(sl{'/?.lua', '/?/init.lua'})
local new_paths_str = table.concat(new_paths, ';')
eq(new_paths_str, eval_lua('package.path'):sub(1, #new_paths_str))
local new_cpaths = get_new_paths(iswin() and {'\\?.dll'} or {'/?.so'})
local new_cpaths_str = table.concat(new_cpaths, ';')
eq(new_cpaths_str, eval_lua('package.cpath'):sub(1, #new_cpaths_str))
end)
it('puts directories from &runtimepath always at the start', function()
meths.set_option('runtimepath', 'a,b')
local new_paths = get_new_paths(sl{'/?.lua', '/?/init.lua'}, {'a', 'b'})
local new_paths_str = table.concat(new_paths, ';')
eq(new_paths_str, eval_lua('package.path'):sub(1, #new_paths_str))
set_path('path', sl'foo/?.lua;foo/?/init.lua;' .. new_paths_str)
neq(new_paths_str, eval_lua('package.path'):sub(1, #new_paths_str))
command('set runtimepath+=c')
new_paths = get_new_paths(sl{'/?.lua', '/?/init.lua'}, {'a', 'b', 'c'})
new_paths_str = table.concat(new_paths, ';')
eq(new_paths_str, eval_lua('package.path'):sub(1, #new_paths_str))
end)
it('understands uncommon suffixes', function()
set_path('cpath', './?/foo/bar/baz/x.nlua')
meths.set_option('runtimepath', 'a')
local new_paths = get_new_paths({'/?/foo/bar/baz/x.nlua'}, {'a'})
local new_paths_str = table.concat(new_paths, ';')
eq(new_paths_str, eval_lua('package.cpath'):sub(1, #new_paths_str))
set_path('cpath', './yyy?zzz/x')
meths.set_option('runtimepath', 'b')
new_paths = get_new_paths({'/yyy?zzz/x'}, {'b'})
new_paths_str = table.concat(new_paths, ';')
eq(new_paths_str, eval_lua('package.cpath'):sub(1, #new_paths_str))
set_path('cpath', './yyy?zzz/123?ghi/x')
meths.set_option('runtimepath', 'b')
new_paths = get_new_paths({'/yyy?zzz/123?ghi/x'}, {'b'})
new_paths_str = table.concat(new_paths, ';')
eq(new_paths_str, eval_lua('package.cpath'):sub(1, #new_paths_str))
end)
it('preserves empty items', function()
local many_empty_path = ';;;;;;'
local many_empty_cpath = ';;;;;;./?.luaso'
set_path('path', many_empty_path)
set_path('cpath', many_empty_cpath)
meths.set_option('runtimepath', 'a')
local new_paths = get_new_paths(sl{'/?.lua', '/?/init.lua'}, {'a'})
local new_paths_str = table.concat(new_paths, ';')
eq(new_paths_str .. ';' .. many_empty_path, eval_lua('package.path'))
local new_cpaths = get_new_paths({'/?.luaso'}, {'a'})
local new_cpaths_str = table.concat(new_cpaths, ';')
eq(new_cpaths_str .. ';' .. many_empty_cpath, eval_lua('package.cpath'))
end)
it('preserves empty value', function()
set_path('path', '')
meths.set_option('runtimepath', 'a')
local new_paths = get_new_paths(sl{'/?.lua', '/?/init.lua'}, {'a'})
local new_paths_str = table.concat(new_paths, ';')
eq(new_paths_str .. ';', eval_lua('package.path'))
end)
it('purges out all additions if runtimepath is set to empty', function()
local new_paths = get_new_paths(sl{'/?.lua', '/?/init.lua'})
local new_paths_str = table.concat(new_paths, ';')
local path = eval_lua('package.path')
eq(new_paths_str, path:sub(1, #new_paths_str))
local new_cpaths = get_new_paths(iswin() and {'\\?.dll'} or {'/?.so'})
local new_cpaths_str = table.concat(new_cpaths, ';')
local cpath = eval_lua('package.cpath')
eq(new_cpaths_str, cpath:sub(1, #new_cpaths_str))
meths.set_option('runtimepath', '')
eq(path:sub(#new_paths_str + 2, -1), eval_lua('package.path'))
eq(cpath:sub(#new_cpaths_str + 2, -1), eval_lua('package.cpath'))
end)
it('works with paths with escaped commas', function()
meths.set_option('runtimepath', '\\,')
local new_paths = get_new_paths(sl{'/?.lua', '/?/init.lua'}, {','})
local new_paths_str = table.concat(new_paths, ';')
eq(new_paths_str, eval_lua('package.path'):sub(1, #new_paths_str))
end)
it('ignores paths with semicolons', function()
meths.set_option('runtimepath', 'foo;bar,\\,')
local new_paths = get_new_paths(sl{'/?.lua', '/?/init.lua'}, {','})
local new_paths_str = table.concat(new_paths, ';')
eq(new_paths_str, eval_lua('package.path'):sub(1, #new_paths_str))
end)
end)

View File

@ -70,7 +70,7 @@ describe('health.vim', function()
health#broken#check health#broken#check
======================================================================== ========================================================================
- ERROR: Failed to run healthcheck for "broken" plugin. Exception: - ERROR: Failed to run healthcheck for "broken" plugin. Exception:
function health#check[20]..health#broken#check, line 1 function health#check[21]..health#broken#check, line 1
caused an error caused an error
]]) ]])
end) end)

Some files were not shown because too many files have changed in this diff Show More