" Test commands that are not compiled in a :def function

source check.vim
import './vim9.vim' as v9
source term_util.vim
source view_util.vim

def Test_vim9cmd()
  var lines =<< trim END
    vim9cmd var x = 123
    let s:y = 'yes'
    vim9c assert_equal(123, x)
    vim9cm assert_equal('yes', y)
  END
  v9.CheckScriptSuccess(lines)

  assert_fails('vim9cmd', 'E1164:')
  assert_fails('legacy', 'E1234:')
  assert_fails('vim9cmd echo "con" . "cat"', 'E15:')

  lines =<< trim END
      let str = 'con'
      vim9cmd str .= 'cat'
  END
  v9.CheckScriptFailure(lines, 'E492:')

  lines =<< trim END
      vim9script
      legacy echo "con" . "cat"
      legacy let str = 'con'
      legacy let str .= 'cat'
  END
  v9.CheckScriptSuccess(lines)

  lines =<< trim END
      vim9script
      def Foo()
        g:found_bar = "bar"
      enddef
      nmap ,; :vim9cmd <SID>Foo()<CR>
  END
  v9.CheckScriptSuccess(lines)

  feedkeys(',;', 'xt')
  assert_equal("bar", g:found_bar)
  nunmap ,;
  unlet g:found_bar

  lines =<< trim END
      vim9script
      legacy echo 1'000
  END
  v9.CheckScriptFailure(lines, 'E115:')

  if has('float')
    lines =<< trim END
        vim9script
        echo .10
    END
    v9.CheckScriptSuccess(lines)
    lines =<< trim END
        vim9cmd echo .10
    END
    v9.CheckScriptSuccess(lines)
    lines =<< trim END
        vim9script
        legacy echo .10
    END
    v9.CheckScriptFailure(lines, 'E15:')
  endif

  echo v:version
  assert_fails('vim9cmd echo version', 'E121:')
  lines =<< trim END
      vim9script
      echo version
  END
  v9.CheckScriptFailure(lines, 'E121:')
  lines =<< trim END
      vim9script
      legacy echo version
  END
  v9.CheckScriptSuccess(lines)
enddef

def Test_defcompile_fails()
  assert_fails('defcompile NotExists', 'E1061:')
  assert_fails('defcompile debug debug Test_defcompile_fails', 'E488:')
  assert_fails('defcompile profile profile Test_defcompile_fails', 'E488:')
enddef

defcompile Test_defcompile_fails
defcompile debug Test_defcompile_fails
defcompile profile Test_defcompile_fails

def Test_cmdmod_execute()
  # "legacy" applies not only to the "exe" argument but also to the commands
  var lines =<< trim END
      vim9script

      b:undo = 'let g:undone = 1 | let g:undtwo = 2'
      legacy exe b:undo
      assert_equal(1, g:undone)
      assert_equal(2, g:undtwo)
  END
  v9.CheckScriptSuccess(lines)

  # same for "vim9cmd" modifier
  lines =<< trim END
      let b:undo = 'g:undone = 11 | g:undtwo = 22'
      vim9cmd exe b:undo
      call assert_equal(11, g:undone)
      call assert_equal(22, g:undtwo)
  END
  v9.CheckScriptSuccess(lines)
  unlet b:undo
  unlet g:undone
  unlet g:undtwo

  # "legacy" does not apply to a loaded script
  lines =<< trim END
      vim9script
      export var exported = 'x'
  END
  writefile(lines, 'Xvim9import.vim')
  lines =<< trim END
      legacy exe "import './Xvim9import.vim'"
  END
  v9.CheckScriptSuccess(lines)
  delete('Xvim9import.vim')

  # "legacy" does not aply to a called function
  lines =<< trim END
      vim9script

      def g:TheFunc()
        if exists('something')
          echo 'yes'
        endif
      enddef
      legacy exe 'call g:TheFunc()'
  END
  v9.CheckScriptSuccess(lines)
  delfunc g:TheFunc
enddef

def Test_edit_wildcards()
  var filename = 'Xtest'
  edit `=filename`
  assert_equal('Xtest', bufname())

  var filenr = 123
  edit Xtest`=filenr`
  assert_equal('Xtest123', bufname())

  filenr = 77
  edit `=filename``=filenr`
  assert_equal('Xtest77', bufname())

  edit X`=filename`xx`=filenr`yy
  assert_equal('XXtestxx77yy', bufname())

  v9.CheckDefFailure(['edit `=xxx`'], 'E1001:')
  v9.CheckDefFailure(['edit `="foo"'], 'E1083:')

  var files = ['file 1', 'file%2', 'file# 3']
  args `=files`
  assert_equal(files, argv())

  filename = 'Xwindo'
  windo edit `=filename`
  assert_equal('Xwindo', bufname())

  filename = 'Xtabdo'
  tabdo edit `=filename`
  assert_equal('Xtabdo', bufname())

  filename = 'Xargdo'
  argdo edit `=filename`
  assert_equal('Xargdo', bufname())

  :%bwipe!
  filename = 'Xbufdo'
  bufdo file `=filename`
  assert_equal('Xbufdo', bufname())
enddef

def Test_expand_alternate_file()
  var lines =<< trim END
    edit Xfileone
    var bone = bufnr()
    edit Xfiletwo
    var btwo = bufnr()
    edit Xfilethree
    var bthree = bufnr()

    edit #
    assert_equal(bthree, bufnr())
    edit %%
    assert_equal(btwo, bufnr())
    edit %% # comment
    assert_equal(bthree, bufnr())
    edit %%yy
    assert_equal('Xfiletwoyy', bufname())

    exe "edit %%" .. bone
    assert_equal(bone, bufnr())
    exe "edit %%" .. btwo .. "xx"
    assert_equal('Xfiletwoxx', bufname())

    next Xfileone Xfiletwo Xfilethree
    assert_equal('Xfileone', argv(0))
    assert_equal('Xfiletwo', argv(1))
    assert_equal('Xfilethree', argv(2))
    next %%%zz
    assert_equal('Xfileone', argv(0))
    assert_equal('Xfiletwo', argv(1))
    assert_equal('Xfilethreezz', argv(2))

    v:oldfiles = ['Xonefile', 'Xtwofile']
    edit %%<1
    assert_equal('Xonefile', bufname())
    edit %%<2
    assert_equal('Xtwofile', bufname())
    assert_fails('edit %%<3', 'E684:')

    edit Xfileone.vim
    edit Xfiletwo
    edit %%:r
    assert_equal('Xfileone', bufname())

    assert_false(bufexists('altfoo'))
    edit altfoo
    edit bar
    assert_true(bufexists('altfoo'))
    assert_true(buflisted('altfoo'))
    bdel %%
    assert_true(bufexists('altfoo'))
    assert_false(buflisted('altfoo'))
    bwipe! altfoo
    bwipe! bar
  END
  v9.CheckDefAndScriptSuccess(lines)
enddef

def Test_global_backtick_expansion()
  var name = 'xxx'
  new
  setline(1, ['one', 'two', 'three'])
  set nomod
  g/two/edit `=name`
  assert_equal('xxx', bufname())
  bwipe!

  new
  setline(1, ['one', 'two', 'three'])
  g/two/s/^/`=name`/
  assert_equal('`=name`two', getline(2))
  bwipe!
enddef

def Test_folddo_backtick_expansion()
  new
  var name = 'xxx'
  folddoopen edit `=name`
  assert_equal('xxx', bufname())
  bwipe!

  new
  setline(1, ['one', 'two'])
  set nomodified
  :1,2fold
  foldclose
  folddoclose edit `=name`
  assert_equal('xxx', bufname())
  bwipe!

  var lines =<< trim END
      g:val = 'value'
      def Test()
        folddoopen echo `=g:val`
      enddef
      call Test()
  END
  v9.CheckScriptFailure(lines, 'E15: Invalid expression: "`=g:val`"')
enddef

def Test_hardcopy_wildcards()
  CheckUnix
  CheckFeature postscript

  var outfile = 'print'
  hardcopy > X`=outfile`.ps
  assert_true(filereadable('Xprint.ps'))

  delete('Xprint.ps')
enddef

def Test_syn_include_wildcards()
  writefile(['syn keyword Found found'], 'Xthemine.vim')
  var save_rtp = &rtp
  &rtp = '.'

  var fname = 'mine'
  syn include @Group Xthe`=fname`.vim
  assert_match('Found.* contained found', execute('syn list Found'))

  &rtp = save_rtp
  delete('Xthemine.vim')
enddef

def Test_echo_linebreak()
  var lines =<< trim END
      vim9script
      redir @a
      echo 'one'
            .. 'two'
      redir END
      assert_equal("\nonetwo", @a)
  END
  v9.CheckScriptSuccess(lines)

  lines =<< trim END
      vim9script
      redir @a
      echo 11 +
            77
            - 22
      redir END
      assert_equal("\n66", @a)
  END
  v9.CheckScriptSuccess(lines)
enddef

def Test_condition_types()
  var lines =<< trim END
      if 'text'
      endif
  END
  v9.CheckDefAndScriptFailure(lines, 'E1135:', 1)

  lines =<< trim END
      if [1]
      endif
  END
  v9.CheckDefFailure(lines, 'E1012:', 1)
  v9.CheckScriptFailure(['vim9script'] + lines, 'E745:', 2)

  lines =<< trim END
      g:cond = 'text'
      if g:cond
      endif
  END
  v9.CheckDefExecAndScriptFailure(lines, 'E1135:', 2)

  lines =<< trim END
      g:cond = 0
      if g:cond
      elseif 'text'
      endif
  END
  v9.CheckDefAndScriptFailure(lines, 'E1135:', 3)

  lines =<< trim END
      g:cond = 0
      if g:cond
      elseif 'text' garbage
      endif
  END
  v9.CheckDefAndScriptFailure(lines, 'E488:', 3)

  lines =<< trim END
      g:cond = 0
      if g:cond
      elseif [1]
      endif
  END
  v9.CheckDefFailure(lines, 'E1012:', 3)
  v9.CheckScriptFailure(['vim9script'] + lines, 'E745:', 4)

  lines =<< trim END
      g:cond = 'text'
      if 0
      elseif g:cond
      endif
  END
  v9.CheckDefExecAndScriptFailure(lines, 'E1135:', 3)

  lines =<< trim END
      while 'text'
      endwhile
  END
  v9.CheckDefFailure(lines, 'E1012:', 1)
  v9.CheckScriptFailure(['vim9script'] + lines, 'E1135:', 2)

  lines =<< trim END
      while [1]
      endwhile
  END
  v9.CheckDefFailure(lines, 'E1012:', 1)
  v9.CheckScriptFailure(['vim9script'] + lines, 'E745:', 2)

  lines =<< trim END
      g:cond = 'text'
      while g:cond
      endwhile
  END
  v9.CheckDefExecAndScriptFailure(lines, 'E1135:', 2)
enddef

def Test_if_linebreak()
  var lines =<< trim END
      vim9script
      if 1 &&
            true
            || 1
        g:res = 42
      endif
      assert_equal(42, g:res)
  END
  v9.CheckScriptSuccess(lines)
  unlet g:res

  lines =<< trim END
      vim9script
      if 1 &&
            0
        g:res = 0
      elseif 0 ||
              0
              || 1
        g:res = 12
      endif
      assert_equal(12, g:res)
  END
  v9.CheckScriptSuccess(lines)
  unlet g:res
enddef

def Test_while_linebreak()
  var lines =<< trim END
      vim9script
      var nr = 0
      while nr <
              10 + 3
            nr = nr
                  + 4
      endwhile
      assert_equal(16, nr)
  END
  v9.CheckScriptSuccess(lines)

  lines =<< trim END
      vim9script
      var nr = 0
      while nr
            <
              10
              +
              3
            nr = nr
                  +
                  4
      endwhile
      assert_equal(16, nr)
  END
  v9.CheckScriptSuccess(lines)
enddef

def Test_for_linebreak()
  var lines =<< trim END
      vim9script
      var nr = 0
      for x
            in
              [1, 2, 3, 4]
          nr = nr + x
      endfor
      assert_equal(10, nr)
  END
  v9.CheckScriptSuccess(lines)

  lines =<< trim END
      vim9script
      var nr = 0
      for x
            in
              [1, 2,
                  3, 4
                  ]
          nr = nr
                 +
                  x
      endfor
      assert_equal(10, nr)
  END
  v9.CheckScriptSuccess(lines)
enddef

def s:MethodAfterLinebreak(arg: string)
  arg
    ->setline(1)
enddef

def Test_method_call_linebreak()
  var lines =<< trim END
      vim9script
      var res = []
      func RetArg(
            arg
            )
            let s:res = a:arg
      endfunc
      [1,
          2,
          3]->RetArg()
      assert_equal([1, 2, 3], res)
  END
  v9.CheckScriptSuccess(lines)

  lines =<< trim END
      new
      var name = [1, 2]
      name
          ->copy()
          ->setline(1)
      assert_equal(['1', '2'], getline(1, 2))
      bwipe!
  END
  v9.CheckDefAndScriptSuccess(lines)

  lines =<< trim END
      new
      def Foo(): string
        return 'the text'
      enddef
      def Bar(F: func): string
        return F()
      enddef
      def Test()
        Foo  ->Bar()
             ->setline(1)
      enddef
      Test()
      assert_equal('the text', getline(1))
      bwipe!
  END
  v9.CheckDefAndScriptSuccess(lines)

  lines =<< trim END
      new
      g:shortlist
          ->copy()
          ->setline(1)
      assert_equal(['1', '2'], getline(1, 2))
      bwipe!
  END
  g:shortlist = [1, 2]
  v9.CheckDefAndScriptSuccess(lines)
  unlet g:shortlist

  new
  MethodAfterLinebreak('foobar')
  assert_equal('foobar', getline(1))
  bwipe!

  lines =<< trim END
      vim9script
      def Foo(): string
          return '# some text'
      enddef

      def Bar(F: func): string
          return F()
      enddef

      Foo->Bar()
         ->setline(1)
  END
  v9.CheckScriptSuccess(lines)
  assert_equal('# some text', getline(1))
  bwipe!
enddef

def Test_method_call_whitespace()
  var lines =<< trim END
    new
    var yank = 'text'
    yank->setline(1)
    yank  ->setline(2)
    yank->  setline(3)
    yank  ->  setline(4)
    assert_equal(['text', 'text', 'text', 'text'], getline(1, 4))
    bwipe!
  END
  v9.CheckDefAndScriptSuccess(lines)
enddef

def Test_method_and_user_command()
  var lines =<< trim END
      vim9script
      def Cmd()
        g:didFunc = 1
      enddef
      command Cmd g:didCmd = 1
      Cmd
      assert_equal(1, g:didCmd)
      Cmd()
      assert_equal(1, g:didFunc)
      unlet g:didFunc
      unlet g:didCmd

      def InDefFunc()
        Cmd
        assert_equal(1, g:didCmd)
        Cmd()
        assert_equal(1, g:didFunc)
        unlet g:didFunc
        unlet g:didCmd
      enddef
      InDefFunc()
  END
  v9.CheckScriptSuccess(lines)
enddef

def Test_option_use_linebreak()
  var lines =<< trim END
      new
      &matchpairs = '(:)'
      &matchpairs->setline(1)
      &matchpairs = '[:]'
      &matchpairs   ->setline(2)
      &matchpairs = '{:}'
      &matchpairs  
          ->setline(3)
      assert_equal(['(:)', '[:]', '{:}'], getline(1, '$'))
      bwipe!
  END
  v9.CheckDefAndScriptSuccess(lines)
enddef

def Test_use_register()
  var lines =<< trim END
      new
      @a = 'one'
      @a->setline(1)
      @b = 'two'
      @b   ->setline(2)
      @c = 'three'
      @c  
          ->setline(3)
      assert_equal(['one', 'two', 'three'], getline(1, '$'))
      bwipe!
  END
  v9.CheckDefAndScriptSuccess(lines)

  lines =<< trim END
      @a = 'echo "text"'
      @a
  END
  v9.CheckDefAndScriptFailure(lines, 'E1207:', 2)

  lines =<< trim END
      @a = 'echo "text"'
      @a

  END
  v9.CheckDefAndScriptFailure(lines, 'E1207:', 2)

  lines =<< trim END
      @a = 'echo "text"'
      @a
          # comment
  END
  v9.CheckDefAndScriptFailure(lines, 'E1207:', 2)

  lines =<< trim END
      @/ = 'pattern'
      @/
  END
  v9.CheckDefAndScriptFailure(lines, 'E1207:', 2)

  lines =<< trim END
      &opfunc = 'nothing'
      &opfunc
  END
  v9.CheckDefAndScriptFailure(lines, 'E1207:', 2)
  &opfunc = ''

  lines =<< trim END
      &l:showbreak = 'nothing'
      &l:showbreak
  END
  v9.CheckDefAndScriptFailure(lines, 'E1207:', 2)
  &l:showbreak = ''

  lines =<< trim END
      &g:showbreak = 'nothing'
      &g:showbreak
  END
  v9.CheckDefAndScriptFailure(lines, 'E1207:', 2)
  &g:showbreak = ''

  lines =<< trim END
      $SomeEnv = 'value'
      $SomeEnv
  END
  v9.CheckDefAndScriptFailure(lines, 'E1207:', 2)
  $SomeEnv = ''

  lines =<< trim END
      eval 'value'
  END
  v9.CheckDefAndScriptFailure(lines, 'E1207:', 1)

  lines =<< trim END
      eval "value"
  END
  v9.CheckDefAndScriptFailure(lines, 'E1207:', 1)
enddef

def Test_environment_use_linebreak()
  var lines =<< trim END
      new
      $TESTENV = 'one'
      $TESTENV->setline(1)
      $TESTENV = 'two'
      $TESTENV  ->setline(2)
      $TESTENV = 'three'
      $TESTENV  
          ->setline(3)
      assert_equal(['one', 'two', 'three'], getline(1, '$'))
      bwipe!
  END
  v9.CheckDefAndScriptSuccess(lines)
enddef

def Test_skipped_expr_linebreak()
  if 0
    var x = []
               ->map(() => 0)
  endif
enddef

def Test_dict_member()
   var test: dict<list<number>> = {data: [3, 1, 2]}
   test.data->sort()
   assert_equal({data: [1, 2, 3]}, test)
   test.data
      ->reverse()
   assert_equal({data: [3, 2, 1]}, test)

  var lines =<< trim END
      vim9script
      var test: dict<list<number>> = {data: [3, 1, 2]}
      test.data->sort()
      assert_equal({data: [1, 2, 3]}, test)
  END
  v9.CheckScriptSuccess(lines)
enddef

def Test_bar_after_command()
  def RedrawAndEcho()
    var x = 'did redraw'
    redraw | echo x
  enddef
  RedrawAndEcho()
  assert_match('did redraw', g:Screenline(&lines))

  def CallAndEcho()
    var x = 'did redraw'
    reg_executing() | echo x
  enddef
  CallAndEcho()
  assert_match('did redraw', g:Screenline(&lines))

  if has('unix')
    # bar in filter write command does not start new command
    def WriteToShell()
      new
      setline(1, 'some text')
      w !cat | cat > Xoutfile
      bwipe!
    enddef
    WriteToShell()
    assert_equal(['some text'], readfile('Xoutfile'))
    delete('Xoutfile')

    # bar in filter read command does not start new command
    def ReadFromShell()
      new
      r! echo hello there | cat > Xoutfile
      r !echo again | cat >> Xoutfile
      bwipe!
    enddef
    ReadFromShell()
    assert_equal(['hello there', 'again'], readfile('Xoutfile'))
    delete('Xoutfile')
  endif
enddef

def Test_filter_is_not_modifier()
  var tags = [{a: 1, b: 2}, {x: 3, y: 4}]
  filter(tags, ( _, v) => has_key(v, 'x') ? 1 : 0 )
  assert_equal([{x: 3, y: 4}], tags)
enddef

def Test_command_modifier_filter()
  var lines =<< trim END
    final expected = "\nType Name Content\n  c  \"c   piyo"
    @a = 'hoge'
    @b = 'fuga'
    @c = 'piyo'

    assert_equal(execute('filter /piyo/ registers abc'), expected)
  END
  v9.CheckDefAndScriptSuccess(lines)

  # also do this compiled
  lines =<< trim END
      @a = 'very specific z3d37dh234 string'
      filter z3d37dh234 registers
      assert_match('very specific z3d37dh234 string', g:Screenline(&lines))
  END
  v9.CheckDefAndScriptSuccess(lines)

  lines =<< trim END
      edit foobar
      redir => g:filter_out
      filter #foobar# ls
      redir END
      assert_match('"foobar"', g:filter_out)
      unlet g:filter_out
  END
  v9.CheckDefAndScriptSuccess(lines)
enddef

def Test_win_command_modifiers()
  assert_equal(1, winnr('$'))

  set splitright
  vsplit
  assert_equal(2, winnr())
  close
  aboveleft vsplit
  assert_equal(1, winnr())
  close
  set splitright&

  vsplit
  assert_equal(1, winnr())
  close
  belowright vsplit
  assert_equal(2, winnr())
  close
  rightbelow vsplit
  assert_equal(2, winnr())
  close

  if has('browse')
    browse set
    assert_equal('option-window', expand('%'))
    close
  endif

  vsplit
  botright split
  assert_equal(3, winnr())
  assert_equal(&columns, winwidth(0))
  close
  close

  vsplit
  topleft split
  assert_equal(1, winnr())
  assert_equal(&columns, winwidth(0))
  close
  close

  gettabinfo()->len()->assert_equal(1)
  tab split
  gettabinfo()->len()->assert_equal(2)
  tabclose

  vertical new
  assert_inrange(&columns / 2 - 2, &columns / 2 + 1, winwidth(0))
  close
enddef

func Test_command_modifier_confirm()
  CheckNotGui
  CheckRunVimInTerminal

  " Test for saving all the modified buffers
  let lines =<< trim END
    call setline(1, 'changed')
    def Getout()
      confirm write Xfile
    enddef
  END
  call writefile(lines, 'Xconfirmscript')
  call writefile(['empty'], 'Xfile')
  let buf = RunVimInTerminal('-S Xconfirmscript', {'rows': 8})
  call term_sendkeys(buf, ":call Getout()\n")
  call WaitForAssert({-> assert_match('(Y)es, \[N\]o: ', term_getline(buf, 8))}, 1000)
  call term_sendkeys(buf, "y")
  call WaitForAssert({-> assert_match('(Y)es, \[N\]o: ', term_getline(buf, 8))}, 1000)
  call term_sendkeys(buf, "\<CR>")
  call TermWait(buf)
  call StopVimInTerminal(buf)

  call assert_equal(['changed'], readfile('Xfile'))
  call delete('Xfile')
  call delete('.Xfile.swp')  " in case Vim was killed
  call delete('Xconfirmscript')
endfunc

def Test_command_modifiers_keep()
  if has('unix')
    def DoTest(addRflag: bool, keepMarks: bool, hasMarks: bool)
      new
      setline(1, ['one', 'two', 'three'])
      normal 1Gma
      normal 2Gmb
      normal 3Gmc
      if addRflag
        set cpo+=R
      else
        set cpo-=R
      endif
      if keepMarks
        keepmarks :%!cat
      else
        :%!cat
      endif
      if hasMarks
        assert_equal(1, line("'a"))
        assert_equal(2, line("'b"))
        assert_equal(3, line("'c"))
      else
        assert_equal(0, line("'a"))
        assert_equal(0, line("'b"))
        assert_equal(0, line("'c"))
      endif
      quit!
    enddef
    DoTest(false, false, true)
    DoTest(true, false, false)
    DoTest(false, true, true)
    DoTest(true, true, true)
    set cpo&vim

    new
    setline(1, ['one', 'two', 'three', 'four'])
    assert_equal(4, line("$"))
    normal 1Gma
    normal 2Gmb
    normal 3Gmc
    lockmarks :1,2!wc
    # line is deleted, marks don't move
    assert_equal(3, line("$"))
    assert_equal('four', getline(3))
    assert_equal(1, line("'a"))
    assert_equal(2, line("'b"))
    assert_equal(3, line("'c"))
    quit!
  endif

  edit Xone
  edit Xtwo
  assert_equal('Xone', expand('#'))
  keepalt edit Xthree
  assert_equal('Xone', expand('#'))

  normal /a*b*
  assert_equal('a*b*', histget("search"))
  keeppatterns normal /c*d*
  assert_equal('a*b*', histget("search"))

  new
  setline(1, range(10))
  :10
  normal gg
  assert_equal(10, getpos("''")[1])
  keepjumps normal 5G
  assert_equal(10, getpos("''")[1])
  quit!
enddef

def Test_bar_line_continuation()
  var lines =<< trim END
      au BufNewFile Xfile g:readFile = 1
          | g:readExtra = 2
      g:readFile = 0
      g:readExtra = 0
      edit Xfile
      assert_equal(1, g:readFile)
      assert_equal(2, g:readExtra)
      bwipe!
      au! BufNewFile

      au BufNewFile Xfile g:readFile = 1
          | g:readExtra = 2
          | g:readMore = 3
      g:readFile = 0
      g:readExtra = 0
      g:readMore = 0
      edit Xfile
      assert_equal(1, g:readFile)
      assert_equal(2, g:readExtra)
      assert_equal(3, g:readMore)
      bwipe!
      au! BufNewFile
      unlet g:readFile
      unlet g:readExtra
      unlet g:readMore
  END
  v9.CheckDefAndScriptSuccess(lines)
enddef

def Test_command_modifier_other()
  new Xsomefile
  setline(1, 'changed')
  var buf = bufnr()
  hide edit Xotherfile
  var info = getbufinfo(buf)
  assert_equal(1, info[0].hidden)
  assert_equal(1, info[0].changed)
  edit Xsomefile
  bwipe!

  au BufNewFile Xfile g:readFile = 1
  g:readFile = 0
  edit Xfile
  assert_equal(1, g:readFile)
  bwipe!
  g:readFile = 0
  noautocmd edit Xfile
  assert_equal(0, g:readFile)
  au! BufNewFile
  unlet g:readFile

  noswapfile edit XnoSwap
  assert_equal(false, &l:swapfile)
  bwipe!

  var caught = false
  try
    sandbox !ls
  catch /E48:/
    caught = true
  endtry
  assert_true(caught)

  :8verbose g:verbose_now = &verbose
  assert_equal(8, g:verbose_now)
  unlet g:verbose_now
enddef

def s:EchoHere()
  echomsg 'here'
enddef
def s:EchoThere()
  unsilent echomsg 'there'
enddef

def Test_modifier_silent_unsilent()
  echomsg 'last one'
  silent echomsg "text"
  assert_equal("\nlast one", execute(':1messages'))

  silent! echoerr "error"

  echomsg 'last one'
  silent EchoHere()
  assert_equal("\nlast one", execute(':1messages'))

  silent EchoThere()
  assert_equal("\nthere", execute(':1messages'))

  try
    silent eval [][0]
  catch
    echomsg "caught"
  endtry
  assert_equal("\ncaught", execute(':1messages'))

  var lines =<< trim END
      vim9script
      set history=11
      silent! while 0
        set history=22
      silent! endwhile
      assert_equal(11, &history)
      set history&
  END
  v9.CheckScriptSuccess(lines)
enddef

def Test_range_after_command_modifier()
  v9.CheckScriptFailure(['vim9script', 'silent keepjump 1d _'], 'E1050: Colon required before a range: 1d _', 2)
  new
  setline(1, 'xxx')
  v9.CheckScriptSuccess(['vim9script', 'silent keepjump :1d _'])
  assert_equal('', getline(1))
  bwipe!

  var lines =<< trim END
      legacy /pat/
  END
  v9.CheckDefExecAndScriptFailure(lines, 'E486: Pattern not found: pat')
enddef

def Test_silent_pattern()
  new
  silent! :/pat/put _
  bwipe!
enddef

def Test_useless_command_modifier()
  g:maybe = true
  var lines =<< trim END
      if g:maybe
      silent endif
  END
  v9.CheckDefAndScriptFailure(lines, 'E1176:', 2)

  lines =<< trim END
      for i in [0]
      silent endfor
  END
  v9.CheckDefFailure(lines, 'E1176:', 2)
  v9.CheckScriptSuccess(['vim9script'] + lines)

  lines =<< trim END
      while g:maybe
      silent endwhile
  END
  v9.CheckDefFailure(lines, 'E1176:', 2)
  g:maybe = false
  v9.CheckScriptSuccess(['vim9script'] + lines)

  lines =<< trim END
      silent try
      finally
      endtry
  END
  v9.CheckDefAndScriptFailure(lines, 'E1176:', 1)

  lines =<< trim END
      try
      silent catch
      endtry
  END
  v9.CheckDefAndScriptFailure(lines, 'E1176:', 2)

  lines =<< trim END
      try
      silent finally
      endtry
  END
  v9.CheckDefAndScriptFailure(lines, 'E1176:', 2)

  lines =<< trim END
      try
      finally
      silent endtry
  END
  v9.CheckDefAndScriptFailure(lines, 'E1176:', 3)

  lines =<< trim END
      leftabove
  END
  v9.CheckDefAndScriptFailure(lines, 'E1082:', 1)

  lines =<< trim END
      leftabove # comment
  END
  v9.CheckDefAndScriptFailure(lines, 'E1082:', 1)
enddef

def Test_eval_command()
  var from = 3
  var to = 5
  g:val = 111
  def Increment(nrs: list<number>)
    for nr in nrs
      g:val += nr
    endfor
  enddef
  eval range(from, to)
        ->Increment()
  assert_equal(111 + 3 + 4 + 5, g:val)
  unlet g:val

  var lines =<< trim END
    vim9script
    g:caught = 'no'
    try
      eval 123 || 0
    catch
      g:caught = 'yes'
    endtry
    assert_equal('yes', g:caught)
    unlet g:caught
  END
  v9.CheckScriptSuccess(lines)
enddef

def Test_map_command()
  var lines =<< trim END
      nnoremap <F3> :echo 'hit F3 #'<CR>
      assert_equal(":echo 'hit F3 #'<CR>", maparg("<F3>", "n"))
  END
  v9.CheckDefAndScriptSuccess(lines)

  # backslash before bar is not removed
  lines =<< trim END
      vim9script

      def Init()
        noremap <buffer> <F5> <ScriptCmd>MyFunc('a') \| MyFunc('b')<CR>
      enddef
      Init()
      unmap <buffer> <F5>
  END
  v9.CheckScriptSuccess(lines)
enddef

def Test_normal_command()
  new
  setline(1, 'doesnotexist')
  var caught = 0
  try
    exe "norm! \<C-]>"
  catch /E433/
    caught = 2
  endtry
  assert_equal(2, caught)

  try
    exe "norm! 3\<C-]>"
  catch /E433/
    caught = 3
  endtry
  assert_equal(3, caught)
  bwipe!
enddef

def Test_put_command()
  new
  @p = 'ppp'
  put p
  assert_equal('ppp', getline(2))

  put ='below'
  assert_equal('below', getline(3))
  put! ='above'
  assert_equal('above', getline(3))
  assert_equal('below', getline(4))

  :2put =['a', 'b', 'c']
  assert_equal(['ppp', 'a', 'b', 'c', 'above'], getline(2, 6))

  :0put =  'first'
  assert_equal('first', getline(1))
  :1put! ='first again'
  assert_equal('first again', getline(1))

  # compute range at runtime
  :%del
  setline(1, range(1, 8))
  @a = 'aaa'
  :$-2put a
  assert_equal('aaa', getline(7))

  setline(1, range(1, 8))
  :2
  :+2put! a
  assert_equal('aaa', getline(4))

  []->mapnew(() => 0)
  :$put ='end'
  assert_equal('end', getline('$'))

  bwipe!

  v9.CheckDefFailure(['put =xxx'], 'E1001:')
enddef

def Test_put_with_linebreak()
  new
  var lines =<< trim END
    vim9script
    pu =split('abc', '\zs')
            ->join()
  END
  v9.CheckScriptSuccess(lines)
  getline(2)->assert_equal('a b c')
  bwipe!
enddef

def Test_command_star_range()
  new
  setline(1, ['xxx foo xxx', 'xxx bar xxx', 'xxx foo xx bar'])
  setpos("'<", [0, 1, 0, 0])
  setpos("'>", [0, 3, 0, 0])
  :*s/\(foo\|bar\)/baz/g
  getline(1, 3)->assert_equal(['xxx baz xxx', 'xxx baz xxx', 'xxx baz xx baz'])

  bwipe!
enddef

def Test_f_args()
  var lines =<< trim END
    vim9script

    func SaveCmdArgs(...)
      let g:args = a:000
    endfunc

    command -nargs=* TestFArgs call SaveCmdArgs(<f-args>)

    TestFArgs
    assert_equal([], g:args)

    TestFArgs one two three
    assert_equal(['one', 'two', 'three'], g:args)
  END
  v9.CheckScriptSuccess(lines)
enddef

def Test_user_command_comment()
  command -nargs=1 Comd echom <q-args>

  var lines =<< trim END
      vim9script
      Comd # comment
  END
  v9.CheckScriptSuccess(lines)

  lines =<< trim END
      vim9script
      Comd# comment
  END
  v9.CheckScriptFailure(lines, 'E1144:')
  delcommand Comd

  lines =<< trim END
      vim9script
      command Foo echo 'Foo'
      Foo3Bar
  END
  v9.CheckScriptFailure(lines, 'E1144: Command "Foo" is not followed by white space: Foo3Bar')

  delcommand Foo
enddef

def Test_star_command()
  var lines =<< trim END
    vim9script
    @s = 'g:success = 8'
    set cpo+=*
    exe '*s'
    assert_equal(8, g:success)
    unlet g:success
    set cpo-=*
    assert_fails("exe '*s'", 'E1050:')
  END
  v9.CheckScriptSuccess(lines)
enddef

def Test_cmd_argument_without_colon()
  new Xfile
  setline(1, ['a', 'b', 'c', 'd'])
  write
  edit +3 %
  assert_equal(3, getcurpos()[1])
  edit +/a %
  assert_equal(1, getcurpos()[1])
  bwipe
  delete('Xfile')
enddef

def Test_ambiguous_user_cmd()
  command Cmd1 eval 0
  command Cmd2 eval 0
  var lines =<< trim END
      Cmd
  END
  v9.CheckDefAndScriptFailure(lines, 'E464:', 1)
  delcommand Cmd1
  delcommand Cmd2
enddef

def Test_command_not_recognized()
  var lines =<< trim END
    d.key = 'asdf'
  END
  v9.CheckDefFailure(lines, 'E1089: Unknown variable: d', 1)

  lines =<< trim END
    d['key'] = 'asdf'
  END
  v9.CheckDefFailure(lines, 'E1089: Unknown variable: d', 1)

  lines =<< trim END
    if 0
      d.key = 'asdf'
    endif
  END
  v9.CheckDefSuccess(lines)
enddef

def Test_magic_not_used()
  new
  for cmd in ['set magic', 'set nomagic']
    exe cmd
    setline(1, 'aaa')
    s/.../bbb/
    assert_equal('bbb', getline(1))
  endfor

  set magic
  setline(1, 'aaa')
  assert_fails('s/.\M../bbb/', 'E486:')
  assert_fails('snomagic/.../bbb/', 'E486:')
  assert_equal('aaa', getline(1))

  bwipe!
enddef

def Test_gdefault_not_used()
  new
  for cmd in ['set gdefault', 'set nogdefault']
    exe cmd
    setline(1, 'aaa')
    s/./b/
    assert_equal('baa', getline(1))
  endfor

  set nogdefault
  bwipe!
enddef

def s:SomeComplFunc(findstart: number, base: string): any
  if findstart
    return 0
  else
    return ['aaa', 'bbb']
  endif
enddef

def Test_insert_complete()
  # this was running into an error with the matchparen hack
  new
  set completefunc=SomeComplFunc
  feedkeys("i\<c-x>\<c-u>\<Esc>", 'ntx')
  assert_equal('aaa', getline(1))

  set completefunc=
  bwipe!
enddef

def Test_wincmd()
  split
  var id1 = win_getid()
  if true
    try | wincmd w | catch | endtry
  endif
  assert_notequal(id1, win_getid())
  close

  split
  var id = win_getid()
  split
  :2wincmd o
  assert_equal(id, win_getid())
  only

  split
  split
  assert_equal(3, winnr('$'))
  :2wincmd c
  assert_equal(2, winnr('$'))
  only

  split
  split
  assert_equal(3, winnr('$'))
  :2wincmd q
  assert_equal(2, winnr('$'))
  only
enddef

def Test_windo_missing_endif()
  var lines =<< trim END
      windo if 1
  END
  v9.CheckDefExecFailure(lines, 'E171:', 1)
enddef

let s:theList = [1, 2, 3]

def Test_lockvar()
  s:theList[1] = 22
  assert_equal([1, 22, 3], s:theList)
  lockvar s:theList
  assert_fails('theList[1] = 77', 'E741:')
  unlockvar s:theList
  s:theList[1] = 44
  assert_equal([1, 44, 3], s:theList)

  if 0
    lockvar whatever
  endif

  g:lockme = [1, 2, 3]
  lockvar 1 g:lockme
  g:lockme[1] = 77
  assert_equal([1, 77, 3], g:lockme)

  lockvar 2 g:lockme
  var caught = false
  try
    g:lockme[1] = 99
  catch /E1119:/
    caught = true
  endtry
  assert_true(caught)
  assert_equal([1, 77, 3], g:lockme)
  unlet g:lockme

  # also for non-materialized list
  g:therange = range(3)
  lockvar 2 g:therange
  caught = false
  try
    g:therange[1] = 99
  catch /E1119:/
    caught = true
  endtry
  assert_true(caught)
  assert_equal([0, 1, 2], g:therange)
  unlet g:therange

  # use exclamation mark for locking deeper
  g:nestedlist = [1, [2, 3], 4]
  lockvar! g:nestedlist
  try
    g:nestedlist[1][0] = 9
  catch /E1119:/
    caught = true
  endtry
  assert_true(caught)
  unlet g:nestedlist

  var d = {a: 1, b: 2}
  d.a = 3
  d.b = 4
  assert_equal({a: 3, b: 4}, d)
  lockvar d.a
  d.b = 5
  var ex = ''
  try
    d.a = 6
  catch
    ex = v:exception
  endtry
  assert_match('E1121:', ex)
  unlockvar d['a']
  d.a = 7
  assert_equal({a: 7, b: 5}, d)

  caught = false
  try
    lockvar d.c
  catch /E716/
    caught = true
  endtry
  assert_true(caught)

  var lines =<< trim END
      vim9script
      g:bl = 0z1122
      lockvar g:bl
      def Tryit()
        g:bl[1] = 99
      enddef
      Tryit()
  END
  v9.CheckScriptFailure(lines, 'E741:', 1)

  lines =<< trim END
      vim9script
      var theList = [1, 2, 3]
      def SetList()
        theList[1] = 22
        assert_equal([1, 22, 3], theList)
        lockvar theList
        theList[1] = 77
      enddef
      SetList()
  END
  v9.CheckScriptFailure(lines, 'E1119', 4)

  lines =<< trim END
      vim9script
      var theList = [1, 2, 3]
      def AddToList()
        lockvar theList
        theList += [4]
      enddef
      AddToList()
  END
  v9.CheckScriptFailure(lines, 'E741', 2)

  lines =<< trim END
      vim9script
      var theList = [1, 2, 3]
      def AddToList()
        lockvar theList
        add(theList, 4)
      enddef
      AddToList()
  END
  v9.CheckScriptFailure(lines, 'E741', 2)

  # can unlet a locked list item but not change it
  lines =<< trim END
    var ll = [1, 2, 3]
    lockvar ll[1]
    unlet ll[1]
    assert_equal([1, 3], ll)
  END
  v9.CheckDefAndScriptSuccess(lines)
  lines =<< trim END
    var ll = [1, 2, 3]
    lockvar ll[1]
    ll[1] = 9
  END
  v9.CheckDefExecAndScriptFailure(lines, ['E1119:', 'E741'], 3)

  # can unlet a locked dict item but not change it
  lines =<< trim END
    var dd = {a: 1, b: 2}
    lockvar dd.a
    unlet dd.a
    assert_equal({b: 2}, dd)
  END
  v9.CheckDefAndScriptSuccess(lines)
  lines =<< trim END
    var dd = {a: 1, b: 2}
    lockvar dd.a
    dd.a = 3
  END
  v9.CheckDefExecAndScriptFailure(lines, ['E1121:', 'E741'], 3)

  lines =<< trim END
      var theList = [1, 2, 3]
      lockvar theList
  END
  v9.CheckDefFailure(lines, 'E1178', 2)

  lines =<< trim END
      var theList = [1, 2, 3]
      unlockvar theList
  END
  v9.CheckDefFailure(lines, 'E1178', 2)

  lines =<< trim END
      vim9script
      var name = 'john'
      lockvar nameX
  END
  v9.CheckScriptFailure(lines, 'E1246', 3)

  lines =<< trim END
      vim9script
      var name = 'john'
      def LockIt()
        lockvar nameX
      enddef
      LockIt()
  END
  v9.CheckScriptFailure(lines, 'E1246', 1)

  lines =<< trim END
      vim9script
      const name = 'john'
      unlockvar name
  END
  v9.CheckScriptFailure(lines, 'E46', 3)

  lines =<< trim END
      vim9script
      const name = 'john'
      def UnLockIt()
        unlockvar name
      enddef
      UnLockIt()
  END
  v9.CheckScriptFailure(lines, 'E46', 1)
enddef

def Test_substitute_expr()
  var to = 'repl'
  new
  setline(1, 'one from two')
  s/from/\=to
  assert_equal('one repl two', getline(1))

  setline(1, 'one from two')
  s/from/\=to .. '_x'
  assert_equal('one repl_x two', getline(1))

  setline(1, 'one from two from three')
  var also = 'also'
  s/from/\=to .. '_' .. also/g#e
  assert_equal('one repl_also two repl_also three', getline(1))

  setline(1, 'abc abc abc')
  for choice in [true, false]
    :1s/abc/\=choice ? 'yes' : 'no'/
  endfor
  assert_equal('yes no abc', getline(1))

  setline(1, 'from')
  v9.CheckDefExecFailure(['s/from/\=g:notexist/'], 'E121: Undefined variable: g:notexist')

  bwipe!

  v9.CheckDefFailure(['s/from/\="x")/'], 'E488:')
  v9.CheckDefFailure(['s/from/\="x"/9'], 'E488:')

  v9.CheckDefExecFailure(['s/this/\="that"/'], 'E486:')

  # When calling a function the right instruction list needs to be restored.
  g:cond = true
  var lines =<< trim END
      vim9script
      def Foo()
          Bar([])
      enddef
      def Bar(l: list<number>)
        if g:cond
          s/^/\=Rep()/
          for n in l[:]
          endfor
        endif
      enddef
      def Rep(): string
          return 'rep'
      enddef
      new
      Foo()
      assert_equal('rep', getline(1))
      bwipe!
  END
  v9.CheckScriptSuccess(lines)
  unlet g:cond

  # List results in multiple lines
  new
  setline(1, 'some text here')
  s/text/\=['aaa', 'bbb', 'ccc']/
  assert_equal(['some aaa', 'bbb', 'ccc', ' here'], getline(1, '$'))
  bwipe!

  # inside "if 0" substitute is ignored
  if 0
    s/a/\=nothing/ and | some more
  endif
enddef

def Test_redir_to_var()
  var result: string
  redir => result
    echo 'something'
  redir END
  assert_equal("\nsomething", result)

  redir =>> result
    echo 'more'
  redir END
  assert_equal("\nsomething\nmore", result)

  var d: dict<string>
  redir => d.redir
    echo 'dict'
  redir END
  assert_equal({redir: "\ndict"}, d)

  var l = ['a', 'b', 'c']
  redir => l[1]
    echo 'list'
  redir END
  assert_equal(['a', "\nlist", 'c'], l)

  var dl = {l: ['x']}
  redir => dl.l[0]
    echo 'dict-list'
  redir END
  assert_equal({l: ["\ndict-list"]}, dl)

  redir =>> d.redir
    echo 'more'
  redir END
  assert_equal({redir: "\ndict\nmore"}, d)

  var lines =<< trim END
    redir => notexist
  END
  v9.CheckDefFailure(lines, 'E1089:')

  lines =<< trim END
    var text: string
    redir => text
  END
  v9.CheckDefFailure(lines, 'E1185:')

  lines =<< trim END
    var ls = 'asdf'
    redir => ls[1]
    redir END
  END
  v9.CheckDefFailure(lines, 'E1141:')

  lines =<< trim END
      var text: string
      redir => text
        echo 'hello'
        redir > Xfile
      redir END
  END
  v9.CheckDefFailure(lines, 'E1092:')

  lines =<< trim END
      var text: number
      redir => text
        echo 'hello'
      redir END
  END
  v9.CheckDefFailure(lines, 'E1012:')
enddef

def Test_echo_void()
  var lines =<< trim END
      vim9script
      def NoReturn()
        echo 'nothing'
      enddef
      echo NoReturn()
  END
  v9.CheckScriptFailure(lines, 'E1186:', 5)

  lines =<< trim END
      vim9script
      def NoReturn()
        echo 'nothing'
      enddef
      def Try()
        echo NoReturn()
      enddef
      defcompile
  END
  v9.CheckScriptFailure(lines, 'E1186:', 1)
enddef

def Test_cmdwin_block()
  augroup justTesting
    autocmd BufEnter * {
      echomsg 'in block'
    }
  augroup END
  feedkeys('q:', 'xt')
  redraw
  feedkeys("aclose\<CR>", 'xt')

  au! justTesting
enddef

def Test_var_not_cmd()
  var lines =<< trim END
      g:notexist:cmd
  END
  v9.CheckDefAndScriptFailure(lines, ['E488: Trailing characters: :cmd', 'E121: Undefined variable: g:notexist'], 1)

  lines =<< trim END
      g-pat-cmd
  END
  v9.CheckDefAndScriptFailure(lines, 'E1241:', 1)
  lines =<< trim END
      g.pat.cmd
  END
  v9.CheckDefAndScriptFailure(lines, ['E1001: Variable not found: g', 'E121: Undefined variable: g'], 1)

  lines =<< trim END
      s:notexist:repl
  END
  v9.CheckDefAndScriptFailure(lines, ['E488: Trailing characters: :repl', 'E1268:'], 1)

  lines =<< trim END
      notexist:repl
  END
  v9.CheckDefAndScriptFailure(lines, ['E476:', 'E492:'], 1)

  lines =<< trim END
      s-pat-repl
  END
  v9.CheckDefAndScriptFailure(lines, 'E1241:', 1)
  lines =<< trim END
      s.pat.repl
  END
  v9.CheckDefAndScriptFailure(lines, ['E1001: Variable not found: s', 'E121: Undefined variable: s'], 1)

  lines =<< trim END
      w:notexist->len()
  END
  v9.CheckDefExecAndScriptFailure(lines, 'E121: Undefined variable: w:notexist', 1)

  lines =<< trim END
      b:notexist->len()
  END
  v9.CheckDefExecAndScriptFailure(lines, 'E121: Undefined variable: b:notexist', 1)

  lines =<< trim END
      t:notexist->len()
  END
  v9.CheckDefExecAndScriptFailure(lines, 'E121: Undefined variable: t:notexist', 1)
enddef

def Test_no_space_after_command()
  var lines =<< trim END
      g /pat/cmd
  END
  v9.CheckDefAndScriptFailure(lines, 'E1242:', 1)
  lines =<< trim END
      g #pat#cmd
  END
  v9.CheckDefAndScriptFailure(lines, 'E1242:', 1)

  new
  setline(1, 'some pat')
  lines =<< trim END
      g#pat#print
  END
  v9.CheckDefAndScriptSuccess(lines)
  lines =<< trim END
      g# pat#print
  END
  v9.CheckDefAndScriptSuccess(lines)
  bwipe!

  lines =<< trim END
      s /pat/repl
  END
  v9.CheckDefAndScriptFailure(lines, 'E1242:', 1)
  lines =<< trim END
      s #pat#repl
  END
  v9.CheckDefAndScriptFailure(lines, 'E1242:', 1)
  lines =<< trim END
      s#pat#repl
  END
  v9.CheckDefExecAndScriptFailure(lines, 'E486:', 1)
  lines =<< trim END
      s# pat#repl
  END
  v9.CheckDefExecAndScriptFailure(lines, 'E486:', 1)
enddef

" Test for the 'previewpopup' option
def Test_previewpopup()
  set previewpopup=height:10,width:60
  pedit Xfile
  var id = popup_findpreview()
  assert_notequal(id, 0)
  assert_match('Xfile', popup_getoptions(id).title)
  popup_clear()
  set previewpopup&
enddef

def Test_syntax_enable_clear()
  syntax clear
  syntax enable
  highlight clear String
  assert_equal(true, hlget('String')->get(0, {})->get('default', false))
  syntax clear
enddef


" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
