aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/FUNDING.yml3
-rw-r--r--runtime/doc/lua.txt284
-rw-r--r--runtime/doc/options.txt8
-rw-r--r--runtime/doc/quickfix.txt39
-rw-r--r--runtime/doc/spell.txt3
-rw-r--r--runtime/doc/treesitter.txt295
-rw-r--r--runtime/doc/vim_diff.txt1
-rw-r--r--runtime/filetype.vim8
-rw-r--r--runtime/lua/vim/lsp/buf.lua45
-rw-r--r--runtime/lua/vim/lsp/callbacks.lua21
-rw-r--r--runtime/lua/vim/lsp/rpc.lua11
-rw-r--r--runtime/lua/vim/lsp/util.lua37
-rw-r--r--runtime/pack/dist/opt/cfilter/plugin/cfilter.vim39
-rw-r--r--src/nvim/api/buffer.c10
-rw-r--r--src/nvim/buffer.c1
-rw-r--r--src/nvim/buffer_defs.h1
-rw-r--r--src/nvim/change.c16
-rw-r--r--src/nvim/eval.c23
-rw-r--r--src/nvim/eval/funcs.c9
-rw-r--r--src/nvim/ex_getln.c34
-rw-r--r--src/nvim/extmark.c14
-rw-r--r--src/nvim/fileio.c3
-rw-r--r--src/nvim/fold.c44
-rw-r--r--src/nvim/fold.h1
-rw-r--r--src/nvim/log.h5
-rw-r--r--src/nvim/macros.h6
-rw-r--r--src/nvim/ops.c29
-rw-r--r--src/nvim/option.c8
-rw-r--r--src/nvim/option_defs.h1
-rw-r--r--src/nvim/options.lua10
-rw-r--r--src/nvim/screen.c440
-rw-r--r--src/nvim/spell.c24
-rw-r--r--src/nvim/syntax.c2
-rw-r--r--src/nvim/testdir/check.vim8
-rw-r--r--src/nvim/testdir/test_autocmd.vim31
-rw-r--r--src/nvim/testdir/test_edit.vim34
-rw-r--r--src/nvim/testdir/test_filetype.vim4
-rw-r--r--src/nvim/testdir/test_functions.vim37
-rw-r--r--src/nvim/testdir/test_gf.vim27
-rw-r--r--src/nvim/testdir/test_search.vim18
-rw-r--r--src/nvim/testdir/test_spell.vim5
-rw-r--r--src/nvim/testdir/test_syntax.vim4
-rw-r--r--src/nvim/testdir/test_timers.vim7
-rw-r--r--src/nvim/window.c6
-rw-r--r--test/functional/api/extmark_spec.lua9
-rw-r--r--test/functional/lua/buffer_updates_spec.lua147
-rw-r--r--test/functional/ui/fold_spec.lua22
47 files changed, 1019 insertions, 815 deletions
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
index 50ef4e6897..90ba2a6d7a 100644
--- a/.github/FUNDING.yml
+++ b/.github/FUNDING.yml
@@ -1 +1,2 @@
-custom: https://salt.bountysource.com/teams/neovim
+github: neovim
+open_collective: neovim
diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt
index 477e70a9ae..2b638a8539 100644
--- a/runtime/doc/lua.txt
+++ b/runtime/doc/lua.txt
@@ -551,290 +551,6 @@ Example: TCP echo-server *tcp-server*
print('TCP echo-server listening on port: '..server:getsockname().port)
------------------------------------------------------------------------------
-VIM.TREESITTER *lua-treesitter*
-
-Nvim integrates the tree-sitter library for incremental parsing of buffers.
-
-Currently Nvim does not provide the tree-sitter parsers, instead these must
-be built separately, for instance using the tree-sitter utility. The only
-exception is a C parser being included in official builds for testing
-purposes. Parsers are searched for as `parser/{lang}.*` in any 'runtimepath'
-directory. A parser can also be loaded manually using a full path: >
-
- vim.treesitter.require_language("python", "/path/to/python.so")
-
-<Create a parser for a buffer and a given language (if another plugin uses the
-same buffer/language combination, it will be safely reused). Use >
-
- parser = vim.treesitter.get_parser(bufnr, lang)
-
-<`bufnr=0` can be used for current buffer. `lang` will default to 'filetype' (this
-doesn't work yet for some filetypes like "cpp") Currently, the parser will be
-retained for the lifetime of a buffer but this is subject to change. A plugin
-should keep a reference to the parser object as long as it wants incremental
-updates.
-
-Parser files *treesitter-parsers*
-
-Parsers are the heart of tree-sitter. They are libraries that tree-sitter will
-search for in the `parser` runtime directory.
-
-For a parser to be available for a given language, there must be a file named
-`{lang}.so` within the parser directory.
-
-Parser methods *lua-treesitter-parser*
-
-tsparser:parse() *tsparser:parse()*
-Whenever you need to access the current syntax tree, parse the buffer: >
-
- tstree = parser:parse()
-
-<This will return an immutable tree that represents the current state of the
-buffer. When the plugin wants to access the state after a (possible) edit
-it should call `parse()` again. If the buffer wasn't edited, the same tree will
-be returned again without extra work. If the buffer was parsed before,
-incremental parsing will be done of the changed parts.
-
-NB: to use the parser directly inside a |nvim_buf_attach| Lua callback, you must
-call `get_parser()` before you register your callback. But preferably parsing
-shouldn't be done directly in the change callback anyway as they will be very
-frequent. Rather a plugin that does any kind of analysis on a tree should use
-a timer to throttle too frequent updates.
-
-tsparser:set_included_ranges({ranges}) *tsparser:set_included_ranges()*
- Changes the ranges the parser should consider. This is used for
- language injection. {ranges} should be of the form (all zero-based): >
- {
- {start_node, end_node},
- ...
- }
-<
- NOTE: `start_node` and `end_node` are both inclusive.
-
-Tree methods *lua-treesitter-tree*
-
-tstree:root() *tstree:root()*
- Return the root node of this tree.
-
-
-Node methods *lua-treesitter-node*
-
-tsnode:parent() *tsnode:parent()*
- Get the node's immediate parent.
-
-tsnode:iter_children() *tsnode:iter_children()*
- Iterates over all the direct children of {tsnode}, regardless of
- wether they are named or not.
- Returns the child node plus the eventual field name corresponding to
- this child node.
-
-tsnode:field({name}) *tsnode:field()*
- Returns a table of the nodes corresponding to the {name} field.
-
-tsnode:child_count() *tsnode:child_count()*
- Get the node's number of children.
-
-tsnode:child({index}) *tsnode:child()*
- Get the node's child at the given {index}, where zero represents the
- first child.
-
-tsnode:named_child_count() *tsnode:named_child_count()*
- Get the node's number of named children.
-
-tsnode:named_child({index}) *tsnode:named_child()*
- Get the node's named child at the given {index}, where zero represents
- the first named child.
-
-tsnode:start() *tsnode:start()*
- Get the node's start position. Return three values: the row, column
- and total byte count (all zero-based).
-
-tsnode:end_() *tsnode:end_()*
- Get the node's end position. Return three values: the row, column
- and total byte count (all zero-based).
-
-tsnode:range() *tsnode:range()*
- Get the range of the node. Return four values: the row, column
- of the start position, then the row, column of the end position.
-
-tsnode:type() *tsnode:type()*
- Get the node's type as a string.
-
-tsnode:symbol() *tsnode:symbol()*
- Get the node's type as a numerical id.
-
-tsnode:named() *tsnode:named()*
- Check if the node is named. Named nodes correspond to named rules in
- the grammar, whereas anonymous nodes correspond to string literals
- in the grammar.
-
-tsnode:missing() *tsnode:missing()*
- Check if the node is missing. Missing nodes are inserted by the
- parser in order to recover from certain kinds of syntax errors.
-
-tsnode:has_error() *tsnode:has_error()*
- Check if the node is a syntax error or contains any syntax errors.
-
-tsnode:sexpr() *tsnode:sexpr()*
- Get an S-expression representing the node as a string.
-
-tsnode:descendant_for_range({start_row}, {start_col}, {end_row}, {end_col})
- *tsnode:descendant_for_range()*
- Get the smallest node within this node that spans the given range of
- (row, column) positions
-
-tsnode:named_descendant_for_range({start_row}, {start_col}, {end_row}, {end_col})
- *tsnode:named_descendant_for_range()*
- Get the smallest named node within this node that spans the given
- range of (row, column) positions
-
-Query methods *lua-treesitter-query*
-
-Tree-sitter queries are supported, with some limitations. Currently, the only
-supported match predicate is `eq?` (both comparing a capture against a string
-and two captures against each other).
-
-vim.treesitter.parse_query({lang}, {query})
- *vim.treesitter.parse_query()*
- Parse {query} as a string. (If the query is in a file, the caller
- should read the contents into a string before calling).
-
-query:iter_captures({node}, {bufnr}, {start_row}, {end_row})
- *query:iter_captures()*
- Iterate over all captures from all matches inside {node}.
- {bufnr} is needed if the query contains predicates, then the caller
- must ensure to use a freshly parsed tree consistent with the current
- text of the buffer. {start_row} and {end_row} can be used to limit
- matches inside a row range (this is typically used with root node
- as the node, i e to get syntax highlight matches in the current
- viewport)
-
- The iterator returns two values, a numeric id identifying the capture
- and the captured node. The following example shows how to get captures
- by name:
->
- for id, node in query:iter_captures(tree:root(), bufnr, first, last) do
- local name = query.captures[id] -- name of the capture in the query
- -- typically useful info about the node:
- local type = node:type() -- type of the captured node
- local row1, col1, row2, col2 = node:range() -- range of the capture
- ... use the info here ...
- end
-<
-query:iter_matches({node}, {bufnr}, {start_row}, {end_row})
- *query:iter_matches()*
- Iterate over all matches within a node. The arguments are the same as
- for |query:iter_captures()| but the iterated values are different:
- an (1-based) index of the pattern in the query, and a table mapping
- capture indices to nodes. If the query has more than one pattern
- the capture table might be sparse, and e.g. `pairs` should be used and not
- `ipairs`. Here an example iterating over all captures in
- every match:
->
- for pattern, match in cquery:iter_matches(tree:root(), bufnr, first, last) do
- for id,node in pairs(match) do
- local name = query.captures[id]
- -- `node` was captured by the `name` capture in the match
- ... use the info here ...
- end
- end
-
-Treesitter Query Predicates *lua-treesitter-predicates*
-
-When writing queries for treesitter, one might use `predicates`, that is,
-special scheme nodes that are evaluted to verify things on a captured node for
-example, the |eq?| predicate : >
- ((identifier) @foo (#eq? @foo "foo"))
-
-This will only match identifier corresponding to the `"foo"` text.
-Here is a list of built-in predicates :
-
- `eq?` *ts-predicate-eq?*
- This predicate will check text correspondance between nodes or
- strings : >
- ((identifier) @foo (#eq? @foo "foo"))
- ((node1) @left (node2) @right (#eq? @left @right))
-<
- `match?` *ts-predicate-match?*
- `vim-match?` *ts-predicate-vim-match?*
- This will match if the provived vim regex matches the text
- corresponding to a node : >
- ((idenfitier) @constant (#match? @constant "^[A-Z_]+$"))
-< Note: the `^` and `$` anchors will respectively match the
- start and end of the node's text.
-
- `lua-match?` *ts-predicate-lua-match?*
- This will match the same way than |match?| but using lua
- regexes.
-
- `contains?` *ts-predicate-contains?*
- Will check if any of the following arguments appears in the
- text corresponding to the node : >
- ((identifier) @foo (#contains? @foo "foo"))
- ((identifier) @foo-bar (#contains @foo-bar "foo" "bar"))
-<
- *lua-treesitter-not-predicate*
-Each predicate has a `not-` prefixed predicate that is just the negation of
-the predicate.
-
- *vim.treesitter.query.add_predicate()*
-vim.treesitter.query.add_predicate({name}, {handler})
-
-This adds a predicate with the name {name} to be used in queries.
-{handler} should be a function whose signature will be : >
- handler(match, pattern, bufnr, predicate)
-<
- *vim.treesitter.query.list_predicates()*
-vim.treesitter.query.list_predicates()
-
-This lists the currently available predicates to use in queries.
-
-Treesitter syntax highlighting (WIP) *lua-treesitter-highlight*
-
-NOTE: This is a partially implemented feature, and not usable as a default
-solution yet. What is documented here is a temporary interface indented
-for those who want to experiment with this feature and contribute to
-its development.
-
-Highlights are defined in the same query format as in the tree-sitter highlight
-crate, which some limitations and additions. Set a highlight query for a
-buffer with this code: >
-
- local query = [[
- "for" @keyword
- "if" @keyword
- "return" @keyword
-
- (string_literal) @string
- (number_literal) @number
- (comment) @comment
-
- (preproc_function_def name: (identifier) @function)
-
- ; ... more definitions
- ]]
-
- highlighter = vim.treesitter.TSHighlighter.new(query, bufnr, lang)
- -- alternatively, to use the current buffer and its filetype:
- -- highlighter = vim.treesitter.TSHighlighter.new(query)
-
- -- Don't recreate the highlighter for the same buffer, instead
- -- modify the query like this:
- local query2 = [[ ... ]]
- highlighter:set_query(query2)
-
-As mentioned above the supported predicate is currently only `eq?`. `match?`
-predicates behave like matching always fails. As an addition a capture which
-begin with an upper-case letter like `@WarningMsg` will map directly to this
-highlight group, if defined. Also if the predicate begins with upper-case and
-contains a dot only the part before the first will be interpreted as the
-highlight group. As an example, this warns of a binary expression with two
-identical identifiers, highlighting both as |hl-WarningMsg|: >
-
- ((binary_expression left: (identifier) @WarningMsg.left right: (identifier) @WarningMsg.right)
- (eq? @WarningMsg.left @WarningMsg.right))
-
-------------------------------------------------------------------------------
VIM.HIGHLIGHT *lua-highlight*
Nvim includes a function for highlighting a selection on yank (see for example
diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt
index 71c4620f1e..bd61d113fb 100644
--- a/runtime/doc/options.txt
+++ b/runtime/doc/options.txt
@@ -5672,6 +5672,14 @@ A jump table for the options with a short description can be found at |Q_op|.
up to the first character that is not an ASCII letter or number and
not a dash. Also see |set-spc-auto|.
+ *'spelloptions'* *'spo'*
+'spelloptions' 'spo' string (default "")
+ local to buffer
+ A comma separated list of options for spell checking:
+ camel When a word is CamelCased, assume "Cased" is a
+ separate word: every upper-case character in a word
+ that comes after a lower case character indicates the
+ start of a new word.
*'spellsuggest'* *'sps'*
'spellsuggest' 'sps' string (default "best")
diff --git a/runtime/doc/quickfix.txt b/runtime/doc/quickfix.txt
index 61e090cc78..188cfc91b6 100644
--- a/runtime/doc/quickfix.txt
+++ b/runtime/doc/quickfix.txt
@@ -501,6 +501,29 @@ EXECUTE A COMMAND IN ALL THE BUFFERS IN QUICKFIX OR LOCATION LIST:
< Otherwise it works the same as `:ldo`.
{not in Vi}
+FILTERING A QUICKFIX OR LOCATION LIST:
+ *cfilter-plugin* *:Cfilter* *:Lfilter*
+If you have too many entries in a quickfix list, you can use the cfilter
+plugin to reduce the number of entries. Load the plugin with: >
+
+ packadd cfilter
+
+Then you can use the following commands to filter a quickfix/location list: >
+
+ :Cfilter[!] /{pat}/
+ :Lfilter[!] /{pat}/
+
+The |:Cfilter| command creates a new quickfix list from the entries matching
+{pat} in the current quickfix list. {pat} is a Vim |regular-expression|
+pattern. Both the file name and the text of the entries are matched against
+{pat}. If the optional ! is supplied, then the entries not matching {pat} are
+used. The pattern can be optionally enclosed using one of the following
+characters: ', ", /. If the pattern is empty, then the last used search
+pattern is used.
+
+The |:Lfilter| command does the same as |:Cfilter| but operates on the current
+location list.
+
=============================================================================
2. The error window *quickfix-window*
@@ -1563,22 +1586,6 @@ The backslashes before the pipe character are required to avoid it to be
recognized as a command separator. The backslash before each space is
required for the set command.
- *cfilter-plugin* *:Cfilter* *:Lfilter*
-If you have too many matching messages, you can use the cfilter plugin to
-reduce the number of entries. Load the plugin with: >
- packadd cfilter
-
-Then you can use these command: >
- :Cfilter[!] /{pat}/
- :Lfilter[!] /{pat}/
-
-:Cfilter creates a new quickfix list from entries matching {pat} in the
-current quickfix list. Both the file name and the text of the entries are
-matched against {pat}. If ! is supplied, then entries not matching {pat} are
-used.
-
-:Lfilter does the same as :Cfilter but operates on the current location list.
-
=============================================================================
8. The directory stack *quickfix-directory-stack*
diff --git a/runtime/doc/spell.txt b/runtime/doc/spell.txt
index 1368c5c35a..0eef976819 100644
--- a/runtime/doc/spell.txt
+++ b/runtime/doc/spell.txt
@@ -187,6 +187,9 @@ When there is a line break right after a sentence the highlighting of the next
line may be postponed. Use |CTRL-L| when needed. Also see |set-spc-auto| for
how it can be set automatically when 'spelllang' is set.
+The 'spelloptions' option has a few more flags that influence the way spell
+checking works.
+
Vim counts the number of times a good word is encountered. This is used to
sort the suggestions: words that have been seen before get a small bonus,
words that have been seen often get a bigger bonus. The COMMON item in the
diff --git a/runtime/doc/treesitter.txt b/runtime/doc/treesitter.txt
new file mode 100644
index 0000000000..7f644486f7
--- /dev/null
+++ b/runtime/doc/treesitter.txt
@@ -0,0 +1,295 @@
+*treesitter.txt* Nvim
+
+
+ NVIM REFERENCE MANUAL
+
+
+Tree-sitter integration *treesitter*
+
+ Type |gO| to see the table of contents.
+
+------------------------------------------------------------------------------
+VIM.TREESITTER *lua-treesitter*
+
+Nvim integrates the tree-sitter library for incremental parsing of buffers.
+
+Currently Nvim does not provide the tree-sitter parsers, instead these must
+be built separately, for instance using the tree-sitter utility. The only
+exception is a C parser being included in official builds for testing
+purposes. Parsers are searched for as `parser/{lang}.*` in any 'runtimepath'
+directory. A parser can also be loaded manually using a full path: >
+
+ vim.treesitter.require_language("python", "/path/to/python.so")
+
+<Create a parser for a buffer and a given language (if another plugin uses the
+same buffer/language combination, it will be safely reused). Use >
+
+ parser = vim.treesitter.get_parser(bufnr, lang)
+
+<`bufnr=0` can be used for current buffer. `lang` will default to 'filetype' (this
+doesn't work yet for some filetypes like "cpp") Currently, the parser will be
+retained for the lifetime of a buffer but this is subject to change. A plugin
+should keep a reference to the parser object as long as it wants incremental
+updates.
+
+Parser files *treesitter-parsers*
+
+Parsers are the heart of tree-sitter. They are libraries that tree-sitter will
+search for in the `parser` runtime directory.
+
+For a parser to be available for a given language, there must be a file named
+`{lang}.so` within the parser directory.
+
+Parser methods *lua-treesitter-parser*
+
+tsparser:parse() *tsparser:parse()*
+Whenever you need to access the current syntax tree, parse the buffer: >
+
+ tstree = parser:parse()
+
+<This will return an immutable tree that represents the current state of the
+buffer. When the plugin wants to access the state after a (possible) edit
+it should call `parse()` again. If the buffer wasn't edited, the same tree will
+be returned again without extra work. If the buffer was parsed before,
+incremental parsing will be done of the changed parts.
+
+NB: to use the parser directly inside a |nvim_buf_attach| Lua callback, you must
+call `get_parser()` before you register your callback. But preferably parsing
+shouldn't be done directly in the change callback anyway as they will be very
+frequent. Rather a plugin that does any kind of analysis on a tree should use
+a timer to throttle too frequent updates.
+
+tsparser:set_included_ranges({ranges}) *tsparser:set_included_ranges()*
+ Changes the ranges the parser should consider. This is used for
+ language injection. {ranges} should be of the form (all zero-based): >
+ {
+ {start_node, end_node},
+ ...
+ }
+<
+ NOTE: `start_node` and `end_node` are both inclusive.
+
+Tree methods *lua-treesitter-tree*
+
+tstree:root() *tstree:root()*
+ Return the root node of this tree.
+
+
+Node methods *lua-treesitter-node*
+
+tsnode:parent() *tsnode:parent()*
+ Get the node's immediate parent.
+
+tsnode:iter_children() *tsnode:iter_children()*
+ Iterates over all the direct children of {tsnode}, regardless of
+ wether they are named or not.
+ Returns the child node plus the eventual field name corresponding to
+ this child node.
+
+tsnode:field({name}) *tsnode:field()*
+ Returns a table of the nodes corresponding to the {name} field.
+
+tsnode:child_count() *tsnode:child_count()*
+ Get the node's number of children.
+
+tsnode:child({index}) *tsnode:child()*
+ Get the node's child at the given {index}, where zero represents the
+ first child.
+
+tsnode:named_child_count() *tsnode:named_child_count()*
+ Get the node's number of named children.
+
+tsnode:named_child({index}) *tsnode:named_child()*
+ Get the node's named child at the given {index}, where zero represents
+ the first named child.
+
+tsnode:start() *tsnode:start()*
+ Get the node's start position. Return three values: the row, column
+ and total byte count (all zero-based).
+
+tsnode:end_() *tsnode:end_()*
+ Get the node's end position. Return three values: the row, column
+ and total byte count (all zero-based).
+
+tsnode:range() *tsnode:range()*
+ Get the range of the node. Return four values: the row, column
+ of the start position, then the row, column of the end position.
+
+tsnode:type() *tsnode:type()*
+ Get the node's type as a string.
+
+tsnode:symbol() *tsnode:symbol()*
+ Get the node's type as a numerical id.
+
+tsnode:named() *tsnode:named()*
+ Check if the node is named. Named nodes correspond to named rules in
+ the grammar, whereas anonymous nodes correspond to string literals
+ in the grammar.
+
+tsnode:missing() *tsnode:missing()*
+ Check if the node is missing. Missing nodes are inserted by the
+ parser in order to recover from certain kinds of syntax errors.
+
+tsnode:has_error() *tsnode:has_error()*
+ Check if the node is a syntax error or contains any syntax errors.
+
+tsnode:sexpr() *tsnode:sexpr()*
+ Get an S-expression representing the node as a string.
+
+tsnode:descendant_for_range({start_row}, {start_col}, {end_row}, {end_col})
+ *tsnode:descendant_for_range()*
+ Get the smallest node within this node that spans the given range of
+ (row, column) positions
+
+tsnode:named_descendant_for_range({start_row}, {start_col}, {end_row}, {end_col})
+ *tsnode:named_descendant_for_range()*
+ Get the smallest named node within this node that spans the given
+ range of (row, column) positions
+
+Query methods *lua-treesitter-query*
+
+Tree-sitter queries are supported, with some limitations. Currently, the only
+supported match predicate is `eq?` (both comparing a capture against a string
+and two captures against each other).
+
+vim.treesitter.parse_query({lang}, {query})
+ *vim.treesitter.parse_query()*
+ Parse {query} as a string. (If the query is in a file, the caller
+ should read the contents into a string before calling).
+
+query:iter_captures({node}, {bufnr}, {start_row}, {end_row})
+ *query:iter_captures()*
+ Iterate over all captures from all matches inside {node}.
+ {bufnr} is needed if the query contains predicates, then the caller
+ must ensure to use a freshly parsed tree consistent with the current
+ text of the buffer. {start_row} and {end_row} can be used to limit
+ matches inside a row range (this is typically used with root node
+ as the node, i e to get syntax highlight matches in the current
+ viewport)
+
+ The iterator returns two values, a numeric id identifying the capture
+ and the captured node. The following example shows how to get captures
+ by name:
+>
+ for id, node in query:iter_captures(tree:root(), bufnr, first, last) do
+ local name = query.captures[id] -- name of the capture in the query
+ -- typically useful info about the node:
+ local type = node:type() -- type of the captured node
+ local row1, col1, row2, col2 = node:range() -- range of the capture
+ ... use the info here ...
+ end
+<
+query:iter_matches({node}, {bufnr}, {start_row}, {end_row})
+ *query:iter_matches()*
+ Iterate over all matches within a node. The arguments are the same as
+ for |query:iter_captures()| but the iterated values are different:
+ an (1-based) index of the pattern in the query, and a table mapping
+ capture indices to nodes. If the query has more than one pattern
+ the capture table might be sparse, and e.g. `pairs` should be used and not
+ `ipairs`. Here an example iterating over all captures in
+ every match:
+>
+ for pattern, match in cquery:iter_matches(tree:root(), bufnr, first, last) do
+ for id,node in pairs(match) do
+ local name = query.captures[id]
+ -- `node` was captured by the `name` capture in the match
+ ... use the info here ...
+ end
+ end
+
+Treesitter Query Predicates *lua-treesitter-predicates*
+
+When writing queries for treesitter, one might use `predicates`, that is,
+special scheme nodes that are evaluted to verify things on a captured node for
+example, the |eq?| predicate : >
+ ((identifier) @foo (#eq? @foo "foo"))
+
+This will only match identifier corresponding to the `"foo"` text.
+Here is a list of built-in predicates :
+
+ `eq?` *ts-predicate-eq?*
+ This predicate will check text correspondance between nodes or
+ strings : >
+ ((identifier) @foo (#eq? @foo "foo"))
+ ((node1) @left (node2) @right (#eq? @left @right))
+<
+ `match?` *ts-predicate-match?*
+ `vim-match?` *ts-predicate-vim-match?*
+ This will match if the provived vim regex matches the text
+ corresponding to a node : >
+ ((idenfitier) @constant (#match? @constant "^[A-Z_]+$"))
+< Note: the `^` and `$` anchors will respectively match the
+ start and end of the node's text.
+
+ `lua-match?` *ts-predicate-lua-match?*
+ This will match the same way than |match?| but using lua
+ regexes.
+
+ `contains?` *ts-predicate-contains?*
+ Will check if any of the following arguments appears in the
+ text corresponding to the node : >
+ ((identifier) @foo (#contains? @foo "foo"))
+ ((identifier) @foo-bar (#contains @foo-bar "foo" "bar"))
+<
+ *lua-treesitter-not-predicate*
+Each predicate has a `not-` prefixed predicate that is just the negation of
+the predicate.
+
+ *vim.treesitter.query.add_predicate()*
+vim.treesitter.query.add_predicate({name}, {handler})
+
+This adds a predicate with the name {name} to be used in queries.
+{handler} should be a function whose signature will be : >
+ handler(match, pattern, bufnr, predicate)
+<
+ *vim.treesitter.query.list_predicates()*
+vim.treesitter.query.list_predicates()
+
+This lists the currently available predicates to use in queries.
+
+Treesitter syntax highlighting (WIP) *lua-treesitter-highlight*
+
+NOTE: This is a partially implemented feature, and not usable as a default
+solution yet. What is documented here is a temporary interface indented
+for those who want to experiment with this feature and contribute to
+its development.
+
+Highlights are defined in the same query format as in the tree-sitter highlight
+crate, which some limitations and additions. Set a highlight query for a
+buffer with this code: >
+
+ local query = [[
+ "for" @keyword
+ "if" @keyword
+ "return" @keyword
+
+ (string_literal) @string
+ (number_literal) @number
+ (comment) @comment
+
+ (preproc_function_def name: (identifier) @function)
+
+ ; ... more definitions
+ ]]
+
+ highlighter = vim.treesitter.TSHighlighter.new(query, bufnr, lang)
+ -- alternatively, to use the current buffer and its filetype:
+ -- highlighter = vim.treesitter.TSHighlighter.new(query)
+
+ -- Don't recreate the highlighter for the same buffer, instead
+ -- modify the query like this:
+ local query2 = [[ ... ]]
+ highlighter:set_query(query2)
+
+As mentioned above the supported predicate is currently only `eq?`. `match?`
+predicates behave like matching always fails. As an addition a capture which
+begin with an upper-case letter like `@WarningMsg` will map directly to this
+highlight group, if defined. Also if the predicate begins with upper-case and
+contains a dot only the part before the first will be interpreted as the
+highlight group. As an example, this warns of a binary expression with two
+identical identifiers, highlighting both as |hl-WarningMsg|: >
+
+ ((binary_expression left: (identifier) @WarningMsg.left right: (identifier) @WarningMsg.right)
+ (eq? @WarningMsg.left @WarningMsg.right))
+
+ vim:tw=78:ts=8:ft=help:norl:
diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt
index 4bcea3e3fe..3090be82f2 100644
--- a/runtime/doc/vim_diff.txt
+++ b/runtime/doc/vim_diff.txt
@@ -508,7 +508,6 @@ Test functions:
test_alloc_fail()
test_autochdir()
test_disable_char_avail()
- test_garbagecollect_now()
test_null_channel()
test_null_dict()
test_null_job()
diff --git a/runtime/filetype.vim b/runtime/filetype.vim
index 32bd6daba0..c464e8cebd 100644
--- a/runtime/filetype.vim
+++ b/runtime/filetype.vim
@@ -1158,10 +1158,10 @@ au BufNewFile,BufRead *.papp,*.pxml,*.pxsl setf papp
au BufNewFile,BufRead */etc/passwd,*/etc/passwd-,*/etc/passwd.edit,*/etc/shadow,*/etc/shadow-,*/etc/shadow.edit,*/var/backups/passwd.bak,*/var/backups/shadow.bak setf passwd
" Pascal (also *.p)
-au BufNewFile,BufRead *.pas setf pascal
+au BufNewFile,BufRead *.pas,*.pp setf pascal
-" Delphi project file
-au BufNewFile,BufRead *.dpr setf pascal
+" Delphi or Lazarus program file
+au BufNewFile,BufRead *.dpr,*.lpr setf pascal
" PDF
au BufNewFile,BufRead *.pdf setf pdf
@@ -1732,7 +1732,7 @@ au BufNewFile,BufRead *.texinfo,*.texi,*.txi setf texinfo
au BufNewFile,BufRead texmf.cnf setf texmf
" Tidy config
-au BufNewFile,BufRead .tidyrc,tidyrc setf tidy
+au BufNewFile,BufRead .tidyrc,tidyrc,tidy.conf setf tidy
" TF mud client
au BufNewFile,BufRead *.tf,.tfrc,tfrc setf tf
diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua
index 3060776d83..c015884f5b 100644
--- a/runtime/lua/vim/lsp/buf.lua
+++ b/runtime/lua/vim/lsp/buf.lua
@@ -1,9 +1,7 @@
local vim = vim
local validate = vim.validate
-local api = vim.api
local vfn = vim.fn
local util = require 'vim.lsp.util'
-local list_extend = vim.list_extend
local M = {}
@@ -154,36 +152,14 @@ end
--@param start_pos ({number, number}, optional) mark-indexed position.
---Defaults to the end of the last visual selection.
function M.range_formatting(options, start_pos, end_pos)
- validate {
- options = {options, 't', true};
- start_pos = {start_pos, 't', true};
- end_pos = {end_pos, 't', true};
- }
+ validate { options = {options, 't', true} }
local sts = vim.bo.softtabstop;
options = vim.tbl_extend('keep', options or {}, {
tabSize = (sts > 0 and sts) or (sts < 0 and vim.bo.shiftwidth) or vim.bo.tabstop;
insertSpaces = vim.bo.expandtab;
})
- local A = list_extend({}, start_pos or api.nvim_buf_get_mark(0, '<'))
- local B = list_extend({}, end_pos or api.nvim_buf_get_mark(0, '>'))
- -- convert to 0-index
- A[1] = A[1] - 1
- B[1] = B[1] - 1
- -- account for encoding.
- if A[2] > 0 then
- A = {A[1], util.character_offset(0, A[1], A[2])}
- end
- if B[2] > 0 then
- B = {B[1], util.character_offset(0, B[1], B[2])}
- end
- local params = {
- textDocument = { uri = vim.uri_from_bufnr(0) };
- range = {
- start = { line = A[1]; character = A[2]; };
- ["end"] = { line = B[1]; character = B[2]; };
- };
- options = options;
- }
+ local params = util.make_given_range_params(start_pos, end_pos)
+ params.options = options
return request('textDocument/rangeFormatting', params)
end
@@ -308,6 +284,21 @@ function M.code_action(context)
request('textDocument/codeAction', params)
end
+--- Performs |vim.lsp.buf.code_action()| for a given range.
+---
+--@param context: (table, optional) Valid `CodeActionContext` object
+--@param start_pos ({number, number}, optional) mark-indexed position.
+---Defaults to the start of the last visual selection.
+--@param end_pos ({number, number}, optional) mark-indexed position.
+---Defaults to the end of the last visual selection.
+function M.range_code_action(context, start_pos, end_pos)
+ validate { context = { context, 't', true } }
+ context = context or { diagnostics = util.get_line_diagnostics() }
+ local params = util.make_given_range_params(start_pos, end_pos)
+ params.context = context
+ request('textDocument/codeAction', params)
+end
+
--- Executes an LSP server command.
---
--@param command A valid `ExecuteCommandParams` object
diff --git a/runtime/lua/vim/lsp/callbacks.lua b/runtime/lua/vim/lsp/callbacks.lua
index 0ee03e6a2f..4e7a8333a0 100644
--- a/runtime/lua/vim/lsp/callbacks.lua
+++ b/runtime/lua/vim/lsp/callbacks.lua
@@ -229,16 +229,19 @@ M['textDocument/implementation'] = location_callback
--@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_signatureHelp
M['textDocument/signatureHelp'] = function(_, method, result)
+ -- When use `autocmd CompleteDone <silent><buffer> lua vim.lsp.buf.signature_help()` to call signatureHelp callback
+ -- If the completion item doesn't have signatures It will make noise. Change to use `print` that can use `<silent>` to ignore
+ if not (result and result.signatures and result.signatures[1]) then
+ print('No signature help available')
+ return
+ end
+ local lines = util.convert_signature_help_to_markdown_lines(result)
+ lines = util.trim_empty_lines(lines)
+ if vim.tbl_isempty(lines) then
+ print('No signature help available')
+ return
+ end
util.focusable_preview(method, function()
- if not (result and result.signatures and result.signatures[1]) then
- return { 'No signature available' }
- end
- -- TODO show popup when signatures is empty?
- local lines = util.convert_signature_help_to_markdown_lines(result)
- lines = util.trim_empty_lines(lines)
- if vim.tbl_isempty(lines) then
- return { 'No signature available' }
- end
return lines, util.try_trim_markdown_code_blocks(lines)
end)
end
diff --git a/runtime/lua/vim/lsp/rpc.lua b/runtime/lua/vim/lsp/rpc.lua
index 64080cf4f2..749a51fecc 100644
--- a/runtime/lua/vim/lsp/rpc.lua
+++ b/runtime/lua/vim/lsp/rpc.lua
@@ -494,10 +494,13 @@ local function start(cmd, cmd_args, handlers, extra_spawn_params)
decoded.error = convert_NIL(decoded.error)
decoded.result = convert_NIL(decoded.result)
- -- Do not surface RequestCancelled to users, it is RPC-internal.
- if decoded.error
- and decoded.error.code == protocol.ErrorCodes.RequestCancelled then
- local _ = log.debug() and log.debug("Received cancellation ack", decoded)
+ -- Do not surface RequestCancelled or ContentModified to users, it is RPC-internal.
+ if decoded.error then
+ if decoded.error.code == protocol.ErrorCodes.RequestCancelled then
+ local _ = log.debug() and log.debug("Received cancellation ack", decoded)
+ elseif decoded.error.code == protocol.ErrorCodes.ContentModified then
+ local _ = log.debug() and log.debug("Received content modified ack", decoded)
+ end
local result_id = tonumber(decoded.id)
-- Clear any callback since this is cancelled now.
-- This is safe to do assuming that these conditions hold:
diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua
index 28bc8cb736..d94a8bb774 100644
--- a/runtime/lua/vim/lsp/util.lua
+++ b/runtime/lua/vim/lsp/util.lua
@@ -1465,11 +1465,46 @@ end
function M.make_range_params()
local position = make_position_param()
return {
- textDocument = { uri = vim.uri_from_bufnr(0) },
+ textDocument = M.make_text_document_params(),
range = { start = position; ["end"] = position; }
}
end
+--- Using the given range in the current buffer, creates an object that
+--- is similar to |vim.lsp.util.make_range_params()|.
+---
+--@param start_pos ({number, number}, optional) mark-indexed position.
+---Defaults to the start of the last visual selection.
+--@param end_pos ({number, number}, optional) mark-indexed position.
+---Defaults to the end of the last visual selection.
+--@returns { textDocument = { uri = `current_file_uri` }, range = { start =
+---`start_position`, end = `end_position` } }
+function M.make_given_range_params(start_pos, end_pos)
+ validate {
+ start_pos = {start_pos, 't', true};
+ end_pos = {end_pos, 't', true};
+ }
+ local A = list_extend({}, start_pos or api.nvim_buf_get_mark(0, '<'))
+ local B = list_extend({}, end_pos or api.nvim_buf_get_mark(0, '>'))
+ -- convert to 0-index
+ A[1] = A[1] - 1
+ B[1] = B[1] - 1
+ -- account for encoding.
+ if A[2] > 0 then
+ A = {A[1], M.character_offset(0, A[1], A[2])}
+ end
+ if B[2] > 0 then
+ B = {B[1], M.character_offset(0, B[1], B[2])}
+ end
+ return {
+ textDocument = M.make_text_document_params(),
+ range = {
+ start = {line = A[1], character = A[2]},
+ ['end'] = {line = B[1], character = B[2]}
+ }
+ }
+end
+
--- Creates a `TextDocumentIdentifier` object for the current buffer.
---
--@returns `TextDocumentIdentifier`
diff --git a/runtime/pack/dist/opt/cfilter/plugin/cfilter.vim b/runtime/pack/dist/opt/cfilter/plugin/cfilter.vim
index 7a6464fc98..fe4455fe2e 100644
--- a/runtime/pack/dist/opt/cfilter/plugin/cfilter.vim
+++ b/runtime/pack/dist/opt/cfilter/plugin/cfilter.vim
@@ -1,15 +1,17 @@
" cfilter.vim: Plugin to filter entries from a quickfix/location list
-" Last Change: May 12, 2018
-" Maintainer: Yegappan Lakshmanan (yegappan AT yahoo DOT com)
-" Version: 1.0
+" Last Change: Aug 23, 2018
+" Maintainer: Yegappan Lakshmanan (yegappan AT yahoo DOT com)
+" Version: 1.1
"
" Commands to filter the quickfix list:
-" :Cfilter[!] {pat}
+" :Cfilter[!] /{pat}/
" Create a new quickfix list from entries matching {pat} in the current
" quickfix list. Both the file name and the text of the entries are
" matched against {pat}. If ! is supplied, then entries not matching
-" {pat} are used.
-" :Lfilter[!] {pat}
+" {pat} are used. The pattern can be optionally enclosed using one of
+" the following characters: ', ", /. If the pattern is empty, then the
+" last used search pattern is used.
+" :Lfilter[!] /{pat}/
" Same as :Cfilter but operates on the current location list.
"
if exists("loaded_cfilter")
@@ -17,7 +19,7 @@ if exists("loaded_cfilter")
endif
let loaded_cfilter = 1
-func s:Qf_filter(qf, pat, bang)
+func s:Qf_filter(qf, searchpat, bang)
if a:qf
let Xgetlist = function('getqflist')
let Xsetlist = function('setqflist')
@@ -28,14 +30,31 @@ func s:Qf_filter(qf, pat, bang)
let cmd = ':Lfilter' . a:bang
endif
+ let firstchar = a:searchpat[0]
+ let lastchar = a:searchpat[-1:]
+ if firstchar == lastchar &&
+ \ (firstchar == '/' || firstchar == '"' || firstchar == "'")
+ let pat = a:searchpat[1:-2]
+ if pat == ''
+ " Use the last search pattern
+ let pat = @/
+ endif
+ else
+ let pat = a:searchpat
+ endif
+
+ if pat == ''
+ return
+ endif
+
if a:bang == '!'
- let cond = 'v:val.text !~# a:pat && bufname(v:val.bufnr) !~# a:pat'
+ let cond = 'v:val.text !~# pat && bufname(v:val.bufnr) !~# pat'
else
- let cond = 'v:val.text =~# a:pat || bufname(v:val.bufnr) =~# a:pat'
+ let cond = 'v:val.text =~# pat || bufname(v:val.bufnr) =~# pat'
endif
let items = filter(Xgetlist(), cond)
- let title = cmd . ' ' . a:pat
+ let title = cmd . ' /' . pat . '/'
call Xsetlist([], ' ', {'title' : title, 'items' : items})
endfunc
diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c
index 6f74ec905c..e77870dcf3 100644
--- a/src/nvim/api/buffer.c
+++ b/src/nvim/api/buffer.c
@@ -1393,15 +1393,17 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id,
}
if (col2 >= 0) {
- if (line2 >= 0) {
- len = STRLEN(ml_get_buf(buf, (linenr_T)line2+1, false));
+ if (line2 >= 0 && line2 < buf->b_ml.ml_line_count) {
+ len = STRLEN(ml_get_buf(buf, (linenr_T)line2 + 1, false));
+ } else if (line2 == buf->b_ml.ml_line_count) {
+ // We are trying to add an extmark past final newline
+ len = 0;
} else {
// reuse len from before
line2 = (int)line;
}
if (col2 > (Integer)len) {
- api_set_error(err, kErrorTypeValidation,
- "end_col value outside range");
+ api_set_error(err, kErrorTypeValidation, "end_col value outside range");
goto error;
}
} else if (line2 >= 0) {
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index 4648631ebe..6f1d9b2365 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -1941,6 +1941,7 @@ void free_buf_options(buf_T *buf, int free_p_ff)
vim_regfree(buf->b_s.b_cap_prog);
buf->b_s.b_cap_prog = NULL;
clear_string_option(&buf->b_s.b_p_spl);
+ clear_string_option(&buf->b_s.b_p_spo);
clear_string_option(&buf->b_p_sua);
clear_string_option(&buf->b_p_ft);
clear_string_option(&buf->b_p_cink);
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h
index bd9cd2f5ec..ea968d9592 100644
--- a/src/nvim/buffer_defs.h
+++ b/src/nvim/buffer_defs.h
@@ -451,6 +451,7 @@ typedef struct {
regprog_T *b_cap_prog; // program for 'spellcapcheck'
char_u *b_p_spf; // 'spellfile'
char_u *b_p_spl; // 'spelllang'
+ char_u *b_p_spo; // 'spelloptions'
int b_cjk; // all CJK letters as OK
char_u b_syn_chartab[32]; // syntax iskeyword option
char_u *b_syn_isk; // iskeyword option
diff --git a/src/nvim/change.c b/src/nvim/change.c
index b8bc08b747..71614363d2 100644
--- a/src/nvim/change.c
+++ b/src/nvim/change.c
@@ -362,8 +362,7 @@ void changed_bytes(linenr_T lnum, colnr_T col)
/// insert/delete bytes at column
///
/// Like changed_bytes() but also adjust extmark for "new" bytes.
-/// When "new" is negative text was deleted.
-static void inserted_bytes(linenr_T lnum, colnr_T col, int old, int new)
+void inserted_bytes(linenr_T lnum, colnr_T col, int old, int new)
{
if (curbuf_splice_pending == 0) {
extmark_splice_cols(curbuf, (int)lnum-1, col, old, new, kExtmarkUndo);
@@ -1677,9 +1676,16 @@ int open_line(
truncate_spaces(saved_line);
}
ml_replace(curwin->w_cursor.lnum, saved_line, false);
- extmark_splice_cols(
- curbuf, (int)curwin->w_cursor.lnum,
- 0, curwin->w_cursor.col, (int)STRLEN(saved_line), kExtmarkUndo);
+
+ int new_len = (int)STRLEN(saved_line);
+
+ // TODO(vigoux): maybe there is issues there with expandtabs ?
+ if (new_len < curwin->w_cursor.col) {
+ extmark_splice_cols(
+ curbuf, (int)curwin->w_cursor.lnum,
+ new_len, curwin->w_cursor.col - new_len, 0, kExtmarkUndo);
+ }
+
saved_line = NULL;
if (did_append) {
changed_lines(curwin->w_cursor.lnum, curwin->w_cursor.col,
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 32830c5d7f..00542e3766 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -4518,7 +4518,6 @@ int get_option_tv(const char **const arg, typval_T *const rettv,
static int get_string_tv(char_u **arg, typval_T *rettv, int evaluate)
{
char_u *p;
- char_u *name;
unsigned int extra = 0;
/*
@@ -4526,11 +4525,14 @@ static int get_string_tv(char_u **arg, typval_T *rettv, int evaluate)
*/
for (p = *arg + 1; *p != NUL && *p != '"'; MB_PTR_ADV(p)) {
if (*p == '\\' && p[1] != NUL) {
- ++p;
- /* A "\<x>" form occupies at least 4 characters, and produces up
- * to 6 characters: reserve space for 2 extra */
- if (*p == '<')
- extra += 2;
+ p++;
+ // A "\<x>" form occupies at least 4 characters, and produces up
+ // to 21 characters (3 * 6 for the char and 3 for a modifier):
+ // reserve space for 18 extra.
+ // Each byte in the char could be encoded as K_SPECIAL K_EXTRA x.
+ if (*p == '<') {
+ extra += 18;
+ }
}
}
@@ -4549,7 +4551,8 @@ static int get_string_tv(char_u **arg, typval_T *rettv, int evaluate)
* Copy the string into allocated memory, handling backslashed
* characters.
*/
- name = xmalloc(p - *arg + extra);
+ const int len = (int)(p - *arg + extra);
+ char_u *name = xmalloc(len);
rettv->v_type = VAR_STRING;
rettv->vval.v_string = name;
@@ -4616,6 +4619,9 @@ static int get_string_tv(char_u **arg, typval_T *rettv, int evaluate)
extra = trans_special((const char_u **)&p, STRLEN(p), name, true, true);
if (extra != 0) {
name += extra;
+ if (name >= rettv->vval.v_string + len) {
+ iemsg("get_string_tv() used more space than allocated");
+ }
break;
}
FALLTHROUGH;
@@ -6962,9 +6968,10 @@ void set_buffer_lines(buf_T *buf, linenr_T lnum_arg, bool append,
if (!append && lnum <= curbuf->b_ml.ml_line_count) {
// Existing line, replace it.
+ int old_len = (int)STRLEN(ml_get(lnum));
if (u_savesub(lnum) == OK
&& ml_replace(lnum, (char_u *)line, true) == OK) {
- changed_bytes(lnum, 0);
+ inserted_bytes(lnum, 0, old_len, STRLEN(line));
if (is_curbuf && lnum == curwin->w_cursor.lnum) {
check_cursor_col();
}
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index 3a4b4f2a50..bd77a3b7e2 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -2584,8 +2584,6 @@ static void f_foldtextresult(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
char_u *text;
char_u buf[FOLD_TEXT_LEN];
- foldinfo_T foldinfo;
- int fold_count;
static bool entered = false;
rettv->v_type = VAR_STRING;
@@ -2599,9 +2597,10 @@ static void f_foldtextresult(typval_T *argvars, typval_T *rettv, FunPtr fptr)
if (lnum < 0) {
lnum = 0;
}
- fold_count = foldedCount(curwin, lnum, &foldinfo);
- if (fold_count > 0) {
- text = get_foldtext(curwin, lnum, lnum + fold_count - 1, &foldinfo, buf);
+
+ foldinfo_T info = fold_info(curwin, lnum);
+ if (info.fi_lines > 0) {
+ text = get_foldtext(curwin, lnum, lnum + info.fi_lines - 1, info, buf);
if (text == buf) {
text = vim_strsave(text);
}
diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c
index 8f2d536e63..f9ca7bfa42 100644
--- a/src/nvim/ex_getln.c
+++ b/src/nvim/ex_getln.c
@@ -288,6 +288,7 @@ static bool do_incsearch_highlighting(int firstc, incsearch_state_T *s,
exarg_T ea;
pos_T save_cursor;
bool use_last_pat;
+ bool retval = false;
*skiplen = 0;
*patlen = ccline.cmdlen;
@@ -307,6 +308,7 @@ static bool do_incsearch_highlighting(int firstc, incsearch_state_T *s,
return false;
}
+ emsg_off++;
memset(&ea, 0, sizeof(ea));
ea.line1 = 1;
ea.line2 = 1;
@@ -318,13 +320,13 @@ static bool do_incsearch_highlighting(int firstc, incsearch_state_T *s,
cmd = skip_range(ea.cmd, NULL);
if (vim_strchr((char_u *)"sgvl", *cmd) == NULL) {
- return false;
+ goto theend;
}
// Skip over "substitute" to find the pattern separator.
for (p = cmd; ASCII_ISALPHA(*p); p++) {}
if (*skipwhite(p) == NUL) {
- return false;
+ goto theend;
}
if (STRNCMP(cmd, "substitute", p - cmd) == 0
@@ -337,12 +339,15 @@ static bool do_incsearch_highlighting(int firstc, incsearch_state_T *s,
p_magic = false;
}
} else if (STRNCMP(cmd, "sort", MAX(p - cmd, 3)) == 0) {
- // skip over flags.
+ // skip over ! and flags
+ if (*p == '!') {
+ p = skipwhite(p + 1);
+ }
while (ASCII_ISALPHA(*(p = skipwhite(p)))) {
p++;
}
if (*p == NUL) {
- return false;
+ goto theend;
}
} else if (STRNCMP(cmd, "vimgrep", MAX(p - cmd, 3)) == 0
|| STRNCMP(cmd, "vimgrepadd", MAX(p - cmd, 8)) == 0
@@ -353,14 +358,14 @@ static bool do_incsearch_highlighting(int firstc, incsearch_state_T *s,
if (*p == '!') {
p++;
if (*skipwhite(p) == NUL) {
- return false;
+ goto theend;
}
}
if (*cmd != 'g') {
delim_optional = true;
}
} else {
- return false;
+ goto theend;
}
p = skipwhite(p);
@@ -369,7 +374,7 @@ static bool do_incsearch_highlighting(int firstc, incsearch_state_T *s,
use_last_pat = end == p && *end == delim;
if (end == p && !use_last_pat) {
- return false;
+ goto theend;
}
// Don't do 'hlsearch' highlighting if the pattern matches everything.
@@ -381,7 +386,7 @@ static bool do_incsearch_highlighting(int firstc, incsearch_state_T *s,
empty = empty_pattern(p);
*end = c;
if (empty) {
- return false;
+ goto theend;
}
}
@@ -409,7 +414,10 @@ static bool do_incsearch_highlighting(int firstc, incsearch_state_T *s,
}
curwin->w_cursor = save_cursor;
- return true;
+ retval = true;
+theend:
+ emsg_off--;
+ return retval;
}
// May do 'incsearch' highlighting if desired.
@@ -549,6 +557,7 @@ static void may_do_incsearch_highlighting(int firstc, long count,
}
update_screen(SOME_VALID);
+ highlight_match = false;
restore_last_search_pattern();
// Leave it at the end to make CTRL-R CTRL-W work. But not when beyond the
@@ -1541,6 +1550,7 @@ static int may_do_command_line_next_incsearch(int firstc, long count,
highlight_match = true;
save_viewstate(&s->old_viewstate);
update_screen(NOT_VALID);
+ highlight_match = false;
redrawcmdline();
curwin->w_cursor = s->match_end;
} else {
@@ -6463,12 +6473,15 @@ static int open_cmdwin(void)
// Save the command line info, can be used recursively.
save_cmdline(&save_ccline);
- /* No Ex mode here! */
+ // No Ex mode here!
exmode_active = 0;
State = NORMAL;
setmouse();
+ // Reset here so it can be set by a CmdWinEnter autocommand.
+ cmdwin_result = 0;
+
// Trigger CmdwinEnter autocommands.
typestr[0] = (char_u)cmdwin_type;
typestr[1] = NUL;
@@ -6484,7 +6497,6 @@ static int open_cmdwin(void)
/*
* Call the main loop until <CR> or CTRL-C is typed.
*/
- cmdwin_result = 0;
normal_enter(true, false);
RedrawingDisabled = i;
diff --git a/src/nvim/extmark.c b/src/nvim/extmark.c
index ba94f55a50..17141f12fd 100644
--- a/src/nvim/extmark.c
+++ b/src/nvim/extmark.c
@@ -568,8 +568,18 @@ void extmark_splice(buf_T *buf,
int new_row, colnr_T new_col, bcount_t new_byte,
ExtmarkOp undo)
{
- long offset = ml_find_line_or_offset(buf, start_row+1, NULL, true);
- extmark_splice_impl(buf, start_row, start_col, offset+start_col,
+ long offset = ml_find_line_or_offset(buf, start_row + 1, NULL, true);
+
+ // On empty buffers, when editing the first line, the line is buffered,
+ // causing offset to be < 0. While the buffer is not actually empty, the
+ // buffered line has not been flushed (and should not be) yet, so the call is
+ // valid but an edge case.
+ //
+ // TODO(vigoux): maybe the is a better way of testing that ?
+ if (offset < 0 && buf->b_ml.ml_chunksize == NULL) {
+ offset = 0;
+ }
+ extmark_splice_impl(buf, start_row, start_col, offset + start_col,
old_row, old_col, old_byte, new_row, new_col, new_byte,
undo);
}
diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c
index 286f2b4fca..ed8b72e2be 100644
--- a/src/nvim/fileio.c
+++ b/src/nvim/fileio.c
@@ -210,7 +210,8 @@ void filemess(buf_T *buf, char_u *name, char_u *s, int attr)
if (msg_silent != 0) {
return;
}
- add_quoted_fname((char *)IObuff, IOSIZE - 80, buf, (const char *)name);
+ add_quoted_fname((char *)IObuff, IOSIZE - 100, buf, (const char *)name);
+ // Avoid an over-long translation to cause trouble.
xstrlcat((char *)IObuff, (const char *)s, IOSIZE);
// For the first message may have to start a new line.
// For further ones overwrite the previous one, reset msg_scroll before
diff --git a/src/nvim/fold.c b/src/nvim/fold.c
index 9994ad3ea8..9188ae6571 100644
--- a/src/nvim/fold.c
+++ b/src/nvim/fold.c
@@ -280,26 +280,31 @@ int foldLevel(linenr_T lnum)
// Return false if line is not folded.
bool lineFolded(win_T *const win, const linenr_T lnum)
{
- return foldedCount(win, lnum, NULL) != 0;
+ return fold_info(win, lnum).fi_lines != 0;
}
-/* foldedCount() {{{2 */
-/*
- * Count the number of lines that are folded at line number "lnum".
- * Normally "lnum" is the first line of a possible fold, and the returned
- * number is the number of lines in the fold.
- * Doesn't use caching from the displayed window.
- * Returns number of folded lines from "lnum", or 0 if line is not folded.
- * When "infop" is not NULL, fills *infop with the fold level info.
- */
-long foldedCount(win_T *win, linenr_T lnum, foldinfo_T *infop)
+/// fold_info() {{{2
+///
+/// Count the number of lines that are folded at line number "lnum".
+/// Normally "lnum" is the first line of a possible fold, and the returned
+/// number is the number of lines in the fold.
+/// Doesn't use caching from the displayed window.
+///
+/// @return with the fold level info.
+/// fi_lines = number of folded lines from "lnum",
+/// or 0 if line is not folded.
+foldinfo_T fold_info(win_T *win, linenr_T lnum)
{
+ foldinfo_T info;
linenr_T last;
- if (hasFoldingWin(win, lnum, NULL, &last, false, infop)) {
- return (long)(last - lnum + 1);
+ if (hasFoldingWin(win, lnum, NULL, &last, false, &info)) {
+ info.fi_lines = (long)(last - lnum + 1);
+ } else {
+ info.fi_lines = 0;
}
- return 0;
+
+ return info;
}
/* foldmethodIsManual() {{{2 */
@@ -1755,7 +1760,7 @@ static void foldDelMarker(
/// When 'foldtext' isn't set puts the result in "buf[FOLD_TEXT_LEN]".
/// Otherwise the result is in allocated memory.
char_u *get_foldtext(win_T *wp, linenr_T lnum, linenr_T lnume,
- foldinfo_T *foldinfo, char_u *buf)
+ foldinfo_T foldinfo, char_u *buf)
FUNC_ATTR_NONNULL_ARG(1)
{
char_u *text = NULL;
@@ -1783,11 +1788,12 @@ char_u *get_foldtext(win_T *wp, linenr_T lnum, linenr_T lnume,
set_vim_var_nr(VV_FOLDSTART, (varnumber_T) lnum);
set_vim_var_nr(VV_FOLDEND, (varnumber_T) lnume);
- /* Set "v:folddashes" to a string of "level" dashes. */
- /* Set "v:foldlevel" to "level". */
- level = foldinfo->fi_level;
- if (level > (int)sizeof(dashes) - 1)
+ // Set "v:folddashes" to a string of "level" dashes.
+ // Set "v:foldlevel" to "level".
+ level = foldinfo.fi_level;
+ if (level > (int)sizeof(dashes) - 1) {
level = (int)sizeof(dashes) - 1;
+ }
memset(dashes, '-', (size_t)level);
dashes[level] = NUL;
set_vim_var_string(VV_FOLDDASHES, dashes, -1);
diff --git a/src/nvim/fold.h b/src/nvim/fold.h
index f35b328fb1..95c4b0c1dc 100644
--- a/src/nvim/fold.h
+++ b/src/nvim/fold.h
@@ -18,6 +18,7 @@ typedef struct foldinfo {
other fields are invalid */
int fi_low_level; /* lowest fold level that starts in the same
line */
+ long fi_lines;
} foldinfo_T;
diff --git a/src/nvim/log.h b/src/nvim/log.h
index 17ff095473..d92b4629ed 100644
--- a/src/nvim/log.h
+++ b/src/nvim/log.h
@@ -5,6 +5,7 @@
#include <stdbool.h>
#include "auto/config.h"
+#include "nvim/macros.h"
#define DEBUG_LOG_LEVEL 0
#define INFO_LOG_LEVEL 1
@@ -68,6 +69,10 @@
# define LOG_CALLSTACK_TO_FILE(fp) log_callstack_to_file(fp, __func__, __LINE__)
#endif
+#if NVIM_HAS_INCLUDE("sanitizer/asan_interface.h")
+# include "sanitizer/asan_interface.h"
+#endif
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "log.h.generated.h"
#endif
diff --git a/src/nvim/macros.h b/src/nvim/macros.h
index 0bbaa87aba..07dcb4a8e8 100644
--- a/src/nvim/macros.h
+++ b/src/nvim/macros.h
@@ -152,6 +152,12 @@
#define STR_(x) #x
#define STR(x) STR_(x)
+#ifndef __has_include
+# define NVIM_HAS_INCLUDE(x) 0
+#else
+# define NVIM_HAS_INCLUDE __has_include
+#endif
+
#ifndef __has_attribute
# define NVIM_HAS_ATTRIBUTE(x) 0
#elif defined(__clang__) && __clang__ == 1 \
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index 1f55d2c315..6209dd6492 100644
--- a/src/nvim/ops.c
+++ b/src/nvim/ops.c
@@ -1544,10 +1544,10 @@ int op_delete(oparg_T *oap)
oap->line_count = 0; // no lines deleted
} else if (oap->motion_type == kMTLineWise) {
if (oap->op_type == OP_CHANGE) {
- /* Delete the lines except the first one. Temporarily move the
- * cursor to the next line. Save the current line number, if the
- * last line is deleted it may be changed.
- */
+ // Delete the lines except the first one. Temporarily move the
+ // cursor to the next line. Save the current line number, if the
+ // last line is deleted it may be changed.
+
if (oap->line_count > 1) {
lnum = curwin->w_cursor.lnum;
++curwin->w_cursor.lnum;
@@ -1560,12 +1560,21 @@ int op_delete(oparg_T *oap)
beginline(BL_WHITE); // cursor on first non-white
did_ai = true; // delete the indent when ESC hit
ai_col = curwin->w_cursor.col;
- } else
- beginline(0); /* cursor in column 0 */
- truncate_line(FALSE); /* delete the rest of the line */
- /* leave cursor past last char in line */
- if (oap->line_count > 1)
- u_clearline(); /* "U" command not possible after "2cc" */
+ } else {
+ beginline(0); // cursor in column 0
+ }
+
+ int old_len = (int)STRLEN(ml_get(curwin->w_cursor.lnum));
+ truncate_line(false); // delete the rest of the line
+
+ extmark_splice_cols(curbuf,
+ (int)curwin->w_cursor.lnum-1, curwin->w_cursor.col,
+ old_len - curwin->w_cursor.col, 0, kExtmarkUndo);
+
+ // leave cursor past last char in line
+ if (oap->line_count > 1) {
+ u_clearline(); // "U" command not possible after "2cc"
+ }
} else {
del_lines(oap->line_count, TRUE);
beginline(BL_WHITE | BL_FIX);
diff --git a/src/nvim/option.c b/src/nvim/option.c
index 8d74cead8d..6ae9378236 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -174,6 +174,7 @@ static char_u *p_syn;
static char_u *p_spc;
static char_u *p_spf;
static char_u *p_spl;
+static char_u *p_spo;
static long p_ts;
static long p_tw;
static int p_udf;
@@ -2285,6 +2286,7 @@ void check_buf_options(buf_T *buf)
check_string_option(&buf->b_s.b_p_spc);
check_string_option(&buf->b_s.b_p_spf);
check_string_option(&buf->b_s.b_p_spl);
+ check_string_option(&buf->b_s.b_p_spo);
check_string_option(&buf->b_p_sua);
check_string_option(&buf->b_p_cink);
check_string_option(&buf->b_p_cino);
@@ -3090,6 +3092,10 @@ ambw_end:
} else if (varp == &(curwin->w_s->b_p_spc)) {
// When 'spellcapcheck' is set compile the regexp program.
errmsg = compile_cap_prog(curwin->w_s);
+ } else if (varp == &(curwin->w_s->b_p_spo)) { // 'spelloptions'
+ if (**varp != NUL && STRCMP("camel", *varp) != 0) {
+ errmsg = e_invarg;
+ }
} else if (varp == &p_sps) { // 'spellsuggest'
if (spell_check_sps() != OK) {
errmsg = e_invarg;
@@ -5896,6 +5902,7 @@ static char_u *get_varp(vimoption_T *p)
case PV_SPC: return (char_u *)&(curwin->w_s->b_p_spc);
case PV_SPF: return (char_u *)&(curwin->w_s->b_p_spf);
case PV_SPL: return (char_u *)&(curwin->w_s->b_p_spl);
+ case PV_SPO: return (char_u *)&(curwin->w_s->b_p_spo);
case PV_SW: return (char_u *)&(curbuf->b_p_sw);
case PV_TFU: return (char_u *)&(curbuf->b_p_tfu);
case PV_TS: return (char_u *)&(curbuf->b_p_ts);
@@ -6175,6 +6182,7 @@ void buf_copy_options(buf_T *buf, int flags)
(void)compile_cap_prog(&buf->b_s);
buf->b_s.b_p_spf = vim_strsave(p_spf);
buf->b_s.b_p_spl = vim_strsave(p_spl);
+ buf->b_s.b_p_spo = vim_strsave(p_spo);
buf->b_p_inde = vim_strsave(p_inde);
buf->b_p_indk = vim_strsave(p_indk);
buf->b_p_fp = empty_option;
diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h
index 02fa7ac216..a09811c8fb 100644
--- a/src/nvim/option_defs.h
+++ b/src/nvim/option_defs.h
@@ -787,6 +787,7 @@ enum {
, BV_SPC
, BV_SPF
, BV_SPL
+ , BV_SPO
, BV_STS
, BV_SUA
, BV_SW
diff --git a/src/nvim/options.lua b/src/nvim/options.lua
index f1221a52a2..02df0ab276 100644
--- a/src/nvim/options.lua
+++ b/src/nvim/options.lua
@@ -2320,6 +2320,16 @@ return {
defaults={if_true={vi="best"}}
},
{
+ full_name='spelloptions', abbreviation='spo',
+ type='string', list='onecomma', scope={'buffer'},
+ deny_duplicates=true,
+ secure=true,
+ vi_def=true,
+ expand=true,
+ varname='p_spo',
+ defaults={if_true={vi="", vim=""}}
+ },
+ {
full_name='splitbelow', abbreviation='sb',
type='bool', scope={'global'},
vi_def=true,
diff --git a/src/nvim/screen.c b/src/nvim/screen.c
index 5ac5306dd9..f3fdafcc70 100644
--- a/src/nvim/screen.c
+++ b/src/nvim/screen.c
@@ -133,8 +133,6 @@ static sattr_T *linebuf_attr = NULL;
static match_T search_hl; /* used for 'hlsearch' highlight matching */
-static foldinfo_T win_foldinfo; /* info for 'foldcolumn' */
-
StlClickDefinition *tab_page_click_defs = NULL;
long tab_page_click_defs_size = 0;
@@ -702,7 +700,6 @@ static void win_update(win_T *wp)
long j;
static int recursive = FALSE; /* being called recursively */
int old_botline = wp->w_botline;
- long fold_count;
// Remember what happened to the previous line.
#define DID_NONE 1 // didn't update a line
#define DID_LINE 2 // updated a normal line
@@ -713,6 +710,7 @@ static void win_update(win_T *wp)
linenr_T mod_bot = 0;
int save_got_int;
+
// If we can compute a change in the automatic sizing of the sign column
// under 'signcolumn=auto:X' and signs currently placed in the buffer, better
// figuring it out here so we can redraw the entire screen for it.
@@ -1229,7 +1227,6 @@ static void win_update(win_T *wp)
// Set the time limit to 'redrawtime'.
proftime_T syntax_tm = profile_setlimit(p_rdt);
syn_set_timeout(&syntax_tm);
- win_foldinfo.fi_level = 0;
/*
* Update all the window rows.
@@ -1459,24 +1456,19 @@ static void win_update(win_T *wp)
* Otherwise, display normally (can be several display lines when
* 'wrap' is on).
*/
- fold_count = foldedCount(wp, lnum, &win_foldinfo);
- if (fold_count != 0) {
- fold_line(wp, fold_count, &win_foldinfo, lnum, row);
- ++row;
- --fold_count;
- wp->w_lines[idx].wl_folded = TRUE;
- wp->w_lines[idx].wl_lastlnum = lnum + fold_count;
- did_update = DID_FOLD;
- } else if (idx < wp->w_lines_valid
- && wp->w_lines[idx].wl_valid
- && wp->w_lines[idx].wl_lnum == lnum
- && lnum > wp->w_topline
- && !(dy_flags & (DY_LASTLINE | DY_TRUNCATE))
- && srow + wp->w_lines[idx].wl_size > wp->w_grid.Rows
- && diff_check_fill(wp, lnum) == 0
- ) {
- /* This line is not going to fit. Don't draw anything here,
- * will draw "@ " lines below. */
+ foldinfo_T foldinfo = fold_info(wp, lnum);
+
+ if (foldinfo.fi_lines == 0
+ && idx < wp->w_lines_valid
+ && wp->w_lines[idx].wl_valid
+ && wp->w_lines[idx].wl_lnum == lnum
+ && lnum > wp->w_topline
+ && !(dy_flags & (DY_LASTLINE | DY_TRUNCATE))
+ && srow + wp->w_lines[idx].wl_size > wp->w_grid.Rows
+ && diff_check_fill(wp, lnum) == 0
+ ) {
+ // This line is not going to fit. Don't draw anything here,
+ // will draw "@ " lines below.
row = wp->w_grid.Rows + 1;
} else {
prepare_search_hl(wp, lnum);
@@ -1485,14 +1477,21 @@ static void win_update(win_T *wp)
&& syntax_present(wp))
syntax_end_parsing(syntax_last_parsed + 1);
- /*
- * Display one line.
- */
- row = win_line(wp, lnum, srow, wp->w_grid.Rows, mod_top == 0, false);
+ // Display one line
+ row = win_line(wp, lnum, srow,
+ foldinfo.fi_lines ? srow : wp->w_grid.Rows,
+ mod_top == 0, false, foldinfo);
- wp->w_lines[idx].wl_folded = FALSE;
+ wp->w_lines[idx].wl_folded = foldinfo.fi_lines != 0;
wp->w_lines[idx].wl_lastlnum = lnum;
did_update = DID_LINE;
+
+ if (foldinfo.fi_lines > 0) {
+ did_update = DID_FOLD;
+ foldinfo.fi_lines--;
+ wp->w_lines[idx].wl_lastlnum = lnum + foldinfo.fi_lines;
+ }
+
syntax_last_parsed = lnum;
}
@@ -1507,20 +1506,17 @@ static void win_update(win_T *wp)
idx++;
break;
}
- if (dollar_vcol == -1)
+ if (dollar_vcol == -1) {
wp->w_lines[idx].wl_size = row - srow;
- ++idx;
- lnum += fold_count + 1;
+ }
+ idx++;
+ lnum += foldinfo.fi_lines + 1;
} else {
if (wp->w_p_rnu) {
// 'relativenumber' set: The text doesn't need to be drawn, but
// the number column nearly always does.
- fold_count = foldedCount(wp, lnum, &win_foldinfo);
- if (fold_count != 0) {
- fold_line(wp, fold_count, &win_foldinfo, lnum, row);
- } else {
- (void)win_line(wp, lnum, srow, wp->w_grid.Rows, true, true);
- }
+ foldinfo_T info = fold_info(wp, lnum);
+ (void)win_line(wp, lnum, srow, wp->w_grid.Rows, true, true, info);
}
// This line does not need to be drawn, advance to the next one.
@@ -1752,31 +1748,6 @@ static int advance_color_col(int vcol, int **color_cols)
return **color_cols >= 0;
}
-// Returns the next grid column.
-static int text_to_screenline(win_T *wp, char_u *text, int col, int off)
- FUNC_ATTR_NONNULL_ALL
-{
- int idx = wp->w_p_rl ? off : off + col;
- LineState s = LINE_STATE(text);
-
- while (*s.p != NUL) {
- // TODO(bfredl): cargo-culted from the old Vim code:
- // if(col + cells > wp->w_width - (wp->w_p_rl ? col : 0)) { break; }
- // This is obvious wrong. If Vim ever fixes this, solve for "cells" again
- // in the correct condition.
- const int maxcells = wp->w_grid.Columns - col - (wp->w_p_rl ? col : 0);
- const int cells = line_putchar(&s, &linebuf_char[idx], maxcells,
- wp->w_p_rl);
- if (cells == -1) {
- break;
- }
- col += cells;
- idx += cells;
- }
-
- return col;
-}
-
// Compute the width of the foldcolumn. Based on 'foldcolumn' and how much
// space is available for window "wp", minus "col".
static int compute_foldcolumn(win_T *wp, int col)
@@ -1841,271 +1812,6 @@ static int line_putchar(LineState *s, schar_T *dest, int maxcells, bool rl)
return cells;
}
-/*
- * Display one folded line.
- */
-static void fold_line(win_T *wp, long fold_count, foldinfo_T *foldinfo, linenr_T lnum, int row)
-{
- char_u buf[FOLD_TEXT_LEN];
- pos_T *top, *bot;
- linenr_T lnume = lnum + fold_count - 1;
- int len;
- char_u *text;
- int fdc;
- int col;
- int txtcol;
- int off;
-
- /* Build the fold line:
- * 1. Add the cmdwin_type for the command-line window
- * 2. Add the 'foldcolumn'
- * 3. Add the 'number' or 'relativenumber' column
- * 4. Compose the text
- * 5. Add the text
- * 6. set highlighting for the Visual area an other text
- */
- col = 0;
- off = 0;
-
- /*
- * 1. Add the cmdwin_type for the command-line window
- * Ignores 'rightleft', this window is never right-left.
- */
- if (cmdwin_type != 0 && wp == curwin) {
- schar_from_ascii(linebuf_char[off], cmdwin_type);
- linebuf_attr[off] = win_hl_attr(wp, HLF_AT);
- col++;
- }
-
-# define RL_MEMSET(p, v, l) \
- do { \
- if (wp->w_p_rl) { \
- for (int ri = 0; ri < l; ri++) { \
- linebuf_attr[off + (wp->w_grid.Columns - (p) - (l)) + ri] = v; \
- } \
- } else { \
- for (int ri = 0; ri < l; ri++) { \
- linebuf_attr[off + (p) + ri] = v; \
- } \
- } \
- } while (0)
-
- // 2. Add the 'foldcolumn'
- // Reduce the width when there is not enough space.
- fdc = compute_foldcolumn(wp, col);
- if (fdc > 0) {
- fill_foldcolumn(buf, wp, true, lnum);
- const char_u *it = &buf[0];
- for (int i = 0; i < fdc; i++) {
- int mb_c = mb_ptr2char_adv(&it);
- if (wp->w_p_rl) {
- schar_from_char(linebuf_char[off + wp->w_grid.Columns - i - 1 - col],
- mb_c);
- } else {
- schar_from_char(linebuf_char[off + col + i], mb_c);
- }
- }
- RL_MEMSET(col, win_hl_attr(wp, HLF_FC), fdc);
- col += fdc;
- }
-
- /* Set all attributes of the 'number' or 'relativenumber' column and the
- * text */
- RL_MEMSET(col, win_hl_attr(wp, HLF_FL), wp->w_grid.Columns - col);
-
- // If signs are being displayed, add spaces.
- if (win_signcol_count(wp) > 0) {
- len = wp->w_grid.Columns - col;
- if (len > 0) {
- int len_max = win_signcol_width(wp) * win_signcol_count(wp);
- if (len > len_max) {
- len = len_max;
- }
- char_u space_buf[18] = " ";
- assert((size_t)len_max <= sizeof(space_buf));
- copy_text_attr(off + col, space_buf, len,
- win_hl_attr(wp, HLF_FL));
- col += len;
- }
- }
-
- /*
- * 3. Add the 'number' or 'relativenumber' column
- */
- if (wp->w_p_nu || wp->w_p_rnu) {
- len = wp->w_grid.Columns - col;
- if (len > 0) {
- int w = number_width(wp);
- long num;
- char *fmt = "%*ld ";
-
- if (len > w + 1)
- len = w + 1;
-
- if (wp->w_p_nu && !wp->w_p_rnu)
- /* 'number' + 'norelativenumber' */
- num = (long)lnum;
- else {
- /* 'relativenumber', don't use negative numbers */
- num = labs((long)get_cursor_rel_lnum(wp, lnum));
- if (num == 0 && wp->w_p_nu && wp->w_p_rnu) {
- /* 'number' + 'relativenumber': cursor line shows absolute
- * line number */
- num = lnum;
- fmt = "%-*ld ";
- }
- }
-
- snprintf((char *)buf, FOLD_TEXT_LEN, fmt, w, num);
- if (wp->w_p_rl) {
- // the line number isn't reversed
- copy_text_attr(off + wp->w_grid.Columns - len - col, buf, len,
- win_hl_attr(wp, HLF_FL));
- } else {
- copy_text_attr(off + col, buf, len, win_hl_attr(wp, HLF_FL));
- }
- col += len;
- }
- }
-
- /*
- * 4. Compose the folded-line string with 'foldtext', if set.
- */
- text = get_foldtext(wp, lnum, lnume, foldinfo, buf);
-
- txtcol = col; /* remember where text starts */
-
- // 5. move the text to linebuf_char[off]. Fill up with "fold".
- // Right-left text is put in columns 0 - number-col, normal text is put
- // in columns number-col - window-width.
- col = text_to_screenline(wp, text, col, off);
-
- /* Fill the rest of the line with the fold filler */
- if (wp->w_p_rl)
- col -= txtcol;
-
- schar_T sc;
- schar_from_char(sc, wp->w_p_fcs_chars.fold);
- while (col < wp->w_grid.Columns
- - (wp->w_p_rl ? txtcol : 0)
- ) {
- schar_copy(linebuf_char[off+col++], sc);
- }
-
- if (text != buf)
- xfree(text);
-
- /*
- * 6. set highlighting for the Visual area an other text.
- * If all folded lines are in the Visual area, highlight the line.
- */
- if (VIsual_active && wp->w_buffer == curwin->w_buffer) {
- if (ltoreq(curwin->w_cursor, VIsual)) {
- /* Visual is after curwin->w_cursor */
- top = &curwin->w_cursor;
- bot = &VIsual;
- } else {
- /* Visual is before curwin->w_cursor */
- top = &VIsual;
- bot = &curwin->w_cursor;
- }
- if (lnum >= top->lnum
- && lnume <= bot->lnum
- && (VIsual_mode != 'v'
- || ((lnum > top->lnum
- || (lnum == top->lnum
- && top->col == 0))
- && (lnume < bot->lnum
- || (lnume == bot->lnum
- && (bot->col - (*p_sel == 'e'))
- >= (colnr_T)STRLEN(ml_get_buf(wp->w_buffer, lnume,
- FALSE))))))) {
- if (VIsual_mode == Ctrl_V) {
- // Visual block mode: highlight the chars part of the block
- if (wp->w_old_cursor_fcol + txtcol < (colnr_T)wp->w_grid.Columns) {
- if (wp->w_old_cursor_lcol != MAXCOL
- && wp->w_old_cursor_lcol + txtcol
- < (colnr_T)wp->w_grid.Columns) {
- len = wp->w_old_cursor_lcol;
- } else {
- len = wp->w_grid.Columns - txtcol;
- }
- RL_MEMSET(wp->w_old_cursor_fcol + txtcol, win_hl_attr(wp, HLF_V),
- len - (int)wp->w_old_cursor_fcol);
- }
- } else {
- // Set all attributes of the text
- RL_MEMSET(txtcol, win_hl_attr(wp, HLF_V), wp->w_grid.Columns - txtcol);
- }
- }
- }
-
- // Show colorcolumn in the fold line, but let cursorcolumn override it.
- if (wp->w_p_cc_cols) {
- int i = 0;
- int j = wp->w_p_cc_cols[i];
- int old_txtcol = txtcol;
-
- while (j > -1) {
- txtcol += j;
- if (wp->w_p_wrap) {
- txtcol -= wp->w_skipcol;
- } else {
- txtcol -= wp->w_leftcol;
- }
- if (txtcol >= 0 && txtcol < wp->w_grid.Columns) {
- linebuf_attr[off + txtcol] =
- hl_combine_attr(linebuf_attr[off + txtcol], win_hl_attr(wp, HLF_MC));
- }
- txtcol = old_txtcol;
- j = wp->w_p_cc_cols[++i];
- }
- }
-
- /* Show 'cursorcolumn' in the fold line. */
- if (wp->w_p_cuc) {
- txtcol += wp->w_virtcol;
- if (wp->w_p_wrap)
- txtcol -= wp->w_skipcol;
- else
- txtcol -= wp->w_leftcol;
- if (txtcol >= 0 && txtcol < wp->w_grid.Columns) {
- linebuf_attr[off + txtcol] = hl_combine_attr(
- linebuf_attr[off + txtcol], win_hl_attr(wp, HLF_CUC));
- }
- }
-
- grid_put_linebuf(&wp->w_grid, row, 0, wp->w_grid.Columns, wp->w_grid.Columns,
- false, wp, wp->w_hl_attr_normal, false);
-
- /*
- * Update w_cline_height and w_cline_folded if the cursor line was
- * updated (saves a call to plines() later).
- */
- if (wp == curwin
- && lnum <= curwin->w_cursor.lnum
- && lnume >= curwin->w_cursor.lnum) {
- curwin->w_cline_row = row;
- curwin->w_cline_height = 1;
- curwin->w_cline_folded = true;
- curwin->w_valid |= (VALID_CHEIGHT|VALID_CROW);
- conceal_cursor_used = conceal_cursor_line(curwin);
- }
-}
-
-
-/// Copy "buf[len]" to linebuf_char["off"] and set attributes to "attr".
-///
-/// Only works for ASCII text!
-static void copy_text_attr(int off, char_u *buf, int len, int attr)
-{
- int i;
-
- for (i = 0; i < len; i++) {
- schar_from_ascii(linebuf_char[off + i], buf[i]);
- linebuf_attr[off + i] = attr;
- }
-}
/// Fills the foldcolumn at "p" for window "wp".
/// Only to be called when 'foldcolumn' > 0.
@@ -2120,7 +1826,7 @@ static size_t
fill_foldcolumn(
char_u *p,
win_T *wp,
- int closed,
+ foldinfo_T foldinfo,
linenr_T lnum
)
{
@@ -2131,10 +1837,11 @@ fill_foldcolumn(
size_t char_counter = 0;
int symbol = 0;
int len = 0;
+ bool closed = foldinfo.fi_lines > 0;
// Init to all spaces.
memset(p, ' ', MAX_MCO * fdc + 1);
- level = win_foldinfo.fi_level;
+ level = foldinfo.fi_level;
// If the column is too narrow, we start at the lowest level that
// fits and use numbers to indicated the depth.
@@ -2144,8 +1851,8 @@ fill_foldcolumn(
}
for (i = 0; i < MIN(fdc, level); i++) {
- if (win_foldinfo.fi_lnum == lnum
- && first_level + i >= win_foldinfo.fi_low_level) {
+ if (foldinfo.fi_lnum == lnum
+ && first_level + i >= foldinfo.fi_low_level) {
symbol = wp->w_p_fcs_chars.foldopen;
} else if (first_level == 1) {
symbol = wp->w_p_fcs_chars.foldsep;
@@ -2176,21 +1883,27 @@ fill_foldcolumn(
return MAX(char_counter + (fdc-i), (size_t)fdc);
}
-/*
- * Display line "lnum" of window 'wp' on the screen.
- * Start at row "startrow", stop when "endrow" is reached.
- * wp->w_virtcol needs to be valid.
- *
- * Return the number of last row the line occupies.
- */
+
+/// Display line "lnum" of window 'wp' on the screen.
+/// Start at row "startrow", stop when "endrow" is reached.
+/// wp->w_virtcol needs to be valid.
+///
+/// @param lnum line to display
+/// @param endrow stop drawing once reaching this row
+/// @param nochange not updating for changed text
+/// @param number_only only update the number column
+/// @param foldinfo fold info for this line
+///
+/// @return the number of last row the line occupies.
static int
win_line (
win_T *wp,
linenr_T lnum,
int startrow,
int endrow,
- bool nochange, // not updating for changed text
- bool number_only // only update the number column
+ bool nochange,
+ bool number_only,
+ foldinfo_T foldinfo
)
{
int c = 0; // init for GCC
@@ -2295,6 +2008,8 @@ win_line (
bool has_decorations = false; // this buffer has decorations
bool do_virttext = false; // draw virtual text for this line
+ char_u buf_fold[FOLD_TEXT_LEN + 1]; // Hold value returned by get_foldtext
+
/* draw_state: items that are drawn in sequence: */
#define WL_START 0 /* nothing done yet */
# define WL_CMDLINE WL_START + 1 /* cmdline window column */
@@ -2846,7 +2561,7 @@ win_line (
// already be in use.
xfree(p_extra_free);
p_extra_free = xmalloc(MAX_MCO * fdc + 1);
- n_extra = fill_foldcolumn(p_extra_free, wp, false, lnum);
+ n_extra = fill_foldcolumn(p_extra_free, wp, foldinfo, lnum);
p_extra_free[n_extra] = NUL;
p_extra = p_extra_free;
c_extra = NUL;
@@ -3072,6 +2787,41 @@ win_line (
break;
}
+ if (draw_state == WL_LINE
+ && foldinfo.fi_level != 0
+ && foldinfo.fi_lines > 0
+ && vcol == 0
+ && n_extra == 0
+ && row == startrow) {
+ char_attr = win_hl_attr(wp, HLF_FL);
+
+ linenr_T lnume = lnum + foldinfo.fi_lines - 1;
+ memset(buf_fold, ' ', FOLD_TEXT_LEN);
+ p_extra = get_foldtext(wp, lnum, lnume, foldinfo, buf_fold);
+ n_extra = STRLEN(p_extra);
+
+ if (p_extra != buf_fold) {
+ xfree(p_extra_free);
+ p_extra_free = p_extra;
+ }
+ c_extra = NUL;
+ c_final = NUL;
+ p_extra[n_extra] = NUL;
+ }
+
+ if (draw_state == WL_LINE
+ && foldinfo.fi_level != 0
+ && foldinfo.fi_lines > 0
+ && col < grid->Columns
+ && n_extra == 0
+ && row == startrow) {
+ // fill rest of line with 'fold'
+ c_extra = wp->w_p_fcs_chars.fold;
+ c_final = NUL;
+
+ n_extra = wp->w_p_rl ? (col + 1) : (grid->Columns - col);
+ }
+
if (draw_state == WL_LINE && (area_highlighting || has_spell)) {
// handle Visual or match highlighting in this line
if (vcol == fromcol
@@ -3300,6 +3050,10 @@ win_line (
p_extra++;
}
n_extra--;
+ } else if (foldinfo.fi_lines > 0) {
+ // skip writing the buffer line itself
+ c = NUL;
+ XFREE_CLEAR(p_extra_free);
} else {
int c0;
@@ -3868,7 +3622,7 @@ win_line (
// not showing the '>', put pointer back to avoid getting stuck
ptr++;
}
- }
+ } // end of printing from buffer content
/* In the cursor line and we may be concealing characters: correct
* the cursor column when we reach its position. */
@@ -4178,11 +3932,10 @@ win_line (
if (wp == curwin && lnum == curwin->w_cursor.lnum) {
curwin->w_cline_row = startrow;
curwin->w_cline_height = row - startrow;
- curwin->w_cline_folded = false;
+ curwin->w_cline_folded = foldinfo.fi_lines > 0;
curwin->w_valid |= (VALID_CHEIGHT|VALID_CROW);
conceal_cursor_used = conceal_cursor_line(curwin);
}
-
break;
}
@@ -4371,6 +4124,7 @@ win_line (
* so far. If there is no more to display it is caught above.
*/
if ((wp->w_p_rl ? (col < 0) : (col >= grid->Columns))
+ && foldinfo.fi_lines == 0
&& (*ptr != NUL
|| filler_todo > 0
|| (wp->w_p_list && wp->w_p_lcs_chars.eol != NUL
diff --git a/src/nvim/spell.c b/src/nvim/spell.c
index dc1bfe25b4..f036d7fe04 100644
--- a/src/nvim/spell.c
+++ b/src/nvim/spell.c
@@ -362,6 +362,8 @@ size_t spell_check(
size_t wrongcaplen = 0;
int lpi;
bool count_word = docount;
+ bool use_camel_case = *wp->w_s->b_p_spo != NUL;
+ bool camel_case = false;
// A word never starts at a space or a control character. Return quickly
// then, skipping over the character.
@@ -394,9 +396,24 @@ size_t spell_check(
mi.mi_word = ptr;
mi.mi_fend = ptr;
if (spell_iswordp(mi.mi_fend, wp)) {
+ int prev_upper;
+ int this_upper;
+
+ if (use_camel_case) {
+ c = PTR2CHAR(mi.mi_fend);
+ this_upper = SPELL_ISUPPER(c);
+ }
+
do {
MB_PTR_ADV(mi.mi_fend);
- } while (*mi.mi_fend != NUL && spell_iswordp(mi.mi_fend, wp));
+ if (use_camel_case) {
+ prev_upper = this_upper;
+ c = PTR2CHAR(mi.mi_fend);
+ this_upper = SPELL_ISUPPER(c);
+ camel_case = !prev_upper && this_upper;
+ }
+ } while (*mi.mi_fend != NUL && spell_iswordp(mi.mi_fend, wp)
+ && !camel_case);
if (capcol != NULL && *capcol == 0 && wp->w_s->b_cap_prog != NULL) {
// Check word starting with capital letter.
@@ -428,6 +445,11 @@ size_t spell_check(
(void)spell_casefold(ptr, (int)(mi.mi_fend - ptr), mi.mi_fword, MAXWLEN + 1);
mi.mi_fwordlen = (int)STRLEN(mi.mi_fword);
+ if (camel_case) {
+ // introduce a fake word end space into the folded word.
+ mi.mi_fword[mi.mi_fwordlen - 1] = ' ';
+ }
+
// The word is bad unless we recognize it.
mi.mi_result = SP_BAD;
mi.mi_result2 = SP_BAD;
diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c
index 4aa7c21ce4..9a9cc45c6b 100644
--- a/src/nvim/syntax.c
+++ b/src/nvim/syntax.c
@@ -5588,9 +5588,11 @@ void ex_ownsyntax(exarg_T *eap)
hash_init(&curwin->w_s->b_keywtab_ic);
// TODO: Keep the spell checking as it was. NOLINT(readability/todo)
curwin->w_p_spell = false; // No spell checking
+ // make sure option values are "empty_option" instead of NULL
clear_string_option(&curwin->w_s->b_p_spc);
clear_string_option(&curwin->w_s->b_p_spf);
clear_string_option(&curwin->w_s->b_p_spl);
+ clear_string_option(&curwin->w_s->b_p_spo);
clear_string_option(&curwin->w_s->b_syn_isk);
}
diff --git a/src/nvim/testdir/check.vim b/src/nvim/testdir/check.vim
index 073873bcb0..e0ebe8fd49 100644
--- a/src/nvim/testdir/check.vim
+++ b/src/nvim/testdir/check.vim
@@ -65,3 +65,11 @@ func CheckCanRunGui()
throw 'Skipped: cannot start the GUI'
endif
endfunc
+
+" Command to check that not currently using the GUI
+command CheckNotGui call CheckNotGui()
+func CheckNotGui()
+ if has('gui_running')
+ throw 'Skipped: only works in the terminal'
+ endif
+endfunc
diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim
index 3dd68873d4..094bb3ebd1 100644
--- a/src/nvim/testdir/test_autocmd.vim
+++ b/src/nvim/testdir/test_autocmd.vim
@@ -1,6 +1,8 @@
" Tests for autocommands
source shared.vim
+source check.vim
+source term_util.vim
func! s:cleanup_buffers() abort
for bnr in range(1, bufnr('$'))
@@ -1735,6 +1737,35 @@ func Test_throw_in_BufWritePre()
au! throwing
endfunc
+func Test_autocmd_CmdWinEnter()
+ CheckRunVimInTerminal
+ " There is not cmdwin switch, so
+ " test for cmdline_hist
+ " (both are available with small builds)
+ CheckFeature cmdline_hist
+ let lines =<< trim END
+ let b:dummy_var = 'This is a dummy'
+ autocmd CmdWinEnter * quit
+ let winnr = winnr('$')
+ END
+ let filename='XCmdWinEnter'
+ call writefile(lines, filename)
+ let buf = RunVimInTerminal('-S '.filename, #{rows: 6})
+
+ call term_sendkeys(buf, "q:")
+ call term_wait(buf)
+ call term_sendkeys(buf, ":echo b:dummy_var\<cr>")
+ call WaitForAssert({-> assert_match('^This is a dummy', term_getline(buf, 6))}, 1000)
+ call term_sendkeys(buf, ":echo &buftype\<cr>")
+ call WaitForAssert({-> assert_notmatch('^nofile', term_getline(buf, 6))}, 1000)
+ call term_sendkeys(buf, ":echo winnr\<cr>")
+ call WaitForAssert({-> assert_match('^1', term_getline(buf, 6))}, 1000)
+
+ " clean up
+ call StopVimInTerminal(buf)
+ call delete(filename)
+endfunc
+
func Test_FileChangedShell_reload()
if !has('unix')
return
diff --git a/src/nvim/testdir/test_edit.vim b/src/nvim/testdir/test_edit.vim
index 12d5d9790e..7f456ffbce 100644
--- a/src/nvim/testdir/test_edit.vim
+++ b/src/nvim/testdir/test_edit.vim
@@ -1,9 +1,11 @@
" Test for edit functions
-"
+
if exists("+t_kD")
let &t_kD="[3;*~"
endif
+source check.vim
+
" Needed for testing basic rightleft: Test_edit_rightleft
source view_util.vim
@@ -733,17 +735,16 @@ func! Test_edit_CTRL_O()
endfunc
func! Test_edit_CTRL_R()
- throw 'skipped: Nvim does not support test_override()'
" Insert Register
new
- call test_override("ALL", 1)
+ " call test_override("ALL", 1)
set showcmd
call feedkeys("AFOOBAR eins zwei\<esc>", 'tnix')
call feedkeys("O\<c-r>.", 'tnix')
call feedkeys("O\<c-r>=10*500\<cr>\<esc>", 'tnix')
call feedkeys("O\<c-r>=getreg('=', 1)\<cr>\<esc>", 'tnix')
call assert_equal(["getreg('=', 1)", '5000', "FOOBAR eins zwei", "FOOBAR eins zwei"], getline(1, '$'))
- call test_override("ALL", 0)
+ " call test_override("ALL", 0)
set noshowcmd
bw!
endfunc
@@ -955,7 +956,6 @@ func! Test_edit_DROP()
endfunc
func! Test_edit_CTRL_V()
- throw 'skipped: Nvim does not support test_override()'
if has("ebcdic")
return
endif
@@ -965,7 +965,7 @@ func! Test_edit_CTRL_V()
" force some redraws
set showmode showcmd
"call test_override_char_avail(1)
- call test_override('ALL', 1)
+ " call test_override('ALL', 1)
call feedkeys("A\<c-v>\<c-n>\<c-v>\<c-l>\<c-v>\<c-b>\<esc>", 'tnix')
call assert_equal(["abc\x0e\x0c\x02"], getline(1, '$'))
@@ -978,7 +978,7 @@ func! Test_edit_CTRL_V()
set norl
endif
- call test_override('ALL', 0)
+ " call test_override('ALL', 0)
set noshowmode showcmd
bw!
endfunc
@@ -1514,3 +1514,23 @@ func Test_edit_startinsert()
set backspace&
bwipe!
endfunc
+
+func Test_edit_noesckeys()
+ CheckNotGui
+ new
+
+ " <Left> moves cursor when 'esckeys' is set
+ exe "set t_kl=\<Esc>OD"
+ " set esckeys
+ call feedkeys("axyz\<Esc>ODX", "xt")
+ " call assert_equal("xyXz", getline(1))
+
+ " <Left> exits Insert mode when 'esckeys' is off
+ " set noesckeys
+ call setline(1, '')
+ call feedkeys("axyz\<Esc>ODX", "xt")
+ call assert_equal(["DX", "xyz"], getline(1, 2))
+
+ bwipe!
+ " set esckeys
+endfunc
diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim
index 617e3dfe41..9f8939f2f6 100644
--- a/src/nvim/testdir/test_filetype.vim
+++ b/src/nvim/testdir/test_filetype.vim
@@ -326,7 +326,7 @@ let s:filename_checks = {
\ 'pamconf': ['/etc/pam.conf'],
\ 'pamenv': ['/etc/security/pam_env.conf', '/home/user/.pam_environment'],
\ 'papp': ['file.papp', 'file.pxml', 'file.pxsl'],
- \ 'pascal': ['file.pas', 'file.dpr'],
+ \ 'pascal': ['file.pas', 'file.pp', 'file.dpr', 'file.lpr'],
\ 'passwd': ['any/etc/passwd', 'any/etc/passwd-', 'any/etc/passwd.edit', 'any/etc/shadow', 'any/etc/shadow-', 'any/etc/shadow.edit', 'any/var/backups/passwd.bak', 'any/var/backups/shadow.bak'],
\ 'pccts': ['file.g'],
\ 'pdf': ['file.pdf'],
@@ -455,7 +455,7 @@ let s:filename_checks = {
\ 'texmf': ['texmf.cnf'],
\ 'text': ['file.text', 'README'],
\ 'tf': ['file.tf', '.tfrc', 'tfrc'],
- \ 'tidy': ['.tidyrc', 'tidyrc'],
+ \ 'tidy': ['.tidyrc', 'tidyrc', 'tidy.conf'],
\ 'tilde': ['file.t.html'],
\ 'tli': ['file.tli'],
\ 'tmux': ['tmuxfile.conf', '.tmuxfile.conf'],
diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim
index 6b45ac61d1..8fa70a5313 100644
--- a/src/nvim/testdir/test_functions.vim
+++ b/src/nvim/testdir/test_functions.vim
@@ -1221,6 +1221,24 @@ func Test_reg_executing_and_recording()
unlet s:reg_stat
endfunc
+func Test_getchar()
+ call feedkeys('a', '')
+ call assert_equal(char2nr('a'), getchar())
+
+ " call test_setmouse(1, 3)
+ " let v:mouse_win = 9
+ " let v:mouse_winid = 9
+ " let v:mouse_lnum = 9
+ " let v:mouse_col = 9
+ " call feedkeys("\<S-LeftMouse>", '')
+ call nvim_input_mouse('left', 'press', 'S', 0, 0, 2)
+ call assert_equal("\<S-LeftMouse>", getchar())
+ call assert_equal(1, v:mouse_win)
+ call assert_equal(win_getid(1), v:mouse_winid)
+ call assert_equal(1, v:mouse_lnum)
+ call assert_equal(3, v:mouse_col)
+endfunc
+
func Test_libcall_libcallnr()
if !has('libcall')
return
@@ -1341,3 +1359,22 @@ func Test_readdir()
call delete('Xdir', 'rf')
endfunc
+
+" Test for the eval() function
+func Test_eval()
+ call assert_fails("call eval('5 a')", 'E488:')
+endfunc
+
+" Test for the nr2char() function
+func Test_nr2char()
+ " set encoding=latin1
+ call assert_equal('@', nr2char(64))
+ set encoding=utf8
+ call assert_equal('a', nr2char(97, 1))
+ call assert_equal('a', nr2char(97, 0))
+
+ call assert_equal("\x80\xfc\b\xf4\x80\xfeX\x80\xfeX\x80\xfeX", eval('"\<M-' .. nr2char(0x100000) .. '>"'))
+ call assert_equal("\x80\xfc\b\xfd\x80\xfeX\x80\xfeX\x80\xfeX\x80\xfeX\x80\xfeX", eval('"\<M-' .. nr2char(0x40000000) .. '>"'))
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/nvim/testdir/test_gf.vim b/src/nvim/testdir/test_gf.vim
index 4a4ffcefa1..ee548037ba 100644
--- a/src/nvim/testdir/test_gf.vim
+++ b/src/nvim/testdir/test_gf.vim
@@ -1,3 +1,4 @@
+" Test for the gf and gF (goto file) commands
" This is a test if a URL is recognized by "gf", with the cursor before and
" after the "://". Also test ":\\".
@@ -109,7 +110,7 @@ func Test_gf()
endfunc
func Test_gf_visual()
- call writefile([], "Xtest_gf_visual")
+ call writefile(['one', 'two', 'three', 'four'], "Xtest_gf_visual")
new
call setline(1, 'XXXtest_gf_visualXXX')
set hidden
@@ -118,6 +119,30 @@ func Test_gf_visual()
norm! ttvtXgf
call assert_equal('Xtest_gf_visual', bufname('%'))
+ " if multiple lines are selected, then gf should fail
+ call setline(1, ["one", "two"])
+ normal VGgf
+ call assert_equal('Xtest_gf_visual', @%)
+
+ " following line number is used for gF
+ bwipe!
+ new
+ call setline(1, 'XXXtest_gf_visual:3XXX')
+ norm! 0ttvt:gF
+ call assert_equal('Xtest_gf_visual', bufname('%'))
+ call assert_equal(3, getcurpos()[1])
+
+ " line number in visual area is used for file name
+ if has('unix')
+ bwipe!
+ call writefile([], "Xtest_gf_visual:3")
+ new
+ call setline(1, 'XXXtest_gf_visual:3XXX')
+ norm! 0ttvtXgF
+ call assert_equal('Xtest_gf_visual:3', bufname('%'))
+ call delete('Xtest_gf_visual:3')
+ endif
+
bwipe!
call delete('Xtest_gf_visual')
set hidden&
diff --git a/src/nvim/testdir/test_search.vim b/src/nvim/testdir/test_search.vim
index e3a0fce5a6..211fc73562 100644
--- a/src/nvim/testdir/test_search.vim
+++ b/src/nvim/testdir/test_search.vim
@@ -708,6 +708,19 @@ func Test_incsearch_substitute_dump()
call VerifyScreenDump(buf, 'Test_incsearch_substitute_12', {})
call term_sendkeys(buf, "\<Esc>")
call VerifyScreenDump(buf, 'Test_incsearch_substitute_13', {})
+ call term_sendkeys(buf, ":%bwipe!\<CR>")
+ call term_sendkeys(buf, ":only!\<CR>")
+
+ " get :'<,'>s command in history
+ call term_sendkeys(buf, ":set cmdheight=2\<CR>")
+ call term_sendkeys(buf, "aasdfasdf\<Esc>")
+ call term_sendkeys(buf, "V:s/a/b/g\<CR>")
+ " Using '<,'> does not give E20
+ call term_sendkeys(buf, ":new\<CR>")
+ call term_sendkeys(buf, "aasdfasdf\<Esc>")
+ call term_sendkeys(buf, ":\<Up>\<Up>")
+ call VerifyScreenDump(buf, 'Test_incsearch_substitute_14', {})
+ call term_sendkeys(buf, "<Esc>")
call StopVimInTerminal(buf)
call delete('Xis_subst_script')
@@ -730,11 +743,14 @@ func Test_incsearch_sort_dump()
" the 'ambiwidth' check.
sleep 100m
- " Need to send one key at a time to force a redraw.
call term_sendkeys(buf, ':sort ni u /on')
call VerifyScreenDump(buf, 'Test_incsearch_sort_01', {})
call term_sendkeys(buf, "\<Esc>")
+ call term_sendkeys(buf, ':sort! /on')
+ call VerifyScreenDump(buf, 'Test_incsearch_sort_02', {})
+ call term_sendkeys(buf, "\<Esc>")
+
call StopVimInTerminal(buf)
call delete('Xis_sort_script')
endfunc
diff --git a/src/nvim/testdir/test_spell.vim b/src/nvim/testdir/test_spell.vim
index 414c7278eb..64e030426d 100644
--- a/src/nvim/testdir/test_spell.vim
+++ b/src/nvim/testdir/test_spell.vim
@@ -79,6 +79,11 @@ func Test_spellbadword()
call assert_equal(['bycycle', 'bad'], spellbadword('My bycycle.'))
call assert_equal(['another', 'caps'], spellbadword('A sentence. another sentence'))
+ call assert_equal(['TheCamelWord', 'bad'], spellbadword('TheCamelWord asdf'))
+ set spelloptions=camel
+ call assert_equal(['asdf', 'bad'], spellbadword('TheCamelWord asdf'))
+ set spelloptions=
+
set spelllang=en
call assert_equal(['', ''], spellbadword('centre'))
call assert_equal(['', ''], spellbadword('center'))
diff --git a/src/nvim/testdir/test_syntax.vim b/src/nvim/testdir/test_syntax.vim
index 85ee42420e..2404f113d9 100644
--- a/src/nvim/testdir/test_syntax.vim
+++ b/src/nvim/testdir/test_syntax.vim
@@ -369,7 +369,11 @@ func Test_ownsyntax()
call setline(1, '#define FOO')
syntax on
set filetype=c
+
ownsyntax perl
+ " this should not crash
+ set
+
call assert_equal('perlComment', synIDattr(synID(line('.'), col('.'), 1), 'name'))
call assert_equal('c', b:current_syntax)
call assert_equal('perl', w:current_syntax)
diff --git a/src/nvim/testdir/test_timers.vim b/src/nvim/testdir/test_timers.vim
index cffd80ff4f..d5ea54b764 100644
--- a/src/nvim/testdir/test_timers.vim
+++ b/src/nvim/testdir/test_timers.vim
@@ -233,16 +233,17 @@ func Test_timer_catch_error()
endfunc
func FeedAndPeek(timer)
- call test_feedinput('a')
+ " call test_feedinput('a')
+ call nvim_input('a')
call getchar(1)
endfunc
func Interrupt(timer)
- call test_feedinput("\<C-C>")
+ " call test_feedinput("\<C-C>")
+ call nvim_input("\<C-C>")
endfunc
func Test_peek_and_get_char()
- throw 'skipped: Nvim does not support test_feedinput()'
if !has('unix') && !has('gui_running')
return
endif
diff --git a/src/nvim/window.c b/src/nvim/window.c
index cec0dfd67f..6608deb231 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -6019,6 +6019,12 @@ char_u *grab_file_name(long count, linenr_T *file_lnum)
char_u *ptr;
if (get_visual_text(NULL, &ptr, &len) == FAIL)
return NULL;
+ // Only recognize ":123" here
+ if (file_lnum != NULL && ptr[len] == ':' && isdigit(ptr[len + 1])) {
+ char_u *p = ptr + len + 1;
+
+ *file_lnum = getdigits_long(&p, false, 0);
+ }
return find_file_name_in_path(ptr, len, options, count, curbuf->b_ffname);
}
return file_name_at_cursor(options | FNAME_HYP, count, file_lnum);
diff --git a/test/functional/api/extmark_spec.lua b/test/functional/api/extmark_spec.lua
index a2a188d036..ab913ba4a4 100644
--- a/test/functional/api/extmark_spec.lua
+++ b/test/functional/api/extmark_spec.lua
@@ -100,6 +100,15 @@ describe('API/extmarks', function()
ns2 = request('nvim_create_namespace', "my-fancy-plugin2")
end)
+ it("can end extranges past final newline using end_col = 0", function()
+ set_extmark(ns, marks[1], 0, 0, {
+ end_col = 0,
+ end_line = 1
+ })
+ eq("end_col value outside range",
+ pcall_err(set_extmark, ns, marks[2], 0, 0, { end_col = 1, end_line = 1 }))
+ end)
+
it('adds, updates and deletes marks', function()
local rv = set_extmark(ns, marks[1], positions[1][1], positions[1][2])
eq(marks[1], rv)
diff --git a/test/functional/lua/buffer_updates_spec.lua b/test/functional/lua/buffer_updates_spec.lua
index 5be47070a7..439cc12192 100644
--- a/test/functional/lua/buffer_updates_spec.lua
+++ b/test/functional/lua/buffer_updates_spec.lua
@@ -5,6 +5,7 @@ local inspect = require'vim.inspect'
local command = helpers.command
local meths = helpers.meths
+local funcs = helpers.funcs
local clear = helpers.clear
local eq = helpers.eq
local fail = helpers.fail
@@ -256,9 +257,9 @@ describe('lua: nvim_buf_attach on_bytes', function()
-- assert the wrong thing), but masks errors with unflushed lines (as
-- nvim_buf_get_offset forces a flush of the memline). To be safe run the
-- test both ways.
- local function setup_eventcheck(verify)
- meths.buf_set_lines(0, 0, -1, true, origlines)
- local shadow = deepcopy(origlines)
+ local function setup_eventcheck(verify, start_txt)
+ meths.buf_set_lines(0, 0, -1, true, start_txt)
+ local shadow = deepcopy(start_txt)
local shadowbytes = table.concat(shadow, '\n') .. '\n'
-- TODO: while we are brewing the real strong coffe,
-- verify should check buf_get_offset after every check_events
@@ -273,7 +274,7 @@ describe('lua: nvim_buf_attach on_bytes', function()
local events = exec_lua("return get_events(...)" )
if not pcall(eq, expected, events) then
- local msg = 'unexpected byte updates received.\n\nBABBLA MER \n\n'
+ local msg = 'unexpected byte updates received.\n\n'
msg = msg .. 'received events:\n'
for _, e in ipairs(events) do
@@ -286,26 +287,36 @@ describe('lua: nvim_buf_attach on_bytes', function()
fail(msg)
end
- if verify then
- for _, event in ipairs(events) do
- if event[1] == verify_name and event[2] == "bytes" then
- local _, _, _, _, _, _, start_byte, _, _, old_byte, _, _, new_byte = unpack(event)
- local before = string.sub(shadowbytes, 1, start_byte)
- -- no text in the tests will contain 0xff bytes (invalid UTF-8)
- -- so we can use it as marker for unknown bytes
- local unknown = string.rep('\255', new_byte)
- local after = string.sub(shadowbytes, start_byte + old_byte + 1)
- shadowbytes = before .. unknown .. after
+ if not verify then
+ return
+ end
+
+ for _, event in ipairs(events) do
+ for _, elem in ipairs(event) do
+ if type(elem) == "number" and elem < 0 then
+ fail(string.format("Received event has negative values"))
end
end
- local text = meths.buf_get_lines(0, 0, -1, true)
- local bytes = table.concat(text, '\n') .. '\n'
- eq(string.len(bytes), string.len(shadowbytes), shadowbytes)
- for i = 1, string.len(shadowbytes) do
- local shadowbyte = string.sub(shadowbytes, i, i)
- if shadowbyte ~= '\255' then
- eq(string.sub(bytes, i, i), shadowbyte, i)
- end
+
+ if event[1] == verify_name and event[2] == "bytes" then
+ local _, _, _, _, _, _, start_byte, _, _, old_byte, _, _, new_byte = unpack(event)
+ local before = string.sub(shadowbytes, 1, start_byte)
+ -- no text in the tests will contain 0xff bytes (invalid UTF-8)
+ -- so we can use it as marker for unknown bytes
+ local unknown = string.rep('\255', new_byte)
+ local after = string.sub(shadowbytes, start_byte + old_byte + 1)
+ shadowbytes = before .. unknown .. after
+ end
+ end
+
+ local text = meths.buf_get_lines(0, 0, -1, true)
+ local bytes = table.concat(text, '\n') .. '\n'
+
+ eq(string.len(bytes), string.len(shadowbytes), '\non_bytes: total bytecount of buffer is wrong')
+ for i = 1, string.len(shadowbytes) do
+ local shadowbyte = string.sub(shadowbytes, i, i)
+ if shadowbyte ~= '\255' then
+ eq(string.sub(bytes, i, i), shadowbyte, i)
end
end
end
@@ -316,7 +327,7 @@ describe('lua: nvim_buf_attach on_bytes', function()
-- Yes, we can do both
local function do_both(verify)
it('single and multiple join', function()
- local check_events = setup_eventcheck(verify)
+ local check_events = setup_eventcheck(verify, origlines)
feed 'ggJ'
check_events {
{'test1', 'bytes', 1, 3, 0, 15, 15, 1, 0, 1, 0, 1, 1};
@@ -330,7 +341,7 @@ describe('lua: nvim_buf_attach on_bytes', function()
end)
it('opening lines', function()
- local check_events = setup_eventcheck(verify)
+ local check_events = setup_eventcheck(verify, origlines)
-- meths.buf_set_option(0, 'autoindent', true)
feed 'Go'
check_events {
@@ -343,7 +354,7 @@ describe('lua: nvim_buf_attach on_bytes', function()
end)
it('opening lines with autoindent', function()
- local check_events = setup_eventcheck(verify)
+ local check_events = setup_eventcheck(verify, origlines)
meths.buf_set_option(0, 'autoindent', true)
feed 'Go'
check_events {
@@ -355,6 +366,92 @@ describe('lua: nvim_buf_attach on_bytes', function()
{ "test1", "bytes", 1, 5, 7, 4, 118, 0, 0, 0, 1, 4, 5 };
}
end)
+
+ it('setline(num, line)', function()
+ local check_events = setup_eventcheck(verify, origlines)
+ funcs.setline(2, "babla")
+ check_events {
+ { "test1", "bytes", 1, 3, 1, 0, 16, 0, 15, 15, 0, 5, 5 };
+ }
+
+ funcs.setline(2, {"foo", "bar"})
+ check_events {
+ { "test1", "bytes", 1, 4, 1, 0, 16, 0, 5, 5, 0, 3, 3 };
+ { "test1", "bytes", 1, 5, 2, 0, 20, 0, 15, 15, 0, 3, 3 };
+ }
+
+ local buf_len = meths.buf_line_count(0)
+ funcs.setline(buf_len + 1, "baz")
+ check_events {
+ { "test1", "bytes", 1, 6, 7, 0, 90, 0, 0, 0, 1, 0, 4 };
+ }
+ end)
+
+ it('continuing comments with fo=or', function()
+ local check_events = setup_eventcheck(verify, {'// Comment'})
+ meths.buf_set_option(0, 'formatoptions', 'ro')
+ meths.buf_set_option(0, 'filetype', 'c')
+ feed 'A<CR>'
+ check_events {
+ { "test1", "bytes", 1, 4, 0, 10, 10, 0, 0, 0, 1, 3, 4 };
+ }
+
+ feed '<ESC>'
+ check_events {
+ { "test1", "bytes", 1, 4, 1, 2, 13, 0, 1, 1, 0, 0, 0 };
+ }
+
+ feed 'ggo' -- goto first line to continue testing
+ check_events {
+ { "test1", "bytes", 1, 6, 1, 0, 11, 0, 0, 0, 1, 0, 4 };
+ }
+
+ feed '<CR>'
+ check_events {
+ { "test1", "bytes", 1, 6, 2, 2, 16, 0, 1, 1, 0, 0, 0 };
+ { "test1", "bytes", 1, 7, 1, 3, 14, 0, 0, 0, 1, 3, 4 };
+ }
+ end)
+
+ it('editing empty buffers', function()
+ local check_events = setup_eventcheck(verify, {})
+
+ feed 'ia'
+ check_events {
+ { "test1", "bytes", 1, 3, 0, 0, 0, 0, 0, 0, 0, 1, 1 };
+ }
+ end)
+
+ it("changing lines", function()
+ local check_events = setup_eventcheck(verify, origlines)
+
+ feed "cc"
+ check_events {
+ { "test1", "bytes", 1, 4, 0, 0, 0, 0, 15, 15, 0, 0, 0 };
+ }
+
+ feed "<ESC>"
+ check_events {}
+
+ feed "c3j"
+ check_events {
+ { "test1", "bytes", 1, 4, 1, 0, 1, 3, 0, 48, 0, 0, 0 };
+ }
+ end)
+
+ it("visual charwise paste", function()
+ local check_events = setup_eventcheck(verify, {'1234567890'})
+ funcs.setreg('a', '___')
+
+ feed '1G1|vll'
+ check_events {}
+
+ feed '"ap'
+ check_events {
+ { "test1", "bytes", 1, 3, 0, 0, 0, 0, 3, 3, 0, 0, 0 };
+ { "test1", "bytes", 1, 5, 0, 0, 0, 0, 0, 0, 0, 3, 3 };
+ }
+ end)
end
describe('(with verify) handles', function()
diff --git a/test/functional/ui/fold_spec.lua b/test/functional/ui/fold_spec.lua
index 6ec45064da..fe67b9f6b0 100644
--- a/test/functional/ui/fold_spec.lua
+++ b/test/functional/ui/fold_spec.lua
@@ -21,6 +21,8 @@ describe("folded lines", function()
[5] = {foreground = Screen.colors.DarkBlue, background = Screen.colors.LightGrey},
[6] = {background = Screen.colors.Yellow},
[7] = {foreground = Screen.colors.DarkBlue, background = Screen.colors.WebGray},
+ [8] = {foreground = Screen.colors.Brown },
+ [9] = {bold = true, foreground = Screen.colors.Brown}
})
end)
@@ -29,7 +31,7 @@ describe("folded lines", function()
feed("i<cr><esc>")
feed("vkzf")
screen:expect([[
- {5: ^+-- 2 lines: ·············}|
+ {7: }{5:^+-- 2 lines: ·············}|
{1:~ }|
{1:~ }|
{1:~ }|
@@ -49,8 +51,8 @@ describe("folded lines", function()
funcs.setline(4, 'line 2')
feed("j")
screen:expect([[
- {7:+ }{5: 1 +-- 2 lines: ·························}|
- {7:+ }{5: 0 ^+-- 2 lines: ·························}|
+ {7:+ }{8: 1 }{5:+-- 2 lines: ·························}|
+ {7:+ }{9: 0 }{5:^+-- 2 lines: ·························}|
{1:~ }|
{1:~ }|
{1:~ }|
@@ -130,8 +132,8 @@ describe("folded lines", function()
]])
feed('vkzf')
- screen:expect([[
- {5:^+-- 2 lines: å 语 x̎͂̀̂͛͛ ﺎﻠﻋَﺮَﺒِﻳَّﺓ·················}|
+ screen:expect{grid=[[
+ {5:^+-- 2 lines: å 语 x̎͂̀̂͛͛ العَرَبِيَّة·················}|
{1:~ }|
{1:~ }|
{1:~ }|
@@ -139,7 +141,7 @@ describe("folded lines", function()
{1:~ }|
{1:~ }|
|
- ]])
+ ]]}
feed_command("set noarabicshape")
screen:expect([[
@@ -155,7 +157,7 @@ describe("folded lines", function()
feed_command("set number foldcolumn=2")
screen:expect([[
- {7:+ }{5: 1 ^+-- 2 lines: å 语 x̎͂̀̂͛͛ العَرَبِيَّة···········}|
+ {7:+ }{8: 1 }{5:^+-- 2 lines: å 语 x̎͂̀̂͛͛ العَرَبِيَّة···········}|
{1:~ }|
{1:~ }|
{1:~ }|
@@ -168,7 +170,7 @@ describe("folded lines", function()
-- Note: too much of the folded line gets cut off.This is a vim bug.
feed_command("set rightleft")
screen:expect([[
- {5:+-- 2 lines: å ······················^· 1 }{7: +}|
+ {5:···········ةيَّبِرَعَلا x̎͂̀̂͛͛ 语 å :senil 2 --^+}{8: 1 }{7: +}|
{1: ~}|
{1: ~}|
{1: ~}|
@@ -180,7 +182,7 @@ describe("folded lines", function()
feed_command("set nonumber foldcolumn=0")
screen:expect([[
- {5:+-- 2 lines: å 语 x̎͂̀̂͛͛ ال·····················^·}|
+ {5:·················ةيَّبِرَعَلا x̎͂̀̂͛͛ 语 å :senil 2 --^+}|
{1: ~}|
{1: ~}|
{1: ~}|
@@ -192,7 +194,7 @@ describe("folded lines", function()
feed_command("set arabicshape")
screen:expect([[
- {5:+-- 2 lines: å 语 x̎͂̀̂͛͛ ﺍﻟ·····················^·}|
+ {5:·················ةيَّبِرَعَلا x̎͂̀̂͛͛ 语 å :senil 2 --^+}|
{1: ~}|
{1: ~}|
{1: ~}|