diff options
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 |