aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.cirrus.yml10
-rw-r--r--.github/workflows/test.yml38
-rw-r--r--CMakeLists.txt12
-rw-r--r--ci/Dockerfile.external_deps22
-rw-r--r--cmake/BuildLuarocks.cmake5
-rw-r--r--cmake/Deps.cmake1
-rw-r--r--runtime/doc/luvref.txt10
-rw-r--r--runtime/ftplugin/eruby.vim12
-rw-r--r--runtime/ftplugin/ruby.vim76
-rw-r--r--runtime/indent/ruby.vim4
-rw-r--r--runtime/makemenu.vim1
-rw-r--r--runtime/synmenu.vim1
-rw-r--r--runtime/syntax/ruby.vim26
-rw-r--r--runtime/syntax/zserio.vim112
-rw-r--r--src/nvim/api/options.c3
-rw-r--r--src/nvim/api/ui.c6
-rw-r--r--src/nvim/buffer.c8
-rw-r--r--src/nvim/edit.c1
-rw-r--r--src/nvim/ex_cmds.c44
-rw-r--r--src/nvim/ex_docmd.c2
-rw-r--r--src/nvim/ex_session.c5
-rw-r--r--src/nvim/lua/executor.c4
-rw-r--r--src/nvim/mark.c26
-rw-r--r--src/nvim/mark.h4
-rw-r--r--src/nvim/ops.c2
-rw-r--r--src/nvim/option.c343
-rw-r--r--src/nvim/option_defs.h5
-rw-r--r--src/nvim/optionstr.c109
-rw-r--r--src/nvim/regexp.c5
-rw-r--r--test/busted_runner.lua57
-rw-r--r--test/functional/legacy/crash_spec.lua16
-rw-r--r--test/functional/shada/marks_spec.lua29
-rw-r--r--test/functional/terminal/tui_spec.lua34
-rw-r--r--test/old/testdir/crash/vim_regsub_both10
-rw-r--r--test/old/testdir/test_crash.vim34
-rw-r--r--test/old/testdir/test_functions.vim5
-rw-r--r--test/old/testdir/test_selectmode.vim11
37 files changed, 725 insertions, 368 deletions
diff --git a/.cirrus.yml b/.cirrus.yml
index 76f15852c4..20e5f0ad76 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -25,13 +25,3 @@ freebsd_task:
- sudo -u cirrus gmake unittest
oldtest_script:
- sudo -u cirrus gmake oldtest
-
-external_deps_task:
- container:
- dockerfile: ci/Dockerfile.external_deps
- deps_script:
- - cmake -S cmake.deps --preset external_deps
- - cmake --build .deps
- build_script:
- - cmake --preset ci
- - cmake --build build
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index bdec96babb..0a75a9aad9 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -301,3 +301,41 @@ jobs:
run: |
cd test/old/testdir
mingw32-make VERBOSE=1
+
+ with-external-deps:
+ runs-on: ubuntu-22.04
+ timeout-minutes: 10
+ steps:
+ - uses: actions/checkout@v3
+
+ - name: Install dependencies
+ run: |
+ sudo add-apt-repository ppa:neovim-ppa/stable
+ ./.github/scripts/install_deps.sh
+ sudo apt-get install -y \
+ libluajit-5.1-dev \
+ libmsgpack-dev \
+ libtermkey-dev \
+ libunibilium-dev \
+ libuv1-dev \
+ lua-filesystem \
+ lua-lpeg \
+ luajit \
+ lua-luv-dev
+ # libtree-sitter-dev \
+ # libvterm-dev
+
+ # Remove comments from packages once we start using these external
+ # dependencies.
+
+ - uses: ./.github/actions/cache
+
+ - name: Build third-party deps
+ run: |
+ cmake -S cmake.deps --preset external_deps
+ cmake --build .deps
+
+ - name: Build
+ run: |
+ cmake --preset ci
+ cmake --build build
diff --git a/CMakeLists.txt b/CMakeLists.txt
index df9f1cfa85..c91821d843 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -316,3 +316,15 @@ ExternalProject_Add(uncrustify
EXCLUDE_FROM_ALL TRUE)
include(BuildLuarocks)
+
+ExternalProject_Add(busted
+ URL https://github.com/neovim/deps/raw/41d2f1b92aef964c8cb86985768702571e190e96/opt/busted-2.1.1.tar.gz
+ URL_HASH SHA256=9b23efce883ad25a3fe140598a32ab89ecc73f4c3d998cb937293d88e5b4c645
+ DOWNLOAD_NO_PROGRESS TRUE
+ DOWNLOAD_DIR ${DEPS_DOWNLOAD_DIR}/busted
+ SOURCE_DIR ${DEPS_SHARE_DIR}
+ CONFIGURE_COMMAND ""
+ BUILD_COMMAND ""
+ INSTALL_COMMAND ""
+ EXCLUDE_FROM_ALL TRUE)
+add_dependencies(test_deps busted)
diff --git a/ci/Dockerfile.external_deps b/ci/Dockerfile.external_deps
deleted file mode 100644
index 0d2a55dd19..0000000000
--- a/ci/Dockerfile.external_deps
+++ /dev/null
@@ -1,22 +0,0 @@
-FROM ubuntu:22.04
-
-RUN apt-get update && \
- apt-get install -y software-properties-common && \
- add-apt-repository ppa:neovim-ppa/stable && \
- apt-get install -y ninja-build \
- gettext \
- cmake \
- unzip \
- curl \
- git \
- libluajit-5.1-dev \
- libmsgpack-dev \
- libtermkey-dev \
- libunibilium-dev \
- libuv1-dev \
- lua-filesystem \
- lua-lpeg \
- luajit \
- lua-luv-dev \
- libtree-sitter-dev \
- libvterm-dev
diff --git a/cmake/BuildLuarocks.cmake b/cmake/BuildLuarocks.cmake
index c5e08d2d74..2dc493a59b 100644
--- a/cmake/BuildLuarocks.cmake
+++ b/cmake/BuildLuarocks.cmake
@@ -89,10 +89,8 @@ function(Download ROCK VER)
endfunction()
if(WIN32)
- set(BUSTED_EXE "${DEPS_BIN_DIR}/busted.bat")
set(LUACHECK_EXE "${DEPS_BIN_DIR}/luacheck.bat")
else()
- set(BUSTED_EXE "${DEPS_BIN_DIR}/busted")
set(LUACHECK_EXE "${DEPS_BIN_DIR}/luacheck")
endif()
@@ -100,9 +98,6 @@ add_custom_target(test_deps)
Download(luacheck 1.1.0-1 ${LUACHECK_EXE})
-Download(busted 2.1.1 ${BUSTED_EXE})
-add_dependencies(test_deps busted)
-
if(PREFER_LUA)
Download(coxpcall 1.17.0-1)
add_dependencies(test_deps coxpcall)
diff --git a/cmake/Deps.cmake b/cmake/Deps.cmake
index 69a950eb0d..e8dcbaa79d 100644
--- a/cmake/Deps.cmake
+++ b/cmake/Deps.cmake
@@ -1,6 +1,7 @@
set(DEPS_INSTALL_DIR "${CMAKE_BINARY_DIR}/usr")
set(DEPS_BIN_DIR "${DEPS_INSTALL_DIR}/bin")
set(DEPS_LIB_DIR "${DEPS_INSTALL_DIR}/lib")
+set(DEPS_SHARE_DIR "${DEPS_INSTALL_DIR}/share/lua/5.1")
set(DEPS_BUILD_DIR "${CMAKE_BINARY_DIR}/build")
set(DEPS_DOWNLOAD_DIR "${DEPS_BUILD_DIR}/downloads")
diff --git a/runtime/doc/luvref.txt b/runtime/doc/luvref.txt
index 9fd69d86eb..915b69efe3 100644
--- a/runtime/doc/luvref.txt
+++ b/runtime/doc/luvref.txt
@@ -1301,11 +1301,11 @@ uv.spawn({path}, {options}, {on_exit}) *uv.spawn()*
The `options` table accepts the following fields:
- `options.args` - Command line arguments as a list of
- string. The first string should be the path to the
- program. On Windows, this uses CreateProcess which
- concatenates the arguments into a string. This can cause
- some strange errors. (See `options.verbatim` below for
- Windows.)
+ strings. The first string should not be the path to the
+ program, since that is already provided via `path`. On
+ Windows, this uses CreateProcess which concatenates the
+ arguments into a string. This can cause some strange
+ errors (see `options.verbatim` below for Windows).
- `options.stdio` - Set the file descriptors that will be
made available to the child process. The convention is
that the first entries are stdin, stdout, and stderr.
diff --git a/runtime/ftplugin/eruby.vim b/runtime/ftplugin/eruby.vim
index e67b00b278..893fa58d32 100644
--- a/runtime/ftplugin/eruby.vim
+++ b/runtime/ftplugin/eruby.vim
@@ -3,7 +3,7 @@
" Maintainer: Tim Pope <vimNOSPAM@tpope.org>
" URL: https://github.com/vim-ruby/vim-ruby
" Release Coordinator: Doug Kearns <dougkearns@gmail.com>
-" Last Change: 2020 Jun 28
+" Last Change: 2022 May 15
" Only do this when not done yet for this buffer
if exists("b:did_ftplugin")
@@ -86,8 +86,12 @@ runtime! ftplugin/ruby.vim ftplugin/ruby_*.vim ftplugin/ruby/*.vim
let b:did_ftplugin = 1
" Combine the new set of values with those previously included.
-if exists("b:undo_ftplugin")
- let s:undo_ftplugin = b:undo_ftplugin . " | " . s:undo_ftplugin
+if !exists('b:undo_ftplugin')
+ " No-op
+ let b:undo_ftplugin = 'exe'
+endif
+if !empty(s:undo_ftplugin)
+ let b:undo_ftplugin .= '|' . s:undo_ftplugin
endif
if exists ("b:browsefilter")
let s:browsefilter = substitute(b:browsefilter,'\cAll Files (\*\.\*)\t\*\.\*\n','','') . s:browsefilter
@@ -119,7 +123,7 @@ endif
setlocal commentstring=<%#%s%>
let b:undo_ftplugin = "setl cms< " .
- \ " | unlet! b:browsefilter b:match_words | " . s:undo_ftplugin
+ \ " | unlet! b:browsefilter b:match_words | " . b:undo_ftplugin
let &cpo = s:save_cpo
unlet s:save_cpo
diff --git a/runtime/ftplugin/ruby.vim b/runtime/ftplugin/ruby.vim
index f4e1f60438..1262099d88 100644
--- a/runtime/ftplugin/ruby.vim
+++ b/runtime/ftplugin/ruby.vim
@@ -3,7 +3,7 @@
" Maintainer: Tim Pope <vimNOSPAM@tpope.org>
" URL: https://github.com/vim-ruby/vim-ruby
" Release Coordinator: Doug Kearns <dougkearns@gmail.com>
-" Last Change: 2022 Mar 21
+" Last Change: 2023 Sep 1st
if (exists("b:did_ftplugin"))
finish
@@ -77,7 +77,11 @@ function! s:query_path(root) abort
let cwd = fnameescape(getcwd())
try
exe cd fnameescape(a:root)
- let path = split(system(path_check),',')
+ if fnamemodify(exepath('ruby'), ':p:h') ==# getcwd()
+ let path = []
+ else
+ let path = split(system(path_check),',')
+ endif
exe cd cwd
return path
finally
@@ -99,51 +103,41 @@ function! s:build_path(path) abort
return path
endfunction
-let s:execute_ruby = 1
-" Security Check, don't execute ruby from the current directory
-if fnamemodify(exepath("ruby"), ":p:h") ==# getcwd()
- let s:execute_ruby = 0
-endif
-
-function SetRubyPath()
- if !exists('b:ruby_version') && !exists('g:ruby_path') && isdirectory(expand('%:p:h'))
- let s:version_file = findfile('.ruby-version', '.;')
- if !empty(s:version_file) && filereadable(s:version_file) && s:execute_ruby
- let b:ruby_version = get(readfile(s:version_file, '', 1), '')
- if !has_key(g:ruby_version_paths, b:ruby_version)
- let g:ruby_version_paths[b:ruby_version] = s:query_path(fnamemodify(s:version_file, ':p:h'))
- endif
+if !exists('b:ruby_version') && !exists('g:ruby_path') && isdirectory(expand('%:p:h'))
+ let s:version_file = findfile('.ruby-version', '.;')
+ if !empty(s:version_file) && filereadable(s:version_file)
+ let b:ruby_version = get(readfile(s:version_file, '', 1), '')
+ if !has_key(g:ruby_version_paths, b:ruby_version)
+ let g:ruby_version_paths[b:ruby_version] = s:query_path(fnamemodify(s:version_file, ':p:h'))
endif
endif
+endif
- if exists("g:ruby_path")
- let s:ruby_path = type(g:ruby_path) == type([]) ? join(g:ruby_path, ',') : g:ruby_path
- elseif has_key(g:ruby_version_paths, get(b:, 'ruby_version', '')) && s:execute_ruby
- let s:ruby_paths = g:ruby_version_paths[b:ruby_version]
- let s:ruby_path = s:build_path(s:ruby_paths)
- else
- if !exists('g:ruby_default_path')
- if has("ruby") && has("win32")
- ruby ::VIM::command( 'let g:ruby_default_path = split("%s",",")' % $:.join(%q{,}) )
- elseif executable('ruby') && !empty($HOME) && s:execute_ruby
- let g:ruby_default_path = s:query_path($HOME)
- else
- let g:ruby_default_path = map(split($RUBYLIB,':'), 'v:val ==# "." ? "" : v:val')
- endif
+if exists("g:ruby_path")
+ let s:ruby_path = type(g:ruby_path) == type([]) ? join(g:ruby_path, ',') : g:ruby_path
+elseif has_key(g:ruby_version_paths, get(b:, 'ruby_version', ''))
+ let s:ruby_paths = g:ruby_version_paths[b:ruby_version]
+ let s:ruby_path = s:build_path(s:ruby_paths)
+else
+ if !exists('g:ruby_default_path')
+ if has("ruby") && has("win32")
+ ruby ::VIM::command( 'let g:ruby_default_path = split("%s",",")' % $:.join(%q{,}) )
+ elseif executable('ruby') && !empty($HOME)
+ let g:ruby_default_path = s:query_path($HOME)
+ else
+ let g:ruby_default_path = map(split($RUBYLIB,':'), 'v:val ==# "." ? "" : v:val')
endif
- let s:ruby_paths = g:ruby_default_path
- let s:ruby_path = s:build_path(s:ruby_paths)
- endif
-
- if stridx(&l:path, s:ruby_path) == -1
- let &l:path = s:ruby_path
- endif
- if exists('s:ruby_paths') && stridx(&l:tags, join(map(copy(s:ruby_paths),'v:val."/tags"'),',')) == -1
- let &l:tags = &tags . ',' . join(map(copy(s:ruby_paths),'v:val."/tags"'),',')
endif
-endfunction
+ let s:ruby_paths = g:ruby_default_path
+ let s:ruby_path = s:build_path(s:ruby_paths)
+endif
-call SetRubyPath()
+if stridx(&l:path, s:ruby_path) == -1
+ let &l:path = s:ruby_path
+endif
+if exists('s:ruby_paths') && stridx(&l:tags, join(map(copy(s:ruby_paths),'v:val."/tags"'),',')) == -1
+ let &l:tags = &tags . ',' . join(map(copy(s:ruby_paths),'v:val."/tags"'),',')
+endif
if (has("gui_win32") || has("gui_gtk")) && !exists("b:browsefilter")
let b:browsefilter = "Ruby Source Files (*.rb)\t*.rb\n" .
diff --git a/runtime/indent/ruby.vim b/runtime/indent/ruby.vim
index 6ce8529fd1..ea5a2a7494 100644
--- a/runtime/indent/ruby.vim
+++ b/runtime/indent/ruby.vim
@@ -4,7 +4,7 @@
" Previous Maintainer: Nikolai Weibull <now at bitwi.se>
" URL: https://github.com/vim-ruby/vim-ruby
" Release Coordinator: Doug Kearns <dougkearns@gmail.com>
-" Last Change: 2022 Mar 22
+" Last Change: 2022 Jun 30
" 0. Initialization {{{1
" =================
@@ -93,7 +93,7 @@ let s:ruby_indent_keywords =
\ '\<\%(if\|for\|while\|until\|case\|unless\|begin\):\@!\>'
" Def without an end clause: def method_call(...) = <expression>
-let s:ruby_endless_def = '\<def\s\+\k\+[!?]\=\%((.*)\|\s\)\s*='
+let s:ruby_endless_def = '\<def\s\+\%(\k\+\.\)\=\k\+[!?]\=\%((.*)\|\s\)\s*='
" Regex used for words that, at the start of a line, remove a level of indent.
let s:ruby_deindent_keywords =
diff --git a/runtime/makemenu.vim b/runtime/makemenu.vim
index 8da7c0b68c..bf9f43cf16 100644
--- a/runtime/makemenu.vim
+++ b/runtime/makemenu.vim
@@ -676,6 +676,7 @@ SynMenu WXYZ.XFree86\ Config:xf86conf
SynMenu WXYZ.YAML:yaml
SynMenu WXYZ.Yacc:yacc
SynMenu WXYZ.Zimbu:zimbu
+SynMenu WXYZ.Zserio:zserio
call append(s:lnum, "")
diff --git a/runtime/synmenu.vim b/runtime/synmenu.vim
index 8a8c6a2b90..b75a0e9497 100644
--- a/runtime/synmenu.vim
+++ b/runtime/synmenu.vim
@@ -650,6 +650,7 @@ an 50.170.390 &Syntax.WXYZ.XFree86\ Config :cal SetSyn("xf86conf")<CR>
an 50.170.410 &Syntax.WXYZ.YAML :cal SetSyn("yaml")<CR>
an 50.170.420 &Syntax.WXYZ.Yacc :cal SetSyn("yacc")<CR>
an 50.170.440 &Syntax.WXYZ.Zimbu :cal SetSyn("zimbu")<CR>
+an 50.170.440 &Syntax.WXYZ.Zserio:cal SetSyn("zserio")<CR>
" The End Of The Syntax Menu
diff --git a/runtime/syntax/ruby.vim b/runtime/syntax/ruby.vim
index c951fcfe1d..e19d61a051 100644
--- a/runtime/syntax/ruby.vim
+++ b/runtime/syntax/ruby.vim
@@ -3,7 +3,7 @@
" Maintainer: Doug Kearns <dougkearns@gmail.com>
" URL: https://github.com/vim-ruby/vim-ruby
" Release Coordinator: Doug Kearns <dougkearns@gmail.com>
-" Last Change: 2021 Nov 03
+" Last Change: 2023 Mar 16
" ----------------------------------------------------------------------------
"
" Previous Maintainer: Mirko Nasato
@@ -145,9 +145,9 @@ syn cluster rubyStringSpecial contains=rubyInterpolation,rubyStringEscape
syn cluster rubyStringNotTop contains=@rubyStringSpecial,@rubyNestedBrackets,@rubySingleCharEscape
" Regular Expression Metacharacters {{{1
-syn region rubyRegexpComment matchgroup=rubyRegexpSpecial start="(?#" skip="\\\\\|\\)" end=")" contained
-syn region rubyRegexpParens matchgroup=rubyRegexpSpecial start="(\(?:\|?<\=[=!]\|?>\|?<[a-z_]\w*>\|?[imx]*-[imx]*:\=\|\%(?#\)\@!\)" skip="\\\\\|\\)" end=")" contained transparent contains=@rubyRegexpSpecial
-syn region rubyRegexpBrackets matchgroup=rubyRegexpCharClass start="\[\^\=" skip="\\\\\|\\\]" end="\]" contained transparent contains=rubyRegexpBrackets,rubyStringEscape,rubyRegexpEscape,rubyRegexpCharClass,rubyRegexpIntersection oneline
+syn region rubyRegexpComment matchgroup=rubyRegexpSpecial start="(?#" skip="\\\\\|\\)" end=")" contained
+syn region rubyRegexpParens matchgroup=rubyRegexpSpecial start="(\%(?:\|?<\=[=!]\|?>\|?<[a-z_]\w*>\|?[imx]*-[imx]*:\=\|\%(?#\)\@!\)" skip="\\\\\|\\)" end=")" contained transparent contains=@rubyRegexpSpecial
+syn region rubyRegexpBrackets matchgroup=rubyRegexpCharClass start="\[\^\=" skip="\\\\\|\\\]" end="\]" contained transparent contains=rubyRegexpBrackets,rubyStringEscape,rubyRegexpEscape,rubyRegexpCharClass,rubyRegexpIntersection oneline
syn match rubyRegexpCharClass "\\[DdHhRSsWw]" contained display
syn match rubyRegexpCharClass "\[:\^\=\%(alnum\|alpha\|ascii\|blank\|cntrl\|digit\|graph\|lower\|print\|punct\|space\|upper\|word\|xdigit\):\]" contained
syn match rubyRegexpCharClass "\\[pP]{^\=.\{-}}" contained display
@@ -346,7 +346,7 @@ syn cluster rubyDeclaration contains=rubyAliasDeclaration,rubyAliasDeclaration2,
syn match rubyControl "\%#=1\<\%(break\|in\|next\|redo\|retry\|return\)\>"
syn match rubyKeyword "\%#=1\<\%(super\|yield\)\>"
syn match rubyBoolean "\%#=1\<\%(true\|false\)\>[?!]\@!"
-syn match rubyPseudoVariable "\%#=1\<\(self\|nil\)\>[?!]\@!"
+syn match rubyPseudoVariable "\%#=1\<\%(self\|nil\)\>[?!]\@!"
syn match rubyPseudoVariable "\%#=1\<__\%(ENCODING\|dir\|FILE\|LINE\|callee\|method\)__\>"
syn match rubyBeginEnd "\%#=1\<\%(BEGIN\|END\)\>"
@@ -399,11 +399,6 @@ if !exists("b:ruby_no_expensive") && !exists("ruby_no_expensive")
SynFold 'for' syn region rubyRepeatExpression start="\<for\>" start="\%(\%(^\|\.\.\.\=\|[{:,;([<>~\*/%&^|+=-]\|\%(\<\%(\h\|[^\x00-\x7F]\)\%(\w\|[^\x00-\x7F]\)*\)\@<![!?]\)\s*\)\@<=\<\%(until\|while\)\>" matchgroup=rubyRepeat skip="\<end:" end="\<end\>" contains=ALLBUT,@rubyNotTop nextgroup=rubyOptionalDoLine
- if !exists("ruby_minlines")
- let ruby_minlines = 500
- endif
- exe "syn sync minlines=" . ruby_minlines
-
else
syn match rubyControl "\<def\>" nextgroup=rubyMethodDeclaration skipwhite skipnl
syn match rubyControl "\<class\>" nextgroup=rubyClassDeclaration skipwhite skipnl
@@ -412,13 +407,18 @@ else
syn match rubyKeyword "\<\%(alias\|undef\)\>"
endif
+if !exists("ruby_minlines")
+ let ruby_minlines = 500
+endif
+exe "syn sync minlines=" . ruby_minlines
+
" Special Methods {{{1
if !exists("ruby_no_special_methods")
syn match rubyAccess "\<\%(public\|protected\|private\)\>" " use re=2
syn match rubyAccess "\%#=1\<\%(public\|private\)_class_method\>"
syn match rubyAccess "\%#=1\<\%(public\|private\)_constant\>"
syn match rubyAccess "\%#=1\<module_function\>"
- syn match rubyAttribute "\%#=1\%(\%(^\|;\)\s*\)\@<=attr\>\(\s*[.=]\)\@!" " attr is a common variable name
+ syn match rubyAttribute "\%#=1\%(\%(^\|;\)\s*\)\@<=attr\>\%(\s*[.=]\)\@!" " attr is a common variable name
syn match rubyAttribute "\%#=1\<attr_\%(accessor\|reader\|writer\)\>"
syn match rubyControl "\%#=1\<\%(abort\|at_exit\|exit\|fork\|loop\|trap\)\>"
syn match rubyEval "\%#=1\<eval\>"
@@ -435,8 +435,8 @@ syn match rubySharpBang "\%^#!.*" display
syn keyword rubyTodo FIXME NOTE TODO OPTIMIZE HACK REVIEW XXX todo contained
syn match rubyEncoding "[[:alnum:]-_]\+" contained display
syn match rubyMagicComment "\c\%<3l#\s*\zs\%(coding\|encoding\):" contained nextgroup=rubyEncoding skipwhite
-syn match rubyMagicComment "\c\%<10l#\s*\zs\%(frozen_string_literal\|warn_indent\|warn_past_scope\):" contained nextgroup=rubyBoolean skipwhite
-syn match rubyMagicComment "\c\%<10l#\s*\zs\%(shareable_constant_value\):" contained nextgroup=rubyEncoding skipwhite
+syn match rubyMagicComment "\c\%<10l#\s*\zs\%(frozen[-_]string[-_]literal\|warn[-_]indent\|warn[-_]past[-_]scope\):" contained nextgroup=rubyBoolean skipwhite
+syn match rubyMagicComment "\c\%<10l#\s*\zs\%(shareable[-_]constant[-_]value\):" contained nextgroup=rubyEncoding skipwhite
syn match rubyComment "#.*" contains=@rubyCommentSpecial,rubySpaceError,@Spell
syn cluster rubyCommentSpecial contains=rubySharpBang,rubyTodo,rubyMagicComment
diff --git a/runtime/syntax/zserio.vim b/runtime/syntax/zserio.vim
new file mode 100644
index 0000000000..5459915c01
--- /dev/null
+++ b/runtime/syntax/zserio.vim
@@ -0,0 +1,112 @@
+" Vim syntax file
+" Language: Zserio
+" Maintainer: Dominique Pellé <dominique.pelle@gmail.com>
+" Last Change: 2023 Jun 18
+"
+" Zserio is a serialization schema language for modeling binary
+" data types, bitstreams or file formats. Based on the zserio
+" language it is possible to automatically generate encoders and
+" decoders for a given schema in various target languages
+" (e.g. Java, C++, Python).
+"
+" Zserio is an evolution of the DataScript language.
+"
+" For more information, see:
+" - http://zserio.org/
+" - https://github.com/ndsev/zserio
+
+" quit when a syntax file was already loaded
+if exists("b:current_syntax")
+ finish
+endif
+
+let s:keepcpo= &cpo
+set cpo&vim
+
+syn case match
+
+syn keyword zserioPackage import package zserio_compatibility_version
+syn keyword zserioType bit bool string
+syn keyword zserioType int int8 int16 int32 int64
+syn keyword zserioType uint8 uint16 uint32 uint64
+syn keyword zserioType float16 float32 float64
+syn keyword zserioType varint varint16 varint32 varint64
+syn keyword zserioType varuint varsize varuint16 varuint32 varuint64
+syn keyword zserioAlign align
+syn keyword zserioLabel case default
+syn keyword zserioConditional if condition
+syn keyword zserioBoolean true false
+syn keyword zserioCompound struct union choice on enum bitmask subtype
+syn keyword zserioKeyword function return
+syn keyword zserioOperator lengthof valueof instanceof numbits isset
+syn keyword zserioRpc service pubsub topic publish subscribe
+syn keyword zserioRule rule_group rule
+syn keyword zserioStorageClass const implicit packed instantiate
+syn keyword zserioTodo contained TODO FIXME XXX
+syn keyword zserioSql sql sql_table sql_database sql_virtual sql_without_rowid
+syn keyword zserioSql explicit using
+
+" zserioCommentGroup allows adding matches for special things in comments.
+syn cluster zserioCommentGroup contains=zserioTodo
+
+syn match zserioOffset display "^\s*[a-zA-Z_:\.][a-zA-Z0-9_:\.]*\s*:"
+
+syn match zserioNumber display "\<\d\+\>"
+syn match zserioNumberHex display "\<0[xX]\x\+\>"
+syn match zserioNumberBin display "\<[01]\+[bB]\>" contains=zserioBinaryB
+syn match zserioBinaryB display contained "[bB]\>"
+syn match zserioOctal display "\<0\o\+\>" contains=zserioOctalZero
+syn match zserioOctalZero display contained "\<0"
+
+syn match zserioOctalError display "\<0\o*[89]\d*\>"
+
+syn match zserioCommentError display "\*/"
+syn match zserioCommentStartError display "/\*"me=e-1 contained
+
+syn region zserioCommentL
+ \ start="//" skip="\\$" end="$" keepend
+ \ contains=@zserioCommentGroup,@Spell
+syn region zserioComment
+ \ matchgroup=zserioCommentStart start="/\*" end="\*/"
+ \ contains=@zserioCommentGroup,zserioCommentStartError,@Spell extend
+
+syn region zserioString
+ \ start=+L\="+ skip=+\\\\\|\\"+ end=+"+ contains=@Spell
+
+syn sync ccomment zserioComment
+
+" Define the default highlighting.
+hi def link zserioType Type
+hi def link zserioEndian StorageClass
+hi def link zserioStorageClass StorageClass
+hi def link zserioAlign Label
+hi def link zserioLabel Label
+hi def link zserioOffset Label
+hi def link zserioSql PreProc
+hi def link zserioCompound Structure
+hi def link zserioConditional Conditional
+hi def link zserioBoolean Boolean
+hi def link zserioKeyword Statement
+hi def link zserioRpc Keyword
+hi def link zserioRule Keyword
+hi def link zserioString String
+hi def link zserioNumber Number
+hi def link zserioNumberBin Number
+hi def link zserioBinaryB Special
+hi def link zserioOctal Number
+hi def link zserioOctalZero Special
+hi def link zserioOctalError Error
+hi def link zserioNumberHex Number
+hi def link zserioTodo Todo
+hi def link zserioOperator Operator
+hi def link zserioPackage Include
+hi def link zserioCommentError Error
+hi def link zserioCommentStartError Error
+hi def link zserioCommentStart zserioComment
+hi def link zserioCommentL zserioComment
+hi def link zserioComment Comment
+
+let b:current_syntax = "zserio"
+
+let &cpo = s:keepcpo
+unlet s:keepcpo
diff --git a/src/nvim/api/options.c b/src/nvim/api/options.c
index e33cb72e8d..eb80683365 100644
--- a/src/nvim/api/options.c
+++ b/src/nvim/api/options.c
@@ -465,9 +465,6 @@ OptVal get_option_value_for(const char *const name, uint32_t *flagsp, int scope,
/// @param[in] name Option name.
/// @param[in] value Option value.
/// @param[in] opt_flags Flags: OPT_LOCAL, OPT_GLOBAL, or 0 (both).
-/// If OPT_CLEAR is set, the value of the option
-/// is cleared (the exact semantics of this depend
-/// on the option).
/// @param[in] opt_type Option type. See SREQ_* in option_defs.h.
/// @param[in] from Target buffer/window.
/// @param[out] err Error message, if any.
diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c
index 9fa5a89407..891c81d470 100644
--- a/src/nvim/api/ui.c
+++ b/src/nvim/api/ui.c
@@ -830,6 +830,7 @@ void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startcol, Int
size_t ncells = (size_t)(endcol - startcol);
int last_hl = -1;
uint32_t nelem = 0;
+ bool was_space = false;
for (size_t i = 0; i < ncells; i++) {
repeat++;
if (i == ncells - 1 || attrs[i] != attrs[i + 1]
@@ -868,9 +869,12 @@ void remote_ui_raw_line(UI *ui, Integer grid, Integer row, Integer startcol, Int
data->ncells_pending += MIN(repeat, 2);
last_hl = attrs[i];
repeat = 0;
+ was_space = strequal(chunk[i], " ");
}
}
- if (endcol < clearcol) {
+ // If the last chunk was all spaces, add a clearing chunk even if there are
+ // no more cells to clear, so there is no ambiguity about what to clear.
+ if (endcol < clearcol || was_space) {
nelem++;
data->ncells_pending += 1;
mpack_array(buf, 3);
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index 9c061269f1..8eacec4d5e 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -858,9 +858,9 @@ static void free_buffer(buf_T *buf)
xfree(buf->b_prompt_text);
callback_free(&buf->b_prompt_callback);
callback_free(&buf->b_prompt_interrupt);
- clear_fmark(&buf->b_last_cursor);
- clear_fmark(&buf->b_last_insert);
- clear_fmark(&buf->b_last_change);
+ clear_fmark(&buf->b_last_cursor, 0);
+ clear_fmark(&buf->b_last_insert, 0);
+ clear_fmark(&buf->b_last_change, 0);
for (size_t i = 0; i < NMARKS; i++) {
free_fmark(buf->b_namedm[i]);
}
@@ -1910,7 +1910,7 @@ buf_T *buflist_new(char *ffname_arg, char *sfname_arg, linenr_T lnum, int flags)
buf->b_flags |= BF_DUMMY;
}
buf_clear_file(buf);
- clrallmarks(buf); // clear marks
+ clrallmarks(buf, 0); // clear marks
fmarks_check_names(buf); // check file marks for this file
buf->b_p_bl = (flags & BLN_LISTED) ? true : false; // init 'buflisted'
kv_destroy(buf->update_channels);
diff --git a/src/nvim/edit.c b/src/nvim/edit.c
index 88ed1e6cb4..06eb81be92 100644
--- a/src/nvim/edit.c
+++ b/src/nvim/edit.c
@@ -3579,6 +3579,7 @@ static void ins_insert(int replaceState)
// Pressed CTRL-O in Insert mode.
static void ins_ctrl_o(void)
{
+ restart_VIsual_select = 0;
if (State & VREPLACE_FLAG) {
restart_edit = 'V';
} else if (State & REPLACE_FLAG) {
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index 494ebce370..a0618ce7d7 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -2230,8 +2230,16 @@ int do_ecmd(int fnum, char *ffname, char *sfname, exarg_T *eap, linenr_T newlnum
// End Visual mode before switching to another buffer, so the text can be
// copied into the GUI selection buffer.
+ // Careful: may trigger ModeChanged() autocommand
+
+ // Should we block autocommands here?
reset_VIsual();
+ // autocommands freed window :(
+ if (oldwin != NULL && !win_valid(oldwin)) {
+ oldwin = NULL;
+ }
+
if ((command != NULL || newlnum > (linenr_T)0)
&& *get_vim_var_str(VV_SWAPCOMMAND) == NUL) {
// Set v:swapcommand for the SwapExists autocommands.
@@ -3169,21 +3177,21 @@ static bool sub_joining_lines(exarg_T *eap, char *pat, const char *sub, const ch
/// Slightly more memory that is strictly necessary is allocated to reduce the
/// frequency of memory (re)allocation.
///
-/// @param[in,out] new_start pointer to the memory for the replacement text
-/// @param[in] needed_len amount of memory needed
+/// @param[in,out] new_start pointer to the memory for the replacement text
+/// @param[in,out] new_start_len pointer to length of new_start
+/// @param[in] needed_len amount of memory needed
///
/// @returns pointer to the end of the allocated memory
-static char *sub_grow_buf(char **new_start, int needed_len)
- FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_NONNULL_RET
+static char *sub_grow_buf(char **new_start, int *new_start_len, int needed_len)
+ FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET
{
- int new_start_len = 0;
char *new_end;
if (*new_start == NULL) {
// Get some space for a temporary buffer to do the
// substitution into (and some extra space to avoid
// too many calls to xmalloc()/free()).
- new_start_len = needed_len + 50;
- *new_start = xmalloc((size_t)new_start_len);
+ *new_start_len = needed_len + 50;
+ *new_start = xmalloc((size_t)(*new_start_len));
**new_start = NUL;
new_end = *new_start;
} else {
@@ -3192,9 +3200,9 @@ static char *sub_grow_buf(char **new_start, int needed_len)
// extra to avoid too many calls to xmalloc()/free()).
size_t len = strlen(*new_start);
needed_len += (int)len;
- if (needed_len > new_start_len) {
- new_start_len = needed_len + 50;
- *new_start = xrealloc(*new_start, (size_t)new_start_len);
+ if (needed_len > *new_start_len) {
+ *new_start_len = needed_len + 50;
+ *new_start = xrealloc(*new_start, (size_t)(*new_start_len));
}
new_end = *new_start + len;
}
@@ -3519,6 +3527,7 @@ static int do_sub(exarg_T *eap, const proftime_T timeout, const long cmdpreview_
colnr_T matchcol;
colnr_T prev_matchcol = MAXCOL;
char *new_end, *new_start = NULL;
+ int new_start_len = 0;
char *p1;
bool did_sub = false;
int lastone;
@@ -3564,7 +3573,8 @@ static int do_sub(exarg_T *eap, const proftime_T timeout, const long cmdpreview_
// accordingly.
//
// The new text is built up in new_start[]. It has some extra
- // room to avoid using xmalloc()/free() too often.
+ // room to avoid using xmalloc()/free() too often. new_start_len is
+ // the length of the allocated memory at new_start.
//
// Make a copy of the old line, so it won't be taken away when
// updating the screen or handling a multi-line match. The "old_"
@@ -3943,15 +3953,19 @@ static int do_sub(exarg_T *eap, const proftime_T timeout, const long cmdpreview_
p1 = ml_get(sub_firstlnum + (linenr_T)nmatch - 1);
nmatch_tl += nmatch - 1;
}
- size_t copy_len = (size_t)(regmatch.startpos[0].col - copycol);
- new_end = sub_grow_buf(&new_start,
+ int copy_len = regmatch.startpos[0].col - copycol;
+ new_end = sub_grow_buf(&new_start, &new_start_len,
(colnr_T)strlen(p1) - regmatch.endpos[0].col
- + (colnr_T)copy_len + sublen + 1);
+ + copy_len + sublen + 1);
// copy the text up to the part that matched
- memmove(new_end, sub_firstline + copycol, copy_len);
+ memmove(new_end, sub_firstline + copycol, (size_t)copy_len);
new_end += copy_len;
+ if (new_start_len - copy_len < sublen) {
+ sublen = new_start_len - copy_len - 1;
+ }
+
// Finally, at this point we can know where the match actually will
// start in the new text
int start_col = (int)(new_end - new_start);
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index 3ff1442640..44610c81d8 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -3136,7 +3136,7 @@ int cmd_exists(const char *const name)
/// "fullcommand" function
void f_fullcommand(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
- char *name = argvars[0].vval.v_string;
+ char *name = (char *)tv_get_string(&argvars[0]);
rettv->v_type = VAR_STRING;
rettv->vval.v_string = NULL;
diff --git a/src/nvim/ex_session.c b/src/nvim/ex_session.c
index d47d40cbee..e2a4f73840 100644
--- a/src/nvim/ex_session.c
+++ b/src/nvim/ex_session.c
@@ -1080,7 +1080,7 @@ void ex_mkrc(exarg_T *eap)
}
/// @return the name of the view file for the current buffer.
-static char *get_view_file(int c)
+static char *get_view_file(char c)
{
if (curbuf->b_ffname == NULL) {
emsg(_(e_noname));
@@ -1119,8 +1119,7 @@ static char *get_view_file(int c)
}
}
*s++ = '=';
- assert(c >= CHAR_MIN && c <= CHAR_MAX);
- *s++ = (char)c;
+ *s++ = c;
xstrlcpy(s, ".vim", 5);
xfree(sname);
diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c
index 4aaf4397f9..f2efd866f8 100644
--- a/src/nvim/lua/executor.c
+++ b/src/nvim/lua/executor.c
@@ -140,8 +140,8 @@ void nlua_error(lua_State *const lstate, const char *const msg)
}
if (in_script) {
- os_errmsg(str);
- os_errmsg("\n");
+ fprintf(stderr, msg, (int)len, str);
+ fprintf(stderr, "\n");
} else {
msg_ext_set_kind("lua_error");
semsg_multiline(msg, (int)len, str);
diff --git a/src/nvim/mark.c b/src/nvim/mark.c
index 6480a47344..0dc1be526b 100644
--- a/src/nvim/mark.c
+++ b/src/nvim/mark.c
@@ -77,11 +77,12 @@ void free_xfmark(xfmark_T fm)
}
/// Free and clear fmark_T item
-void clear_fmark(fmark_T *fm)
+void clear_fmark(fmark_T *const fm, const Timestamp timestamp)
FUNC_ATTR_NONNULL_ALL
{
free_fmark(*fm);
*fm = (fmark_T)INIT_FMARK;
+ fm->timestamp = timestamp;
}
// Set named mark "c" to position "pos".
@@ -763,20 +764,20 @@ bool mark_check_line_bounds(buf_T *buf, fmark_T *fm, const char **errormsg)
/// Used mainly when trashing the entire buffer during ":e" type commands.
///
/// @param[out] buf Buffer to clear marks in.
-void clrallmarks(buf_T *const buf)
+void clrallmarks(buf_T *const buf, const Timestamp timestamp)
FUNC_ATTR_NONNULL_ALL
{
for (size_t i = 0; i < NMARKS; i++) {
- clear_fmark(&buf->b_namedm[i]);
+ clear_fmark(&buf->b_namedm[i], timestamp);
}
- clear_fmark(&buf->b_last_cursor);
+ clear_fmark(&buf->b_last_cursor, timestamp);
buf->b_last_cursor.mark.lnum = 1;
- clear_fmark(&buf->b_last_insert);
- clear_fmark(&buf->b_last_change);
+ clear_fmark(&buf->b_last_insert, timestamp);
+ clear_fmark(&buf->b_last_change, timestamp);
buf->b_op_start.lnum = 0; // start/end op mark cleared
buf->b_op_end.lnum = 0;
for (int i = 0; i < buf->b_changelistlen; i++) {
- clear_fmark(&buf->b_changelist[i]);
+ clear_fmark(&buf->b_changelist[i], timestamp);
}
buf->b_changelistlen = 0;
}
@@ -925,7 +926,7 @@ void ex_delmarks(exarg_T *eap)
if (*eap->arg == NUL && eap->forceit) {
// clear all marks
- clrallmarks(curbuf);
+ clrallmarks(curbuf, os_time());
} else if (eap->forceit) {
emsg(_(e_invarg));
} else if (*eap->arg == NUL) {
@@ -973,16 +974,13 @@ void ex_delmarks(exarg_T *eap)
} else {
switch (*p) {
case '"':
- curbuf->b_last_cursor.timestamp = timestamp;
- CLEAR_FMARK(&curbuf->b_last_cursor);
+ clear_fmark(&curbuf->b_last_cursor, timestamp);
break;
case '^':
- curbuf->b_last_insert.timestamp = timestamp;
- CLEAR_FMARK(&curbuf->b_last_insert);
+ clear_fmark(&curbuf->b_last_insert, timestamp);
break;
case '.':
- curbuf->b_last_change.timestamp = timestamp;
- CLEAR_FMARK(&curbuf->b_last_change);
+ clear_fmark(&curbuf->b_last_change, timestamp);
break;
case '[':
curbuf->b_op_start.lnum = 0; break;
diff --git a/src/nvim/mark.h b/src/nvim/mark.h
index 8c72579d0f..d84f6a8bb0 100644
--- a/src/nvim/mark.h
+++ b/src/nvim/mark.h
@@ -34,10 +34,6 @@
SET_FMARK(fmarkp___, mark_, fnum_, view_); \
} while (0)
-/// Clear given fmark
-#define CLEAR_FMARK(fmarkp_) \
- RESET_FMARK(fmarkp_, ((pos_T) { 0, 0, 0 }), 0, ((fmarkv_T)INIT_FMARKV))
-
/// Set given extended mark (regular mark + file name)
#define SET_XFMARK(xfmarkp_, mark_, fnum_, view_, fname_) \
do { \
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
index 8270641256..96deae228f 100644
--- a/src/nvim/ops.c
+++ b/src/nvim/ops.c
@@ -4770,7 +4770,7 @@ int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1)
}
}
- while (bits > 0) {
+ while (bits > 0 && i < NUMBUFLEN - 1) {
buf2[i++] = ((n >> --bits) & 0x1) ? '1' : '0';
}
diff --git a/src/nvim/option.c b/src/nvim/option.c
index 716fd3775a..cc3a9c181d 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -1098,9 +1098,10 @@ static char *stropt_get_newval(int nextchar, int opt_idx, char **argp, void *var
/// Part of do_set() for string options.
static void do_set_option_string(int opt_idx, int opt_flags, char **argp, int nextchar,
set_op_T op_arg, uint32_t flags, void *varp_arg, char *errbuf,
- size_t errbuflen, int *value_checked, const char **errmsg)
+ size_t errbuflen, bool *value_checked, const char **errmsg)
{
- char *arg = *argp;
+ vimoption_T *opt = get_option(opt_idx);
+
set_op_T op = op_arg;
void *varp = varp_arg;
char *origval_l = NULL;
@@ -1110,20 +1111,20 @@ static void do_set_option_string(int opt_idx, int opt_flags, char **argp, int ne
// with a local value the local value will be
// reset, use the global value here.
if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0
- && ((int)options[opt_idx].indir & PV_BOTH)) {
- varp = options[opt_idx].var;
+ && ((int)opt->indir & PV_BOTH)) {
+ varp = opt->var;
}
// The old value is kept until we are sure that the new value is valid.
char *oldval = *(char **)varp;
if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) {
- origval_l = *(char **)get_varp_scope(&(options[opt_idx]), OPT_LOCAL);
- origval_g = *(char **)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL);
+ origval_l = *(char **)get_varp_scope(opt, OPT_LOCAL);
+ origval_g = *(char **)get_varp_scope(opt, OPT_GLOBAL);
// A global-local string option might have an empty option as value to
// indicate that the global value should be used.
- if (((int)options[opt_idx].indir & PV_BOTH) && origval_l == empty_option) {
+ if (((int)opt->indir & PV_BOTH) && origval_l == empty_option) {
origval_l = origval_g;
}
}
@@ -1131,61 +1132,57 @@ static void do_set_option_string(int opt_idx, int opt_flags, char **argp, int ne
char *origval;
// When setting the local value of a global option, the old value may be
// the global value.
- if (((int)options[opt_idx].indir & PV_BOTH) && (opt_flags & OPT_LOCAL)) {
- origval = *(char **)get_varp(&options[opt_idx]);
+ if (((int)opt->indir & PV_BOTH) && (opt_flags & OPT_LOCAL)) {
+ origval = *(char **)get_varp(opt);
} else {
origval = oldval;
}
// Get the new value for the option
- char *newval = stropt_get_newval(nextchar, opt_idx, &arg, varp, origval, &op, flags);
+ char *newval = stropt_get_newval(nextchar, opt_idx, argp, varp, origval, &op, flags);
// Set the new value.
- *(char **)(varp) = newval;
- if (newval == NULL) {
- *(char **)(varp) = empty_option;
- }
+ *(char **)(varp) = newval != NULL ? newval : empty_option;
// origval may be freed by did_set_string_option(), make a copy.
- char *saved_origval = (origval != NULL) ? xstrdup(origval) : NULL;
- char *saved_origval_l = (origval_l != NULL) ? xstrdup(origval_l) : NULL;
- char *saved_origval_g = (origval_g != NULL) ? xstrdup(origval_g) : NULL;
+ char *const saved_origval = (origval != NULL) ? xstrdup(origval) : NULL;
+ char *const saved_origval_l = (origval_l != NULL) ? xstrdup(origval_l) : NULL;
+ char *const saved_origval_g = (origval_g != NULL) ? xstrdup(origval_g) : NULL;
// newval (and varp) may become invalid if the buffer is closed by
// autocommands.
- char *saved_newval = (newval != NULL) ? xstrdup(newval) : NULL;
+ char *const saved_newval = (newval != NULL) ? xstrdup(newval) : NULL;
- {
- uint32_t *p = insecure_flag(curwin, opt_idx, opt_flags);
- const int secure_saved = secure;
+ uint32_t *p = insecure_flag(curwin, opt_idx, opt_flags);
+ const int secure_saved = secure;
- // When an option is set in the sandbox, from a modeline or in secure
- // mode, then deal with side effects in secure mode. Also when the
- // value was set with the P_INSECURE flag and is not completely
- // replaced.
- if ((opt_flags & OPT_MODELINE)
- || sandbox != 0
- || (op != OP_NONE && (*p & P_INSECURE))) {
- secure = 1;
- }
+ // When an option is set in the sandbox, from a modeline or in secure
+ // mode, then deal with side effects in secure mode. Also when the
+ // value was set with the P_INSECURE flag and is not completely
+ // replaced.
+ if ((opt_flags & OPT_MODELINE)
+ || sandbox != 0
+ || (op != OP_NONE && (*p & P_INSECURE))) {
+ secure = 1;
+ }
- // Handle side effects, and set the global value for ":set" on local
- // options. Note: when setting 'syntax' or 'filetype' autocommands may
- // be triggered that can cause havoc.
- *errmsg = did_set_string_option(opt_idx, (char **)varp, oldval, newval,
- errbuf, errbuflen,
- opt_flags, value_checked);
+ // Handle side effects, and set the global value for ":set" on local
+ // options. Note: when setting 'syntax' or 'filetype' autocommands may
+ // be triggered that can cause havoc.
+ *errmsg = did_set_string_option(curbuf, curwin, opt_idx, (char **)varp, oldval,
+ errbuf, errbuflen,
+ opt_flags, value_checked);
- secure = secure_saved;
- }
+ secure = secure_saved;
+ // call autocommand after handling side effects
if (*errmsg == NULL) {
if (!starting) {
trigger_optionset_string(opt_idx, opt_flags, saved_origval, saved_origval_l,
saved_origval_g, saved_newval);
}
if (options[opt_idx].flags & P_UI_OPTION) {
- ui_call_option_set(cstr_as_string(options[opt_idx].fullname),
+ ui_call_option_set(cstr_as_string(opt->fullname),
CSTR_AS_OBJ(saved_newval));
}
}
@@ -1193,8 +1190,6 @@ static void do_set_option_string(int opt_idx, int opt_flags, char **argp, int ne
xfree(saved_origval_l);
xfree(saved_origval_g);
xfree(saved_newval);
-
- *argp = arg;
}
static set_op_T get_op(const char *arg)
@@ -1337,7 +1332,7 @@ static void do_set_option_value(int opt_idx, int opt_flags, char **argp, int pre
set_op_T op, uint32_t flags, void *varp, char *errbuf,
size_t errbuflen, const char **errmsg)
{
- int value_checked = false;
+ bool value_checked = false;
if (flags & P_BOOL) { // boolean
do_set_bool(opt_idx, opt_flags, prefix, nextchar, varp, errmsg);
} else if (flags & P_NUM) { // numeric
@@ -1598,7 +1593,7 @@ int do_set(char *arg, int opt_flags)
/// @param opt_flags possibly with OPT_MODELINE
/// @param new_value value was replaced completely
/// @param value_checked value was checked to be safe, no need to set P_INSECURE
-void did_set_option(int opt_idx, int opt_flags, int new_value, int value_checked)
+void did_set_option(int opt_idx, int opt_flags, bool new_value, bool value_checked)
{
options[opt_idx].flags |= P_WAS_SET;
@@ -2981,181 +2976,189 @@ static const char *check_num_option_bounds(long *pp, long old_value, long old_Ro
return errmsg;
}
-/// Set the value of a number option, taking care of side effects
-///
-/// @param[in] opt_idx Option index in options[] table.
-/// @param[out] varp Pointer to the option variable.
-/// @param[in] value New value.
-/// @param errbuf Buffer for error messages.
-/// @param[in] errbuflen Length of `errbuf`.
-/// @param[in] opt_flags OPT_LOCAL, OPT_GLOBAL or OPT_MODELINE.
-///
-/// @return NULL on success, error message on error.
-static const char *set_num_option(int opt_idx, void *varp, long value, char *errbuf,
- size_t errbuflen, int opt_flags)
+/// Options that need some validation.
+static const char *validate_num_option(const long *pp, long *valuep)
{
- const char *errmsg = NULL;
- long old_value = *(long *)varp;
- long old_global_value = 0; // only used when setting a local and global option
- long old_Rows = Rows; // remember old Rows
- long *pp = (long *)varp;
-
- // Disallow changing some options from secure mode.
- if ((secure || sandbox != 0) && (options[opt_idx].flags & P_SECURE)) {
- return e_secure;
- }
-
- // Save the global value before changing anything. This is needed as for
- // a global-only option setting the "local value" in fact sets the global
- // value (since there is only one value).
- if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) {
- old_global_value = *(long *)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL);
- }
+ long value = *valuep;
// Many number options assume their value is in the signed int range.
if (value < INT_MIN || value > INT_MAX) {
return e_invarg;
}
- // Options that need some validation.
if (pp == &p_wh) {
if (value < 1) {
- errmsg = e_positive;
+ return e_positive;
} else if (p_wmh > value) {
- errmsg = e_winheight;
+ return e_winheight;
}
} else if (pp == &p_hh) {
if (value < 0) {
- errmsg = e_positive;
+ return e_positive;
}
} else if (pp == &p_wmh) {
if (value < 0) {
- errmsg = e_positive;
+ return e_positive;
} else if (value > p_wh) {
- errmsg = e_winheight;
+ return e_winheight;
}
} else if (pp == &p_wiw) {
if (value < 1) {
- errmsg = e_positive;
+ return e_positive;
} else if (p_wmw > value) {
- errmsg = e_winwidth;
+ return e_winwidth;
}
} else if (pp == &p_wmw) {
if (value < 0) {
- errmsg = e_positive;
+ return e_positive;
} else if (value > p_wiw) {
- errmsg = e_winwidth;
+ return e_winwidth;
}
} else if (pp == &p_mco) {
- value = MAX_MCO;
+ *valuep = MAX_MCO;
} else if (pp == &p_titlelen) {
if (value < 0) {
- errmsg = e_positive;
+ return e_positive;
}
} else if (pp == &p_uc) {
if (value < 0) {
- errmsg = e_positive;
+ return e_positive;
}
} else if (pp == &p_ch) {
if (value < 0) {
- errmsg = e_positive;
+ return e_positive;
} else {
p_ch_was_zero = value == 0;
}
} else if (pp == &p_tm) {
if (value < 0) {
- errmsg = e_positive;
+ return e_positive;
}
} else if (pp == &p_hi) {
if (value < 0) {
- errmsg = e_positive;
+ return e_positive;
} else if (value > 10000) {
- errmsg = e_invarg;
+ return e_invarg;
}
} else if (pp == &p_pyx) {
if (value == 0) {
- value = 3;
+ *valuep = 3;
} else if (value != 3) {
- errmsg = e_invarg;
+ return e_invarg;
}
} else if (pp == &p_re) {
if (value < 0 || value > 2) {
- errmsg = e_invarg;
+ return e_invarg;
}
} else if (pp == &p_report) {
if (value < 0) {
- errmsg = e_positive;
+ return e_positive;
}
} else if (pp == &p_so) {
if (value < 0 && full_screen) {
- errmsg = e_positive;
+ return e_positive;
}
} else if (pp == &p_siso) {
if (value < 0 && full_screen) {
- errmsg = e_positive;
+ return e_positive;
}
} else if (pp == &p_cwh) {
if (value < 1) {
- errmsg = e_positive;
+ return e_positive;
}
} else if (pp == &p_ut) {
if (value < 0) {
- errmsg = e_positive;
+ return e_positive;
}
} else if (pp == &p_ss) {
if (value < 0) {
- errmsg = e_positive;
+ return e_positive;
}
} else if (pp == &curwin->w_p_fdl || pp == &curwin->w_allbuf_opt.wo_fdl) {
if (value < 0) {
- errmsg = e_positive;
+ return e_positive;
}
} else if (pp == &curwin->w_p_cole || pp == &curwin->w_allbuf_opt.wo_cole) {
if (value < 0) {
- errmsg = e_positive;
+ return e_positive;
} else if (value > 3) {
- errmsg = e_invarg;
+ return e_invarg;
}
} else if (pp == &curwin->w_p_nuw || pp == &curwin->w_allbuf_opt.wo_nuw) {
if (value < 1) {
- errmsg = e_positive;
+ return e_positive;
} else if (value > MAX_NUMBERWIDTH) {
- errmsg = e_invarg;
+ return e_invarg;
}
} else if (pp == &curbuf->b_p_iminsert || pp == &p_iminsert) {
if (value < 0 || value > B_IMODE_LAST) {
- errmsg = e_invarg;
+ return e_invarg;
}
} else if (pp == &curbuf->b_p_imsearch || pp == &p_imsearch) {
if (value < -1 || value > B_IMODE_LAST) {
- errmsg = e_invarg;
+ return e_invarg;
}
} else if (pp == &curbuf->b_p_channel || pp == &p_channel) {
- errmsg = e_invarg;
+ return e_invarg;
} else if (pp == &curbuf->b_p_scbk || pp == &p_scbk) {
if (value < -1 || value > SB_MAX) {
- errmsg = e_invarg;
+ return e_invarg;
}
} else if (pp == &curbuf->b_p_sw || pp == &p_sw) {
if (value < 0) {
- errmsg = e_positive;
+ return e_positive;
}
} else if (pp == &curbuf->b_p_ts || pp == &p_ts) {
if (value < 1) {
- errmsg = e_positive;
+ return e_positive;
} else if (value > TABSTOP_MAX) {
- errmsg = e_invarg;
+ return e_invarg;
}
} else if (pp == &curbuf->b_p_tw || pp == &p_tw) {
if (value < 0) {
- errmsg = e_positive;
+ return e_positive;
}
} else if (pp == &p_wd) {
if (value < 0) {
- errmsg = e_positive;
+ return e_positive;
}
}
+ return NULL;
+}
+
+/// Set the value of a number option, taking care of side effects
+///
+/// @param[in] opt_idx Option index in options[] table.
+/// @param[out] varp Pointer to the option variable.
+/// @param[in] value New value.
+/// @param errbuf Buffer for error messages.
+/// @param[in] errbuflen Length of `errbuf`.
+/// @param[in] opt_flags OPT_LOCAL, OPT_GLOBAL or OPT_MODELINE.
+///
+/// @return NULL on success, error message on error.
+static const char *set_num_option(int opt_idx, void *varp, long value, char *errbuf,
+ size_t errbuflen, int opt_flags)
+{
+ long old_value = *(long *)varp;
+ long old_global_value = 0; // only used when setting a local and global option
+ long old_Rows = Rows; // remember old Rows
+ long *pp = (long *)varp;
+
+ // Disallow changing some options from secure mode.
+ if ((secure || sandbox != 0) && (options[opt_idx].flags & P_SECURE)) {
+ return e_secure;
+ }
+
+ // Save the global value before changing anything. This is needed as for
+ // a global-only option setting the "local value" in fact sets the global
+ // value (since there is only one value).
+ if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) {
+ old_global_value = *(long *)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL);
+ }
+
+ const char *errmsg = validate_num_option(pp, &value);
+
// Don't change the value and return early if validation failed.
if (errmsg != NULL) {
return errmsg;
@@ -3714,14 +3717,73 @@ vimoption_T *get_option(int opt_idx)
return &options[opt_idx];
}
+/// Clear an option
+///
+/// The exact semantics of this depend on the option.
+static OptVal clear_optval(const char *name, uint32_t flags, void *varp, buf_T *buf, win_T *win)
+{
+ OptVal v = NIL_OPTVAL;
+
+ // Change the type of the OptVal to the type used by the option so that it can be cleared.
+ // TODO(famiu): Clean up all of this after set_(num|bool|string)_option() is unified.
+
+ if (flags & P_BOOL) {
+ v.type = kOptValTypeBoolean;
+ if ((int *)varp == &buf->b_p_ar) {
+ // TODO(lewis6991): replace this with a more general condition that
+ // indicates we are setting the local value of a global-local option
+ v.data.boolean = kNone;
+ } else {
+ v = get_option_value(name, NULL, OPT_GLOBAL, NULL);
+ }
+ } else if (flags & P_NUM) {
+ v.type = kOptValTypeNumber;
+ if ((long *)varp == &curbuf->b_p_ul) {
+ // The one true special case
+ v.data.number = NO_LOCAL_UNDOLEVEL;
+ } else if ((long *)varp == &win->w_p_so || (long *)varp == &win->w_p_siso) {
+ // TODO(lewis6991): replace this with a more general condition that
+ // indicates we are setting the local value of a global-local option
+ v.data.number = -1;
+ } else {
+ v = get_option_value(name, NULL, OPT_GLOBAL, NULL);
+ }
+ } else if (flags & P_STRING) {
+ v.type = kOptValTypeString;
+ v.data.string.data = NULL;
+ }
+
+ return v;
+}
+
+static const char *set_option(int opt_idx, void *varp, OptVal *v, int opt_flags, char *errbuf,
+ size_t errbuflen)
+{
+ const char *errmsg = NULL;
+
+ bool value_checked = false;
+
+ if (v->type == kOptValTypeBoolean) {
+ errmsg = set_bool_option(opt_idx, varp, (int)v->data.boolean, opt_flags);
+ } else if (v->type == kOptValTypeNumber) {
+ errmsg = set_num_option(opt_idx, varp, (long)v->data.number, errbuf, errbuflen, opt_flags);
+ } else if (v->type == kOptValTypeString) {
+ errmsg = set_string_option(opt_idx, varp, v->data.string.data, opt_flags, &value_checked,
+ errbuf, errbuflen);
+ }
+
+ if (errmsg != NULL) {
+ did_set_option(opt_idx, opt_flags, true, value_checked);
+ }
+
+ return errmsg;
+}
+
/// Set the value of an option
///
/// @param[in] name Option name.
/// @param[in] value Option value. If NIL_OPTVAL, the option value is cleared.
/// @param[in] opt_flags Flags: OPT_LOCAL, OPT_GLOBAL, or 0 (both).
-/// If OPT_CLEAR is set, the value of the option
-/// is cleared (the exact semantics of this depend
-/// on the option).
///
/// @return NULL on success, an untranslated error message on error.
const char *set_option_value(const char *const name, const OptVal value, int opt_flags)
@@ -3763,17 +3825,7 @@ const char *set_option_value(const char *const name, const OptVal value, int opt
OptVal v = optval_copy(value);
if (v.type == kOptValTypeNil) {
- opt_flags |= OPT_CLEAR;
-
- // Change the type of the OptVal to the type used by the option so that it can be cleared.
- // TODO(famiu): Clean up all of this after set_(num|bool|string)_option() is unified.
- if (flags & P_BOOL) {
- v.type = kOptValTypeBoolean;
- } else if (flags & P_NUM) {
- v.type = kOptValTypeNumber;
- } else if (flags & P_STRING) {
- v.type = kOptValTypeString;
- }
+ v = clear_optval(name, flags, varp, curbuf, curwin);
} else if (!optval_match_type(v, opt_idx)) {
char *rep = optval_to_cstr(v);
char *valid_types = option_get_valid_types(opt_idx);
@@ -3785,42 +3837,7 @@ const char *set_option_value(const char *const name, const OptVal value, int opt
goto end;
}
- switch (v.type) {
- case kOptValTypeNil:
- abort(); // This will never happen.
- case kOptValTypeBoolean: {
- if (opt_flags & OPT_CLEAR) {
- if ((int *)varp == &curbuf->b_p_ar) {
- v.data.boolean = kNone;
- } else {
- v = get_option_value(name, NULL, OPT_GLOBAL, NULL);
- }
- }
- errmsg = set_bool_option(opt_idx, varp, (int)v.data.boolean, opt_flags);
- break;
- }
- case kOptValTypeNumber: {
- if (opt_flags & OPT_CLEAR) {
- if ((long *)varp == &curbuf->b_p_ul) {
- v.data.number = NO_LOCAL_UNDOLEVEL;
- } else if ((long *)varp == &curwin->w_p_so || (long *)varp == &curwin->w_p_siso) {
- v.data.number = -1;
- } else {
- v = get_option_value(name, NULL, OPT_GLOBAL, NULL);
- }
- }
- errmsg = set_num_option(opt_idx, varp, (long)v.data.number, errbuf, sizeof(errbuf), opt_flags);
- break;
- }
- case kOptValTypeString: {
- const char *s = v.data.string.data;
- if (s == NULL || opt_flags & OPT_CLEAR) {
- s = "";
- }
- errmsg = set_string_option(opt_idx, s, opt_flags, errbuf, sizeof(errbuf));
- break;
- }
- }
+ errmsg = set_option(opt_idx, varp, &v, opt_flags, errbuf, sizeof(errbuf));
end:
optval_free(v); // Free the copied OptVal.
@@ -4477,7 +4494,7 @@ void *get_option_varp_scope_from(int opt_idx, int scope, buf_T *buf, win_T *win)
return get_varp_scope_from(&(options[opt_idx]), scope, buf, win);
}
-static void *get_varp_from(vimoption_T *p, buf_T *buf, win_T *win)
+void *get_varp_from(vimoption_T *p, buf_T *buf, win_T *win)
{
// hidden option, always return NULL
if (p->var == NULL) {
diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h
index 35687a19b7..1007925ccb 100644
--- a/src/nvim/option_defs.h
+++ b/src/nvim/option_defs.h
@@ -70,7 +70,6 @@ typedef enum {
OPT_ONECOLUMN = 0x40, ///< list options one per line
OPT_NO_REDRAW = 0x80, ///< ignore redraw flags on option
OPT_SKIPRTP = 0x100, ///< "skiprtp" in 'sessionoptions'
- OPT_CLEAR = 0x200, ///< Clear local value of an option.
} OptionFlags;
// Return value from get_option_value_strict
@@ -1009,9 +1008,9 @@ typedef struct {
// Option value was checked to be safe, no need to set P_INSECURE
// Used for the 'keymap', 'filetype' and 'syntax' options.
- int os_value_checked;
+ bool os_value_checked;
// Option value changed. Used for the 'filetype' and 'syntax' options.
- int os_value_changed;
+ bool os_value_changed;
// Used by the 'isident', 'iskeyword', 'isprint' and 'isfname' options.
// Set to true if the character table is modified when processing the
diff --git a/src/nvim/optionstr.c b/src/nvim/optionstr.c
index 07e2e5eed1..17afd10ee3 100644
--- a/src/nvim/optionstr.c
+++ b/src/nvim/optionstr.c
@@ -419,7 +419,9 @@ void set_string_option_direct_in_buf(buf_T *buf, const char *name, int opt_idx,
curbuf = save_curbuf;
unblock_autocmds();
}
+
/// Set a string option to a new value, handling the effects
+/// Must not be called with a hidden option!
///
/// @param[in] opt_idx Option to set.
/// @param[in] value New value.
@@ -427,56 +429,86 @@ void set_string_option_direct_in_buf(buf_T *buf, const char *name, int opt_idx,
/// #OPT_GLOBAL.
///
/// @return NULL on success, an untranslated error message on error.
-const char *set_string_option(const int opt_idx, const char *const value, const int opt_flags,
- char *const errbuf, const size_t errbuflen)
- FUNC_ATTR_NONNULL_ARG(2) FUNC_ATTR_WARN_UNUSED_RESULT
+const char *set_string_option(const int opt_idx, void *varp_arg, const char *value,
+ const int opt_flags, bool *value_checked, char *const errbuf,
+ const size_t errbuflen)
+ FUNC_ATTR_WARN_UNUSED_RESULT
{
vimoption_T *opt = get_option(opt_idx);
- if (opt->var == NULL) { // don't set hidden option
- return NULL;
+ void *varp = (char **)varp_arg;
+ char *origval_l = NULL;
+ char *origval_g = NULL;
+
+ // When using ":set opt=val" for a global option
+ // with a local value the local value will be
+ // reset, use the global value here.
+ if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0
+ && ((int)opt->indir & PV_BOTH)) {
+ varp = opt->var;
}
- char *const s = xstrdup(value);
- char **const varp
- = (char **)get_varp_scope(opt, ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0
- ? ((opt->indir & PV_BOTH) ? OPT_GLOBAL : OPT_LOCAL)
- : opt_flags));
- char *const oldval = *varp;
- char *oldval_l = NULL;
- char *oldval_g = NULL;
+ // The old value is kept until we are sure that the new value is valid.
+ char *oldval = *(char **)varp;
if ((opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0) {
- oldval_l = *(char **)get_varp_scope(opt, OPT_LOCAL);
- oldval_g = *(char **)get_varp_scope(opt, OPT_GLOBAL);
+ origval_l = *(char **)get_varp_scope(opt, OPT_LOCAL);
+ origval_g = *(char **)get_varp_scope(opt, OPT_GLOBAL);
+
+ // A global-local string option might have an empty option as value to
+ // indicate that the global value should be used.
+ if (((int)opt->indir & PV_BOTH) && origval_l == empty_option) {
+ origval_l = origval_g;
+ }
}
- *varp = s;
+ char *origval;
+ // When setting the local value of a global option, the old value may be
+ // the global value.
+ if (((int)opt->indir & PV_BOTH) && (opt_flags & OPT_LOCAL)) {
+ origval = *(char **)get_varp_from(opt, curbuf, curwin);
+ } else {
+ origval = oldval;
+ }
- char *const saved_oldval = xstrdup(oldval);
- char *const saved_oldval_l = (oldval_l != NULL) ? xstrdup(oldval_l) : 0;
- char *const saved_oldval_g = (oldval_g != NULL) ? xstrdup(oldval_g) : 0;
- char *const saved_newval = xstrdup(s);
+ *(char **)varp = xstrdup(value != NULL ? value : empty_option);
- int value_checked = false;
- const char *const errmsg = did_set_string_option(opt_idx, varp, oldval, s, errbuf, errbuflen,
- opt_flags, &value_checked);
- if (errmsg == NULL) {
- did_set_option(opt_idx, opt_flags, true, value_checked);
+ char *const saved_origval = (origval != NULL) ? xstrdup(origval) : NULL;
+ char *const saved_oldval_l = (origval_l != NULL) ? xstrdup(origval_l) : 0;
+ char *const saved_oldval_g = (origval_g != NULL) ? xstrdup(origval_g) : 0;
+
+ // newval (and varp) may become invalid if the buffer is closed by
+ // autocommands.
+ char *const saved_newval = xstrdup(*(char **)varp);
+
+ const int secure_saved = secure;
+
+ // When an option is set in the sandbox, from a modeline or in secure
+ // mode, then deal with side effects in secure mode. Also when the
+ // value was set with the P_INSECURE flag and is not completely
+ // replaced.
+ if ((opt_flags & OPT_MODELINE)
+ || sandbox != 0) {
+ secure = 1;
}
+ const char *const errmsg = did_set_string_option(curbuf, curwin, opt_idx, varp, oldval, errbuf,
+ errbuflen, opt_flags, value_checked);
+
+ secure = secure_saved;
+
// call autocommand after handling side effects
if (errmsg == NULL) {
if (!starting) {
- trigger_optionset_string(opt_idx, opt_flags, saved_oldval, saved_oldval_l, saved_oldval_g,
- saved_newval);
+ trigger_optionset_string(opt_idx, opt_flags, saved_origval, saved_oldval_l,
+ saved_oldval_g, saved_newval);
}
if (opt->flags & P_UI_OPTION) {
ui_call_option_set(cstr_as_string(opt->fullname),
- STRING_OBJ(cstr_as_string(saved_newval)));
+ CSTR_AS_OBJ(saved_newval));
}
}
- xfree(saved_oldval);
+ xfree(saved_origval);
xfree(saved_oldval_l);
xfree(saved_oldval_g);
xfree(saved_newval);
@@ -2066,9 +2098,9 @@ static void do_spelllang_source(win_T *win)
/// @param value_checked value was checked to be safe, no need to set P_INSECURE
///
/// @return NULL for success, or an untranslated error message for an error
-static const char *did_set_string_option_for(buf_T *buf, win_T *win, int opt_idx, char **varp,
- char *oldval, const char *value, char *errbuf,
- size_t errbuflen, int opt_flags, int *value_checked)
+const char *did_set_string_option(buf_T *buf, win_T *win, int opt_idx, char **varp, char *oldval,
+ char *errbuf, size_t errbuflen, int opt_flags,
+ bool *value_checked)
{
const char *errmsg = NULL;
int restore_chartab = false;
@@ -2082,14 +2114,14 @@ static const char *did_set_string_option_for(buf_T *buf, win_T *win, int opt_idx
.os_idx = opt_idx,
.os_flags = opt_flags,
.os_oldval.string = oldval,
- .os_newval.string = value,
+ .os_newval.string = *varp,
.os_value_checked = false,
.os_value_changed = false,
.os_restore_chartab = false,
.os_errbuf = errbuf,
.os_errbuflen = errbuflen,
- .os_win = curwin,
- .os_buf = curbuf,
+ .os_win = win,
+ .os_buf = buf,
};
// Disallow changing some options from secure mode
@@ -2186,13 +2218,6 @@ static const char *did_set_string_option_for(buf_T *buf, win_T *win, int opt_idx
return errmsg;
}
-const char *did_set_string_option(int opt_idx, char **varp, char *oldval, char *value, char *errbuf,
- size_t errbuflen, int opt_flags, int *value_checked)
-{
- return did_set_string_option_for(curbuf, curwin, opt_idx, varp, oldval, value, errbuf,
- errbuflen, opt_flags, value_checked);
-}
-
/// Check an option that can be a range of string values.
///
/// @param list when true: accept a list of values
diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c
index d5d0f3346f..1d0a987780 100644
--- a/src/nvim/regexp.c
+++ b/src/nvim/regexp.c
@@ -1765,9 +1765,10 @@ static int vim_regsub_both(char *source, typval_T *expr, char *dest, int destlen
// "flags & REGSUB_COPY" == 0 to the call with
// "flags & REGSUB_COPY" != 0.
if (copy) {
- if (eval_result[nested] != NULL) {
+ size_t reslen = eval_result[nested] != NULL ? strlen(eval_result[nested]) : 0;
+ if (eval_result[nested] != NULL && reslen < (size_t)destlen) {
STRCPY(dest, eval_result[nested]);
- dst += strlen(eval_result[nested]);
+ dst += reslen;
XFREE_CLEAR(eval_result[nested]);
}
} else {
diff --git a/test/busted_runner.lua b/test/busted_runner.lua
index d6864c6492..8176292bcf 100644
--- a/test/busted_runner.lua
+++ b/test/busted_runner.lua
@@ -4,4 +4,61 @@ local suffix = (platform and platform.sysname:lower():find'windows') and '.dll'
package.path = deps_install_dir.."/share/lua/5.1/?.lua;"..deps_install_dir.."/share/lua/5.1/?/init.lua;"..package.path
package.cpath = deps_install_dir.."/lib/lua/5.1/?"..suffix..";"..package.cpath;
+local uv = vim.uv
+
+local system = {}
+package.loaded['system.core'] = system
+function system.monotime()
+ uv.update_time()
+ return uv.now()*1e-3
+end
+function system.gettime()
+ local sec, usec = uv.gettimeofday()
+ return sec+usec*1e-6
+end
+function system.sleep(sec)
+ uv.sleep(sec*1e3)
+end
+
+local term = {}
+package.loaded['term.core'] = term
+function term.isatty(_)
+ return uv.guess_handle(1) == 'tty'
+end
+
+local lfs = {}
+package.loaded['lfs'] = lfs
+
+function lfs.attributes(path, attr)
+ if attr == 'mode' then
+ local stat = uv.fs_stat(path)
+ return stat and stat.type or ''
+ else
+ error('not implemented')
+ end
+end
+
+function lfs.currentdir()
+ return uv.cwd()
+end
+
+function lfs.chdir(dir)
+ local status, err = pcall(uv.chdir, dir)
+ if status then
+ return true
+ else
+ return nil, err
+ end
+end
+
+function lfs.dir(path)
+ local fs = uv.fs_scandir(path)
+ return function()
+ if not fs then
+ return
+ end
+ return uv.fs_scandir_next(fs)
+ end
+end
+
require 'busted.runner'({ standalone = false })
diff --git a/test/functional/legacy/crash_spec.lua b/test/functional/legacy/crash_spec.lua
new file mode 100644
index 0000000000..5094f81847
--- /dev/null
+++ b/test/functional/legacy/crash_spec.lua
@@ -0,0 +1,16 @@
+local helpers = require('test.functional.helpers')(after_each)
+local assert_alive = helpers.assert_alive
+local clear = helpers.clear
+local command = helpers.command
+local feed = helpers.feed
+
+before_each(clear)
+
+-- oldtest: Test_crash1()
+it('no crash when ending Visual mode while editing buffer closes window', function()
+ command('new')
+ command('autocmd ModeChanged v:n ++once close')
+ feed('v')
+ command('enew')
+ assert_alive()
+end)
diff --git a/test/functional/shada/marks_spec.lua b/test/functional/shada/marks_spec.lua
index 07364382e8..7f03022ab8 100644
--- a/test/functional/shada/marks_spec.lua
+++ b/test/functional/shada/marks_spec.lua
@@ -250,7 +250,7 @@ describe('ShaDa support code', function()
eq(0, exc_exec('rshada'))
end)
- it('updates deleted marks', function()
+ it('updates deleted marks with :delmarks', function()
nvim_command('edit ' .. testfilename)
nvim_command('mark A')
@@ -259,14 +259,39 @@ describe('ShaDa support code', function()
-- since it can't be set via :mark
feed('ggifoobar<esc>')
nvim_command('wshada')
- nvim_command('normal! `A`a`.')
+ reset()
+ nvim_command('edit ' .. testfilename)
+ nvim_command('normal! `A`a`.')
nvim_command('delmarks A a .')
nvim_command('wshada')
reset()
+ nvim_command('edit ' .. testfilename)
eq('Vim(normal):E20: Mark not set', exc_exec('normal! `A'))
eq('Vim(normal):E20: Mark not set', exc_exec('normal! `a'))
eq('Vim(normal):E20: Mark not set', exc_exec('normal! `.'))
end)
+
+ it('updates deleted marks with :delmarks!', function()
+ nvim_command('edit ' .. testfilename)
+
+ nvim_command('mark A')
+ nvim_command('mark a')
+ feed('ggifoobar<esc>')
+ nvim_command('wshada')
+
+ reset()
+ nvim_command('edit ' .. testfilename)
+ nvim_command('normal! `A`a`.')
+ nvim_command('delmarks!')
+ nvim_command('wshada')
+
+ reset()
+ nvim_command('edit ' .. testfilename)
+ eq('Vim(normal):E20: Mark not set', exc_exec('normal! `a'))
+ eq('Vim(normal):E20: Mark not set', exc_exec('normal! `.'))
+ -- Make sure that uppercase marks aren't deleted.
+ nvim_command('normal! `A')
+ end)
end)
diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua
index 1958281592..57dcb14cf8 100644
--- a/test/functional/terminal/tui_spec.lua
+++ b/test/functional/terminal/tui_spec.lua
@@ -27,8 +27,6 @@ local is_os = helpers.is_os
local new_pipename = helpers.new_pipename
local spawn_argv = helpers.spawn_argv
local set_session = helpers.set_session
-local feed = helpers.feed
-local eval = helpers.eval
local write_file = helpers.write_file
if helpers.skip(helpers.is_os('win')) then return end
@@ -898,7 +896,7 @@ describe('TUI', function()
feed_data('\022\027[107;33u') -- Meta + k
feed_data('\022\027[13;41u') -- Super + Meta + Enter
feed_data('\022\027[127;48u') -- Shift + Alt + Ctrl + Super + Meta + Backspace
- feed('\n')
+ feed_data('\n')
feed_data('\022\027[57376;9u') -- Super + F13
feed_data('\022\027[57377;33u') -- Meta + F14
feed_data('\022\027[57378;41u') -- Super + Meta + F15
@@ -1752,7 +1750,7 @@ describe('TUI', function()
|
{5:-- TERMINAL --} |
]])
- feed('i')
+ feed_data('i')
screen:expect([[
{1: } |
{2:~}{3: }|
@@ -1898,6 +1896,30 @@ describe('TUI', function()
{3:-- TERMINAL --} |
]])
end)
+
+ it('draws line with many trailing spaces correctly #24955', function()
+ local screen = thelpers.screen_setup(0, '["'..nvim_prog..[[", "-u", "NONE", "-i", "NONE"]]
+ ..[[, "--cmd", "call setline(1, ['1st line' .. repeat(' ', 153), '2nd line'])"]]..']', 80)
+ screen:expect{grid=[[
+ {1:1}st line |
+ |
+ |
+ 2nd line |
+ {5:[No Name] [+] 1,1 All}|
+ |
+ {3:-- TERMINAL --} |
+ ]]}
+ feed_data('$')
+ screen:expect{grid=[[
+ 1st line |
+ |
+ {1: } |
+ 2nd line |
+ {5:[No Name] [+] 1,161 All}|
+ |
+ {3:-- TERMINAL --} |
+ ]]}
+ end)
end)
describe('TUI UIEnter/UILeave', function()
@@ -2703,8 +2725,8 @@ describe("TUI as a client", function()
local client_super = spawn_argv(true)
set_session(server)
- local server_pipe = eval'v:servername'
- feed'iHalloj!<esc>'
+ local server_pipe = meths.get_vvar('servername')
+ server:request('nvim_input', 'iHalloj!<Esc>')
set_session(client_super)
local screen = thelpers.screen_setup(0,
diff --git a/test/old/testdir/crash/vim_regsub_both b/test/old/testdir/crash/vim_regsub_both
new file mode 100644
index 0000000000..a82b205c8f
--- /dev/null
+++ b/test/old/testdir/crash/vim_regsub_both
@@ -0,0 +1,10 @@
+fu R()
+sil!norm0z=
+endf
+cal R()
+s/\%')/\=R()
+d
+no0 normyynore sm:vs0@vvvvvvvvvvse()dir(ĽXtest=csd{so88
+vs
+0scr
+so
diff --git a/test/old/testdir/test_crash.vim b/test/old/testdir/test_crash.vim
new file mode 100644
index 0000000000..445fe8d5a7
--- /dev/null
+++ b/test/old/testdir/test_crash.vim
@@ -0,0 +1,34 @@
+" Some tests, that used to crash Vim
+source check.vim
+source screendump.vim
+
+CheckScreendump
+
+func Test_crash1()
+ " The following used to crash Vim
+ let opts = #{wait_for_ruler: 0, rows: 20}
+ let args = ' -u NONE -i NONE -n -e -s -S '
+ let buf = RunVimInTerminal(args .. ' crash/poc_huaf1', opts)
+ call VerifyScreenDump(buf, 'Test_crash_01', {})
+ exe buf .. "bw!"
+
+ let buf = RunVimInTerminal(args .. ' crash/poc_huaf2', opts)
+ call VerifyScreenDump(buf, 'Test_crash_01', {})
+ exe buf .. "bw!"
+
+ let buf = RunVimInTerminal(args .. ' crash/poc_huaf3', opts)
+ call VerifyScreenDump(buf, 'Test_crash_01', {})
+ exe buf .. "bw!"
+
+endfunc
+
+func Test_crash2()
+ " The following used to crash Vim
+ let opts = #{wait_for_ruler: 0, rows: 20}
+ let args = ' -u NONE -i NONE -n -e -s -S '
+ let buf = RunVimInTerminal(args .. ' crash/vim_regsub_both', opts)
+ call VerifyScreenDump(buf, 'Test_crash_01', {})
+ exe buf .. "bw!"
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
diff --git a/test/old/testdir/test_functions.vim b/test/old/testdir/test_functions.vim
index 37541c4af3..eff4e36f34 100644
--- a/test/old/testdir/test_functions.vim
+++ b/test/old/testdir/test_functions.vim
@@ -3273,4 +3273,9 @@ func Test_string_reverse()
let &encoding = save_enc
endfunc
+func Test_fullcommand()
+ " this used to crash vim
+ call assert_equal('', fullcommand(10))
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/test/old/testdir/test_selectmode.vim b/test/old/testdir/test_selectmode.vim
index dd341d99d1..59a1deba65 100644
--- a/test/old/testdir/test_selectmode.vim
+++ b/test/old/testdir/test_selectmode.vim
@@ -312,4 +312,15 @@ func Test_selectmode_register()
bw!
endfunc
+func Test_ins_ctrl_o_in_insert_mode_resets_selectmode()
+ new
+ " ctrl-o in insert mode resets restart_VIsual_select
+ call setline(1, 'abcdef')
+ call cursor(1, 1)
+ exe "norm! \<c-v>\<c-g>\<c-o>c\<c-o>\<c-v>\<right>\<right>IABC"
+ call assert_equal('ABCbcdef', getline(1))
+
+ bwipe!
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab