aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.editorconfig12
-rw-r--r--CMakeLists.txt8
-rwxr-xr-xclint.py2
-rw-r--r--cmake/RunLint.cmake6
-rw-r--r--config/config.h.in3
-rw-r--r--runtime/autoload/msgpack.vim3
-rw-r--r--runtime/doc/api.txt99
-rw-r--r--runtime/doc/autocmd.txt14
-rw-r--r--runtime/doc/eval.txt82
-rw-r--r--runtime/doc/msgpack_rpc.txt20
-rw-r--r--runtime/doc/options.txt1
-rw-r--r--runtime/doc/repeat.txt6
-rw-r--r--runtime/doc/tabpage.txt4
-rw-r--r--runtime/doc/vim_diff.txt2
-rwxr-xr-xscripts/gendeclarations.lua31
-rw-r--r--src/nvim/CMakeLists.txt1
-rw-r--r--src/nvim/api/buffer.c94
-rw-r--r--src/nvim/auevents.lua1
-rw-r--r--src/nvim/buffer.c233
-rw-r--r--src/nvim/buffer_defs.h39
-rw-r--r--src/nvim/bufhl_defs.h25
-rw-r--r--src/nvim/edit.c75
-rw-r--r--src/nvim/eval.c180
-rw-r--r--src/nvim/eval.h3
-rw-r--r--src/nvim/ex_cmds.c11
-rw-r--r--src/nvim/ex_cmds2.c164
-rw-r--r--src/nvim/ex_docmd.c10
-rw-r--r--src/nvim/getchar.c14
-rw-r--r--src/nvim/if_cscope.c122
-rw-r--r--src/nvim/lib/khash.h2
-rw-r--r--src/nvim/lib/kvec.h4
-rw-r--r--src/nvim/main.c16
-rw-r--r--src/nvim/map.c24
-rw-r--r--src/nvim/map.h4
-rw-r--r--src/nvim/mark.c1
-rw-r--r--src/nvim/mbyte.c4
-rw-r--r--src/nvim/misc1.c14
-rw-r--r--src/nvim/msgpack_rpc/defs.h18
-rw-r--r--src/nvim/normal.c9
-rw-r--r--src/nvim/ops.c107
-rw-r--r--src/nvim/option.c32
-rw-r--r--src/nvim/option_defs.h55
-rw-r--r--src/nvim/os/env.c10
-rw-r--r--src/nvim/po/CMakeLists.txt4
-rw-r--r--src/nvim/pos.h6
-rw-r--r--src/nvim/quickfix.c12
-rw-r--r--src/nvim/screen.c59
-rw-r--r--src/nvim/terminal.c36
-rw-r--r--src/nvim/testdir/Makefile52
-rw-r--r--src/nvim/undo.c94
-rw-r--r--src/nvim/version.c28
-rw-r--r--src/nvim/vim.h6
-rw-r--r--src/nvim/window.c35
-rw-r--r--test/functional/autocmd/textyankpost_spec.lua216
-rw-r--r--test/functional/eval/operators_spec.lua28
-rw-r--r--test/functional/eval/string_spec.lua175
-rw-r--r--test/functional/eval/vvar_event_spec.lua15
-rw-r--r--test/functional/fixtures/shell-test.c66
-rw-r--r--test/functional/legacy/036_regexp_character_classes_spec.lua7
-rw-r--r--test/functional/legacy/062_tab_pages_spec.lua4
-rw-r--r--test/functional/terminal/edit_spec.lua75
-rw-r--r--test/functional/ui/bufhl_spec.lua261
-rw-r--r--test/functional/ui/screen.lua21
-rw-r--r--test/functional/viml/errorlist_spec.lua49
-rw-r--r--test/functional/viml/function_spec.lua29
-rw-r--r--third-party/CMakeLists.txt4
66 files changed, 2268 insertions, 579 deletions
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000000..b08a27f2a2
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,12 @@
+root = true
+
+[*]
+indent_style = space
+indent_size = 2
+end_of_line = lf
+insert_final_newline = true
+charset = utf_8
+
+[Makefile]
+indent_style = tab
+tab_width = 4
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 527a085d3e..3dbe98ab67 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 2.8.7)
-project(NEOVIM)
+project(nvim)
# Point CMake at any custom modules we may ship
list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake")
@@ -206,6 +206,12 @@ if(MSVC)
else()
add_definitions(-Wall -Wextra -pedantic -Wno-unused-parameter
-Wstrict-prototypes -std=gnu99)
+
+ # On FreeBSD 64 math.h uses unguarded C11 extension, which taints clang
+ # 3.4.1 used there.
+ if(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
+ add_definitions(-Wno-c11-extensions)
+ endif()
endif()
if(MINGW)
diff --git a/clint.py b/clint.py
index 9f6a35b7f4..fc081dcd14 100755
--- a/clint.py
+++ b/clint.py
@@ -2552,7 +2552,7 @@ def CheckBraces(filename, clean_lines, linenum, error):
# If should always have a brace
for blockstart in ('if', 'while', 'for'):
- if Match(r'\s*{0}[^{{]*$'.format(blockstart), line):
+ if Match(r'\s*{0}(?!\w)[^{{]*$'.format(blockstart), line):
pos = line.find(blockstart)
pos = line.find('(', pos)
if pos > 0:
diff --git a/cmake/RunLint.cmake b/cmake/RunLint.cmake
index 42ef7a86ad..306e938232 100644
--- a/cmake/RunLint.cmake
+++ b/cmake/RunLint.cmake
@@ -2,7 +2,11 @@ get_filename_component(LINT_DIR ${LINT_DIR} ABSOLUTE)
get_filename_component(LINT_PREFIX ${LINT_DIR} PATH)
set(LINT_SUPPRESS_FILE "${LINT_PREFIX}/errors.json")
-file(GLOB_RECURSE LINT_FILES ${LINT_DIR}/*.c ${LINT_DIR}/*.h)
+if(DEFINED ENV{LINT_FILE})
+ file(GLOB_RECURSE LINT_FILES "$ENV{LINT_FILE}")
+else()
+ file(GLOB_RECURSE LINT_FILES ${LINT_DIR}/*.c ${LINT_DIR}/*.h)
+endif()
set(LINT_ARGS)
diff --git a/config/config.h.in b/config/config.h.in
index 27705f8b38..d13e7de2e6 100644
--- a/config/config.h.in
+++ b/config/config.h.in
@@ -12,6 +12,9 @@
#define ARCH_32
#endif
+#define PROJECT_NAME "@PROJECT_NAME@"
+#define LOCALE_INSTALL_DIR "@CMAKE_INSTALL_FULL_LOCALEDIR@"
+
#cmakedefine HAVE__NSGETENVIRON
#cmakedefine HAVE_FD_CLOEXEC
#cmakedefine HAVE_FSEEKO
diff --git a/runtime/autoload/msgpack.vim b/runtime/autoload/msgpack.vim
index e6022922fe..2bb7ec5b02 100644
--- a/runtime/autoload/msgpack.vim
+++ b/runtime/autoload/msgpack.vim
@@ -395,7 +395,8 @@ endfunction
""
" Dump floating-point value.
function s:msgpack_dump_float(v) abort
- return string(type(a:v) == type({}) ? a:v._VAL : a:v)
+ return substitute(string(type(a:v) == type({}) ? a:v._VAL : a:v),
+ \'\V\^\(-\)\?str2float(''\(inf\|nan\)'')\$', '\1\2', '')
endfunction
""
diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt
new file mode 100644
index 0000000000..ca79465e0d
--- /dev/null
+++ b/runtime/doc/api.txt
@@ -0,0 +1,99 @@
+*api.txt* For Nvim. {Nvim}
+
+
+ NVIM REFERENCE MANUAL by Thiago de Arruda
+
+The C API of Nvim *nvim-api*
+
+1. Introduction |nvim-api-intro|
+2. API Types |nvim-api-types|
+3. API metadata |nvim-api-metadata|
+4. Buffer highlighting |nvim-api-highlights|
+
+==============================================================================
+1. Introduction *nvim-api-intro*
+
+Nvim defines a C API as the primary way for external code to interact with
+the NVim core. In the present version of Nvim the API is primarily used by
+external processes to interact with Nvim using the msgpack-rpc protocol, see
+|msgpack-rpc|. The API will also be used from vimscript to access new Nvim core
+features, but this is not implemented yet. Later on, Nvim might be embeddable
+in C applications as libnvim, and the application will then control the
+embedded instance by calling the C API directly.
+
+==============================================================================
+2. API Types *nvim-api-types*
+
+Nvim's C API uses custom types for all functions. Some are just typedefs
+around C99 standard types, and some are Nvim defined data structures.
+
+Boolean -> bool
+Integer (signed 64-bit integer) -> int64_t
+Float (IEEE 754 double precision) -> double
+String -> {char* data, size_t size} struct
+
+Additionally, the following data structures are defined:
+
+Array
+Dictionary
+Object
+
+The following handle types are defined as integer typedefs, but are
+discriminated as separate types in an Object:
+
+Buffer -> enum value kObjectTypeBuffer
+Window -> enum value kObjectTypeWindow
+Tabpage -> enum value kObjectTypeTabpage
+
+==============================================================================
+3. API metadata *nvim-api-metadata*
+
+Nvim exposes metadata about the API as a Dictionary with the following keys:
+
+functions calling signature of the API functions
+types The custom handle types defined by Nvim
+error_types The possible kinds of errors an API function can exit with.
+
+This metadata is mostly useful for external programs accessing the api over
+msgpack-api, see |msgpack-rpc-api|.
+
+==============================================================================
+4. Buffer highlighting *nvim-api-highlights*
+
+Nvim allows plugins to add position-based highlights to buffers. This is
+similar to |matchaddpos()| but with some key differences. The added highlights
+are associated with a buffer and adapts to line insertions and deletions,
+similar to signs. It is also possible to manage a set of highlights as a group
+and delete or replace all at once.
+
+The intended use case are linter or semantic highlighter plugins that monitor
+a buffer for changes, and in the background compute highlights to the buffer.
+Another use case are plugins that show output in an append-only buffer, and
+want to add highlights to the outputs. Highlight data cannot be preserved
+on writing and loading a buffer to file, nor in undo/redo cycles.
+
+Highlights are registered using the |buffer_add_highlight| function, see the
+generated API documentation for details. If an external highlighter plugin is
+adding a large number of highlights in a batch, performance can be improved by
+calling |buffer_add_highlight| as an asynchronous notification, after first
+(synchronously) reqesting a source id. Here is an example using wrapper
+functions in the python client:
+>
+ src = vim.new_highlight_source()
+
+ buf = vim.current.buffer
+ for i in range(5):
+ buf.add_highlight("String",i,0,-1,src_id=src)
+
+ # some time later
+
+ buf.clear_highlight(src)
+<
+If the highlights don't need to be deleted or updated, just pass -1 as
+src_id (this is the default in python). |buffer_clear_highlight| can be used
+to clear highligts from a specific source, in a specific line range or the
+entire buffer by passing in the line range 0, -1 (the later is the default
+in python as used above).
+
+==============================================================================
+ vim:tw=78:ts=8:noet:ft=help:norl:
diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt
index e17281821c..ec5818e16f 100644
--- a/runtime/doc/autocmd.txt
+++ b/runtime/doc/autocmd.txt
@@ -308,6 +308,8 @@ Name triggered by ~
|InsertCharPre| when a character was typed in Insert mode, before
inserting it
+|TextYankPost| when some text is yanked or deleted
+
|TextChanged| after a change was made to the text in Normal mode
|TextChangedI| after a change was made to the text in Insert mode
@@ -722,6 +724,18 @@ InsertCharPre When a character is typed in Insert mode,
It is not allowed to change the text |textlock|.
The event is not triggered when 'paste' is
set.
+ *TextYankPost*
+TextYankPost Just after a |yank| or |deleting| command, but not
+ if the black hole register |quote_| is used nor
+ for |setreg()|. Pattern must be * because its
+ meaning may change in the future.
+ Sets these |v:event| keys:
+ operator
+ regcontents
+ regname
+ regtype
+ Recursion is ignored.
+ It is not allowed to change the text |textlock|.
*InsertEnter*
InsertEnter Just before starting Insert mode. Also for
Replace mode and Virtual Replace mode. The
diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt
index 45980f5d94..0b2880ef03 100644
--- a/runtime/doc/eval.txt
+++ b/runtime/doc/eval.txt
@@ -1386,6 +1386,22 @@ v:errors Errors found by assert functions, such as |assert_true()|.
< If v:errors is set to anything but a list it is made an empty
list by the assert function.
+ *v:event* *event-variable*
+v:event Dictionary of event data for the current |autocommand|. The
+ available keys differ per event type and are specified at the
+ documentation for each |event|. The possible keys are:
+ operator The operation performed. Unlike
+ |v:operator|, it is set also for an Ex
+ mode command. For instance, |:yank| is
+ translated to "|y|".
+ regcontents Text stored in the register as a
+ |readfile()|-style list of lines.
+ regname Requested register (e.g "x" for "xyy)
+ or the empty string for an unnamed
+ operation.
+ regtype Type of register as returned by
+ |getregtype()|.
+
*v:exception* *exception-variable*
v:exception The value of the exception most recently caught and not
finished. See also |v:throwpoint| and |throw-variables|.
@@ -1786,8 +1802,10 @@ cursor( {lnum}, {col} [, {off}])
cursor( {list}) Number move cursor to position in {list}
deepcopy( {expr} [, {noref}]) any make a full copy of {expr}
delete( {fname}) Number delete file {fname}
-dictwatcheradd({dict}, {pattern}, {callback}) Start watching a dictionary
-dictwatcherdel({dict}, {pattern}, {callback}) Stop watching a dictionary
+dictwatcheradd( {dict}, {pattern}, {callback})
+ Start watching a dictionary
+dictwatcherdel( {dict}, {pattern}, {callback})
+ Stop watching a dictionary
did_filetype() Number TRUE if FileType autocommand event used
diff_filler( {lnum}) Number diff filler lines about {lnum}
diff_hlID( {lnum}, {col}) Number diff highlighting at {lnum}/{col}
@@ -1807,7 +1825,7 @@ feedkeys( {string} [, {mode}]) Number add key sequence to typeahead buffer
filereadable( {file}) Number TRUE if {file} is a readable file
filewritable( {file}) Number TRUE if {file} is a writable file
filter( {expr}, {string}) List/Dict remove items from {expr} where
- {string} is 0
+ {string} is 0
finddir( {name}[, {path}[, {count}]])
String find directory {name} in {path}
findfile( {name}[, {path}[, {count}]])
@@ -1889,20 +1907,22 @@ inputdialog( {p} [, {t} [, {c}]]) String like input() but in a GUI dialog
inputlist( {textlist}) Number let the user pick from a choice list
inputrestore() Number restore typeahead
inputsave() Number save and clear typeahead
-inputsecret( {prompt} [, {text}]) String like input() but hiding the text
-insert( {list}, {item} [, {idx}]) List insert {item} in {list} [before {idx}]
+inputsecret( {prompt} [, {text}])
+ String like input() but hiding the text
+insert( {list}, {item} [, {idx}])
+ List insert {item} in {list} [before {idx}]
invert( {expr}) Number bitwise invert
isdirectory( {directory}) Number TRUE if {directory} is a directory
islocked( {expr}) Number TRUE if {expr} is locked
items( {dict}) List key-value pairs in {dict}
-jobclose({job}[, {stream}]) Number Closes a job stream(s)
-jobpid({job}) Number Returns pid of a job.
-jobresize({job}, {width}, {height})
+jobclose( {job}[, {stream}]) Number Closes a job stream(s)
+jobpid( {job}) Number Returns pid of a job.
+jobresize( {job}, {width}, {height})
Number Resize {job}'s pseudo terminal window
-jobsend({job}, {data}) Number Writes {data} to {job}'s stdin
-jobstart({cmd}[, {opts}]) Number Spawns {cmd} as a job
-jobstop({job}) Number Stops a job
-jobwait({ids}[, {timeout}]) Number Wait for a set of jobs
+jobsend( {job}, {data}) Number Writes {data} to {job}'s stdin
+jobstart( {cmd}[, {opts}]) Number Spawns {cmd} as a job
+jobstop( {job}) Number Stops a job
+jobwait( {ids}[, {timeout}]) Number Wait for a set of jobs
join( {list} [, {sep}]) String join {list} items into one String
keys( {dict}) List keys in {dict}
len( {expr}) Number the length of {expr}
@@ -1972,12 +1992,12 @@ repeat( {expr}, {count}) String repeat {expr} {count} times
resolve( {filename}) String get filename a shortcut points to
reverse( {list}) List reverse {list} in-place
round( {expr}) Float round off {expr}
-rpcnotify({channel}, {event}[, {args}...])
+rpcnotify( {channel}, {event}[, {args}...])
Sends a |msgpack-rpc| notification to {channel}
-rpcrequest({channel}, {method}[, {args}...])
+rpcrequest( {channel}, {method}[, {args}...])
Sends a |msgpack-rpc| request to {channel}
-rpcstart({prog}[, {argv}]) Spawns {prog} and opens a |msgpack-rpc| channel
-rpcstop({channel}) Closes a |msgpack-rpc| {channel}
+rpcstart( {prog}[, {argv}]) Spawns {prog} and opens a |msgpack-rpc| channel
+rpcstop( {channel}) Closes a |msgpack-rpc| {channel}
screenattr( {row}, {col}) Number attribute at screen position
screenchar( {row}, {col}) Number character at screen position
screencol() Number current cursor column
@@ -1999,11 +2019,12 @@ setbufvar( {expr}, {varname}, {val}) set {varname} in buffer {expr} to {val}
setcharsearch( {dict}) Dict set character search from {dict}
setcmdpos( {pos}) Number set cursor position in command-line
setline( {lnum}, {line}) Number set line {lnum} to {line}
-setloclist( {nr}, {list}[, {action}])
+setloclist( {nr}, {list}[, {action}[, {title}]])
Number modify location list using {list}
setmatches( {list}) Number restore a list of matches
setpos( {expr}, {list}) Number set the {expr} position to {list}
-setqflist( {list}[, {action}]) Number modify quickfix list using {list}
+setqflist( {list}[, {action}[, {title}]]
+ Number modify quickfix list using {list}
setreg( {n}, {v}[, {opt}]) Number set register to value and type
settabvar( {nr}, {varname}, {val}) set {varname} in tab page {nr} to {val}
settabwinvar( {tabnr}, {winnr}, {varname}, {val}) set {varname} in window
@@ -5732,11 +5753,13 @@ setline({lnum}, {text}) *setline()*
:endfor
< Note: The '[ and '] marks are not set.
-setloclist({nr}, {list} [, {action}]) *setloclist()*
+setloclist({nr}, {list} [, {action}[, {title}]]) *setloclist()*
Create or replace or add to the location list for window {nr}.
When {nr} is zero the current window is used. For a location
list window, the displayed location list is modified. For an
- invalid window number {nr}, -1 is returned.
+ invalid window number {nr}, -1 is returned. If {title} is
+ given, it will be used to set |w:quickfix_title| after opening
+ the location window.
Otherwise, same as |setqflist()|.
Also see |location-list|.
@@ -5793,7 +5816,7 @@ setpos({expr}, {list})
|winrestview()|.
-setqflist({list} [, {action}]) *setqflist()*
+setqflist({list} [, {action}[, {title}]]) *setqflist()*
Create or replace or add to the quickfix list using the items
in {list}. Each item in {list} is a dictionary.
Non-dictionary items in {list} are ignored. Each dictionary
@@ -5832,6 +5855,9 @@ setqflist({list} [, {action}]) *setqflist()*
with the items from {list}. If {action} is not present or is
set to ' ', then a new list is created.
+ If {title} is given, it will be used to set |w:quickfix_title|
+ after opening the quickfix window.
+
Returns zero for success, -1 for failure.
This function can be used to create a quickfix list
@@ -6232,12 +6258,22 @@ string({expr}) Return {expr} converted to a String. If {expr} is a Number,
{expr} type result ~
String 'string'
Number 123
- Float 123.123456 or 1.123456e8
- Funcref function('name')
+ Float 123.123456 or 1.123456e8 or
+ `str2float('inf')`
+ Funcref `function('name')`
List [item, item]
Dictionary {key: value, key: value}
Note that in String values the ' character is doubled.
Also see |strtrans()|.
+ Note 2: Output format is mostly compatible with YAML, except
+ for infinite and NaN floating-point values representations
+ which use |str2float()|. Strings are also dumped literally,
+ only single quote is escaped, which does not allow using YAML
+ for parsing back binary strings (including text when
+ 'encoding' is not UTF-8). |eval()| should always work for
+ strings and floats though and this is the only official
+ method, use |msgpackdump()| or |json_encode()| if you need to
+ share data with other application.
*strlen()*
strlen({expr}) The result is a Number, which is the length of the String
diff --git a/runtime/doc/msgpack_rpc.txt b/runtime/doc/msgpack_rpc.txt
index d732e7f818..bafb9dfc2c 100644
--- a/runtime/doc/msgpack_rpc.txt
+++ b/runtime/doc/msgpack_rpc.txt
@@ -7,7 +7,7 @@
The Msgpack-RPC Interface to Nvim *msgpack-rpc*
1. Introduction |msgpack-rpc-intro|
-2. API |msgpack-rpc-api|
+2. API mapping |msgpack-rpc-api|
3. Connecting |msgpack-rpc-connecting|
4. Clients |msgpack-rpc-clients|
5. Types |msgpack-rpc-types|
@@ -36,13 +36,13 @@ Nvim's msgpack-rpc interface is like a more powerful version of Vim's
`clientserver` feature.
==============================================================================
-2. API *msgpack-rpc-api*
+2. API mapping *msgpack-rpc-api*
-The Nvim C API is automatically exposed to the msgpack-rpc interface by the
-build system, which parses headers at src/nvim/api from the project root. A
-dispatch function is generated, which matches msgpack-rpc method names with
-non-static API functions, converting/validating arguments and return values
-back to msgpack.
+The Nvim C API, see |nvim-api|, is automatically exposed to the msgpack-rpc
+interface by the build system, which parses headers at src/nvim/api from the
+project root. A dispatch function is generated, which matches msgpack-rpc method
+names with non-static API functions, converting/validating arguments and return
+values back to msgpack.
Client libraries will normally provide wrappers that hide msgpack-rpc details
from programmers. The wrappers can be automatically generated by reading
@@ -63,7 +63,7 @@ Here's a simple way to get human-readable description of the API (requires
Python and the `pyyaml`/`msgpack-python` pip packages):
>
nvim --api-info | python -c 'import msgpack, sys, yaml; print yaml.dump(msgpack.unpackb(sys.stdin.read()))' > api.yaml
-
+<
==============================================================================
3. Connecting *msgpack-rpc-connecting*
@@ -162,8 +162,8 @@ https://github.com/msgpack-rpc/msgpack-rpc-ruby/blob/master/lib/msgpack/rpc/tran
==============================================================================
5. Types *msgpack-rpc-types*
-Nvim's C API uses custom types for all functions (some are just typedefs
-around C99 standard types). The types can be split into two groups:
+Nvim's C API uses custom types for all functions, se |nvim-api-types|.
+For the purpose of mapping to msgpack, he types can be split into two groups:
- Basic types that map natively to msgpack (and probably have a default
representation in msgpack-supported programming languages)
diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt
index ced303947b..44696d10bb 100644
--- a/runtime/doc/options.txt
+++ b/runtime/doc/options.txt
@@ -5538,6 +5538,7 @@ A jump table for the options with a short description can be found at |Q_op|.
c don't give |ins-completion-menu| messages. For example,
"-- XXX completion (YYY)", "match 1 of 2", "The only match",
"Pattern not found", "Back at original", etc.
+ q use "recording" instead of "recording @a"
This gives you the opportunity to avoid that a change between buffers
requires you to hit <Enter>, but still gives as useful a message as
diff --git a/runtime/doc/repeat.txt b/runtime/doc/repeat.txt
index 21b5eef811..b2e935eb3f 100644
--- a/runtime/doc/repeat.txt
+++ b/runtime/doc/repeat.txt
@@ -109,6 +109,12 @@ q{0-9a-zA-Z"} Record typed characters into register {0-9a-zA-Z"}
while executing a register, and it doesn't work inside
a mapping and |:normal|.
+ Note: If the register being used for recording is also
+ used for |y| and |p| the result is most likely not
+ what is expected, because the put will paste the
+ recorded macro and the yank will overwrite the
+ recorded macro.
+
q Stops recording.
Implementation note: The 'q' that stops recording is
not stored in the register, unless it was the result
diff --git a/runtime/doc/tabpage.txt b/runtime/doc/tabpage.txt
index 59c4a28ff2..70e6953211 100644
--- a/runtime/doc/tabpage.txt
+++ b/runtime/doc/tabpage.txt
@@ -273,8 +273,8 @@ window on the same buffer and then edit another buffer. Thus ":tabnew"
triggers:
WinLeave leave current window
TabLeave leave current tab page
- TabEnter enter new tab page
WinEnter enter window in new tab page
+ TabEnter enter new tab page
BufLeave leave current buffer
BufEnter enter new empty buffer
@@ -282,8 +282,8 @@ When switching to another tab page the order is:
BufLeave
WinLeave
TabLeave
- TabEnter
WinEnter
+ TabEnter
BufEnter
When entering a new tab page (|:tabnew|), TabNew is triggered before TabEnter
diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt
index d3768409f5..8722fced26 100644
--- a/runtime/doc/vim_diff.txt
+++ b/runtime/doc/vim_diff.txt
@@ -99,6 +99,8 @@ are always available and may be used simultaneously in separate plugins. The
Same thing applies to |string()| (though it uses construct like
"{E724@level}"), but this is not reliable because |string()| continues to
error out.
+4. Stringifyed infinite and NaN values now use |str2float()| and can be evaled
+ back.
Viminfo text files were replaced with binary (messagepack) ShaDa files.
Additional differences:
diff --git a/scripts/gendeclarations.lua b/scripts/gendeclarations.lua
index 637f4cdffa..4e74e4e301 100755
--- a/scripts/gendeclarations.lua
+++ b/scripts/gendeclarations.lua
@@ -239,24 +239,23 @@ end
non_static = non_static .. footer
static = static .. footer
+local F
+F = io.open(static_fname, 'w')
+F:write(static)
+F:close()
--- Before generating the headers, check if the current file (if exists) is
--- different from the new one. If they are the same, we won't touch the
--- current version to avoid triggering an unnecessary rebuilds of modules
+-- Before generating the non-static headers, check if the current file(if
+-- exists) is different from the new one. If they are the same, we won't touch
+-- the current version to avoid triggering an unnecessary rebuilds of modules
-- that depend on this one
-local update_changed = function (fname, contents)
- local F = io.open(fname, 'r')
- if F ~= nil then
- if F:read('*a') == contents then
- return
- end
- io.close(F)
+F = io.open(non_static_fname, 'r')
+if F ~= nil then
+ if F:read('*a') == non_static then
+ os.exit(0)
end
-
- F = io.open(fname, 'w')
- F:write(contents)
- F:close()
+ io.close(F)
end
-update_changed(static_fname, static)
-update_changed(non_static_fname, non_static)
+F = io.open(non_static_fname, 'w')
+F:write(non_static)
+F:close()
diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt
index ea5125e4e7..ef9e11fbef 100644
--- a/src/nvim/CMakeLists.txt
+++ b/src/nvim/CMakeLists.txt
@@ -76,7 +76,6 @@ set(CONV_SOURCES
diff.c
edit.c
eval.c
- ex_cmds2.c
ex_cmds.c
ex_docmd.c
ex_getln.c
diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c
index b7a86af134..fa4b8e5f7d 100644
--- a/src/nvim/api/buffer.c
+++ b/src/nvim/api/buffer.c
@@ -19,6 +19,7 @@
#include "nvim/mark.h"
#include "nvim/fileio.h"
#include "nvim/move.h"
+#include "nvim/syntax.h"
#include "nvim/window.h"
#include "nvim/undo.h"
@@ -514,6 +515,99 @@ ArrayOf(Integer, 2) buffer_get_mark(Buffer buffer, String name, Error *err)
return rv;
}
+/// Adds a highlight to buffer.
+///
+/// This can be used for plugins which dynamically generate highlights to a
+/// buffer (like a semantic highlighter or linter). The function adds a single
+/// highlight to a buffer. Unlike matchaddpos() highlights follow changes to
+/// line numbering (as lines are inserted/removed above the highlighted line),
+/// like signs and marks do.
+///
+/// "src_id" is useful for batch deletion/updating of a set of highlights. When
+/// called with src_id = 0, an unique source id is generated and returned.
+/// Succesive calls can pass in it as "src_id" to add new highlights to the same
+/// source group. All highlights in the same group can then be cleared with
+/// buffer_clear_highlight. If the highlight never will be manually deleted
+/// pass in -1 for "src_id".
+///
+/// If "hl_group" is the empty string no highlight is added, but a new src_id
+/// is still returned. This is useful for an external plugin to synchrounously
+/// request an unique src_id at initialization, and later asynchronously add and
+/// clear highlights in response to buffer changes.
+///
+/// @param buffer The buffer handle
+/// @param src_id Source group to use or 0 to use a new group,
+/// or -1 for ungrouped highlight
+/// @param hl_group Name of the highlight group to use
+/// @param line The line to highlight
+/// @param col_start Start of range of columns to highlight
+/// @param col_end End of range of columns to highlight,
+/// or -1 to highlight to end of line
+/// @param[out] err Details of an error that may have occurred
+/// @return The src_id that was used
+Integer buffer_add_highlight(Buffer buffer,
+ Integer src_id,
+ String hl_group,
+ Integer line,
+ Integer col_start,
+ Integer col_end,
+ Error *err)
+{
+ buf_T *buf = find_buffer_by_handle(buffer, err);
+ if (!buf) {
+ return 0;
+ }
+
+ if (line < 0 || line >= MAXLNUM) {
+ api_set_error(err, Validation, _("Line number outside range"));
+ return 0;
+ }
+ if (col_start < 0 || col_start > MAXCOL) {
+ api_set_error(err, Validation, _("Column value outside range"));
+ return 0;
+ }
+ if (col_end < 0 || col_end > MAXCOL) {
+ col_end = MAXCOL;
+ }
+
+ int hlg_id = syn_name2id((char_u*)hl_group.data);
+ src_id = bufhl_add_hl(buf, (int)src_id, hlg_id, (linenr_T)line+1,
+ (colnr_T)col_start+1, (colnr_T)col_end);
+ return src_id;
+}
+
+/// Clears highlights from a given source group and a range of lines
+///
+/// To clear a source group in the entire buffer, pass in 1 and -1 to
+/// line_start and line_end respectively.
+///
+/// @param buffer The buffer handle
+/// @param src_id Highlight source group to clear, or -1 to clear all groups.
+/// @param line_start Start of range of lines to clear
+/// @param line_end End of range of lines to clear (exclusive)
+/// or -1 to clear to end of file.
+/// @param[out] err Details of an error that may have occurred
+void buffer_clear_highlight(Buffer buffer,
+ Integer src_id,
+ Integer line_start,
+ Integer line_end,
+ Error *err)
+{
+ buf_T *buf = find_buffer_by_handle(buffer, err);
+ if (!buf) {
+ return;
+ }
+
+ if (line_start < 0 || line_start >= MAXLNUM) {
+ api_set_error(err, Validation, _("Line number outside range"));
+ return;
+ }
+ if (line_end < 0 || line_end > MAXLNUM) {
+ line_end = MAXLNUM;
+ }
+
+ bufhl_clear_line_range(buf, (int)src_id, (int)line_start+1, (int)line_end);
+}
// Check if deleting lines made the cursor position invalid.
// Changed the lines from "lo" to "hi" and added "extra" lines (negative if
diff --git a/src/nvim/auevents.lua b/src/nvim/auevents.lua
index aa4a8d8332..8d891effae 100644
--- a/src/nvim/auevents.lua
+++ b/src/nvim/auevents.lua
@@ -83,6 +83,7 @@ return {
'TermResponse', -- after setting "v:termresponse"
'TextChanged', -- text was modified
'TextChangedI', -- text was modified in Insert mode
+ 'TextYankPost', -- after a yank or delete was done (y, d, c)
'User', -- user defined autocommand
'VimEnter', -- after starting Vim
'VimLeave', -- before exiting Vim
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index c05090bbf6..62ab7495da 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -580,16 +580,17 @@ free_buffer_stuff (
)
{
if (free_options) {
- clear_wininfo(buf); /* including window-local options */
- free_buf_options(buf, TRUE);
+ clear_wininfo(buf); // including window-local options
+ free_buf_options(buf, true);
ga_clear(&buf->b_s.b_langp);
}
- vars_clear(&buf->b_vars->dv_hashtab); /* free all internal variables */
+ vars_clear(&buf->b_vars->dv_hashtab); // free all internal variables
hash_init(&buf->b_vars->dv_hashtab);
- uc_clear(&buf->b_ucmds); /* clear local user commands */
- buf_delete_signs(buf); /* delete any signs */
- map_clear_int(buf, MAP_ALL_MODES, TRUE, FALSE); /* clear local mappings */
- map_clear_int(buf, MAP_ALL_MODES, TRUE, TRUE); /* clear local abbrevs */
+ uc_clear(&buf->b_ucmds); // clear local user commands
+ buf_delete_signs(buf); // delete any signs
+ bufhl_clear_all(buf); // delete any highligts
+ map_clear_int(buf, MAP_ALL_MODES, true, false); // clear local mappings
+ map_clear_int(buf, MAP_ALL_MODES, true, true); // clear local abbrevs
xfree(buf->b_start_fenc);
buf->b_start_fenc = NULL;
}
@@ -4870,6 +4871,224 @@ void sign_mark_adjust(linenr_T line1, linenr_T line2, long amount, long amount_a
}
}
+// bufhl: plugin highlights associated with a buffer
+
+/// Adds a highlight to buffer.
+///
+/// Unlike matchaddpos() highlights follow changes to line numbering (as lines
+/// are inserted/removed above the highlighted line), like signs and marks do.
+///
+/// When called with "src_id" set to 0, a unique source id is generated and
+/// returned. Succesive calls can pass it in as "src_id" to add new highlights
+/// to the same source group. All highlights in the same group can be cleared
+/// at once. If the highlight never will be manually deleted pass in -1 for
+/// "src_id"
+///
+/// if "hl_id" or "lnum" is invalid no highlight is added, but a new src_id
+/// is still returned.
+///
+/// @param buf The buffer to add highlights to
+/// @param src_id src_id to use or 0 to use a new src_id group,
+/// or -1 for ungrouped highlight.
+/// @param hl_id Id of the highlight group to use
+/// @param lnum The line to highlight
+/// @param col_start First column to highlight
+/// @param col_end The last column to highlight,
+/// or -1 to highlight to end of line
+/// @return The src_id that was used
+int bufhl_add_hl(buf_T *buf,
+ int src_id,
+ int hl_id,
+ linenr_T lnum,
+ colnr_T col_start,
+ colnr_T col_end) {
+ static int next_src_id = 1;
+ if (src_id == 0) {
+ src_id = next_src_id++;
+ }
+ if (hl_id <= 0) {
+ // no highlight group or invalid line, just return src_id
+ return src_id;
+ }
+ if (!buf->b_bufhl_info) {
+ buf->b_bufhl_info = map_new(linenr_T, bufhl_vec_T)();
+ }
+ bufhl_vec_T* lineinfo = map_ref(linenr_T, bufhl_vec_T)(buf->b_bufhl_info,
+ lnum, true);
+
+ bufhl_hl_item_T *hlentry = kv_pushp(bufhl_hl_item_T, *lineinfo);
+ hlentry->src_id = src_id;
+ hlentry->hl_id = hl_id;
+ hlentry->start = col_start;
+ hlentry->stop = col_end;
+
+ if (0 < lnum && lnum <= buf->b_ml.ml_line_count) {
+ changed_lines_buf(buf, lnum, lnum+1, 0);
+ redraw_buf_later(buf, VALID);
+ }
+ return src_id;
+}
+
+/// Clear bufhl highlights from a given source group and range of lines.
+///
+/// @param buf The buffer to remove highlights from
+/// @param src_id highlight source group to clear, or -1 to clear all groups.
+/// @param line_start first line to clear
+/// @param line_end last line to clear or MAXLNUM to clear to end of file.
+void bufhl_clear_line_range(buf_T *buf,
+ int src_id,
+ linenr_T line_start,
+ linenr_T line_end) {
+ if (!buf->b_bufhl_info) {
+ return;
+ }
+ linenr_T line;
+ linenr_T first_changed = MAXLNUM, last_changed = -1;
+ // In the case line_start - line_end << bufhl_info->size
+ // it might be better to reverse this, i e loop over the lines
+ // to clear on.
+ bufhl_vec_T unused;
+ map_foreach(buf->b_bufhl_info, line, unused, {
+ (void)unused;
+ if (line_start <= line && line <= line_end) {
+ if (bufhl_clear_line(buf->b_bufhl_info, src_id, line)) {
+ if (line > last_changed) {
+ last_changed = line;
+ }
+ if (line < first_changed) {
+ first_changed = line;
+ }
+ }
+ }
+ })
+
+ if (last_changed != -1) {
+ changed_lines_buf(buf, first_changed, last_changed+1, 0);
+ redraw_buf_later(buf, VALID);
+ }
+}
+
+/// Clear bufhl highlights from a given source group and given line
+///
+/// @param bufhl_info The highlight info for the buffer
+/// @param src_id Highlight source group to clear, or -1 to clear all groups.
+/// @param lnum Linenr where the highlight should be cleared
+static bool bufhl_clear_line(bufhl_info_T *bufhl_info, int src_id, int lnum) {
+ bufhl_vec_T* lineinfo = map_ref(linenr_T, bufhl_vec_T)(bufhl_info,
+ lnum, false);
+ size_t oldsize = kv_size(*lineinfo);
+ if (src_id < 0) {
+ kv_size(*lineinfo) = 0;
+ } else {
+ size_t newind = 0;
+ for (size_t i = 0; i < kv_size(*lineinfo); i++) {
+ if (kv_A(*lineinfo, i).src_id != src_id) {
+ if (i != newind) {
+ kv_A(*lineinfo, newind) = kv_A(*lineinfo, i);
+ }
+ newind++;
+ }
+ }
+ kv_size(*lineinfo) = newind;
+ }
+
+ if (kv_size(*lineinfo) == 0) {
+ kv_destroy(*lineinfo);
+ map_del(linenr_T, bufhl_vec_T)(bufhl_info, lnum);
+ }
+ return kv_size(*lineinfo) != oldsize;
+}
+
+/// Remove all highlights and free the highlight data
+void bufhl_clear_all(buf_T* buf) {
+ if (!buf->b_bufhl_info) {
+ return;
+ }
+ bufhl_clear_line_range(buf, -1, 1, MAXLNUM);
+ map_free(linenr_T, bufhl_vec_T)(buf->b_bufhl_info);
+ buf->b_bufhl_info = NULL;
+}
+
+/// Adjust a placed highlight for inserted/deleted lines.
+void bufhl_mark_adjust(buf_T* buf,
+ linenr_T line1,
+ linenr_T line2,
+ long amount,
+ long amount_after) {
+ if (!buf->b_bufhl_info) {
+ return;
+ }
+
+ bufhl_info_T *newmap = map_new(linenr_T, bufhl_vec_T)();
+ linenr_T line;
+ bufhl_vec_T lineinfo;
+ map_foreach(buf->b_bufhl_info, line, lineinfo, {
+ if (line >= line1 && line <= line2) {
+ if (amount == MAXLNUM) {
+ bufhl_clear_line(buf->b_bufhl_info, -1, line);
+ continue;
+ } else {
+ line += amount;
+ }
+ } else if (line > line2) {
+ line += amount_after;
+ }
+ map_put(linenr_T, bufhl_vec_T)(newmap, line, lineinfo);
+ });
+ map_free(linenr_T, bufhl_vec_T)(buf->b_bufhl_info);
+ buf->b_bufhl_info = newmap;
+}
+
+
+/// Get highlights to display at a specific line
+///
+/// @param buf The buffer handle
+/// @param lnum The line number
+/// @param[out] info The highligts for the line
+/// @return true if there was highlights to display
+bool bufhl_start_line(buf_T *buf, linenr_T lnum, bufhl_lineinfo_T *info) {
+ if (!buf->b_bufhl_info) {
+ return false;
+ }
+
+ info->valid_to = -1;
+ info->entries = map_get(linenr_T, bufhl_vec_T)(buf->b_bufhl_info, lnum);
+ return kv_size(info->entries) > 0;
+}
+
+/// get highlighting at column col
+///
+/// It is is assumed this will be called with
+/// non-decreasing column nrs, so that it is
+/// possible to only recalculate highlights
+/// at endpoints.
+///
+/// @param info The info returned by bufhl_start_line
+/// @param col The column to get the attr for
+/// @return The highilight attr to display at the column
+int bufhl_get_attr(bufhl_lineinfo_T *info, colnr_T col) {
+ if (col <= info->valid_to) {
+ return info->current;
+ }
+ int attr = 0;
+ info->valid_to = MAXCOL;
+ for (size_t i = 0; i < kv_size(info->entries); i++) {
+ bufhl_hl_item_T entry = kv_A(info->entries, i);
+ if (entry.start <= col && col <= entry.stop) {
+ int entry_attr = syn_id2attr(entry.hl_id);
+ attr = hl_combine_attr(attr, entry_attr);
+ if (entry.stop < info->valid_to) {
+ info->valid_to = entry.stop;
+ }
+ } else if (col < entry.start && entry.start-1 < info->valid_to) {
+ info->valid_to = entry.start-1;
+ }
+ }
+ info->current = attr;
+ return attr;
+}
+
+
/*
* Set 'buflisted' for curbuf to "on" and trigger autocommands if it changed.
*/
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
index bdea609820..936a14b903 100644
--- a/src/nvim/buffer_defs.h
+++ b/src/nvim/buffer_defs.h
@@ -28,6 +28,8 @@ typedef struct file_buffer buf_T; // Forward declaration
#include "nvim/profile.h"
// for String
#include "nvim/api/private/defs.h"
+// for Map(K, V)
+#include "nvim/map.h"
#define MODIFIABLE(buf) (!buf->terminal && buf->b_p_ma)
@@ -59,21 +61,21 @@ typedef struct file_buffer buf_T; // Forward declaration
#define VALID_BOTLINE_AP 0x40 /* w_botine is approximated */
#define VALID_TOPLINE 0x80 /* w_topline is valid (for cursor position) */
-/* flags for b_flags */
-#define BF_RECOVERED 0x01 /* buffer has been recovered */
-#define BF_CHECK_RO 0x02 /* need to check readonly when loading file
- into buffer (set by ":e", may be reset by
- ":buf" */
-#define BF_NEVERLOADED 0x04 /* file has never been loaded into buffer,
- many variables still need to be set */
-#define BF_NOTEDITED 0x08 /* Set when file name is changed after
- starting to edit, reset when file is
- written out. */
-#define BF_NEW 0x10 /* file didn't exist when editing started */
-#define BF_NEW_W 0x20 /* Warned for BF_NEW and file created */
-#define BF_READERR 0x40 /* got errors while reading the file */
-#define BF_DUMMY 0x80 /* dummy buffer, only used internally */
-#define BF_PRESERVED 0x100 /* ":preserve" was used */
+// flags for b_flags
+#define BF_RECOVERED 0x01 // buffer has been recovered
+#define BF_CHECK_RO 0x02 // need to check readonly when loading file
+ // into buffer (set by ":e", may be reset by
+ // ":buf")
+#define BF_NEVERLOADED 0x04 // file has never been loaded into buffer,
+ // many variables still need to be set
+#define BF_NOTEDITED 0x08 // Set when file name is changed after
+ // starting to edit, reset when file is
+ // written out.
+#define BF_NEW 0x10 // file didn't exist when editing started
+#define BF_NEW_W 0x20 // Warned for BF_NEW and file created
+#define BF_READERR 0x40 // got errors while reading the file
+#define BF_DUMMY 0x80 // dummy buffer, only used internally
+#define BF_PRESERVED 0x100 // ":preserve" was used
/* Mask to check for flags that prevent normal writing */
#define BF_WRITE_MASK (BF_NOTEDITED + BF_NEW + BF_READERR)
@@ -101,6 +103,11 @@ typedef int scid_T; /* script ID */
// for signlist_T
#include "nvim/sign_defs.h"
+// for bufhl_*_T
+#include "nvim/bufhl_defs.h"
+
+typedef Map(linenr_T, bufhl_vec_T) bufhl_info_T;
+
// for FileID
#include "nvim/os/fs_defs.h"
@@ -754,6 +761,8 @@ struct file_buffer {
dict_T *additional_data; // Additional data from shada file if any.
int b_mapped_ctrl_c; // modes where CTRL-C is mapped
+
+ bufhl_info_T *b_bufhl_info; // buffer stored highlights
};
/*
diff --git a/src/nvim/bufhl_defs.h b/src/nvim/bufhl_defs.h
new file mode 100644
index 0000000000..e47bb2a238
--- /dev/null
+++ b/src/nvim/bufhl_defs.h
@@ -0,0 +1,25 @@
+#ifndef NVIM_BUFHL_DEFS_H
+#define NVIM_BUFHL_DEFS_H
+
+#include "nvim/pos.h"
+#include "nvim/lib/kvec.h"
+// bufhl: buffer specific highlighting
+
+struct bufhl_hl_item
+{
+ int src_id;
+ int hl_id; // highlight group
+ colnr_T start; // first column to highlight
+ colnr_T stop; // last column to highlight
+};
+typedef struct bufhl_hl_item bufhl_hl_item_T;
+
+typedef kvec_t(struct bufhl_hl_item) bufhl_vec_T;
+
+typedef struct {
+ bufhl_vec_T entries;
+ int current;
+ colnr_T valid_to;
+} bufhl_lineinfo_T;
+
+#endif // NVIM_BUFHL_DEFS_H
diff --git a/src/nvim/edit.c b/src/nvim/edit.c
index a983fa5772..6313ea2e81 100644
--- a/src/nvim/edit.c
+++ b/src/nvim/edit.c
@@ -1247,7 +1247,7 @@ normalchar:
static void insert_do_complete(InsertState *s)
{
compl_busy = true;
- if (ins_complete(s->c) == FAIL) {
+ if (ins_complete(s->c, true) == FAIL) {
compl_cont_status = 0;
}
compl_busy = false;
@@ -2321,9 +2321,10 @@ static int ins_compl_make_cyclic(void)
*/
void set_completion(colnr_T startcol, list_T *list)
{
- /* If already doing completions stop it. */
- if (ctrl_x_mode != 0)
+ // If already doing completions stop it.
+ if (ctrl_x_mode != 0) {
ins_compl_prep(' ');
+ }
ins_compl_clear();
if (stop_arrow() == FAIL)
@@ -2349,16 +2350,23 @@ void set_completion(colnr_T startcol, list_T *list)
compl_started = TRUE;
compl_used_match = TRUE;
compl_cont_status = 0;
+ int save_w_wrow = curwin->w_wrow;
compl_curr_match = compl_first_match;
if (compl_no_insert) {
- ins_complete(K_DOWN);
+ ins_complete(K_DOWN, false);
} else {
- ins_complete(Ctrl_N);
+ ins_complete(Ctrl_N, false);
if (compl_no_select) {
- ins_complete(Ctrl_P);
+ ins_complete(Ctrl_P, false);
}
}
+
+ // Lazily show the popup menu, unless we got interrupted.
+ if (!compl_interrupted) {
+ show_pum(save_w_wrow);
+ }
+
ui_flush();
}
@@ -2935,18 +2943,17 @@ static void ins_compl_new_leader(void)
ins_bytes(compl_leader + ins_compl_len());
compl_used_match = FALSE;
- if (compl_started)
+ if (compl_started) {
ins_compl_set_original_text(compl_leader);
- else {
- spell_bad_len = 0; /* need to redetect bad word */
- /*
- * Matches were cleared, need to search for them now.
- * Set "compl_restarting" to avoid that the first match is inserted.
- */
- compl_restarting = TRUE;
- if (ins_complete(Ctrl_N) == FAIL)
+ } else {
+ spell_bad_len = 0; // need to redetect bad word
+ // Matches were cleared, need to search for them now.
+ // Set "compl_restarting" to avoid that the first match is inserted.
+ compl_restarting = true;
+ if (ins_complete(Ctrl_N, true) == FAIL) {
compl_cont_status = 0;
- compl_restarting = FALSE;
+ }
+ compl_restarting = false;
}
compl_enter_selects = !compl_used_match;
@@ -4298,7 +4305,7 @@ static int ins_compl_use_match(int c)
* Called when character "c" was typed, which has a meaning for completion.
* Returns OK if completion was done, FAIL if something failed.
*/
-static int ins_complete(int c)
+static int ins_complete(int c, bool enable_pum)
{
char_u *line;
int startcol = 0; /* column where searched text starts */
@@ -4781,20 +4788,9 @@ static int ins_complete(int c)
}
}
- /* Show the popup menu, unless we got interrupted. */
- if (!compl_interrupted) {
- /* RedrawingDisabled may be set when invoked through complete(). */
- n = RedrawingDisabled;
- RedrawingDisabled = 0;
-
- /* If the cursor moved we need to remove the pum first. */
- setcursor();
- if (save_w_wrow != curwin->w_wrow)
- ins_compl_del_pum();
-
- ins_compl_show_pum();
- setcursor();
- RedrawingDisabled = n;
+ // Show the popup menu, unless we got interrupted.
+ if (enable_pum && !compl_interrupted) {
+ show_pum(save_w_wrow);
}
compl_was_interrupted = compl_interrupted;
compl_interrupted = FALSE;
@@ -8520,3 +8516,20 @@ static char_u *do_insert_char_pre(int c)
return res;
}
+
+static void show_pum(int save_w_wrow)
+{
+ // RedrawingDisabled may be set when invoked through complete().
+ int n = RedrawingDisabled;
+ RedrawingDisabled = 0;
+
+ // If the cursor moved we need to remove the pum first.
+ setcursor();
+ if (save_w_wrow != curwin->w_wrow) {
+ ins_compl_del_pum();
+ }
+
+ ins_compl_show_pum();
+ setcursor();
+ RedrawingDisabled = n;
+}
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 96fc79504f..0e5da13242 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -378,6 +378,7 @@ static struct vimvar {
{ VV_NAME("option_type", VAR_STRING), VV_RO },
{ VV_NAME("errors", VAR_LIST), 0 },
{ VV_NAME("msgpack_types", VAR_DICT), VV_RO },
+ { VV_NAME("event", VAR_DICT), VV_RO },
};
/* shorthand */
@@ -545,6 +546,10 @@ void eval_init(void)
set_vim_var_dict(VV_MSGPACK_TYPES, msgpack_types_dict);
set_vim_var_dict(VV_COMPLETED_ITEM, dict_alloc());
+
+ dict_T *v_event = dict_alloc();
+ v_event->dv_lock = VAR_FIXED;
+ set_vim_var_dict(VV_EVENT, v_event);
set_vim_var_list(VV_ERRORS, list_alloc());
set_vim_var_nr(VV_SEARCHFORWARD, 1L);
set_vim_var_nr(VV_HLSEARCH, 1L);
@@ -4010,12 +4015,22 @@ eval6 (
* When either side is a float the result is a float.
*/
if (use_float) {
- if (op == '*')
+ if (op == '*') {
f1 = f1 * f2;
- else if (op == '/') {
- /* We rely on the floating point library to handle divide
- * by zero to result in "inf" and not a crash. */
- f1 = f2 != 0 ? f1 / f2 : INFINITY;
+ } else if (op == '/') {
+ // Division by zero triggers error from AddressSanitizer
+ f1 = (f2 == 0
+ ? (
+#ifdef NAN
+ f1 == 0
+ ? NAN
+ :
+#endif
+ (f1 > 0
+ ? INFINITY
+ : -INFINITY)
+ )
+ : f1 / f2);
} else {
EMSG(_("E804: Cannot use '%' with Float"));
return FAIL;
@@ -6007,6 +6022,27 @@ static void rettv_dict_alloc(typval_T *rettv)
++d->dv_refcount;
}
+/// Clear all the keys of a Dictionary. "d" remains a valid empty Dictionary.
+///
+/// @param d The Dictionary to clear
+void dict_clear(dict_T *d)
+ FUNC_ATTR_NONNULL_ALL
+{
+ hash_lock(&d->dv_hashtab);
+ assert(d->dv_hashtab.ht_locked > 0);
+
+ size_t todo = d->dv_hashtab.ht_used;
+ for (hashitem_T *hi = d->dv_hashtab.ht_array; todo > 0; hi++) {
+ if (!HASHITEM_EMPTY(hi)) {
+ dictitem_free(HI2DI(hi));
+ hash_remove(&d->dv_hashtab, hi);
+ todo--;
+ }
+ }
+
+ hash_unlock(&d->dv_hashtab);
+}
+
/*
* Unreference a Dictionary: decrement the reference count and free it when it
@@ -6248,6 +6284,24 @@ int dict_add_list(dict_T *d, char *key, list_T *list)
return OK;
}
+/// Set all existing keys in "dict" as read-only.
+///
+/// This does not protect against adding new keys to the Dictionary.
+///
+/// @param dict The dict whose keys should be frozen
+void dict_set_keys_readonly(dict_T *dict)
+ FUNC_ATTR_NONNULL_ALL
+{
+ size_t todo = dict->dv_hashtab.ht_used;
+ for (hashitem_T *hi = dict->dv_hashtab.ht_array; todo > 0 ; hi++) {
+ if (HASHITEM_EMPTY(hi)) {
+ continue;
+ }
+ todo--;
+ HI2DI(hi)->di_flags |= DI_FLAGS_RO | DI_FLAGS_FIX;
+ }
+}
+
/*
* Get the number of items in a Dictionary.
*/
@@ -6848,9 +6902,25 @@ vim_to_msgpack_error_ret: \
#define CONV_FLOAT(flt) \
do { \
- char numbuf[NUMBUFLEN]; \
- vim_snprintf(numbuf, NUMBUFLEN - 1, "%g", (flt)); \
- ga_concat(gap, (char_u *) numbuf); \
+ const float_T flt_ = (flt); \
+ switch (fpclassify(flt_)) { \
+ case FP_NAN: { \
+ ga_concat(gap, (char_u *) "str2float('nan')"); \
+ break; \
+ } \
+ case FP_INFINITE: { \
+ if (flt_ < 0) { \
+ ga_append(gap, '-'); \
+ } \
+ ga_concat(gap, (char_u *) "str2float('inf')"); \
+ break; \
+ } \
+ default: { \
+ char numbuf[NUMBUFLEN]; \
+ vim_snprintf(numbuf, NUMBUFLEN - 1, "%g", flt_); \
+ ga_concat(gap, (char_u *) numbuf); \
+ } \
+ } \
} while (0)
#define CONV_FUNC(fun) \
@@ -7290,7 +7360,7 @@ static struct fst {
{ "pathshorten", 1, 1, f_pathshorten },
{ "pow", 2, 2, f_pow },
{ "prevnonblank", 1, 1, f_prevnonblank },
- { "printf", 2, 19, f_printf },
+ { "printf", 2, MAX_FUNC_ARGS, f_printf },
{ "pumvisible", 0, 0, f_pumvisible },
{ "py3eval", 1, 1, f_py3eval },
{ "pyeval", 1, 1, f_pyeval },
@@ -7304,8 +7374,8 @@ static struct fst {
{ "resolve", 1, 1, f_resolve },
{ "reverse", 1, 1, f_reverse },
{ "round", 1, 1, f_round },
- { "rpcnotify", 2, 64, f_rpcnotify },
- { "rpcrequest", 2, 64, f_rpcrequest },
+ { "rpcnotify", 2, MAX_FUNC_ARGS, f_rpcnotify },
+ { "rpcrequest", 2, MAX_FUNC_ARGS, f_rpcrequest },
{ "rpcstart", 1, 2, f_rpcstart },
{ "rpcstop", 1, 1, f_rpcstop },
{ "screenattr", 2, 2, f_screenattr },
@@ -7324,10 +7394,10 @@ static struct fst {
{ "setcharsearch", 1, 1, f_setcharsearch },
{ "setcmdpos", 1, 1, f_setcmdpos },
{ "setline", 2, 2, f_setline },
- { "setloclist", 2, 3, f_setloclist },
+ { "setloclist", 2, 4, f_setloclist },
{ "setmatches", 1, 1, f_setmatches },
{ "setpos", 2, 2, f_setpos },
- { "setqflist", 1, 2, f_setqflist },
+ { "setqflist", 1, 3, f_setqflist },
{ "setreg", 2, 3, f_setreg },
{ "settabvar", 3, 3, f_settabvar },
{ "settabwinvar", 4, 4, f_settabwinvar },
@@ -10553,8 +10623,6 @@ static void f_getregtype(typval_T *argvars, typval_T *rettv)
{
char_u *strregname;
int regname;
- char_u buf[NUMBUFLEN + 2];
- long reglen = 0;
if (argvars[0].v_type != VAR_UNKNOWN) {
strregname = get_tv_string_chk(&argvars[0]);
@@ -10571,18 +10639,13 @@ static void f_getregtype(typval_T *argvars, typval_T *rettv)
if (regname == 0)
regname = '"';
- buf[0] = NUL;
- buf[1] = NUL;
- switch (get_reg_type(regname, &reglen)) {
- case MLINE: buf[0] = 'V'; break;
- case MCHAR: buf[0] = 'v'; break;
- case MBLOCK:
- buf[0] = Ctrl_V;
- sprintf((char *)buf + 1, "%" PRId64, (int64_t)(reglen + 1));
- break;
- }
+ colnr_T reglen = 0;
+ char buf[NUMBUFLEN + 2];
+ char_u reg_type = get_reg_type(regname, &reglen);
+ format_reg_type(reg_type, reglen, buf, ARRAY_SIZE(buf));
+
rettv->v_type = VAR_STRING;
- rettv->vval.v_string = vim_strsave(buf);
+ rettv->vval.v_string = (char_u *)xstrdup(buf);
}
/*
@@ -15215,14 +15278,26 @@ static void f_setline(typval_T *argvars, typval_T *rettv)
appended_lines_mark(lcount, added);
}
-
-/*
- * Used by "setqflist()" and "setloclist()" functions
- */
-static void set_qf_ll_list(win_T *wp, typval_T *list_arg, typval_T *action_arg, typval_T *rettv)
+/// Create quickfix/location list from VimL values
+///
+/// Used by `setqflist()` and `setloclist()` functions. Accepts invalid
+/// list_arg, action_arg and title_arg arguments in which case errors out,
+/// including VAR_UNKNOWN parameters.
+///
+/// @param[in,out] wp Window to create location list for. May be NULL in
+/// which case quickfix list will be created.
+/// @param[in] list_arg Quickfix list contents.
+/// @param[in] action_arg Action to perform: append to an existing list,
+/// replace its content or create a new one.
+/// @param[in] title_arg New list title. Defaults to caller function name.
+/// @param[out] rettv Return value: 0 in case of success, -1 otherwise.
+static void set_qf_ll_list(win_T *wp, typval_T *list_arg, typval_T *action_arg,
+ typval_T *title_arg, typval_T *rettv)
+ FUNC_ATTR_NONNULL_ARG(2, 3, 4, 5)
{
char_u *act;
int action = ' ';
+ char_u *title = NULL;
rettv->vval.v_number = -1;
@@ -15231,7 +15306,7 @@ static void set_qf_ll_list(win_T *wp, typval_T *list_arg, typval_T *action_arg,
else {
list_T *l = list_arg->vval.v_list;
- if (action_arg->v_type == VAR_STRING) {
+ if (action_arg->v_type != VAR_UNKNOWN) {
act = get_tv_string_chk(action_arg);
if (act == NULL)
return; /* type error; errmsg already given */
@@ -15239,9 +15314,20 @@ static void set_qf_ll_list(win_T *wp, typval_T *list_arg, typval_T *action_arg,
action = *act;
}
- if (l != NULL && set_errorlist(wp, l, action,
- (char_u *)(wp == NULL ? "setqflist()" : "setloclist()")) == OK)
+ if (title_arg->v_type == VAR_STRING) {
+ title = get_tv_string_chk(title_arg);
+ if (!title) {
+ return; // type error; errmsg already given
+ }
+ }
+
+ if (!title) {
+ title = (char_u*)(wp ? "setloclist()" : "setqflist()");
+ }
+
+ if (l && set_errorlist(wp, l, action, title) == OK) {
rettv->vval.v_number = 0;
+ }
}
}
@@ -15255,8 +15341,9 @@ static void f_setloclist(typval_T *argvars, typval_T *rettv)
rettv->vval.v_number = -1;
win = find_win_by_nr(&argvars[0], NULL);
- if (win != NULL)
- set_qf_ll_list(win, &argvars[1], &argvars[2], rettv);
+ if (win != NULL) {
+ set_qf_ll_list(win, &argvars[1], &argvars[2], &argvars[3], rettv);
+ }
}
/*
@@ -15392,7 +15479,7 @@ static void f_setpos(typval_T *argvars, typval_T *rettv)
*/
static void f_setqflist(typval_T *argvars, typval_T *rettv)
{
- set_qf_ll_list(NULL, &argvars[0], &argvars[1], rettv);
+ set_qf_ll_list(NULL, &argvars[0], &argvars[1], &argvars[2], rettv);
}
/*
@@ -18104,23 +18191,16 @@ void set_vim_var_list(int idx, list_T *val)
}
/// Set Dictionary v: variable to "val".
-void set_vim_var_dict(int idx, dict_T *val) FUNC_ATTR_NONNULL_ALL
+void set_vim_var_dict(int idx, dict_T *val)
{
dict_unref(vimvars[idx].vv_dict);
+ vimvars[idx].vv_dict = val;
- // Set readonly
- int todo = (int)val->dv_hashtab.ht_used;
- for (hashitem_T *hi = val->dv_hashtab.ht_array; todo > 0 ; ++hi) {
- if (HASHITEM_EMPTY(hi)) {
- continue;
- }
-
- --todo;
- HI2DI(hi)->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX;
+ if (val != NULL) {
+ ++val->dv_refcount;
+ // Set readonly
+ dict_set_keys_readonly(val);
}
-
- vimvars[idx].vv_dict = val;
- ++val->dv_refcount;
}
/*
diff --git a/src/nvim/eval.h b/src/nvim/eval.h
index 79a1341d98..f51b0f4921 100644
--- a/src/nvim/eval.h
+++ b/src/nvim/eval.h
@@ -113,7 +113,8 @@ enum {
VV_OPTION_TYPE,
VV_ERRORS,
VV_MSGPACK_TYPES,
- VV_LEN, /* number of v: vars */
+ VV_EVENT,
+ VV_LEN, // number of v: vars
};
/// Maximum number of function arguments
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index 4a423269cc..96a7c8c5af 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -3017,7 +3017,7 @@ void do_sub(exarg_T *eap)
// The number of lines joined is the number of lines in the range
linenr_T joined_lines_count = eap->line2 - eap->line1 + 1
// plus one extra line if not at the end of file.
- + eap->line2 < curbuf->b_ml.ml_line_count ? 1 : 0;
+ + (eap->line2 < curbuf->b_ml.ml_line_count ? 1 : 0);
if (joined_lines_count > 1) {
do_join(joined_lines_count, FALSE, TRUE, FALSE, true);
sub_nsubs = joined_lines_count - 1;
@@ -4877,16 +4877,13 @@ helptags_one (
int fi;
char_u *s;
char_u *fname;
- int dirlen;
int utf8 = MAYBE;
int this_utf8;
int firstline;
int mix = FALSE; /* detected mixed encodings */
- /*
- * Find all *.txt files.
- */
- dirlen = (int)STRLEN(dir);
+ // Find all *.txt files.
+ size_t dirlen = STRLEN(dir);
STRCPY(NameBuff, dir);
STRCAT(NameBuff, "/**/*");
STRCAT(NameBuff, ext);
@@ -4908,7 +4905,7 @@ helptags_one (
*/
STRCPY(NameBuff, dir);
add_pathsep((char *)NameBuff);
- STRCAT(NameBuff, tagfname);
+ STRNCAT(NameBuff, tagfname, sizeof(NameBuff) - dirlen - 2);
fd_tags = mch_fopen((char *)NameBuff, "w");
if (fd_tags == NULL) {
EMSG2(_("E152: Cannot open %s for writing"), NameBuff);
diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c
index 47774f5a99..71ea170e1c 100644
--- a/src/nvim/ex_cmds2.c
+++ b/src/nvim/ex_cmds2.c
@@ -57,23 +57,23 @@ typedef struct scriptitem_S {
char_u *sn_name;
bool file_id_valid;
FileID file_id;
- int sn_prof_on; /* TRUE when script is/was profiled */
- int sn_pr_force; /* forceit: profile functions in this script */
- proftime_T sn_pr_child; /* time set when going into first child */
- int sn_pr_nest; /* nesting for sn_pr_child */
- /* profiling the script as a whole */
- int sn_pr_count; /* nr of times sourced */
- proftime_T sn_pr_total; /* time spent in script + children */
- proftime_T sn_pr_self; /* time spent in script itself */
- proftime_T sn_pr_start; /* time at script start */
- proftime_T sn_pr_children; /* time in children after script start */
- /* profiling the script per line */
- garray_T sn_prl_ga; /* things stored for every line */
- proftime_T sn_prl_start; /* start time for current line */
- proftime_T sn_prl_children; /* time spent in children for this line */
- proftime_T sn_prl_wait; /* wait start time for current line */
- int sn_prl_idx; /* index of line being timed; -1 if none */
- int sn_prl_execed; /* line being timed was executed */
+ bool sn_prof_on; ///< true when script is/was profiled
+ int sn_pr_force; ///< forceit: profile functions in this script
+ proftime_T sn_pr_child; ///< time set when going into first child
+ int sn_pr_nest; ///< nesting for sn_pr_child
+ // profiling the script as a whole
+ int sn_pr_count; ///< nr of times sourced
+ proftime_T sn_pr_total; ///< time spent in script + children
+ proftime_T sn_pr_self; ///< time spent in script itself
+ proftime_T sn_pr_start; ///< time at script start
+ proftime_T sn_pr_children; ///< time in children after script start
+ // profiling the script per line
+ garray_T sn_prl_ga; ///< things stored for every line
+ proftime_T sn_prl_start; ///< start time for current line
+ proftime_T sn_prl_children; ///< time spent in children for this line
+ proftime_T sn_prl_wait; ///< wait start time for current line
+ linenr_T sn_prl_idx; ///< index of line being timed; -1 if none
+ int sn_prl_execed; ///< line being timed was executed
} scriptitem_T;
static garray_T script_items = {0, 0, sizeof(scriptitem_T), 4, NULL};
@@ -81,9 +81,9 @@ static garray_T script_items = {0, 0, sizeof(scriptitem_T), 4, NULL};
/* Struct used in sn_prl_ga for every line of a script. */
typedef struct sn_prl_S {
- int snp_count; /* nr of times line was executed */
- proftime_T sn_prl_total; /* time spent in a line + children */
- proftime_T sn_prl_self; /* time spent in a line itself */
+ int snp_count; ///< nr of times line was executed
+ proftime_T sn_prl_total; ///< time spent in a line + children
+ proftime_T sn_prl_self; ///< time spent in a line itself
} sn_prl_T;
/*
@@ -93,18 +93,18 @@ typedef struct sn_prl_S {
* sourcing can be done recursively.
*/
struct source_cookie {
- FILE *fp; /* opened file for sourcing */
- char_u *nextline; /* if not NULL: line that was read ahead */
- int finished; /* ":finish" used */
+ FILE *fp; ///< opened file for sourcing
+ char_u *nextline; ///< if not NULL: line that was read ahead
+ int finished; ///< ":finish" used
#if defined(USE_CRNL)
- int fileformat; /* EOL_UNKNOWN, EOL_UNIX or EOL_DOS */
- int error; /* TRUE if LF found after CR-LF */
+ int fileformat; ///< EOL_UNKNOWN, EOL_UNIX or EOL_DOS
+ int error; ///< TRUE if LF found after CR-LF
#endif
- linenr_T breakpoint; /* next line with breakpoint or zero */
- char_u *fname; /* name of sourced file */
- int dbg_tick; /* debug_tick when breakpoint was set */
- int level; /* top nesting level of sourced file */
- vimconv_T conv; /* type of conversion */
+ linenr_T breakpoint; ///< next line with breakpoint or zero
+ char_u *fname; ///< name of sourced file
+ int dbg_tick; ///< debug_tick when breakpoint was set
+ int level; ///< top nesting level of sourced file
+ vimconv_T conv; ///< type of conversion
};
# define PRL_ITEM(si, idx) (((sn_prl_T *)(si)->sn_prl_ga.ga_data)[(idx)])
@@ -272,7 +272,7 @@ void do_debug(char_u *cmd)
xfree(cmdline);
}
- lines_left = Rows - 1;
+ lines_left = (int)(Rows - 1);
}
xfree(cmdline);
@@ -281,7 +281,7 @@ void do_debug(char_u *cmd)
redraw_all_later(NOT_VALID);
need_wait_return = FALSE;
msg_scroll = save_msg_scroll;
- lines_left = Rows - 1;
+ lines_left = (int)(Rows - 1);
State = save_State;
did_emsg = save_did_emsg;
cmd_silent = save_cmd_silent;
@@ -392,12 +392,12 @@ int dbg_check_skipped(exarg_T *eap)
* This is a grow-array of structs.
*/
struct debuggy {
- int dbg_nr; /* breakpoint number */
- int dbg_type; /* DBG_FUNC or DBG_FILE */
- char_u *dbg_name; /* function or file name */
- regprog_T *dbg_prog; /* regexp program */
- linenr_T dbg_lnum; /* line number in function or file */
- int dbg_forceit; /* ! used */
+ int dbg_nr; ///< breakpoint number
+ int dbg_type; ///< DBG_FUNC or DBG_FILE
+ char_u *dbg_name; ///< function or file name
+ regprog_T *dbg_prog; ///< regexp program
+ linenr_T dbg_lnum; ///< line number in function or file
+ int dbg_forceit; ///< ! used
};
static garray_T dbg_breakp = {0, 0, sizeof(struct debuggy), 4, NULL};
@@ -563,13 +563,14 @@ void ex_breakdel(exarg_T *eap)
}
if (ascii_isdigit(*eap->arg)) {
- /* ":breakdel {nr}" */
- nr = atol((char *)eap->arg);
- for (int i = 0; i < gap->ga_len; ++i)
+ // ":breakdel {nr}"
+ nr = atoi((char *)eap->arg);
+ for (int i = 0; i < gap->ga_len; ++i) {
if (DEBUGGY(gap, i).dbg_nr == nr) {
todel = i;
break;
}
+ }
} else if (*eap->arg == '*') {
todel = 0;
del_all = TRUE;
@@ -602,11 +603,13 @@ void ex_breakdel(exarg_T *eap)
--gap->ga_len;
if (todel < gap->ga_len)
memmove(&DEBUGGY(gap, todel), &DEBUGGY(gap, todel + 1),
- (gap->ga_len - todel) * sizeof(struct debuggy));
- if (eap->cmdidx == CMD_breakdel)
+ (size_t)(gap->ga_len - todel) * sizeof(struct debuggy));
+ if (eap->cmdidx == CMD_breakdel) {
++debug_tick;
- if (!del_all)
+ }
+ if (!del_all) {
break;
+ }
}
/* If all breakpoints were removed clear the array. */
@@ -810,8 +813,8 @@ void ex_pydo3(exarg_T *eap)
/* Command line expansion for :profile. */
static enum {
- PEXP_SUBCMD, /* expand :profile sub-commands */
- PEXP_FUNC /* expand :profile func {funcname} */
+ PEXP_SUBCMD, ///< expand :profile sub-commands
+ PEXP_FUNC ///< expand :profile func {funcname}
} pexpand_what;
static char *pexpand_cmds[] = {
@@ -892,7 +895,7 @@ static void profile_reset(void)
for (int id = 1; id <= script_items.ga_len; id++) {
scriptitem_T *si = &SCRIPT_ITEM(id);
if (si->sn_prof_on) {
- si->sn_prof_on = 0;
+ si->sn_prof_on = false;
si->sn_pr_force = 0;
si->sn_pr_child = profile_zero();
si->sn_pr_nest = 0;
@@ -949,7 +952,7 @@ static void profile_init(scriptitem_T *si)
ga_init(&si->sn_prl_ga, sizeof(sn_prl_T), 100);
si->sn_prl_idx = -1;
- si->sn_prof_on = TRUE;
+ si->sn_prof_on = true;
si->sn_pr_nest = 0;
}
@@ -1255,7 +1258,7 @@ check_changed_any (
int save;
int i;
int bufnum = 0;
- int bufcount = 0;
+ size_t bufcount = 0;
int *bufnrs;
FOR_ALL_BUFFERS(buf) {
@@ -1520,7 +1523,7 @@ do_arglist (
didone = TRUE;
xfree(ARGLIST[match].ae_fname);
memmove(ARGLIST + match, ARGLIST + match + 1,
- (ARGCOUNT - match - 1) * sizeof(aentry_T));
+ (size_t)(ARGCOUNT - match - 1) * sizeof(aentry_T));
--ALIST(curwin)->al_ga.ga_len;
if (curwin->w_arg_idx > match)
--curwin->w_arg_idx;
@@ -1537,9 +1540,7 @@ do_arglist (
int i = expand_wildcards(new_ga.ga_len, (char_u **)new_ga.ga_data,
&exp_count, &exp_files, EW_DIR|EW_FILE|EW_ADDSLASH|EW_NOTFOUND);
ga_clear(&new_ga);
- if (i == FAIL)
- return FAIL;
- if (exp_count == 0) {
+ if (i == FAIL || exp_count == 0) {
EMSG(_(e_nomatch));
return FAIL;
}
@@ -1702,10 +1703,11 @@ void ex_argument(exarg_T *eap)
{
int i;
- if (eap->addr_count > 0)
- i = eap->line2 - 1;
- else
+ if (eap->addr_count > 0) {
+ i = (int)eap->line2 - 1;
+ } else {
i = curwin->w_arg_idx;
+ }
do_argfile(eap, i);
}
@@ -1844,22 +1846,25 @@ void ex_argadd(exarg_T *eap)
void ex_argdelete(exarg_T *eap)
{
if (eap->addr_count > 0) {
- /* ":1,4argdel": Delete all arguments in the range. */
- if (eap->line2 > ARGCOUNT)
+ // ":1,4argdel": Delete all arguments in the range.
+ if (eap->line2 > ARGCOUNT) {
eap->line2 = ARGCOUNT;
- int n = eap->line2 - eap->line1 + 1;
- if (*eap->arg != NUL || n <= 0)
+ }
+ linenr_T n = eap->line2 - eap->line1 + 1;
+ if (*eap->arg != NUL || n <= 0) {
EMSG(_(e_invarg));
- else {
- for (int i = eap->line1; i <= eap->line2; ++i)
+ } else {
+ for (linenr_T i = eap->line1; i <= eap->line2; ++i) {
xfree(ARGLIST[i - 1].ae_fname);
+ }
memmove(ARGLIST + eap->line1 - 1, ARGLIST + eap->line2,
- (size_t)((ARGCOUNT - eap->line2) * sizeof(aentry_T)));
- ALIST(curwin)->al_ga.ga_len -= n;
- if (curwin->w_arg_idx >= eap->line2)
- curwin->w_arg_idx -= n;
- else if (curwin->w_arg_idx > eap->line1)
- curwin->w_arg_idx = eap->line1;
+ (size_t)(ARGCOUNT - eap->line2) * sizeof(aentry_T));
+ ALIST(curwin)->al_ga.ga_len -= (int)n;
+ if (curwin->w_arg_idx >= eap->line2) {
+ curwin->w_arg_idx -= (int)n;
+ } else if (curwin->w_arg_idx > eap->line1) {
+ curwin->w_arg_idx = (int)eap->line1;
+ }
}
} else if (*eap->arg == NUL)
EMSG(_(e_argreq));
@@ -1909,7 +1914,7 @@ void ex_listdo(exarg_T *eap)
}
break;
case CMD_argdo:
- i = eap->line1 - 1;
+ i = (int)eap->line1 - 1;
break;
default:
break;
@@ -1942,10 +1947,11 @@ void ex_listdo(exarg_T *eap)
ex_cc(eap);
buf = curbuf;
- i = eap->line1 - 1;
+ i = (int)eap->line1 - 1;
if (eap->addr_count <= 0) {
// Default to all quickfix/location list entries.
- eap->line2 = qf_size;
+ assert(qf_size < MAXLNUM);
+ eap->line2 = (linenr_T)qf_size;
}
}
} else {
@@ -2098,7 +2104,7 @@ alist_add_list (
after = ARGCOUNT;
if (after < ARGCOUNT)
memmove(&(ARGLIST[after + count]), &(ARGLIST[after]),
- (ARGCOUNT - after) * sizeof(aentry_T));
+ (size_t)(ARGCOUNT - after) * sizeof(aentry_T));
for (int i = 0; i < count; ++i) {
ARGLIST[after + i].ae_fname = files[i];
ARGLIST[after + i].ae_fnum = buflist_add(files[i], BLN_LISTED);
@@ -2571,7 +2577,7 @@ do_source (
while (script_items.ga_len < current_SID) {
++script_items.ga_len;
SCRIPT_ITEM(script_items.ga_len).sn_name = NULL;
- SCRIPT_ITEM(script_items.ga_len).sn_prof_on = FALSE;
+ SCRIPT_ITEM(script_items.ga_len).sn_prof_on = false;
}
si = &SCRIPT_ITEM(current_SID);
si->sn_name = fname_exp;
@@ -3386,8 +3392,8 @@ static void script_host_execute(char *name, exarg_T *eap)
// script
list_append_string(args, script ? script : eap->arg, -1);
// current range
- list_append_number(args, eap->line1);
- list_append_number(args, eap->line2);
+ list_append_number(args, (int)eap->line1);
+ list_append_number(args, (int)eap->line2);
(void)eval_call_provider(name, "execute", args);
}
@@ -3403,16 +3409,16 @@ static void script_host_execute_file(char *name, exarg_T *eap)
// filename
list_append_string(args, buffer, -1);
// current range
- list_append_number(args, eap->line1);
- list_append_number(args, eap->line2);
+ list_append_number(args, (int)eap->line1);
+ list_append_number(args, (int)eap->line2);
(void)eval_call_provider(name, "execute_file", args);
}
static void script_host_do_range(char *name, exarg_T *eap)
{
list_T *args = list_alloc();
- list_append_number(args, eap->line1);
- list_append_number(args, eap->line2);
+ list_append_number(args, (int)eap->line1);
+ list_append_number(args, (int)eap->line2);
list_append_string(args, eap->arg, -1);
(void)eval_call_provider(name, "do_range", args);
}
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index 2d17b31f0f..a7e98e7f04 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -4320,7 +4320,7 @@ static void ex_unmap(exarg_T *eap)
*/
static void ex_mapclear(exarg_T *eap)
{
- map_clear(eap->cmd, eap->arg, eap->forceit, FALSE);
+ map_clear_mode(eap->cmd, eap->arg, eap->forceit, false);
}
/*
@@ -4328,7 +4328,7 @@ static void ex_mapclear(exarg_T *eap)
*/
static void ex_abclear(exarg_T *eap)
{
- map_clear(eap->cmd, eap->arg, TRUE, TRUE);
+ map_clear_mode(eap->cmd, eap->arg, true, true);
}
static void ex_autocmd(exarg_T *eap)
@@ -6290,10 +6290,8 @@ void ex_splitview(exarg_T *eap)
if (eap->cmdidx == CMD_tabedit
|| eap->cmdidx == CMD_tabfind
|| eap->cmdidx == CMD_tabnew) {
- if (win_new_tabpage(cmdmod.tab != 0 ? cmdmod.tab
- : eap->addr_count == 0 ? 0
- : (int)eap->line2 + 1) != FAIL) {
- apply_autocmds(EVENT_TABNEW, eap->arg, eap->arg, FALSE, curbuf);
+ if (win_new_tabpage(cmdmod.tab != 0 ? cmdmod.tab : eap->addr_count == 0
+ ? 0 : (int)eap->line2 + 1, eap->arg) != FAIL) {
do_exedit(eap, old_curwin);
apply_autocmds(EVENT_TABNEWENTERED, NULL, NULL, FALSE, curbuf);
diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c
index 73b3de0b5d..437495faa4 100644
--- a/src/nvim/getchar.c
+++ b/src/nvim/getchar.c
@@ -2915,9 +2915,9 @@ do_map (
did_it = TRUE;
}
}
- if (mp->m_mode == 0) { /* entry can be deleted */
- map_free(mpp);
- continue; /* continue with *mpp */
+ if (mp->m_mode == 0) { // entry can be deleted
+ mapblock_free(mpp);
+ continue; // continue with *mpp
}
/*
@@ -3012,7 +3012,7 @@ theend:
* Delete one entry from the abbrlist or maphash[].
* "mpp" is a pointer to the m_next field of the PREVIOUS entry!
*/
-static void map_free(mapblock_T **mpp)
+static void mapblock_free(mapblock_T **mpp)
{
mapblock_T *mp;
@@ -3080,7 +3080,7 @@ int get_map_mode(char_u **cmdp, int forceit)
* Clear all mappings or abbreviations.
* 'abbr' should be FALSE for mappings, TRUE for abbreviations.
*/
-void map_clear(char_u *cmdp, char_u *arg, int forceit, int abbr)
+void map_clear_mode(char_u *cmdp, char_u *arg, int forceit, int abbr)
{
int mode;
int local;
@@ -3132,8 +3132,8 @@ map_clear_int (
mp = *mpp;
if (mp->m_mode & mode) {
mp->m_mode &= ~mode;
- if (mp->m_mode == 0) { /* entry can be deleted */
- map_free(mpp);
+ if (mp->m_mode == 0) { // entry can be deleted
+ mapblock_free(mpp);
continue;
}
/*
diff --git a/src/nvim/if_cscope.c b/src/nvim/if_cscope.c
index d236501b3f..7169a1d963 100644
--- a/src/nvim/if_cscope.c
+++ b/src/nvim/if_cscope.c
@@ -1632,77 +1632,79 @@ static char *cs_pathcomponents(char *path)
return s;
}
-/*
- * PRIVATE: cs_print_tags_priv
- *
- * called from cs_manage_matches()
- */
+/// Print cscope output that was converted into ctags style entries.
+///
+/// Only called from cs_manage_matches().
+///
+/// @param matches Array of cscope lines in ctags style. Every entry was
+// produced with a format string of the form
+// "%s\t%s\t%s;\"\t%s" or
+// "%s\t%s\t%s;\""
+// by cs_make_vim_style_matches().
+/// @param cntxts Context for matches.
+/// @param num_matches Number of entries in matches/cntxts, always greater 0.
static void cs_print_tags_priv(char **matches, char **cntxts,
- size_t num_matches)
+ size_t num_matches) FUNC_ATTR_NONNULL_ALL
{
- char *ptag;
- char *fname, *lno, *extra, *tbuf;
- size_t num;
- char *globalcntx = "GLOBAL";
- char *context;
- char *cstag_msg = _("Cscope tag: %s");
+ char *globalcntx = "GLOBAL";
+ char *cstag_msg = _("Cscope tag: %s");
- assert (num_matches > 0);
+ assert(num_matches > 0);
+ assert(strcnt(matches[0], '\t') >= 2);
- tbuf = xmalloc(strlen(matches[0]) + 1);
+ char *ptag = matches[0];
+ char *ptag_end = strchr(ptag, '\t');
+ assert(ptag_end >= ptag);
+ // NUL terminate tag string in matches[0].
+ *ptag_end = NUL;
- strcpy(tbuf, matches[0]);
- ptag = strtok(tbuf, "\t");
-
- size_t newsize = strlen(cstag_msg) + strlen(ptag);
+ // The "%s" in cstag_msg won't appear in the result string, so we don't need
+ // extra memory for terminating NUL.
+ size_t newsize = strlen(cstag_msg) + (size_t)(ptag_end - ptag);
char *buf = xmalloc(newsize);
size_t bufsize = newsize; // Track available bufsize
- (void)sprintf(buf, cstag_msg, ptag);
+ (void)snprintf(buf, bufsize, cstag_msg, ptag);
MSG_PUTS_ATTR(buf, hl_attr(HLF_T));
+ msg_clr_eos();
- xfree(tbuf);
+ // restore matches[0]
+ *ptag_end = '\t';
- MSG_PUTS_ATTR(_("\n # line"), hl_attr(HLF_T)); /* strlen is 7 */
+ // Column headers for match number, line number and filename.
+ MSG_PUTS_ATTR(_("\n # line"), hl_attr(HLF_T));
msg_advance(msg_col + 2);
MSG_PUTS_ATTR(_("filename / context / line\n"), hl_attr(HLF_T));
- num = 1;
for (size_t i = 0; i < num_matches; i++) {
- size_t idx = i;
-
- /* if we really wanted to, we could avoid this malloc and strcpy
- * by parsing matches[i] on the fly and placing stuff into buf
- * directly, but that's too much of a hassle
- */
- tbuf = xmalloc(strlen(matches[idx]) + 1);
- (void)strcpy(tbuf, matches[idx]);
-
- if (strtok(tbuf, (const char *)"\t") == NULL)
- continue;
- if ((fname = strtok(NULL, (const char *)"\t")) == NULL)
- continue;
- if ((lno = strtok(NULL, (const char *)"\t")) == NULL)
- continue;
- extra = strtok(NULL, (const char *)"\t");
-
- lno[strlen(lno)-2] = '\0'; /* ignore ;" at the end */
+ assert(strcnt(matches[i], '\t') >= 2);
+
+ // Parse filename, line number and optional part.
+ char *fname = strchr(matches[i], '\t') + 1;
+ char *fname_end = strchr(fname, '\t');
+ // Replace second '\t' in matches[i] with NUL to terminate fname.
+ *fname_end = NUL;
+
+ char *lno = fname_end + 1;
+ char *extra = xstrchrnul(lno, '\t');
+ // Ignore ;" at the end of lno.
+ char *lno_end = extra - 2;
+ *lno_end = NUL;
+ // Do we have an optional part?
+ extra = *extra ? extra + 1 : NULL;
const char *csfmt_str = "%4zu %6s ";
- /* hopefully 'num' (num of matches) will be less than 10^16 */
- newsize = strlen(csfmt_str) + 16 + strlen(lno);
+ // hopefully num_matches will be less than 10^16
+ newsize = strlen(csfmt_str) + 16 + (size_t)(lno_end - lno);
if (bufsize < newsize) {
buf = xrealloc(buf, newsize);
bufsize = newsize;
}
- (void)sprintf(buf, csfmt_str, num, lno);
+ (void)snprintf(buf, bufsize, csfmt_str, i + 1, lno);
MSG_PUTS_ATTR(buf, hl_attr(HLF_CM));
MSG_PUTS_LONG_ATTR(cs_pathcomponents(fname), hl_attr(HLF_CM));
- /* compute the required space for the context */
- if (cntxts[idx] != NULL)
- context = cntxts[idx];
- else
- context = globalcntx;
+ // compute the required space for the context
+ char *context = cntxts[i] ? cntxts[i] : globalcntx;
const char *cntxformat = " <<%s>>";
// '%s' won't appear in result string, so:
@@ -1713,11 +1715,13 @@ static void cs_print_tags_priv(char **matches, char **cntxts,
buf = xrealloc(buf, newsize);
bufsize = newsize;
}
- (void)sprintf(buf, cntxformat, context);
+ int buf_len = snprintf(buf, bufsize, cntxformat, context);
+ assert(buf_len >= 0);
- /* print the context only if it fits on the same line */
- if (msg_col + (int)strlen(buf) >= (int)Columns)
+ // Print the context only if it fits on the same line.
+ if (msg_col + buf_len >= (int)Columns) {
msg_putchar('\n');
+ }
msg_advance(12);
MSG_PUTS_LONG(buf);
msg_putchar('\n');
@@ -1726,23 +1730,23 @@ static void cs_print_tags_priv(char **matches, char **cntxts,
MSG_PUTS_LONG(extra);
}
- xfree(tbuf); /* only after printing extra due to strtok use */
+ // restore matches[i]
+ *fname_end = '\t';
+ *lno_end = ';';
- if (msg_col)
+ if (msg_col) {
msg_putchar('\n');
+ }
os_breakcheck();
if (got_int) {
- got_int = FALSE; /* don't print any more matches */
+ got_int = false; // don't print any more matches
break;
}
-
- num++;
- } /* for all matches */
+ }
xfree(buf);
-} /* cs_print_tags_priv */
-
+}
/*
* PRIVATE: cs_read_prompt
diff --git a/src/nvim/lib/khash.h b/src/nvim/lib/khash.h
index 56be29d14c..8287cb14da 100644
--- a/src/nvim/lib/khash.h
+++ b/src/nvim/lib/khash.h
@@ -184,7 +184,7 @@ typedef khint_t khiter_t;
#define kfree(P) xfree(P)
#endif
-static const double __ac_HASH_UPPER = 0.77;
+#define __ac_HASH_UPPER 0.77
#define __KHASH_TYPE(name, khkey_t, khval_t) \
typedef struct { \
diff --git a/src/nvim/lib/kvec.h b/src/nvim/lib/kvec.h
index 0466cb229c..53ecf232c6 100644
--- a/src/nvim/lib/kvec.h
+++ b/src/nvim/lib/kvec.h
@@ -77,10 +77,10 @@ int main() {
(v).items[(v).size++] = (x); \
} while (0)
-#define kv_pushp(type, v) (((v).size == (v).capacity)? \
+#define kv_pushp(type, v) ((((v).size == (v).capacity)? \
((v).capacity = ((v).capacity? (v).capacity<<1 : 8), \
(v).items = (type*)xrealloc((v).items, sizeof(type) * (v).capacity), 0) \
- : 0), ((v).items + ((v).size++))
+ : 0), ((v).items + ((v).size++)))
#define kv_a(type, v, i) (((v).capacity <= (size_t)(i)? \
((v).capacity = (v).size = (i) + 1, kv_roundup32((v).capacity), \
diff --git a/src/nvim/main.c b/src/nvim/main.c
index d3cdfe3edf..5b5c8a22aa 100644
--- a/src/nvim/main.c
+++ b/src/nvim/main.c
@@ -317,14 +317,16 @@ int main(int argc, char **argv)
}
// open terminals when opening files that start with term://
- do_cmdline_cmd("autocmd BufReadCmd term://* "
+#define PROTO "term://"
+ do_cmdline_cmd("autocmd BufReadCmd " PROTO "* nested "
":call termopen( "
// Capture the command string
"matchstr(expand(\"<amatch>\"), "
- "'\\c\\mterm://\\%(.\\{-}//\\%(\\d\\+:\\)\\?\\)\\?\\zs.*'), "
+ "'\\c\\m" PROTO "\\%(.\\{-}//\\%(\\d\\+:\\)\\?\\)\\?\\zs.*'), "
// capture the working directory
"{'cwd': get(matchlist(expand(\"<amatch>\"), "
- "'\\c\\mterm://\\(.\\{-}\\)//'), 1, '')})");
+ "'\\c\\m" PROTO "\\(.\\{-}\\)//'), 1, '')})");
+#undef PROTO
/* Execute --cmd arguments. */
exe_pre_commands(&params);
@@ -657,6 +659,9 @@ static void init_locale(void)
setlocale(LC_NUMERIC, "C");
# endif
+# ifdef LOCALE_INSTALL_DIR // gnu/linux standard: $prefix/share/locale
+ bindtextdomain(PROJECT_NAME, LOCALE_INSTALL_DIR);
+# else // old vim style: $runtime/lang
{
char_u *p;
@@ -665,11 +670,12 @@ static void init_locale(void)
p = (char_u *)vim_getenv("VIMRUNTIME");
if (p != NULL && *p != NUL) {
vim_snprintf((char *)NameBuff, MAXPATHL, "%s/lang", p);
- bindtextdomain(VIMPACKAGE, (char *)NameBuff);
+ bindtextdomain(PROJECT_NAME, (char *)NameBuff);
}
xfree(p);
- textdomain(VIMPACKAGE);
}
+# endif
+ textdomain(PROJECT_NAME);
TIME_MSG("locale set");
}
#endif
diff --git a/src/nvim/map.c b/src/nvim/map.c
index ed7bda4cce..d4262ae9a8 100644
--- a/src/nvim/map.c
+++ b/src/nvim/map.c
@@ -18,6 +18,9 @@
#define uint32_t_eq kh_int_hash_equal
#define int_hash kh_int_hash_func
#define int_eq kh_int_hash_equal
+#define linenr_T_hash kh_int_hash_func
+#define linenr_T_eq kh_int_hash_equal
+
#if defined(ARCH_64)
#define ptr_t_hash(key) uint64_t_hash((uint64_t)key)
@@ -78,6 +81,25 @@
return rv; \
} \
\
+ U *map_##T##_##U##_ref(Map(T, U) *map, T key, bool put) \
+ { \
+ int ret; \
+ khiter_t k; \
+ if (put) { \
+ k = kh_put(T##_##U##_map, map->table, key, &ret); \
+ if (ret) { \
+ kh_val(map->table, k) = INITIALIZER(T, U); \
+ } \
+ } else { \
+ k = kh_get(T##_##U##_map, map->table, key); \
+ if (k == kh_end(map->table)) { \
+ return NULL; \
+ } \
+ } \
+ \
+ return &kh_val(map->table, k); \
+ } \
+ \
U map_##T##_##U##_del(Map(T, U) *map, T key) \
{ \
U rv = INITIALIZER(T, U); \
@@ -118,3 +140,5 @@ MAP_IMPL(ptr_t, ptr_t, DEFAULT_INITIALIZER)
MAP_IMPL(uint64_t, ptr_t, DEFAULT_INITIALIZER)
#define MSGPACK_HANDLER_INITIALIZER {.fn = NULL, .async = false}
MAP_IMPL(String, MsgpackRpcRequestHandler, MSGPACK_HANDLER_INITIALIZER)
+#define KVEC_INITIALIZER { .size = 0, .capacity = 0, .items = NULL }
+MAP_IMPL(linenr_T, bufhl_vec_T, KVEC_INITIALIZER)
diff --git a/src/nvim/map.h b/src/nvim/map.h
index c0e2ca3aac..e90cc360ce 100644
--- a/src/nvim/map.h
+++ b/src/nvim/map.h
@@ -6,6 +6,7 @@
#include "nvim/map_defs.h"
#include "nvim/api/private/defs.h"
#include "nvim/msgpack_rpc/defs.h"
+#include "nvim/bufhl_defs.h"
#define MAP_DECLS(T, U) \
KHASH_DECLARE(T##_##U##_map, T, U) \
@@ -19,6 +20,7 @@
U map_##T##_##U##_get(Map(T, U) *map, T key); \
bool map_##T##_##U##_has(Map(T, U) *map, T key); \
U map_##T##_##U##_put(Map(T, U) *map, T key, U value); \
+ U *map_##T##_##U##_ref(Map(T, U) *map, T key, bool put); \
U map_##T##_##U##_del(Map(T, U) *map, T key); \
void map_##T##_##U##_clear(Map(T, U) *map);
@@ -28,12 +30,14 @@ MAP_DECLS(cstr_t, ptr_t)
MAP_DECLS(ptr_t, ptr_t)
MAP_DECLS(uint64_t, ptr_t)
MAP_DECLS(String, MsgpackRpcRequestHandler)
+MAP_DECLS(linenr_T, bufhl_vec_T)
#define map_new(T, U) map_##T##_##U##_new
#define map_free(T, U) map_##T##_##U##_free
#define map_get(T, U) map_##T##_##U##_get
#define map_has(T, U) map_##T##_##U##_has
#define map_put(T, U) map_##T##_##U##_put
+#define map_ref(T, U) map_##T##_##U##_ref
#define map_del(T, U) map_##T##_##U##_del
#define map_clear(T, U) map_##T##_##U##_clear
diff --git a/src/nvim/mark.c b/src/nvim/mark.c
index e2f212340c..fe802e48ba 100644
--- a/src/nvim/mark.c
+++ b/src/nvim/mark.c
@@ -922,6 +922,7 @@ void mark_adjust(linenr_T line1, linenr_T line2, long amount, long amount_after)
}
sign_mark_adjust(line1, line2, amount, amount_after);
+ bufhl_mark_adjust(curbuf, line1, line2, amount, amount_after);
}
/* previous context mark */
diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c
index fdd83f9dac..f0a249919f 100644
--- a/src/nvim/mbyte.c
+++ b/src/nvim/mbyte.c
@@ -571,8 +571,8 @@ char_u * mb_init(void)
#ifdef HAVE_WORKING_LIBINTL
/* GNU gettext 0.10.37 supports this feature: set the codeset used for
* translated messages independently from the current locale. */
- (void)bind_textdomain_codeset(VIMPACKAGE,
- enc_utf8 ? "utf-8" : (char *)p_enc);
+ (void)bind_textdomain_codeset(PROJECT_NAME,
+ enc_utf8 ? "utf-8" : (char *)p_enc);
#endif
diff --git a/src/nvim/misc1.c b/src/nvim/misc1.c
index 6c969a43fc..db303fd54a 100644
--- a/src/nvim/misc1.c
+++ b/src/nvim/misc1.c
@@ -1985,13 +1985,13 @@ changed_lines (
changed_common(lnum, col, lnume, xtra);
}
-static void
-changed_lines_buf (
- buf_T *buf,
- linenr_T lnum, /* first line with change */
- linenr_T lnume, /* line below last changed line */
- long xtra /* number of extra lines (negative when deleting) */
-)
+/// Mark line range in buffer as changed.
+///
+/// @param buf the buffer where lines were changed
+/// @param lnum first line with change
+/// @param lnume line below last changed line
+/// @param xtra number of extra lines (negative when deleting)
+void changed_lines_buf(buf_T *buf, linenr_T lnum, linenr_T lnume, long xtra)
{
if (buf->b_mod_set) {
/* find the maximum area that must be redisplayed */
diff --git a/src/nvim/msgpack_rpc/defs.h b/src/nvim/msgpack_rpc/defs.h
index d97cf28ca1..5611636d4f 100644
--- a/src/nvim/msgpack_rpc/defs.h
+++ b/src/nvim/msgpack_rpc/defs.h
@@ -1,8 +1,6 @@
#ifndef NVIM_MSGPACK_RPC_DEFS_H
#define NVIM_MSGPACK_RPC_DEFS_H
-#include <msgpack.h>
-
/// The rpc_method_handlers table, used in msgpack_rpc_dispatch(), stores
/// functions of this type.
@@ -24,22 +22,6 @@ void msgpack_rpc_add_method_handler(String method,
void msgpack_rpc_init_function_metadata(Dictionary *metadata);
-/// Dispatches to the actual API function after basic payload validation by
-/// `msgpack_rpc_call`. It is responsible for validating/converting arguments
-/// to C types, and converting the return value back to msgpack types.
-/// The implementation is generated at compile time with metadata extracted
-/// from the api/*.h headers,
-///
-/// @param channel_id The channel id
-/// @param method_id The method id
-/// @param req The parsed request object
-/// @param error Pointer to error structure
-/// @return Some object
-Object msgpack_rpc_dispatch(uint64_t channel_id,
- msgpack_object *req,
- Error *error)
- FUNC_ATTR_NONNULL_ARG(2) FUNC_ATTR_NONNULL_ARG(3);
-
MsgpackRpcRequestHandler msgpack_rpc_get_handler_for(const char *name,
size_t name_len)
FUNC_ATTR_NONNULL_ARG(1);
diff --git a/src/nvim/normal.c b/src/nvim/normal.c
index e064d34e09..f5fb47568e 100644
--- a/src/nvim/normal.c
+++ b/src/nvim/normal.c
@@ -2344,9 +2344,12 @@ do_mouse (
if (mouse_row == 0 && firstwin->w_winrow > 0) {
if (is_drag) {
if (in_tab_line) {
- tabpage_move(tab_page_click_defs[mouse_col].type == kStlClickTabClose
- ? 9999
- : tab_page_click_defs[mouse_col].tabnr - 1);
+ if (tab_page_click_defs[mouse_col].type == kStlClickTabClose) {
+ tabpage_move(9999);
+ } else {
+ int tabnr = tab_page_click_defs[mouse_col].tabnr;
+ tabpage_move(tabnr < tabpage_index(curtab) ? tabnr - 1 : tabnr);
+ }
}
return false;
}
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index b1adc85e1d..8c7805ba04 100644
--- a/src/nvim/ops.c
+++ b/src/nvim/ops.c
@@ -19,6 +19,7 @@
#include "nvim/ex_cmds.h"
#include "nvim/ex_cmds2.h"
#include "nvim/ex_getln.h"
+#include "nvim/fileio.h"
#include "nvim/fold.h"
#include "nvim/getchar.h"
#include "nvim/indent.h"
@@ -835,12 +836,13 @@ int do_record(int c)
yankreg_T *old_y_previous;
int retval;
- if (Recording == FALSE) { /* start recording */
- /* registers 0-9, a-z and " are allowed */
- if (c < 0 || (!ASCII_ISALNUM(c) && c != '"'))
+ if (Recording == false) {
+ // start recording
+ // registers 0-9, a-z and " are allowed
+ if (c < 0 || (!ASCII_ISALNUM(c) && c != '"')) {
retval = FAIL;
- else {
- Recording = TRUE;
+ } else {
+ Recording = c;
showmode();
regname = c;
retval = OK;
@@ -1409,8 +1411,9 @@ int op_delete(oparg_T *oap)
op_yank_reg(oap, false, reg, false);
}
- if(oap->regname == 0) {
+ if (oap->regname == 0) {
set_clipboard(0, reg);
+ yank_do_autocmd(oap, reg);
}
}
@@ -2309,6 +2312,8 @@ bool op_yank(oparg_T *oap, bool message)
yankreg_T *reg = get_yank_register(oap->regname, YREG_YANK);
op_yank_reg(oap, message, reg, is_append_register(oap->regname));
set_clipboard(oap->regname, reg);
+ yank_do_autocmd(oap, reg);
+
return true;
}
@@ -2524,6 +2529,58 @@ static void yank_copy_line(yankreg_T *reg, struct block_def *bd, long y_idx)
*pnew = NUL;
}
+/// Execute autocommands for TextYankPost.
+///
+/// @param oap Operator arguments.
+/// @param reg The yank register used.
+static void yank_do_autocmd(oparg_T *oap, yankreg_T *reg)
+ FUNC_ATTR_NONNULL_ALL
+{
+ static bool recursive = false;
+
+ if (recursive || !has_event(EVENT_TEXTYANKPOST)) {
+ // No autocommand was defined
+ // or we yanked from this autocommand.
+ return;
+ }
+
+ recursive = true;
+
+ // set v:event to a dictionary with information about the yank
+ dict_T *dict = get_vim_var_dict(VV_EVENT);
+
+ // the yanked text
+ list_T *list = list_alloc();
+ for (linenr_T i = 0; i < reg->y_size; i++) {
+ list_append_string(list, reg->y_array[i], -1);
+ }
+ list->lv_lock = VAR_FIXED;
+ dict_add_list(dict, "regcontents", list);
+
+ // the register type
+ char buf[NUMBUFLEN+2];
+ format_reg_type(reg->y_type, reg->y_width, buf, ARRAY_SIZE(buf));
+ dict_add_nr_str(dict, "regtype", 0, (char_u *)buf);
+
+ // name of requested register or the empty string for an unnamed operation.
+ buf[0] = (char)oap->regname;
+ buf[1] = NUL;
+ dict_add_nr_str(dict, "regname", 0, (char_u *)buf);
+
+ // kind of operation (yank/delete/change)
+ buf[0] = get_op_char(oap->op_type);
+ buf[1] = NUL;
+ dict_add_nr_str(dict, "operator", 0, (char_u *)buf);
+
+ dict_set_keys_readonly(dict);
+ textlock++;
+ apply_autocmds(EVENT_TEXTYANKPOST, NULL, NULL, false, curbuf);
+ textlock--;
+ dict_clear(dict);
+
+ recursive = false;
+}
+
/*
* Put contents of register "regname" into the text.
@@ -4631,7 +4688,7 @@ theend:
* Used for getregtype()
* Returns MAUTO for error.
*/
-char_u get_reg_type(int regname, long *reglen)
+char_u get_reg_type(int regname, colnr_T *reg_width)
{
switch (regname) {
case '%': /* file name */
@@ -4654,13 +4711,45 @@ char_u get_reg_type(int regname, long *reglen)
yankreg_T *reg = get_yank_register(regname, YREG_PASTE);
if (reg->y_array != NULL) {
- if (reglen != NULL && reg->y_type == MBLOCK)
- *reglen = reg->y_width;
+ if (reg_width != NULL && reg->y_type == MBLOCK) {
+ *reg_width = reg->y_width;
+ }
return reg->y_type;
}
return MAUTO;
}
+/// Format the register type as a string.
+///
+/// @param reg_type The register type.
+/// @param reg_width The width, only used if "reg_type" is MBLOCK.
+/// @param[out] buf Buffer to store formatted string. The allocated size should
+/// be at least NUMBUFLEN+2 to always fit the value.
+/// @param buf_len The allocated size of the buffer.
+void format_reg_type(char_u reg_type, colnr_T reg_width,
+ char* buf, size_t buf_len)
+ FUNC_ATTR_NONNULL_ALL
+{
+ assert(buf_len > 1);
+ switch (reg_type) {
+ case MLINE:
+ buf[0] = 'V';
+ buf[1] = NUL;
+ break;
+ case MCHAR:
+ buf[0] = 'v';
+ buf[1] = NUL;
+ break;
+ case MBLOCK:
+ snprintf(buf, buf_len, CTRL_V_STR "%" PRIdCOLNR, reg_width + 1);
+ break;
+ case MAUTO:
+ buf[0] = NUL;
+ break;
+ }
+}
+
+
/// When `flags` has `kGRegList` return a list with text `s`.
/// Otherwise just return `s`.
///
diff --git a/src/nvim/option.c b/src/nvim/option.c
index 0f6874e941..5efd71444a 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -5947,13 +5947,17 @@ option_value2string (
if (opp->flags & P_NUM) {
long wc = 0;
- if (wc_use_keyname(varp, &wc))
- STRCPY(NameBuff, get_special_key_name((int)wc, 0));
- else if (wc != 0)
- STRCPY(NameBuff, transchar((int)wc));
- else
- sprintf((char *)NameBuff, "%" PRId64, (int64_t)*(long *)varp);
- } else { /* P_STRING */
+ if (wc_use_keyname(varp, &wc)) {
+ STRLCPY(NameBuff, get_special_key_name((int)wc, 0), sizeof(NameBuff));
+ } else if (wc != 0) {
+ STRLCPY(NameBuff, transchar((int)wc), sizeof(NameBuff));
+ } else {
+ snprintf((char *)NameBuff,
+ sizeof(NameBuff),
+ "%" PRId64,
+ (int64_t)*(long *)varp);
+ }
+ } else { // P_STRING
varp = *(char_u **)(varp);
if (varp == NULL) /* just in case */
NameBuff[0] = NUL;
@@ -6163,16 +6167,14 @@ int has_format_option(int x)
return vim_strchr(curbuf->b_p_fo, x) != NULL;
}
-/*
- * Return TRUE if "x" is present in 'shortmess' option, or
- * 'shortmess' contains 'a' and "x" is present in SHM_A.
- */
-int shortmess(int x)
+/// @returns true if "x" is present in 'shortmess' option, or
+/// 'shortmess' contains 'a' and "x" is present in SHM_ALL_ABBREVIATIONS.
+bool shortmess(int x)
{
return p_shm != NULL &&
- ( vim_strchr(p_shm, x) != NULL
- || (vim_strchr(p_shm, 'a') != NULL
- && vim_strchr((char_u *)SHM_A, x) != NULL));
+ (vim_strchr(p_shm, x) != NULL
+ || (vim_strchr(p_shm, 'a') != NULL
+ && vim_strchr((char_u *)SHM_ALL_ABBREVIATIONS, x) != NULL));
}
/*
diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h
index 10706a0753..547bd9442c 100644
--- a/src/nvim/option_defs.h
+++ b/src/nvim/option_defs.h
@@ -151,26 +151,41 @@
#define COCU_ALL "nvic" /* flags for 'concealcursor' */
-/* characters for p_shm option: */
-#define SHM_RO 'r' /* readonly */
-#define SHM_MOD 'm' /* modified */
-#define SHM_FILE 'f' /* (file 1 of 2) */
-#define SHM_LAST 'i' /* last line incomplete */
-#define SHM_TEXT 'x' /* tx instead of textmode */
-#define SHM_LINES 'l' /* "L" instead of "lines" */
-#define SHM_NEW 'n' /* "[New]" instead of "[New file]" */
-#define SHM_WRI 'w' /* "[w]" instead of "written" */
-#define SHM_A "rmfixlnw" /* represented by 'a' flag */
-#define SHM_WRITE 'W' /* don't use "written" at all */
-#define SHM_TRUNC 't' /* trunctate file messages */
-#define SHM_TRUNCALL 'T' /* trunctate all messages */
-#define SHM_OVER 'o' /* overwrite file messages */
-#define SHM_OVERALL 'O' /* overwrite more messages */
-#define SHM_SEARCH 's' /* no search hit bottom messages */
-#define SHM_ATTENTION 'A' /* no ATTENTION messages */
-#define SHM_INTRO 'I' /* intro messages */
-#define SHM_COMPLETIONMENU 'c' // completion menu messages
-#define SHM_ALL "rmfixlnwaWtToOsAIc" /* all possible flags for 'shm' */
+/// characters for p_shm option:
+enum {
+ SHM_RO = 'r', ///< Readonly.
+ SHM_MOD = 'm', ///< Modified.
+ SHM_FILE = 'f', ///< (file 1 of 2)
+ SHM_LAST = 'i', ///< Last line incomplete.
+ SHM_TEXT = 'x', ///< Tx instead of textmode.
+ SHM_LINES = 'l', ///< "L" instead of "lines".
+ SHM_NEW = 'n', ///< "[New]" instead of "[New file]".
+ SHM_WRI = 'w', ///< "[w]" instead of "written".
+ SHM_ABBREVIATIONS = 'a', ///< Use abbreviations from #SHM_ALL_ABBREVIATIONS.
+ SHM_WRITE = 'W', ///< Don't use "written" at all.
+ SHM_TRUNC = 't', ///< Trunctate file messages.
+ SHM_TRUNCALL = 'T', ///< Trunctate all messages.
+ SHM_OVER = 'o', ///< Overwrite file messages.
+ SHM_OVERALL = 'O', ///< Overwrite more messages.
+ SHM_SEARCH = 's', ///< No search hit bottom messages.
+ SHM_ATTENTION = 'A', ///< No ATTENTION messages.
+ SHM_INTRO = 'I', ///< Intro messages.
+ SHM_COMPLETIONMENU = 'c', ///< Completion menu messages.
+ SHM_RECORDING = 'q', ///< Short recording message.
+};
+/// Represented by 'a' flag.
+#define SHM_ALL_ABBREVIATIONS ((char_u[]) { \
+ SHM_RO, SHM_MOD, SHM_FILE, SHM_LAST, SHM_TEXT, SHM_LINES, SHM_NEW, SHM_WRI, \
+ 0, \
+})
+/// All possible flags for 'shm'.
+#define SHM_ALL ((char_u[]) { \
+ SHM_RO, SHM_MOD, SHM_FILE, SHM_LAST, SHM_TEXT, SHM_LINES, SHM_NEW, SHM_WRI, \
+ SHM_ABBREVIATIONS, SHM_WRITE, SHM_TRUNC, SHM_TRUNCALL, SHM_OVER, \
+ SHM_OVERALL, SHM_SEARCH, SHM_ATTENTION, SHM_INTRO, SHM_COMPLETIONMENU, \
+ SHM_RECORDING, \
+ 0, \
+})
/* characters for p_go: */
#define GO_ASEL 'a' /* autoselect */
diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c
index 41ce8ddbc2..384a17004e 100644
--- a/src/nvim/os/env.c
+++ b/src/nvim/os/env.c
@@ -759,15 +759,15 @@ char_u * home_replace_save(buf_T *buf, char_u *src) FUNC_ATTR_NONNULL_RET
void vim_setenv(const char *name, const char *val)
{
os_setenv(name, val, 1);
- /*
- * When setting $VIMRUNTIME adjust the directory to find message
- * translations to $VIMRUNTIME/lang.
- */
+#ifndef LOCALE_INSTALL_DIR
+ // When setting $VIMRUNTIME adjust the directory to find message
+ // translations to $VIMRUNTIME/lang.
if (*val != NUL && STRICMP(name, "VIMRUNTIME") == 0) {
char *buf = (char *)concat_str((char_u *)val, (char_u *)"/lang");
- bindtextdomain(VIMPACKAGE, buf);
+ bindtextdomain(PROJECT_NAME, buf);
xfree(buf);
}
+#endif
}
diff --git a/src/nvim/po/CMakeLists.txt b/src/nvim/po/CMakeLists.txt
index d1e08db65e..184c4894b9 100644
--- a/src/nvim/po/CMakeLists.txt
+++ b/src/nvim/po/CMakeLists.txt
@@ -72,8 +72,8 @@ if(HAVE_WORKING_LIBINTL AND GETTEXT_FOUND AND XGETTEXT_PRG AND ICONV_PRG)
install_helper(
FILES ${moFile}
- DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/nvim/runtime/lang/${name}/LC_MESSAGES
- RENAME nvim.mo)
+ DESTINATION ${CMAKE_INSTALL_LOCALEDIR}/${name}/LC_MESSAGES
+ RENAME ${PROJECT_NAME}.mo)
list(APPEND LANGUAGE_MO_FILES ${moFile})
endmacro()
diff --git a/src/nvim/pos.h b/src/nvim/pos.h
index 7071df51e8..864f3fe866 100644
--- a/src/nvim/pos.h
+++ b/src/nvim/pos.h
@@ -2,7 +2,11 @@
#define NVIM_POS_H
typedef long linenr_T; // line number type
-typedef int colnr_T; // column number type
+
+/// Column number type
+typedef int colnr_T;
+/// Format used to print values which have colnr_T type
+#define PRIdCOLNR "d"
#define MAXLNUM 0x7fffffff // maximum (invalid) line number
#define MAXCOL 0x7fffffff // maximum column number, 31 bits
diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c
index f3abf864fb..4a8391430b 100644
--- a/src/nvim/quickfix.c
+++ b/src/nvim/quickfix.c
@@ -2299,13 +2299,15 @@ static void qf_fill_buffer(qf_info_T *qi)
if (qfp->qf_fnum != 0
&& (errbuf = buflist_findnr(qfp->qf_fnum)) != NULL
&& errbuf->b_fname != NULL) {
- if (qfp->qf_type == 1) /* :helpgrep */
- STRCPY(IObuff, path_tail(errbuf->b_fname));
- else
- STRCPY(IObuff, errbuf->b_fname);
+ if (qfp->qf_type == 1) { // :helpgrep
+ STRLCPY(IObuff, path_tail(errbuf->b_fname), sizeof(IObuff));
+ } else {
+ STRLCPY(IObuff, errbuf->b_fname, sizeof(IObuff));
+ }
len = (int)STRLEN(IObuff);
- } else
+ } else {
len = 0;
+ }
IObuff[len++] = '|';
if (qfp->qf_lnum > 0) {
diff --git a/src/nvim/screen.c b/src/nvim/screen.c
index 3b5836f0b5..1cc270023c 100644
--- a/src/nvim/screen.c
+++ b/src/nvim/screen.c
@@ -2184,6 +2184,10 @@ win_line (
int prev_c1 = 0; /* first composing char for prev_c */
int did_line_attr = 0;
+ bool has_bufhl = false; // this buffer has highlight matches
+ int bufhl_attr = 0; // attributes desired by bufhl
+ bufhl_lineinfo_T bufhl_info; // bufhl data for this line
+
/* draw_state: items that are drawn in sequence: */
#define WL_START 0 /* nothing done yet */
# define WL_CMDLINE WL_START + 1 /* cmdline window column */
@@ -2244,6 +2248,11 @@ win_line (
}
}
+ if (bufhl_start_line(wp->w_buffer, lnum, &bufhl_info)) {
+ has_bufhl = true;
+ extra_check = true;
+ }
+
/* Check for columns to display for 'colorcolumn'. */
color_cols = wp->w_buffer->terminal ? NULL : wp->w_p_cc_cols;
if (color_cols != NULL)
@@ -3335,6 +3344,17 @@ win_line (
char_attr = hl_combine_attr(spell_attr, char_attr);
}
+ if (has_bufhl && v > 0) {
+ bufhl_attr = bufhl_get_attr(&bufhl_info, (colnr_T)v);
+ if (bufhl_attr != 0) {
+ if (!attr_pri) {
+ char_attr = hl_combine_attr(char_attr, bufhl_attr);
+ } else {
+ char_attr = hl_combine_attr(bufhl_attr, char_attr);
+ }
+ }
+ }
+
if (wp->w_buffer->terminal) {
char_attr = hl_combine_attr(char_attr, term_attrs[vcol]);
}
@@ -6760,8 +6780,8 @@ int showmode(void)
if (Recording
&& edit_submode == NULL /* otherwise it gets too long */
) {
- MSG_PUTS_ATTR(_("recording"), attr);
- need_clear = TRUE;
+ recording_mode(attr);
+ need_clear = true;
}
mode_displayed = TRUE;
@@ -6800,26 +6820,33 @@ static void msg_pos_mode(void)
msg_row = Rows - 1;
}
-/*
- * Delete mode message. Used when ESC is typed which is expected to end
- * Insert mode (but Insert mode didn't end yet!).
- * Caller should check "mode_displayed".
- */
-void unshowmode(int force)
+/// Delete mode message. Used when ESC is typed which is expected to end
+/// Insert mode (but Insert mode didn't end yet!).
+/// Caller should check "mode_displayed".
+void unshowmode(bool force)
{
- /*
- * Don't delete it right now, when not redrawing or inside a mapping.
- */
- if (!redrawing() || (!force && char_avail() && !KeyTyped))
- redraw_cmdline = TRUE; /* delete mode later */
- else {
+ // Don't delete it right now, when not redrawing or inside a mapping.
+ if (!redrawing() || (!force && char_avail() && !KeyTyped)) {
+ redraw_cmdline = true; // delete mode later
+ } else {
msg_pos_mode();
- if (Recording)
- MSG_PUTS_ATTR(_("recording"), hl_attr(HLF_CM));
+ if (Recording) {
+ recording_mode(hl_attr(HLF_CM));
+ }
msg_clr_eos();
}
}
+static void recording_mode(int attr)
+{
+ MSG_PUTS_ATTR(_("recording"), attr);
+ if (!shortmess(SHM_RECORDING)) {
+ char_u s[4];
+ vim_snprintf((char *)s, ARRAY_SIZE(s), " @%c", Recording);
+ MSG_PUTS_ATTR(s, attr);
+ }
+}
+
/*
* Draw the tab pages line at the top of the Vim window.
*/
diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c
index fb74569e3b..42fd81f643 100644
--- a/src/nvim/terminal.c
+++ b/src/nvim/terminal.c
@@ -141,10 +141,6 @@ struct terminal {
int pressed_button;
// pending width/height
bool pending_resize;
- // color palette. this isn't set directly in the vterm instance because
- // the default values are used to obtain the color numbers passed to cterm
- // colors
- RgbValue colors[256];
// With a reference count of 0 the terminal can be freed.
size_t refcount;
};
@@ -205,6 +201,7 @@ void terminal_teardown(void)
Terminal *terminal_open(TerminalOptions opts)
{
+ bool true_color = ui_rgb_attached();
// Create a new terminal instance and configure it
Terminal *rv = xcalloc(1, sizeof(Terminal));
rv->opts = opts;
@@ -220,7 +217,7 @@ Terminal *terminal_open(TerminalOptions opts)
// Set up screen
rv->vts = vterm_obtain_screen(rv->vt);
vterm_screen_enable_altscreen(rv->vts, true);
- // delete empty lines at the end of the buffer
+ // delete empty lines at the end of the buffer
vterm_screen_set_callbacks(rv->vts, &vterm_screen_callbacks, rv);
vterm_screen_set_damage_merge(rv->vts, VTERM_DAMAGE_SCROLL);
vterm_screen_reset(rv->vts, 1);
@@ -250,12 +247,18 @@ Terminal *terminal_open(TerminalOptions opts)
rv->sb_size = MIN(rv->sb_size, 100000);
rv->sb_buffer = xmalloc(sizeof(ScrollbackLine *) * rv->sb_size);
+ if (!true_color) {
+ return rv;
+ }
+
+ vterm_state_set_bold_highbright(state, true);
+
// Configure the color palette. Try to get the color from:
//
// - b:terminal_color_{NUM}
// - g:terminal_color_{NUM}
// - the VTerm instance
- for (int i = 0; i < (int)ARRAY_SIZE(rv->colors); i++) {
+ for (int i = 0; i < 16; i++) {
RgbValue color_val = -1;
char var[64];
snprintf(var, sizeof(var), "terminal_color_%d", i);
@@ -265,16 +268,13 @@ Terminal *terminal_open(TerminalOptions opts)
xfree(name);
if (color_val != -1) {
- rv->colors[i] = color_val;
+ VTermColor color;
+ color.red = (uint8_t)((color_val >> 16) & 0xFF);
+ color.green = (uint8_t)((color_val >> 8) & 0xFF);
+ color.blue = (uint8_t)((color_val >> 0) & 0xFF);
+ vterm_state_set_palette_color(state, i, &color);
}
}
-
- if (color_val == -1) {
- // the default is taken from vterm
- VTermColor color;
- vterm_state_get_palette_color(state, i, &color);
- rv->colors[i] = RGB(color.red, color.green, color.blue);
- }
}
return rv;
@@ -548,10 +548,6 @@ void terminal_get_line_attributes(Terminal *term, win_T *wp, int linenr,
map_get(int, int)(color_indexes, vt_fg) : 0;
int vt_bg_idx = vt_bg != default_vt_bg ?
map_get(int, int)(color_indexes, vt_bg) : 0;
- // The index is now used to get the final rgb value from the
- // user-customizable palette.
- int vt_fg_rgb = vt_fg_idx != 0 ? term->colors[vt_fg_idx - 1] : -1;
- int vt_bg_rgb = vt_bg_idx != 0 ? term->colors[vt_bg_idx - 1] : -1;
int hl_attrs = (cell.attrs.bold ? HL_BOLD : 0)
| (cell.attrs.italic ? HL_ITALIC : 0)
@@ -566,8 +562,8 @@ void terminal_get_line_attributes(Terminal *term, win_T *wp, int linenr,
.cterm_fg_color = vt_fg_idx,
.cterm_bg_color = vt_bg_idx,
.rgb_ae_attr = (int16_t)hl_attrs,
- .rgb_fg_color = vt_fg_rgb,
- .rgb_bg_color = vt_bg_rgb,
+ .rgb_fg_color = vt_fg,
+ .rgb_bg_color = vt_bg,
});
}
diff --git a/src/nvim/testdir/Makefile b/src/nvim/testdir/Makefile
index 7886835b4d..63ca4cf6c4 100644
--- a/src/nvim/testdir/Makefile
+++ b/src/nvim/testdir/Makefile
@@ -7,27 +7,37 @@ export SHELL := sh
VIMPROG := ../../../build/bin/nvim
SCRIPTSOURCE := ../../../runtime
-SCRIPTS := \
- test8.out test10.out \
- test12.out test13.out test14.out \
- test17.out \
- test24.out \
- test30.out \
- test32.out test34.out \
- test37.out test40.out \
- test42.out \
- test47.out test48.out test49.out \
- test52.out test53.out test55.out \
- test64.out \
- test68.out test69.out \
- test73.out \
- test79.out \
- test88.out \
- test_listlbr.out \
- test_breakindent.out \
- test_close_count.out \
- test_marks.out \
- test_match_conceal.out \
+SCRIPTS := \
+ test8.out \
+ test10.out \
+ test12.out \
+ test13.out \
+ test14.out \
+ test17.out \
+ test24.out \
+ test30.out \
+ test32.out \
+ test34.out \
+ test37.out \
+ test40.out \
+ test42.out \
+ test47.out \
+ test48.out \
+ test49.out \
+ test52.out \
+ test53.out \
+ test55.out \
+ test64.out \
+ test68.out \
+ test69.out \
+ test73.out \
+ test79.out \
+ test88.out \
+ test_listlbr.out \
+ test_breakindent.out \
+ test_close_count.out \
+ test_marks.out \
+ test_match_conceal.out \
NEW_TESTS =
diff --git a/src/nvim/undo.c b/src/nvim/undo.c
index b8cdffcda0..4a8a24d79d 100644
--- a/src/nvim/undo.c
+++ b/src/nvim/undo.c
@@ -4,29 +4,29 @@
* The saved lines are stored in a list of lists (one for each buffer):
*
* b_u_oldhead------------------------------------------------+
- * |
- * V
- * +--------------+ +--------------+ +--------------+
- * b_u_newhead--->| u_header | | u_header | | u_header |
- * | uh_next------>| uh_next------>| uh_next---->NULL
- * NULL<--------uh_prev |<---------uh_prev |<---------uh_prev |
- * | uh_entry | | uh_entry | | uh_entry |
- * +--------|-----+ +--------|-----+ +--------|-----+
- * | | |
- * V V V
- * +--------------+ +--------------+ +--------------+
- * | u_entry | | u_entry | | u_entry |
- * | ue_next | | ue_next | | ue_next |
- * +--------|-----+ +--------|-----+ +--------|-----+
- * | | |
- * V V V
- * +--------------+ NULL NULL
- * | u_entry |
- * | ue_next |
- * +--------|-----+
- * |
- * V
- * etc.
+ * |
+ * V
+ * +--------------+ +--------------+ +--------------+
+ * b_u_newhead--->| u_header | | u_header | | u_header |
+ * | uh_next------>| uh_next------>| uh_next---->NULL
+ * NULL<--------uh_prev |<---------uh_prev |<---------uh_prev |
+ * | uh_entry | | uh_entry | | uh_entry |
+ * +--------|-----+ +--------|-----+ +--------|-----+
+ * | | |
+ * V V V
+ * +--------------+ +--------------+ +--------------+
+ * | u_entry | | u_entry | | u_entry |
+ * | ue_next | | ue_next | | ue_next |
+ * +--------|-----+ +--------|-----+ +--------|-----+
+ * | | |
+ * V V V
+ * +--------------+ NULL NULL
+ * | u_entry |
+ * | ue_next |
+ * +--------|-----+
+ * |
+ * V
+ * etc.
*
* Each u_entry list contains the information for one undo or redo.
* curbuf->b_u_curhead points to the header of the last undo (the next redo),
@@ -37,30 +37,30 @@
* uh_seq field is numbered sequentially to be able to find a newer or older
* branch.
*
- * +---------------+ +---------------+
- * b_u_oldhead --->| u_header | | u_header |
- * | uh_alt_next ---->| uh_alt_next ----> NULL
- * NULL <----- uh_alt_prev |<------ uh_alt_prev |
- * | uh_prev | | uh_prev |
- * +-----|---------+ +-----|---------+
- * | |
- * V V
- * +---------------+ +---------------+
- * | u_header | | u_header |
- * | uh_alt_next | | uh_alt_next |
- * b_u_newhead --->| uh_alt_prev | | uh_alt_prev |
- * | uh_prev | | uh_prev |
- * +-----|---------+ +-----|---------+
- * | |
- * V V
- * NULL +---------------+ +---------------+
- * | u_header | | u_header |
- * | uh_alt_next ---->| uh_alt_next |
- * | uh_alt_prev |<------ uh_alt_prev |
- * | uh_prev | | uh_prev |
- * +-----|---------+ +-----|---------+
- * | |
- * etc. etc.
+ * +---------------+ +---------------+
+ * b_u_oldhead --->| u_header | | u_header |
+ * | uh_alt_next ---->| uh_alt_next ----> NULL
+ * NULL <----- uh_alt_prev |<------ uh_alt_prev |
+ * | uh_prev | | uh_prev |
+ * +-----|---------+ +-----|---------+
+ * | |
+ * V V
+ * +---------------+ +---------------+
+ * | u_header | | u_header |
+ * | uh_alt_next | | uh_alt_next |
+ * b_u_newhead --->| uh_alt_prev | | uh_alt_prev |
+ * | uh_prev | | uh_prev |
+ * +-----|---------+ +-----|---------+
+ * | |
+ * V V
+ * NULL +---------------+ +---------------+
+ * | u_header | | u_header |
+ * | uh_alt_next ---->| uh_alt_next |
+ * | uh_alt_prev |<------ uh_alt_prev |
+ * | uh_prev | | uh_prev |
+ * +-----|---------+ +-----|---------+
+ * | |
+ * etc. etc.
*
*
* All data is allocated and will all be freed when the buffer is unloaded.
diff --git a/src/nvim/version.c b/src/nvim/version.c
index a54e232f78..064a853b4c 100644
--- a/src/nvim/version.c
+++ b/src/nvim/version.c
@@ -178,7 +178,7 @@ static int included_patches[] = {
// 1115 NA
// 1114,
// 1113,
- // 1112,
+ 1112,
// 1111,
// 1110,
// 1109 NA
@@ -297,7 +297,7 @@ static int included_patches[] = {
// 996 NA
// 995 NA
// 994 NA
- // 993,
+ // 993 NA
// 992 NA
991,
// 990 NA
@@ -316,12 +316,12 @@ static int included_patches[] = {
977,
// 976 NA
975,
- // 974,
+ 974,
// 973,
972,
// 971 NA
// 970 NA
- // 969,
+ // 969 NA
// 968 NA
// 967 NA
// 966 NA
@@ -334,7 +334,7 @@ static int included_patches[] = {
// 959 NA
// 958,
// 957,
- // 956,
+ // 956 NA
955,
// 954 NA
953,
@@ -343,11 +343,11 @@ static int included_patches[] = {
950,
949,
// 948 NA
- // 947,
+ // 947 NA
946,
945,
944,
- // 943,
+ // 943 NA
// 942,
// 941,
// 940 NA
@@ -365,7 +365,7 @@ static int included_patches[] = {
// 928 NA
// 927 NA
926,
- // 925,
+ 925,
// 924 NA
// 923 NA
922,
@@ -376,17 +376,17 @@ static int included_patches[] = {
// 917 NA
916,
915,
- // 914,
+ // 914 NA
// 913 NA
912,
// 911 NA
// 910 NA
- // 909,
+ // 909 NA
// 908 NA
// 907 NA
// 906 NA
- // 905,
- // 904,
+ // 905 NA
+ // 904 NA
903,
// 902 NA
901,
@@ -398,7 +398,7 @@ static int included_patches[] = {
895,
// 894 NA
893,
- // 892,
+ // 892 NA
891,
// 890 NA
// 889,
@@ -433,7 +433,7 @@ static int included_patches[] = {
// 860 NA
859,
858,
- // 857,
+ 857,
856,
// 855 NA
// 854 NA
diff --git a/src/nvim/vim.h b/src/nvim/vim.h
index 762d349470..545b903d2f 100644
--- a/src/nvim/vim.h
+++ b/src/nvim/vim.h
@@ -27,12 +27,6 @@ Error: configure did not run properly.Check auto/config.log.
# endif
#endif
-
-/* Can't use "PACKAGE" here, conflicts with a Perl include file. */
-#ifndef VIMPACKAGE
-# define VIMPACKAGE "nvim"
-#endif
-
#include "nvim/os/os_defs.h" /* bring lots of system header files */
/// length of a buffer to store a number in ASCII (64 bits binary + NUL)
diff --git a/src/nvim/window.c b/src/nvim/window.c
index 39106a7b8d..b79aa366e5 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -248,7 +248,7 @@ newwindow:
/* First create a new tab with the window, then go back to
* the old tab and close the window there. */
wp = curwin;
- if (win_new_tabpage((int)Prenum) == OK
+ if (win_new_tabpage((int)Prenum, NULL) == OK
&& valid_tabpage(oldtab)) {
newtab = curtab;
goto_tabpage_tp(oldtab, TRUE, TRUE);
@@ -2952,14 +2952,15 @@ void free_tabpage(tabpage_T *tp)
xfree(tp);
}
-/*
- * Create a new Tab page with one window.
- * It will edit the current buffer, like after ":split".
- * When "after" is 0 put it just after the current Tab page.
- * Otherwise put it just before tab page "after".
- * Return FAIL or OK.
- */
-int win_new_tabpage(int after)
+/// Create a new tabpage with one window.
+///
+/// It will edit the current buffer, like after :split.
+///
+/// @param after Put new tabpage after tabpage "after", or after the current
+/// tabpage in case of 0.
+/// @param filename Will be passed to apply_autocmds().
+/// @return Was the new tabpage created successfully? FAIL or OK.
+int win_new_tabpage(int after, char_u *filename)
{
tabpage_T *tp = curtab;
tabpage_T *newtp;
@@ -2999,10 +3000,12 @@ int win_new_tabpage(int after)
newtp->tp_topframe = topframe;
last_status(FALSE);
-
redraw_all_later(CLEAR);
- apply_autocmds(EVENT_WINENTER, NULL, NULL, FALSE, curbuf);
- apply_autocmds(EVENT_TABENTER, NULL, NULL, FALSE, curbuf);
+
+ apply_autocmds(EVENT_TABNEW, filename, filename, false, curbuf);
+ apply_autocmds(EVENT_WINENTER, NULL, NULL, false, curbuf);
+ apply_autocmds(EVENT_TABENTER, NULL, NULL, false, curbuf);
+
return OK;
}
@@ -3023,7 +3026,7 @@ int may_open_tabpage(void)
if (n != 0) {
cmdmod.tab = 0; /* reset it to avoid doing it twice */
postponed_split_tab = 0;
- return win_new_tabpage(n);
+ return win_new_tabpage(n, NULL);
}
return FAIL;
}
@@ -3047,9 +3050,11 @@ int make_tabpages(int maxcount)
*/
block_autocmds();
- for (todo = count - 1; todo > 0; --todo)
- if (win_new_tabpage(0) == FAIL)
+ for (todo = count - 1; todo > 0; --todo) {
+ if (win_new_tabpage(0, NULL) == FAIL) {
break;
+ }
+ }
unblock_autocmds();
diff --git a/test/functional/autocmd/textyankpost_spec.lua b/test/functional/autocmd/textyankpost_spec.lua
new file mode 100644
index 0000000000..965b19581a
--- /dev/null
+++ b/test/functional/autocmd/textyankpost_spec.lua
@@ -0,0 +1,216 @@
+local helpers = require('test.functional.helpers')
+local clear, eval, eq, insert = helpers.clear, helpers.eval, helpers.eq, helpers.insert
+local feed, execute, expect, command = helpers.feed, helpers.execute, helpers.expect, helpers.command
+local curbufmeths, funcs, neq = helpers.curbufmeths, helpers.funcs, helpers.neq
+
+describe('TextYankPost', function()
+ before_each(function()
+ clear()
+
+ -- emulate the clipboard so system clipboard isn't affected
+ execute('let &rtp = "test/functional/fixtures,".&rtp')
+
+ execute('let g:count = 0')
+ execute('autocmd TextYankPost * let g:event = copy(v:event)')
+ execute('autocmd TextYankPost * let g:count += 1')
+
+ curbufmeths.set_line_slice(0, -1, true, true, {
+ 'foo\0bar',
+ 'baz text',
+ })
+ end)
+
+ it('is executed after yank and handles register types', function()
+ feed('yy')
+ eq({
+ operator = 'y',
+ regcontents = { 'foo\nbar' },
+ regname = '',
+ regtype = 'V'
+ }, eval('g:event'))
+ eq(1, eval('g:count'))
+
+ -- v:event is cleared after the autocommand is done
+ eq({}, eval('v:event'))
+
+ feed('+yw')
+ eq({
+ operator = 'y',
+ regcontents = { 'baz ' },
+ regname = '',
+ regtype = 'v'
+ }, eval('g:event'))
+ eq(2, eval('g:count'))
+
+ feed('<c-v>eky')
+ eq({
+ operator = 'y',
+ regcontents = { 'foo', 'baz' },
+ regname = '',
+ regtype = "\0223" -- ^V + block width
+ }, eval('g:event'))
+ eq(3, eval('g:count'))
+ end)
+
+ it('makes v:event immutable', function()
+ feed('yy')
+ eq({
+ operator = 'y',
+ regcontents = { 'foo\nbar' },
+ regname = '',
+ regtype = 'V'
+ }, eval('g:event'))
+
+ execute('set debug=msg')
+ -- the regcontents should not be changed without copy.
+ local status, err = pcall(command,'call extend(g:event.regcontents, ["more text"])')
+ eq(status,false)
+ neq(nil, string.find(err, ':E742:'))
+
+ -- can't mutate keys inside the autocommand
+ execute('autocmd! TextYankPost * let v:event.regcontents = 0')
+ status, err = pcall(command,'normal yy')
+ eq(status,false)
+ neq(nil, string.find(err, ':E46:'))
+
+ -- can't add keys inside the autocommand
+ execute('autocmd! TextYankPost * let v:event.mykey = 0')
+ status, err = pcall(command,'normal yy')
+ eq(status,false)
+ neq(nil, string.find(err, ':E742:'))
+ end)
+
+ it('is not invoked recursively', function()
+ execute('autocmd TextYankPost * normal "+yy')
+ feed('yy')
+ eq({
+ operator = 'y',
+ regcontents = { 'foo\nbar' },
+ regname = '',
+ regtype = 'V'
+ }, eval('g:event'))
+ eq(1, eval('g:count'))
+ eq({ 'foo\nbar' }, funcs.getreg('+',1,1))
+ end)
+
+ it('is executed after delete and change', function()
+ feed('dw')
+ eq({
+ operator = 'd',
+ regcontents = { 'foo' },
+ regname = '',
+ regtype = 'v'
+ }, eval('g:event'))
+ eq(1, eval('g:count'))
+
+ feed('dd')
+ eq({
+ operator = 'd',
+ regcontents = { '\nbar' },
+ regname = '',
+ regtype = 'V'
+ }, eval('g:event'))
+ eq(2, eval('g:count'))
+
+ feed('cwspam<esc>')
+ eq({
+ operator = 'c',
+ regcontents = { 'baz' },
+ regname = '',
+ regtype = 'v'
+ }, eval('g:event'))
+ eq(3, eval('g:count'))
+ end)
+
+ it('is not executed after black-hole operation', function()
+ feed('"_dd')
+ eq(0, eval('g:count'))
+
+ feed('"_cwgood<esc>')
+ eq(0, eval('g:count'))
+
+ expect([[
+ good text]])
+ feed('"_yy')
+ eq(0, eval('g:count'))
+
+ execute('delete _')
+ eq(0, eval('g:count'))
+ end)
+
+ it('gives the correct register name', function()
+ feed('$"byiw')
+ eq({
+ operator = 'y',
+ regcontents = { 'bar' },
+ regname = 'b',
+ regtype = 'v'
+ }, eval('g:event'))
+
+ feed('"*yy')
+ eq({
+ operator = 'y',
+ regcontents = { 'foo\nbar' },
+ regname = '*',
+ regtype = 'V'
+ }, eval('g:event'))
+
+ execute("set clipboard=unnamed")
+
+ -- regname still shows the name the user requested
+ feed('yy')
+ eq({
+ operator = 'y',
+ regcontents = { 'foo\nbar' },
+ regname = '',
+ regtype = 'V'
+ }, eval('g:event'))
+
+ feed('"*yy')
+ eq({
+ operator = 'y',
+ regcontents = { 'foo\nbar' },
+ regname = '*',
+ regtype = 'V'
+ }, eval('g:event'))
+ end)
+
+ it('works with Ex commands', function()
+ execute('1delete +')
+ eq({
+ operator = 'd',
+ regcontents = { 'foo\nbar' },
+ regname = '+',
+ regtype = 'V'
+ }, eval('g:event'))
+ eq(1, eval('g:count'))
+
+ execute('yank')
+ eq({
+ operator = 'y',
+ regcontents = { 'baz text' },
+ regname = '',
+ regtype = 'V'
+ }, eval('g:event'))
+ eq(2, eval('g:count'))
+
+ execute('normal yw')
+ eq({
+ operator = 'y',
+ regcontents = { 'baz ' },
+ regname = '',
+ regtype = 'v'
+ }, eval('g:event'))
+ eq(3, eval('g:count'))
+
+ execute('normal! dd')
+ eq({
+ operator = 'd',
+ regcontents = { 'baz text' },
+ regname = '',
+ regtype = 'V'
+ }, eval('g:event'))
+ eq(4, eval('g:count'))
+ end)
+
+end)
diff --git a/test/functional/eval/operators_spec.lua b/test/functional/eval/operators_spec.lua
new file mode 100644
index 0000000000..bc9a17935c
--- /dev/null
+++ b/test/functional/eval/operators_spec.lua
@@ -0,0 +1,28 @@
+local helpers = require('test.functional.helpers')
+local eq = helpers.eq
+local eval = helpers.eval
+local clear = helpers.clear
+
+describe('Division operator', function()
+ before_each(clear)
+
+ it('returns infinity on {positive}/0.0', function()
+ eq('str2float(\'inf\')', eval('string(1.0/0.0)'))
+ eq('str2float(\'inf\')', eval('string(1.0e-100/0.0)'))
+ eq('str2float(\'inf\')', eval('string(1.0e+100/0.0)'))
+ eq('str2float(\'inf\')', eval('string((1.0/0.0)/0.0)'))
+ end)
+
+ it('returns -infinity on {negative}/0.0', function()
+ eq('-str2float(\'inf\')', eval('string((-1.0)/0.0)'))
+ eq('-str2float(\'inf\')', eval('string((-1.0e-100)/0.0)'))
+ eq('-str2float(\'inf\')', eval('string((-1.0e+100)/0.0)'))
+ eq('-str2float(\'inf\')', eval('string((-1.0/0.0)/0.0)'))
+ end)
+
+ it('returns NaN on 0.0/0.0', function()
+ eq('str2float(\'nan\')', eval('string(0.0/0.0)'))
+ eq('str2float(\'nan\')', eval('string(-(0.0/0.0))'))
+ eq('str2float(\'nan\')', eval('string((-0.0)/0.0)'))
+ end)
+end)
diff --git a/test/functional/eval/string_spec.lua b/test/functional/eval/string_spec.lua
new file mode 100644
index 0000000000..f7f5dca70a
--- /dev/null
+++ b/test/functional/eval/string_spec.lua
@@ -0,0 +1,175 @@
+local helpers = require('test.functional.helpers')
+local clear = helpers.clear
+local eq = helpers.eq
+local command = helpers.command
+local meths = helpers.meths
+local eval = helpers.eval
+local exc_exec = helpers.exc_exec
+local redir_exec = helpers.redir_exec
+local funcs = helpers.funcs
+local write_file = helpers.write_file
+
+describe('string() function', function()
+ before_each(clear)
+
+ 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 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)
+ end)
+
+ describe('used to represent funcrefs', function()
+ local fname = 'Xtest-functional-eval-string_spec-fref-script.vim'
+
+ before_each(function()
+ write_file(fname, [[
+ function Test1()
+ endfunction
+
+ function s:Test2()
+ endfunction
+
+ function g:Test3()
+ endfunction
+
+ let g:Test2_f = function('s:Test2')
+ ]])
+ command('source ' .. fname)
+ end)
+
+ after_each(function()
+ os.remove(fname)
+ end)
+
+ it('dumps references to built-in functions', function()
+ eq('function(\'function\')', eval('string(function("function"))'))
+ end)
+
+ it('dumps references to user functions', function()
+ eq('function(\'Test1\')', eval('string(function("Test1"))'))
+ eq('function(\'g:Test3\')', eval('string(function("g:Test3"))'))
+ end)
+
+ it('dumps references to script functions', function()
+ eq('function(\'<SNR>1_Test2\')', eval('string(Test2_f)'))
+ 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('errors when dumping recursive lists', function()
+ meths.set_var('l', {})
+ eval('add(l, l)')
+ eq('Vim(echo):E724: unable to correctly dump variable with self-referencing container',
+ exc_exec('echo string(l)'))
+ end)
+
+ it('dumps recursive lists despite the error', function()
+ meths.set_var('l', {})
+ eval('add(l, l)')
+ eq('\nE724: unable to correctly dump variable with self-referencing container\n[{E724@0}]',
+ redir_exec('echo string(l)'))
+ eq('\nE724: unable to correctly dump variable with self-referencing container\n[[{E724@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 non-empty dictionary', function()
+ eq('{\'t\'\'est\': 1}', funcs.string({['t\'est']=1}))
+ end)
+
+ it('errors when dumping recursive dictionaries', function()
+ meths.set_var('d', {d=1})
+ eval('extend(d, {"d": d})')
+ eq('Vim(echo):E724: unable to correctly dump variable with self-referencing container',
+ exc_exec('echo string(d)'))
+ end)
+
+ it('dumps recursive dictionaries despite the error', function()
+ meths.set_var('d', {d=1})
+ eval('extend(d, {"d": d})')
+ eq('\nE724: unable to correctly dump variable with self-referencing container\n{\'d\': {E724@0}}',
+ redir_exec('echo string(d)'))
+ eq('\nE724: unable to correctly dump variable with self-referencing container\n{\'out\': {\'d\': {E724@1}}}',
+ redir_exec('echo string({"out": d})'))
+ end)
+ end)
+end)
diff --git a/test/functional/eval/vvar_event_spec.lua b/test/functional/eval/vvar_event_spec.lua
new file mode 100644
index 0000000000..bbac86524f
--- /dev/null
+++ b/test/functional/eval/vvar_event_spec.lua
@@ -0,0 +1,15 @@
+local helpers = require('test.functional.helpers')
+local clear, eval, eq = helpers.clear, helpers.eval, helpers.eq
+local command = helpers.command
+describe('v:event', function()
+ before_each(clear)
+ it('is empty before any autocommand', function()
+ eq({}, eval('v:event'))
+ end)
+
+ it('is immutable', function()
+ eq(false, pcall(command, 'let v:event = {}'))
+ eq(false, pcall(command, 'let v:event.mykey = {}'))
+ end)
+end)
+
diff --git a/test/functional/fixtures/shell-test.c b/test/functional/fixtures/shell-test.c
index 5fa8a58049..d9ec254aff 100644
--- a/test/functional/fixtures/shell-test.c
+++ b/test/functional/fixtures/shell-test.c
@@ -1,25 +1,59 @@
-// A simple implementation of a shell for testing
-// `termopen([&sh, &shcf, '{cmd'}])` and `termopen([&sh])`.
-//
-// If launched with no arguments, prints "ready $ ", otherwise prints
-// "ready $ {cmd}\n".
-
#include <stdio.h>
#include <string.h>
+#include <stdint.h>
+
+static void help(void)
+{
+ puts("A simple implementation of a shell for testing termopen().");
+ puts("");
+ puts("Usage:");
+ puts(" shell-test --help");
+ puts(" Prints this help to stdout.");
+ puts(" shell-test");
+ puts(" shell-test EXE");
+ puts(" Prints \"ready $ \" to stderr.");
+ puts(" shell-test EXE \"prog args...\"");
+ puts(" Prints \"ready $ prog args...\\n\" to stderr.");
+ puts(" shell-test REP {byte} \"line line line\"");
+ puts(" Prints \"{lnr}: line line line\\n\" to stdout {byte} times.");
+ puts(" I.e. for `shell-test REP ab \"test\"'");
+ puts(" 0: test");
+ puts(" ...");
+ puts(" 96: test");
+ puts(" will be printed because byte `a' is equal to 97.");
+}
int main(int argc, char **argv)
{
- fprintf(stderr, "ready $ ");
+ if (argc == 2 && strcmp(argv[1], "--help") == 0) {
+ help();
+ }
- if (argc == 3) {
- // argv should be {"terminal-test", "EXE", "prog args..."}
- if (strcmp(argv[1], "EXE") != 0) {
- fprintf(stderr, "first argument must be 'EXE'\n");
- return 2;
+ if (argc >= 2) {
+ if (strcmp(argv[1], "EXE") == 0) {
+ fprintf(stderr, "ready $ ");
+ if (argc >= 3) {
+ fprintf(stderr, "%s\n", argv[2]);
+ }
+ } else if (strcmp(argv[1], "REP") == 0) {
+ if (argc < 4) {
+ fprintf(stderr, "Not enough REP arguments\n");
+ return 4;
+ }
+ uint8_t number = (uint8_t) *argv[2];
+ for (uint8_t i = 0; i < number; i++) {
+ printf("%d: %s\n", (int) i, argv[3]);
+ }
+ } else {
+ fprintf(stderr, "Unknown first argument\n");
+ return 3;
}
-
- fprintf(stderr, "%s\n", argv[2]);
+ return 0;
+ } else if (argc == 1) {
+ fprintf(stderr, "ready $ ");
+ return 0;
+ } else {
+ fprintf(stderr, "Missing first argument\n");
+ return 2;
}
-
- return 0;
}
diff --git a/test/functional/legacy/036_regexp_character_classes_spec.lua b/test/functional/legacy/036_regexp_character_classes_spec.lua
index 205922eac2..3c264423ff 100644
--- a/test/functional/legacy/036_regexp_character_classes_spec.lua
+++ b/test/functional/legacy/036_regexp_character_classes_spec.lua
@@ -2,13 +2,12 @@
local helpers = require('test.functional.helpers')
local ffi = require('ffi')
-local feed, insert, source = helpers.feed, helpers.insert, helpers.source
-local clear, execute, expect, eq, eval = helpers.clear, helpers.execute, helpers.expect, helpers.eq, helpers.eval
-local write_file = helpers.write_file
+local clear, execute, expect = helpers.clear, helpers.execute, helpers.expect
+local source, write_file = helpers.source, helpers.write_file
local function sixlines(text)
local result = ''
- for i = 1, 6 do
+ for _ = 1, 6 do
result = result .. text .. '\n'
end
return result
diff --git a/test/functional/legacy/062_tab_pages_spec.lua b/test/functional/legacy/062_tab_pages_spec.lua
index f1c8b8d58b..c3cdcac471 100644
--- a/test/functional/legacy/062_tab_pages_spec.lua
+++ b/test/functional/legacy/062_tab_pages_spec.lua
@@ -139,6 +139,7 @@ describe('tab pages', function()
autocmd TabLeave * :call add(g:r, 'TabLeave')
autocmd WinLeave * :call add(g:r, 'WinLeave')
autocmd BufLeave * :call add(g:r, 'BufLeave')
+ autocmd TabNew * :call add(g:r, 'TabNew')
let t:a='a'
C tab split
let t:a='b'
@@ -185,11 +186,13 @@ describe('tab pages', function()
=== tab split ===
WinLeave
TabLeave
+ TabNew
WinEnter
TabEnter
=== tabnew ===
WinLeave
TabLeave
+ TabNew
WinEnter
TabEnter
BufLeave
@@ -222,6 +225,7 @@ describe('tab pages', function()
=== tabnew ===
WinLeave
TabLeave
+ TabNew
WinEnter
TabEnter
BufLeave
diff --git a/test/functional/terminal/edit_spec.lua b/test/functional/terminal/edit_spec.lua
new file mode 100644
index 0000000000..6da1521121
--- /dev/null
+++ b/test/functional/terminal/edit_spec.lua
@@ -0,0 +1,75 @@
+local helpers = require('test.functional.helpers')
+local screen = require('test.functional.ui.screen')
+
+local curbufmeths = helpers.curbufmeths
+local curwinmeths = helpers.curwinmeths
+local nvim_dir = helpers.nvim_dir
+local command = helpers.command
+local meths = helpers.meths
+local clear = helpers.clear
+local eq = helpers.eq
+
+describe(':edit term://*', function()
+ local get_screen = function(columns, lines)
+ local scr = screen.new(columns, lines)
+ scr:attach(false)
+ return scr
+ end
+
+ before_each(function()
+ clear()
+ meths.set_option('shell', nvim_dir .. '/shell-test')
+ meths.set_option('shellcmdflag', 'EXE')
+ end)
+
+ it('runs TermOpen event', function()
+ meths.set_var('termopen_runs', {})
+ command('autocmd TermOpen * :call add(g:termopen_runs, expand("<amatch>"))')
+ command('edit term://')
+ local termopen_runs = meths.get_var('termopen_runs')
+ eq(1, #termopen_runs)
+ eq(termopen_runs[1], termopen_runs[1]:match('^term://.//%d+:$'))
+ end)
+
+ it('runs TermOpen early enough to respect terminal_scrollback_buffer_size', function()
+ local columns, lines = 20, 4
+ local scr = get_screen(columns, lines)
+ local rep = 'a'
+ meths.set_option('shellcmdflag', 'REP ' .. rep)
+ local rep_size = rep:byte()
+ local sb = 10
+ local gsb = 20
+ meths.set_var('terminal_scrollback_buffer_size', gsb)
+ command('autocmd TermOpen * :let b:terminal_scrollback_buffer_size = '
+ .. tostring(sb))
+ command('edit term://foobar')
+ local bufcontents = {}
+ local winheight = curwinmeths.get_height()
+ -- I have no idea why there is + 4 needed. But otherwise it works fine with
+ -- different scrollbacks.
+ local shift = -4
+ local buf_cont_start = rep_size - 1 - sb - winheight - shift
+ local bufline = function(i) return ('%d: foobar'):format(i) end
+ for i = buf_cont_start,(rep_size - 1) do
+ bufcontents[#bufcontents + 1] = bufline(i)
+ end
+ bufcontents[#bufcontents + 1] = ''
+ bufcontents[#bufcontents + 1] = '[Process exited 0]'
+ -- Do not ask me why displayed screen is one line *before* buffer
+ -- contents: buffer starts with 87:, screen with 86:.
+ local exp_screen = '\n'
+ local did_cursor = false
+ for i = 0,(winheight - 1) do
+ local line = bufline(buf_cont_start + i - 1)
+ exp_screen = (exp_screen
+ .. (did_cursor and '' or '^')
+ .. line
+ .. (' '):rep(columns - #line)
+ .. '|\n')
+ did_cursor = true
+ end
+ exp_screen = exp_screen .. (' '):rep(columns) .. '|\n'
+ scr:expect(exp_screen)
+ eq(bufcontents, curbufmeths.get_line_slice(1, -1, true, true))
+ end)
+end)
diff --git a/test/functional/ui/bufhl_spec.lua b/test/functional/ui/bufhl_spec.lua
new file mode 100644
index 0000000000..58f5b11de0
--- /dev/null
+++ b/test/functional/ui/bufhl_spec.lua
@@ -0,0 +1,261 @@
+local helpers = require('test.functional.helpers')
+local Screen = require('test.functional.ui.screen')
+local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert
+local execute, request, neq = helpers.execute, helpers.request, helpers.neq
+
+
+describe('Buffer highlighting', function()
+ local screen
+ local curbuf
+
+ local hl_colors = {
+ NonText = Screen.colors.Blue,
+ Question = Screen.colors.SeaGreen,
+ String = Screen.colors.Fuchsia,
+ Statement = Screen.colors.Brown,
+ Special = Screen.colors.SlateBlue,
+ Identifier = Screen.colors.DarkCyan
+ }
+
+ before_each(function()
+ clear()
+ execute("syntax on")
+ screen = Screen.new(40, 8)
+ screen:attach()
+ screen:set_default_attr_ignore( {{bold=true, foreground=hl_colors.NonText}} )
+ screen:set_default_attr_ids({
+ [1] = {foreground = hl_colors.String},
+ [2] = {foreground = hl_colors.Statement, bold = true},
+ [3] = {foreground = hl_colors.Special},
+ [4] = {bold = true, foreground = hl_colors.Special},
+ [5] = {foreground = hl_colors.Identifier},
+ [6] = {bold = true},
+ [7] = {underline = true, bold = true, foreground = hl_colors.Special},
+ [8] = {foreground = hl_colors.Special, underline = true}
+ })
+ curbuf = request('vim_get_current_buffer')
+ end)
+
+ after_each(function()
+ screen:detach()
+ end)
+
+ local function add_hl(...)
+ return request('buffer_add_highlight', curbuf, ...)
+ end
+
+ local function clear_hl(...)
+ return request('buffer_clear_highlight', curbuf, ...)
+ end
+
+
+ it('works', function()
+ insert([[
+ these are some lines
+ with colorful text]])
+ feed('+')
+
+ screen:expect([[
+ these are some lines |
+ with colorful tex^t |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ |
+ ]])
+
+ add_hl(-1, "String", 0 , 10, 14)
+ add_hl(-1, "Statement", 1 , 5, -1)
+
+ screen:expect([[
+ these are {1:some} lines |
+ with {2:colorful tex^t} |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ |
+ ]])
+
+ feed("ggo<esc>")
+ screen:expect([[
+ these are {1:some} lines |
+ ^ |
+ with {2:colorful text} |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ |
+ ]])
+
+ clear_hl(-1, 0 , -1)
+ screen:expect([[
+ these are some lines |
+ ^ |
+ with colorful text |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ |
+ ]])
+ end)
+
+ describe('support adding multiple sources', function()
+ local id1, id2
+ before_each(function()
+ insert([[
+ a longer example
+ in order to demonstrate
+ combining highlights
+ from different sources]])
+
+ execute("hi ImportantWord gui=bold cterm=bold")
+ id1 = add_hl(0, "ImportantWord", 0, 2, 8)
+ add_hl(id1, "ImportantWord", 1, 12, -1)
+ add_hl(id1, "ImportantWord", 2, 0, 9)
+ add_hl(id1, "ImportantWord", 3, 5, 14)
+
+ id2 = add_hl(0, "Special", 0, 2, 8)
+ add_hl(id2, "Identifier", 1, 3, 8)
+ add_hl(id2, "Special", 1, 14, 20)
+ add_hl(id2, "Underlined", 2, 6, 12)
+ add_hl(id2, "Underlined", 3, 0, 9)
+ neq(id1, id2)
+
+ screen:expect([[
+ a {4:longer} example |
+ in {5:order} to {6:de}{4:monstr}{6:ate} |
+ {6:combin}{7:ing}{8: hi}ghlights |
+ {8:from }{7:diff}{6:erent} source^s |
+ ~ |
+ ~ |
+ ~ |
+ :hi ImportantWord gui=bold cterm=bold |
+ ]])
+ end)
+
+ it('and clearing the first added', function()
+ clear_hl(id1, 0, -1)
+ screen:expect([[
+ a {3:longer} example |
+ in {5:order} to de{3:monstr}ate |
+ combin{8:ing hi}ghlights |
+ {8:from diff}erent source^s |
+ ~ |
+ ~ |
+ ~ |
+ :hi ImportantWord gui=bold cterm=bold |
+ ]])
+ end)
+
+ it('and clearing the second added', function()
+ clear_hl(id2, 0, -1)
+ screen:expect([[
+ a {6:longer} example |
+ in order to {6:demonstrate} |
+ {6:combining} highlights |
+ from {6:different} source^s |
+ ~ |
+ ~ |
+ ~ |
+ :hi ImportantWord gui=bold cterm=bold |
+ ]])
+ end)
+
+ it('and clearing line ranges', function()
+ clear_hl(-1, 0, 1)
+ clear_hl(id1, 1, 2)
+ clear_hl(id2, 2, -1)
+ screen:expect([[
+ a longer example |
+ in {5:order} to de{3:monstr}ate |
+ {6:combining} highlights |
+ from {6:different} source^s |
+ ~ |
+ ~ |
+ ~ |
+ :hi ImportantWord gui=bold cterm=bold |
+ ]])
+ end)
+
+ it('and renumbering lines', function()
+ feed('3Gddggo<esc>')
+ screen:expect([[
+ a {4:longer} example |
+ ^ |
+ in {5:order} to {6:de}{4:monstr}{6:ate} |
+ {8:from }{7:diff}{6:erent} sources |
+ ~ |
+ ~ |
+ ~ |
+ |
+ ]])
+
+ execute(':3move 4')
+ screen:expect([[
+ a {4:longer} example |
+ |
+ {8:from }{7:diff}{6:erent} sources |
+ ^in {5:order} to {6:de}{4:monstr}{6:ate} |
+ ~ |
+ ~ |
+ ~ |
+ ::3move 4 |
+ ]])
+ end)
+ end)
+
+ it('prioritizes latest added highlight', function()
+ insert([[
+ three overlapping colors]])
+ add_hl(0, "Identifier", 0, 6, 17)
+ add_hl(0, "String", 0, 14, 23)
+ local id = add_hl(0, "Special", 0, 0, 9)
+
+ screen:expect([[
+ {3:three ove}{5:rlapp}{1:ing color}^s |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ |
+ ]])
+
+ clear_hl(id, 0, 1)
+ screen:expect([[
+ three {5:overlapp}{1:ing color}^s |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ |
+ ]])
+ end)
+
+ it('works with multibyte text', function()
+ insert([[
+ Ta båten över sjön!]])
+ add_hl(-1, "Identifier", 0, 3, 9)
+ add_hl(-1, "String", 0, 16, 21)
+
+ screen:expect([[
+ Ta {5:båten} över {1:sjön}^! |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ ~ |
+ |
+ ]])
+ end)
+end)
diff --git a/test/functional/ui/screen.lua b/test/functional/ui/screen.lua
index 80f46326ee..b35e0cde69 100644
--- a/test/functional/ui/screen.lua
+++ b/test/functional/ui/screen.lua
@@ -219,12 +219,23 @@ function Screen:expect(expected, attr_ids, attr_ignore)
local ids = attr_ids or self._default_attr_ids
local ignore = attr_ignore or self._default_attr_ignore
self:wait(function()
+ local actual_rows = {}
for i = 1, self._height do
- local expected_row = expected_rows[i]
- local actual_row = self:_row_repr(self._rows[i], ids, ignore)
- if expected_row ~= actual_row then
- return 'Row '..tostring(i)..' didn\'t match.\nExpected: "'..
- expected_row..'"\nActual: "'..actual_row..'"'
+ actual_rows[i] = self:_row_repr(self._rows[i], ids, ignore)
+ end
+ for i = 1, self._height do
+ if expected_rows[i] ~= actual_rows[i] then
+ local msg_expected_rows = {}
+ for j = 1, #expected_rows do
+ msg_expected_rows[j] = expected_rows[j]
+ end
+ msg_expected_rows[i] = '*' .. msg_expected_rows[i]
+ actual_rows[i] = '*' .. actual_rows[i]
+ return (
+ 'Row ' .. tostring(i) .. ' didn\'t match.\n'
+ .. 'Expected:\n|' .. table.concat(msg_expected_rows, '|\n|') .. '|\n'
+ .. 'Actual:\n|' .. table.concat(actual_rows, '|\n|') .. '|'
+ )
end
end
end)
diff --git a/test/functional/viml/errorlist_spec.lua b/test/functional/viml/errorlist_spec.lua
new file mode 100644
index 0000000000..78e25297f2
--- /dev/null
+++ b/test/functional/viml/errorlist_spec.lua
@@ -0,0 +1,49 @@
+local helpers = require('test.functional.helpers')
+
+local clear = helpers.clear
+local command = helpers.command
+local eq = helpers.eq
+local get_cur_win_var = helpers.curwinmeths.get_var
+-- local exc_exec = helpers.exc_exec
+
+describe('setqflist()', function()
+ local setqflist = helpers.funcs.setqflist
+
+ before_each(clear)
+
+ it('sets w:quickfix_title', function()
+ setqflist({''}, 'r', 'foo')
+ command('copen')
+ eq(':foo', get_cur_win_var('quickfix_title'))
+ end)
+
+ it('expects a proper type for {title}', function()
+ command('copen')
+ setqflist({''}, 'r', '5')
+ eq(':5', get_cur_win_var('quickfix_title'))
+ setqflist({''}, 'r', 6)
+ eq(':setqflist()', get_cur_win_var('quickfix_title'))
+ -- local exc = exc_exec('call setqflist([""], "r", function("function"))')
+ -- eq('Vim(call):E729: using Funcref as a String', exc)
+ -- exc = exc_exec('call setqflist([""], "r", [])')
+ -- eq('Vim(call):E730: using List as a String', exc)
+ -- exc = exc_exec('call setqflist([""], "r", {})')
+ -- eq('Vim(call):E731: using Dictionary as a String', exc)
+ end)
+end)
+
+describe('setloclist()', function()
+ local setloclist = helpers.funcs.setloclist
+
+ before_each(clear)
+
+ it('sets w:quickfix_title for the correct window', function()
+ command('rightbelow vsplit')
+ setloclist(1, {''}, 'r', 'foo')
+ setloclist(2, {''}, 'r', 'bar')
+ command('lopen')
+ eq(':bar', get_cur_win_var('quickfix_title'))
+ command('lclose | wincmd w | lopen')
+ eq(':foo', get_cur_win_var('quickfix_title'))
+ end)
+end)
diff --git a/test/functional/viml/function_spec.lua b/test/functional/viml/function_spec.lua
new file mode 100644
index 0000000000..665f5d4467
--- /dev/null
+++ b/test/functional/viml/function_spec.lua
@@ -0,0 +1,29 @@
+local helpers = require('test.functional.helpers')
+
+local clear = helpers.clear
+local eq = helpers.eq
+local exc_exec = helpers.exc_exec
+
+describe('Up to MAX_FUNC_ARGS arguments are handled by', function()
+ local max_func_args = 20 -- from eval.h
+ local range = helpers.funcs.range
+
+ before_each(clear)
+
+ it('printf()', function()
+ local printf = helpers.funcs.printf
+ local rep = helpers.funcs['repeat']
+ local expected = '2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,'
+ eq(expected, printf(rep('%d,', max_func_args-1), unpack(range(2, max_func_args))))
+ local ret = exc_exec('call printf("", 2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21)')
+ eq('Vim(call):E740: Too many arguments for function printf', ret)
+ end)
+
+ it('rpcnotify()', function()
+ local rpcnotify = helpers.funcs.rpcnotify
+ local ret = rpcnotify(0, 'foo', unpack(range(3, max_func_args)))
+ eq(1, ret)
+ ret = exc_exec('call rpcnotify(0, "foo", 3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21)')
+ eq('Vim(call):E740: Too many arguments for function rpcnotify', ret)
+ end)
+end)
diff --git a/third-party/CMakeLists.txt b/third-party/CMakeLists.txt
index d7bb620236..08e76ceef0 100644
--- a/third-party/CMakeLists.txt
+++ b/third-party/CMakeLists.txt
@@ -71,8 +71,8 @@ endif()
include(ExternalProject)
-set(LIBUV_URL https://github.com/libuv/libuv/archive/v1.7.3.tar.gz)
-set(LIBUV_SHA256 db5d46318e18330c696d954747036e1be8e2346411d4f30236d7e2f499f0cfab)
+set(LIBUV_URL https://github.com/libuv/libuv/archive/v1.8.0.tar.gz)
+set(LIBUV_SHA256 906e1a5c673c95cb261adeacdb7308a65b4a8f7c9c50d85f3021364951fa9cde)
set(MSGPACK_URL https://github.com/msgpack/msgpack-c/archive/cpp-1.0.0.tar.gz)
set(MSGPACK_SHA256 afda64ca445203bb7092372b822bae8b2539fdcebbfc3f753f393628c2bcfe7d)