aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/test.yml4
-rw-r--r--CONTRIBUTING.md2
-rw-r--r--cmake.deps/deps.txt4
-rwxr-xr-xcontrib/asan.sh3
-rw-r--r--contrib/flake.nix1
-rw-r--r--runtime/doc/treesitter.txt16
-rw-r--r--runtime/lua/vim/fs.lua6
-rw-r--r--runtime/lua/vim/treesitter/_meta.lua4
-rw-r--r--runtime/lua/vim/treesitter/_range.lua38
-rw-r--r--runtime/lua/vim/treesitter/languagetree.lua67
-rw-r--r--runtime/lua/vim/treesitter/query.lua33
-rw-r--r--runtime/queries/c/highlights.scm17
-rw-r--r--runtime/queries/lua/highlights.scm10
-rw-r--r--runtime/queries/lua/injections.scm10
-rw-r--r--runtime/queries/vim/highlights.scm2
-rw-r--r--runtime/queries/vimdoc/highlights.scm8
-rw-r--r--src/nvim/CMakeLists.txt1
-rw-r--r--src/nvim/README.md3
-rw-r--r--src/nvim/buffer.c6
-rw-r--r--src/nvim/bufwrite.c16
-rw-r--r--src/nvim/cmdhist.c7
-rw-r--r--src/nvim/eval/userfunc.c6
-rw-r--r--src/nvim/ex_docmd.c8
-rw-r--r--src/nvim/fileio.c27
-rw-r--r--src/nvim/lua/treesitter.c89
-rw-r--r--src/nvim/main.c14
-rw-r--r--src/nvim/move.c4
-rw-r--r--src/nvim/option.c2
-rw-r--r--src/nvim/os/stdpaths.c2
-rw-r--r--src/nvim/runtime.c2
-rw-r--r--src/nvim/tag.c2
-rw-r--r--src/nvim/testing.c4
-rw-r--r--src/nvim/textobject.c3
-rw-r--r--src/nvim/undo.c2
-rw-r--r--test/functional/legacy/display_spec.lua55
-rw-r--r--test/functional/treesitter/parser_spec.lua58
-rw-r--r--test/old/testdir/test_display.vim19
-rw-r--r--test/old/testdir/test_ex_mode.vim4
-rw-r--r--test/old/testdir/test_scroll_opt.vim2
39 files changed, 458 insertions, 103 deletions
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 23019428c8..ba696ad85a 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -13,14 +13,14 @@ concurrency:
cancel-in-progress: true
env:
- ASAN_OPTIONS: detect_leaks=1:check_initialization_order=1:handle_abort=1:handle_sigill=1:log_path=${{ github.workspace }}/build/log/asan:intercept_tls_get_addr=0
+ ASAN_OPTIONS: detect_leaks=1:check_initialization_order=1:log_path=${{ github.workspace }}/build/log/asan:intercept_tls_get_addr=0
BIN_DIR: ${{ github.workspace }}/bin
BUILD_DIR: ${{ github.workspace }}/build
INSTALL_PREFIX: ${{ github.workspace }}/nvim-install
LOG_DIR: ${{ github.workspace }}/build/log
NVIM_LOG_FILE: ${{ github.workspace }}/build/.nvimlog
TSAN_OPTIONS: log_path=${{ github.workspace }}/build/log/tsan
- UBSAN_OPTIONS: "print_stacktrace=1 log_path=${{ github.workspace }}/build/log/ubsan"
+ UBSAN_OPTIONS: log_path=${{ github.workspace }}/build/log/ubsan
VALGRIND_LOG: ${{ github.workspace }}/build/log/valgrind-%p.log
# TEST_FILE: test/functional/core/startup_spec.lua
# TEST_FILTER: foo
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 85dc0fdbfd..4c8387fbd2 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -187,7 +187,7 @@ master build. To view the defects, just request access; you will be approved.
```
- When running Neovim, use
```
- UBSAN_OPTIONS=print_stacktrace=1 ASAN_OPTIONS=log_path=/tmp/nvim_asan,handle_abort=1,handle_sigill=1 nvim args...
+ ASAN_OPTIONS=log_path=/tmp/nvim_asan nvim args...
```
- If Neovim exits unexpectedly, check `/tmp/nvim_asan.{PID}` (or your preferred `log_path`) for log files with error messages.
diff --git a/cmake.deps/deps.txt b/cmake.deps/deps.txt
index 5c9d29bea6..385ede0e42 100644
--- a/cmake.deps/deps.txt
+++ b/cmake.deps/deps.txt
@@ -52,8 +52,8 @@ LIBICONV_SHA256 ccf536620a45458d26ba83887a983b96827001e92a13847b45e4925cc8913178
TREESITTER_C_URL https://github.com/tree-sitter/tree-sitter-c/archive/v0.20.2.tar.gz
TREESITTER_C_SHA256 af66fde03feb0df4faf03750102a0d265b007e5d957057b6b293c13116a70af2
-TREESITTER_LUA_URL https://github.com/MunifTanjim/tree-sitter-lua/archive/v0.0.14.tar.gz
-TREESITTER_LUA_SHA256 930d0370dc15b66389869355c8e14305b9ba7aafd36edbfdb468c8023395016d
+TREESITTER_LUA_URL https://github.com/MunifTanjim/tree-sitter-lua/archive/v0.0.17.tar.gz
+TREESITTER_LUA_SHA256 8963fd0a185d786c164dfca3824941c7eaec497ce49a3a0bc24bf753f5e0e59c
TREESITTER_VIM_URL https://github.com/neovim/tree-sitter-vim/archive/v0.3.0.tar.gz
TREESITTER_VIM_SHA256 403acec3efb7cdb18ff3d68640fc823502a4ffcdfbb71cec3f98aa786c21cbe2
TREESITTER_VIMDOC_URL https://github.com/neovim/tree-sitter-vimdoc/archive/v2.0.0.tar.gz
diff --git a/contrib/asan.sh b/contrib/asan.sh
index baf7abb5a8..6354d65514 100755
--- a/contrib/asan.sh
+++ b/contrib/asan.sh
@@ -13,9 +13,6 @@ export CC='clang'
# Change to detect_leaks=1 to detect memory leaks (slower).
export ASAN_OPTIONS="detect_leaks=0:log_path=$log_path/asan"
-# Show backtraces in the logs.
-export UBSAN_OPTIONS="print_stacktrace=1"
-
make -C "$root_path" CMAKE_EXTRA_FLAGS="-DENABLE_ASAN_UBSAN=ON"
VIMRUNTIME="$root_path"/runtime "$root_path"/build/bin/nvim
diff --git a/contrib/flake.nix b/contrib/flake.nix
index d38d1fd2b7..59d977b748 100644
--- a/contrib/flake.nix
+++ b/contrib/flake.nix
@@ -106,7 +106,6 @@
# ASAN_OPTIONS=detect_leaks=1
export ASAN_OPTIONS="log_path=./test.log:abort_on_error=1"
- export UBSAN_OPTIONS=print_stacktrace=1
# for treesitter functionaltests
mkdir -p runtime/parser
diff --git a/runtime/doc/treesitter.txt b/runtime/doc/treesitter.txt
index 483812b7c9..64b4730eee 100644
--- a/runtime/doc/treesitter.txt
+++ b/runtime/doc/treesitter.txt
@@ -221,8 +221,7 @@ The following predicates are built in:
similar to `match?`
`contains?` *treesitter-predicate-contains?*
- Match a string against parts of the text corresponding to a node:
- >query
+ Match a string against parts of the text corresponding to a node: >query
((identifier) @foo (#contains? @foo "foo"))
((identifier) @foo-bar (#contains? @foo-bar "foo" "bar"))
<
@@ -234,6 +233,19 @@ The following predicates are built in:
This is the recommended way to check if the node matches one of many
keywords, as it has been optimized for this.
+ `has-ancestor?` *treesitter-predicate-has-ancestor?*
+ Match any of the given node types against all ancestors of a node: >query
+ ((identifier) @variable.builtin
+ (#any-of? @variable.builtin "begin" "end")
+ (#has-ancestor? @variable.builtin range_expression))
+<
+ `has-parent?` *treesitter-predicate-has-parent?*
+ Match any of the given node types against the direct ancestor of a
+ node: >query
+ (((field_expression
+ (field_identifier) @method)) @_parent
+ (#has-parent? @_parent template_method function_declarator))
+<
*lua-treesitter-not-predicate*
Each predicate has a `not-` prefixed predicate that is just the negation of
the predicate.
diff --git a/runtime/lua/vim/fs.lua b/runtime/lua/vim/fs.lua
index 2a51bde263..7f9f3a2bce 100644
--- a/runtime/lua/vim/fs.lua
+++ b/runtime/lua/vim/fs.lua
@@ -73,10 +73,14 @@ function M.basename(file)
end
---@private
-local function join_paths(...)
+---@param ... string
+---@return string
+function M._join_paths(...)
return (table.concat({ ... }, '/'):gsub('//+', '/'))
end
+local join_paths = M._join_paths
+
---@alias Iterator fun(): string?, string?
--- Return an iterator over the files and directories located in {path}
diff --git a/runtime/lua/vim/treesitter/_meta.lua b/runtime/lua/vim/treesitter/_meta.lua
index c1009f5f5d..9ca4b560c6 100644
--- a/runtime/lua/vim/treesitter/_meta.lua
+++ b/runtime/lua/vim/treesitter/_meta.lua
@@ -45,6 +45,8 @@ function TSNode:_rawquery(query, captures, start, end_, opts) end
---@return fun(): string, any
function TSNode:_rawquery(query, captures, start, end_, opts) end
+---@alias TSLoggerCallback fun(logtype: 'parse'|'lex', msg: string)
+
---@class TSParser
---@field parse fun(self: TSParser, tree: TSTree?, source: integer|string, include_bytes: boolean?): TSTree, integer[]
---@field reset fun(self: TSParser)
@@ -52,6 +54,8 @@ function TSNode:_rawquery(query, captures, start, end_, opts) end
---@field set_included_ranges fun(self: TSParser, ranges: Range6[])
---@field set_timeout fun(self: TSParser, timeout: integer)
---@field timeout fun(self: TSParser): integer
+---@field _set_logger fun(self: TSParser, lex: boolean, parse: boolean, cb: TSLoggerCallback)
+---@field _logger fun(self: TSParser): TSLoggerCallback
---@class TSTree
---@field root fun(self: TSTree): TSNode
diff --git a/runtime/lua/vim/treesitter/_range.lua b/runtime/lua/vim/treesitter/_range.lua
index f4db5016ac..35081c6400 100644
--- a/runtime/lua/vim/treesitter/_range.lua
+++ b/runtime/lua/vim/treesitter/_range.lua
@@ -143,6 +143,29 @@ function M.contains(r1, r2)
return true
end
+--- @param source integer|string
+--- @param index integer
+--- @return integer
+local function get_offset(source, index)
+ if index == 0 then
+ return 0
+ end
+
+ if type(source) == 'number' then
+ return api.nvim_buf_get_offset(source, index)
+ end
+
+ local byte = 0
+ local next_offset = source:gmatch('()\n')
+ local line = 1
+ while line <= index do
+ byte = next_offset() --[[@as integer]]
+ line = line + 1
+ end
+
+ return byte
+end
+
---@private
---@param source integer|string
---@param range Range
@@ -152,19 +175,10 @@ function M.add_bytes(source, range)
return range --[[@as Range6]]
end
- local start_row, start_col, end_row, end_col = range[1], range[2], range[3], range[4]
- local start_byte = 0
- local end_byte = 0
+ local start_row, start_col, end_row, end_col = M.unpack4(range)
-- TODO(vigoux): proper byte computation here, and account for EOL ?
- if type(source) == 'number' then
- -- Easy case, this is a buffer parser
- start_byte = api.nvim_buf_get_offset(source, start_row) + start_col
- end_byte = api.nvim_buf_get_offset(source, end_row) + end_col
- elseif type(source) == 'string' then
- -- string parser, single `\n` delimited string
- start_byte = vim.fn.byteidx(source, start_col)
- end_byte = vim.fn.byteidx(source, end_col)
- end
+ local start_byte = get_offset(source, start_row) + start_col
+ local end_byte = get_offset(source, end_row) + end_col
return { start_row, start_col, start_byte, end_row, end_col, end_byte }
end
diff --git a/runtime/lua/vim/treesitter/languagetree.lua b/runtime/lua/vim/treesitter/languagetree.lua
index 0efe3af85c..6c780f33c4 100644
--- a/runtime/lua/vim/treesitter/languagetree.lua
+++ b/runtime/lua/vim/treesitter/languagetree.lua
@@ -31,8 +31,17 @@
--- 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.
+---
+
+-- Debugging:
+--
+-- vim.g.__ts_debug levels:
+-- - 1. Messages from languagetree.lua
+-- - 2. Parse messages from treesitter
+-- - 2. Lex messages from treesitter
+--
+-- Log file can be found in stdpath('log')/treesitter.log
-local api = vim.api
local query = require('vim.treesitter.query')
local language = require('vim.treesitter.language')
local Range = require('vim.treesitter._range')
@@ -75,6 +84,8 @@ local TSCallbackNames = {
---@field private _source (integer|string) Buffer or string to parse
---@field private _trees TSTree[] Reference to parsed tree (one for each language)
---@field private _valid boolean|table<integer,boolean> If the parsed tree is valid
+---@field private _logger? fun(logtype: string, msg: string)
+---@field private _logfile? file*
local LanguageTree = {}
---@class LanguageTreeOpts
@@ -114,6 +125,10 @@ function LanguageTree.new(source, lang, opts)
_callbacks_rec = {},
}, LanguageTree)
+ if vim.g.__ts_debug and type(vim.g.__ts_debug) == 'number' then
+ self:_set_logger()
+ end
+
for _, name in pairs(TSCallbackNames) do
self._callbacks[name] = {}
self._callbacks_rec[name] = {}
@@ -122,6 +137,33 @@ function LanguageTree.new(source, lang, opts)
return self
end
+function LanguageTree:_set_logger()
+ local source = self:source()
+ source = type(source) == 'string' and 'text' or tostring(source)
+
+ local lang = self:lang()
+
+ local logfilename = vim.fs._join_paths(vim.fn.stdpath('log'), 'treesitter.log')
+
+ local logfile, openerr = io.open(logfilename, 'a+')
+
+ if not logfile or openerr then
+ error(string.format('Could not open file (%s) for logging: %s', logfilename, openerr))
+ return
+ end
+
+ self._logfile = logfile
+
+ self._logger = function(logtype, msg)
+ self._logfile:write(string.format('%s:%s:(%s) %s\n', source, lang, logtype, msg))
+ self._logfile:flush()
+ end
+
+ local log_lex = vim.g.__ts_debug >= 3
+ local log_parse = vim.g.__ts_debug >= 2
+ self._parser:_set_logger(log_lex, log_parse, self._logger)
+end
+
---@private
---Measure execution time of a function
---@generic R1, R2, R3
@@ -139,7 +181,11 @@ end
---@private
---@vararg any
function LanguageTree:_log(...)
- if vim.g.__ts_debug == nil then
+ if not self._logger then
+ return
+ end
+
+ if not vim.g.__ts_debug or vim.g.__ts_debug < 1 then
return
end
@@ -150,19 +196,17 @@ function LanguageTree:_log(...)
local info = debug.getinfo(2, 'nl')
local nregions = #self:included_regions()
- local prefix =
- string.format('%s:%d: [%s:%d] ', info.name, info.currentline, self:lang(), nregions)
+ local prefix = string.format('%s:%d: (#regions=%d) ', info.name, info.currentline, nregions)
- api.nvim_out_write(prefix)
+ local msg = { prefix }
for _, x in ipairs(args) do
if type(x) == 'string' then
- api.nvim_out_write(x)
+ msg[#msg + 1] = x
else
- api.nvim_out_write(vim.inspect(x, { newline = ' ', indent = '' }))
+ msg[#msg + 1] = vim.inspect(x, { newline = ' ', indent = '' })
end
- api.nvim_out_write(' ')
end
- api.nvim_out_write('\n')
+ self._logger('nvim', table.concat(msg, ' '))
end
--- Invalidates this parser and all its children
@@ -876,6 +920,11 @@ end
function LanguageTree:_on_detach(...)
self:invalidate(true)
self:_do_callback('detach', ...)
+ if self._logfile then
+ self._logger('nvim', 'detaching')
+ self._logger = nil
+ self._logfile:close()
+ end
end
--- Registers callbacks for the |LanguageTree|.
diff --git a/runtime/lua/vim/treesitter/query.lua b/runtime/lua/vim/treesitter/query.lua
index 75e5bf8870..73b561c777 100644
--- a/runtime/lua/vim/treesitter/query.lua
+++ b/runtime/lua/vim/treesitter/query.lua
@@ -382,6 +382,39 @@ local predicate_handlers = {
return string_set[node_text]
end,
+
+ ['has-ancestor?'] = function(match, _, _, predicate)
+ local node = match[predicate[2]]
+ if not node then
+ return true
+ end
+
+ local ancestor_types = {}
+ for _, type in ipairs({ unpack(predicate, 3) }) do
+ ancestor_types[type] = true
+ end
+
+ node = node:parent()
+ while node do
+ if ancestor_types[node:type()] then
+ return true
+ end
+ node = node:parent()
+ end
+ return false
+ end,
+
+ ['has-parent?'] = function(match, _, _, predicate)
+ local node = match[predicate[2]]
+ if not node then
+ return true
+ end
+
+ if vim.list_contains({ unpack(predicate, 3) }, node:parent():type()) then
+ return true
+ end
+ return false
+ end,
}
-- As we provide lua-match? also expose vim-match?
diff --git a/runtime/queries/c/highlights.scm b/runtime/queries/c/highlights.scm
index dee70f9cc7..523a792403 100644
--- a/runtime/queries/c/highlights.scm
+++ b/runtime/queries/c/highlights.scm
@@ -117,12 +117,19 @@
(preproc_defined)
] @function.macro
-(field_identifier) @property
+(((field_expression
+ (field_identifier) @property)) @_parent
+ (#not-has-parent? @_parent template_method function_declarator call_expression))
+
+(field_designator) @property
+(((field_identifier) @property)
+ (#has-ancestor? @property field_declaration)
+ (#not-has-ancestor? @property function_declarator))
+
(statement_identifier) @label
[
(type_identifier)
- (sized_type_specifier)
(type_descriptor)
] @type
@@ -138,6 +145,8 @@
(primitive_type) @type.builtin
+(sized_type_specifier _ @type.builtin type: _?)
+
((identifier) @constant
(#lua-match? @constant "^[A-Z][A-Z0-9_]+$"))
(enumerator
@@ -163,6 +172,10 @@
field: (field_identifier) @function.call))
(function_declarator
declarator: (identifier) @function)
+(function_declarator
+ declarator: (parenthesized_declarator
+ (pointer_declarator
+ declarator: (field_identifier) @function)))
(preproc_function_def
name: (identifier) @function.macro)
diff --git a/runtime/queries/lua/highlights.scm b/runtime/queries/lua/highlights.scm
index 5fbf8a1833..537a171441 100644
--- a/runtime/queries/lua/highlights.scm
+++ b/runtime/queries/lua/highlights.scm
@@ -127,8 +127,14 @@
(identifier) @variable
+((identifier) @constant.builtin
+ (#eq? @constant.builtin "_VERSION"))
+
((identifier) @variable.builtin
- (#any-of? @variable.builtin "_G" "_VERSION" "debug" "io" "jit" "math" "os" "package" "self" "string" "table" "utf8"))
+ (#eq? @variable.builtin "self"))
+
+((identifier) @namespace.builtin
+ (#any-of? @namespace.builtin "_G" "debug" "io" "jit" "math" "os" "package" "string" "table" "utf8"))
((identifier) @keyword.coroutine
(#eq? @keyword.coroutine "coroutine"))
@@ -210,5 +216,7 @@
(string) @string @spell
+(escape_sequence) @string.escape
+
;; Error
(ERROR) @error
diff --git a/runtime/queries/lua/injections.scm b/runtime/queries/lua/injections.scm
index 1c7bdaf951..3fcebe83f3 100644
--- a/runtime/queries/lua/injections.scm
+++ b/runtime/queries/lua/injections.scm
@@ -13,6 +13,14 @@
(#set! injection.language "vim")
(#any-of? @_vimcmd_identifier "vim.cmd" "vim.api.nvim_command" "vim.api.nvim_exec2" "vim.api.nvim_cmd"))
+; vim.rcprequest(123, "nvim_exec_lua", "return vim.api.nvim_buf_get_lines(0, 0, -1, false)", false)
+((function_call
+ name: (_) @_vimcmd_identifier
+ arguments: (arguments . (_) . (string content: _ @_method) . (string content: _ @injection.content)))
+ (#set! injection.language "lua")
+ (#any-of? @_vimcmd_identifier "vim.rpcrequest" "vim.rpcnotify")
+ (#eq? @_method "nvim_exec_lua"))
+
((function_call
name: (_) @_vimcmd_identifier
arguments: (arguments (string content: _ @injection.content) .))
@@ -20,7 +28,7 @@
(#any-of? @_vimcmd_identifier "vim.treesitter.query.set" "vim.treesitter.query.parse"))
;; highlight string as query if starts with `;; query`
-((string ("string_content") @injection.content)
+(string content: _ @injection.content
(#set! injection.language "query")
(#lua-match? @injection.content "^%s*;+%s?query"))
diff --git a/runtime/queries/vim/highlights.scm b/runtime/queries/vim/highlights.scm
index ce25b13b9f..09188ddb68 100644
--- a/runtime/queries/vim/highlights.scm
+++ b/runtime/queries/vim/highlights.scm
@@ -275,7 +275,7 @@
; Options
((set_value) @number
- (#match? @number "^[0-9]+(\.[0-9]+)?$"))
+ (#lua-match? @number "^[%d]+(%.[%d]+)?$"))
(inv_option "!" @operator)
(set_item "?" @operator)
diff --git a/runtime/queries/vimdoc/highlights.scm b/runtime/queries/vimdoc/highlights.scm
index c0d88301bc..e0dce49b2a 100644
--- a/runtime/queries/vimdoc/highlights.scm
+++ b/runtime/queries/vimdoc/highlights.scm
@@ -1,7 +1,7 @@
-(h1) @text.title
-(h2) @text.title
-(h3) @text.title
-(column_heading) @text.title
+(h1) @text.title.1
+(h2) @text.title.2
+(h3) @text.title.3
+(column_heading) @text.title.4
(column_heading
"~" @conceal (#set! conceal ""))
(tag
diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt
index 325b376b30..070cea3b59 100644
--- a/src/nvim/CMakeLists.txt
+++ b/src/nvim/CMakeLists.txt
@@ -836,6 +836,7 @@ if(ENABLE_ASAN_UBSAN)
-fsanitize=address
-fsanitize=undefined)
target_link_libraries(nvim PRIVATE -fsanitize=address -fsanitize=undefined)
+ target_compile_definitions(nvim PRIVATE ENABLE_ASAN_UBSAN)
elseif(ENABLE_MSAN)
message(STATUS "Enabling memory sanitizer for nvim.")
target_compile_options(nvim PRIVATE
diff --git a/src/nvim/README.md b/src/nvim/README.md
index cbd5daba4e..75155fb9c6 100644
--- a/src/nvim/README.md
+++ b/src/nvim/README.md
@@ -71,9 +71,8 @@ Create a directory to store logs:
Configure the sanitizer(s) via these environment variables:
# Change to detect_leaks=1 to detect memory leaks (slower, noisier).
- export ASAN_OPTIONS="detect_leaks=0:log_path=$HOME/logs/asan,handle_abort=1,handle_sigill=1"
+ export ASAN_OPTIONS="detect_leaks=0:log_path=$HOME/logs/asan"
# Show backtraces in the logs.
- export UBSAN_OPTIONS=print_stacktrace=1
export MSAN_OPTIONS="log_path=${HOME}/logs/msan"
export TSAN_OPTIONS="log_path=${HOME}/logs/tsan"
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index e734a340d9..49fd09ebd0 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -1084,11 +1084,11 @@ char *do_bufdel(int command, char *arg, int addr_count, int start_bnr, int end_b
if (deleted == 0) {
if (command == DOBUF_UNLOAD) {
- STRCPY(IObuff, _("E515: No buffers were unloaded"));
+ xstrlcpy(IObuff, _("E515: No buffers were unloaded"), IOSIZE);
} else if (command == DOBUF_DEL) {
- STRCPY(IObuff, _("E516: No buffers were deleted"));
+ xstrlcpy(IObuff, _("E516: No buffers were deleted"), IOSIZE);
} else {
- STRCPY(IObuff, _("E517: No buffers were wiped out"));
+ xstrlcpy(IObuff, _("E517: No buffers were wiped out"), IOSIZE);
}
errormsg = IObuff;
} else if (deleted >= p_report) {
diff --git a/src/nvim/bufwrite.c b/src/nvim/bufwrite.c
index 84c1276b8b..cfa3ea5bf3 100644
--- a/src/nvim/bufwrite.c
+++ b/src/nvim/bufwrite.c
@@ -745,7 +745,7 @@ static int buf_write_make_backup(char *fname, bool append, FileInfo *file_info_o
// the ones from the original file.
// First find a file name that doesn't exist yet (use some
// arbitrary numbers).
- STRCPY(IObuff, fname);
+ xstrlcpy(IObuff, fname, IOSIZE);
for (int i = 4913;; i += 123) {
char *tail = path_tail(IObuff);
size_t size = (size_t)(tail - IObuff);
@@ -1749,24 +1749,24 @@ restore_backup:
add_quoted_fname(IObuff, IOSIZE, buf, fname);
bool insert_space = false;
if (write_info.bw_conv_error) {
- STRCAT(IObuff, _(" CONVERSION ERROR"));
+ xstrlcat(IObuff, _(" CONVERSION ERROR"), IOSIZE);
insert_space = true;
if (write_info.bw_conv_error_lnum != 0) {
vim_snprintf_add(IObuff, IOSIZE, _(" in line %" PRId64 ";"),
(int64_t)write_info.bw_conv_error_lnum);
}
} else if (notconverted) {
- STRCAT(IObuff, _("[NOT converted]"));
+ xstrlcat(IObuff, _("[NOT converted]"), IOSIZE);
insert_space = true;
} else if (converted) {
- STRCAT(IObuff, _("[converted]"));
+ xstrlcat(IObuff, _("[converted]"), IOSIZE);
insert_space = true;
}
if (device) {
- STRCAT(IObuff, _("[Device]"));
+ xstrlcat(IObuff, _("[Device]"), IOSIZE);
insert_space = true;
} else if (newfile) {
- STRCAT(IObuff, new_file_message());
+ xstrlcat(IObuff, new_file_message(), IOSIZE);
insert_space = true;
}
if (no_eol) {
@@ -1780,9 +1780,9 @@ restore_backup:
msg_add_lines(insert_space, (long)lnum, nchars); // add line/char count
if (!shortmess(SHM_WRITE)) {
if (append) {
- STRCAT(IObuff, shortmess(SHM_WRI) ? _(" [a]") : _(" appended"));
+ xstrlcat(IObuff, shortmess(SHM_WRI) ? _(" [a]") : _(" appended"), IOSIZE);
} else {
- STRCAT(IObuff, shortmess(SHM_WRI) ? _(" [w]") : _(" written"));
+ xstrlcat(IObuff, shortmess(SHM_WRI) ? _(" [w]") : _(" written"), IOSIZE);
}
}
diff --git a/src/nvim/cmdhist.c b/src/nvim/cmdhist.c
index 81b93e5304..fc84cecc1a 100644
--- a/src/nvim/cmdhist.c
+++ b/src/nvim/cmdhist.c
@@ -641,9 +641,10 @@ void ex_history(exarg_T *eap)
}
for (; !got_int && histype1 <= histype2; histype1++) {
- STRCPY(IObuff, "\n # ");
+ xstrlcpy(IObuff, "\n # ", IOSIZE);
assert(history_names[histype1] != NULL);
- STRCAT(STRCAT(IObuff, history_names[histype1]), " history");
+ xstrlcat(IObuff, history_names[histype1], IOSIZE);
+ xstrlcat(IObuff, " history", IOSIZE);
msg_puts_title(IObuff);
int idx = hisidx[histype1];
histentry_T *hist = history[histype1];
@@ -669,7 +670,7 @@ void ex_history(exarg_T *eap)
trunc_string(hist[i].hisstr, IObuff + strlen(IObuff),
Columns - 10, IOSIZE - (int)strlen(IObuff));
} else {
- STRCAT(IObuff, hist[i].hisstr);
+ xstrlcat(IObuff, hist[i].hisstr, IOSIZE);
}
msg_outtrans(IObuff);
}
diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c
index 7e20a298dd..a52b8d3f18 100644
--- a/src/nvim/eval/userfunc.c
+++ b/src/nvim/eval/userfunc.c
@@ -2892,9 +2892,9 @@ char *get_user_func_name(expand_T *xp, int idx)
cat_func_name(IObuff, IOSIZE, fp);
if (xp->xp_context != EXPAND_USER_FUNC) {
- STRCAT(IObuff, "(");
+ xstrlcat(IObuff, "(", IOSIZE);
if (!fp->uf_varargs && GA_EMPTY(&fp->uf_args)) {
- STRCAT(IObuff, ")");
+ xstrlcat(IObuff, ")", IOSIZE);
}
}
return IObuff;
@@ -3505,7 +3505,7 @@ char *get_return_cmd(void *rettv)
s = "";
}
- STRCPY(IObuff, ":return ");
+ xstrlcpy(IObuff, ":return ", IOSIZE);
xstrlcpy(IObuff + 8, s, IOSIZE - 8);
if (strlen(s) + 8 >= IOSIZE) {
STRCPY(IObuff + IOSIZE - 4, "...");
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index 83232d5f17..9666d80de2 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -1454,7 +1454,7 @@ bool parse_cmdline(char *cmdline, exarg_T *eap, CmdParseInfo *cmdinfo, char **er
}
// Fail if command is invalid
if (eap->cmdidx == CMD_SIZE) {
- STRCPY(IObuff, _(e_not_an_editor_command));
+ xstrlcpy(IObuff, _(e_not_an_editor_command), IOSIZE);
// If the modifier was parsed OK the error must be in the following command
char *cmdname = after_modifier ? after_modifier : cmdline;
append_command(cmdname);
@@ -2044,7 +2044,7 @@ static char *do_one_cmd(char **cmdlinep, int flags, cstack_T *cstack, LineGetter
// Check for wrong commands.
if (ea.cmdidx == CMD_SIZE) {
if (!ea.skip) {
- STRCPY(IObuff, _(e_not_an_editor_command));
+ xstrlcpy(IObuff, _(e_not_an_editor_command), IOSIZE);
// If the modifier was parsed OK the error must be in the following
// command
char *cmdname = after_modifier ? after_modifier : *cmdlinep;
@@ -2321,7 +2321,7 @@ doend:
if (errormsg != NULL && *errormsg != NUL && !did_emsg) {
if (flags & DOCMD_VERBOSE) {
if (errormsg != IObuff) {
- STRCPY(IObuff, errormsg);
+ xstrlcpy(IObuff, errormsg, IOSIZE);
errormsg = IObuff;
}
append_command(*ea.cmdlinep);
@@ -2888,7 +2888,7 @@ static void append_command(char *cmd)
d -= utf_head_off(IObuff, d);
STRCPY(d, "...");
}
- STRCAT(IObuff, ": ");
+ xstrlcat(IObuff, ": ", IOSIZE);
d = IObuff + strlen(IObuff);
while (*s != NUL && d - IObuff + 5 < IOSIZE) {
if ((uint8_t)s[0] == 0xc2 && (uint8_t)s[1] == 0xa0) {
diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c
index d659987686..57aa063504 100644
--- a/src/nvim/fileio.c
+++ b/src/nvim/fileio.c
@@ -1693,22 +1693,22 @@ failed:
#ifdef UNIX
if (S_ISFIFO(perm)) { // fifo
- STRCAT(IObuff, _("[fifo]"));
+ xstrlcat(IObuff, _("[fifo]"), IOSIZE);
c = true;
}
if (S_ISSOCK(perm)) { // or socket
- STRCAT(IObuff, _("[socket]"));
+ xstrlcat(IObuff, _("[socket]"), IOSIZE);
c = true;
}
# ifdef OPEN_CHR_FILES
if (S_ISCHR(perm)) { // or character special
- STRCAT(IObuff, _("[character special]"));
+ xstrlcat(IObuff, _("[character special]"), IOSIZE);
c = true;
}
# endif
#endif
if (curbuf->b_p_ro) {
- STRCAT(IObuff, shortmess(SHM_RO) ? _("[RO]") : _("[readonly]"));
+ xstrlcat(IObuff, shortmess(SHM_RO) ? _("[RO]") : _("[readonly]"), IOSIZE);
c = true;
}
if (read_no_eol_lnum) {
@@ -1716,18 +1716,18 @@ failed:
c = true;
}
if (ff_error == EOL_DOS) {
- STRCAT(IObuff, _("[CR missing]"));
+ xstrlcat(IObuff, _("[CR missing]"), IOSIZE);
c = true;
}
if (split) {
- STRCAT(IObuff, _("[long lines split]"));
+ xstrlcat(IObuff, _("[long lines split]"), IOSIZE);
c = true;
}
if (notconverted) {
- STRCAT(IObuff, _("[NOT converted]"));
+ xstrlcat(IObuff, _("[NOT converted]"), IOSIZE);
c = true;
} else if (converted) {
- STRCAT(IObuff, _("[converted]"));
+ xstrlcat(IObuff, _("[converted]"), IOSIZE);
c = true;
}
if (conv_error != 0) {
@@ -1739,7 +1739,7 @@ failed:
_("[ILLEGAL BYTE in line %" PRId64 "]"), (int64_t)illegal_byte);
c = true;
} else if (error) {
- STRCAT(IObuff, _("[READ ERRORS]"));
+ xstrlcat(IObuff, _("[READ ERRORS]"), IOSIZE);
c = true;
}
if (msg_add_fileformat(fileformat)) {
@@ -2128,17 +2128,17 @@ bool msg_add_fileformat(int eol_type)
{
#ifndef USE_CRNL
if (eol_type == EOL_DOS) {
- STRCAT(IObuff, shortmess(SHM_TEXT) ? _("[dos]") : _("[dos format]"));
+ xstrlcat(IObuff, shortmess(SHM_TEXT) ? _("[dos]") : _("[dos format]"), IOSIZE);
return true;
}
#endif
if (eol_type == EOL_MAC) {
- STRCAT(IObuff, shortmess(SHM_TEXT) ? _("[mac]") : _("[mac format]"));
+ xstrlcat(IObuff, shortmess(SHM_TEXT) ? _("[mac]") : _("[mac format]"), IOSIZE);
return true;
}
#ifdef USE_CRNL
if (eol_type == EOL_UNIX) {
- STRCAT(IObuff, shortmess(SHM_TEXT) ? _("[unix]") : _("[unix format]"));
+ xstrlcat(IObuff, shortmess(SHM_TEXT) ? _("[unix]") : _("[unix format]"), IOSIZE);
return true;
}
#endif
@@ -2170,8 +2170,7 @@ void msg_add_lines(int insert_space, long lnum, off_T nchars)
/// Append message for missing line separator to IObuff.
void msg_add_eol(void)
{
- STRCAT(IObuff,
- shortmess(SHM_LAST) ? _("[noeol]") : _("[Incomplete last line]"));
+ xstrlcat(IObuff, shortmess(SHM_LAST) ? _("[noeol]") : _("[Incomplete last line]"), IOSIZE);
}
bool time_differs(const FileInfo *file_info, long mtime, long mtime_ns) FUNC_ATTR_CONST
diff --git a/src/nvim/lua/treesitter.c b/src/nvim/lua/treesitter.c
index dae1365272..a9e7838980 100644
--- a/src/nvim/lua/treesitter.c
+++ b/src/nvim/lua/treesitter.c
@@ -20,6 +20,7 @@
#include "nvim/api/private/helpers.h"
#include "nvim/buffer_defs.h"
#include "nvim/globals.h"
+#include "nvim/lua/executor.h"
#include "nvim/lua/treesitter.h"
#include "nvim/macros.h"
#include "nvim/map.h"
@@ -43,6 +44,13 @@ typedef struct {
int max_match_id;
} TSLua_cursor;
+typedef struct {
+ LuaRef cb;
+ lua_State *lstate;
+ bool lex;
+ bool parse;
+} TSLuaLoggerOpts;
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "lua/treesitter.c.generated.h"
#endif
@@ -56,6 +64,8 @@ static struct luaL_Reg parser_meta[] = {
{ "included_ranges", parser_get_ranges },
{ "set_timeout", parser_set_timeout },
{ "timeout", parser_get_timeout },
+ { "_set_logger", parser_set_logger },
+ { "_logger", parser_get_logger },
{ NULL, NULL }
};
@@ -322,6 +332,12 @@ static int parser_gc(lua_State *L)
return 0;
}
+ TSLogger logger = ts_parser_logger(*p);
+ if (logger.log) {
+ TSLuaLoggerOpts *opts = (TSLuaLoggerOpts *)logger.payload;
+ xfree(opts);
+ }
+
ts_parser_delete(*p);
return 0;
}
@@ -669,9 +685,82 @@ static int parser_get_timeout(lua_State *L)
}
lua_pushinteger(L, (long)ts_parser_timeout_micros(*p));
+ return 1;
+}
+
+static void logger_cb(void *payload, TSLogType logtype, const char *s)
+{
+ TSLuaLoggerOpts *opts = (TSLuaLoggerOpts *)payload;
+ if ((!opts->lex && logtype == TSLogTypeLex)
+ || (!opts->parse && logtype == TSLogTypeParse)) {
+ return;
+ }
+
+ lua_State *lstate = opts->lstate;
+
+ nlua_pushref(lstate, opts->cb);
+ lua_pushstring(lstate, logtype == TSLogTypeParse ? "parse" : "lex");
+ lua_pushstring(lstate, s);
+ if (lua_pcall(lstate, 2, 0, 0)) {
+ luaL_error(lstate, "Error executing treesitter logger callback");
+ }
+}
+
+static int parser_set_logger(lua_State *L)
+{
+ TSParser **p = parser_check(L, 1);
+ if (!p) {
+ return 0;
+ }
+
+ if (!lua_isboolean(L, 2)) {
+ return luaL_argerror(L, 2, "boolean expected");
+ }
+
+ if (!lua_isboolean(L, 3)) {
+ return luaL_argerror(L, 3, "boolean expected");
+ }
+
+ if (!lua_isfunction(L, 4)) {
+ return luaL_argerror(L, 4, "function expected");
+ }
+
+ TSLuaLoggerOpts *opts = xmalloc(sizeof(TSLuaLoggerOpts));
+
+ *opts = (TSLuaLoggerOpts){
+ .lex = lua_toboolean(L, 2),
+ .parse = lua_toboolean(L, 3),
+ .cb = nlua_ref_global(L, 4),
+ .lstate = L
+ };
+
+ TSLogger logger = {
+ .payload = (void *)opts,
+ .log = logger_cb
+ };
+
+ ts_parser_set_logger(*p, logger);
return 0;
}
+static int parser_get_logger(lua_State *L)
+{
+ TSParser **p = parser_check(L, 1);
+ if (!p) {
+ return 0;
+ }
+
+ TSLogger logger = ts_parser_logger(*p);
+ if (logger.log) {
+ TSLuaLoggerOpts *opts = (TSLuaLoggerOpts *)logger.payload;
+ nlua_pushref(L, opts->cb);
+ } else {
+ lua_pushnil(L);
+ }
+
+ return 1;
+}
+
// Tree methods
/// push tree interface on lua stack.
diff --git a/src/nvim/main.c b/src/nvim/main.c
index 659eccc6f0..4999d9dd2a 100644
--- a/src/nvim/main.c
+++ b/src/nvim/main.c
@@ -2222,3 +2222,17 @@ static void check_swap_exists_action(void)
}
handle_swap_exists(NULL);
}
+
+#ifdef ENABLE_ASAN_UBSAN
+const char *__ubsan_default_options(void);
+const char *__ubsan_default_options(void)
+{
+ return "print_stacktrace=1";
+}
+
+const char *__asan_default_options(void);
+const char *__asan_default_options(void)
+{
+ return "handle_abort=1,handle_sigill=1";
+}
+#endif
diff --git a/src/nvim/move.c b/src/nvim/move.c
index 6a6f8149dd..ead2b3b0d3 100644
--- a/src/nvim/move.c
+++ b/src/nvim/move.c
@@ -1502,11 +1502,11 @@ void adjust_skipcol(void)
} else {
curwin->w_skipcol -= width1;
}
- redraw_later(curwin, UPD_NOT_VALID);
scrolled = true;
- validate_virtcol();
}
if (scrolled) {
+ validate_virtcol();
+ redraw_later(curwin, UPD_NOT_VALID);
return; // don't scroll in the other direction now
}
long col = curwin->w_virtcol - curwin->w_skipcol + scrolloff_cols;
diff --git a/src/nvim/option.c b/src/nvim/option.c
index fc1fc87b62..327c5fc089 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -1626,7 +1626,7 @@ int do_set(char *arg, int opt_flags)
int i = (int)strlen(IObuff) + 2;
if (i + (arg - startarg) < IOSIZE) {
// append the argument with the error
- STRCAT(IObuff, ": ");
+ xstrlcat(IObuff, ": ", IOSIZE);
assert(arg >= startarg);
memmove(IObuff + i, startarg, (size_t)(arg - startarg));
IObuff[i + (arg - startarg)] = NUL;
diff --git a/src/nvim/os/stdpaths.c b/src/nvim/os/stdpaths.c
index 5235828f7a..8b62b9e895 100644
--- a/src/nvim/os/stdpaths.c
+++ b/src/nvim/os/stdpaths.c
@@ -130,7 +130,7 @@ char *get_xdg_home(const XDGVarType idx)
xstrlcpy(IObuff, appname, appname_len + 1);
#if defined(MSWIN)
if (idx == kXDGDataHome || idx == kXDGStateHome) {
- STRCAT(IObuff, "-data");
+ xstrlcat(IObuff, "-data", IOSIZE);
}
#endif
dir = concat_fnames_realloc(dir, IObuff, true);
diff --git a/src/nvim/runtime.c b/src/nvim/runtime.c
index f7b7723553..a9068fabc8 100644
--- a/src/nvim/runtime.c
+++ b/src/nvim/runtime.c
@@ -1544,7 +1544,7 @@ static inline char *add_dir(char *dest, const char *const dir, const size_t dir_
xstrlcpy(IObuff, appname, appname_len + 1);
#if defined(MSWIN)
if (type == kXDGDataHome || type == kXDGStateHome) {
- STRCAT(IObuff, "-data");
+ xstrlcat(IObuff, "-data", IOSIZE);
appname_len += 5;
}
#endif
diff --git a/src/nvim/tag.c b/src/nvim/tag.c
index 81bca67cf7..18331cc95d 100644
--- a/src/nvim/tag.c
+++ b/src/nvim/tag.c
@@ -732,7 +732,7 @@ void do_tag(char *tag, int type, int count, int forceit, int verbose)
num_matches,
max_num_matches != MAXCOL ? _(" or more") : "");
if (ic) {
- STRCAT(IObuff, _(" Using tag with different case!"));
+ xstrlcat(IObuff, _(" Using tag with different case!"), IOSIZE);
}
if ((num_matches > prev_num_matches || new_tag)
&& num_matches > 1) {
diff --git a/src/nvim/testing.c b/src/nvim/testing.c
index 5483a58525..25ec8e898a 100644
--- a/src/nvim/testing.c
+++ b/src/nvim/testing.c
@@ -418,11 +418,11 @@ static int assert_equalfile(typval_T *argvars)
const int c2 = fgetc(fd2);
if (c1 == EOF) {
if (c2 != EOF) {
- STRCPY(IObuff, "first file is shorter");
+ xstrlcpy(IObuff, "first file is shorter", IOSIZE);
}
break;
} else if (c2 == EOF) {
- STRCPY(IObuff, "second file is shorter");
+ xstrlcpy(IObuff, "second file is shorter", IOSIZE);
break;
} else {
line1[lineidx] = (char)c1;
diff --git a/src/nvim/textobject.c b/src/nvim/textobject.c
index 428b14a68d..5036c10827 100644
--- a/src/nvim/textobject.c
+++ b/src/nvim/textobject.c
@@ -22,6 +22,7 @@
#include "nvim/mbyte.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
+#include "nvim/move.h"
#include "nvim/normal.h"
#include "nvim/option_defs.h"
#include "nvim/pos.h"
@@ -414,6 +415,7 @@ int bck_word(long count, bool bigword, bool stop)
finished:
stop = false;
}
+ adjust_skipcol();
return OK;
}
@@ -518,6 +520,7 @@ int bckend_word(long count, bool bigword, bool eol)
}
}
}
+ adjust_skipcol();
return OK;
}
diff --git a/src/nvim/undo.c b/src/nvim/undo.c
index 35f46e512d..00a3922a5b 100644
--- a/src/nvim/undo.c
+++ b/src/nvim/undo.c
@@ -2667,7 +2667,7 @@ void ex_undolist(exarg_T *eap)
undo_fmt_time(IObuff + strlen(IObuff), IOSIZE - strlen(IObuff), uhp->uh_time);
if (uhp->uh_save_nr > 0) {
while (strlen(IObuff) < 33) {
- STRCAT(IObuff, " ");
+ xstrlcat(IObuff, " ", IOSIZE);
}
vim_snprintf_add(IObuff, IOSIZE, " %3ld", uhp->uh_save_nr);
}
diff --git a/test/functional/legacy/display_spec.lua b/test/functional/legacy/display_spec.lua
index feb2662100..482b88eae8 100644
--- a/test/functional/legacy/display_spec.lua
+++ b/test/functional/legacy/display_spec.lua
@@ -286,17 +286,56 @@ describe('display', function()
local screen = Screen.new(75, 8)
screen:attach()
exec([[
- call setline(1, ['a', 'bbbbb '->repeat(100), 'c'])
+ call setline(1, ['a', 'b ' .. 'bbbbb'->repeat(150), 'c'])
norm $j
]])
screen:expect([[
- <<<bbbbb bbbbb bbbbb bbbbb bbbbb bbbbb bbbbb bbbbb bbbbb bbbbb bbbbb bbbbb |
- bbbbb bbbbb bbbbb bbbbb bbbbb bbbbb bbbbb bbbbb bbbbb bbbbb bbbbb bbbbb bbb|
- bb bbbbb bbbbb bbbbb bbbbb bbbbb bbbbb bbbbb bbbbb bbbbb bbbbb bbbbb bbbbb |
- bbbbb bbbbb bbbbb bbbbb bbbbb bbbbb bbbbb bbbbb bbbbb bbbbb bbbbb bbbbb bbb|
- bb bbbbb bbbbb bbbbb bbbbb bbbbb bbbbb bbbbb bbbbb bbbbb bbbbb bbbbb bbbbb |
- bbbbb bbbbb bbbbb bbbbb bbbbb bbbbb bbbbb bbbbb bbbbb bbbbb bbbbb bbbbb bbb|
- bb bbbbb bbbbb bbbbb bbbbb bbbbb bbbbb bbbbb bbbbb bbbbb bbbbb bbbbb bbbbb^ |
+ <<<bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb|
+ bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb|
+ bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb|
+ bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb|
+ bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb|
+ bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb|
+ b^b |
+ |
+ ]])
+ -- FIXME: moving the cursor above the topline does not set w_skipcol
+ -- correctly with cpo+=n and zero scrolloff (curs_columns() extra == 1).
+ exec('set number cpo+=n scrolloff=0')
+ feed('$0')
+ screen:expect([[
+ <<<b^bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb|
+ bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb|
+ bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb|
+ bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb|
+ bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb|
+ bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb|
+ bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb|
+ |
+ ]])
+ -- Going to the start of the line with "b" did not set w_skipcol correctly with 'smoothscroll'.
+ exec('set smoothscroll')
+ feed('$b')
+ screen:expect([[
+ 2 b ^bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb|
+ bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb|
+ bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb|
+ bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb|
+ bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb|
+ bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb|
+ bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb|
+ |
+ ]])
+ -- Same for "ge".
+ feed('$ge')
+ screen:expect([[
+ 2 ^b bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb|
+ bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb|
+ bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb|
+ bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb|
+ bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb|
+ bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb|
+ bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb|
|
]])
end)
diff --git a/test/functional/treesitter/parser_spec.lua b/test/functional/treesitter/parser_spec.lua
index 9afce0b3a0..14de07639b 100644
--- a/test/functional/treesitter/parser_spec.lua
+++ b/test/functional/treesitter/parser_spec.lua
@@ -483,10 +483,9 @@ end]]
return list
]]
- eq({ 'any-of?', 'contains?', 'eq?', 'is-main?', 'lua-match?', 'match?', 'vim-match?' }, res_list)
+ eq({ 'any-of?', 'contains?', 'eq?', 'has-ancestor?', 'has-parent?', 'is-main?', 'lua-match?', 'match?', 'vim-match?' }, res_list)
end)
-
it('allows to set simple ranges', function()
insert(test_text)
@@ -528,6 +527,7 @@ end]]
eq(range_tbl, { { { 0, 0, 0, 17, 1, 508 } } })
end)
+
it("allows to set complex ranges", function()
insert(test_text)
@@ -992,4 +992,58 @@ int x = INT_MAX;
}, run_query())
end)
+
+ it('handles ranges when source is a multiline string (#20419)', function()
+ local source = [==[
+ vim.cmd[[
+ set number
+ set cmdheight=2
+ set lastsatus=2
+ ]]
+
+ set query = [[;; query
+ ((function_call
+ name: [
+ (identifier) @_cdef_identifier
+ (_ _ (identifier) @_cdef_identifier)
+ ]
+ arguments: (arguments (string content: _ @injection.content)))
+ (#set! injection.language "c")
+ (#eq? @_cdef_identifier "cdef"))
+ ]]
+ ]==]
+
+ local r = exec_lua([[
+ local parser = vim.treesitter.get_string_parser(..., 'lua')
+ parser:parse()
+ local ranges = {}
+ parser:for_each_tree(function(tstree, tree)
+ ranges[tree:lang()] = { tstree:root():range(true) }
+ end)
+ return ranges
+ ]], source)
+
+ eq({
+ lua = { 0, 6, 6, 16, 4, 438 },
+ query = { 6, 20, 113, 15, 6, 431 },
+ vim = { 1, 0, 16, 4, 6, 89 }
+ }, r)
+
+ -- The above ranges are provided directly from treesitter, however query directives may mutate
+ -- the ranges but only provide a Range4. Strip the byte entries from the ranges and make sure
+ -- add_bytes() produces the same result.
+
+ local rb = exec_lua([[
+ local r, source = ...
+ local add_bytes = require('vim.treesitter._range').add_bytes
+ for lang, range in pairs(r) do
+ r[lang] = {range[1], range[2], range[4], range[5]}
+ r[lang] = add_bytes(source, r[lang])
+ end
+ return r
+ ]], r, source)
+
+ eq(rb, r)
+
+ end)
end)
diff --git a/test/old/testdir/test_display.vim b/test/old/testdir/test_display.vim
index a2b40521d9..e007b6f741 100644
--- a/test/old/testdir/test_display.vim
+++ b/test/old/testdir/test_display.vim
@@ -518,14 +518,29 @@ func Test_display_cursor_long_line()
CheckScreendump
let lines =<< trim END
- call setline(1, ['a', 'bbbbb '->repeat(100), 'c'])
+ call setline(1, ['a', 'b ' .. 'bbbbb'->repeat(150), 'c'])
norm $j
END
call writefile(lines, 'XdispCursorLongline', 'D')
let buf = RunVimInTerminal('-S XdispCursorLongline', #{rows: 8})
- call VerifyScreenDump(buf, 'Test_display_cursor_long_line', {})
+ call VerifyScreenDump(buf, 'Test_display_cursor_long_line_1', {})
+
+ " FIXME: moving the cursor above the topline does not set w_skipcol
+ " correctly with cpo+=n and zero scrolloff (curs_columns() extra == 1).
+ call term_sendkeys(buf, ":set number cpo+=n scrolloff=0\<CR>")
+ call term_sendkeys(buf, '$0')
+ call VerifyScreenDump(buf, 'Test_display_cursor_long_line_2', {})
+
+ " Going to the start of the line with "b" did not set w_skipcol correctly
+ " with 'smoothscroll'.
+ call term_sendkeys(buf, ":set smoothscroll\<CR>")
+ call term_sendkeys(buf, '$b')
+ call VerifyScreenDump(buf, 'Test_display_cursor_long_line_3', {})
+ " Same for "ge".
+ call term_sendkeys(buf, '$ge')
+ call VerifyScreenDump(buf, 'Test_display_cursor_long_line_4', {})
call StopVimInTerminal(buf)
endfunc
diff --git a/test/old/testdir/test_ex_mode.vim b/test/old/testdir/test_ex_mode.vim
index b30dce8630..3332bc6ab9 100644
--- a/test/old/testdir/test_ex_mode.vim
+++ b/test/old/testdir/test_ex_mode.vim
@@ -218,9 +218,9 @@ func Test_Ex_echo_backslash()
let bsl = '\\\\'
let bsl2 = '\\\'
call assert_fails('call feedkeys("Qecho " .. bsl .. "\nvisual\n", "xt")',
- \ "E15: Invalid expression: \\\\")
+ \ 'E15: Invalid expression: "\\"')
call assert_fails('call feedkeys("Qecho " .. bsl2 .. "\nm\nvisual\n", "xt")',
- \ "E15: Invalid expression: \\\nm")
+ \ "E15: Invalid expression: \"\\\nm\"")
endfunc
func Test_ex_mode_errors()
diff --git a/test/old/testdir/test_scroll_opt.vim b/test/old/testdir/test_scroll_opt.vim
index 13ef4b76df..74c2de81aa 100644
--- a/test/old/testdir/test_scroll_opt.vim
+++ b/test/old/testdir/test_scroll_opt.vim
@@ -502,7 +502,7 @@ func Test_smoothscroll_cursor_position()
" regardless of number and cpo-=n.
setl number list listchars=precedes:< cpo-=n
call s:check_col_calc(5, 1, 1)
- exe "normal 2|\<C-E>"
+ exe "normal 3|\<C-E>h"
call s:check_col_calc(6, 1, 18)
norm h
call s:check_col_calc(5, 2, 17)