neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

test_substitute.vim (46232B)


      1 " Tests for the substitute (:s) command
      2 
      3 source shared.vim
      4 source check.vim
      5 source screendump.vim
      6 
      7 " NOTE: This needs to be the first test to be
      8 "       run in the file, since it depends on
      9 "       that the previous substitution atom
     10 "       was not yet set.
     11 "
     12 " recursive call of :s and sub-replace special
     13 " (did cause heap-use-after free in < v9.0.2121)
     14 func Test_aaaa_substitute_expr_recursive_special()
     15  func R()
     16    " FIXME: leaving out the 'n' flag leaks memory, why?
     17    %s/./\='.'/gn
     18  endfunc
     19  new Xfoobar_UAF
     20  put ='abcdef'
     21  let bufnr = bufnr('%')
     22  try
     23    silent! :s/./~\=R()/0
     24    "call assert_fails(':s/./~\=R()/0', 'E939:')
     25    let @/='.'
     26    ~g
     27  catch /^Vim\%((\a\+)\)\=:E565:/
     28  endtry
     29  delfunc R
     30  exe bufnr .. "bw!"
     31 endfunc
     32 
     33 func Test_multiline_subst()
     34  enew!
     35  call append(0, ["1 aa",
     36       \ "bb",
     37       \ "cc",
     38       \ "2 dd",
     39       \ "ee",
     40       \ "3 ef",
     41       \ "gh",
     42       \ "4 ij",
     43       \ "5 a8",
     44       \ "8b c9",
     45       \ "9d",
     46       \ "6 e7",
     47       \ "77f",
     48       \ "xxxxx"])
     49 
     50  1
     51  " test if replacing a line break works with a back reference
     52  /^1/,/^2/s/\n\(.\)/ \1/
     53  " test if inserting a line break works with a back reference
     54  /^3/,/^4/s/\(.\)$/\r\1/
     55  " test if replacing a line break with another line break works
     56  /^5/,/^6/s/\(\_d\{3}\)/x\1x/
     57  call assert_equal('1 aa bb cc 2 dd ee', getline(1))
     58  call assert_equal('3 e', getline(2))
     59  call assert_equal('f', getline(3))
     60  call assert_equal('g', getline(4))
     61  call assert_equal('h', getline(5))
     62  call assert_equal('4 i', getline(6))
     63  call assert_equal('j', getline(7))
     64  call assert_equal('5 ax8', getline(8))
     65  call assert_equal('8xb cx9', getline(9))
     66  call assert_equal('9xd', getline(10))
     67  call assert_equal('6 ex7', getline(11))
     68  call assert_equal('7x7f', getline(12))
     69  call assert_equal('xxxxx', getline(13))
     70  enew!
     71 endfunc
     72 
     73 func Test_substitute_variants()
     74  " Validate that all the 2-/3-letter variants which embed the flags into the
     75  " command name actually work.
     76  enew!
     77  let ln = 'Testing string'
     78  let variants = [
     79 \ { 'cmd': ':s/Test/test/c', 'exp': 'testing string', 'prompt': 'y' },
     80 \ { 'cmd': ':s/foo/bar/ce', 'exp': ln },
     81 \ { 'cmd': ':s/t/r/cg', 'exp': 'Tesring srring', 'prompt': 'a' },
     82 \ { 'cmd': ':s/t/r/ci', 'exp': 'resting string', 'prompt': 'y' },
     83 \ { 'cmd': ':s/t/r/cI', 'exp': 'Tesring string', 'prompt': 'y' },
     84 \ { 'cmd': ':s/t/r/c', 'exp': 'Testing string', 'prompt': 'n' },
     85 \ { 'cmd': ':s/t/r/cn', 'exp': ln },
     86 \ { 'cmd': ':s/t/r/cp', 'exp': 'Tesring string', 'prompt': 'y' },
     87 \ { 'cmd': ':s/t/r/cl', 'exp': 'Tesring string', 'prompt': 'y' },
     88 \ { 'cmd': ':s/t/r/gc', 'exp': 'Tesring srring', 'prompt': 'a' },
     89 \ { 'cmd': ':s/i/I/gc', 'exp': 'TestIng string', 'prompt': 'l' },
     90 \ { 'cmd': ':s/foo/bar/ge', 'exp': ln },
     91 \ { 'cmd': ':s/t/r/g', 'exp': 'Tesring srring' },
     92 \ { 'cmd': ':s/t/r/gi', 'exp': 'resring srring' },
     93 \ { 'cmd': ':s/t/r/gI', 'exp': 'Tesring srring' },
     94 \ { 'cmd': ':s/t/r/gn', 'exp': ln },
     95 \ { 'cmd': ':s/t/r/gp', 'exp': 'Tesring srring' },
     96 \ { 'cmd': ':s/t/r/gl', 'exp': 'Tesring srring' },
     97 \ { 'cmd': ':s//r/gr', 'exp': 'Testr strr' },
     98 \ { 'cmd': ':s/t/r/ic', 'exp': 'resting string', 'prompt': 'y' },
     99 \ { 'cmd': ':s/foo/bar/ie', 'exp': ln },
    100 \ { 'cmd': ':s/t/r/i', 'exp': 'resting string' },
    101 \ { 'cmd': ':s/t/r/iI', 'exp': 'Tesring string' },
    102 \ { 'cmd': ':s/t/r/in', 'exp': ln },
    103 \ { 'cmd': ':s/t/r/ip', 'exp': 'resting string' },
    104 \ { 'cmd': ':s//r/ir', 'exp': 'Testr string' },
    105 \ { 'cmd': ':s/t/r/Ic', 'exp': 'Tesring string', 'prompt': 'y' },
    106 \ { 'cmd': ':s/foo/bar/Ie', 'exp': ln },
    107 \ { 'cmd': ':s/t/r/Ig', 'exp': 'Tesring srring' },
    108 \ { 'cmd': ':s/t/r/Ii', 'exp': 'resting string' },
    109 \ { 'cmd': ':s/t/r/I', 'exp': 'Tesring string' },
    110 \ { 'cmd': ':s/t/r/Ip', 'exp': 'Tesring string' },
    111 \ { 'cmd': ':s/t/r/Il', 'exp': 'Tesring string' },
    112 \ { 'cmd': ':s//r/Ir', 'exp': 'Testr string' },
    113 \ { 'cmd': ':s//r/rc', 'exp': 'Testr string', 'prompt': 'y' },
    114 \ { 'cmd': ':s//r/rg', 'exp': 'Testr strr' },
    115 \ { 'cmd': ':s//r/ri', 'exp': 'Testr string' },
    116 \ { 'cmd': ':s//r/rI', 'exp': 'Testr string' },
    117 \ { 'cmd': ':s//r/rn', 'exp': 'Testing string' },
    118 \ { 'cmd': ':s//r/rp', 'exp': 'Testr string' },
    119 \ { 'cmd': ':s//r/rl', 'exp': 'Testr string' },
    120 \ { 'cmd': ':s//r/r', 'exp': 'Testr string' },
    121 \ { 'cmd': ':s/i/I/gc', 'exp': 'Testing string', 'prompt': 'q' },
    122 \]
    123 
    124  for var in variants
    125    for run in [1, 2]
    126      let cmd = var.cmd
    127      if run == 2 && cmd =~ "/.*/.*/."
    128 " Change  :s/from/to/{flags}  to  :s{flags}
    129 let cmd = substitute(cmd, '/.*/', '', '')
    130      endif
    131      call setline(1, [ln])
    132      let msg = printf('using "%s"', cmd)
    133      let @/='ing'
    134      let v:errmsg = ''
    135      call feedkeys(cmd . "\<CR>" . get(var, 'prompt', ''), 'ntx')
    136      " No error should exist (matters for testing e flag)
    137      call assert_equal('', v:errmsg, msg)
    138      call assert_equal(var.exp, getline('.'), msg)
    139    endfor
    140  endfor
    141 endfunc
    142 
    143 " Test the l, p, # flags.
    144 func Test_substitute_flags_lp()
    145  new
    146  call setline(1, "abc\tdef\<C-h>ghi")
    147 
    148  let a = execute('s/a/a/p')
    149  call assert_equal("\nabc     def^Hghi", a)
    150 
    151  let a = execute('s/a/a/l')
    152  call assert_equal("\nabc^Idef^Hghi$", a)
    153 
    154  let a = execute('s/a/a/#')
    155  call assert_equal("\n  1 abc     def^Hghi", a)
    156 
    157  let a = execute('s/a/a/p#')
    158  call assert_equal("\n  1 abc     def^Hghi", a)
    159 
    160  let a = execute('s/a/a/l#')
    161  call assert_equal("\n  1 abc^Idef^Hghi$", a)
    162 
    163  let a = execute('s/a/a/')
    164  call assert_equal("", a)
    165 
    166  bwipe!
    167 endfunc
    168 
    169 func Test_substitute_repeat()
    170  " This caused an invalid memory access.
    171  split Xfile
    172  s/^/x
    173  call feedkeys("Qsc\<CR>y", 'tx')
    174  bwipe!
    175 endfunc
    176 
    177 " Test :s with ? as delimiter.
    178 func Test_substitute_question_delimiter()
    179  new
    180  call setline(1, '??:??')
    181  %s?\?\??!!?g
    182  call assert_equal('!!:!!', getline(1))
    183  bwipe!
    184 endfunc
    185 
    186 " Test %s/\n// which is implemented as a special case to use a
    187 " more efficient join rather than doing a regular substitution.
    188 func Test_substitute_join()
    189  new
    190 
    191  call setline(1, ["foo\tbar", "bar\<C-H>foo"])
    192  let a = execute('%s/\n//')
    193  call assert_equal("", a)
    194  call assert_equal(["foo\tbarbar\<C-H>foo"], getline(1, '$'))
    195  call assert_equal('\n', histget("search", -1))
    196 
    197  call setline(1, ["foo\tbar", "bar\<C-H>foo"])
    198  let a = execute('%s/\n//g')
    199  call assert_equal("", a)
    200  call assert_equal(["foo\tbarbar\<C-H>foo"], getline(1, '$'))
    201  call assert_equal('\n', histget("search", -1))
    202 
    203  call setline(1, ["foo\tbar", "bar\<C-H>foo"])
    204  let a = execute('%s/\n//p')
    205  call assert_equal("\nfoo     barbar^Hfoo", a)
    206  call assert_equal(["foo\tbarbar\<C-H>foo"], getline(1, '$'))
    207  call assert_equal('\n', histget("search", -1))
    208 
    209  call setline(1, ["foo\tbar", "bar\<C-H>foo"])
    210  let a = execute('%s/\n//l')
    211  call assert_equal("\nfoo^Ibarbar^Hfoo$", a)
    212  call assert_equal(["foo\tbarbar\<C-H>foo"], getline(1, '$'))
    213  call assert_equal('\n', histget("search", -1))
    214 
    215  call setline(1, ["foo\tbar", "bar\<C-H>foo"])
    216  let a = execute('%s/\n//#')
    217  call assert_equal("\n  1 foo     barbar^Hfoo", a)
    218  call assert_equal(["foo\tbarbar\<C-H>foo"], getline(1, '$'))
    219  call assert_equal('\n', histget("search", -1))
    220 
    221  call setline(1, ['foo', 'bar', 'baz', 'qux'])
    222  call execute('1,2s/\n//')
    223  call assert_equal(['foobarbaz', 'qux'], getline(1, '$'))
    224 
    225  bwipe!
    226 endfunc
    227 
    228 func Test_substitute_count()
    229  new
    230  call setline(1, ['foo foo', 'foo foo', 'foo foo', 'foo foo', 'foo foo'])
    231  2
    232 
    233  s/foo/bar/3
    234  call assert_equal(['foo foo', 'bar foo', 'bar foo', 'bar foo', 'foo foo'],
    235  \                 getline(1, '$'))
    236 
    237  call assert_fails('s/foo/bar/0', 'E939:')
    238 
    239  call setline(1, ['foo foo', 'foo foo', 'foo foo', 'foo foo', 'foo foo'])
    240  2,4s/foo/bar/ 10
    241  call assert_equal(['foo foo', 'foo foo', 'foo foo', 'bar foo', 'bar foo'],
    242        \           getline(1, '$'))
    243 
    244  call assert_fails('s/./b/2147483647', 'E1510:')
    245  bwipe!
    246 endfunc
    247 
    248 " Test substitute 'n' flag (report number of matches, do not substitute).
    249 func Test_substitute_flag_n()
    250  new
    251  let lines = ['foo foo', 'foo foo', 'foo foo', 'foo foo', 'foo foo']
    252  call setline(1, lines)
    253 
    254  call assert_equal("\n3 matches on 3 lines", execute('2,4s/foo/bar/n'))
    255  call assert_equal("\n6 matches on 3 lines", execute('2,4s/foo/bar/gn'))
    256 
    257  " c flag (confirm) should be ignored when using n flag.
    258  call assert_equal("\n3 matches on 3 lines", execute('2,4s/foo/bar/nc'))
    259 
    260  " No substitution should have been done.
    261  call assert_equal(lines, getline(1, '$'))
    262 
    263  %delete _
    264  call setline(1, ['A', 'Bar', 'Baz'])
    265  call assert_equal("\n1 match on 1 line", execute('s/\nB\@=//gn'))
    266 
    267  bwipe!
    268 endfunc
    269 
    270 func Test_substitute_errors()
    271  new
    272  call setline(1, 'foobar')
    273 
    274  call assert_fails('s/FOO/bar/', 'E486:')
    275  call assert_fails('s/foo/bar/@', 'E488:')
    276  call assert_fails('s/\(/bar/', 'E54:')
    277  call assert_fails('s afooabara', 'E146:')
    278  call assert_fails('s\\a', 'E10:')
    279 
    280  setl nomodifiable
    281  call assert_fails('s/foo/bar/', 'E21:')
    282 
    283  call assert_fails("let s=substitute([], 'a', 'A', 'g')", 'E730:')
    284  call assert_fails("let s=substitute('abcda', [], 'A', 'g')", 'E730:')
    285  call assert_fails("let s=substitute('abcda', 'a', [], 'g')", 'E730:')
    286  call assert_fails("let s=substitute('abcda', 'a', 'A', [])", 'E730:')
    287  call assert_fails("let s=substitute('abc', '\\%(', 'A', 'g')", 'E53:')
    288 
    289  bwipe!
    290 endfunc
    291 
    292 " Test for *sub-replace-special* and *sub-replace-expression* on substitute().
    293 func Test_sub_replace_1()
    294  " Run the tests with 'magic' on
    295  set magic
    296  set cpo&
    297  call assert_equal('AA', substitute('A', 'A', '&&', ''))
    298  call assert_equal('&', substitute('B', 'B', '\&', ''))
    299  call assert_equal('C123456789987654321', substitute('C123456789', 'C\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)', '\0\9\8\7\6\5\4\3\2\1', ''))
    300  call assert_equal('d', substitute('D', 'D', 'd', ''))
    301  call assert_equal('~', substitute('E', 'E', '~', ''))
    302  call assert_equal('~', substitute('F', 'F', '\~', ''))
    303  call assert_equal('Gg', substitute('G', 'G', '\ugg', ''))
    304  call assert_equal('Hh', substitute('H', 'H', '\Uh\Eh', ''))
    305  call assert_equal('iI', substitute('I', 'I', '\lII', ''))
    306  call assert_equal('jJ', substitute('J', 'J', '\LJ\EJ', ''))
    307  call assert_equal('Kk', substitute('K', 'K', '\Uk\ek', ''))
    308  call assert_equal("l\<C-V>\<C-M>l",
    309 		\ substitute('lLl', 'L', "\<C-V>\<C-M>", ''))
    310  call assert_equal("m\<C-M>m", substitute('mMm', 'M', '\r', ''))
    311  call assert_equal("n\<C-V>\<C-M>n",
    312 		\ substitute('nNn', 'N', "\\\<C-V>\<C-M>", ''))
    313  call assert_equal("o\no", substitute('oOo', 'O', '\n', ''))
    314  call assert_equal("p\<C-H>p", substitute('pPp', 'P', '\b', ''))
    315  call assert_equal("q\tq", substitute('qQq', 'Q', '\t', ''))
    316  call assert_equal('r\r', substitute('rRr', 'R', '\\', ''))
    317  call assert_equal('scs', substitute('sSs', 'S', '\c', ''))
    318  call assert_equal("u\nu", substitute('uUu', 'U', "\n", ''))
    319  call assert_equal("v\<C-H>v", substitute('vVv', 'V', "\b", ''))
    320  call assert_equal("w\\w", substitute('wWw', 'W', "\\", ''))
    321  call assert_equal("x\<C-M>x", substitute('xXx', 'X', "\r", ''))
    322  call assert_equal("YyyY", substitute('Y', 'Y', '\L\uyYy\l\EY', ''))
    323  call assert_equal("zZZz", substitute('Z', 'Z', '\U\lZzZ\u\Ez', ''))
    324  " \v or \V after $
    325  call assert_equal('abxx', substitute('abcd', 'xy$\v|cd$', 'xx', ''))
    326  call assert_equal('abxx', substitute('abcd', 'xy$\V\|cd\$', 'xx', ''))
    327 endfunc
    328 
    329 func Test_sub_replace_2()
    330  " Run the tests with 'magic' off
    331  set nomagic
    332  set cpo&
    333  call assert_equal('AA', substitute('A', 'A', '&&', ''))
    334  call assert_equal('&', substitute('B', 'B', '\&', ''))
    335  call assert_equal('C123456789987654321', substitute('C123456789', 'C\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)', '\0\9\8\7\6\5\4\3\2\1', ''))
    336  call assert_equal('d', substitute('D', 'D', 'd', ''))
    337  call assert_equal('~', substitute('E', 'E', '~', ''))
    338  call assert_equal('~', substitute('F', 'F', '\~', ''))
    339  call assert_equal('Gg', substitute('G', 'G', '\ugg', ''))
    340  call assert_equal('Hh', substitute('H', 'H', '\Uh\Eh', ''))
    341  call assert_equal('iI', substitute('I', 'I', '\lII', ''))
    342  call assert_equal('jJ', substitute('J', 'J', '\LJ\EJ', ''))
    343  call assert_equal('Kk', substitute('K', 'K', '\Uk\ek', ''))
    344  call assert_equal("l\<C-V>\<C-M>l",
    345 		\ substitute('lLl', 'L', "\<C-V>\<C-M>", ''))
    346  call assert_equal("m\<C-M>m", substitute('mMm', 'M', '\r', ''))
    347  call assert_equal("n\<C-V>\<C-M>n",
    348 		\ substitute('nNn', 'N', "\\\<C-V>\<C-M>", ''))
    349  call assert_equal("o\no", substitute('oOo', 'O', '\n', ''))
    350  call assert_equal("p\<C-H>p", substitute('pPp', 'P', '\b', ''))
    351  call assert_equal("q\tq", substitute('qQq', 'Q', '\t', ''))
    352  call assert_equal('r\r', substitute('rRr', 'R', '\\', ''))
    353  call assert_equal('scs', substitute('sSs', 'S', '\c', ''))
    354  call assert_equal("t\<C-M>t", substitute('tTt', 'T', "\r", ''))
    355  call assert_equal("u\nu", substitute('uUu', 'U', "\n", ''))
    356  call assert_equal("v\<C-H>v", substitute('vVv', 'V', "\b", ''))
    357  call assert_equal('w\w', substitute('wWw', 'W', "\\", ''))
    358  call assert_equal('XxxX', substitute('X', 'X', '\L\uxXx\l\EX', ''))
    359  call assert_equal('yYYy', substitute('Y', 'Y', '\U\lYyY\u\Ey', ''))
    360 endfunc
    361 
    362 func Test_sub_replace_3()
    363  set magic&
    364  set cpo&
    365  call assert_equal('a\a', substitute('aAa', 'A', '\="\\"', ''))
    366  call assert_equal('b\\b', substitute('bBb', 'B', '\="\\\\"', ''))
    367  call assert_equal("c\rc", substitute('cCc', 'C', "\\=\"\r\"", ''))
    368  call assert_equal("d\\\rd", substitute('dDd', 'D', "\\=\"\\\\\r\"", ''))
    369  call assert_equal("e\\\\\re", substitute('eEe', 'E', "\\=\"\\\\\\\\\r\"", ''))
    370  call assert_equal('f\rf', substitute('fFf', 'F', '\="\\r"', ''))
    371  call assert_equal('j\nj', substitute('jJj', 'J', '\="\\n"', ''))
    372  call assert_equal("k\<C-M>k", substitute('kKk', 'K', '\="\r"', ''))
    373  call assert_equal("l\nl", substitute('lLl', 'L', '\="\n"', ''))
    374 endfunc
    375 
    376 " Test for submatch() on substitute().
    377 func Test_sub_replace_4()
    378  set magic&
    379  set cpo&
    380  call assert_equal('a\a', substitute('aAa', 'A',
    381 	\ '\=substitute(submatch(0), ".", "\\", "")', ''))
    382  call assert_equal('b\b', substitute('bBb', 'B',
    383 	\ '\=substitute(submatch(0), ".", "\\\\", "")', ''))
    384  call assert_equal("c\<C-V>\<C-M>c", substitute('cCc', 'C', '\=substitute(submatch(0), ".", "\<C-V>\<C-M>", "")', ''))
    385  call assert_equal("d\<C-V>\<C-M>d", substitute('dDd', 'D', '\=substitute(submatch(0), ".", "\\\<C-V>\<C-M>", "")', ''))
    386  call assert_equal("e\\\<C-V>\<C-M>e", substitute('eEe', 'E', '\=substitute(submatch(0), ".", "\\\\\<C-V>\<C-M>", "")', ''))
    387  call assert_equal("f\<C-M>f", substitute('fFf', 'F', '\=substitute(submatch(0), ".", "\\r", "")', ''))
    388  call assert_equal("j\nj", substitute('jJj', 'J', '\=substitute(submatch(0), ".", "\\n", "")', ''))
    389  call assert_equal("k\rk", substitute('kKk', 'K', '\=substitute(submatch(0), ".", "\r", "")', ''))
    390  call assert_equal("l\nl", substitute('lLl', 'L', '\=substitute(submatch(0), ".", "\n", "")', ''))
    391 endfunc
    392 
    393 func Test_sub_replace_5()
    394  set magic&
    395  set cpo&
    396  call assert_equal('A123456789987654321', substitute('A123456789',
    397 	\ 'A\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)',
    398 	\ '\=submatch(0) . submatch(9) . submatch(8) . ' .
    399 	\ 'submatch(7) . submatch(6) . submatch(5) . ' .
    400 	\ 'submatch(4) . submatch(3) . submatch(2) . submatch(1)',
    401 	\ ''))
    402   call assert_equal("[['A123456789'], ['9'], ['8'], ['7'], ['6'], " .
    403 	\ "['5'], ['4'], ['3'], ['2'], ['1']]",
    404 	\ substitute('A123456789',
    405 	\ 'A\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)',
    406 	\ '\=string([submatch(0, 1), submatch(9, 1), ' .
    407 	\ 'submatch(8, 1), 7->submatch(1), submatch(6, 1), ' .
    408 	\ 'submatch(5, 1), submatch(4, 1), submatch(3, 1), ' .
    409 	\ 'submatch(2, 1), submatch(1, 1)])',
    410 	\ ''))
    411 endfunc
    412 
    413 func Test_sub_replace_6()
    414  set magic&
    415  " Nvim: no "/" flag in 'cpoptions'.
    416  " set cpo+=/
    417  call assert_equal('a', substitute('A', 'A', 'a', ''))
    418  call assert_equal('%', substitute('B', 'B', '%', ''))
    419  set cpo-=/
    420  call assert_equal('c', substitute('C', 'C', 'c', ''))
    421  call assert_equal('%', substitute('D', 'D', '%', ''))
    422 endfunc
    423 
    424 func Test_sub_replace_7()
    425  set magic&
    426  set cpo&
    427  call assert_equal('AA', substitute('AA', 'A.', '\=submatch(0)', ''))
    428  call assert_equal("B\nB", substitute("B\nB", 'B.', '\=submatch(0)', ''))
    429  call assert_equal("['B\n']B", substitute("B\nB", 'B.', '\=string(submatch(0, 1))', ''))
    430  call assert_equal('-abab', substitute('-bb', '\zeb', 'a', 'g'))
    431  call assert_equal('c-cbcbc', substitute('-bb', '\ze', 'c', 'g'))
    432 endfunc
    433 
    434 " Test for *:s%* on :substitute.
    435 func Test_sub_replace_8()
    436  new
    437  set magic&
    438  set cpo&
    439  $put =',,X'
    440  s/\(^\|,\)\ze\(,\|X\)/\1N/g
    441  call assert_equal('N,,NX', getline("$"))
    442  $put =',,Y'
    443  let cmd = ':s/\(^\|,\)\ze\(,\|Y\)/\1N/gc'
    444  call feedkeys(cmd . "\<CR>a", "xt")
    445  call assert_equal('N,,NY', getline("$"))
    446  :$put =',,Z'
    447  let cmd = ':s/\(^\|,\)\ze\(,\|Z\)/\1N/gc'
    448  call feedkeys(cmd . "\<CR>yy", "xt")
    449  call assert_equal('N,,NZ', getline("$"))
    450  enew! | close
    451 endfunc
    452 
    453 func Test_sub_replace_9()
    454  new
    455  set magic&
    456  set cpo&
    457  $put ='xxx'
    458  call feedkeys(":s/x/X/gc\<CR>yyq", "xt")
    459  call assert_equal('XXx', getline("$"))
    460  enew! | close
    461 endfunc
    462 
    463 func Test_sub_replace_10()
    464   set magic&
    465   set cpo&
    466   call assert_equal('a1a2a3a', substitute('123', '\zs', 'a', 'g'))
    467   call assert_equal('aaa', substitute('123', '\zs.', 'a', 'g'))
    468   call assert_equal('1a2a3a', substitute('123', '.\zs', 'a', 'g'))
    469   call assert_equal('a1a2a3a', substitute('123', '\ze', 'a', 'g'))
    470   call assert_equal('a1a2a3', substitute('123', '\ze.', 'a', 'g'))
    471   call assert_equal('aaa', substitute('123', '.\ze', 'a', 'g'))
    472   call assert_equal('aa2a3a', substitute('123', '1\|\ze', 'a', 'g'))
    473   call assert_equal('1aaa', substitute('123', '1\zs\|[23]', 'a', 'g'))
    474 endfunc
    475 
    476 func SubReplacer(text, submatches)
    477  return a:text .. a:submatches[0] .. a:text
    478 endfunc
    479 func SubReplacerVar(text, ...)
    480  return a:text .. a:1[0] .. a:text
    481 endfunc
    482 func SubReplacer20(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, t18, t19, submatches)
    483  return a:t3 .. a:submatches[0] .. a:t11
    484 endfunc
    485 
    486 func Test_substitute_partial()
    487  call assert_equal('1foo2foo3', substitute('123', '2', function('SubReplacer', ['foo']), 'g'))
    488  call assert_equal('1foo2foo3', substitute('123', '2', function('SubReplacerVar', ['foo']), 'g'))
    489 
    490  " 19 arguments plus one is just OK
    491  let Replacer = function('SubReplacer20', repeat(['foo'], 19))
    492  call assert_equal('1foo2foo3', substitute('123', '2', Replacer, 'g'))
    493 
    494  " 20 arguments plus one is too many
    495  let Replacer = function('SubReplacer20', repeat(['foo'], 20))
    496  call assert_fails("call substitute('123', '2', Replacer, 'g')", 'E118:')
    497 endfunc
    498 
    499 func Test_substitute_float()
    500  CheckFeature float
    501 
    502  call assert_equal('number 1.23', substitute('number ', '$', { -> 1.23 }, ''))
    503  " vim9 assert_equal('number 1.23', substitute('number ', '$', () => 1.23, ''))
    504 endfunc
    505 
    506 " Tests for *sub-replace-special* and *sub-replace-expression* on :substitute.
    507 
    508 " Execute a list of :substitute command tests
    509 func Run_SubCmd_Tests(tests)
    510  enew!
    511  for t in a:tests
    512    let start = line('.') + 1
    513    let end = start + len(t[2]) - 1
    514    exe "normal o" . t[0]
    515    call cursor(start, 1)
    516    exe t[1]
    517    call assert_equal(t[2], getline(start, end), t[1])
    518  endfor
    519  enew!
    520 endfunc
    521 
    522 func Test_sub_cmd_1()
    523  set magic
    524  set cpo&
    525 
    526  " List entry format: [input, cmd, output]
    527  let tests = [['A', 's/A/&&/', ['AA']],
    528       \ ['B', 's/B/\&/', ['&']],
    529       \ ['C123456789', 's/C\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)/\0\9\8\7\6\5\4\3\2\1/', ['C123456789987654321']],
    530       \ ['D', 's/D/d/', ['d']],
    531       \ ['E', 's/E/~/', ['d']],
    532       \ ['F', 's/F/\~/', ['~']],
    533       \ ['G', 's/G/\ugg/', ['Gg']],
    534       \ ['H', 's/H/\Uh\Eh/', ['Hh']],
    535       \ ['I', 's/I/\lII/', ['iI']],
    536       \ ['J', 's/J/\LJ\EJ/', ['jJ']],
    537       \ ['K', 's/K/\Uk\ek/', ['Kk']],
    538       \ ['lLl', "s/L/\<C-V>\<C-M>/", ["l\<C-V>", 'l']],
    539       \ ['mMm', 's/M/\r/', ['m', 'm']],
    540       \ ['nNn', "s/N/\\\<C-V>\<C-M>/", ["n\<C-V>", 'n']],
    541       \ ['oOo', 's/O/\n/', ["o\no"]],
    542       \ ['pPp', 's/P/\b/', ["p\<C-H>p"]],
    543       \ ['qQq', 's/Q/\t/', ["q\tq"]],
    544       \ ['rRr', 's/R/\\/', ['r\r']],
    545       \ ['sSs', 's/S/\c/', ['scs']],
    546       \ ['tTt', "s/T/\<C-V>\<C-J>/", ["t\<C-V>\<C-J>t"]],
    547       \ ['U', 's/U/\L\uuUu\l\EU/', ['UuuU']],
    548       \ ['V', 's/V/\U\lVvV\u\Ev/', ['vVVv']],
    549       \ ['\', 's/\\/\\\\/', ['\\']]
    550       \ ]
    551  call Run_SubCmd_Tests(tests)
    552 endfunc
    553 
    554 func Test_sub_cmd_2()
    555  set nomagic
    556  set cpo&
    557 
    558  " List entry format: [input, cmd, output]
    559  let tests = [['A', 's/A/&&/', ['&&']],
    560       \ ['B', 's/B/\&/', ['B']],
    561       \ ['C123456789', 's/\mC\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)/\0\9\8\7\6\5\4\3\2\1/', ['C123456789987654321']],
    562       \ ['D', 's/D/d/', ['d']],
    563       \ ['E', 's/E/~/', ['~']],
    564       \ ['F', 's/F/\~/', ['~']],
    565       \ ['G', 's/G/\ugg/', ['Gg']],
    566       \ ['H', 's/H/\Uh\Eh/', ['Hh']],
    567       \ ['I', 's/I/\lII/', ['iI']],
    568       \ ['J', 's/J/\LJ\EJ/', ['jJ']],
    569       \ ['K', 's/K/\Uk\ek/', ['Kk']],
    570       \ ['lLl', "s/L/\<C-V>\<C-M>/", ["l\<C-V>", 'l']],
    571       \ ['mMm', 's/M/\r/', ['m', 'm']],
    572       \ ['nNn', "s/N/\\\<C-V>\<C-M>/", ["n\<C-V>", 'n']],
    573       \ ['oOo', 's/O/\n/', ["o\no"]],
    574       \ ['pPp', 's/P/\b/', ["p\<C-H>p"]],
    575       \ ['qQq', 's/Q/\t/', ["q\tq"]],
    576       \ ['rRr', 's/R/\\/', ['r\r']],
    577       \ ['sSs', 's/S/\c/', ['scs']],
    578       \ ['tTt', "s/T/\<C-V>\<C-J>/", ["t\<C-V>\<C-J>t"]],
    579       \ ['U', 's/U/\L\uuUu\l\EU/', ['UuuU']],
    580       \ ['V', 's/V/\U\lVvV\u\Ev/', ['vVVv']],
    581       \ ['\', 's/\\/\\\\/', ['\\']]
    582       \ ]
    583  call Run_SubCmd_Tests(tests)
    584 endfunc
    585 
    586 func Test_sub_cmd_3()
    587  set nomagic
    588  set cpo&
    589 
    590  " List entry format: [input, cmd, output]
    591  let tests = [['aAa', "s/A/\\='\\'/", ['a\a']],
    592       \ ['bBb', "s/B/\\='\\\\'/", ['b\\b']],
    593       \ ['cCc', "s/C/\\='\<C-V>\<C-M>'/", ["c\<C-V>", 'c']],
    594       \ ['dDd', "s/D/\\='\\\<C-V>\<C-M>'/", ["d\\\<C-V>", 'd']],
    595       \ ['eEe', "s/E/\\='\\\\\<C-V>\<C-M>'/", ["e\\\\\<C-V>", 'e']],
    596       \ ['fFf', "s/F/\\='\r'/", ['f', 'f']],
    597       \ ['gGg', "s/G/\\='\<C-V>\<C-J>'/", ["g\<C-V>", 'g']],
    598       \ ['hHh', "s/H/\\='\\\<C-V>\<C-J>'/", ["h\\\<C-V>", 'h']],
    599       \ ['iIi', "s/I/\\='\\\\\<C-V>\<C-J>'/", ["i\\\\\<C-V>", 'i']],
    600       \ ['jJj', "s/J/\\='\n'/", ['j', 'j']],
    601       \ ['kKk', 's/K/\="\r"/', ['k', 'k']],
    602       \ ['lLl', 's/L/\="\n"/', ['l', 'l']]
    603       \ ]
    604  call Run_SubCmd_Tests(tests)
    605 endfunc
    606 
    607 " Test for submatch() on :substitute.
    608 func Test_sub_cmd_4()
    609  set magic&
    610  set cpo&
    611 
    612  " List entry format: [input, cmd, output]
    613  let tests = [ ['aAa', "s/A/\\=substitute(submatch(0), '.', '\\', '')/",
    614       \				['a\a']],
    615       \ ['bBb', "s/B/\\=substitute(submatch(0), '.', '\\', '')/",
    616       \				['b\b']],
    617       \ ['cCc', "s/C/\\=substitute(submatch(0), '.', '\<C-V>\<C-M>', '')/",
    618       \				["c\<C-V>", 'c']],
    619       \ ['dDd', "s/D/\\=substitute(submatch(0), '.', '\\\<C-V>\<C-M>', '')/",
    620       \				["d\<C-V>", 'd']],
    621       \ ['eEe', "s/E/\\=substitute(submatch(0), '.', '\\\\\<C-V>\<C-M>', '')/",
    622       \				["e\\\<C-V>", 'e']],
    623       \ ['fFf', "s/F/\\=substitute(submatch(0), '.', '\\r', '')/",
    624       \				['f', 'f']],
    625       \ ['gGg', 's/G/\=substitute(submatch(0), ".", "\<C-V>\<C-J>", "")/',
    626       \				["g\<C-V>", 'g']],
    627       \ ['hHh', 's/H/\=substitute(submatch(0), ".", "\\\<C-V>\<C-J>", "")/',
    628       \				["h\<C-V>", 'h']],
    629       \ ['iIi', 's/I/\=substitute(submatch(0), ".", "\\\\\<C-V>\<C-J>", "")/',
    630       \				["i\\\<C-V>", 'i']],
    631       \ ['jJj', "s/J/\\=substitute(submatch(0), '.', '\\n', '')/",
    632       \				['j', 'j']],
    633       \ ['kKk', "s/K/\\=substitute(submatch(0), '.', '\\r', '')/",
    634       \				['k', 'k']],
    635       \ ['lLl', "s/L/\\=substitute(submatch(0), '.', '\\n', '')/",
    636       \				['l', 'l']],
    637       \ ]
    638  call Run_SubCmd_Tests(tests)
    639 endfunc
    640 
    641 func Test_sub_cmd_5()
    642  set magic&
    643  set cpo&
    644 
    645  " List entry format: [input, cmd, output]
    646  let tests = [ ['A123456789', 's/A\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)/\=submatch(0) . submatch(9) . submatch(8) . submatch(7) . submatch(6) . submatch(5) . submatch(4) . submatch(3) . submatch(2) . submatch(1)/', ['A123456789987654321']],
    647       \ ['B123456789', 's/B\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)/\=string([submatch(0, 1), submatch(9, 1), submatch(8, 1), submatch(7, 1), submatch(6, 1), submatch(5, 1), submatch(4, 1), submatch(3, 1), submatch(2, 1), submatch(1, 1)])/', ["[['B123456789'], ['9'], ['8'], ['7'], ['6'], ['5'], ['4'], ['3'], ['2'], ['1']]"]],
    648       \ ]
    649  call Run_SubCmd_Tests(tests)
    650 endfunc
    651 
    652 " Test for *:s%* on :substitute.
    653 func Test_sub_cmd_6()
    654  set magic&
    655  " Nvim: no "/" flag in 'cpoptions'.
    656  " set cpo+=/
    657 
    658  " List entry format: [input, cmd, output]
    659  let tests = [ ['A', 's/A/a/', ['a']],
    660       \ ['B', 's/B/%/', ['a']],
    661       \ ]
    662  " call Run_SubCmd_Tests(tests)
    663 
    664  set cpo-=/
    665  let tests = [ ['C', 's/C/c/', ['c']],
    666       \ ['D', 's/D/%/', ['%']],
    667       \ ]
    668  call Run_SubCmd_Tests(tests)
    669 
    670  set cpo&
    671 endfunc
    672 
    673 " Test for :s replacing \n with  line break.
    674 func Test_sub_cmd_7()
    675  set magic&
    676  set cpo&
    677 
    678  " List entry format: [input, cmd, output]
    679  let tests = [ ["A\<C-V>\<C-M>A", 's/A./\=submatch(0)/', ['A', 'A']],
    680       \ ["B\<C-V>\<C-J>B", 's/B./\=submatch(0)/', ['B', 'B']],
    681       \ ["C\<C-V>\<C-J>C", 's/C./\=strtrans(string(submatch(0, 1)))/', [strtrans("['C\<C-J>']C")]],
    682       \ ["D\<C-V>\<C-J>\nD", 's/D.\nD/\=strtrans(string(submatch(0, 1)))/', [strtrans("['D\<C-J>', 'D']")]],
    683       \ ["E\<C-V>\<C-J>\n\<C-V>\<C-J>\n\<C-V>\<C-J>\n\<C-V>\<C-J>\n\<C-V>\<C-J>E", 's/E\_.\{-}E/\=strtrans(string(submatch(0, 1)))/', [strtrans("['E\<C-J>', '\<C-J>', '\<C-J>', '\<C-J>', '\<C-J>E']")]],
    684       \ ]
    685  call Run_SubCmd_Tests(tests)
    686 
    687  exe "normal oQ\nQ\<Esc>k"
    688  call assert_fails('s/Q[^\n]Q/\=submatch(0)."foobar"/', 'E486')
    689  enew!
    690 endfunc
    691 
    692 func TitleString()
    693  let check = 'foo' =~ 'bar'
    694  return ""
    695 endfunc
    696 
    697 func Test_sub_cmd_8()
    698  set titlestring=%{TitleString()}
    699 
    700  enew!
    701  call append(0, ['', 'test_one', 'test_two'])
    702  call cursor(1,1)
    703  /^test_one/s/.*/\="foo\nbar"/
    704  call assert_equal('foo', getline(2))
    705  call assert_equal('bar', getline(3))
    706  call feedkeys(':/^test_two/s/.*/\="foo\nbar"/c', "t")
    707  call feedkeys("\<CR>y", "xt")
    708  call assert_equal('foo', getline(4))
    709  call assert_equal('bar', getline(5))
    710 
    711  enew!
    712  set titlestring&
    713 endfunc
    714 
    715 func Test_sub_cmd_9()
    716  new
    717  let input = ['1 aaa', '2 aaa', '3 aaa']
    718  call setline(1, input)
    719  func Foo()
    720    return submatch(0)
    721  endfunc
    722  %s/aaa/\=Foo()/gn
    723  call assert_equal(input, getline(1, '$'))
    724  call assert_equal(1, &modifiable)
    725 
    726  delfunc Foo
    727  bw!
    728 endfunc
    729 
    730 func Test_sub_highlight_zero_match()
    731  CheckScreendump
    732  CheckRunVimInTerminal
    733 
    734  let lines =<< trim END
    735    call setline(1, ['one', 'two', 'three'])
    736  END
    737  call writefile(lines, 'XscriptSubHighlight', 'D')
    738  let buf = RunVimInTerminal('-S XscriptSubHighlight', #{rows: 8, cols: 60})
    739  call term_sendkeys(buf, ":%s/^/   /c\<CR>")
    740  call VerifyScreenDump(buf, 'Test_sub_highlight_zer_match_1', {})
    741 
    742  call term_sendkeys(buf, "\<Esc>")
    743  call StopVimInTerminal(buf)
    744 endfunc
    745 
    746 func Test_nocatch_sub_failure_handling()
    747  " normal error results in all replacements
    748  func Foo()
    749    foobar
    750  endfunc
    751  new
    752  call setline(1, ['1 aaa', '2 aaa', '3 aaa'])
    753  " need silent! to avoid a delay when entering Insert mode
    754  silent! %s/aaa/\=Foo()/g
    755  call assert_equal(['1 0', '2 0', '3 0'], getline(1, 3))
    756 
    757  " Throw without try-catch causes abort after the first line.
    758  " We cannot test this, since it would stop executing the test script.
    759 
    760  " try/catch does not result in any changes
    761  func! Foo()
    762    throw 'error'
    763  endfunc
    764  call setline(1, ['1 aaa', '2 aaa', '3 aaa'])
    765  let error_caught = 0
    766  try
    767    %s/aaa/\=Foo()/g
    768  catch
    769    let error_caught = 1
    770  endtry
    771  call assert_equal(1, error_caught)
    772  call assert_equal(['1 aaa', '2 aaa', '3 aaa'], getline(1, 3))
    773 
    774  " Same, but using "n" flag so that "sandbox" gets set
    775  call setline(1, ['1 aaa', '2 aaa', '3 aaa'])
    776  let error_caught = 0
    777  try
    778    %s/aaa/\=Foo()/gn
    779  catch
    780    let error_caught = 1
    781  endtry
    782  call assert_equal(1, error_caught)
    783  call assert_equal(['1 aaa', '2 aaa', '3 aaa'], getline(1, 3))
    784 
    785  delfunc Foo
    786  bwipe!
    787 endfunc
    788 
    789 " Test ":s/pat/sub/" with different ~s in sub.
    790 func Test_replace_with_tilde()
    791  new
    792  " Set the last replace string to empty
    793  s/^$//
    794  call append(0, ['- Bug in "vPPPP" on this text:'])
    795  normal gg
    796  s/u/~u~/
    797  call assert_equal('- Bug in "vPPPP" on this text:', getline(1))
    798  s/i/~u~/
    799  call assert_equal('- Bug uuun "vPPPP" on this text:', getline(1))
    800  s/o/~~~/
    801  call assert_equal('- Bug uuun "vPPPP" uuuuuuuuun this text:', getline(1))
    802  close!
    803 endfunc
    804 
    805 func Test_replace_keeppatterns()
    806  new
    807  a
    808 foobar
    809 
    810 substitute foo asdf foo
    811 
    812 one two
    813 .
    814 
    815  normal gg
    816  /^substitute
    817  s/foo/bar/
    818  call assert_equal('foo', @/)
    819  call assert_equal('substitute bar asdf foo', getline('.'))
    820 
    821  /^substitute
    822  keeppatterns s/asdf/xyz/
    823  call assert_equal('^substitute', @/)
    824  call assert_equal('substitute bar xyz foo', getline('.'))
    825 
    826  /^substitute
    827  &
    828  call assert_equal('^substitute', @/)
    829  call assert_equal('substitute bar xyz bar', getline('.'))
    830 
    831  exe "normal /bar /e\<CR>"
    832  call assert_equal(15, col('.'))
    833  normal -
    834  keeppatterns /xyz
    835  call assert_equal('bar ', @/)
    836  call assert_equal('substitute bar xyz bar', getline('.'))
    837  exe "normal 0dn"
    838  call assert_equal('xyz bar', getline('.'))
    839 
    840  close!
    841 endfunc
    842 
    843 func Test_sub_beyond_end()
    844  new
    845  call setline(1, '#')
    846  let @/ = '^#\n\zs'
    847  s///e
    848  call assert_equal('#', getline(1))
    849  bwipe!
    850 endfunc
    851 
    852 " Test for repeating last substitution using :~ and :&r
    853 func Test_repeat_last_sub()
    854  new
    855  call setline(1, ['blue green yellow orange white'])
    856  s/blue/red/
    857  let @/ = 'yellow'
    858  ~
    859  let @/ = 'white'
    860  :&r
    861  let @/ = 'green'
    862  s//gray
    863  call assert_equal('red gray red orange red', getline(1))
    864  close!
    865 endfunc
    866 
    867 " Test for Vi compatible substitution:
    868 "     \/{string}/, \?{string}? and \&{string}&
    869 func Test_sub_vi_compatibility()
    870  new
    871  call setline(1, ['blue green yellow orange blue'])
    872  let @/ = 'orange'
    873  s\/white/
    874  let @/ = 'blue'
    875  s\?amber?
    876  let @/ = 'white'
    877  s\&green&
    878  call assert_equal('amber green yellow white green', getline(1))
    879  close!
    880 endfunc
    881 
    882 " Test for substitute with the new text longer than the original text
    883 func Test_sub_expand_text()
    884  new
    885  call setline(1, 'abcabcabcabcabcabcabcabc')
    886  s/b/\=repeat('B', 10)/g
    887  call assert_equal(repeat('aBBBBBBBBBBc', 8), getline(1))
    888  close!
    889 endfunc
    890 
    891 " Test for command failures when the last substitute pattern is not set.
    892 func Test_sub_with_no_last_pat()
    893  let lines =<< trim [SCRIPT]
    894    call assert_fails('~', 'E33:')
    895    call assert_fails('s//abc/g', 'E35:')
    896    call assert_fails('s\/bar', 'E35:')
    897    call assert_fails('s\&bar&', 'E33:')
    898    call writefile(v:errors, 'Xresult')
    899    qall!
    900  [SCRIPT]
    901  call writefile(lines, 'Xscript', 'D')
    902  if RunVim([], [], '--clean -S Xscript')
    903    call assert_equal([], readfile('Xresult'))
    904  endif
    905 
    906  let lines =<< trim [SCRIPT]
    907    set cpo+=/
    908    call assert_fails('s/abc/%/', 'E33:')
    909    call writefile(v:errors, 'Xresult')
    910    qall!
    911  [SCRIPT]
    912  " Nvim: no "/" flag in 'cpoptions'.
    913  " call writefile(lines, 'Xscript')
    914  " if RunVim([], [], '--clean -S Xscript')
    915  "   call assert_equal([], readfile('Xresult'))
    916  " endif
    917 
    918  call delete('Xresult')
    919 endfunc
    920 
    921 func Test_substitute()
    922  call assert_equal('a1a2a3a', substitute('123', '\zs', 'a', 'g'))
    923  " Substitute with special keys
    924  call assert_equal("a\<End>c", substitute('abc', "a.c", "a\<End>c", ''))
    925 endfunc
    926 
    927 func Test_substitute_expr()
    928  let g:val = 'XXX'
    929  call assert_equal('XXX', substitute('yyy', 'y*', '\=g:val', ''))
    930  call assert_equal('XXX', substitute('yyy', 'y*', {-> g:val}, ''))
    931  call assert_equal("-\u1b \uf2-", substitute("-%1b %f2-", '%\(\x\x\)',
    932 		   \ '\=nr2char("0x" . submatch(1))', 'g'))
    933  call assert_equal("-\u1b \uf2-", substitute("-%1b %f2-", '%\(\x\x\)',
    934 		   \ {-> nr2char("0x" . submatch(1))}, 'g'))
    935 
    936  call assert_equal('231', substitute('123', '\(.\)\(.\)\(.\)',
    937 \ {-> submatch(2) . submatch(3) . submatch(1)}, ''))
    938 
    939  func Recurse()
    940    return substitute('yyy', 'y\(.\)y', {-> submatch(1)}, '')
    941  endfunc
    942  " recursive call works
    943  call assert_equal('-y-x-', substitute('xxx', 'x\(.\)x', {-> '-' . Recurse() . '-' . submatch(1) . '-'}, ''))
    944 
    945  call assert_fails("let s=submatch([])", 'E745:')
    946  call assert_fails("let s=submatch(2, [])", 'E745:')
    947 endfunc
    948 
    949 func Test_invalid_submatch()
    950  " This was causing invalid memory access in Vim-7.4.2232 and older
    951  call assert_fails("call substitute('x', '.', {-> submatch(10)}, '')", 'E935:')
    952  call assert_fails('eval submatch(-1)', 'E935:')
    953  call assert_equal('', submatch(0))
    954  call assert_equal('', submatch(1))
    955  call assert_equal([], submatch(0, 1))
    956  call assert_equal([], submatch(1, 1))
    957 endfunc
    958 
    959 func Test_submatch_list_concatenate()
    960  let pat = 'A\(.\)'
    961  let Rep = {-> string([submatch(0, 1)] + [[submatch(1)]])}
    962  call substitute('A1', pat, Rep, '')->assert_equal("[['A1'], ['1']]")
    963 endfunc
    964 
    965 func Test_substitute_expr_arg()
    966  call assert_equal('123456789-123456789=', substitute('123456789',
    967 \ '\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)',
    968 \ {m -> m[0] . '-' . m[1] . m[2] . m[3] . m[4] . m[5] . m[6] . m[7] . m[8] . m[9] . '='}, ''))
    969 
    970  call assert_equal('123456-123456=789', substitute('123456789',
    971 \ '\(.\)\(.\)\(.\)\(a*\)\(n*\)\(.\)\(.\)\(.\)\(x*\)',
    972 \ {m -> m[0] . '-' . m[1] . m[2] . m[3] . m[4] . m[5] . m[6] . m[7] . m[8] . m[9] . '='}, ''))
    973 
    974  call assert_equal('123456789-123456789x=', substitute('123456789',
    975 \ '\(.\)\(.\)\(.*\)',
    976 \ {m -> m[0] . '-' . m[1] . m[2] . m[3] . 'x' . m[4] . m[5] . m[6] . m[7] . m[8] . m[9] . '='}, ''))
    977 
    978  call assert_fails("call substitute('xxx', '.', {m -> string(add(m, 'x'))}, '')", 'E742:')
    979  call assert_fails("call substitute('xxx', '.', {m -> string(insert(m, 'x'))}, '')", 'E742:')
    980  call assert_fails("call substitute('xxx', '.', {m -> string(extend(m, ['x']))}, '')", 'E742:')
    981  call assert_fails("call substitute('xxx', '.', {m -> string(remove(m, 1))}, '')", 'E742:')
    982 endfunc
    983 
    984 " Test for using a function to supply the substitute string
    985 func Test_substitute_using_func()
    986  func Xfunc()
    987    return '1234'
    988  endfunc
    989  call assert_equal('a1234f', substitute('abcdef', 'b..e',
    990        \ function("Xfunc"), ''))
    991  delfunc Xfunc
    992 endfunc
    993 
    994 " Test for using submatch() with a multiline match
    995 func Test_substitute_multiline_submatch()
    996  new
    997  call setline(1, ['line1', 'line2', 'line3', 'line4'])
    998  %s/^line1\(\_.\+\)line4$/\=submatch(1)/
    999  call assert_equal(['', 'line2', 'line3', ''], getline(1, '$'))
   1000  close!
   1001 endfunc
   1002 
   1003 func Test_substitute_skipped_range()
   1004  new
   1005  if 0
   1006    /1/5/2/2/\n
   1007  endif
   1008  call assert_equal([0, 1, 1, 0, 1], getcurpos())
   1009  bwipe!
   1010 endfunc
   1011 
   1012 " Test using the 'gdefault' option (when on, flag 'g' is default on).
   1013 func Test_substitute_gdefault()
   1014  new
   1015 
   1016  " First check without 'gdefault'
   1017  call setline(1, 'foo bar foo')
   1018  s/foo/FOO/
   1019  call assert_equal('FOO bar foo', getline(1))
   1020  call setline(1, 'foo bar foo')
   1021  s/foo/FOO/g
   1022  call assert_equal('FOO bar FOO', getline(1))
   1023  call setline(1, 'foo bar foo')
   1024  s/foo/FOO/gg
   1025  call assert_equal('FOO bar foo', getline(1))
   1026 
   1027  " Then check with 'gdefault'
   1028  set gdefault
   1029  call setline(1, 'foo bar foo')
   1030  s/foo/FOO/
   1031  call assert_equal('FOO bar FOO', getline(1))
   1032  call setline(1, 'foo bar foo')
   1033  s/foo/FOO/g
   1034  call assert_equal('FOO bar foo', getline(1))
   1035  call setline(1, 'foo bar foo')
   1036  s/foo/FOO/gg
   1037  call assert_equal('FOO bar FOO', getline(1))
   1038 
   1039  " Setting 'compatible' should reset 'gdefault'
   1040  call assert_equal(1, &gdefault)
   1041  " set compatible
   1042  set nogdefault
   1043  call assert_equal(0, &gdefault)
   1044  set nocompatible
   1045  call assert_equal(0, &gdefault)
   1046 
   1047  bw!
   1048 endfunc
   1049 
   1050 " This was using "old_sub" after it was freed.
   1051 func Test_using_old_sub()
   1052  " set compatible maxfuncdepth=10
   1053  set maxfuncdepth=10
   1054  new
   1055  call setline(1, 'some text.')
   1056  func Repl()
   1057    ~
   1058    s/
   1059  endfunc
   1060  silent! s/\%')/\=Repl()
   1061 
   1062  delfunc Repl
   1063  bwipe!
   1064  set nocompatible
   1065 endfunc
   1066 
   1067 " This was switching windows in between computing the length and using it.
   1068 func Test_sub_change_window()
   1069  silent! lfile
   1070  sil! norm o0000000000000000000000000000000000000000000000000000
   1071  func Repl()
   1072    lopen
   1073  endfunc
   1074  silent!  s/\%')/\=Repl()
   1075  bwipe!
   1076  bwipe!
   1077  delfunc Repl
   1078 endfunc
   1079 
   1080 " This was undoign a change in between computing the length and using it.
   1081 func Do_Test_sub_undo_change()
   1082  new
   1083  norm o0000000000000000000000000000000000000000000000000000
   1084  silent! s/\%')/\=Repl()
   1085  bwipe!
   1086 endfunc
   1087 
   1088 func Test_sub_undo_change()
   1089  func Repl()
   1090    silent! norm g-
   1091  endfunc
   1092  call Do_Test_sub_undo_change()
   1093 
   1094  func! Repl()
   1095    silent earlier
   1096  endfunc
   1097  call Do_Test_sub_undo_change()
   1098 
   1099  delfunc Repl
   1100 endfunc
   1101 
   1102 " This was opening a command line window from the expression
   1103 func Test_sub_open_cmdline_win()
   1104  " the error only happens in a very specific setup, run a new Vim instance to
   1105  " get a clean starting point.
   1106  let lines =<< trim [SCRIPT]
   1107    set vb t_vb=
   1108    norm o0000000000000000000000000000000000000000000000000000
   1109    func Replace()
   1110      norm q/
   1111    endfunc
   1112    s/\%')/\=Replace()
   1113    redir >Xresult
   1114    messages
   1115    redir END
   1116    qall!
   1117  [SCRIPT]
   1118  call writefile(lines, 'Xscript', 'D')
   1119  if RunVim([], [], '-u NONE -S Xscript')
   1120    call assert_match('E565: Not allowed to change text or change window',
   1121          \ readfile('Xresult')->join('XX'))
   1122  endif
   1123 
   1124  call delete('Xresult')
   1125 endfunc
   1126 
   1127 " This was editing a script file from the expression
   1128 func Test_sub_edit_scriptfile()
   1129  new
   1130  norm o0000000000000000000000000000000000000000000000000000
   1131  func EditScript()
   1132    silent! scr! Xfile
   1133  endfunc
   1134  s/\%')/\=EditScript()
   1135 
   1136  delfunc EditScript
   1137  bwipe!
   1138 endfunc
   1139 
   1140 " This was editing another file from the expression.
   1141 func Test_sub_expr_goto_other_file()
   1142  call writefile([''], 'Xfileone', 'D')
   1143  enew!
   1144  call setline(1, ['a', 'b', 'c', 'd',
   1145 \ 'Xfileone zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz'])
   1146 
   1147  func g:SplitGotoFile()
   1148    exe "sil! norm 0\<C-W>gf"
   1149    return ''
   1150  endfunc
   1151 
   1152  $
   1153  s/\%')/\=g:SplitGotoFile()
   1154 
   1155  delfunc g:SplitGotoFile
   1156  bwipe!
   1157 endfunc
   1158 
   1159 func Test_recursive_expr_substitute()
   1160  " this was reading invalid memory
   1161  let lines =<< trim END
   1162      func Repl(g, n)
   1163        s
   1164        r%:s000
   1165      endfunc
   1166      next 0
   1167      let caught = 0
   1168      s/\%')/\=Repl(0, 0)
   1169      qall!
   1170  END
   1171  call writefile(lines, 'XexprSubst', 'D')
   1172  call RunVim([], [], '--clean -S XexprSubst')
   1173 endfunc
   1174 
   1175 " Test for the 2-letter and 3-letter :substitute commands
   1176 func Test_substitute_short_cmd()
   1177  new
   1178  call setline(1, ['one', 'one one one'])
   1179  s/one/two
   1180  call cursor(2, 1)
   1181 
   1182  " :sc
   1183  call feedkeys(":sc\<CR>y", 'xt')
   1184  call assert_equal('two one one', getline(2))
   1185 
   1186  " :scg
   1187  call setline(2, 'one one one')
   1188  call feedkeys(":scg\<CR>nyq", 'xt')
   1189  call assert_equal('one two one', getline(2))
   1190 
   1191  " :sci
   1192  call setline(2, 'ONE One onE')
   1193  call feedkeys(":sci\<CR>y", 'xt')
   1194  call assert_equal('two One onE', getline(2))
   1195 
   1196  " :scI
   1197  set ignorecase
   1198  call setline(2, 'ONE One one')
   1199  call feedkeys(":scI\<CR>y", 'xt')
   1200  call assert_equal('ONE One two', getline(2))
   1201  set ignorecase&
   1202 
   1203  " :scn
   1204  call setline(2, 'one one one')
   1205  let t = execute('scn')->split("\n")
   1206  call assert_equal(['1 match on 1 line'], t)
   1207  call assert_equal('one one one', getline(2))
   1208 
   1209  " :scp
   1210  call setline(2, "\tone one one")
   1211  redir => output
   1212  call feedkeys(":scp\<CR>y", 'xt')
   1213  redir END
   1214  call assert_equal('        two one one', output->split("\n")[-1])
   1215  call assert_equal("\ttwo one one", getline(2))
   1216 
   1217  " :scl
   1218  call setline(2, "\tone one one")
   1219  redir => output
   1220  call feedkeys(":scl\<CR>y", 'xt')
   1221  redir END
   1222  call assert_equal("^Itwo one one$", output->split("\n")[-1])
   1223  call assert_equal("\ttwo one one", getline(2))
   1224 
   1225  " :sgc
   1226  call setline(2, 'one one one one one')
   1227  call feedkeys(":sgc\<CR>nyyq", 'xt')
   1228  call assert_equal('one two two one one', getline(2))
   1229 
   1230  " :sg
   1231  call setline(2, 'one one one')
   1232  sg
   1233  call assert_equal('two two two', getline(2))
   1234 
   1235  " :sgi
   1236  call setline(2, 'ONE One onE')
   1237  sgi
   1238  call assert_equal('two two two', getline(2))
   1239 
   1240  " :sgI
   1241  set ignorecase
   1242  call setline(2, 'ONE One one')
   1243  sgI
   1244  call assert_equal('ONE One two', getline(2))
   1245  set ignorecase&
   1246 
   1247  " :sgn
   1248  call setline(2, 'one one one')
   1249  let t = execute('sgn')->split("\n")
   1250  call assert_equal(['3 matches on 1 line'], t)
   1251  call assert_equal('one one one', getline(2))
   1252 
   1253  " :sgp
   1254  call setline(2, "\tone one one")
   1255  redir => output
   1256  sgp
   1257  redir END
   1258  call assert_equal('        two two two', output->split("\n")[-1])
   1259  call assert_equal("\ttwo two two", getline(2))
   1260 
   1261  " :sgl
   1262  call setline(2, "\tone one one")
   1263  redir => output
   1264  sgl
   1265  redir END
   1266  call assert_equal("^Itwo two two$", output->split("\n")[-1])
   1267  call assert_equal("\ttwo two two", getline(2))
   1268 
   1269  " :sgr
   1270  call setline(2, "one one one")
   1271  call cursor(2, 1)
   1272  s/abc/xyz/e
   1273  let @/ = 'one'
   1274  sgr
   1275  call assert_equal('xyz xyz xyz', getline(2))
   1276 
   1277  " :sic
   1278  call cursor(1, 1)
   1279  s/one/two/e
   1280  call setline(2, "ONE One one")
   1281  call cursor(2, 1)
   1282  call feedkeys(":sic\<CR>y", 'xt')
   1283  call assert_equal('two One one', getline(2))
   1284 
   1285  " :si
   1286  call setline(2, "ONE One one")
   1287  si
   1288  call assert_equal('two One one', getline(2))
   1289 
   1290  " :siI
   1291  call setline(2, "ONE One one")
   1292  siI
   1293  call assert_equal('ONE One two', getline(2))
   1294 
   1295  " :sin
   1296  call setline(2, 'ONE One onE')
   1297  let t = execute('sin')->split("\n")
   1298  call assert_equal(['1 match on 1 line'], t)
   1299  call assert_equal('ONE One onE', getline(2))
   1300 
   1301  " :sip
   1302  call setline(2, "\tONE One onE")
   1303  redir => output
   1304  sip
   1305  redir END
   1306  call assert_equal('        two One onE', output->split("\n")[-1])
   1307  call assert_equal("\ttwo One onE", getline(2))
   1308 
   1309  " :sir
   1310  call setline(2, "ONE One onE")
   1311  call cursor(2, 1)
   1312  s/abc/xyz/e
   1313  let @/ = 'one'
   1314  sir
   1315  call assert_equal('xyz One onE', getline(2))
   1316 
   1317  " :sIc
   1318  call cursor(1, 1)
   1319  s/one/two/e
   1320  call setline(2, "ONE One one")
   1321  call cursor(2, 1)
   1322  call feedkeys(":sIc\<CR>y", 'xt')
   1323  call assert_equal('ONE One two', getline(2))
   1324 
   1325  " :sIg
   1326  call setline(2, "ONE one onE one")
   1327  sIg
   1328  call assert_equal('ONE two onE two', getline(2))
   1329 
   1330  " :sIi
   1331  call setline(2, "ONE One one")
   1332  sIi
   1333  call assert_equal('two One one', getline(2))
   1334 
   1335  " :sI
   1336  call setline(2, "ONE One one")
   1337  sI
   1338  call assert_equal('ONE One two', getline(2))
   1339 
   1340  " :sIn
   1341  call setline(2, 'ONE One one')
   1342  let t = execute('sIn')->split("\n")
   1343  call assert_equal(['1 match on 1 line'], t)
   1344  call assert_equal('ONE One one', getline(2))
   1345 
   1346  " :sIp
   1347  call setline(2, "\tONE One one")
   1348  redir => output
   1349  sIp
   1350  redir END
   1351  call assert_equal('        ONE One two', output->split("\n")[-1])
   1352  call assert_equal("\tONE One two", getline(2))
   1353 
   1354  " :sIl
   1355  call setline(2, "\tONE onE one")
   1356  redir => output
   1357  sIl
   1358  redir END
   1359  call assert_equal("^IONE onE two$", output->split("\n")[-1])
   1360  call assert_equal("\tONE onE two", getline(2))
   1361 
   1362  " :sIr
   1363  call setline(2, "ONE one onE")
   1364  call cursor(2, 1)
   1365  s/abc/xyz/e
   1366  let @/ = 'one'
   1367  sIr
   1368  call assert_equal('ONE xyz onE', getline(2))
   1369 
   1370  " :src
   1371  call setline(2, "ONE one one")
   1372  call cursor(2, 1)
   1373  s/abc/xyz/e
   1374  let @/ = 'one'
   1375  call feedkeys(":src\<CR>y", 'xt')
   1376  call assert_equal('ONE xyz one', getline(2))
   1377 
   1378  " :srg
   1379  call setline(2, "one one one")
   1380  call cursor(2, 1)
   1381  s/abc/xyz/e
   1382  let @/ = 'one'
   1383  srg
   1384  call assert_equal('xyz xyz xyz', getline(2))
   1385 
   1386  " :sri
   1387  call setline(2, "ONE one onE")
   1388  call cursor(2, 1)
   1389  s/abc/xyz/e
   1390  let @/ = 'one'
   1391  sri
   1392  call assert_equal('xyz one onE', getline(2))
   1393 
   1394  " :srI
   1395  call setline(2, "ONE one onE")
   1396  call cursor(2, 1)
   1397  s/abc/xyz/e
   1398  let @/ = 'one'
   1399  srI
   1400  call assert_equal('ONE xyz onE', getline(2))
   1401 
   1402  " :srn
   1403  call setline(2, "ONE one onE")
   1404  call cursor(2, 1)
   1405  s/abc/xyz/e
   1406  let @/ = 'one'
   1407  let t = execute('srn')->split("\n")
   1408  call assert_equal(['1 match on 1 line'], t)
   1409  call assert_equal('ONE one onE', getline(2))
   1410 
   1411  " :srp
   1412  call setline(2, "\tONE one onE")
   1413  call cursor(2, 1)
   1414  s/abc/xyz/e
   1415  let @/ = 'one'
   1416  redir => output
   1417  srp
   1418  redir END
   1419  call assert_equal('        ONE xyz onE', output->split("\n")[-1])
   1420  call assert_equal("\tONE xyz onE", getline(2))
   1421 
   1422  " :srl
   1423  call setline(2, "\tONE one onE")
   1424  call cursor(2, 1)
   1425  s/abc/xyz/e
   1426  let @/ = 'one'
   1427  redir => output
   1428  srl
   1429  redir END
   1430  call assert_equal("^IONE xyz onE$", output->split("\n")[-1])
   1431  call assert_equal("\tONE xyz onE", getline(2))
   1432 
   1433  " :sr
   1434  call setline(2, "ONE one onE")
   1435  call cursor(2, 1)
   1436  s/abc/xyz/e
   1437  let @/ = 'one'
   1438  sr
   1439  call assert_equal('ONE xyz onE', getline(2))
   1440 
   1441  " :sce
   1442  s/abc/xyz/e
   1443  call assert_fails("sc", 'E486:')
   1444  sce
   1445  " :sge
   1446  call assert_fails("sg", 'E486:')
   1447  sge
   1448  " :sie
   1449  call assert_fails("si", 'E486:')
   1450  sie
   1451  " :sIe
   1452  call assert_fails("sI", 'E486:')
   1453  sIe
   1454 
   1455  bw!
   1456 endfunc
   1457 
   1458 " Check handling expanding "~" resulting in extremely long text.
   1459 " FIXME: disabled, it takes too long to run on CI
   1460 "func Test_substitute_tilde_too_long()
   1461 "  enew!
   1462 "
   1463 "  s/.*/ixxx
   1464 "  s//~~~~~~~~~AAAAAAA@(
   1465 "
   1466 "  " Either fails with "out of memory" or "text too long".
   1467 "  " This can take a long time.
   1468 "  call assert_fails('sil! norm &&&&&&&&&', ['E1240:\|E342:'])
   1469 "
   1470 "  bwipe!
   1471 "endfunc
   1472 
   1473 " This should be done last to reveal a memory leak when vim_regsub_both() is
   1474 " called to evaluate an expression but it is not used in a second call.
   1475 func Test_z_substitute_expr_leak()
   1476  func SubExpr()
   1477    ~n
   1478  endfunc
   1479  silent! s/\%')/\=SubExpr()
   1480  delfunc SubExpr
   1481 endfunc
   1482 
   1483 func Test_substitute_expr_switch_win()
   1484  func R()
   1485    wincmd x
   1486    return 'XXXX'
   1487  endfunc
   1488  new Xfoobar
   1489  let bufnr = bufnr('%')
   1490  put ='abcdef'
   1491  silent! s/\%')/\=R()
   1492  call assert_fails(':%s/./\=R()/g', 'E565:')
   1493  delfunc R
   1494  exe bufnr .. "bw!"
   1495 endfunc
   1496 
   1497 " recursive call of :s using test-replace special
   1498 func Test_substitute_expr_recursive()
   1499  func Q()
   1500    %s/./\='foobar'/gn
   1501    return "foobar"
   1502  endfunc
   1503  func R()
   1504    %s/./\=Q()/g
   1505  endfunc
   1506  new Xfoobar_UAF
   1507  let bufnr = bufnr('%')
   1508  put ='abcdef'
   1509  silent! s/./\=R()/g
   1510  call assert_fails(':%s/./\=R()/g', 'E565:')
   1511  delfunc R
   1512  delfunc Q
   1513  exe bufnr .. "bw!"
   1514 endfunc
   1515 
   1516 " Test for changing 'cpo' in a substitute expression
   1517 func Test_substitute_expr_cpo()
   1518  func XSubExpr()
   1519    set cpo=
   1520    return 'x'
   1521  endfunc
   1522 
   1523  let save_cpo = &cpo
   1524  call assert_equal('xxx', substitute('abc', '.', '\=XSubExpr()', 'g'))
   1525  call assert_equal(save_cpo, &cpo)
   1526 
   1527  delfunc XSubExpr
   1528 endfunc
   1529 
   1530 " vim: shiftwidth=2 sts=2 expandtab