changeset 19969:b07672d13ff9 v8.2.0540

patch 8.2.0540: regexp and other code not tested Commit: https://github.com/vim/vim/commit/004a6781b3cf15ca5dd632c38cc09bb3b253d1f8 Author: Bram Moolenaar <Bram@vim.org> Date: Sat Apr 11 17:09:31 2020 +0200 patch 8.2.0540: regexp and other code not tested Problem: Regexp and other code not tested. Solution: Add more tests. (Yegappan Lakshmanan, closes https://github.com/vim/vim/issues/5904)
author Bram Moolenaar <Bram@vim.org>
date Sat, 11 Apr 2020 17:15:06 +0200
parents 1908e92b02fd
children 881b067e7cc6
files src/testdir/test_backspace_opt.vim src/testdir/test_expr.vim src/testdir/test_increment.vim src/testdir/test_normal.vim src/testdir/test_options.vim src/testdir/test_regexp_latin.vim src/testdir/test_search.vim src/testdir/test_substitute.vim src/testdir/test_terminal.vim src/testdir/test_virtualedit.vim src/version.c
diffstat 11 files changed, 311 insertions(+), 95 deletions(-) [+]
line wrap: on
line diff
--- a/src/testdir/test_backspace_opt.vim
+++ b/src/testdir/test_backspace_opt.vim
@@ -1,6 +1,6 @@
 " Tests for 'backspace' settings
 
-:func Exec(expr)
+func Exec(expr)
   let str=''
   try
     exec a:expr
@@ -8,7 +8,7 @@
     let str=v:exception
   endtry
   return str
-:endfunc
+endfunc
 
 func Test_backspace_option()
   set backspace=
--- a/src/testdir/test_expr.vim
+++ b/src/testdir/test_expr.vim
@@ -429,52 +429,6 @@ function Test_printf_spec_b()
   call assert_equal("1111111111111111111111111111111111111111111111111111111111111111", printf('%b', -1))
 endfunc
 
-func Test_substitute_expr()
-  let g:val = 'XXX'
-  call assert_equal('XXX', substitute('yyy', 'y*', '\=g:val', ''))
-  call assert_equal('XXX', substitute('yyy', 'y*', {-> g:val}, ''))
-  call assert_equal("-\u1b \uf2-", substitute("-%1b %f2-", '%\(\x\x\)',
-			   \ '\=nr2char("0x" . submatch(1))', 'g'))
-  call assert_equal("-\u1b \uf2-", substitute("-%1b %f2-", '%\(\x\x\)',
-			   \ {-> nr2char("0x" . submatch(1))}, 'g'))
-
-  call assert_equal('231', substitute('123', '\(.\)\(.\)\(.\)',
-	\ {-> submatch(2) . submatch(3) . submatch(1)}, ''))
-
-  func Recurse()
-    return substitute('yyy', 'y\(.\)y', {-> submatch(1)}, '')
-  endfunc
-  " recursive call works
-  call assert_equal('-y-x-', substitute('xxx', 'x\(.\)x', {-> '-' . Recurse() . '-' . submatch(1) . '-'}, ''))
-
-  call assert_fails("let s=submatch([])", 'E745:')
-  call assert_fails("let s=submatch(2, [])", 'E745:')
-endfunc
-
-func Test_invalid_submatch()
-  " This was causing invalid memory access in Vim-7.4.2232 and older
-  call assert_fails("call substitute('x', '.', {-> submatch(10)}, '')", 'E935:')
-endfunc
-
-func Test_substitute_expr_arg()
-  call assert_equal('123456789-123456789=', substitute('123456789',
-	\ '\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)',
-	\ {m -> m[0] . '-' . m[1] . m[2] . m[3] . m[4] . m[5] . m[6] . m[7] . m[8] . m[9] . '='}, ''))
-
-  call assert_equal('123456-123456=789', substitute('123456789',
-	\ '\(.\)\(.\)\(.\)\(a*\)\(n*\)\(.\)\(.\)\(.\)\(x*\)',
-	\ {m -> m[0] . '-' . m[1] . m[2] . m[3] . m[4] . m[5] . m[6] . m[7] . m[8] . m[9] . '='}, ''))
-
-  call assert_equal('123456789-123456789x=', substitute('123456789',
-	\ '\(.\)\(.\)\(.*\)',
-	\ {m -> m[0] . '-' . m[1] . m[2] . m[3] . 'x' . m[4] . m[5] . m[6] . m[7] . m[8] . m[9] . '='}, ''))
-
-  call assert_fails("call substitute('xxx', '.', {m -> string(add(m, 'x'))}, '')", 'E742:')
-  call assert_fails("call substitute('xxx', '.', {m -> string(insert(m, 'x'))}, '')", 'E742:')
-  call assert_fails("call substitute('xxx', '.', {m -> string(extend(m, ['x']))}, '')", 'E742:')
-  call assert_fails("call substitute('xxx', '.', {m -> string(remove(m, 1))}, '')", 'E742:')
-endfunc
-
 func Test_function_with_funcref()
   let s:f = function('type')
   let s:fref = function(s:f)
--- a/src/testdir/test_increment.vim
+++ b/src/testdir/test_increment.vim
@@ -475,6 +475,10 @@ func Test_visual_increment_20()
   exec "norm! \<C-A>"
   call assert_equal(["b"], getline(1, '$'))
   call assert_equal([0, 1, 1, 0], getpos('.'))
+  " decrement a and A and increment z and Z
+  call setline(1, ['a', 'A', 'z', 'Z'])
+  exe "normal 1G\<C-X>2G\<C-X>3G\<C-A>4G\<C-A>"
+  call assert_equal(['a', 'A', 'z', 'Z'], getline(1, '$'))
 endfunc
 
 " 21) block-wise increment on part of hexadecimal
@@ -565,12 +569,14 @@ endfunc
 "   1) <ctrl-a>
 " 0b11111111111111111111111111111111
 func Test_visual_increment_26()
-  set nrformats+=alpha
+  set nrformats+=bin
   call setline(1, ["0b11111111111111111111111111111110"])
   exec "norm! \<C-V>$\<C-A>"
   call assert_equal(["0b11111111111111111111111111111111"], getline(1, '$'))
   call assert_equal([0, 1, 1, 0], getpos('.'))
-  set nrformats-=alpha
+  exec "norm! \<C-V>$\<C-X>"
+  call assert_equal(["0b11111111111111111111111111111110"], getline(1, '$'))
+  set nrformats-=bin
 endfunc
 
 " 27) increment with 'rightreft', if supported
@@ -771,7 +777,6 @@ func Test_normal_increment_03()
 endfunc
 
 func Test_increment_empty_line()
-  new
   call setline(1, ['0', '0', '0', '0', '0', '0', ''])
   exe "normal Gvgg\<C-A>"
   call assert_equal(['1', '1', '1', '1', '1', '1', ''], getline(1, 7))
@@ -782,8 +787,13 @@ func Test_increment_empty_line()
   exe "normal! c\<C-A>l"
   exe "normal! c\<C-X>l"
   call assert_equal('one two', getline(1))
+endfunc
 
-  bwipe!
+" Try incrementing/decrementing a non-digit/alpha character
+func Test_increment_special_char()
+  call setline(1, '!')
+  call assert_beeps("normal \<C-A>")
+  call assert_beeps("normal \<C-X>")
 endfunc
 
 " vim: shiftwidth=2 sts=2 expandtab
--- a/src/testdir/test_normal.vim
+++ b/src/testdir/test_normal.vim
@@ -138,16 +138,15 @@ func Test_normal03_join()
   $
   :j 10
   call assert_equal('100', getline('.'))
+  call assert_beeps('normal GVJ')
   " clean up
   bw!
 endfunc
 
+" basic filter test
 func Test_normal04_filter()
-  " basic filter test
   " only test on non windows platform
-  if has('win32')
-    return
-  endif
+  CheckNotMSWindows
   call Setup_NewWindow()
   1
   call feedkeys("!!sed -e 's/^/|    /'\n", 'tx')
@@ -205,12 +204,10 @@ func Test_normal05_formatexpr_setopt()
   set formatexpr=
 endfunc
 
+" basic test for formatprg
 func Test_normal06_formatprg()
-  " basic test for formatprg
   " only test on non windows platform
-  if has('win32')
-    return
-  endif
+  CheckNotMSWindows
 
   " uses sed to number non-empty lines
   call writefile(['#!/bin/sh', 'sed ''/./=''|sed ''/./{', 'N', 's/\n/    /', '}'''], 'Xsed_format.sh')
@@ -223,16 +220,24 @@ func Test_normal06_formatprg()
   set formatprg=./Xsed_format.sh
   norm! gggqG
   call assert_equal(expected, getline(1, '$'))
-  bw!
+  %d
 
-  10new
   call setline(1, text)
   set formatprg=donothing
   setlocal formatprg=./Xsed_format.sh
   norm! gggqG
   call assert_equal(expected, getline(1, '$'))
+  %d
+
+  " Check for the command-line ranges added to 'formatprg'
+  set formatprg=cat
+  call setline(1, ['one', 'two', 'three', 'four', 'five'])
+  call feedkeys('gggqG', 'xt')
+  call assert_equal('.,$!cat', @:)
+  call feedkeys('2Ggq2j', 'xt')
+  call assert_equal('.,.+2!cat', @:)
+
   bw!
-
   " clean up
   set formatprg=
   setlocal formatprg=
@@ -246,18 +251,16 @@ func Test_normal07_internalfmt()
   10new
   call setline(1, list)
   set tw=12
-  norm! gggqG
+  norm! ggVGgq
   call assert_equal(['1    2    3', '4    5    6', '7    8    9', '10    11    '], getline(1, '$'))
   " clean up
   set tw=0
   bw!
 endfunc
 
+" basic tests for foldopen/folddelete
 func Test_normal08_fold()
-  " basic tests for foldopen/folddelete
-  if !has("folding")
-    return
-  endif
+  CheckFeature folding
   call Setup_NewWindow()
   50
   setl foldenable fdm=marker
@@ -757,11 +760,9 @@ func Test_scroll_cmds()
   close!
 endfunc
 
+" basic tests for foldopen/folddelete
 func Test_normal18_z_fold()
-  " basic tests for foldopen/folddelete
-  if !has("folding")
-    return
-  endif
+  CheckFeature folding
   call Setup_NewWindow()
   50
   setl foldenable fdm=marker foldlevel=5
@@ -1132,10 +1133,8 @@ func Test_normal18_z_fold()
 endfunc
 
 func Test_normal20_exmode()
-  if !has("unix")
-    " Reading from redirected file doesn't work on MS-Windows
-    return
-  endif
+  " Reading from redirected file doesn't work on MS-Windows
+  CheckNotMSWindows
   call writefile(['1a', 'foo', 'bar', '.', 'w! Xfile2', 'q!'], 'Xscript')
   call writefile(['1', '2'], 'Xfile')
   call system(GetVimCommand() .. ' -e -s < Xscript Xfile')
@@ -1762,6 +1761,12 @@ func Test_normal31_r_cmd()
   " r command should fail in operator pending mode
   call assert_beeps('normal! cr')
 
+  " replace a tab character in visual mode
+  %d
+  call setline(1, ["a\tb", "c\td", "e\tf"])
+  normal gglvjjrx
+  call assert_equal(['axx', 'xxx', 'xxf'], getline(1, '$'))
+
   " clean up
   set noautoindent
   bw!
@@ -1786,9 +1791,7 @@ endfunc
 " Test for g`, g;, g,, g&, gv, gk, gj, gJ, g0, g^, g_, gm, g$, gM, g CTRL-G,
 " gi and gI commands
 func Test_normal33_g_cmd2()
-  if !has("jumplist")
-    return
-  endif
+  CheckFeature jumplist
   call Setup_NewWindow()
   " Test for g`
   clearjumps
@@ -2503,9 +2506,8 @@ func Test_normal49_counts()
 endfunc
 
 func Test_normal50_commandline()
-  if !has("timers") || !has("cmdline_hist")
-    return
-  endif
+  CheckFeature timers
+  CheckFeature cmdline_hist
   func! DoTimerWork(id)
     call assert_equal('[Command Line]', bufname(''))
     " should fail, with E11, but does fail with E23?
@@ -2534,9 +2536,7 @@ func Test_normal50_commandline()
 endfunc
 
 func Test_normal51_FileChangedRO()
-  if !has("autocmd")
-    return
-  endif
+  CheckFeature autocmd
   " Don't sleep after the warning message.
   call test_settime(1)
   call writefile(['foo'], 'Xreadonly.log')
@@ -2554,9 +2554,7 @@ func Test_normal51_FileChangedRO()
 endfunc
 
 func Test_normal52_rl()
-  if !has("rightleft")
-    return
-  endif
+  CheckFeature rightleft
   new
   call setline(1, 'abcde fghij klmnopq')
   norm! 1gg$
@@ -2589,9 +2587,7 @@ func Test_normal52_rl()
 endfunc
 
 func Test_normal53_digraph()
-  if !has('digraphs')
-    return
-  endif
+  CheckFeature digraphs
   new
   call setline(1, 'abcdefgh|')
   exe "norm! 1gg0f\<c-k>!!"
@@ -2819,11 +2815,9 @@ Piece of Java
   close!
 endfunc
 
+" Tests for g cmds
 func Test_normal_gdollar_cmd()
-  if !has("jumplist")
-    return
-  endif
-  " Tests for g cmds
+  CheckFeature jumplist
   call Setup_NewWindow()
   " Make long lines that will wrap
   %s/$/\=repeat(' foobar', 10)/
@@ -3028,4 +3022,47 @@ func Test_normal_colon_op()
   close!
 endfunc
 
+" Test for d and D commands
+func Test_normal_delete_cmd()
+  new
+  " D in an empty line
+  call setline(1, '')
+  normal D
+  call assert_equal('', getline(1))
+  " D in an empty line in virtualedit mode
+  set virtualedit=all
+  normal D
+  call assert_equal('', getline(1))
+  set virtualedit&
+  " delete to a readonly register
+  call setline(1, ['abcd'])
+  call assert_beeps('normal ":d2l')
+  close!
+endfunc
+
+" Test for the 'E' flag in 'cpo' with yank, change, delete, etc. operators
+func Test_empty_region_error()
+  new
+  call setline(1, '')
+  set cpo+=E
+  " yank an empty line
+  call assert_beeps('normal "ayl')
+  " change an empty line
+  call assert_beeps('normal lcTa')
+  " delete an empty line
+  call assert_beeps('normal D')
+  call assert_beeps('normal dl')
+  call assert_equal('', getline(1))
+  " change case of an empty line
+  call assert_beeps('normal gul')
+  call assert_beeps('normal gUl')
+  " replace a character
+  call assert_beeps('normal vrx')
+  " increment and decrement
+  call assert_beeps('exe "normal v\<C-A>"')
+  call assert_beeps('exe "normal v\<C-X>"')
+  set cpo-=E
+  close!
+endfunc
+
 " vim: shiftwidth=2 sts=2 expandtab
--- a/src/testdir/test_options.vim
+++ b/src/testdir/test_options.vim
@@ -22,6 +22,21 @@ func Test_whichwrap()
   set whichwrap=h,h,h
   call assert_equal('h', &whichwrap)
 
+  " For compatibility with Vim 3.0 and before, number values are also
+  " supported for 'whichwrap'
+  set whichwrap=1
+  call assert_equal('b', &whichwrap)
+  set whichwrap=2
+  call assert_equal('s', &whichwrap)
+  set whichwrap=4
+  call assert_equal('h,l', &whichwrap)
+  set whichwrap=8
+  call assert_equal('<,>', &whichwrap)
+  set whichwrap=16
+  call assert_equal('[,]', &whichwrap)
+  set whichwrap=31
+  call assert_equal('b,s,h,l,<,>,[,]', &whichwrap)
+
   set whichwrap&
 endfunc
 
@@ -82,6 +97,13 @@ func Test_options_command()
 
   " close option-window
   close
+
+  " Open the options window browse
+  if has('browse')
+    browse set
+    call assert_equal('option-window', bufname(''))
+    close
+  endif
 endfunc
 
 func Test_path_keep_commas()
@@ -308,6 +330,17 @@ func Test_set_errors()
   call assert_fails('set winminwidth=10 winwidth=9', 'E592:')
   call assert_fails("set showbreak=\x01", 'E595:')
   call assert_fails('set t_foo=', 'E846:')
+  call assert_fails('set tabstop??', 'E488:')
+  call assert_fails('set wrapscan!!', 'E488:')
+  call assert_fails('set tabstop&&', 'E488:')
+  call assert_fails('set wrapscan<<', 'E488:')
+  call assert_fails('set wrapscan=1', 'E474:')
+  call assert_fails('set autoindent@', 'E488:')
+  call assert_fails('set wildchar=<abc>', 'E474:')
+  call assert_fails('set cmdheight=1a', 'E521:')
+  if has('python') && has('python3')
+    call assert_fails('set pyxversion=6', 'E474:')
+  endif
 endfunc
 
 func CheckWasSet(name)
@@ -730,4 +763,72 @@ func Test_debug_option()
   set debug&
 endfunc
 
+" Test for the default CDPATH option
+func Test_opt_default_cdpath()
+  CheckFeature file_in_path
+  let after =<< trim [CODE]
+    call assert_equal(',/path/to/dir1,/path/to/dir2', &cdpath)
+    call writefile(v:errors, 'Xtestout')
+    qall
+  [CODE]
+  if has('unix')
+    let $CDPATH='/path/to/dir1:/path/to/dir2'
+  else
+    let $CDPATH='/path/to/dir1;/path/to/dir2'
+  endif
+  if RunVim([], after, '')
+    call assert_equal([], readfile('Xtestout'))
+    call delete('Xtestout')
+  endif
+endfunc
+
+" Test for setting keycodes using set
+func Test_opt_set_keycode()
+  call assert_fails('set <t_k1=l', 'E474:')
+  call assert_fails('set <Home=l', 'E474:')
+  set <t_k9>=abcd
+  call assert_equal('abcd', &t_k9)
+  set <t_k9>&
+  set <F9>=xyz
+  call assert_equal('xyz', &t_k9)
+  set <t_k9>&
+endfunc
+
+" Test for changing options in a sandbox
+func Test_opt_sandbox()
+  for opt in ['backupdir', 'cdpath', 'exrc']
+    call assert_fails('sandbox set ' .. opt .. '?', 'E48:')
+  endfor
+endfunc
+
+" Test for setting an option with local value to global value
+func Test_opt_local_to_global()
+  setglobal equalprg=gprg
+  setlocal equalprg=lprg
+  call assert_equal('gprg', &g:equalprg)
+  call assert_equal('lprg', &l:equalprg)
+  call assert_equal('lprg', &equalprg)
+  set equalprg<
+  call assert_equal('', &l:equalprg)
+  call assert_equal('gprg', &equalprg)
+  setglobal equalprg=gnewprg
+  setlocal equalprg=lnewprg
+  setlocal equalprg<
+  call assert_equal('gnewprg', &l:equalprg)
+  call assert_equal('gnewprg', &equalprg)
+  set equalprg&
+endfunc
+
+" Test for incrementing, decrementing and multiplying a number option value
+func Test_opt_num_op()
+  set shiftwidth=4
+  set sw+=2
+  call assert_equal(6, &sw)
+  set sw-=2
+  call assert_equal(4, &sw)
+  set sw^=2
+  call assert_equal(8, &sw)
+  set shiftwidth&
+endfunc
+
 " vim: shiftwidth=2 sts=2 expandtab
--- a/src/testdir/test_regexp_latin.vim
+++ b/src/testdir/test_regexp_latin.vim
@@ -171,6 +171,10 @@ func Test_regexp_single_line_pat()
   call add(tl, [2, 'c*', 'abdef', ''])
   call add(tl, [2, 'bc\+', 'abccccdef', 'bcccc'])
   call add(tl, [2, 'bc\+', 'abdef']) " no match
+  " match escape character in a string
+  call add(tl, [2, '.\e.', "one\<Esc>two", "e\<Esc>t"])
+  " match backspace character in a string
+  call add(tl, [2, '.\b.', "one\<C-H>two", "e\<C-H>t"])
   " match newline character in a string
   call add(tl, [2, 'o\nb', "foo\nbar", "o\nb"])
 
@@ -913,6 +917,8 @@ func Test_regexp_error()
   call assert_fails("call matchlist('x x', '\\%#=2 \\zs*')", 'E888:')
   call assert_fails("call matchlist('x x', '\\%#=2 \\ze*')", 'E888:')
   call assert_fails('exe "normal /\\%#=1\\%[x\\%[x]]\<CR>"', 'E369:')
+  call assert_fails("call matchstr('abcd', '\\%o841\\%o142')", 'E678:')
+  call assert_equal('', matchstr('abcd', '\%o181\%o142'))
 endfunc
 
 " Test for using the last substitute string pattern (~)
--- a/src/testdir/test_search.vim
+++ b/src/testdir/test_search.vim
@@ -1593,4 +1593,23 @@ func Test_invalid_regexp()
   call assert_fails("call search('\\%#=3ab')", 'E864:')
 endfunc
 
+" Test for searching a very complex pattern in a string. Should switch the
+" regexp engine from NFA to the old engine.
+func Test_regexp_switch_engine()
+  let l = readfile('samples/re.freeze.txt')
+  let v = substitute(l[4], '..\@<!', '', '')
+  call assert_equal(l[4], v)
+endfunc
+
+" Test for the \%V atom to search within visually selected text
+func Test_search_in_visual_area()
+  new
+  call setline(1, ['foo bar1', 'foo bar2', 'foo bar3', 'foo bar4'])
+  exe "normal 2GVjo/\\%Vbar\<CR>\<Esc>"
+  call assert_equal([2, 5], [line('.'), col('.')])
+  exe "normal 2GVj$?\\%Vbar\<CR>\<Esc>"
+  call assert_equal([3, 5], [line('.'), col('.')])
+  close!
+endfunc
+
 " vim: shiftwidth=2 sts=2 expandtab
--- a/src/testdir/test_substitute.vim
+++ b/src/testdir/test_substitute.vim
@@ -282,6 +282,9 @@ func Test_sub_replace_1()
   call assert_equal("x\<C-M>x", substitute('xXx', 'X', "\r", ''))
   call assert_equal("YyyY", substitute('Y', 'Y', '\L\uyYy\l\EY', ''))
   call assert_equal("zZZz", substitute('Z', 'Z', '\U\lZzZ\u\Ez', ''))
+  " \v or \V after $
+  call assert_equal('abxx', substitute('abcd', 'xy$\v|cd$', 'xx', ''))
+  call assert_equal('abxx', substitute('abcd', 'xy$\V\|cd\$', 'xx', ''))
 endfunc
 
 func Test_sub_replace_2()
@@ -842,6 +845,78 @@ endfunc
 
 func Test_substitute()
   call assert_equal('a1a2a3a', substitute('123', '\zs', 'a', 'g'))
+  " Substitute with special keys
+  call assert_equal("a\<End>c", substitute('abc', "a.c", "a\<End>c", ''))
+endfunc
+
+func Test_substitute_expr()
+  let g:val = 'XXX'
+  call assert_equal('XXX', substitute('yyy', 'y*', '\=g:val', ''))
+  call assert_equal('XXX', substitute('yyy', 'y*', {-> g:val}, ''))
+  call assert_equal("-\u1b \uf2-", substitute("-%1b %f2-", '%\(\x\x\)',
+			   \ '\=nr2char("0x" . submatch(1))', 'g'))
+  call assert_equal("-\u1b \uf2-", substitute("-%1b %f2-", '%\(\x\x\)',
+			   \ {-> nr2char("0x" . submatch(1))}, 'g'))
+
+  call assert_equal('231', substitute('123', '\(.\)\(.\)\(.\)',
+	\ {-> submatch(2) . submatch(3) . submatch(1)}, ''))
+
+  func Recurse()
+    return substitute('yyy', 'y\(.\)y', {-> submatch(1)}, '')
+  endfunc
+  " recursive call works
+  call assert_equal('-y-x-', substitute('xxx', 'x\(.\)x', {-> '-' . Recurse() . '-' . submatch(1) . '-'}, ''))
+
+  call assert_fails("let s=submatch([])", 'E745:')
+  call assert_fails("let s=submatch(2, [])", 'E745:')
+endfunc
+
+func Test_invalid_submatch()
+  " This was causing invalid memory access in Vim-7.4.2232 and older
+  call assert_fails("call substitute('x', '.', {-> submatch(10)}, '')", 'E935:')
+  call assert_fails('eval submatch(-1)', 'E935:')
+  call assert_equal('', submatch(0))
+  call assert_equal('', submatch(1))
+  call assert_equal([], submatch(0, 1))
+  call assert_equal([], submatch(1, 1))
+endfunc
+
+func Test_substitute_expr_arg()
+  call assert_equal('123456789-123456789=', substitute('123456789',
+	\ '\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)',
+	\ {m -> m[0] . '-' . m[1] . m[2] . m[3] . m[4] . m[5] . m[6] . m[7] . m[8] . m[9] . '='}, ''))
+
+  call assert_equal('123456-123456=789', substitute('123456789',
+	\ '\(.\)\(.\)\(.\)\(a*\)\(n*\)\(.\)\(.\)\(.\)\(x*\)',
+	\ {m -> m[0] . '-' . m[1] . m[2] . m[3] . m[4] . m[5] . m[6] . m[7] . m[8] . m[9] . '='}, ''))
+
+  call assert_equal('123456789-123456789x=', substitute('123456789',
+	\ '\(.\)\(.\)\(.*\)',
+	\ {m -> m[0] . '-' . m[1] . m[2] . m[3] . 'x' . m[4] . m[5] . m[6] . m[7] . m[8] . m[9] . '='}, ''))
+
+  call assert_fails("call substitute('xxx', '.', {m -> string(add(m, 'x'))}, '')", 'E742:')
+  call assert_fails("call substitute('xxx', '.', {m -> string(insert(m, 'x'))}, '')", 'E742:')
+  call assert_fails("call substitute('xxx', '.', {m -> string(extend(m, ['x']))}, '')", 'E742:')
+  call assert_fails("call substitute('xxx', '.', {m -> string(remove(m, 1))}, '')", 'E742:')
+endfunc
+
+" Test for using a function to supply the substitute string
+func Test_substitute_using_func()
+  func Xfunc()
+    return '1234'
+  endfunc
+  call assert_equal('a1234f', substitute('abcdef', 'b..e',
+        \ function("Xfunc"), ''))
+  delfunc Xfunc
+endfunc
+
+" Test for using submatch() with a multiline match
+func Test_substitute_multiline_submatch()
+  new
+  call setline(1, ['line1', 'line2', 'line3', 'line4'])
+  %s/^line1\(\_.\+\)line4$/\=submatch(1)/
+  call assert_equal(['', 'line2', 'line3', ''], getline(1, '$'))
+  close!
 endfunc
 
 " vim: shiftwidth=2 sts=2 expandtab
--- a/src/testdir/test_terminal.vim
+++ b/src/testdir/test_terminal.vim
@@ -42,6 +42,7 @@ func Test_terminal_basic()
   call assert_match('%aR[^\n]*running]', execute('ls R'))
   call assert_notmatch('%[^\n]*running]', execute('ls F'))
   call assert_notmatch('%[^\n]*running]', execute('ls ?'))
+  call assert_fails('set modifiable', 'E946:')
 
   call StopShellInTerminal(buf)
   call TermWait(buf)
--- a/src/testdir/test_virtualedit.vim
+++ b/src/testdir/test_virtualedit.vim
@@ -346,4 +346,15 @@ func Test_yank_paste_small_del_reg()
   set virtualedit=
 endfunc
 
+" Test for delete that breaks a tab into spaces
+func Test_delete_break_tab()
+  new
+  call setline(1, "one\ttwo")
+  set virtualedit=all
+  normal v3ld
+  call assert_equal('    two', getline(1))
+  set virtualedit&
+  close!
+endfunc
+
 " vim: shiftwidth=2 sts=2 expandtab
--- a/src/version.c
+++ b/src/version.c
@@ -739,6 +739,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    540,
+/**/
     539,
 /**/
     538,